invoiceSummaryCredit($invoices,TRUE) ->get(); } public static function InvoicesDue(Collection $invoices=NULL): Collection { return (new self) ->invoiceSummaryDue($invoices,TRUE) ->get(); } /* INTERFACES */ public function getLIDAttribute(): string { return sprintf('%04s',$this->id); } public function getSIDAttribute(): string { return sprintf('%02s-%s',$this->site_id,$this->getLIDAttribute()); } /* RELATIONS */ /** * Charges assigned to this account * * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function charges() { return $this->hasMany(Charge::class); } /** * Country this account belongs to */ public function country() { return $this->belongsTo(Country::class); } public function external() { return $this->belongsToMany(External\Integrations::class,'external_account',NULL,'external_integration_id'); } public function group() { return $this->hasOneThrough(Group::class,AccountGroup::class,'account_id','id','id','group_id'); } /** * Invoices created for this account * * @todo This needs to be optimised, to only return outstanding invoices and invoices for a specific age (eg: 2 years worth) */ public function invoices() { return $this->hasMany(Invoice::class) ->with(['items.taxes','paymentitems.payment']); } /** * Relation to only return active invoices * * @todo Only return active invoice_items */ public function invoices_active() { return $this->invoices() ->active(); } /** * Payments received and assigned to this account */ public function payments() { return $this->hasMany(Payment::class) ->with(['items']); } /** * Relation to only return active payments * * @todo Only return active payment_items */ public function payments_active() { return $this->payments() ->active(); } public function providers() { return $this->belongsToMany(ProviderOauth::class,'account__provider') ->where('account__provider.site_id',$this->site_id) ->withPivot('ref','synctoken','created_at','updated_at'); } /** * Services assigned to this account */ public function services() { return $this->hasMany(Service::class) ->with(['product.translate','product.type.supplied']); } /** * Relation to only return active services */ public function services_active() { return $this->services() ->active(); } public function taxes() { return $this->hasMany(Tax::class,'country_id','country_id') ->select(['id','zone','rate','country_id']); } /** * User that owns this account */ public function user() { return $this->belongsTo(User::class); } /* SCOPES */ /** * Search for a record * * @param $query * @param string $term * @return mixed */ public function scopeSearch($query,string $term) { // Build our where clause if (is_numeric($term)) { $query->where('id','like','%'.$term.'%'); } else { $query->where('company','ilike','%'.$term.'%') ->orWhere('address1','ilike','%'.$term.'%') ->orWhere('address2','ilike','%'.$term.'%') ->orWhere('city','ilike','%'.$term.'%'); } return $query; } /* ATTRIBUTES */ /** * Get the address for the account * * @return array * @todo Change this to return a collection */ public function getAddressAttribute(): array { return collect([ 'address1' => $this->address1, 'address2' => $this->address2, 'location' => sprintf('%s %s %s', $this->city.(($this->state || $this->zip) ? ',' : ''), $this->state, $this->zip) ]) ->filter() ->values() ->toArray(); } /** * Return the account name * * @return string */ public function getNameAttribute(): string { return $this->company ?: ($this->user_id ? $this->user->getSurFirstNameAttribute() : 'LID:'.$this->id); } /** * Return the type of account this is - if it has a company name, then its a business account. * * @return string */ public function getTypeAttribute(): string { return $this->company ? 'Business' : 'Private'; } /* METHODS */ /** * Get the due invoices on an account * * @return mixed * @deprecated use invoiceSummary->filter(_balance > 0) */ public function dueInvoices() { return $this->invoices->filter(function($item) { return $item->active AND $item->due > 0; }); } /** * List of invoices (summary) for this account * * @param Collection|NULL $invoices * @return Collection */ public function invoiceSummary(Collection $invoices=NULL,bool $all=FALSE): Builder { return (new Invoice) ->select([ 'invoices.account_id', 'invoices.id as id', DB::raw('SUM(item) AS _item'), DB::raw('SUM(tax) AS _tax'), DB::raw('SUM(payments) AS _payment'), DB::raw('SUM(discount)+COALESCE(invoices.discount_amt,0) AS _discount'), DB::raw('SUM(item_total) AS _item_total'), DB::raw('SUM(payment_fees) AS _payment_fee'), DB::raw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0) AS NUMERIC),2) AS _total'), DB::raw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) AS _balance'), 'invoices.due_at', 'invoices.created_at', ]) ->from( (new Payment) ->select([ 'invoice_id', DB::raw('0 as item'), DB::raw('0 as tax'), DB::raw('0 as discount'), DB::raw('0 as item_total'), DB::raw('SUM(amount) AS payments'), DB::raw('SUM(fees_amt) AS payment_fees'), ]) ->leftjoin('payment_items',['payment_items.payment_id'=>'payments.id']) ->where('payments.active',TRUE) ->where('payment_items.active',TRUE) ->groupBy(['payment_items.invoice_id']) ->union( (new InvoiceItem) ->select([ 'invoice_id', DB::raw('ROUND(CAST(SUM(quantity*price_base) AS NUMERIC),2) AS item'), DB::raw('ROUND(CAST(SUM(COALESCE(amount,0)) AS NUMERIC),2) AS tax'), DB::raw('ROUND(CAST(SUM(COALESCE(invoice_items.discount_amt,0)) AS NUMERIC),2) AS discount'), DB::raw('ROUND(CAST(SUM(ROUND(CAST(quantity*(price_base-COALESCE(invoice_items.discount_amt,0)) AS NUMERIC),2))+SUM(ROUND(CAST(COALESCE(amount,0) AS NUMERIC),2)) AS NUMERIC),2) AS item_total'), DB::raw('0 as payments'), DB::raw('0 as payment_fees'), ]) ->leftjoin('invoice_item_taxes',['invoice_item_taxes.invoice_item_id'=>'invoice_items.id']) ->rightjoin('invoices',['invoices.id'=>'invoice_items.invoice_id']) ->where('invoice_items.active',TRUE) ->where(fn($query)=>$query->where('invoice_item_taxes.active',TRUE)->orWhereNull('invoice_item_taxes.active')) ->where('invoices.active',TRUE) ->groupBy(['invoice_items.invoice_id']), ),'p') ->join('invoices',['invoices.id'=>'invoice_id']) ->when(($all === FALSE),fn($query)=>$query->where('invoices.account_id',$this->id)) ->orderBy('due_at') ->groupBy(['invoices.account_id','invoices.id','invoices.created_at','invoices.due_at','invoices.discount_amt']) ->with(['account']); } public function invoiceSummaryDue(Collection $invoices=NULL,bool $all=FALSE): Builder { return $this->invoiceSummary($invoices,$all) ->havingRaw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) > 0'); } public function invoiceSummaryCredit(Collection $invoices=NULL,bool $all=FALSE): Builder { return $this->invoiceSummary($invoices,$all) ->havingRaw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) < 0'); } public function invoiceSummaryPast(Collection $invoices=NULL,bool $all=FALSE): Builder { return $this->invoiceSummary($invoices,$all) ->join('payment_items',['payment_items.invoice_id'=>'invoices.id']) ->join('payments',['payments.id'=>'payment_items.payment_id']) ->addSelect(DB::raw('max(paid_at) as _paid_at')) ->havingRaw('ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) <= 0'); } /** * Return the taxed value of a value * * @param float $value * @return float */ public function taxed(float $value): float { return Tax::calc($value,$this->taxes); } }