@extends('layouts.portal') @php $rows = $plan->installments->where('status', '!=', 'cancelled')->values(); $next = $plan->nextInstallment(); $fs = $plan->financialStatus(); $open = $plan->status === 'active'; $inv = $plan->invoice; $course = $plan->enrollment?->batch?->course ?? $plan->course; $verify = \App\Services\DocumentVerifier::url('plan', $plan->code); $tone = fn ($t) => match ($t) { 'payment_received', 'allocation', 'plan_created' => 'green', 'refund', 'reversal', 'cancel', 'invoice_voided' => 'red', 'waiver', 'discount' => 'amber', default => '' }; @endphp @section('title', 'Payment plan '.$plan->code) @section('subtitle', $plan->customer_name.' · '.($course?->title ?? 'Course').($plan->enrollment ? ' · '.$plan->enrollment->batch->batch_code : '')) @section('actions') All plans Send details @if($plan->status !== 'cancelled') Edit plan@endif @endsection @section('content')
@if($plan->status === 'cancelled')
This plan is cancelled. Payments already received stay on record; unpaid installments were cancelled.
@elseif($plan->status === 'completed')
Fully paid{{ $plan->completed_at ? ' on '.$plan->completed_at->format('d M Y') : '' }}. The enrollment's financial status is Paid.
@elseif($plan->overdueAmount() > 0)
{{ money2($plan->overdueAmount()) }} overdue. The learner receives an automatic overdue notice; you can also send one from the installment row.
@endif

Fee breakdown

Course fee{{ money2($plan->course_fee) }}
@if((float) $plan->exam_fee > 0)
Exam fee{{ money2($plan->exam_fee) }}
@endif @if((float) $plan->additional_charges > 0)
{{ $plan->additional_label ?: 'Additional charges' }}{{ money2($plan->additional_charges) }}
@endif @if((float) $plan->discount > 0)
Discount ({{ $plan->discount_reason }}{{ $plan->discountAuthorizer ? ' · '.$plan->discountAuthorizer->name : '' }})− {{ money2($plan->discount) }}
@endif @if((float) $plan->tax_amount > 0)
Tax {{ rtrim(rtrim(number_format((float) $plan->tax_percent, 2), '0'), '.') }}%{{ money2($plan->tax_amount) }}
@endif
Paid{{ money2($plan->paidAmount()) }}
@if($plan->waivedAmount() > 0)
Waived{{ money2($plan->waivedAmount()) }}
@endif
Final fee {{ money2($plan->total) }} · remaining{{ money2($plan->remaining()) }}
{{ $plan->progressPercent() }}% paid · status {{ str_replace('_', ' ', ucfirst($fs)) }}
Next installment @if($next)
{{ money2($next->outstanding()) }}
{{ $next->label }} · due {{ $next->due_date->format('d M Y') }} ({{ $next->due_date->diffForHumans() }})
Reminders sent: {{ $next->reminder_count }}{{ $next->last_reminder_at ? ' · last '.$next->last_reminder_at->format('d M, h:i A') : '' }}
@if($open)
@csrf
@endif @else
All clear
No installment is outstanding.
@endif

Installments

{{ ucfirst($plan->frequency) }} · {{ $rows->count() }} parts
@foreach($rows as $x) @endforeach
InstallmentDue dateAmountBalanceStatusActions
{{ $x->label }}@if((float) $x->waived_amount > 0)Waived {{ money2($x->waived_amount) }} — {{ $x->waiver_reason }}{{ $x->waiver ? ' · '.$x->waiver->name : '' }}@endif @if($x->allocations->where('reversed', false)->isNotEmpty())Receipts: {{ $x->allocations->where('reversed', false)->map(fn ($a) => $a->payment->receipt_no)->filter()->unique()->implode(', ') }}@endif {{ $x->due_date->format('d M Y') }}@if($x->original_due_date && ! $x->original_due_date->eq($x->due_date))was {{ $x->original_due_date->format('d M Y') }}@endif {{ money2($x->amount) }}@if((float) $x->paid_amount > 0)paid {{ money2($x->paid_amount) }}@endif {{ money2($x->outstanding()) }}
@if($open && $x->outstanding() > 0)
@csrf
@else—@endif
Total{{ money2($rows->sum('amount')) }}paid {{ money2($rows->sum('paid_amount')) }}{{ money2($plan->remaining()) }}
@if($inv)
@forelse($inv->payments->sortByDesc('id') as $p)
{{ $p->receipt_no ?: $p->reference }}
{{ ($p->paid_at ?? $p->created_at)->format('d M Y') }} · {{ $methods[$p->method] ?? ucfirst(str_replace('_', ' ', (string) $p->method)) }}@if($p->transaction_ref) · Ref {{ $p->transaction_ref }}@endif · {{ $p->receiver ? 'recorded by '.$p->receiver->name : 'submitted by learner' }}
{{ money2($p->amount) }}
@if($p->status === 'paid') Receipt
@csrf
@elseif($p->status === 'pending') Review proof @endif
@empty
No payments yet.
@endforelse
@endif

Financial audit trail

immutable
    @forelse($adjustments as $a)
  • {{ $a->typeLabel() }}@if($a->amount !== null) · {{ money2($a->amount) }}@endif{{ $a->created_at->format('d M Y, h:i A') }}
    @if($a->amount_before !== null || $a->amount_after !== null){{ $a->amount_before !== null ? money2($a->amount_before) : '—' }} → {{ $a->amount_after !== null ? money2($a->amount_after) : '—' }} · @endif{{ $a->reason }}@if($a->meta['from'] ?? null) ({{ $a->meta['from'] }} → {{ $a->meta['to'] }})@endif
    by {{ $a->creator?->name ?? 'System' }}@if($a->authorizer && $a->authorizer->id !== $a->creator?->id) · authorised by {{ $a->authorizer->name }}@endif
  • @empty
  • No changes recorded yet.
  • @endforelse

Automation & emails

    @foreach($runs->take(12) as $r)
  • {{ \App\Services\AutomationCatalog::events()[$r->event][0] ?? $r->event }}{{ $r->created_at->format('d M, h:i A') }}
    {{ $r->message }}
  • @endforeach @foreach($emails->take(12) as $m)
  • {{ $m->subject }}{{ $m->created_at->format('d M, h:i A') }}
    {{ $m->to_email }} · {{ $m->status }}@if($m->status === 'failed') · retry@endif
  • @endforeach @if($runs->isEmpty() && $emails->isEmpty())
  • Nothing yet.
  • @endif
@foreach($rows->filter(fn ($x) => $x->outstanding() > 0) as $x) @endforeach @if($open) @endif @endsection