2018-07-06 06:57:49 +00:00
< ? php
namespace App\Models ;
2024-07-31 12:36:28 +00:00
use Carbon\Carbon ;
2024-07-05 06:38:31 +00:00
use Illuminate\Database\Eloquent\Builder ;
2021-06-30 04:00:41 +00:00
use Illuminate\Database\Eloquent\Factories\HasFactory ;
2018-07-06 06:57:49 +00:00
use Illuminate\Database\Eloquent\Model ;
2024-07-05 02:04:08 +00:00
use Illuminate\Support\Collection ;
use Illuminate\Support\Facades\DB ;
2021-06-29 06:36:34 +00:00
use Leenooks\Traits\ScopeActive ;
2018-07-06 06:57:49 +00:00
2021-06-29 06:36:34 +00:00
use App\Interfaces\IDs ;
2018-08-20 12:15:28 +00:00
2021-06-29 06:36:34 +00:00
/**
* Class Account
* Service Accounts
*
* Attributes for accounts :
2021-12-24 01:14:01 +00:00
* + lid : Local ID for account
* + sid : System ID for account
* + name : Account Name
* + taxes : Taxes Applicable to this account
2021-06-29 06:36:34 +00:00
*/
class Account extends Model implements IDs
2018-07-06 06:57:49 +00:00
{
2024-07-05 02:04:08 +00:00
use HasFactory , ScopeActive ;
2018-08-20 12:15:28 +00:00
2024-07-05 06:38:31 +00:00
/* STATIC */
2024-07-07 00:21:27 +00:00
/**
* A list of invoices that are in credit for all accounts
*
* @ param Collection | NULL $invoices
* @ return Collection
*/
2024-07-05 13:36:28 +00:00
public static function InvoicesCredit ( Collection $invoices = NULL ) : Collection
{
return ( new self )
-> invoiceSummaryCredit ( $invoices , TRUE )
-> get ();
}
2024-07-07 00:21:27 +00:00
/**
* A list of invoices that are due for all accounts
*
* @ param Collection | NULL $invoices
* @ return Collection
*/
2024-07-05 06:38:31 +00:00
public static function InvoicesDue ( Collection $invoices = NULL ) : Collection
{
return ( new self )
-> invoiceSummaryDue ( $invoices , TRUE )
-> get ();
}
2022-04-21 04:41:26 +00:00
/* INTERFACES */
public function getLIDAttribute () : string
{
return sprintf ( '%04s' , $this -> id );
}
public function getSIDAttribute () : string
{
return sprintf ( '%02s-%s' , $this -> site_id , $this -> getLIDAttribute ());
}
2018-07-17 04:10:40 +00:00
2021-06-29 06:36:34 +00:00
/* RELATIONS */
2024-07-05 02:04:08 +00:00
/**
* Charges assigned to this account
*
* @ return \Illuminate\Database\Eloquent\Relations\HasMany
*/
2021-10-01 04:59:04 +00:00
public function charges ()
{
return $this -> hasMany ( Charge :: class );
}
2018-07-13 04:53:44 +00:00
/**
2024-07-05 02:04:08 +00:00
* Country this account belongs to
2018-07-13 04:53:44 +00:00
*/
public function country ()
{
return $this -> belongsTo ( Country :: class );
}
2024-07-07 00:21:27 +00:00
/**
* Group this account is assigned to
* Groups are used for pricing control
*
* @ return \Illuminate\Database\Eloquent\Relations\HasOneThrough
*/
2021-12-24 01:14:01 +00:00
public function group ()
{
return $this -> hasOneThrough ( Group :: class , AccountGroup :: class , 'account_id' , 'id' , 'id' , 'group_id' );
}
2023-05-09 10:28:51 +00:00
/**
2024-07-05 02:04:08 +00:00
* Invoices created for this account
2023-05-09 10:28:51 +00:00
*/
2019-06-11 02:36:58 +00:00
public function invoices ()
{
2023-05-09 10:28:51 +00:00
return $this -> hasMany ( Invoice :: class )
2024-07-09 10:17:30 +00:00
-> with ([ 'items.taxes' , 'payment_items.payment' ]);
2019-06-11 02:36:58 +00:00
}
2024-07-05 02:04:08 +00:00
/**
* Relation to only return active invoices
*/
public function invoices_active ()
2018-07-13 04:53:44 +00:00
{
2024-07-05 02:04:08 +00:00
return $this -> invoices ()
2024-07-07 00:21:27 +00:00
-> with ( 'active' , TRUE );;
2018-07-13 04:53:44 +00:00
}
2024-07-05 02:04:08 +00:00
/**
* Payments received and assigned to this account
*/
2019-06-07 06:54:27 +00:00
public function payments ()
{
2023-05-09 10:28:51 +00:00
return $this -> hasMany ( Payment :: class )
-> with ([ 'items' ]);
2019-06-07 06:54:27 +00:00
}
2024-07-05 02:04:08 +00:00
/**
* Relation to only return active payments
*/
public function payments_active ()
{
return $this -> payments ()
2024-07-07 00:21:27 +00:00
-> with ( 'active' , TRUE );
2024-07-05 02:04:08 +00:00
}
2024-07-07 00:21:27 +00:00
/**
* Return the link to a provider ' s info for this account
*/
2022-08-19 10:12:08 +00:00
public function providers ()
{
2023-05-10 07:37:43 +00:00
return $this -> belongsToMany ( ProviderOauth :: class , 'account__provider' )
2022-08-19 10:12:08 +00:00
-> withPivot ( 'ref' , 'synctoken' , 'created_at' , 'updated_at' );
}
2024-07-05 02:04:08 +00:00
/**
* Services assigned to this account
*/
public function services ()
2018-08-08 23:33:51 +00:00
{
2024-07-05 02:04:08 +00:00
return $this -> hasMany ( Service :: class )
2024-07-05 06:38:31 +00:00
-> with ([ 'product.translate' , 'product.type.supplied' ]);
2018-08-08 23:33:51 +00:00
}
2024-07-05 02:04:08 +00:00
/**
* Relation to only return active services
*/
public function services_active ()
2022-03-05 00:05:35 +00:00
{
2024-07-05 02:04:08 +00:00
return $this -> services ()
2024-07-29 13:12:53 +00:00
-> ServiceActive ();
2022-03-05 00:05:35 +00:00
}
2024-07-23 12:17:09 +00:00
/**
* Supplier configuration for this account
*/
public function suppliers ()
{
return $this -> belongsToMany ( Supplier :: class )
-> withPivot ( 'supplier_ref' , 'created_at' );
}
2024-07-07 00:21:27 +00:00
/**
* Taxes applicable for this account
*/
2021-12-24 01:14:01 +00:00
public function taxes ()
{
2024-07-05 02:04:08 +00:00
return $this -> hasMany ( Tax :: class , 'country_id' , 'country_id' )
-> select ([ 'id' , 'zone' , 'rate' , 'country_id' ]);
2021-12-24 01:14:01 +00:00
}
2024-07-05 02:04:08 +00:00
/**
* User that owns this account
*/
2018-07-13 04:53:44 +00:00
public function user ()
{
2021-06-29 03:18:52 +00:00
return $this -> belongsTo ( User :: class );
2018-07-13 04:53:44 +00:00
}
2018-07-17 04:10:40 +00:00
2021-06-29 06:36:34 +00:00
/* SCOPES */
2020-04-01 12:35:06 +00:00
2019-06-21 06:21:48 +00:00
/**
* Search for a record
*
2019-06-29 00:14:12 +00:00
* @ param $query
* @ param string $term
2021-07-23 07:25:26 +00:00
* @ return mixed
2019-06-21 06:21:48 +00:00
*/
public function scopeSearch ( $query , string $term )
{
// Build our where clause
2021-07-07 07:45:16 +00:00
if ( is_numeric ( $term )) {
2019-06-21 06:21:48 +00:00
$query -> where ( 'id' , 'like' , '%' . $term . '%' );
} else {
2024-07-06 12:56:51 +00:00
$query -> where ( 'company' , 'ilike' , '%' . $term . '%' )
-> orWhere ( 'address1' , 'ilike' , '%' . $term . '%' )
-> orWhere ( 'address2' , 'ilike' , '%' . $term . '%' )
-> orWhere ( 'city' , 'ilike' , '%' . $term . '%' );
2019-06-21 06:21:48 +00:00
}
return $query ;
}
2021-06-29 06:36:34 +00:00
/* ATTRIBUTES */
2019-06-21 06:21:48 +00:00
2021-07-01 23:12:34 +00:00
/**
* Get the address for the account
*
2024-07-07 05:14:55 +00:00
* @ return Collection
2021-07-01 23:12:34 +00:00
*/
2024-07-07 05:14:55 +00:00
public function getAddressAttribute () : Collection
2021-07-01 23:12:34 +00:00
{
2024-07-05 02:04:08 +00:00
return collect ([
'address1' => $this -> address1 ,
'address2' => $this -> address2 ,
'location' => sprintf ( '%s %s %s' ,
$this -> city . (( $this -> state || $this -> zip ) ? ',' : '' ),
$this -> state ,
$this -> zip )
])
2024-07-07 05:14:55 +00:00
-> filter ();
2022-07-29 07:45:17 +00:00
}
2021-06-29 06:36:34 +00:00
/**
2022-04-21 04:41:26 +00:00
* Return the account name
2021-06-29 06:36:34 +00:00
*
2024-07-05 02:04:08 +00:00
* @ return string
2021-06-29 06:36:34 +00:00
*/
2022-04-21 04:41:26 +00:00
public function getNameAttribute () : string
2019-06-07 06:54:27 +00:00
{
2024-07-07 00:21:27 +00:00
return $this -> company ? : ( $this -> user_id ? $this -> user -> getNameSurFirstAttribute () : 'LID:' . $this -> id );
2018-08-08 23:33:51 +00:00
}
2022-06-12 01:21:20 +00:00
/**
* Return the type of account this is - if it has a company name , then its a business account .
*
* @ return string
*/
2024-07-05 02:04:08 +00:00
public function getTypeAttribute () : string
2019-06-12 06:25:15 +00:00
{
return $this -> company ? 'Business' : 'Private' ;
}
2022-04-22 00:36:41 +00:00
/* METHODS */
2019-06-11 02:36:58 +00:00
2024-07-31 12:36:28 +00:00
public function invoice_next () : Collection
{
// Collect all the invoice items for our active services
$nextdate = ( $x = $this
-> services_active
-> filter ( fn ( $item ) => $item -> isBilled () && $item -> invoice_next )
-> sortBy ( fn ( $item ) => ( string ) $item -> invoice_next ))
-> first ()
-> invoice_next
-> clone ();
// Add any additional items that will be invoiced 30 days
$nextitemsdate = max ( $nextdate , Carbon :: now ())
-> clone ()
-> addMonth ()
-> subDay ()
-> endOfday ();
$items = $x
-> filter ( fn ( $item ) => $item -> invoice_next -> lessThan ( $nextitemsdate ))
-> sortBy ( fn ( $item ) => $item -> invoice_next . $item -> name )
-> map ( fn ( $item ) => $item -> next_invoice_items ( $nextitemsdate ))
-> flatten ();
// Add any account charges (charges with no active service)
foreach ( $this -> charges -> filter ( function ( $item ) { return $item -> unprocessed && (( ! $this -> service_id ) || ( ! $item -> service -> isBilled ())); }) as $oo ) {
$ii = new InvoiceItem ;
$ii -> active = TRUE ;
$ii -> service_id = $oo -> service_id ;
$ii -> product_id = $this -> product_id ;
$ii -> quantity = $oo -> quantity ;
$ii -> item_type = $oo -> type ;
$ii -> price_base = $oo -> amount ;
$ii -> start_at = $oo -> start_at ;
$ii -> stop_at = $oo -> stop_at ;
$ii -> module_id = 30 ; // @todo This shouldnt be hard coded
$ii -> module_ref = $oo -> id ;
$ii -> site_id = 1 ; // @todo
$ii -> addTaxes ( $this -> country -> taxes );
$items -> push ( $ii );
}
return $items ;
}
2024-07-05 02:04:08 +00:00
/**
* List of invoices ( summary ) for this account
*
* @ param Collection | NULL $invoices
2024-07-31 12:36:28 +00:00
* @ param bool $all
* @ return Builder
2024-07-05 02:04:08 +00:00
*/
2024-07-05 06:38:31 +00:00
public function invoiceSummary ( Collection $invoices = NULL , bool $all = FALSE ) : Builder
2024-07-05 02:04:08 +00:00
{
return ( new Invoice )
-> select ([
2024-07-05 06:38:31 +00:00
'invoices.account_id' ,
'invoices.id as id' ,
2024-07-05 02:04:08 +00:00
DB :: raw ( 'SUM(item) AS _item' ),
DB :: raw ( 'SUM(tax) AS _tax' ),
DB :: raw ( 'SUM(payments) AS _payment' ),
2024-07-05 13:36:28 +00:00
DB :: raw ( 'SUM(discount)+COALESCE(invoices.discount_amt,0) AS _discount' ),
2024-07-05 02:04:08 +00:00
DB :: raw ( 'SUM(item_total) AS _item_total' ),
DB :: raw ( 'SUM(payment_fees) AS _payment_fee' ),
2024-07-05 13:36:28 +00:00
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' ),
2024-07-05 06:38:31 +00:00
'invoices.due_at' ,
'invoices.created_at' ,
2024-07-05 02:04:08 +00:00
])
-> 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' ),
])
2024-07-06 09:28:14 +00:00
-> leftjoin ( 'payment_items' ,[ 'payment_items.payment_id' => 'payments.id' ])
2024-07-05 02:04:08 +00:00
-> 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' ),
2024-07-06 12:17:41 +00:00
DB :: raw ( 'ROUND(CAST(SUM(COALESCE(amount,0)) AS NUMERIC),2) AS tax' ),
2024-07-05 13:36:28 +00:00
DB :: raw ( 'ROUND(CAST(SUM(COALESCE(invoice_items.discount_amt,0)) AS NUMERIC),2) AS discount' ),
2024-07-06 12:17:41 +00:00
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' ),
2024-07-05 02:04:08 +00:00
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 )
2024-07-06 12:17:41 +00:00
-> where ( fn ( $query ) => $query -> where ( 'invoice_item_taxes.active' , TRUE ) -> orWhereNull ( 'invoice_item_taxes.active' ))
2024-07-05 02:04:08 +00:00
-> where ( 'invoices.active' , TRUE )
-> groupBy ([ 'invoice_items.invoice_id' ]),
), 'p' )
-> join ( 'invoices' ,[ 'invoices.id' => 'invoice_id' ])
2024-07-05 06:38:31 +00:00
-> 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 )
2024-07-05 13:36:28 +00:00
-> 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' );
2024-07-05 06:38:31 +00:00
}
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' ))
2024-07-05 13:36:28 +00:00
-> havingRaw ( 'ROUND(CAST(SUM(item_total)-COALESCE(invoices.discount_amt,0)-SUM(payments) AS NUMERIC),2) <= 0' );
2024-07-05 02:04:08 +00:00
}
2023-05-05 05:48:24 +00:00
/**
* Return the taxed value of a value
*
* @ param float $value
* @ return float
*/
public function taxed ( float $value ) : float
{
return Tax :: calc ( $value , $this -> taxes );
}
2018-07-06 06:57:49 +00:00
}