Commit f1fb0d5a authored by Mahmoud Aglan's avatar Mahmoud Aglan

dghdfhmk

parent 5b4e277d
...@@ -73,7 +73,10 @@ class CashierController extends Controller ...@@ -73,7 +73,10 @@ class CashierController extends Controller
return $this->redirect('/cashier/' . $id)->withError($result['error']); return $this->redirect('/cashier/' . $id)->withError($result['error']);
} }
return $this->redirect('/cashier')->withSuccess( $receiptId = $result['receipt_id'] ?? null;
$printParam = $receiptId ? '?print_receipt=' . $receiptId : '';
return $this->redirect('/cashier' . $printParam)->withSuccess(
'تم تحصيل الدفعة — إيصال: ' . ($result['receipt_number'] ?? '') 'تم تحصيل الدفعة — إيصال: ' . ($result['receipt_number'] ?? '')
); );
} }
......
...@@ -36,16 +36,19 @@ final class PaymentRequestService ...@@ -36,16 +36,19 @@ final class PaymentRequestService
if ($entityType !== null && $entityId !== null) { if ($entityType !== null && $entityId !== null) {
$existing = $db->selectOne( $existing = $db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = ? AND related_entity_type = ? AND related_entity_id = ? AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1", "SELECT id, status FROM payment_requests WHERE member_id = ? AND payment_type = ? AND related_entity_type = ? AND related_entity_id = ? AND is_voided = 0 AND status IN ('pending','processing','completed') ORDER BY FIELD(status,'pending','processing','completed') LIMIT 1",
[$memberId, $paymentType, $entityType, (int) $entityId] [$memberId, $paymentType, $entityType, (int) $entityId]
); );
} else { } else {
$existing = $db->selectOne( $existing = $db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = ? AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1", "SELECT id, status FROM payment_requests WHERE member_id = ? AND payment_type = ? AND is_voided = 0 AND status IN ('pending','processing') LIMIT 1",
[$memberId, $paymentType] [$memberId, $paymentType]
); );
} }
if ($existing) { if ($existing) {
if ($existing['status'] === 'completed') {
return ['success' => false, 'error' => 'تم دفع هذا البند بالفعل'];
}
return ['success' => false, 'error' => 'يوجد طلب دفع معلق بالفعل لنفس النوع']; return ['success' => false, 'error' => 'يوجد طلب دفع معلق بالفعل لنفس النوع'];
} }
...@@ -147,6 +150,7 @@ final class PaymentRequestService ...@@ -147,6 +150,7 @@ final class PaymentRequestService
return [ return [
'success' => true, 'success' => true,
'payment_id' => $paymentResult['payment_id'], 'payment_id' => $paymentResult['payment_id'],
'receipt_id' => $paymentResult['receipt_id'] ?? null,
'receipt_number' => $paymentResult['receipt_number'] ?? '', 'receipt_number' => $paymentResult['receipt_number'] ?? '',
'request_number' => $request['request_number'], 'request_number' => $request['request_number'],
]; ];
...@@ -221,10 +225,12 @@ final class PaymentRequestService ...@@ -221,10 +225,12 @@ final class PaymentRequestService
return $db->select( return $db->select(
"SELECT pr.*, m.full_name_ar as member_name, m.form_number, m.membership_number, "SELECT pr.*, m.full_name_ar as member_name, m.form_number, m.membership_number,
e.full_name_ar as requested_by_name e.full_name_ar as requested_by_name,
pay.receipt_id
FROM payment_requests pr FROM payment_requests pr
LEFT JOIN members m ON m.id = pr.member_id LEFT JOIN members m ON m.id = pr.member_id
LEFT JOIN employees e ON e.id = pr.requested_by LEFT JOIN employees e ON e.id = pr.requested_by
LEFT JOIN payments pay ON pay.id = pr.payment_id
WHERE {$where} WHERE {$where}
ORDER BY pr.created_at ASC ORDER BY pr.created_at ASC
LIMIT 200", LIMIT 200",
......
...@@ -59,15 +59,15 @@ $statusLabel = match($pr['status']) { ...@@ -59,15 +59,15 @@ $statusLabel = match($pr['status']) {
<?php if (is_array($feeBreakdown) && !empty($feeBreakdown)): ?> <?php if (is_array($feeBreakdown) && !empty($feeBreakdown)): ?>
<tr> <tr>
<td colspan="2" style="padding:12px 0;"> <td colspan="2" style="padding:12px 0;">
<div style="background:#F9FAFB;border:1px solid #E5E7EB;border-radius:8px;padding:12px 16px;"> <div style="background:#FFFBEB;border:2px solid #F59E0B;border-radius:10px;padding:16px 20px;">
<div style="font-weight:700;color:#1A1A2E;margin-bottom:8px;font-size:13px;">تفاصيل المبلغ:</div> <div style="font-weight:700;color:#92400E;margin-bottom:10px;font-size:14px;">📊 تفصيل المبلغ (لماذا هذا الرقم):</div>
<?php foreach ($feeBreakdown as $line): ?> <?php foreach ($feeBreakdown as $line): ?>
<?php if (str_contains($line, '═══')): ?> <?php if (str_contains($line, '═══')): ?>
<hr style="border:0;border-top:1px solid #D1D5DB;margin:6px 0;"> <hr style="border:0;border-top:2px solid #FCD34D;margin:8px 0;">
<?php elseif (str_starts_with($line, '💵')): ?> <?php elseif (str_starts_with($line, '💵')): ?>
<div style="font-weight:700;color:#059669;font-size:14px;margin-top:4px;"><?= e($line) ?></div> <div style="font-weight:700;color:#059669;font-size:16px;margin-top:6px;background:#ECFDF5;padding:6px 10px;border-radius:6px;"><?= e($line) ?></div>
<?php else: ?> <?php else: ?>
<div style="font-size:13px;color:#374151;padding:2px 0;"><?= e($line) ?></div> <div style="font-size:14px;color:#374151;padding:3px 0;"><?= e($line) ?></div>
<?php endif; ?> <?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
......
...@@ -114,18 +114,20 @@ ...@@ -114,18 +114,20 @@
$nd = $r['notes'] ? json_decode($r['notes'], true) : null; $nd = $r['notes'] ? json_decode($r['notes'], true) : null;
$fb = $nd['fee_breakdown'] ?? null; $fb = $nd['fee_breakdown'] ?? null;
if (is_array($fb) && !empty($fb)): if (is_array($fb) && !empty($fb)):
$summary = []; $detailLines = [];
$totalLine = '';
foreach ($fb as $ln) { foreach ($fb as $ln) {
if (str_contains($ln, '═══') || str_starts_with($ln, '💵')) continue; if (str_contains($ln, '═══')) continue;
$summary[] = $ln; if (str_starts_with($ln, '💵')) { $totalLine = $ln; continue; }
$detailLines[] = $ln;
} }
?> ?>
<div style="font-size:11px;font-weight:400;color:#6B7280;margin-top:2px;max-width:220px;line-height:1.4;"> <div style="font-size:11px;font-weight:400;color:#374151;margin-top:4px;max-width:280px;line-height:1.5;text-align:right;direction:rtl;background:#F9FAFB;border:1px solid #E5E7EB;border-radius:6px;padding:6px 8px;">
<?php foreach (array_slice($summary, 0, 3) as $sl): ?> <?php foreach ($detailLines as $dl): ?>
<div><?= e($sl) ?></div> <div style="padding:1px 0;"><?= e($dl) ?></div>
<?php endforeach; ?> <?php endforeach; ?>
<?php if (count($summary) > 3): ?> <?php if ($totalLine): ?>
<div style="color:#9CA3AF;">+<?= count($summary) - 3 ?> بنود أخرى</div> <div style="border-top:1px solid #D1D5DB;margin-top:4px;padding-top:4px;font-weight:700;color:#059669;"><?= e($totalLine) ?></div>
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
...@@ -137,7 +139,10 @@ ...@@ -137,7 +139,10 @@
<?php if ($r['status'] === 'pending' || $r['status'] === 'processing'): ?> <?php if ($r['status'] === 'pending' || $r['status'] === 'processing'): ?>
<a href="/cashier/<?= (int)$r['id'] ?>" class="btn btn-sm btn-primary">تحصيل</a> <a href="/cashier/<?= (int)$r['id'] ?>" class="btn btn-sm btn-primary">تحصيل</a>
<?php elseif ($r['status'] === 'completed'): ?> <?php elseif ($r['status'] === 'completed'): ?>
<span style="color:#059669;font-size:12px;">تم</span> <span style="color:#059669;font-size:12px;">&#x2705; تم</span>
<?php if (!empty($r['receipt_id'])): ?>
<a href="/receipts/<?= (int)$r['receipt_id'] ?>/print" target="_blank" class="btn btn-sm btn-outline" style="font-size:11px;margin-top:4px;">&#x1f5a8; طباعة</a>
<?php endif; ?>
<?php endif; ?> <?php endif; ?>
</td> </td>
</tr> </tr>
...@@ -155,5 +160,15 @@ ...@@ -155,5 +160,15 @@
<script> <script>
setTimeout(function(){ location.reload(); }, 30000); setTimeout(function(){ location.reload(); }, 30000);
(function(){
var params = new URLSearchParams(window.location.search);
var printId = params.get('print_receipt');
if (printId) {
window.open('/receipts/' + printId + '/print', '_blank');
params.delete('print_receipt');
var newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
window.history.replaceState({}, '', newUrl);
}
})();
</script> </script>
<?php $__template->endSection(); ?> <?php $__template->endSection(); ?>
...@@ -36,7 +36,7 @@ class Child extends Model ...@@ -36,7 +36,7 @@ class Child extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$row = $db->selectOne( $row = $db->selectOne(
"SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active'", "SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0",
[$memberId] [$memberId]
); );
return (int) ($row['cnt'] ?? 0); return (int) ($row['cnt'] ?? 0);
...@@ -46,7 +46,7 @@ class Child extends Model ...@@ -46,7 +46,7 @@ class Child extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$row = $db->selectOne( $row = $db->selectOne(
"SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0 AND status = 'active' AND age_years < 18", "SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0 AND age_years < 18",
[$memberId] [$memberId]
); );
return (int) ($row['cnt'] ?? 0); return (int) ($row['cnt'] ?? 0);
......
...@@ -21,7 +21,8 @@ final class ChildFeeCalculator ...@@ -21,7 +21,8 @@ final class ChildFeeCalculator
} }
$membershipValue = $member['membership_value'] ?? '0.00'; $membershipValue = $member['membership_value'] ?? '0.00';
if (bccomp($membershipValue, '0.00', 2) <= 0) { $isOnInitialForm = FormFeeService::isOnInitialForm($member);
if (bccomp($membershipValue, '0.00', 2) <= 0 && !$isOnInitialForm) {
return ['error' => 'قيمة العضوية غير محددة', 'fee' => '0.00', 'classification' => 'included']; return ['error' => 'قيمة العضوية غير محددة', 'fee' => '0.00', 'classification' => 'included'];
} }
......
...@@ -90,6 +90,19 @@ class DeathController extends Controller ...@@ -90,6 +90,19 @@ class DeathController extends Controller
// Send payment to cashier queue // Send payment to cashier queue
if (bccomp($totalFee, '0', 2) > 0) { if (bccomp($totalFee, '0', 2) > 0) {
$deceasedLabel = match ($deceasedType) {
'primary_member' => 'العضو الأصلي',
'spouse' => 'الزوج/ة',
default => $deceasedType,
};
$breakdown = [
'📋 نوع الوفاة: ' . $deceasedLabel,
'📝 رسوم استمارة نقل: ' . money($formFee),
'📅 اشتراك سنوي: ' . money($annualSubBase) . ' + تنمية ' . money($devFee) . ' = ' . money($annualSub),
'═══════════════════════════',
'💵 الإجمالي: ' . money($totalFee),
];
$result = PaymentRequestService::createRequest([ $result = PaymentRequestService::createRequest([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'amount' => $totalFee, 'amount' => $totalFee,
...@@ -97,6 +110,7 @@ class DeathController extends Controller ...@@ -97,6 +110,7 @@ class DeathController extends Controller
'related_entity_type' => 'death_cases', 'related_entity_type' => 'death_cases',
'related_entity_id' => (int) $case->id, 'related_entity_id' => (int) $case->id,
'description_ar' => 'رسوم وفاة (استمارة + اشتراك) — حالة #' . $case->id, 'description_ar' => 'رسوم وفاة (استمارة + اشتراك) — حالة #' . $case->id,
'notes' => json_encode(['fee_breakdown' => $breakdown], JSON_UNESCAPED_UNICODE),
]); ]);
if ($result['success']) { if ($result['success']) {
......
...@@ -75,6 +75,17 @@ class DivorceController extends Controller ...@@ -75,6 +75,17 @@ class DivorceController extends Controller
// Send payment to cashier queue // Send payment to cashier queue
$amount = $feeCalc['total_fee'] ?? '0.00'; $amount = $feeCalc['total_fee'] ?? '0.00';
if (bccomp((string) $amount, '0', 2) > 0) { if (bccomp((string) $amount, '0', 2) > 0) {
$caseTypeLabel = DivorceCase::getCaseTypeLabel($feeCalc['case_type']);
$breakdown = [
'📋 نوع الحالة: ' . $caseTypeLabel,
'💰 قيمة العضوية: ' . money($feeCalc['membership_value']),
'📊 النسبة: ' . $feeCalc['fee_percentage'] . '%',
'💵 رسوم الطلاق: ' . money($feeCalc['fee_amount']),
'📝 رسوم استمارة نقل: ' . money($feeCalc['form_fee']),
'═══════════════════════════',
'💵 الإجمالي: ' . money($amount),
];
$result = PaymentRequestService::createRequest([ $result = PaymentRequestService::createRequest([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'amount' => $amount, 'amount' => $amount,
...@@ -82,6 +93,7 @@ class DivorceController extends Controller ...@@ -82,6 +93,7 @@ class DivorceController extends Controller
'related_entity_type' => 'divorce_cases', 'related_entity_type' => 'divorce_cases',
'related_entity_id' => (int) $case->id, 'related_entity_id' => (int) $case->id,
'description_ar' => 'رسوم طلاق — حالة #' . $case->id, 'description_ar' => 'رسوم طلاق — حالة #' . $case->id,
'notes' => json_encode(['fee_breakdown' => $breakdown], JSON_UNESCAPED_UNICODE),
]); ]);
if ($result['success']) { if ($result['success']) {
......
...@@ -100,6 +100,10 @@ class MemberController extends Controller ...@@ -100,6 +100,10 @@ class MemberController extends Controller
if ($dob) { $age = age_from_dob($dob); $ageYears = $age['years']; $ageMonths = $age['months']; } if ($dob) { $age = age_from_dob($dob); $ageYears = $age['years']; $ageMonths = $age['months']; }
$idType = 'passport'; $idType = 'passport';
} }
if ($ageYears !== null && $ageYears < 21) {
$errors[] = 'الحد الأدنى لسن العضوية العاملة 21 سنة (السن الحالي: ' . $ageYears . ')';
}
if (!empty($errors)) { $session = App::getInstance()->session(); $session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors)); $session->flash('_old_input', $request->all()); return $this->redirect('/members/create'); } if (!empty($errors)) { $session = App::getInstance()->session(); $session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors)); $session->flash('_old_input', $request->all()); return $this->redirect('/members/create'); }
$formNumber = MemberNumberGenerator::next(); $formNumber = MemberNumberGenerator::next();
...@@ -246,6 +250,15 @@ class MemberController extends Controller ...@@ -246,6 +250,15 @@ class MemberController extends Controller
return $this->redirect('/members/' . $id)->withError('بيانات غير صالحة'); return $this->redirect('/members/' . $id)->withError('بيانات غير صالحة');
} }
// Block if a combined membership payment is already pending
$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",
[(int) $id]
);
if ($hasCombined) {
return $this->redirect('/members/' . $id)->withError('يوجد طلب دفع مجمع في الخزينة بالفعل — الرسوم مشمولة فيه');
}
$entity = $db->selectOne("SELECT * FROM {$entityType} WHERE id = ? AND member_id = ? AND is_archived = 0", [$entityId, (int) $id]); $entity = $db->selectOne("SELECT * FROM {$entityType} WHERE id = ? AND member_id = ? AND is_archived = 0", [$entityId, (int) $id]);
if (!$entity) return $this->redirect('/members/' . $id)->withError('العنصر غير موجود'); if (!$entity) return $this->redirect('/members/' . $id)->withError('العنصر غير موجود');
...@@ -260,6 +273,14 @@ class MemberController extends Controller ...@@ -260,6 +273,14 @@ class MemberController extends Controller
default => '', default => '',
}; };
$breakdown = [
'👤 ' . $typeLabel . ': ' . $nameLabel,
'📌 نوع الرسوم: رسوم إضافة تابع',
'💰 رسوم الإضافة: ' . money($fee),
'═══════════════════════════',
'💵 الإجمالي: ' . money($fee),
];
$result = PaymentRequestService::createRequest([ $result = PaymentRequestService::createRequest([
'member_id' => (int) $id, 'member_id' => (int) $id,
'amount' => $fee, 'amount' => $fee,
...@@ -267,6 +288,7 @@ class MemberController extends Controller ...@@ -267,6 +288,7 @@ class MemberController extends Controller
'related_entity_type' => $entityType, 'related_entity_type' => $entityType,
'related_entity_id' => $entityId, 'related_entity_id' => $entityId,
'description_ar' => 'رسوم إضافة ' . $typeLabel . ' — ' . $nameLabel, 'description_ar' => 'رسوم إضافة ' . $typeLabel . ' — ' . $nameLabel,
'notes' => json_encode(['fee_breakdown' => $breakdown], 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']);
......
...@@ -32,6 +32,23 @@ $pendingAdditions ??= []; ...@@ -32,6 +32,23 @@ $pendingAdditions ??= [];
<div style="font-size:20px;font-weight:700;color:#D97706;direction:ltr;"><?= e($pendingFormFee['request_number']) ?></div> <div style="font-size:20px;font-weight:700;color:#D97706;direction:ltr;"><?= e($pendingFormFee['request_number']) ?></div>
<div style="font-size:14px;color:#6B7280;margin-top:5px;">المبلغ: <strong style="color:#059669;"><?= money($pendingFormFee['amount']) ?></strong></div> <div style="font-size:14px;color:#6B7280;margin-top:5px;">المبلغ: <strong style="color:#059669;"><?= money($pendingFormFee['amount']) ?></strong></div>
</div> </div>
<?php
$ffNotes = $pendingFormFee['notes'] ? json_decode($pendingFormFee['notes'], true) : null;
$ffBreakdown = $ffNotes['fee_breakdown'] ?? null;
if (is_array($ffBreakdown) && !empty($ffBreakdown)): ?>
<div style="margin-top:15px;background:#FFFBEB;border:1px solid #FCD34D;border-radius:8px;padding:10px 14px;text-align:right;max-width:350px;display:inline-block;">
<div style="font-weight:700;color:#92400E;margin-bottom:6px;font-size:12px;">📊 تفصيل:</div>
<?php foreach ($ffBreakdown as $ffLine): ?>
<?php if (str_contains($ffLine, '═══')): ?>
<hr style="border:0;border-top:1px solid #FCD34D;margin:4px 0;">
<?php elseif (str_starts_with($ffLine, '💵')): ?>
<div style="font-weight:700;color:#059669;font-size:13px;"><?= e($ffLine) ?></div>
<?php else: ?>
<div style="font-size:12px;color:#374151;padding:1px 0;"><?= e($ffLine) ?></div>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div> </div>
</div> </div>
<?php else: ?> <?php else: ?>
...@@ -58,13 +75,18 @@ $pendingAdditions ??= []; ...@@ -58,13 +75,18 @@ $pendingAdditions ??= [];
<!-- ═══════════════════════════════════════ --> <!-- ═══════════════════════════════════════ -->
<?php elseif ($bill['form_fee_paid'] && !$formFilled && $member->status === 'potential'): ?> <?php elseif ($bill['form_fee_paid'] && !$formFilled && $member->status === 'potential'): ?>
<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;"> <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;">✅ تم دفع رسوم الاستمارة — الآن قم بملء البيانات وإضافة الأسرة</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> — يمكنك إضافة أفراد الأسرة الآن بدون رسوم إضافية</p>
</div> </div>
<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>
</div> </div>
<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 ?>/children/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> </div>
<?php endif; ?> <?php endif; ?>
...@@ -181,9 +203,10 @@ $pendingAdditions ??= []; ...@@ -181,9 +203,10 @@ $pendingAdditions ??= [];
<!-- Add Family Before Paying --> <!-- Add Family Before Paying -->
<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>
</div> </div>
<!-- Payment Section --> <!-- Payment Section -->
...@@ -199,6 +222,23 @@ $pendingAdditions ??= []; ...@@ -199,6 +222,23 @@ $pendingAdditions ??= [];
<div style="font-size:14px;color:#6B7280;margin-top:5px;">المبلغ: <strong style="color:#059669;"><?= money($pendingMembership['amount']) ?></strong></div> <div style="font-size:14px;color:#6B7280;margin-top:5px;">المبلغ: <strong style="color:#059669;"><?= money($pendingMembership['amount']) ?></strong></div>
<div style="font-size:12px;color:#6B7280;margin-top:5px;">النوع: <?= \App\Modules\Cashier\Services\PaymentRequestService::getPaymentTypeLabel($pendingMembership['payment_type']) ?></div> <div style="font-size:12px;color:#6B7280;margin-top:5px;">النوع: <?= \App\Modules\Cashier\Services\PaymentRequestService::getPaymentTypeLabel($pendingMembership['payment_type']) ?></div>
</div> </div>
<?php
$pmNotes = $pendingMembership['notes'] ? json_decode($pendingMembership['notes'], true) : null;
$pmBreakdown = $pmNotes['fee_breakdown'] ?? null;
if (is_array($pmBreakdown) && !empty($pmBreakdown)): ?>
<div style="margin-top:15px;background:#FFFBEB;border:1px solid #FCD34D;border-radius:8px;padding:12px 16px;text-align:right;max-width:400px;display:inline-block;">
<div style="font-weight:700;color:#92400E;margin-bottom:6px;font-size:12px;">📊 تفصيل المبلغ:</div>
<?php foreach ($pmBreakdown as $pmLine): ?>
<?php if (str_contains($pmLine, '═══')): ?>
<hr style="border:0;border-top:1px solid #FCD34D;margin:4px 0;">
<?php elseif (str_starts_with($pmLine, '💵')): ?>
<div style="font-weight:700;color:#059669;font-size:13px;"><?= e($pmLine) ?></div>
<?php else: ?>
<div style="font-size:12px;color:#374151;padding:1px 0;"><?= e($pmLine) ?></div>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<div style="padding:20px;background:#FFF7ED;border-top:2px solid #F59E0B;"> <div style="padding:20px;background:#FFF7ED;border-top:2px solid #F59E0B;">
...@@ -302,7 +342,7 @@ foreach ($bill['items'] as $item) { ...@@ -302,7 +342,7 @@ foreach ($bill['items'] as $item) {
$statusText = $inQueue ? ' — في انتظار الخزينة' : ''; $statusText = $inQueue ? ' — في انتظار الخزينة' : '';
$statusColor = $inQueue ? '#D97706' : '#DC2626'; $statusColor = $inQueue ? '#D97706' : '#DC2626';
$stepEntry = ['icon' => $inQueue ? '&#x1f4b3;' : '&#x26a0;', 'text' => 'لم يتم سداد: ' . $item['label'] . $statusText, 'color' => $statusColor, 'done' => false]; $stepEntry = ['icon' => $inQueue ? '&#x1f4b3;' : '&#x26a0;', 'text' => 'لم يتم سداد: ' . $item['label'] . $statusText, 'color' => $statusColor, 'done' => false];
if (!$inQueue && !empty($item['entity_type']) && !empty($item['entity_id']) && bccomp($item['amount'], '0.01', 2) >= 0) { if (!$inQueue && empty($pendingMembership) && !empty($item['entity_type']) && !empty($item['entity_id']) && bccomp($item['amount'], '0.01', 2) >= 0) {
$stepEntry['entity_type'] = $item['entity_type']; $stepEntry['entity_type'] = $item['entity_type'];
$stepEntry['entity_id'] = $item['entity_id']; $stepEntry['entity_id'] = $item['entity_id'];
$stepEntry['amount'] = $item['amount']; $stepEntry['amount'] = $item['amount'];
...@@ -395,8 +435,8 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done'])); ...@@ -395,8 +435,8 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done']));
</div> </div>
<?php endif; ?> <?php endif; ?>
<!-- Family Management --> <!-- Family Management — available once form fee is paid (during filling) or after -->
<?php if ($formFilled): ?> <?php if ($bill['form_fee_paid'] || $formFilled || $isActive): ?>
<div style="margin-bottom:15px;"> <div style="margin-bottom:15px;">
<div style="font-size:12px;color:#6B7280;font-weight:600;margin-bottom:8px;text-transform:uppercase;">👨‍👩‍👧‍👦 الأسرة</div> <div style="font-size:12px;color:#6B7280;font-weight:600;margin-bottom:8px;text-transform:uppercase;">👨‍👩‍👧‍👦 الأسرة</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;"> <div style="display:flex;gap:8px;flex-wrap:wrap;">
...@@ -431,7 +471,7 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done'])); ...@@ -431,7 +471,7 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done']));
</div> </div>
<!-- Documents & Carnet --> <!-- Documents & Carnet -->
<?php if ($formFilled): ?> <?php if ($bill['form_fee_paid'] || $formFilled || $isActive): ?>
<div style="margin-bottom:15px;"> <div style="margin-bottom:15px;">
<div style="font-size:12px;color:#6B7280;font-weight:600;margin-bottom:8px;text-transform:uppercase;">📁 المستندات والكارنيه</div> <div style="font-size:12px;color:#6B7280;font-weight:600;margin-bottom:8px;text-transform:uppercase;">📁 المستندات والكارنيه</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;"> <div style="display:flex;gap:8px;flex-wrap:wrap;">
...@@ -503,6 +543,8 @@ $categoryLabels = [ ...@@ -503,6 +543,8 @@ $categoryLabels = [
'parent' => 'والد/ة', 'special_needs' => 'ذوي احتياجات خاصة', 'unmarried_daughter' => 'ابنة غير متزوجة', 'parent' => 'والد/ة', 'special_needs' => 'ذوي احتياجات خاصة', 'unmarried_daughter' => 'ابنة غير متزوجة',
'sister' => 'شقيقة', 'stepchild' => 'ابن/ة زوج', 'orphan' => 'يتيم', 'disabled_sibling' => 'شقيق معاق', 'nanny' => 'مربية', 'sister' => 'شقيقة', 'stepchild' => 'ابن/ة زوج', 'orphan' => 'يتيم', 'disabled_sibling' => 'شقيق معاق', 'nanny' => 'مربية',
]; ];
$membershipTypeLabels = ['working' => 'عاملة', 'seasonal' => 'موسمية', 'sports' => 'رياضية', 'honorary' => 'شرفية', 'foreign' => 'أجنبية'];
$membershipTypeLabel = $membershipTypeLabels[$member->membership_type ?? 'working'] ?? 'عاملة';
?> ?>
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;background:linear-gradient(135deg, #F0FDF4, #ECFDF5);border-bottom:2px solid #A7F3D0;display:flex;justify-content:space-between;align-items:center;"> <div style="padding:15px 20px;background:linear-gradient(135deg, #F0FDF4, #ECFDF5);border-bottom:2px solid #A7F3D0;display:flex;justify-content:space-between;align-items:center;">
...@@ -525,11 +567,12 @@ $categoryLabels = [ ...@@ -525,11 +567,12 @@ $categoryLabels = [
<!-- Spouses --> <!-- Spouses -->
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f48d; الزوجات (<?= count($spouses) ?>)</div> <div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f48d; الزوجات (<?= count($spouses) ?>)</div>
<div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>#</th><th>الاسم</th><th>الرقم القومي</th><th>تاريخ الالتحاق</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody> <div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>#</th><th>الاسم</th><th>نوع العضوية</th><th>الرقم القومي</th><th>تاريخ الالتحاق</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody>
<?php foreach ($spouses as $s): ?> <?php foreach ($spouses as $s): ?>
<tr> <tr>
<td><?= (int) $s['spouse_order'] ?></td> <td><?= (int) $s['spouse_order'] ?></td>
<td style="font-weight:600;"><?= e($s['full_name_ar']) ?></td> <td style="font-weight:600;"><?= e($s['full_name_ar']) ?></td>
<td><span style="background:#E0F2FE;color:#0369A1;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;"><?= $membershipTypeLabel ?></span></td>
<td style="direction:ltr;text-align:right;font-size:12px;"><?= e($s['national_id'] ?? '—') ?></td> <td style="direction:ltr;text-align:right;font-size:12px;"><?= e($s['national_id'] ?? '—') ?></td>
<td style="font-size:12px;"><?= $s['join_date'] ? e($s['join_date']) : '<span style="color:#D97706;">لم يُحدد بعد</span>' ?></td> <td style="font-size:12px;"><?= $s['join_date'] ? e($s['join_date']) : '<span style="color:#D97706;">لم يُحدد بعد</span>' ?></td>
<td style="font-weight:600;"><?php <td style="font-weight:600;"><?php
...@@ -548,7 +591,7 @@ $categoryLabels = [ ...@@ -548,7 +591,7 @@ $categoryLabels = [
</td> </td>
<td style="white-space:nowrap;"> <td style="white-space:nowrap;">
<a href="/members/<?= (int) $member->id ?>/spouses/<?= (int) $s['id'] ?>" class="btn btn-sm btn-outline">عرض</a> <a href="/members/<?= (int) $member->id ?>/spouses/<?= (int) $s['id'] ?>" class="btn btn-sm btn-outline">عرض</a>
<?php if ($s['status'] === 'pending_payment' && !$sInQueue && bccomp($s['addition_fee'] ?? '0', '0.01', 2) >= 0): ?> <?php if ($s['status'] === 'pending_payment' && !$sInQueue && empty($pendingMembership) && bccomp($s['addition_fee'] ?? '0', '0.01', 2) >= 0): ?>
<form method="POST" action="/members/<?= (int) $member->id ?>/pay-addition" style="display:inline;margin:0;"> <form method="POST" action="/members/<?= (int) $member->id ?>/pay-addition" style="display:inline;margin:0;">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="entity_type" value="spouses"> <input type="hidden" name="entity_type" value="spouses">
...@@ -567,11 +610,12 @@ $categoryLabels = [ ...@@ -567,11 +610,12 @@ $categoryLabels = [
<!-- Children --> <!-- Children -->
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f476; الأبناء (<?= count($children) ?>)</div> <div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f476; الأبناء (<?= count($children) ?>)</div>
<div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>#</th><th>الاسم</th><th>النوع</th><th>السن</th><th>التصنيف</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody> <div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>#</th><th>الاسم</th><th>نوع العضوية</th><th>النوع</th><th>السن</th><th>التصنيف</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody>
<?php foreach ($children as $c): ?> <?php foreach ($children as $c): ?>
<tr> <tr>
<td><?= (int) $c['child_order'] ?></td> <td><?= (int) $c['child_order'] ?></td>
<td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td> <td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td>
<td><span style="background:#E0F2FE;color:#0369A1;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;"><?= $membershipTypeLabel ?></span></td>
<td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td> <td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td>
<td><?= (int) ($c['age_years'] ?? 0) ?></td> <td><?= (int) ($c['age_years'] ?? 0) ?></td>
<td style="font-size:12px;"><?= match($c['classification'] ?? '') { <td style="font-size:12px;"><?= match($c['classification'] ?? '') {
...@@ -596,7 +640,7 @@ $categoryLabels = [ ...@@ -596,7 +640,7 @@ $categoryLabels = [
</td> </td>
<td style="white-space:nowrap;"> <td style="white-space:nowrap;">
<a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a> <a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a>
<?php if (($c['status'] ?? '') === 'pending_payment' && !$cInQueue && bccomp($c['addition_fee'] ?? '0', '0.01', 2) >= 0): ?> <?php if (($c['status'] ?? '') === 'pending_payment' && !$cInQueue && empty($pendingMembership) && bccomp($c['addition_fee'] ?? '0', '0.01', 2) >= 0): ?>
<form method="POST" action="/members/<?= (int) $member->id ?>/pay-addition" style="display:inline;margin:0;"> <form method="POST" action="/members/<?= (int) $member->id ?>/pay-addition" style="display:inline;margin:0;">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="entity_type" value="children"> <input type="hidden" name="entity_type" value="children">
...@@ -615,10 +659,11 @@ $categoryLabels = [ ...@@ -615,10 +659,11 @@ $categoryLabels = [
<!-- Temporary Members --> <!-- Temporary Members -->
<div style="padding:15px 20px;"> <div style="padding:15px 20px;">
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f464; الأعضاء المؤقتون (<?= count($temporaries) ?>)</div> <div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f464; الأعضاء المؤقتون (<?= count($temporaries) ?>)</div>
<div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>الاسم</th><th>الصلة</th><th>النوع</th><th>السن</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody> <div class="table-responsive"><table class="data-table" style="margin:0;"><thead><tr><th>الاسم</th><th>نوع العضوية</th><th>الصلة</th><th>النوع</th><th>السن</th><th>الرسوم</th><th>الحالة</th><th></th></tr></thead><tbody>
<?php foreach ($temporaries as $t): ?> <?php foreach ($temporaries as $t): ?>
<tr> <tr>
<td style="font-weight:600;"><?= e($t['full_name_ar']) ?></td> <td style="font-weight:600;"><?= e($t['full_name_ar']) ?></td>
<td><span style="background:#E0F2FE;color:#0369A1;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;"><?= $membershipTypeLabel ?></span></td>
<td style="font-size:12px;"><?= $categoryLabels[$t['category']] ?? e($t['category']) ?></td> <td style="font-size:12px;"><?= $categoryLabels[$t['category']] ?? e($t['category']) ?></td>
<td><?= $t['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td> <td><?= $t['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td>
<td><?= (int) ($t['age_years'] ?? 0) ?></td> <td><?= (int) ($t['age_years'] ?? 0) ?></td>
...@@ -637,7 +682,7 @@ $categoryLabels = [ ...@@ -637,7 +682,7 @@ $categoryLabels = [
</td> </td>
<td style="white-space:nowrap;"> <td style="white-space:nowrap;">
<a href="/members/<?= (int) $member->id ?>/temporary/<?= (int) $t['id'] ?>" class="btn btn-sm btn-outline">عرض</a> <a href="/members/<?= (int) $member->id ?>/temporary/<?= (int) $t['id'] ?>" class="btn btn-sm btn-outline">عرض</a>
<?php if (($t['status'] ?? '') === 'pending_payment' && !$tInQueue && bccomp($t['addition_fee'] ?? '0', '0.01', 2) >= 0): ?> <?php if (($t['status'] ?? '') === 'pending_payment' && !$tInQueue && empty($pendingMembership) && bccomp($t['addition_fee'] ?? '0', '0.01', 2) >= 0): ?>
<form method="POST" action="/members/<?= (int) $member->id ?>/pay-addition" style="display:inline;margin:0;"> <form method="POST" action="/members/<?= (int) $member->id ?>/pay-addition" style="display:inline;margin:0;">
<?= csrf_field() ?> <?= csrf_field() ?>
<input type="hidden" name="entity_type" value="temporary_members"> <input type="hidden" name="entity_type" value="temporary_members">
......
...@@ -17,8 +17,9 @@ class Receipt ...@@ -17,8 +17,9 @@ class Receipt
public static function findWithDetails(int $id): ?array public static function findWithDetails(int $id): ?array
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->selectOne( $receipt = $db->selectOne(
"SELECT r.*, m.full_name_ar as member_name, m.membership_number, m.phone_mobile, "SELECT r.*, m.full_name_ar as member_name, m.membership_number, m.phone_mobile,
m.form_number,
e.full_name_ar as issued_by_name, ve.full_name_ar as voided_by_name, e.full_name_ar as issued_by_name, ve.full_name_ar as voided_by_name,
p.payment_type, p.payment_method p.payment_type, p.payment_method
FROM receipts r FROM receipts r
...@@ -29,6 +30,19 @@ class Receipt ...@@ -29,6 +30,19 @@ class Receipt
WHERE r.id = ?", WHERE r.id = ?",
[$id] [$id]
); );
if (!$receipt) return null;
$pr = $db->selectOne(
"SELECT notes FROM payment_requests WHERE payment_id = ? AND is_voided = 0 LIMIT 1",
[(int) $receipt['payment_id']]
);
$receipt['fee_breakdown'] = null;
if ($pr && $pr['notes']) {
$decoded = json_decode($pr['notes'], true);
$receipt['fee_breakdown'] = $decoded['fee_breakdown'] ?? null;
}
return $receipt;
} }
public static function generateNumber(): string public static function generateNumber(): string
......
...@@ -56,17 +56,15 @@ $showFooter = $rd['show_footer_print_info']; ...@@ -56,17 +56,15 @@ $showFooter = $rd['show_footer_print_info'];
<td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">رقم العضوية</td> <td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">رقم العضوية</td>
<td style="padding:8px;border:1px solid #E5E7EB;"><?= e($receipt['membership_number'] ?? '—') ?></td> <td style="padding:8px;border:1px solid #E5E7EB;"><?= e($receipt['membership_number'] ?? '—') ?></td>
</tr> </tr>
<?php if ($receipt['form_number'] ?? ''): ?>
<tr> <tr>
<td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">البيان</td> <td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">رقم الاستمارة</td>
<td style="padding:8px;border:1px solid #E5E7EB;"><?= e($receipt['description_ar'] ?? '—') ?></td> <td style="padding:8px;border:1px solid #E5E7EB;"><?= e($receipt['form_number']) ?></td>
</tr>
<tr>
<td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">المبلغ (رقماً)</td>
<td style="padding:8px;border:1px solid #E5E7EB;font-size:22px;font-weight:700;color:<?= e($headerColor) ?>;direction:ltr;text-align:right;"><?= money($receipt['amount']) ?></td>
</tr> </tr>
<?php endif; ?>
<tr> <tr>
<td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">المبلغ (كتابةً)</td> <td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">البيان</td>
<td style="padding:8px;border:1px solid #E5E7EB;font-size:13px;"><?= e($receipt['amount_in_words_ar'] ?? '') ?></td> <td style="padding:8px;border:1px solid #E5E7EB;"><?= e($receipt['description_ar'] ?? '—') ?></td>
</tr> </tr>
<?php if ($receipt['payment_type'] ?? ''): ?> <?php if ($receipt['payment_type'] ?? ''): ?>
<tr> <tr>
...@@ -82,6 +80,60 @@ $showFooter = $rd['show_footer_print_info']; ...@@ -82,6 +80,60 @@ $showFooter = $rd['show_footer_print_info'];
<?php endif; ?> <?php endif; ?>
</table> </table>
<?php
$breakdown = $receipt['fee_breakdown'] ?? null;
if (is_array($breakdown) && !empty($breakdown)):
$detailItems = [];
$totalLine = '';
foreach ($breakdown as $bLine) {
if (str_contains($bLine, '═══')) continue;
if (str_starts_with($bLine, '💵')) { $totalLine = $bLine; continue; }
$detailItems[] = $bLine;
}
?>
<table style="width:100%;border-collapse:collapse;margin-bottom:15px;font-size:13px;">
<thead>
<tr style="background:#F9FAFB;">
<th style="padding:8px 10px;border:1px solid #E5E7EB;text-align:right;font-weight:700;color:<?= e($headerColor) ?>;" colspan="2">تفصيل المبلغ</th>
</tr>
</thead>
<tbody>
<?php foreach ($detailItems as $idx => $dItem): ?>
<tr>
<td style="padding:6px 10px;border:1px solid #E5E7EB;width:30px;text-align:center;color:#6B7280;font-size:12px;"><?= $idx + 1 ?></td>
<td style="padding:6px 10px;border:1px solid #E5E7EB;"><?= e($dItem) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr style="background:#F0FDF4;">
<td colspan="2" style="padding:10px;border:2px solid <?= e($headerColor) ?>;font-weight:700;font-size:16px;text-align:center;color:<?= e($headerColor) ?>;">
<?= e($totalLine ?: ('💵 الإجمالي: ' . money($receipt['amount']))) ?>
</td>
</tr>
</tfoot>
</table>
<?php else: ?>
<table style="width:100%;border-collapse:collapse;margin-bottom:15px;font-size:14px;">
<tr>
<td style="padding:10px;border:2px solid <?= e($headerColor) ?>;font-weight:700;font-size:22px;text-align:center;color:<?= e($headerColor) ?>;direction:ltr;">
<?= money($receipt['amount']) ?>
</td>
</tr>
</table>
<?php endif; ?>
<table style="width:100%;border-collapse:collapse;margin-bottom:20px;font-size:14px;">
<tr>
<td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;width:30%;">المبلغ (رقماً)</td>
<td style="padding:8px;border:1px solid #E5E7EB;font-size:22px;font-weight:700;color:<?= e($headerColor) ?>;direction:ltr;text-align:right;"><?= money($receipt['amount']) ?></td>
</tr>
<tr>
<td style="padding:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-weight:600;">المبلغ (كتابةً)</td>
<td style="padding:8px;border:1px solid #E5E7EB;font-size:13px;"><?= e($receipt['amount_in_words_ar'] ?? '') ?></td>
</tr>
</table>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:30px;margin-top:50px;text-align:center;font-size:13px;"> <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:30px;margin-top:50px;text-align:center;font-size:13px;">
<div style="border-top:1px solid #000;padding-top:10px;">توقيع المستلم</div> <div style="border-top:1px solid #000;padding-top:10px;">توقيع المستلم</div>
<div style="border-top:1px solid #000;padding-top:10px;">أمين الخزينة<br><small><?= e($receipt['issued_by_name'] ?? '') ?></small></div> <div style="border-top:1px solid #000;padding-top:10px;">أمين الخزينة<br><small><?= e($receipt['issued_by_name'] ?? '') ?></small></div>
......
...@@ -36,7 +36,7 @@ class Spouse extends Model ...@@ -36,7 +36,7 @@ class Spouse extends Model
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$row = $db->selectOne( $row = $db->selectOne(
"SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0 AND status = 'active'", "SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0",
[$memberId] [$memberId]
); );
return (int) ($row['cnt'] ?? 0); return (int) ($row['cnt'] ?? 0);
......
...@@ -33,8 +33,9 @@ final class SpouseFeeCalculator ...@@ -33,8 +33,9 @@ final class SpouseFeeCalculator
} }
$membershipValue = $member['membership_value'] ?? '0.00'; $membershipValue = $member['membership_value'] ?? '0.00';
$isOnInitialForm = FormFeeService::isOnInitialForm($member);
if (bccomp($membershipValue, '0.01', 2) < 0) { if (bccomp($membershipValue, '0.01', 2) < 0 && !$isOnInitialForm) {
return self::error('يجب تحديد قيمة العضوية أولاً (ملء الاستمارة واختيار المؤهل)'); return self::error('يجب تحديد قيمة العضوية أولاً (ملء الاستمارة واختيار المؤهل)');
} }
......
...@@ -18,7 +18,8 @@ final class TemporaryFeeCalculator ...@@ -18,7 +18,8 @@ final class TemporaryFeeCalculator
} }
$membershipValue = $member['membership_value'] ?? '0.00'; $membershipValue = $member['membership_value'] ?? '0.00';
if (bccomp($membershipValue, '0.00', 2) <= 0) { $isOnInitialForm = FormFeeService::isOnInitialForm($member);
if (bccomp($membershipValue, '0.00', 2) <= 0 && !$isOnInitialForm) {
return ['fee' => '0.00', 'error' => 'قيمة العضوية غير محددة']; return ['fee' => '0.00', 'error' => 'قيمة العضوية غير محددة'];
} }
......
...@@ -82,6 +82,16 @@ class WaiverController extends Controller ...@@ -82,6 +82,16 @@ class WaiverController extends Controller
// Send payment to cashier queue // Send payment to cashier queue
if (bccomp($waiverFee, '0', 2) > 0) { if (bccomp($waiverFee, '0', 2) > 0) {
$breakdown = [
'📋 نوع العملية: تنازل عن العضوية',
'💰 قيمة العضوية: ' . money($membershipValue),
'📊 نسبة رسوم التنازل: ' . $waiverPct . '%',
'💵 الرسوم: ' . $waiverPct . '% × ' . money($membershipValue) . ' = ' . money($waiverFee),
'👥 عدد التابعين: ' . ($spouseCount + $childCount + $tempCount),
'═══════════════════════════',
'💵 الإجمالي: ' . money($waiverFee),
];
$result = PaymentRequestService::createRequest([ $result = PaymentRequestService::createRequest([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'amount' => $waiverFee, 'amount' => $waiverFee,
...@@ -89,6 +99,7 @@ class WaiverController extends Controller ...@@ -89,6 +99,7 @@ class WaiverController extends Controller
'related_entity_type' => 'waiver_requests', 'related_entity_type' => 'waiver_requests',
'related_entity_id' => (int) $waiver->id, 'related_entity_id' => (int) $waiver->id,
'description_ar' => 'رسوم تنازل — طلب #' . $waiver->id, 'description_ar' => 'رسوم تنازل — طلب #' . $waiver->id,
'notes' => json_encode(['fee_breakdown' => $breakdown], JSON_UNESCAPED_UNICODE),
]); ]);
if ($result['success']) { if ($result['success']) {
......
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