Commit 435aeddb authored by Mahmoud Aglan's avatar Mahmoud Aglan

Fix subscription payment status: only check program fee, not all invoices

The group view was marking players as "unpaid" if they had ANY unpaid
invoice (products, etc.), even if their program subscription was fully paid.

Now the logic only checks:
1. Enrollment payment_status (paid/waived = done)
2. Enrollment's linked invoice status
3. Recent subscription invoice with matching program name

Also fixed ActivateEnrollmentOnPayment listener to properly set
payment_status='paid' on enrollments when subscription invoice is paid
(was only activating status, not updating payment_status).
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 521f6d7b
......@@ -14,6 +14,7 @@ public function handle(InvoicePaid $event): void
try {
$invoice = $event->invoice;
// Activate pending enrollments linked to this invoice
$pendingEnrollments = Enrollment::where('invoice_id', $invoice->id)
->where('status', 'pending')
->get();
......@@ -21,9 +22,30 @@ public function handle(InvoicePaid $event): void
foreach ($pendingEnrollments as $enrollment) {
$enrollment->update([
'status' => 'active',
'payment_status' => 'paid',
'activated_at' => now(),
]);
}
// Mark payment_status as paid for active enrollments linked to this invoice
Enrollment::where('invoice_id', $invoice->id)
->where('status', 'active')
->where('payment_status', '!=', 'paid')
->update(['payment_status' => 'paid']);
// Handle renewal invoices: match by participant + subscription description
if ($invoice->billable_type === \App\Domain\Participant\Models\Participant::class) {
$hasSubscriptionItem = $invoice->items()
->where('description', 'ilike', '%اشتراك%')
->exists();
if ($hasSubscriptionItem) {
Enrollment::where('participant_id', $invoice->billable_id)
->where('status', 'active')
->whereIn('payment_status', ['pending', 'partial', 'overdue'])
->update(['payment_status' => 'paid']);
}
}
} catch (\Throwable $e) {
Log::error('ActivateEnrollmentOnPayment failed: ' . $e->getMessage(), [
'invoice_id' => $event->invoice->id,
......
......@@ -100,27 +100,58 @@ public function render()
->orderBy('enrollment_date')
->get();
// Payment status per participant: has unpaid/overdue invoices this month?
// Subscription payment status — ONLY checks program subscription, not products
$participantIds = $activeEnrollments->pluck('participant_id')->toArray();
$unpaidParticipantIds = [];
if ($participantIds) {
$unpaidParticipantIds = Invoice::where('billable_type', Participant::class)
->whereIn('billable_id', $participantIds)
->whereIn('status', [InvoiceStatus::Sent, InvoiceStatus::Overdue, InvoiceStatus::PartiallyPaid])
->where('due_amount', '>', 0)
$notPaidParticipantIds = [];
$programName = $this->group->program?->name_ar ?? '';
foreach ($activeEnrollments as $enrollment) {
if ($enrollment->participant?->is_free) {
continue;
}
$paymentStatus = $enrollment->payment_status?->value ?? $enrollment->payment_status;
// Enrollment explicitly marked as paid or waived
if (in_array($paymentStatus, ['paid', 'waived'])) {
continue;
}
// Check if the enrollment's linked invoice is paid
if ($enrollment->invoice_id) {
$invoiceStatus = Invoice::where('id', $enrollment->invoice_id)
->whereNull('deleted_at')
->value('status');
$paidStatuses = [InvoiceStatus::Paid, InvoiceStatus::Overpaid];
if ($invoiceStatus && in_array($invoiceStatus, $paidStatuses)) {
continue;
}
}
// Check if there's a paid renewal/subscription invoice for current period
$hasPaidSubscriptionInvoice = Invoice::where('billable_type', Participant::class)
->where('billable_id', $enrollment->participant_id)
->whereIn('status', [InvoiceStatus::Paid, InvoiceStatus::Overpaid])
->whereNull('deleted_at')
->pluck('billable_id')
->unique()
->toArray();
}
->when($enrollment->last_billed_at, fn ($q) => $q->where('issue_date', '>=', $enrollment->last_billed_at))
->whereHas('items', function ($q) use ($programName) {
$q->where('description', 'ilike', '%اشتراك%');
if ($programName) {
$q->where('description', 'ilike', "%{$programName}%");
}
})
->exists();
// Also flag enrollments where next_billing_date is past and payment_status != paid
$overdueEnrollmentParticipantIds = $activeEnrollments
->filter(fn ($e) => $e->next_billing_date && $e->next_billing_date->isPast() && ($e->payment_status?->value ?? $e->payment_status) !== 'paid')
->pluck('participant_id')
->toArray();
if ($hasPaidSubscriptionInvoice) {
continue;
}
$notPaidParticipantIds[] = $enrollment->participant_id;
}
$notPaidParticipantIds = array_unique(array_merge($unpaidParticipantIds, $overdueEnrollmentParticipantIds));
$notPaidParticipantIds = array_unique($notPaidParticipantIds);
// Payment summary: paid, unpaid, free
$freePlayerIds = $activeEnrollments
......
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