Code refactor work. New optimised query to get invoice status summary for an account

This commit is contained in:
2024-07-05 12:04:08 +10:00
parent 59dc825bf7
commit 648d941893
15 changed files with 375 additions and 169 deletions

View File

@@ -2,7 +2,6 @@
namespace App\Models;
use Awobaz\Compoships\Compoships;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Casts\AsCollection;
@@ -19,10 +18,8 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
use Leenooks\Carbon as LeenooksCarbon;
use App\Models\Product\Type;
use App\Models\Scopes\SiteScope;
use App\Interfaces\IDs;
use App\Traits\ScopeServiceUserAuthorised;
use App\Traits\SiteID;
/**
* Class Service
@@ -60,24 +57,23 @@ use App\Traits\SiteID;
*/
class Service extends Model implements IDs
{
use HasFactory,ScopeServiceUserAuthorised,SiteID,Compoships;
use HasFactory,ScopeServiceUserAuthorised;
protected $casts = [
'order_info'=>AsCollection::class,
];
protected $dates = [
'invoice_last_at',
'invoice_next_at',
'start_at',
'stop_at',
'order_info' => AsCollection::class,
'invoice_last_at' => 'datetime:Y-m-d', // @todo Can these be removed, since we can work out invoice dynamically now
'invoice_next_at' => 'datetime:Y-m-d', // @todo Can these be removed, since we can work out invoice dynamically now
'stop_at' => 'datetime:Y-m-d',
'start_at' => 'datetime:Y-m-d',
];
/** @deprecated */
protected $appends = [
'category_name',
'name_short',
];
/** @deprecated */
protected $visible = [
// 'account_name',
// 'admin_service_id_url',
@@ -95,8 +91,6 @@ class Service extends Model implements IDs
];
protected $with = [
//'invoice_items',
//'product.type.supplied',
'type',
];
@@ -332,8 +326,7 @@ class Service extends Model implements IDs
*/
public function account()
{
return $this->belongsTo(Account::class,['account_id','site_id'],['id','site_id'])
->withoutGlobalScope(SiteScope::class);
return $this->belongsTo(Account::class);
}
/**
@@ -353,12 +346,31 @@ class Service extends Model implements IDs
*/
public function charges()
{
return $this->hasMany(Charge::class,['service_id','site_id'],['id','site_id'])
->withoutGlobalScope(SiteScope::class)
->where('active','=',TRUE)
return $this->hasMany(Charge::class)
->orderBy('created_at');
}
/**
* Return only the active charges
*/
public function charges_active()
{
return $this->charges()
->active();
}
/**
* Return only the charges not yet processed
*/
public function charges_pending()
{
return $this->charges()
->pending();
}
/**
* Product changes for this service
*/
public function changes()
{
return $this->belongsToMany(Product::class,'service__change','service_id','product_id','id','id')
@@ -367,53 +379,80 @@ class Service extends Model implements IDs
->withTimestamps();
}
// @todo changed to invoiced_items
/**
* @deprecated use invoiced_items
*/
public function invoice_items($active=TRUE)
{
$query = $this->hasMany(InvoiceItem::class,['service_id','site_id'],['id','site_id'])
->withoutGlobalScope(SiteScope::class)
->where('item_type','=',0)
->orderBy('start_at');
// @todo Change to $query->active();
if ($active)
$query->where('active','=',TRUE);
return $query;
return $this->invoiced_items_active();
}
/**
* Invoices for this service
* Invoices that this service is itemised on
*/
public function invoices($active=TRUE)
public function invoiced_items()
{
$query = $this->hasManyThrough(Invoice::class,InvoiceItem::class,NULL,'id',NULL,'invoice_id')
->when($this->site_id,function($q) {
return $q->where('invoices.site_id', $this->site_id)
->withoutGlobalScope(SiteScope::class);
})
->distinct('id')
->where('invoices.site_id','=',$this->site_id)
->where('invoice_items.site_id','=',$this->site_id)
->orderBy('created_at')
->orderBy('due_at');
if ($active)
$query->where('invoice_items.active','=',TRUE)
->where('invoices.active','=',TRUE);
return $query;
return $this->hasMany(InvoiceItem::class)
->with(['taxes']);
}
/**
* Account that ordered the service
* Invoices that this service is itemised on that is active
*/
public function invoiced_items_active()
{
return $this->invoiced_items()
->where('active',TRUE);
}
/**
* Return the extra charged items for this service (ie: item_type != 0)
*/
public function invoiced_extra_items()
{
return $this->hasMany(InvoiceItem::class)
->where('item_type','<>',0)
->orderBy('service_id')
->orderBy('start_at');
}
/**
* Return active extra items charged
*/
public function invoiced_extra_items_active()
{
return $this->invoiced_extra_items()
->where('active',TRUE);
}
/**
* Return the service charged items for this service (ie: item_type == 0)
*/
public function invoiced_service_items()
{
return $this->hasMany(InvoiceItem::class)
->where('item_type','=',0)
->orderBy('service_id')
->orderBy('start_at');
}
/**
* Return active service items charged
*/
public function invoiced_service_items_active()
{
return $this->invoiced_service_items()
->where('active',TRUE);
}
/**
* User that ordered the service
*
* @return BelongsTo
*/
public function orderedby()
{
return $this->belongsTo(Account::class,['ordered_by','site_id'],['id','site_id'])
->withoutGlobalScope(SiteScope::class);
return $this->belongsTo(User::class);
}
/**
@@ -423,8 +462,7 @@ class Service extends Model implements IDs
*/
public function product()
{
return $this->belongsTo(Product::class,['product_id','site_id'],['id','site_id'])
->withoutGlobalScope(SiteScope::class);
return $this->belongsTo(Product::class);
}
/**
@@ -444,10 +482,11 @@ class Service extends Model implements IDs
*/
public function scopeActive($query)
{
return $query->where(function () use ($query) {
$query->where($this->getTable().'.active',TRUE)
->orWhereNotIn('order_status',self::INACTIVE_STATUS);
});
return $query->where(
fn($query)=>
$query->where($this->getTable().'.active',TRUE)
->orWhereNotIn('order_status',self::INACTIVE_STATUS)
);
}
/**
@@ -458,20 +497,11 @@ class Service extends Model implements IDs
*/
public function scopeInActive($query)
{
return $query->where(function () use ($query) {
$query->where($this->getTable().'.active',FALSE)
->orWhereIn('order_status',self::INACTIVE_STATUS);
});
}
/**
* Enable to perform queries without eager loading
*
* @param $query
* @return mixed
*/
public function scopeNoEagerLoads($query){
return $query->setEagerLoads([]);
return $query->where(
fn($query)=>
$query->where($this->getTable().'.active',FALSE)
->orWhereIn('order_status',self::INACTIVE_STATUS)
);
}
/**
@@ -723,7 +753,7 @@ class Service extends Model implements IDs
if (! $this->product->price_recur_strict)
return 1;
$n = $this->invoice_next->diff($this->invoice_next_end)->days+1;
$n = round($this->invoice_next->diffInDays($this->invoice_next_end),0);
switch ($this->recur_schedule) {
case Invoice::BILL_WEEKLY:
@@ -735,7 +765,7 @@ class Service extends Model implements IDs
break;
case Invoice::BILL_QUARTERLY:
$d = $this->invoice_next->addQuarter()->startOfQuarter()->diff($this->invoice_next_end->startOfQuarter())->days;
$d = round($this->invoice_next_end->startOfQuarter()->diffInDays($this->invoice_next->addQuarter()->startOfQuarter()),1);
break;
case Invoice::BILL_SEMI_YEARLY:
@@ -845,19 +875,15 @@ class Service extends Model implements IDs
*/
public function getPaidToAttribute(): ?Carbon
{
if (! $this->invoices->count())
return NULL;
// Last paid invoice
$lastpaid = $this
->invoices()
->filter(fn($item)=>$item->_balance <= 0)
->last();
foreach ($this->invoices->reverse() as $o)
if ($o->due == 0)
break;
return $o->items
->filter(function($item) {
return $item->item_type === 0;
})
->last()
->stop_at;
return $lastpaid
? $this->invoiced_service_items_active->where('invoice_id',$lastpaid->id)->where('type',0)->max('stop_at')
: NULL;
}
/**
@@ -1148,6 +1174,15 @@ class Service extends Model implements IDs
return $this->product->hasUsage();
}
/**
* Return this service invoices
*/
public function invoices()
{
return $this->account
->invoiceSummary($this->invoiced_service_items_active->pluck('invoice_id'));
}
/**
* Determine if a service is active. It is active, if active=1, or the order_status is not in self::INACTIVE_STATUS[]
*