From 4c24f0919535c4108d66bde356e86fad3ca13339 Mon Sep 17 00:00:00 2001 From: Deon George Date: Fri, 16 May 2025 11:10:28 +1000 Subject: [PATCH] Introduce cost and cost override into service model. Update service internal view to show cost/billing left on contracted services --- app/Models/Service.php | 74 +++++++++++++++++-- .../service/widget/internal.blade.php | 29 +++++++- 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/app/Models/Service.php b/app/Models/Service.php index 16a6441..d6388f0 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -45,6 +45,7 @@ use App\Traits\{ScopeAccountUserAuthorised,ScopeServiceActive,SiteID}; * * Methods: * + isChargeOverridden : Has the price been overridden? + * + isCostOverridden : Has the cost been overridden? * + isPending : Is this a pending active service * * @package App\Models @@ -535,6 +536,11 @@ class Service extends Model implements IDs return $this->account->taxed($this->billing_charge()); } + public function getBillingCostAttribute(): float + { + return $this->account->taxed($this->billing_cost()); + } + /** * Determine a monthly price for a service, even if it is billed at a different frequency * @@ -543,7 +549,16 @@ class Service extends Model implements IDs */ public function getBillingChargeNormalisedAttribute(): float { - return number_format($this->getBillingChargeAttribute()*Invoice::billing_change($this->getBillingIntervalAttribute(),$this->offering->billing_interval),2); + return number_format( + $this->getBillingChargeAttribute()*Invoice::billing_change($this->getBillingIntervalAttribute(),$this->offering->billing_interval), + 2); + } + + public function getBillingCostNormalisedAttribute(): float + { + return number_format( + $this->getBillingCostAttribute()*Invoice::billing_change($this->getBillingIntervalAttribute(),$this->offering->billing_interval), + 2); } /** @@ -567,7 +582,7 @@ class Service extends Model implements IDs } /** - * Return the earliest date that the service can be cancelled + * Return the earliest date that the service can be canceled as per contract/billing intervals * * @return Carbon */ @@ -916,17 +931,21 @@ class Service extends Model implements IDs /** * Provide billing charge to a future date * + * If $date is earlier than our contract end date, then our charge is to the contract end date. + * If $date is after our contract end date: + * + and we are still in contract, then our charge is to contract end date plus additional time, else + * + then our charge is the months to the $date + * * @param Carbon $date * @return float * @throws Exception */ public function billing_charge_to(Carbon $date): float { - // if the date is less than the paid to, but less than the cancel date to, return cancel-paid to charge - // If the date is greater than the paid to, and less than the cancel date to, return cancel-paid to charge - if ($this->getPaidToAttribute()->lessThan($this->getCancelDateAttribute())) { - $max = max($date,$this->getPaidToAttribute())->clone(); - $d = $max->diffInDays($this->getCancelDateAttribute()); + $max = max($date,$this->getCancelDateAttribute())->clone(); + + if ($this->getPaidToAttribute()->lessThan($max)) { + $d = $this->getPaidToAttribute()->diffInDays($max); return $this->account->taxed($d/30*$this->getBillingChargeNormalisedAttribute()); } @@ -934,6 +953,42 @@ class Service extends Model implements IDs return 0; } + /** + * The amount we are charged for the client to have this service + * @return float + */ + public function billing_cost(): float + { + return is_null($this->cost) + ? $this->product->getBaseCostAttribute() + : $this->cost; + } + + /** + * Calculate our costs to keep this service to a future date + * + * If $date is earlier than the contract end date, then our cost is to the contract end date. + * If $date is after the contract end date: + * + and we are still in contract, then our cost is to contract end date plus additional time, else + * + then our cost is the months to the $date + * + * @param Carbon $date + * @return float + * @throws Exception + */ + public function billing_cost_to(Carbon $date): float + { + $max = max($date,$this->getCancelDateAttribute())->clone(); + + if ($this->getInvoicedToAttribute()->lessThan($max)) { + $d = $this->getInvoicedToAttribute()->diffInDays($max); + + return $this->account->taxed($d/30*$this->getBillingCostNormalisedAttribute()); + } + + return 0; + } + /** * Get the stage parameters * @@ -1065,6 +1120,11 @@ class Service extends Model implements IDs return ! is_null($this->price); } + public function isCostOverridden(): bool + { + return ! is_null($this->cost); + } + public function isContract(): bool { return $this->getContractEndAttribute()->greaterThan(Carbon::now()); diff --git a/resources/views/theme/backend/adminlte/service/widget/internal.blade.php b/resources/views/theme/backend/adminlte/service/widget/internal.blade.php index 4a3b35e..cefe3a4 100644 --- a/resources/views/theme/backend/adminlte/service/widget/internal.blade.php +++ b/resources/views/theme/backend/adminlte/service/widget/internal.blade.php @@ -1,4 +1,6 @@ @use(App\Models\Invoice) +@use(Carbon\CarbonInterface) + @php($c=$o->product) @@ -29,7 +31,8 @@ Product - #{{ $c->supplied->id }}: {{ $c->supplied->name_long }} + #{{ $o->product_id }}: {{ $o->product->name }} + #{{ $c->supplied->id }}: {{ $c->supplied->name_long }} @if($p->exists)   #{{ $p->supplied->id }}: {{ $p->supplied->name_long }} @@ -79,10 +82,16 @@ @if($x) ${{ number_format($b=$o->billing_charge_normalised,2) }} @else - {{ number_format($b=$o->billing_charge_normalised,2) }} + ${{ number_format($b=$o->billing_charge_normalised,2) }} + @endif + + isCostOverridden()) class="text-danger" @endif> + @if($x) + ${{ number_format($b=$o->billing_cost_normalised,2) }} + @else + ${{ number_format($o->billing_cost_normalised,2) }} @endif - ${{ number_format($a=$o->account->taxed($c->base_cost)*Invoice::billing_change($o->billing_interval,Invoice::BILL_MONTHLY),2) }} {!! markup($a,$b) !!} @if($p->exists) isChargeOverridden()) class="text-danger" @endif>${{ number_format($b=$o->account->taxed($p->base_charge)*Invoice::billing_change($o->billing_interval,Invoice::BILL_MONTHLY),2) }} @@ -109,11 +118,25 @@ ${{ number_format($b=$o->account->taxed($o->product->min_charge),2) }} ${{ number_format($a=$o->account->taxed($c->supplied->min_cost),2) }} {!! markup($a,$b) !!} + @if($p->exists) ${{ number_format($a=$o->account->taxed($p->min_charge),2) }} ${{ number_format($a=$o->account->taxed($p->supplied->min_cost ?? 0),2) }} {!! markup($a,$b) !!} @endif + + + Contract Left + @if($o->isContract()) + ${{ number_format($o->billing_charge_to($o->contract_end),2) }} ({{ $o->paid_to->format('Y-m-d') }}) + ${{ number_format($o->billing_cost_to($o->contract_end),2) }} ({{ $o->invoiced_to->format('Y-m-d') }}) + {{ $o->contract_end->format('Y-m-d') }}
({{ $o->contract_end->diffForHumans(now(),CarbonInterface::DIFF_RELATIVE_TO_OTHER,FALSE,2) }} today) + + @else + Not on contract +   + @endif + \ No newline at end of file