Code refactor work. New optimised query to get invoice status summary for an account
This commit is contained in:
@@ -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[]
|
||||
*
|
||||
|
Reference in New Issue
Block a user