Commit 28433036 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(members): show subscription status, transfer details, and overdue amounts

1. Subscription badge: shows if active (paid), overdue, or awaiting renewal
2. Overdue table: lists all unpaid subscriptions with amounts needed to renew
3. Transfer origin card: shows who transferred, when, and actual amount paid
4. Financial summary: shows actual paid amount (not original membership_value) for transferred members
5. Payment history link for deceased member (accessible from transferee's page)
6. Redirect /members/{id} to archive page if member is archived
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent f8b4b352
...@@ -180,7 +180,8 @@ class MemberController extends Controller ...@@ -180,7 +180,8 @@ class MemberController extends Controller
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member || $member->is_archived) return $this->redirect('/members')->withError('العضو غير موجود'); if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
if ($member->is_archived) return $this->redirect('/members/archive/' . (int) $id);
$branch = $db->selectOne("SELECT name_ar FROM branches WHERE id = ?", [(int) $member->branch_id]); $branch = $db->selectOne("SELECT name_ar FROM branches WHERE id = ?", [(int) $member->branch_id]);
$qualification = $member->qualification_id ? $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member->qualification_id]) : null; $qualification = $member->qualification_id ? $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member->qualification_id]) : null;
...@@ -286,11 +287,50 @@ class MemberController extends Controller ...@@ -286,11 +287,50 @@ class MemberController extends Controller
$deathTransfer = null; $deathTransfer = null;
if (!empty($member->transferred_from_death_id)) { if (!empty($member->transferred_from_death_id)) {
$deathTransfer = $db->selectOne( $deathTransfer = $db->selectOne(
"SELECT dc.id, dc.member_id as deceased_member_id, m.full_name_ar as deceased_member_name "SELECT dc.id, dc.member_id as deceased_member_id, m.full_name_ar as deceased_member_name,
dc.completed_at as transfer_date
FROM death_cases dc LEFT JOIN members m ON m.id = dc.member_id FROM death_cases dc LEFT JOIN members m ON m.id = dc.member_id
WHERE dc.id = ?", WHERE dc.id = ?",
[(int) $member->transferred_from_death_id] [(int) $member->transferred_from_death_id]
); );
if ($deathTransfer) {
$deathPayment = $db->selectOne(
"SELECT amount, payment_date FROM payments WHERE member_id = ? AND payment_type = 'death_transfer_fee' AND is_voided = 0 ORDER BY id DESC LIMIT 1",
[(int) $id]
);
$deathTransfer['paid_amount'] = $deathPayment['amount'] ?? null;
$deathTransfer['payment_date'] = $deathPayment['payment_date'] ?? null;
}
}
// Subscription status for current FY
$currentMonth = (int) date('n');
$currentYear = (int) date('Y');
$currentFY = $currentMonth >= 7 ? "$currentYear-" . ($currentYear + 1) : ($currentYear - 1) . "-$currentYear";
$subscriptionStatus = $db->selectOne(
"SELECT id, financial_year, status, total_amount, paid_amount, fine_amount
FROM subscriptions WHERE member_id = ? AND financial_year = ? LIMIT 1",
[(int) $id, $currentFY]
);
$overdueSubscriptions = $db->select(
"SELECT financial_year, total_amount, paid_amount, fine_amount, status
FROM subscriptions WHERE member_id = ? AND status IN ('pending','overdue')
ORDER BY financial_year ASC",
[(int) $id]
);
// Transfer fee payment (for separated children / waiver-acquired members)
$transferFeePayment = null;
if (!empty($member->transferred_from_waiver_id)) {
$transferFeePayment = $db->selectOne(
"SELECT amount, payment_date FROM payments WHERE member_id = ? AND payment_type = 'waiver_fee' AND is_voided = 0 ORDER BY id DESC LIMIT 1",
[(int) $id]
);
} elseif (!empty($member->transferred_from_divorce_id)) {
$transferFeePayment = $db->selectOne(
"SELECT amount, payment_date FROM payments WHERE member_id = ? AND payment_type IN ('separation_fee','transfer_fee','child_separation_fee') AND is_voided = 0 ORDER BY id DESC LIMIT 1",
[(int) $id]
);
} }
$instRateData = RuleEngine::get('INSTALLMENT_INTEREST_RATE'); $instRateData = RuleEngine::get('INSTALLMENT_INTEREST_RATE');
...@@ -322,6 +362,9 @@ class MemberController extends Controller ...@@ -322,6 +362,9 @@ class MemberController extends Controller
: null, : null,
'installInterestRate' => (float) ($instRateData['percentage'] ?? 22), 'installInterestRate' => (float) ($instRateData['percentage'] ?? 22),
'installMaxMonths' => (int) ($instMonthsData['months'] ?? 30), 'installMaxMonths' => (int) ($instMonthsData['months'] ?? 30),
'subscriptionStatus' => $subscriptionStatus,
'overdueSubscriptions' => $overdueSubscriptions,
'transferFeePayment' => $transferFeePayment,
]); ]);
} }
......
...@@ -132,11 +132,86 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false)); ...@@ -132,11 +132,86 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
</div> </div>
<div style="text-align:left;"> <div style="text-align:left;">
<span style="display:inline-block;padding:6px 16px;border-radius:20px;font-weight:700;font-size:14px;color:#fff;background:<?= $statusColor ?>;"><?= e($statusLabel) ?></span> <span style="display:inline-block;padding:6px 16px;border-radius:20px;font-weight:700;font-size:14px;color:#fff;background:<?= $statusColor ?>;"><?= e($statusLabel) ?></span>
<?php if ($member->status === 'active'): ?>
<?php if (!empty($overdueSubscriptions)): ?>
<div style="margin-top:8px;"><span style="display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:600;background:#FEF2F2;color:#DC2626;border:1px solid #FECACA;">⚠️ اشتراكات متأخرة</span></div>
<?php elseif (!empty($subscriptionStatus) && $subscriptionStatus['status'] === 'paid'): ?>
<div style="margin-top:8px;"><span style="display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:600;background:#ECFDF5;color:#059669;border:1px solid #A7F3D0;">✓ العضوية سارية</span></div>
<?php else: ?>
<div style="margin-top:8px;"><span style="display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:600;background:#FEF9C3;color:#92400E;border:1px solid #FDE68A;">⏳ في انتظار التجديد</span></div>
<?php endif; ?>
<?php endif; ?>
<div style="margin-top:10px;font-size:12px;color:#9CA3AF;">تاريخ: <?= e(substr($member->membership_origin_date ?? $member->activated_at ?? $member->created_at, 0, 10)) ?></div> <div style="margin-top:10px;font-size:12px;color:#9CA3AF;">تاريخ: <?= e(substr($member->membership_origin_date ?? $member->activated_at ?? $member->created_at, 0, 10)) ?></div>
</div> </div>
</div> </div>
</div> </div>
<!-- ═══════════════════════════════════════════════ -->
<!-- OVERDUE SUBSCRIPTIONS WARNING -->
<!-- ═══════════════════════════════════════════════ -->
<?php if (!empty($overdueSubscriptions)): ?>
<div class="card" style="margin-bottom:20px;padding:20px;background:#FEF2F2;border:1px solid #FECACA;">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
<span style="font-size:20px;">⚠️</span>
<h3 style="margin:0;color:#DC2626;font-size:15px;">اشتراكات متأخرة — يجب السداد للتجديد</h3>
</div>
<table style="width:100%;font-size:13px;border-collapse:collapse;">
<thead><tr style="background:#FEE2E2;"><th style="padding:8px;text-align:right;">السنة المالية</th><th style="padding:8px;text-align:right;">المبلغ</th><th style="padding:8px;text-align:right;">المدفوع</th><th style="padding:8px;text-align:right;">الغرامة</th><th style="padding:8px;text-align:right;">المتبقي</th></tr></thead>
<tbody>
<?php $totalDue = '0.00'; foreach ($overdueSubscriptions as $sub):
$remaining = bcsub(bcadd($sub['total_amount'], $sub['fine_amount'] ?? '0', 2), $sub['paid_amount'] ?? '0', 2);
$totalDue = bcadd($totalDue, $remaining, 2);
?>
<tr>
<td style="padding:6px 8px;font-weight:600;"><?= e($sub['financial_year']) ?></td>
<td style="padding:6px 8px;"><?= number_format((float) $sub['total_amount'], 2) ?></td>
<td style="padding:6px 8px;"><?= number_format((float) ($sub['paid_amount'] ?? 0), 2) ?></td>
<td style="padding:6px 8px;color:#DC2626;"><?= number_format((float) ($sub['fine_amount'] ?? 0), 2) ?></td>
<td style="padding:6px 8px;font-weight:700;color:#DC2626;"><?= number_format((float) $remaining, 2) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot><tr style="border-top:2px solid #DC2626;"><td colspan="4" style="padding:8px;font-weight:700;">إجمالي المطلوب للتجديد</td><td style="padding:8px;font-weight:800;font-size:16px;color:#DC2626;"><?= number_format((float) $totalDue, 2) ?> ج.م</td></tr></tfoot>
</table>
</div>
<?php endif; ?>
<!-- ═══════════════════════════════════════════════ -->
<!-- TRANSFER ORIGIN DETAILS -->
<!-- ═══════════════════════════════════════════════ -->
<?php if (!empty($deathTransfer) || !empty($waiverTransfer) || !empty($divorceTransfer)): ?>
<div class="card" style="margin-bottom:20px;padding:20px;background:#F0F9FF;border:1px solid #BAE6FD;">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
<span style="font-size:18px;">🔄</span>
<h3 style="margin:0;color:#0369A1;font-size:15px;">بيانات انتقال العضوية</h3>
</div>
<table style="width:100%;font-size:13px;border-collapse:collapse;">
<?php if (!empty($deathTransfer)): ?>
<tr><td style="padding:6px 0;color:#6B7280;width:160px;">انتقلت من</td><td style="padding:6px 0;font-weight:600;"><a href="/members/archive/<?= (int) $deathTransfer['deceased_member_id'] ?>" style="color:#2563EB;"><?= e($deathTransfer['deceased_member_name'] ?? '—') ?></a> (وفاة)</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الانتقال</td><td style="padding:6px 0;font-weight:600;"><?= e($member->ownership_transfer_date ?? substr($deathTransfer['transfer_date'] ?? '', 0, 10)) ?></td></tr>
<?php if (!empty($deathTransfer['paid_amount'])): ?>
<tr><td style="padding:6px 0;color:#6B7280;">المبلغ المدفوع فعلياً</td><td style="padding:6px 0;font-weight:700;color:#059669;font-size:15px;"><?= number_format((float) $deathTransfer['paid_amount'], 2) ?> ج.م</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الدفع</td><td style="padding:6px 0;"><?= e($deathTransfer['payment_date'] ?? '—') ?></td></tr>
<?php endif; ?>
<?php elseif (!empty($waiverTransfer)): ?>
<tr><td style="padding:6px 0;color:#6B7280;width:160px;">انتقلت من</td><td style="padding:6px 0;font-weight:600;"><a href="/members/archive/<?= (int) ($waiverTransfer['source_member_id_ref'] ?? 0) ?>" style="color:#7C3AED;"><?= e($waiverTransfer['source_member_name'] ?? $member->waived_from_member_name ?? '—') ?></a> (تنازل)</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الانتقال</td><td style="padding:6px 0;font-weight:600;"><?= e($member->ownership_transfer_date ?? substr($waiverTransfer['waiver_date'] ?? '', 0, 10)) ?></td></tr>
<?php if (!empty($transferFeePayment)): ?>
<tr><td style="padding:6px 0;color:#6B7280;">المبلغ المدفوع فعلياً</td><td style="padding:6px 0;font-weight:700;color:#059669;font-size:15px;"><?= number_format((float) $transferFeePayment['amount'], 2) ?> ج.م</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الدفع</td><td style="padding:6px 0;"><?= e($transferFeePayment['payment_date'] ?? '—') ?></td></tr>
<?php endif; ?>
<?php elseif (!empty($divorceTransfer)): ?>
<tr><td style="padding:6px 0;color:#6B7280;width:160px;">انتقلت من</td><td style="padding:6px 0;font-weight:600;"><a href="/members/<?= (int) $divorceTransfer['original_member_id'] ?>" style="color:#D97706;"><?= e($divorceTransfer['original_member_name'] ?? '—') ?></a> (فصل/طلاق)</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الانتقال</td><td style="padding:6px 0;font-weight:600;"><?= e($member->ownership_transfer_date ?? '—') ?></td></tr>
<?php if (!empty($transferFeePayment)): ?>
<tr><td style="padding:6px 0;color:#6B7280;">المبلغ المدفوع فعلياً</td><td style="padding:6px 0;font-weight:700;color:#059669;font-size:15px;"><?= number_format((float) $transferFeePayment['amount'], 2) ?> ج.م</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الدفع</td><td style="padding:6px 0;"><?= e($transferFeePayment['payment_date'] ?? '—') ?></td></tr>
<?php endif; ?>
<?php endif; ?>
</table>
</div>
<?php endif; ?>
<!-- ═══════════════════════════════════════════════ --> <!-- ═══════════════════════════════════════════════ -->
<!-- PENDING CHEQUES — member paid down payment but hasn't submitted cheques yet --> <!-- PENDING CHEQUES — member paid down payment but hasn't submitted cheques yet -->
<!-- ═══════════════════════════════════════════════ --> <!-- ═══════════════════════════════════════════════ -->
...@@ -695,7 +770,19 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done'])); ...@@ -695,7 +770,19 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done']));
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;font-size:15px;">💰 الملخص المالي</h3></div> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;font-size:15px;">💰 الملخص المالي</h3></div>
<div style="padding:15px 20px;"> <div style="padding:15px 20px;">
<div style="display:grid;grid-template-columns:<?= !empty($specialDiscount) ? '1fr 1fr 1fr' : '1fr 1fr' ?>;gap:15px;"> <div style="display:grid;grid-template-columns:<?= !empty($specialDiscount) ? '1fr 1fr 1fr' : '1fr 1fr' ?>;gap:15px;">
<div style="background:#F0FDF4;padding:15px;border-radius:8px;text-align:center;"><div style="font-size:20px;font-weight:700;color:#059669;"><?= money($member->membership_value ?? '0') ?></div><div style="color:#6B7280;font-size:11px;">قيمة العضوية</div></div> <?php
$isTransferred = !empty($member->transferred_from_death_id) || !empty($member->transferred_from_waiver_id) || !empty($member->transferred_from_divorce_id);
$displayValue = $member->membership_value ?? '0';
$valueLabel = 'قيمة العضوية';
if ($isTransferred && !empty($transferFeePayment)) {
$displayValue = $transferFeePayment['amount'];
$valueLabel = 'المبلغ المدفوع عند الانتقال';
} elseif ($isTransferred && !empty($deathTransfer['paid_amount'])) {
$displayValue = $deathTransfer['paid_amount'];
$valueLabel = 'المبلغ المدفوع عند الانتقال';
}
?>
<div style="background:#F0FDF4;padding:15px;border-radius:8px;text-align:center;"><div style="font-size:20px;font-weight:700;color:#059669;"><?= money($displayValue) ?></div><div style="color:#6B7280;font-size:11px;"><?= $valueLabel ?></div></div>
<?php if (!empty($specialDiscount)): ?> <?php if (!empty($specialDiscount)): ?>
<div style="background:#FFF7ED;padding:15px;border-radius:8px;text-align:center;"> <div style="background:#FFF7ED;padding:15px;border-radius:8px;text-align:center;">
<div style="font-size:20px;font-weight:700;color:#D97706;">-<?= money($member->discount_amount ?? '0') ?></div> <div style="font-size:20px;font-weight:700;color:#D97706;">-<?= money($member->discount_amount ?? '0') ?></div>
...@@ -708,6 +795,9 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done'])); ...@@ -708,6 +795,9 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done']));
<div style="background:#EFF6FF;padding:15px;border-radius:8px;text-align:center;"><div style="font-size:20px;font-weight:700;color:#0284C7;"><?= money($bill['total_paid']) ?></div><div style="color:#6B7280;font-size:11px;">إجمالي المدفوع</div></div> <div style="background:#EFF6FF;padding:15px;border-radius:8px;text-align:center;"><div style="font-size:20px;font-weight:700;color:#0284C7;"><?= money($bill['total_paid']) ?></div><div style="color:#6B7280;font-size:11px;">إجمالي المدفوع</div></div>
</div> </div>
<a href="/payments/member/<?= (int) $member->id ?>" class="btn btn-outline" style="width:100%;text-align:center;margin-top:10px;">📜 سجل المدفوعات</a> <a href="/payments/member/<?= (int) $member->id ?>" class="btn btn-outline" style="width:100%;text-align:center;margin-top:10px;">📜 سجل المدفوعات</a>
<?php if (!empty($deathTransfer['deceased_member_id'])): ?>
<a href="/payments/member/<?= (int) $deathTransfer['deceased_member_id'] ?>" class="btn btn-outline" style="width:100%;text-align:center;margin-top:6px;color:#6B7280;border-color:#D1D5DB;">📋 سجل مدفوعات العضو المتوفي (<?= e($deathTransfer['deceased_member_name'] ?? '') ?>)</a>
<?php endif; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;font-size:15px;">💰 الملخص المالي</h3></div> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;font-size:15px;">💰 الملخص المالي</h3></div>
......
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