Commit 4b76c4b3 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Enhance billing display with full calculation breakdowns for all membership types

- Foreign bill: branch-specific fee lookup, exchange rate conversion, EGP display
- Seasonal bill: duration, nationality, dates, base amount, discounts, VAT, family members
- Sports bill: improved breakdown with separator and total line
- Form fee: add in_queue status to bill item
- Controller: type-specific payment types, validation whitelist, descriptions
- View: conditional installment option, working-only family links, type-specific labels
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 39382feb
...@@ -217,7 +217,10 @@ class MemberController extends Controller ...@@ -217,7 +217,10 @@ class MemberController extends Controller
\App\Modules\Members\Services\MembershipPaymentGuard::reconcile((int) $id); \App\Modules\Members\Services\MembershipPaymentGuard::reconcile((int) $id);
$bill = BillingService::getMemberBill((int) $id); $bill = BillingService::getMemberBill((int) $id);
$formFilled = ($member->qualification_id !== null && $member->qualification_id > 0); $membershipType = $member->membership_type ?? 'working';
$formFilled = ($membershipType !== 'working')
? true
: ($member->qualification_id !== null && $member->qualification_id > 0);
$specialDiscount = null; $specialDiscount = null;
if ($member->special_discount_id) { if ($member->special_discount_id) {
...@@ -357,6 +360,11 @@ class MemberController extends Controller ...@@ -357,6 +360,11 @@ class MemberController extends Controller
$paymentType = trim((string) $request->post('payment_type', '')); $paymentType = trim((string) $request->post('payment_type', ''));
$amount = trim((string) $request->post('amount', '0')); $amount = trim((string) $request->post('amount', '0'));
$validPaymentTypes = ['membership_fee', 'down_payment', 'foreign_membership_fee', 'sports_membership_fee', 'seasonal_fee'];
if (!in_array($paymentType, $validPaymentTypes, true)) {
return $this->redirect('/members/' . $id)->withError('نوع الدفع غير صالح');
}
if (bccomp($amount, '0.01', 2) < 0) return $this->redirect('/members/' . $id)->withError('المبلغ غير صالح'); if (bccomp($amount, '0.01', 2) < 0) return $this->redirect('/members/' . $id)->withError('المبلغ غير صالح');
$months = ($paymentType === 'down_payment') ? min(30, max(1, (int) $request->post('installment_months', 30))) : null; $months = ($paymentType === 'down_payment') ? min(30, max(1, (int) $request->post('installment_months', 30))) : null;
...@@ -387,13 +395,22 @@ class MemberController extends Controller ...@@ -387,13 +395,22 @@ class MemberController extends Controller
$notesData = ['fee_breakdown' => $breakdown]; $notesData = ['fee_breakdown' => $breakdown];
if ($months) $notesData['installment_months'] = $months; if ($months) $notesData['installment_months'] = $months;
$typeDescriptions = [
'down_payment' => 'مقدم تقسيط',
'membership_fee' => 'قيمة العضوية',
'foreign_membership_fee' => 'رسوم عضوية أجنبية',
'sports_membership_fee' => 'رسوم عضوية رياضية',
'seasonal_fee' => 'رسوم عضوية موسمية',
];
$descriptionAr = ($typeDescriptions[$paymentType] ?? 'قيمة العضوية') . ' — استمارة ' . ($member->form_number ?? '');
$result = PaymentRequestService::createRequest([ $result = PaymentRequestService::createRequest([
'member_id' => (int) $id, 'member_id' => (int) $id,
'amount' => $amount, 'amount' => $amount,
'payment_type' => $paymentType, 'payment_type' => $paymentType,
'related_entity_type' => 'members', 'related_entity_type' => 'members',
'related_entity_id' => (int) $id, 'related_entity_id' => (int) $id,
'description_ar' => ($paymentType === 'down_payment' ? 'مقدم تقسيط' : 'قيمة العضوية') . ' — استمارة ' . ($member->form_number ?? ''), 'description_ar' => $descriptionAr,
'notes' => json_encode($notesData, JSON_UNESCAPED_UNICODE), 'notes' => json_encode($notesData, JSON_UNESCAPED_UNICODE),
]); ]);
if (!$result['success']) return $this->redirect('/members/' . $id)->withError($result['error']); if (!$result['success']) return $this->redirect('/members/' . $id)->withError($result['error']);
...@@ -416,7 +433,7 @@ class MemberController extends Controller ...@@ -416,7 +433,7 @@ class MemberController extends Controller
// Block if a combined membership payment is already pending // Block if a combined membership payment is already pending
$hasCombined = $db->selectOne( $hasCombined = $db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1", "SELECT id FROM payment_requests WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment','foreign_membership_fee','sports_membership_fee','seasonal_fee') AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1",
[(int) $id] [(int) $id]
); );
if ($hasCombined) { if ($hasCombined) {
......
...@@ -60,8 +60,40 @@ final class BillingService ...@@ -60,8 +60,40 @@ final class BillingService
$formFeeData = self::getFormFeeItem($db, $memberId); $formFeeData = self::getFormFeeItem($db, $memberId);
$items[] = $formFeeData['item']; $items[] = $formFeeData['item'];
$foreignRules = MembershipRulesService::getForeignMemberRules(); $branchCode = 'sheraton';
$feeUsd = $foreignRules['fee_usd'] ?? '10000'; if (!empty($member['branch_id'])) {
$branch = $db->selectOne("SELECT branch_code FROM branches WHERE id = ?", [(int) $member['branch_id']]);
$branchCode = $branch['branch_code'] ?? 'sheraton';
}
$feeRuleCode = $branchCode === 'new_capital' ? 'FOREIGN_MEMBER_FEE_CAPITAL' : 'FOREIGN_MEMBER_FEE_SHERATON';
$feeData = RuleEngine::get($feeRuleCode);
$feeUsd = $feeData['amount_usd'] ?? '10000.00';
$foreignDetail = $db->selectOne(
"SELECT fee_amount_usd, exchange_rate, fee_amount_egp FROM foreign_member_details WHERE member_id = ? AND is_archived = 0 ORDER BY id DESC LIMIT 1",
[$memberId]
);
$displayAmount = $feeUsd;
$breakdown = [];
$breakdown[] = 'رسوم العضوية الأجنبية: $' . number_format((float) $feeUsd, 2) . ' USD';
$breakdown[] = 'الفرع: ' . ($branchCode === 'new_capital' ? 'العاصمة الجديدة' : 'شيراتون');
if ($foreignDetail) {
if (!empty($foreignDetail['exchange_rate'])) {
$egpAmount = $foreignDetail['fee_amount_egp'] ?? bcmul($feeUsd, $foreignDetail['exchange_rate'], 2);
$breakdown[] = '═══════════════════════════';
$breakdown[] = 'سعر الصرف: ' . number_format((float) $foreignDetail['exchange_rate'], 4) . ' ج.م/دولار';
$breakdown[] = '💵 المبلغ بالجنيه: ' . number_format((float) $egpAmount, 2) . ' ج.م';
$displayAmount = $egpAmount;
}
} else {
$breakdown[] = '═══════════════════════════';
$breakdown[] = 'سيتم تحديد سعر الصرف عند التسجيل';
}
$breakdown[] = '═══════════════════════════';
$breakdown[] = 'شاملة الزوج/ة و 3 أبناء';
$membershipPaid = false; $membershipPaid = false;
$membershipPending = false; $membershipPending = false;
...@@ -90,14 +122,15 @@ final class BillingService ...@@ -90,14 +122,15 @@ final class BillingService
$items[] = [ $items[] = [
'type' => 'membership_fee', 'type' => 'membership_fee',
'label' => 'رسوم عضوية أجنبية ($' . number_format((float) $feeUsd) . ' USD)', 'label' => 'رسوم عضوية أجنبية ($' . number_format((float) $feeUsd) . ' USD)',
'amount' => $feeUsd, 'amount' => $displayAmount,
'paid' => $membershipPaid, 'paid' => $membershipPaid,
'in_queue' => $membershipPending,
'included' => false, 'included' => false,
'category' => 'required', 'category' => 'required',
'breakdown' => ['رسوم العضوية الأجنبية: $' . number_format((float) $feeUsd) . ' USD', 'شاملة الزوج/ة و 3 أبناء'], 'breakdown' => $breakdown,
]; ];
return self::buildBillResult($items, $feeUsd, $formFeeData['paid'], $formFeeData['pending'], $membershipPaid, $membershipPending); return self::buildBillResult($items, $displayAmount, $formFeeData['paid'], $formFeeData['pending'], $membershipPaid, $membershipPending);
} }
private static function getSeasonalBill(array $member, int $memberId): array private static function getSeasonalBill(array $member, int $memberId): array
...@@ -122,19 +155,96 @@ final class BillingService ...@@ -122,19 +155,96 @@ final class BillingService
[$memberId] [$memberId]
); );
$seasonalPending = ($req !== null); $seasonalPending = ($req !== null);
if (!$seasonalPending) {
$completedReq = $db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'seasonal_fee' AND is_voided = 0 AND status = 'completed' LIMIT 1",
[$memberId]
);
$seasonalPaid = ($completedReq !== null);
}
} }
$durationLabel = $seasonalRecord ? ($seasonalRecord['duration_months'] ?? '—') . ' شهر' : '—'; $durationLabel = $seasonalRecord ? ($seasonalRecord['duration_months'] ?? '—') . ' شهر' : '—';
$nationalityLabel = match ($seasonalRecord['nationality_type'] ?? 'egyptian') {
'foreign' => 'أجنبي',
default => 'مصري',
};
$breakdown = [];
if ($seasonalRecord) {
$breakdown[] = 'المدة: ' . ($seasonalRecord['duration_months'] ?? '—') . ' شهر';
$breakdown[] = 'الجنسية: ' . $nationalityLabel;
if (!empty($seasonalRecord['start_date']) && !empty($seasonalRecord['end_date'])) {
$breakdown[] = 'من: ' . $seasonalRecord['start_date'] . ' إلى: ' . $seasonalRecord['end_date'];
}
$breakdown[] = '═══════════════════════════';
$baseAmount = $seasonalRecord['fee_amount'] ?? '0.00';
$breakdown[] = 'الرسوم الأساسية: ' . number_format((float) $baseAmount, 2) . ' ج.م';
if (!empty($seasonalRecord['discount_amount']) && bccomp($seasonalRecord['discount_amount'], '0', 2) > 0) {
$breakdown[] = 'خصم: -' . number_format((float) $seasonalRecord['discount_amount'], 2) . ' ج.م';
}
if (!empty($seasonalRecord['vat_amount']) && bccomp($seasonalRecord['vat_amount'], '0', 2) > 0) {
$breakdown[] = 'ضريبة قيمة مضافة: ' . number_format((float) $seasonalRecord['vat_amount'], 2) . ' ج.م';
}
$breakdown[] = '💵 الإجمالي: ' . number_format((float) $seasonalFee, 2) . ' ج.م';
$familyMembers = $db->select(
"SELECT person_type, person_name, total_amount FROM seasonal_memberships WHERE member_id = ? AND person_type != 'member' AND is_archived = 0 AND id > ?",
[$memberId, (int) $seasonalRecord['id']]
);
if (!empty($familyMembers)) {
$breakdown[] = '═══════════════════════════';
$breakdown[] = 'أفراد الأسرة المشمولين:';
foreach ($familyMembers as $fm) {
$pType = match ($fm['person_type']) {
'spouse' => 'زوج/ة',
'child' => 'ابن/ة',
default => $fm['person_type'],
};
$breakdown[] = ' • ' . $pType . ': ' . $fm['person_name'] . ' — ' . number_format((float) $fm['total_amount'], 2) . ' ج.م';
}
}
}
$items[] = [ $items[] = [
'type' => 'membership_fee', 'type' => 'membership_fee',
'label' => 'رسوم عضوية موسمية (' . $durationLabel . ')', 'label' => 'رسوم عضوية موسمية (' . $durationLabel . ' — ' . $nationalityLabel . ')',
'amount' => $seasonalFee, 'amount' => $seasonalFee,
'paid' => $seasonalPaid, 'paid' => $seasonalPaid,
'in_queue' => $seasonalPending,
'included' => false, 'included' => false,
'category' => 'required', 'category' => 'required',
'breakdown' => !empty($breakdown) ? $breakdown : null,
]; ];
return self::buildBillResult($items, $seasonalFee, $formFeeData['paid'], $formFeeData['pending'], $seasonalPaid, $seasonalPending); $totalSeasonalFamily = '0.00';
if ($seasonalRecord) {
$familyRows = $db->select(
"SELECT person_type, person_name, total_amount, status FROM seasonal_memberships WHERE member_id = ? AND person_type != 'member' AND is_archived = 0 ORDER BY id",
[$memberId]
);
foreach ($familyRows as $fr) {
$frPaid = ($fr['status'] === 'active');
$frAmount = $fr['total_amount'] ?? '0.00';
$totalSeasonalFamily = bcadd($totalSeasonalFamily, $frAmount, 2);
$pLabel = match ($fr['person_type']) {
'spouse' => 'زوج/ة',
'child' => 'ابن/ة',
default => $fr['person_type'],
};
$items[] = [
'type' => 'seasonal_family',
'label' => 'عضوية موسمية — ' . $pLabel . ': ' . $fr['person_name'],
'amount' => $frAmount,
'paid' => $frPaid,
'included' => false,
'category' => 'addition',
];
}
}
$totalFee = bcadd($seasonalFee, $totalSeasonalFamily, 2);
return self::buildBillResult($items, $totalFee, $formFeeData['paid'], $formFeeData['pending'], $seasonalPaid, $seasonalPending);
} }
private static function getSportsBill(array $member, int $memberId): array private static function getSportsBill(array $member, int $memberId): array
...@@ -189,9 +299,10 @@ final class BillingService ...@@ -189,9 +299,10 @@ final class BillingService
'label' => 'رسوم عضوية رياضية (' . $pct . '% من قيمة العضوية)', 'label' => 'رسوم عضوية رياضية (' . $pct . '% من قيمة العضوية)',
'amount' => $sportsFee, 'amount' => $sportsFee,
'paid' => $membershipPaid, 'paid' => $membershipPaid,
'in_queue' => $membershipPending,
'included' => false, 'included' => false,
'category' => 'required', 'category' => 'required',
'breakdown' => ['قيمة العضوية الأساسية: ' . number_format((float) $membershipValue, 2) . ' ج.م', 'نسبة التحويل: ' . $pct . '%', 'رسوم التحويل: ' . number_format((float) $sportsFee, 2) . ' ج.م'], 'breakdown' => ['قيمة العضوية الأساسية: ' . number_format((float) $membershipValue, 2) . ' ج.م', 'نسبة التحويل: ' . $pct . '%', '═══════════════════════════', '💵 رسوم التحويل: ' . number_format((float) $sportsFee, 2) . ' ج.م'],
]; ];
return self::buildBillResult($items, $sportsFee, $formFeeData['paid'], $formFeeData['pending'], $membershipPaid, $membershipPending); return self::buildBillResult($items, $sportsFee, $formFeeData['paid'], $formFeeData['pending'], $membershipPaid, $membershipPending);
...@@ -578,6 +689,7 @@ final class BillingService ...@@ -578,6 +689,7 @@ final class BillingService
'label' => 'رسوم استمارة عضوية', 'label' => 'رسوم استمارة عضوية',
'amount' => $formFeeAmount, 'amount' => $formFeeAmount,
'paid' => $formFeePaid, 'paid' => $formFeePaid,
'in_queue' => $formFeePending,
'included' => false, 'included' => false,
'category' => 'required', 'category' => 'required',
'breakdown' => ['رسوم الاستمارة: 500.00 ج.م', 'طابع شهداء: 5.00 ج.م'], 'breakdown' => ['رسوم الاستمارة: 500.00 ج.م', 'طابع شهداء: 5.00 ج.م'],
......
...@@ -83,18 +83,20 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false)); ...@@ -83,18 +83,20 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<div class="card" style="margin-bottom:20px;padding:25px;background:#F0FDF4;border:2px solid #059669;"> <div class="card" style="margin-bottom:20px;padding:25px;background:#F0FDF4;border:2px solid #059669;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;"> <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<div> <div>
<h3 style="color:#059669;margin:0 0 8px;">✅ تم دفع رسوم الاستمارة — الآن قم بملء البيانات وإضافة الأسرة</h3> <h3 style="color:#059669;margin:0 0 8px;">✅ تم دفع رسوم الاستمارة — الآن قم بملء البيانات<?= ($member->membership_type ?? 'working') === 'working' ? ' وإضافة الأسرة' : '' ?></h3>
<p style="color:#6B7280;margin:0;font-size:14px;">استمارة رقم <strong><?= e($member->form_number) ?></strong> — يمكنك إضافة أفراد الأسرة الآن بدون رسوم إضافية</p> <p style="color:#6B7280;margin:0;font-size:14px;">استمارة رقم <strong><?= e($member->form_number) ?></strong><?= ($member->membership_type ?? 'working') === 'working' ? ' — يمكنك إضافة أفراد الأسرة الآن بدون رسوم إضافية' : '' ?></p>
</div> </div>
<?php if (can('member.fill_form')): ?> <?php if (can('member.fill_form')): ?>
<a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary" style="padding:15px 30px;font-size:18px;">📝 ملء الاستمارة</a> <a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary" style="padding:15px 30px;font-size:18px;">📝 ملء الاستمارة</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php if (($member->membership_type ?? 'working') === 'working'): ?>
<div style="display:flex;gap:8px;flex-wrap:wrap;padding-top:12px;border-top:1px solid #A7F3D0;"> <div style="display:flex;gap:8px;flex-wrap:wrap;padding-top:12px;border-top:1px solid #A7F3D0;">
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">💍 إضافة زوج/ة</a> <a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">💍 إضافة زوج/ة</a>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">👶 إضافة ابن/ة</a> <a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">👶 إضافة ابن/ة</a>
<a href="/members/<?= (int) $member->id ?>/temporary/create" class="btn btn-sm btn-outline">👤 إضافة عضو مؤقت</a> <a href="/members/<?= (int) $member->id ?>/temporary/create" class="btn btn-sm btn-outline">👤 إضافة عضو مؤقت</a>
</div> </div>
<?php endif; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
...@@ -228,13 +230,15 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false)); ...@@ -228,13 +230,15 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
</table> </table>
</div> </div>
<!-- Add Family Before Paying --> <!-- Add Family Before Paying (working type only) -->
<?php if (($member->membership_type ?? 'working') === 'working'): ?>
<div style="padding:0 20px 15px;display:flex;gap:10px;flex-wrap:wrap;border-top:1px solid #E5E7EB;padding-top:15px;"> <div style="padding:0 20px 15px;display:flex;gap:10px;flex-wrap:wrap;border-top:1px solid #E5E7EB;padding-top:15px;">
<span style="color:#6B7280;font-size:13px;padding:8px 0;">أضف أفراد الأسرة (بدون رسوم استمارة إضافية):</span> <span style="color:#6B7280;font-size:13px;padding:8px 0;">أضف أفراد الأسرة (بدون رسوم استمارة إضافية):</span>
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">💍 إضافة زوج/ة</a> <a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">💍 إضافة زوج/ة</a>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">👶 إضافة ابن/ابنة</a> <a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">👶 إضافة ابن/ابنة</a>
<a href="/members/<?= (int) $member->id ?>/temporary/create" class="btn btn-sm btn-outline">👤 عضو مؤقت</a> <a href="/members/<?= (int) $member->id ?>/temporary/create" class="btn btn-sm btn-outline">👤 عضو مؤقت</a>
</div> </div>
<?php endif; ?>
<!-- Special Discount Section --> <!-- Special Discount Section -->
<?php if (!empty($availableDiscounts) && in_array($member->status, ['accepted', 'payment_pending']) && empty($pendingMembership)): ?> <?php if (!empty($availableDiscounts) && in_array($member->status, ['accepted', 'payment_pending']) && empty($pendingMembership)): ?>
...@@ -302,9 +306,18 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false)); ...@@ -302,9 +306,18 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<?php
$paymentTypeForType = match ($member->membership_type ?? 'working') {
'foreign' => 'foreign_membership_fee',
'sports' => 'sports_membership_fee',
'seasonal' => 'seasonal_fee',
default => 'membership_fee',
};
$allowInstallment = in_array($member->membership_type ?? 'working', ['working', 'sports'], true);
?>
<div style="padding:20px;background:#FFF7ED;border-top:2px solid #F59E0B;"> <div style="padding:20px;background:#FFF7ED;border-top:2px solid #F59E0B;">
<h4 style="margin:0 0 15px;color:#D97706;">&#x1f4b0; اختر طريقة السداد وأرسل للخزينة</h4> <h4 style="margin:0 0 15px;color:#D97706;">&#x1f4b0; اختر طريقة السداد وأرسل للخزينة</h4>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;"> <div style="display:grid;grid-template-columns:<?= $allowInstallment ? '1fr 1fr' : '1fr' ?>;gap:20px;">
<!-- Cash Full --> <!-- Cash Full -->
<div style="background:#fff;border:2px solid #059669;border-radius:12px;padding:20px;"> <div style="background:#fff;border:2px solid #059669;border-radius:12px;padding:20px;">
<h5 style="margin:0 0 10px;color:#059669;">&#x1f4b5; كاش كامل</h5> <h5 style="margin:0 0 10px;color:#059669;">&#x1f4b5; كاش كامل</h5>
...@@ -312,11 +325,12 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false)); ...@@ -312,11 +325,12 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<div style="font-size:24px;font-weight:700;color:#059669;margin-bottom:15px;"><?= money($bill['total_pending']) ?></div> <div style="font-size:24px;font-weight:700;color:#059669;margin-bottom:15px;"><?= money($bill['total_pending']) ?></div>
<form method="POST" action="/members/<?= (int) $member->id ?>/pay-membership"> <form method="POST" action="/members/<?= (int) $member->id ?>/pay-membership">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="payment_type" value="membership_fee"> <input type="hidden" name="payment_type" value="<?= e($paymentTypeForType) ?>">
<input type="hidden" name="amount" value="<?= e($bill['total_pending']) ?>"> <input type="hidden" name="amount" value="<?= e($bill['total_pending']) ?>">
<button type="submit" class="btn btn-primary" style="width:100%;background:#D97706;border-color:#D97706;" onclick="return confirm('إرسال طلب دفع <?= money($bill['total_pending']) ?> للخزينة؟')">&#x1f4e4; إرسال للخزينة</button> <button type="submit" class="btn btn-primary" style="width:100%;background:#D97706;border-color:#D97706;" onclick="return confirm('إرسال طلب دفع <?= money($bill['total_pending']) ?> للخزينة؟')">&#x1f4e4; إرسال للخزينة</button>
</form> </form>
</div> </div>
<?php if ($allowInstallment): ?>
<!-- Installment --> <!-- Installment -->
<div style="background:#fff;border:2px solid #0284C7;border-radius:12px;padding:20px;"> <div style="background:#fff;border:2px solid #0284C7;border-radius:12px;padding:20px;">
<h5 style="margin:0 0 10px;color:#0284C7;">&#x1f4c5; تقسيط</h5> <h5 style="margin:0 0 10px;color:#0284C7;">&#x1f4c5; تقسيط</h5>
...@@ -348,6 +362,7 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false)); ...@@ -348,6 +362,7 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<button type="submit" class="btn btn-primary" style="width:100%;background:#D97706;border-color:#D97706;" onclick="return confirm('إرسال طلب تقسيط للخزينة؟')">&#x1f4e4; إرسال للخزينة</button> <button type="submit" class="btn btn-primary" style="width:100%;background:#D97706;border-color:#D97706;" onclick="return confirm('إرسال طلب تقسيط للخزينة؟')">&#x1f4e4; إرسال للخزينة</button>
</form> </form>
</div> </div>
<?php endif; ?>
</div> </div>
<div style="margin-top:15px;padding:10px;background:#FEF2F2;border-radius:8px;font-size:12px;color:#DC2626;"> <div style="margin-top:15px;padding:10px;background:#FEF2F2;border-radius:8px;font-size:12px;color:#DC2626;">
&#x26a0;&#xfe0f; مهلة السداد: 15 يوم من تاريخ القبول — بعدها تنتهي الاستمارة &#x26a0;&#xfe0f; مهلة السداد: 15 يوم من تاريخ القبول — بعدها تنتهي الاستمارة
...@@ -357,7 +372,9 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false)); ...@@ -357,7 +372,9 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<?php elseif ($formFilled && in_array($member->status, ['under_review', 'interview_scheduled'])): ?> <?php elseif ($formFilled && in_array($member->status, ['under_review', 'interview_scheduled'])): ?>
<div style="padding:20px;background:#EFF6FF;border-top:2px solid #0284C7;"> <div style="padding:20px;background:#EFF6FF;border-top:2px solid #0284C7;">
<p style="margin:0;color:#0284C7;font-size:14px;">📋 الفاتورة جاهزة — في انتظار قرار مجلس الأمناء قبل السداد</p> <p style="margin:0;color:#0284C7;font-size:14px;">📋 الفاتورة جاهزة — في انتظار قرار مجلس الأمناء قبل السداد</p>
<?php if (($member->membership_type ?? 'working') === 'working'): ?>
<p style="margin:5px 0 0;color:#6B7280;font-size:13px;">يمكنك إضافة أفراد الأسرة الآن وستُضاف رسومهم تلقائياً</p> <p style="margin:5px 0 0;color:#6B7280;font-size:13px;">يمكنك إضافة أفراد الأسرة الآن وستُضاف رسومهم تلقائياً</p>
<?php endif; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
...@@ -492,16 +509,30 @@ if ($bill['form_fee_paid'] && !$formFilled) { ...@@ -492,16 +509,30 @@ if ($bill['form_fee_paid'] && !$formFilled) {
$missingSteps[] = ['icon' => '&#x2705;', 'text' => 'الاستمارة مكتملة', 'color' => '#059669', 'done' => true]; $missingSteps[] = ['icon' => '&#x2705;', 'text' => 'الاستمارة مكتملة', 'color' => '#059669', 'done' => true];
} }
if ($formFilled && !$bill['membership_paid'] && !in_array($member->status, ['active'], true)) { $membershipFeeLabel = match ($member->membership_type ?? 'working') {
'foreign' => 'رسوم العضوية الأجنبية',
'sports' => 'رسوم العضوية الرياضية',
'seasonal' => 'رسوم العضوية الموسمية',
'honorary' => 'تفعيل العضوية الشرفية',
default => 'رسوم العضوية',
};
if (($member->membership_type ?? 'working') === 'honorary') {
if ($member->status !== 'active') {
$missingSteps[] = ['icon' => '&#x1f4cb;', 'text' => 'في انتظار تسجيل بيانات العضوية الشرفية والتفعيل', 'color' => '#3B82F6', 'done' => false];
} else {
$missingSteps[] = ['icon' => '&#x2705;', 'text' => $membershipFeeLabel, 'color' => '#059669', 'done' => true];
}
} elseif ($formFilled && !$bill['membership_paid'] && !in_array($member->status, ['active'], true)) {
if (!empty($bill['membership_pending']) || !empty($pendingMembership)) { if (!empty($bill['membership_pending']) || !empty($pendingMembership)) {
$missingSteps[] = ['icon' => '&#x1f4b3;', 'text' => 'رسوم العضوية في انتظار الخزينة', 'color' => '#D97706', 'done' => false]; $missingSteps[] = ['icon' => '&#x1f4b3;', 'text' => $membershipFeeLabel . ' في انتظار الخزينة', 'color' => '#D97706', 'done' => false];
} elseif (in_array($member->status, ['accepted', 'payment_pending'], true)) { } elseif (in_array($member->status, ['accepted', 'payment_pending'], true)) {
$missingSteps[] = ['icon' => '&#x23f3;', 'text' => 'لم يتم سداد رسوم العضوية', 'color' => '#DC2626', 'done' => false]; $missingSteps[] = ['icon' => '&#x23f3;', 'text' => 'لم يتم سداد ' . $membershipFeeLabel, 'color' => '#DC2626', 'done' => false];
} elseif (in_array($member->status, ['under_review', 'interview_scheduled'], true)) { } elseif (in_array($member->status, ['under_review', 'interview_scheduled'], true)) {
$missingSteps[] = ['icon' => '&#x1f4cb;', 'text' => 'في انتظار قرار مجلس الأمناء', 'color' => '#3B82F6', 'done' => false]; $missingSteps[] = ['icon' => '&#x1f4cb;', 'text' => 'في انتظار قرار مجلس الأمناء', 'color' => '#3B82F6', 'done' => false];
} }
} elseif ($bill['membership_paid'] || $member->status === 'active') { } elseif ($bill['membership_paid'] || $member->status === 'active') {
$missingSteps[] = ['icon' => '&#x2705;', 'text' => 'رسوم العضوية', 'color' => '#059669', 'done' => true]; $missingSteps[] = ['icon' => '&#x2705;', 'text' => $membershipFeeLabel, 'color' => '#059669', 'done' => true];
} }
foreach ($bill['items'] as $item) { foreach ($bill['items'] as $item) {
......
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