Commit 98d2b625 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Add consolidated platform fee for retroactive enrollments

Platform fees for ALL retroactive months are charged in the current month
(registration date), not backdated. Individual month invoices carry
service_fee_amount=0 with a metadata flag, while a single consolidated
fee invoice is created dated today covering all months.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 59719212
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
use App\Domain\Participant\Models\Participant; use App\Domain\Participant\Models\Participant;
use App\Domain\Pricing\Services\PricingService; use App\Domain\Pricing\Services\PricingService;
use App\Domain\Shared\Exceptions\DomainException; use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Services\PlatformFeeService;
use App\Domain\Shared\Traits\UsesBranchScope; use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\Activity; use App\Domain\Training\Models\Activity;
use App\Domain\Training\Models\TrainingProgram; use App\Domain\Training\Models\TrainingProgram;
...@@ -255,16 +256,15 @@ public function confirm(): void ...@@ -255,16 +256,15 @@ public function confirm(): void
]); ]);
} }
// 3. Generate invoices per month // 3. Generate invoices per month (no platform fee on individual past invoices)
$invoicesCreated = 0; $invoicesCreated = 0;
$paymentsRecorded = 0; $paymentsRecorded = 0;
$totalRetroactiveRevenue = 0;
foreach ($this->monthStatuses as $monthKey => $status) { foreach ($this->monthStatuses as $monthKey => $status) {
$monthDate = Carbon::parse($monthKey . '-01'); $monthDate = Carbon::parse($monthKey . '-01');
$isCurrentMonth = $monthDate->isSameMonth(now());
if ($status === 'paid_outside') { if ($status === 'paid_outside') {
// Create invoice marked as paid (record that payment happened outside)
$invoice = $invoiceService->create([ $invoice = $invoiceService->create([
'academy_id' => $participant->academy_id, 'academy_id' => $participant->academy_id,
'branch_id' => $this->branchId, 'branch_id' => $this->branchId,
...@@ -274,10 +274,11 @@ public function confirm(): void ...@@ -274,10 +274,11 @@ public function confirm(): void
'subtotal_amount' => $perMonth, 'subtotal_amount' => $perMonth,
'discount_amount' => 0, 'discount_amount' => 0,
'tax_amount' => 0, 'tax_amount' => 0,
'service_fee_amount' => 0,
'due_date' => $monthDate->copy()->addDays(7)->toDateString(), 'due_date' => $monthDate->copy()->addDays(7)->toDateString(),
'contact_name' => $participant->person?->name_ar, 'contact_name' => $participant->person?->name_ar,
'notes' => 'اشتراك ' . $program->name_ar . ' — ' . $this->getArabicMonth($monthDate), 'notes' => 'اشتراك ' . $program->name_ar . ' — ' . $this->getArabicMonth($monthDate),
'metadata' => ['retroactive' => true, 'month' => $monthKey], 'metadata' => ['retroactive' => true, 'month' => $monthKey, 'fee_deferred_to_registration_month' => true],
], [ ], [
[ [
'description' => 'اشتراك: ' . $program->name_ar . ' (' . $this->getArabicMonth($monthDate) . ')', 'description' => 'اشتراك: ' . $program->name_ar . ' (' . $this->getArabicMonth($monthDate) . ')',
...@@ -288,7 +289,6 @@ public function confirm(): void ...@@ -288,7 +289,6 @@ public function confirm(): void
], ],
], $actor); ], $actor);
// Record as paid
$invoice->update([ $invoice->update([
'status' => InvoiceStatus::Paid, 'status' => InvoiceStatus::Paid,
'paid_amount' => $perMonth, 'paid_amount' => $perMonth,
...@@ -301,9 +301,9 @@ public function confirm(): void ...@@ -301,9 +301,9 @@ public function confirm(): void
]), ]),
]); ]);
$totalRetroactiveRevenue += $perMonth;
$invoicesCreated++; $invoicesCreated++;
} elseif ($status === 'unpaid') { } elseif ($status === 'unpaid') {
// Create outstanding invoice
$invoice = $invoiceService->create([ $invoice = $invoiceService->create([
'academy_id' => $participant->academy_id, 'academy_id' => $participant->academy_id,
'branch_id' => $this->branchId, 'branch_id' => $this->branchId,
...@@ -313,10 +313,11 @@ public function confirm(): void ...@@ -313,10 +313,11 @@ public function confirm(): void
'subtotal_amount' => $perMonth, 'subtotal_amount' => $perMonth,
'discount_amount' => 0, 'discount_amount' => 0,
'tax_amount' => 0, 'tax_amount' => 0,
'service_fee_amount' => 0,
'due_date' => $monthDate->copy()->addDays(7)->toDateString(), 'due_date' => $monthDate->copy()->addDays(7)->toDateString(),
'contact_name' => $participant->person?->name_ar, 'contact_name' => $participant->person?->name_ar,
'notes' => 'اشتراك ' . $program->name_ar . ' — ' . $this->getArabicMonth($monthDate), 'notes' => 'اشتراك ' . $program->name_ar . ' — ' . $this->getArabicMonth($monthDate),
'metadata' => ['retroactive' => true, 'month' => $monthKey], 'metadata' => ['retroactive' => true, 'month' => $monthKey, 'fee_deferred_to_registration_month' => true],
], [ ], [
[ [
'description' => 'اشتراك: ' . $program->name_ar . ' (' . $this->getArabicMonth($monthDate) . ')', 'description' => 'اشتراك: ' . $program->name_ar . ' (' . $this->getArabicMonth($monthDate) . ')',
...@@ -328,9 +329,9 @@ public function confirm(): void ...@@ -328,9 +329,9 @@ public function confirm(): void
], $actor); ], $actor);
$invoice->update(['status' => InvoiceStatus::Sent]); $invoice->update(['status' => InvoiceStatus::Sent]);
$totalRetroactiveRevenue += $perMonth;
$invoicesCreated++; $invoicesCreated++;
// Pay now if selected
if ($this->pay_now && $perMonth > 0) { if ($this->pay_now && $perMonth > 0) {
$paymentService->recordPayment([ $paymentService->recordPayment([
'invoice_id' => $invoice->id, 'invoice_id' => $invoice->id,
...@@ -347,7 +348,49 @@ public function confirm(): void ...@@ -347,7 +348,49 @@ public function confirm(): void
} }
} }
// 4. Set next_billing_date to 1st of next month // 4. Platform fee invoice — charged TODAY for ALL retroactive months
$platformFeeService = app(PlatformFeeService::class);
if ($platformFeeService->isActive() && $totalRetroactiveRevenue > 0) {
$totalPlatformFee = $platformFeeService->calculate($totalRetroactiveRevenue);
if ($totalPlatformFee > 0) {
$monthCount = count($this->monthStatuses);
$feeInvoice = $invoiceService->create([
'academy_id' => $participant->academy_id,
'branch_id' => $this->branchId,
'billable_type' => $participant->getMorphClass(),
'billable_id' => $participant->id,
'total_amount' => $totalPlatformFee,
'subtotal_amount' => 0,
'discount_amount' => 0,
'tax_amount' => 0,
'service_fee_amount' => $totalPlatformFee,
'due_date' => now()->addDays(7)->toDateString(),
'contact_name' => $participant->person?->name_ar,
'notes' => 'رسوم خدمة — تسجيل بأثر رجعي (' . $monthCount . ' شهور) — ' . $program->name_ar,
'metadata' => [
'retroactive_platform_fee' => true,
'covers_months' => array_keys($this->monthStatuses),
'total_revenue_base' => $totalRetroactiveRevenue,
'fee_percentage' => $platformFeeService->getPercentage(),
'registered_at' => now()->toIso8601String(),
],
], [
[
'description' => 'رسوم خدمة المنصة — تسجيل بأثر رجعي (' . $monthCount . ' شهور)',
'quantity' => 1,
'unit_price' => $totalPlatformFee,
'discount_amount' => 0,
'tax_amount' => 0,
],
], $actor);
$feeInvoice->update(['status' => InvoiceStatus::Sent]);
$invoicesCreated++;
}
}
// 5. Set next_billing_date to 1st of next month
$enrollment->update([ $enrollment->update([
'next_billing_date' => now()->addMonth()->startOfMonth()->toDateString(), 'next_billing_date' => now()->addMonth()->startOfMonth()->toDateString(),
'last_billed_at' => now()->toDateString(), 'last_billed_at' => now()->toDateString(),
......
...@@ -355,6 +355,14 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-indigo-600 text-whit ...@@ -355,6 +355,14 @@ class="inline-flex items-center gap-2 px-6 py-3 min-h-16 bg-indigo-600 text-whit
<span class="text-lg font-bold text-gray-800">{{ __('إجمالي المطلوب') }}</span> <span class="text-lg font-bold text-gray-800">{{ __('إجمالي المطلوب') }}</span>
<span class="text-2xl font-bold text-gray-800" dir="ltr">{{ number_format($this->totalOwed / 100, 2) }} {{ __('ج.م') }}</span> <span class="text-2xl font-bold text-gray-800" dir="ltr">{{ number_format($this->totalOwed / 100, 2) }} {{ __('ج.م') }}</span>
</div> </div>
@php $platformFee = app(\App\Domain\Shared\Services\PlatformFeeService::class); @endphp
@if($platformFee->isActive())
<div class="border-t border-gray-200 pt-3 flex items-center justify-between text-sm">
<span class="text-gray-500">{{ __('رسوم خدمة المنصة') }} ({{ $platformFee->getPercentage() }}%)</span>
<span class="font-medium text-gray-600" dir="ltr">{{ number_format($platformFee->calculate(count($monthStatuses) * $monthlyPrice) / 100, 2) }} {{ __('ج.م') }}</span>
</div>
<p class="text-xs text-gray-400 mt-1">{{ __('تُحسب على كل الشهور وتُسجَّل في الشهر الحالي') }}</p>
@endif
</div> </div>
{{-- Pay Now? --}} {{-- Pay Now? --}}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment