Optimise Invoice
This commit is contained in:
@@ -7,6 +7,7 @@ use Clarkeash\Doorman\Facades\Doorman;
|
||||
use Clarkeash\Doorman\Models\Invite;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Leenooks\Casts\LeenooksCarbon;
|
||||
use Leenooks\Traits\ScopeActive;
|
||||
|
||||
@@ -19,16 +20,16 @@ use App\Traits\PushNew;
|
||||
* Invoices that belong to an Account
|
||||
*
|
||||
* Attributes for services:
|
||||
* + created_at : Date the invoice was created
|
||||
* + due : Balance due on an invoice
|
||||
* + due_date : Date the invoice is due
|
||||
* + invoice_date : Date the invoice was created
|
||||
* + due_at : Date the invoice is due
|
||||
* + lid : Local ID for invoice
|
||||
* + paid : Total of payments received (excluding pending)
|
||||
* + paid_date : Date the invoice was paid in full
|
||||
* + paid_pending : Total of pending payments received
|
||||
* + sid : System ID for invoice
|
||||
* + sub_total : Invoice sub-total before taxes
|
||||
* + total_tax : Invoices total of taxes
|
||||
* + tax_total : Invoices total of taxes
|
||||
* + total : Invoice total
|
||||
*
|
||||
* @package App\Models
|
||||
@@ -97,13 +98,13 @@ class Invoice extends Model implements IDs
|
||||
// Array of items that can be updated with PushNew
|
||||
protected $pushable = ['items'];
|
||||
|
||||
/*
|
||||
protected $with = [
|
||||
'account.country.currency',
|
||||
'items.taxes',
|
||||
'paymentitems'
|
||||
'items_active:id,start_at,stop_at,quantity,price_base,discount_amt,item_type,product_id,service_id,invoice_id',
|
||||
'items_active.taxes:id,invoice_item_id,amount,tax_id',
|
||||
'items_active.product:id',
|
||||
'items_active.product.translate:id,product_id,name_short,name_detail',
|
||||
'payment_items_active:id,amount,payment_id,invoice_id',
|
||||
];
|
||||
*/
|
||||
|
||||
/* STATIC METHODS */
|
||||
|
||||
@@ -181,29 +182,58 @@ class Invoice extends Model implements IDs
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
/**
|
||||
* Account this invoice belongs to
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Items on this invoice belongs to
|
||||
*/
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany(InvoiceItem::class)
|
||||
->where('active',TRUE)
|
||||
->with(['taxes','product']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Active items on this invoice belongs to
|
||||
*/
|
||||
public function items_active()
|
||||
{
|
||||
return $this->items()
|
||||
->where('active',TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Payments applied to this invoice
|
||||
*/
|
||||
public function payments()
|
||||
{
|
||||
return $this->hasManyThrough(Payment::class,PaymentItem::class,NULL,'id',NULL,'payment_id')
|
||||
->active();
|
||||
->where('active',TRUE);
|
||||
}
|
||||
|
||||
public function paymentitems()
|
||||
/**
|
||||
* Payment items attached to this invoice
|
||||
*/
|
||||
public function payment_items()
|
||||
{
|
||||
return $this->hasMany(PaymentItem::class);
|
||||
}
|
||||
|
||||
public function payment_items_active()
|
||||
{
|
||||
return $this->payment_items()
|
||||
->where('payment_items.active',TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 3rd party provider details to this invoice (eg: accounting providers)
|
||||
*/
|
||||
public function providers()
|
||||
{
|
||||
return $this->belongsToMany(ProviderOauth::class,'invoice__provider')
|
||||
@@ -236,31 +266,6 @@ class Invoice extends Model implements IDs
|
||||
return sprintf('%3.2f',$this->getTotalAttribute()-$this->getPaidAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* @todo Change references to due_at to use due_date
|
||||
*/
|
||||
public function getDueDateAttribute(): Carbon
|
||||
{
|
||||
return $this->due_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date the invoices was created
|
||||
*
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getInvoiceDateAttribute(): Carbon
|
||||
{
|
||||
return $this->created_at;
|
||||
}
|
||||
|
||||
// @todo Move this to a site configuration
|
||||
public function getInvoiceTextAttribute()
|
||||
{
|
||||
return sprintf('Thank you for using %s for your Internet Services.',config('site')->site_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total of payments received for this invoice
|
||||
* excluding pending payments
|
||||
@@ -269,9 +274,7 @@ class Invoice extends Model implements IDs
|
||||
*/
|
||||
public function getPaidAttribute(): float
|
||||
{
|
||||
return $this->paymentitems
|
||||
->filter(function($item) { return ! $item->payment->pending_status && $item->payment->active; })
|
||||
->sum('amount');
|
||||
return $this->payment_items_active->sum('amount');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,11 +285,13 @@ class Invoice extends Model implements IDs
|
||||
*/
|
||||
public function getPaidDateAttribute(): ?Carbon
|
||||
{
|
||||
// If the invoice still has a due balance, its not paid
|
||||
if ($this->getDueAttribute())
|
||||
return NULL;
|
||||
|
||||
$o = $this->payments
|
||||
->filter(function($item) { return ! $item->pending_status; })
|
||||
$o = $this
|
||||
->payments
|
||||
->filter(fn($item)=>(! $item->pending_status))
|
||||
->last();
|
||||
|
||||
return $o?->paid_at;
|
||||
@@ -299,8 +304,8 @@ class Invoice extends Model implements IDs
|
||||
*/
|
||||
public function getPaidPendingAttribute(): float
|
||||
{
|
||||
return $this->paymentitems
|
||||
->filter(function($item) { return $item->payment->pending_status; })
|
||||
return $this->payment_items
|
||||
->filter(fn($item)=>$item->payment->pending_status)
|
||||
->sum('amount');
|
||||
}
|
||||
|
||||
@@ -311,28 +316,17 @@ class Invoice extends Model implements IDs
|
||||
*/
|
||||
public function getSubTotalAttribute(): float
|
||||
{
|
||||
return $this->items->where('active',TRUE)->sum('sub_total');
|
||||
return $this->items_active->sum('sub_total');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invoices taxes total
|
||||
*
|
||||
* @return float
|
||||
* @deprecated use getTotalTaxAttribute();
|
||||
*/
|
||||
public function getTaxTotalAttribute(): float
|
||||
{
|
||||
return $this->getTotalTaxAttribute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invoices taxes total
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getTotalTaxAttribute(): float
|
||||
{
|
||||
return $this->items->where('active',TRUE)->sum('tax');
|
||||
return $this->items_active->sum('tax');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,17 +336,11 @@ class Invoice extends Model implements IDs
|
||||
*/
|
||||
public function getTotalAttribute(): float
|
||||
{
|
||||
return $this->getSubTotalAttribute()+$this->getTotalTaxAttribute();
|
||||
return $this->getSubTotalAttribute()+$this->getTaxTotalAttribute();
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
// @todo This shouldnt be here - current should be handled at an account level.
|
||||
public function currency()
|
||||
{
|
||||
return $this->account->country->currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a download link for non-auth downloads
|
||||
*
|
||||
@@ -366,57 +354,37 @@ class Invoice extends Model implements IDs
|
||||
$tokendate = ($x=Carbon::now()->addDays(21)) > ($y=$this->due_at->addDays(21)) ? $x : $y;
|
||||
|
||||
// Extend the expire date
|
||||
if ($io AND ($tokendate > $io->valid_until)) {
|
||||
if ($io && ($tokendate > $io->valid_until)) {
|
||||
$io->valid_until = $tokendate;
|
||||
$io->save();
|
||||
}
|
||||
|
||||
$code = (! $io) ? Doorman::generate()->for($this->account->user->email)->uses(0)->expiresOn($tokendate)->make()->first()->code : $io->code;
|
||||
$code = (! $io)
|
||||
? Doorman::generate()
|
||||
->for($this->account->user->email)
|
||||
->uses(0)
|
||||
->expiresOn($tokendate)
|
||||
->make()
|
||||
->first()
|
||||
->code
|
||||
: $io->code;
|
||||
|
||||
return url('u/invoice',[$this->id,'email',$code]);
|
||||
}
|
||||
|
||||
// @todo document
|
||||
public function products()
|
||||
/**
|
||||
* Return all the items on an invoice for a particular service and product
|
||||
*
|
||||
* @param Product $po
|
||||
* @param Service $so
|
||||
* @return Collection
|
||||
*/
|
||||
public function product_service_items(Product $po,Service $so): Collection
|
||||
{
|
||||
$return = collect();
|
||||
|
||||
foreach ($this->items->groupBy('product_id') as $o) {
|
||||
$po = $o->first()->product;
|
||||
$po->count = count($o->pluck('service_id')->unique());
|
||||
|
||||
$return->push($po);
|
||||
}
|
||||
|
||||
return $return->sortBy(function ($item) {
|
||||
return $item->name;
|
||||
});
|
||||
}
|
||||
|
||||
// @todo document
|
||||
public function product_services(Product $po)
|
||||
{
|
||||
$return = collect();
|
||||
|
||||
$this->items->load(['service']);
|
||||
|
||||
foreach ($this->items->filter(function ($item) use ($po) {
|
||||
return $item->product_id == $po->id;
|
||||
}) as $o)
|
||||
{
|
||||
$so = $o->service;
|
||||
$return->push($so);
|
||||
};
|
||||
|
||||
return $return->unique()->sortBy('name');
|
||||
}
|
||||
|
||||
// @todo document
|
||||
public function product_service_items(Product $po,Service $so)
|
||||
{
|
||||
return $this->items->filter(function ($item) use ($po,$so) {
|
||||
return $item->product_id == $po->id AND $item->service_id == $so->id;
|
||||
})->filter()->sortBy('item_type');
|
||||
return $this
|
||||
->items_active
|
||||
->filter(fn($item)=>($item->product_id === $po->id) && ($item->service_id === $so->id))
|
||||
->sortBy('item_type');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,6 +407,7 @@ class Invoice extends Model implements IDs
|
||||
*
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @todo Change this to a saving event
|
||||
*/
|
||||
public function save(array $options = [])
|
||||
{
|
||||
@@ -453,4 +422,29 @@ class Invoice extends Model implements IDs
|
||||
|
||||
return parent::save($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group the invoice items by product ID, returning the number of products and total
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function summary_products(): Collection
|
||||
{
|
||||
$return = collect();
|
||||
|
||||
foreach ($this->items_active->groupBy('product_id') as $o) {
|
||||
$po = $o->first()->product;
|
||||
$po->count = count($o->pluck('service_id')->unique());
|
||||
|
||||
$return->push([
|
||||
'product' => $o->first()->product,
|
||||
'services' => $o->pluck('service_id')->unique(),
|
||||
'sub_total' => $o->sum('sub_total'),
|
||||
'tax_total' => $o->sum('tax'),
|
||||
'total' => $o->sum('total'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $return->sortBy('product.name');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user