From f43748e20aaed84e0ef621ef8ffb0623d80b89d9 Mon Sep 17 00:00:00 2001 From: Deon George Date: Wed, 31 Jul 2024 22:36:28 +1000 Subject: [PATCH] Add account next invoice --- app/Http/Controllers/ChargeController.php | 3 +- app/Models/Account.php | 51 ++++++++++++++++++- app/Models/Service.php | 13 ++--- app/Models/User.php | 37 -------------- .../account/widget/invoice_next.blade.php | 35 +++++++++++++ .../account/widget/service_active.blade.php | 2 +- .../backend/adminlte/charge/pending.blade.php | 21 +++++--- .../theme/backend/adminlte/home.blade.php | 2 +- .../adminlte/invoice/widget/next.blade.php | 41 --------------- 9 files changed, 109 insertions(+), 96 deletions(-) create mode 100644 resources/views/theme/backend/adminlte/account/widget/invoice_next.blade.php delete mode 100644 resources/views/theme/backend/adminlte/invoice/widget/next.blade.php diff --git a/app/Http/Controllers/ChargeController.php b/app/Http/Controllers/ChargeController.php index e0bada6..0a4bdde 100644 --- a/app/Http/Controllers/ChargeController.php +++ b/app/Http/Controllers/ChargeController.php @@ -37,7 +37,8 @@ class ChargeController extends Controller public function delete(Charge $o): array { if (Gate::allows('delete',$o)) { - $o->delete(); + $o->active = FALSE; + $o->save(); return ['ok']; diff --git a/app/Models/Account.php b/app/Models/Account.php index 63a5fa0..1918ca2 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -2,6 +2,7 @@ namespace App\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -252,11 +253,59 @@ class Account extends Model implements IDs /* METHODS */ + 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; + } + /** * List of invoices (summary) for this account * * @param Collection|NULL $invoices - * @return Collection + * @param bool $all + * @return Builder */ public function invoiceSummary(Collection $invoices=NULL,bool $all=FALSE): Builder { diff --git a/app/Models/Service.php b/app/Models/Service.php index d285ca8..40d1623 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -2,6 +2,7 @@ namespace App\Models; +use Carbon\Carbon; use Exception; use Illuminate\Database\Eloquent\Casts\AsCollection; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -11,7 +12,6 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Symfony\Component\HttpKernel\Exception\HttpException; -use Leenooks\Carbon; use Leenooks\Casts\LeenooksCarbon; use App\Models\Product\Type; @@ -33,8 +33,6 @@ use App\Traits\{ScopeServiceActive,ScopeServiceUserAuthorised}; * + billing_interval : The period that this service is billed for by default * + billing_interval_string : The period that this service is billed for by default as a name * + invoiced_to : When this service has been billed to - * + category : The type of service this is, eg: broadband, phone - * + category_name : The type of service this is, eg: Broadband, Telephone (in human friendly) * + contract_term : The term that this service must be active * + contract_end : The date that the contract ends for this service * + name : Service short name with service address @@ -615,10 +613,13 @@ class Service extends Model implements IDs * * @return Carbon */ - public function getInvoiceNextAttribute(): Carbon + public function getInvoiceNextAttribute(): ?Carbon { $last = $this->getInvoicedToAttribute(); + if ($this->stop_at && $last->greaterThan($this->stop_at)) + return NULL; + return $last ? $last->addDay() : (min($this->start_at,$this->invoice_next_at) ?: Carbon::now()); @@ -1074,9 +1075,9 @@ class Service extends Model implements IDs if (is_null($billdate)) $billdate = $invoiced_to->clone()->addDays(config('osb.invoice_days')); - while ($invoiced_to < ($this->stop_at ?: $billdate)) { + while ($invoiced_to <= ($this->stop_at ?: $billdate)) { $ii = new InvoiceItem; - $period = Invoice::invoice_period($invoiced_to,$this->getBillingIntervalAttribute(),$this->product->price_recur_strict); + $period = Invoice::invoice_period($invoiced_to,$this->getBillingIntervalAttribute(),(bool)$this->product->price_recur_strict); $ii->active = TRUE; $ii->service_id = $this->id; diff --git a/app/Models/User.php b/app/Models/User.php index ebb1174..bd6fae5 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,8 +5,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; -use Illuminate\Support\Collection; -use Illuminate\Database\Eloquent\Collection as DatabaseCollection; use Illuminate\Support\Facades\Log; use Laravel\Passport\HasApiTokens; use Leenooks\Traits\ScopeActive; @@ -272,41 +270,6 @@ class User extends Authenticatable implements IDs return in_array($this->role(),['wholesaler']); } - /** - * Get all the items for the next invoice - * - * @param bool $future - * @return DatabaseCollection - * @deprecated This should be done in accounts - */ - public function next_invoice_items(bool $future=FALSE): Collection - { - return collect(); - $result = new DatabaseCollection; - $this->services->load(['invoice_items.taxes']); - - foreach ($this->services as $o) { - if ($future) { - if ($o->invoice_next->subDays(config('app.invoice_inadvance'))->isPast()) - continue; - - } else { - if ($o->invoice_next->subDays(config('app.invoice_inadvance'))->isFuture()) - continue; - } - - foreach ($o->next_invoice_items($future) as $oo) - $result->push($oo); - } - - $result->load([ - 'product.translate', - 'service.type', - ]); - - return $result; - } - /** * Determine what the logged in user's role is * + Wholesaler - aka Super User diff --git a/resources/views/theme/backend/adminlte/account/widget/invoice_next.blade.php b/resources/views/theme/backend/adminlte/account/widget/invoice_next.blade.php new file mode 100644 index 0000000..bb736be --- /dev/null +++ b/resources/views/theme/backend/adminlte/account/widget/invoice_next.blade.php @@ -0,0 +1,35 @@ + +@use(Carbon\Carbon) + + +@if(($x=$o->invoice_next())->count()) +
+
+

The following items will be invoiced on or after {{ max($x->first()->start_at->subDays(config('osb.invoice_days')),Carbon::now())->format('Y-m-d') }}

+ + + @foreach ($x->groupBy('service_id') as $id => $oo) + + + + + + @endforeach + + + + + +
{{ $oo->first()?->product?->category_name ?: '-' }} + @if($id) + {{ $oo->first()->service->name }} + @else + Account Charges + @endif + ${{ number_format($oo->sum('total'),2) }}
TOTAL${{ number_format($x->sum('total'),2) }}
+
+
+ +@else +

No items currently due to invoice.

+@endif diff --git a/resources/views/theme/backend/adminlte/account/widget/service_active.blade.php b/resources/views/theme/backend/adminlte/account/widget/service_active.blade.php index 64bfdb9..4f0dc99 100644 --- a/resources/views/theme/backend/adminlte/account/widget/service_active.blade.php +++ b/resources/views/theme/backend/adminlte/account/widget/service_active.blade.php @@ -27,7 +27,7 @@ {{ $so->product->category_name }} {{ $so->name_short }} {{ $so->product->name }} - {{ $so->external_billing ? '-' : $so->invoice_next->format('Y-m-d') }} + {{ ($so->external_billing || (! $so->invoice_next)) ? '-' : $so->invoice_next->format('Y-m-d') }} @endforeach diff --git a/resources/views/theme/backend/adminlte/charge/pending.blade.php b/resources/views/theme/backend/adminlte/charge/pending.blade.php index bec70ba..049e57c 100644 --- a/resources/views/theme/backend/adminlte/charge/pending.blade.php +++ b/resources/views/theme/backend/adminlte/charge/pending.blade.php @@ -30,6 +30,7 @@ Service Description Total +   @@ -42,7 +43,8 @@ {{ $o->account->name }} {{ $o->service->name_short }} {{ $o->description }} - {{ number_format($o->quantity*$o->amount,2) }} + {{ number_format($o->total,2) }} + @empty @@ -55,18 +57,21 @@ + + @endsection -@section('page-styles') - @css(datatables,bootstrap4) -@append -@section('page-scripts') - @js(datatables,bootstrap4) +@pa(datatables,rowgroup|conditionalpaging) +@section('page-scripts') diff --git a/resources/views/theme/backend/adminlte/home.blade.php b/resources/views/theme/backend/adminlte/home.blade.php index 85e321d..9405261 100644 --- a/resources/views/theme/backend/adminlte/home.blade.php +++ b/resources/views/theme/backend/adminlte/home.blade.php @@ -76,7 +76,7 @@
- @include('theme.backend.adminlte.invoice.widget.next',['future'=>TRUE]) + @include('theme.backend.adminlte.account.widget.invoice_next',['o'=>$ao])
diff --git a/resources/views/theme/backend/adminlte/invoice/widget/next.blade.php b/resources/views/theme/backend/adminlte/invoice/widget/next.blade.php deleted file mode 100644 index c33cd34..0000000 --- a/resources/views/theme/backend/adminlte/invoice/widget/next.blade.php +++ /dev/null @@ -1,41 +0,0 @@ - - -@if(($x=$o->next_invoice_items($future))->count()) -
-
- - - @foreach ($x->groupBy('product_id') as $id => $oo) - - - - - - @foreach ($oo->groupBy('service_id') as $ooo) - - - - - - @foreach ($ooo as $io) - - - - - - - @endforeach - @endforeach - @endforeach - - - - - -
{{ $oo->first()->product->name }}${{ number_format($oo->sum('total'),2) }}
{{ $ooo->first()->service->sid }}{{ $ooo->first()->service->name }}
  {{ $io->item_type_name }}${{ number_format($io->total,2) }}
TOTAL${{ number_format($x->sum('total'),2) }}
-
-
- -@else -

No items currently due to invoice.

-@endif \ No newline at end of file