Commit 8b8f9ee3 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(waiver): comprehensive bylaws compliance rewrite

Full implementation of waiver module per club bylaws:
- Debt verification (subscriptions + fines + payment requests) blocks form
- Dependent count comparison with excess detection
- Board sets excess fee percentage during approval
- Document upload support (waiver form + target membership form)
- Auto-computed fields (no manual input for existing members)
- Enhanced show view with dependent comparison and fee breakdown
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 63605b9a
......@@ -35,18 +35,17 @@ class WaiverController extends Controller
$waiverPct = $waiverPctData['percentage'] ?? '30.00';
$waiverFee = bcdiv(bcmul($membershipValue, $waiverPct, 4), '100', 2);
// Count dependents
$spouseCount = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0", [(int) $memberId])['cnt'] ?? 0);
$childCount = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0", [(int) $memberId])['cnt'] ?? 0);
$tempCount = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM temporary_members WHERE member_id = ? AND is_archived = 0", [(int) $memberId])['cnt'] ?? 0);
$totalDependents = $spouseCount + $childCount + $tempCount;
$dependents = WaiverProcessor::countDependents((int) $memberId);
$debtCheck = WaiverProcessor::checkDebts((int) $memberId);
return $this->view('Waiver.Views.create', [
'member' => $member,
'membership_value' => $membershipValue,
'waiver_pct' => $waiverPct,
'waiver_fee' => $waiverFee,
'total_dependents' => $totalDependents,
'dependents' => $dependents,
'total_dependents' => $dependents['total'],
'debt_check' => $debtCheck,
]);
}
......@@ -57,18 +56,34 @@ class WaiverController extends Controller
if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
if (!$member['membership_number']) return $this->redirect("/members/{$memberId}")->withError('العضو ليس لديه رقم عضوية');
// Check debts — cannot proceed if unpaid
$debtCheck = WaiverProcessor::checkDebts((int) $memberId);
if (!$debtCheck['clear']) {
return $this->redirect("/members/{$memberId}")->withError(
'لا يمكن تقديم طلب التنازل — يوجد مديونية بمبلغ ' . money($debtCheck['total']) . ' يجب سدادها أولاً'
);
}
$membershipValue = self::getCurrentMembershipValue($db, $member);
$waiverPctData = RuleEngine::get('WAIVER_FEE');
$waiverPct = $waiverPctData['percentage'] ?? '30.00';
$waiverFee = bcdiv(bcmul($membershipValue, $waiverPct, 4), '100', 2);
$paymentMethod = trim($request->post('payment_method', 'cash'));
$spouseCount = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0", [(int) $memberId])['cnt'] ?? 0);
$childCount = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0", [(int) $memberId])['cnt'] ?? 0);
$tempCount = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM temporary_members WHERE member_id = ? AND is_archived = 0", [(int) $memberId])['cnt'] ?? 0);
$dependents = WaiverProcessor::countDependents((int) $memberId);
$targetMemberId = (int) $request->post('target_member_id', 0);
// Handle document uploads
$waiverDocPath = null;
$targetFormPath = null;
$uploadDir = 'uploads/waivers/' . date('Y/m');
if (!empty($_FILES['waiver_request_doc']['tmp_name'])) {
$waiverDocPath = self::handleUpload($_FILES['waiver_request_doc'], $uploadDir, 'waiver_req_' . $memberId);
}
if (!empty($_FILES['target_form_doc']['tmp_name'])) {
$targetFormPath = self::handleUpload($_FILES['target_form_doc'], $uploadDir, 'target_form_' . $memberId);
}
$waiver = WaiverRequest::create([
'source_member_id' => (int) $memberId,
'target_member_id' => $targetMemberId > 0 ? $targetMemberId : null,
......@@ -76,22 +91,25 @@ class WaiverController extends Controller
'membership_value_at_waiver' => $membershipValue,
'waiver_fee_percentage' => $waiverPct,
'waiver_fee_amount' => $waiverFee,
'original_dependent_count' => $spouseCount + $childCount + $tempCount,
'original_dependent_count' => $dependents['total'],
'board_approval_required' => 1,
'annual_renewal_paid' => 1,
'debts_cleared' => 1,
'status' => 'requested',
'notes' => trim($request->post('notes', '')) ?: null,
'waiver_request_doc_path' => $waiverDocPath,
'target_form_doc_path' => $targetFormPath,
]);
EventBus::dispatch('waiver.requested', ['waiver_id' => (int) $waiver->id, 'member_id' => (int) $memberId]);
// Send payment to cashier queue
if (bccomp($waiverFee, '0', 2) > 0) {
$breakdown = [
'📋 نوع العملية: تنازل عن العضوية',
'💰 قيمة العضوية: ' . money($membershipValue),
'💰 قيمة العضوية الحالية: ' . money($membershipValue),
'📊 نسبة رسوم التنازل: ' . $waiverPct . '%',
'💵 الرسوم: ' . $waiverPct . '% × ' . money($membershipValue) . ' = ' . money($waiverFee),
'👥 عدد التابعين: ' . ($spouseCount + $childCount + $tempCount),
'👥 عدد التابعين الأصلي: ' . $dependents['total'] . ' (زوجات: ' . $dependents['spouses'] . ' — أبناء: ' . $dependents['children'] . ' — مؤقتين: ' . $dependents['temporary'] . ')',
'═══════════════════════════',
'💵 الإجمالي: ' . money($waiverFee),
];
......@@ -124,7 +142,8 @@ class WaiverController extends Controller
{
$db = App::getInstance()->db();
$waiver = $db->selectOne(
"SELECT wr.*, m.full_name_ar as source_name, tm.full_name_ar as target_name, e.full_name_ar as approved_by_name
"SELECT wr.*, m.full_name_ar as source_name, m.membership_number as current_source_number,
tm.full_name_ar as target_name, e.full_name_ar as approved_by_name
FROM waiver_requests wr JOIN members m ON m.id = wr.source_member_id
LEFT JOIN members tm ON tm.id = wr.target_member_id
LEFT JOIN employees e ON e.id = wr.approved_by
......@@ -132,7 +151,16 @@ class WaiverController extends Controller
[(int) $id]
);
if (!$waiver) return $this->redirect('/waivers')->withError('الطلب غير موجود');
return $this->view('Waiver.Views.show', ['waiver' => $waiver]);
// Get live dependent counts for display
$sourceDeps = WaiverProcessor::countDependents((int) $waiver['source_member_id']);
$targetDeps = $waiver['target_member_id'] ? WaiverProcessor::countDependents((int) $waiver['target_member_id']) : null;
return $this->view('Waiver.Views.show', [
'waiver' => $waiver,
'source_deps' => $sourceDeps,
'target_deps' => $targetDeps,
]);
}
public function pay(Request $request, string $id): Response
......@@ -140,23 +168,27 @@ class WaiverController extends Controller
$db = App::getInstance()->db();
$waiver = $db->selectOne("SELECT * FROM waiver_requests WHERE id = ?", [(int) $id]);
if (!$waiver) return $this->redirect('/waivers')->withError('الطلب غير موجود');
if (in_array($waiver['status'], ['completed', 'fee_paid', 'rejected'])) {
if (\in_array($waiver['status'], ['completed', 'fee_paid', 'rejected'])) {
return $this->redirect("/waivers/{$id}")->withError('لا يمكن الدفع لهذا الطلب');
}
$amount = $waiver['waiver_fee_amount'] ?? '0.00';
if (bccomp((string) $amount, '0', 2) <= 0) {
// Include excess fee if any
$excessFee = $waiver['excess_fee_amount'] ?? '0.00';
$totalPayment = bcadd((string) $amount, (string) $excessFee, 2);
if (bccomp($totalPayment, '0', 2) <= 0) {
return $this->redirect("/waivers/{$id}")->withError('لا توجد رسوم مطلوبة');
}
$result = PaymentService::processPayment([
'member_id' => (int) $waiver['source_member_id'],
'amount' => $amount,
'amount' => $totalPayment,
'payment_type' => 'waiver_fee',
'payment_method' => $request->post('payment_method', 'cash'),
'related_entity_type' => 'waiver_requests',
'related_entity_id' => (int) $id,
'description' => 'رسوم تنازل — طلب #' . $id,
'description' => 'رسوم تنازل — طلب #' . $id . (bccomp($excessFee, '0', 2) > 0 ? ' (يشمل رسوم تابعين إضافيين)' : ''),
]);
if (!$result['success']) {
......@@ -182,15 +214,65 @@ class WaiverController extends Controller
$employee = App::getInstance()->currentEmployee();
$boardRef = trim($request->post('board_decision_reference', ''));
$db->update('waiver_requests', [
// Handle excess dependents — board sets the percentage
$excessPct = trim($request->post('excess_fee_percentage', ''));
$excessFeeAmount = '0.00';
$excessCount = 0;
if ($waiver['target_member_id']) {
$targetDeps = WaiverProcessor::countDependents((int) $waiver['target_member_id']);
$originalCount = (int) $waiver['original_dependent_count'];
if ($targetDeps['total'] > $originalCount) {
$excessCount = $targetDeps['total'] - $originalCount;
if ($excessPct !== '' && is_numeric($excessPct)) {
$membershipValue = $waiver['membership_value_at_waiver'];
$excessFeeAmount = bcdiv(bcmul((string) $membershipValue, $excessPct, 4), '100', 2);
$excessFeeAmount = bcmul($excessFeeAmount, (string) $excessCount, 2);
}
}
}
$updateData = [
'status' => 'approved',
'approved_by' => $employee ? (int) $employee->id : null,
'approved_at' => date('Y-m-d H:i:s'),
'board_decision_reference' => $boardRef ?: null,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $id]);
];
if ($excessCount > 0) {
$updateData['excess_dependent_count'] = $excessCount;
$updateData['excess_fee_percentage'] = $excessPct !== '' ? $excessPct : null;
$updateData['excess_fee_amount'] = $excessFeeAmount;
}
$db->update('waiver_requests', $updateData, '`id` = ?', [(int) $id]);
// Send payment request including excess fees if applicable
$totalFee = bcadd((string) ($waiver['waiver_fee_amount'] ?? '0'), $excessFeeAmount, 2);
if (bccomp($totalFee, '0', 2) > 0) {
$breakdown = [
'📋 نوع العملية: تنازل عن العضوية (معتمد من مجلس الأمناء)',
'💰 رسوم التنازل: ' . money($waiver['waiver_fee_amount']),
];
if (bccomp($excessFeeAmount, '0', 2) > 0) {
$breakdown[] = '👥 رسوم أعضاء إضافيين: ' . $excessCount . ' عضو × ' . $excessPct . '% = ' . money($excessFeeAmount);
}
$breakdown[] = '═══════════════════════════';
$breakdown[] = '💵 الإجمالي: ' . money($totalFee);
return $this->redirect("/waivers/{$id}")->withSuccess('تمت الموافقة على طلب التنازل');
PaymentRequestService::createRequest([
'member_id' => (int) $waiver['source_member_id'],
'amount' => $totalFee,
'payment_type' => 'waiver_fee',
'related_entity_type' => 'waiver_requests',
'related_entity_id' => (int) $id,
'description_ar' => 'رسوم تنازل (معتمد) — طلب #' . $id,
'notes' => json_encode(['fee_breakdown' => $breakdown], JSON_UNESCAPED_UNICODE),
]);
}
return $this->redirect("/waivers/{$id}")->withSuccess('تمت الموافقة على طلب التنازل' . ($excessCount > 0 ? ' — تم احتساب رسوم إضافية عن ' . $excessCount . ' عضو زائد' : ''));
}
public function reject(Request $request, string $id): Response
......@@ -205,6 +287,7 @@ class WaiverController extends Controller
'status' => 'rejected',
'approved_by' => $employee ? (int) $employee->id : null,
'approved_at' => date('Y-m-d H:i:s'),
'notes' => trim($request->post('rejection_reason', '')) ?: ($waiver['notes'] ?? null),
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $id]);
......@@ -231,40 +314,46 @@ class WaiverController extends Controller
return $this->redirect("/waivers/{$id}")->withError('يجب تحديد العضو المتنازل إليه أولاً');
}
// Validate: annual renewal must be paid before completing waiver
$unpaidSubs = $db->selectOne(
"SELECT COUNT(*) as cnt FROM subscriptions WHERE member_id = ? AND status IN ('pending','overdue')",
[(int) $waiver['source_member_id']]
);
if ((int) ($unpaidSubs['cnt'] ?? 0) > 0) {
return $this->redirect("/waivers/{$id}")->withError('يجب سداد جميع الاشتراكات السنوية المستحقة قبل إتمام التنازل');
// VALIDATION 1: All financial obligations must be cleared
$debtCheck = WaiverProcessor::checkDebts((int) $waiver['source_member_id']);
if (!$debtCheck['clear']) {
$debtLines = array_map(fn($d) => $d['type'] . ': ' . money($d['amount']), $debtCheck['debts']);
return $this->redirect("/waivers/{$id}")->withError(
'لا يمكن إتمام التنازل — توجد مديونيات مستحقة: ' . implode(' | ', $debtLines) . ' — الإجمالي: ' . money($debtCheck['total'])
);
}
// Validate: target member's dependents must not exceed source's original count
// VALIDATION 2: Target dependent count check
$originalDependentCount = (int) $waiver['original_dependent_count'];
$targetSpouses = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0", [$targetMemberId])['cnt'] ?? 0);
$targetChildren = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0", [$targetMemberId])['cnt'] ?? 0);
$targetTemps = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM temporary_members WHERE member_id = ? AND is_archived = 0", [$targetMemberId])['cnt'] ?? 0);
$targetDependentCount = $targetSpouses + $targetChildren + $targetTemps;
$targetDeps = WaiverProcessor::countDependents($targetMemberId);
if ($targetDeps['total'] > $originalDependentCount) {
$excess = $targetDeps['total'] - $originalDependentCount;
// If board already approved excess with fees, check that excess fee was set
if (bccomp($waiver['excess_fee_amount'] ?? '0', '0', 2) <= 0) {
return $this->redirect("/waivers/{$id}")->withError(
'عدد التابعين للعضو المتنازل إليه (' . $targetDeps['total'] . ') يتجاوز عدد التابعين الأصليين (' . $originalDependentCount . ') بمقدار ' . $excess . ' — يجب أن يحدد مجلس الأمناء نسبة الرسوم الإضافية أثناء الاعتماد'
);
}
}
if ($targetDependentCount > $originalDependentCount) {
$excess = $targetDependentCount - $originalDependentCount;
return $this->redirect("/waivers/{$id}")->withError(
'عدد التابعين للعضو المتنازل إليه (' . $targetDependentCount . ') يتجاوز عدد التابعين الأصليين (' . $originalDependentCount . ') بمقدار ' . $excess . ' — يجب موافقة مجلس الأمناء على الإضافة الزائدة واحتساب الرسوم الإضافية قبل إتمام التنازل'
);
// VALIDATION 3: Waiver fee must be paid
if (!\in_array($waiver['status'], ['fee_paid', 'approved'])) {
return $this->redirect("/waivers/{$id}")->withError('يجب سداد رسوم التنازل قبل إتمام العملية');
}
// Store actual dependent count on the waiver
// Record final state
$db->update('waiver_requests', [
'new_dependent_count' => $targetDependentCount,
'new_dependent_count' => $targetDeps['total'],
'annual_renewal_paid' => 1,
'debts_cleared' => 1,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $id]);
$result = WaiverProcessor::execute((int) $id);
if (!$result['success']) return $this->redirect("/waivers/{$id}")->withError($result['error']);
return $this->redirect("/waivers/{$id}")->withSuccess('تم إتمام التنازل — العضوية نُقلت بنجاح');
return $this->redirect("/waivers/{$id}")->withSuccess('تم إتمام التنازل — العضوية رقم ' . ($waiver['membership_number'] ?? '') . ' نُقلت بنجاح للعضو المتنازل إليه');
}
private static function getCurrentMembershipValue(\App\Core\Database $db, array $member): string
......@@ -291,4 +380,22 @@ class WaiverController extends Controller
return $member['membership_value'] ?? '0.00';
}
}
\ No newline at end of file
private static function handleUpload(array $file, string $dir, string $prefix): ?string
{
$allowed = ['application/pdf', 'image/jpeg', 'image/png'];
if (!isset($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) return null;
if (!empty($file['type']) && !\in_array($file['type'], $allowed)) return null;
if (($file['size'] ?? 0) > 5 * 1024 * 1024) return null;
$ext = pathinfo($file['name'] ?? '', PATHINFO_EXTENSION) ?: 'pdf';
$fullDir = rtrim($_SERVER['DOCUMENT_ROOT'] ?? 'public', '/') . '/' . $dir;
if (!is_dir($fullDir)) @mkdir($fullDir, 0755, true);
$filename = $prefix . '_' . date('Ymd_His') . '.' . $ext;
$path = $dir . '/' . $filename;
move_uploaded_file($file['tmp_name'], $fullDir . '/' . $filename);
return $path;
}
}
......@@ -27,9 +27,11 @@ final class WaiverProcessor
$sourceMember = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $waiver['source_member_id']]);
if (!$sourceMember) return ['success' => false, 'error' => 'العضو المصدر غير موجود'];
$targetMember = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $waiver['target_member_id']]);
if (!$targetMember) return ['success' => false, 'error' => 'العضو المستفيد غير موجود'];
$db->beginTransaction();
try {
// Archive snapshot
$snapshotId = ArchiveService::takeSnapshot('members', (int) $sourceMember['id'], 'waiver', 'تنازل — طلب #' . $waiverId);
// Archive source member FIRST (release number to avoid unique constraint violation)
......@@ -42,17 +44,16 @@ final class WaiverProcessor
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $waiver['source_member_id']]);
// Transfer membership number to target member
// Transfer membership number to target — becomes the new primary member
$db->update('members', [
'membership_number' => $waiver['membership_number'],
'status' => 'active',
'membership_type' => $sourceMember['membership_type'] ?? 'working',
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $waiver['target_member_id']]);
// Record number chain
ArchiveService::recordNumberTransfer($waiver['membership_number'], 'waiver', 'members', (int) $waiver['target_member_id']);
// Complete waiver
$db->update('waiver_requests', [
'archive_snapshot_id' => $snapshotId,
'status' => 'completed',
......@@ -77,4 +78,85 @@ final class WaiverProcessor
return ['success' => false, 'error' => $e->getMessage()];
}
}
}
\ No newline at end of file
/**
* Check all financial obligations for a member.
* Returns ['clear' => bool, 'debts' => [...details...]]
*/
public static function checkDebts(int $memberId): array
{
$db = App::getInstance()->db();
$debts = [];
// Unpaid subscriptions
$unpaidSubs = $db->selectOne(
"SELECT COUNT(*) as cnt, COALESCE(SUM(total_amount - paid_amount + fine_amount), 0) as total
FROM subscriptions WHERE member_id = ? AND status IN ('pending','overdue')",
[$memberId]
);
if ((int) ($unpaidSubs['cnt'] ?? 0) > 0) {
$debts[] = [
'type' => 'اشتراكات سنوية',
'count' => (int) $unpaidSubs['cnt'],
'amount' => $unpaidSubs['total'] ?? '0.00',
];
}
// Unpaid fines
$unpaidFines = $db->selectOne(
"SELECT COUNT(*) as cnt, COALESCE(SUM(amount - paid_amount), 0) as total
FROM fines WHERE member_id = ? AND status = 'pending'",
[$memberId]
);
if ((int) ($unpaidFines['cnt'] ?? 0) > 0) {
$debts[] = [
'type' => 'غرامات',
'count' => (int) $unpaidFines['cnt'],
'amount' => $unpaidFines['total'] ?? '0.00',
];
}
// Unpaid payment requests (cashier queue)
$unpaidRequests = $db->selectOne(
"SELECT COUNT(*) as cnt, COALESCE(SUM(amount), 0) as total
FROM payment_requests WHERE member_id = ? AND status = 'pending' AND is_voided = 0",
[$memberId]
);
if ((int) ($unpaidRequests['cnt'] ?? 0) > 0) {
$debts[] = [
'type' => 'طلبات دفع معلقة',
'count' => (int) $unpaidRequests['cnt'],
'amount' => $unpaidRequests['total'] ?? '0.00',
];
}
$totalDebt = '0.00';
foreach ($debts as $d) {
$totalDebt = bcadd($totalDebt, (string) $d['amount'], 2);
}
return [
'clear' => empty($debts),
'debts' => $debts,
'total' => $totalDebt,
];
}
/**
* Count dependents for a member (spouses + children + temporary).
*/
public static function countDependents(int $memberId): array
{
$db = App::getInstance()->db();
$spouses = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM spouses WHERE member_id = ? AND is_archived = 0", [$memberId])['cnt'] ?? 0);
$children = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM children WHERE member_id = ? AND is_archived = 0", [$memberId])['cnt'] ?? 0);
$temps = (int) ($db->selectOne("SELECT COUNT(*) as cnt FROM temporary_members WHERE member_id = ? AND is_archived = 0", [$memberId])['cnt'] ?? 0);
return [
'spouses' => $spouses,
'children' => $children,
'temporary' => $temps,
'total' => $spouses + $children + $temps,
];
}
}
......@@ -8,22 +8,91 @@
<li>يتطلب موافقة مجلس الأمناء</li>
<li>العضو المستفيد يحصل على <strong>نفس رقم العضوية</strong></li>
<li>عدد التابعين للمتنازل إليه لا يتجاوز عدد التابعين الأصليين (<?= (int) $total_dependents ?>) — أي زيادة تتطلب رسوم إضافية يحددها مجلس الأمناء</li>
<li>يجب سداد جميع الاشتراكات السنوية المستحقة قبل إتمام التنازل</li>
<li>يجب تقديم استمارة عضوية جديدة للمتنازل إليه</li>
<li>يجب سداد <strong>جميع</strong> الالتزامات المالية (اشتراكات + غرامات + طلبات دفع) قبل التنازل</li>
<li>يجب تقديم استمارة طلب تنازل + استمارة عضوية جديدة للمتنازل إليه</li>
</ul>
</div>
<table style="width:100%;max-width:500px;font-size:14px;">
<?php if (!$debt_check['clear']): ?>
<div style="padding:15px;background:#FEF2F2;border:2px solid #DC2626;border-radius:8px;margin-bottom:20px;">
<strong style="color:#DC2626;">🚫 لا يمكن تقديم طلب التنازل — يوجد مديونية مستحقة:</strong>
<table style="width:100%;margin-top:10px;font-size:13px;">
<thead><tr style="background:#FEE2E2;"><th style="padding:6px;text-align:right;">نوع المديونية</th><th style="padding:6px;text-align:center;">العدد</th><th style="padding:6px;text-align:left;">المبلغ</th></tr></thead>
<tbody>
<?php foreach ($debt_check['debts'] as $debt): ?>
<tr style="border-bottom:1px solid #FECACA;">
<td style="padding:6px;font-weight:600;"><?= e($debt['type']) ?></td>
<td style="padding:6px;text-align:center;"><?= (int) $debt['count'] ?></td>
<td style="padding:6px;text-align:left;color:#DC2626;font-weight:700;"><?= money($debt['amount']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot><tr style="border-top:2px solid #DC2626;"><td colspan="2" style="padding:8px;font-weight:700;">إجمالي المديونية</td><td style="padding:8px;font-weight:700;color:#DC2626;"><?= money($debt_check['total']) ?></td></tr></tfoot>
</table>
<p style="margin:10px 0 0;font-size:12px;color:#7F1D1D;">يجب سداد جميع المبالغ المستحقة أعلاه قبل تقديم طلب التنازل.</p>
</div>
<?php endif; ?>
<table style="width:100%;max-width:600px;font-size:14px;">
<tr><td style="padding:6px 0;color:#6B7280;">العضو</td><td style="padding:6px 0;font-weight:600;"><?= e($member['full_name_ar']) ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">رقم العضوية</td><td style="padding:6px 0;font-weight:700;"><?= e($member['membership_number'] ?? '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">قيمة العضوية الحالية</td><td style="padding:6px 0;font-weight:600;"><?= money($membership_value) ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">نسبة التنازل</td><td style="padding:6px 0;"><?= e($waiver_pct) ?>%</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">عدد التابعين</td><td style="padding:6px 0;"><?= (int) $total_dependents ?></td></tr>
<tr style="border-top:2px solid #0D7377;"><td style="padding:10px 0;font-weight:700;font-size:16px;">رسوم التنازل</td><td style="padding:10px 0;font-weight:700;font-size:18px;color:#0D7377;"><?= money($waiver_fee) ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">نسبة رسوم التنازل</td><td style="padding:6px 0;"><?= e($waiver_pct) ?>%</td></tr>
</table>
<div style="margin-top:15px;padding:12px;background:#F0F9FF;border:1px solid #BAE6FD;border-radius:8px;">
<strong style="color:#0369A1;">👥 التابعون الحاليون:</strong>
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:8px;">
<div style="text-align:center;padding:8px;background:#fff;border-radius:6px;border:1px solid #E0F2FE;">
<div style="font-size:20px;font-weight:700;color:#0D7377;"><?= (int) $dependents['spouses'] ?></div>
<div style="font-size:11px;color:#6B7280;">زوجات</div>
</div>
<div style="text-align:center;padding:8px;background:#fff;border-radius:6px;border:1px solid #E0F2FE;">
<div style="font-size:20px;font-weight:700;color:#0D7377;"><?= (int) $dependents['children'] ?></div>
<div style="font-size:11px;color:#6B7280;">أبناء</div>
</div>
<div style="text-align:center;padding:8px;background:#fff;border-radius:6px;border:1px solid #E0F2FE;">
<div style="font-size:20px;font-weight:700;color:#0D7377;"><?= (int) $dependents['temporary'] ?></div>
<div style="font-size:11px;color:#6B7280;">مؤقتين</div>
</div>
<div style="text-align:center;padding:8px;background:#fff;border-radius:6px;border:1px solid #0D7377;">
<div style="font-size:20px;font-weight:700;color:#0D7377;"><?= (int) $dependents['total'] ?></div>
<div style="font-size:11px;color:#6B7280;font-weight:600;">الإجمالي</div>
</div>
</div>
<p style="margin:8px 0 0;font-size:12px;color:#0369A1;">هذا العدد هو الحد الأقصى للمتنازل إليه بدون رسوم إضافية. أي زيادة يحدد مجلس الأمناء نسبتها.</p>
</div>
<div style="margin-top:15px;padding:12px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;">
<table style="width:100%;font-size:14px;">
<tr style="border-bottom:1px dashed #86EFAC;"><td style="padding:6px 0;color:#166534;">رسوم التنازل الأساسية</td><td style="padding:6px 0;font-weight:600;"><?= e($waiver_pct) ?>% × <?= money($membership_value) ?></td><td style="padding:6px 0;font-weight:700;text-align:left;"><?= money($waiver_fee) ?></td></tr>
<tr><td style="padding:8px 0;font-weight:700;font-size:16px;color:#0D7377;" colspan="2">الإجمالي المبدئي</td><td style="padding:8px 0;font-weight:700;font-size:18px;color:#0D7377;text-align:left;"><?= money($waiver_fee) ?></td></tr>
</table>
<p style="margin:8px 0 0;font-size:11px;color:#166534;">* قد تُضاف رسوم إضافية عن التابعين الزائدين بعد اعتماد مجلس الأمناء</p>
</div>
</div>
<form method="POST" action="/waivers/store/<?= (int) $member['id'] ?>" id="waiver-form">
<?php if ($debt_check['clear']): ?>
<form method="POST" action="/waivers/store/<?= (int) $member['id'] ?>" id="waiver-form" enctype="multipart/form-data">
<?= csrf_field() ?>
<!-- Documents -->
<div class="card" style="padding:20px;margin-bottom:15px;">
<h4 style="margin:0 0 15px;color:#374151;">📄 المستندات المطلوبة</h4>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">استمارة طلب التنازل <span style="color:#DC2626;">*</span></label>
<input type="file" name="waiver_request_doc" class="form-input" accept=".pdf,.jpg,.jpeg,.png" style="padding:8px;">
<small style="color:#6B7280;">PDF أو صورة (حد أقصى 5 ميجا)</small>
</div>
<div class="form-group">
<label class="form-label">استمارة عضوية المتنازل إليه</label>
<input type="file" name="target_form_doc" class="form-input" accept=".pdf,.jpg,.jpeg,.png" style="padding:8px;">
<small style="color:#6B7280;">PDF أو صورة (حد أقصى 5 ميجا)</small>
</div>
</div>
</div>
<!-- Target Member -->
<div class="card" style="padding:20px;margin-bottom:15px;background:#ECFDF5;border:2px solid #059669;">
<h4 style="margin:0 0 15px;color:#059669;">👤 المستفيد (المتنازل إليه)</h4>
......@@ -49,23 +118,20 @@
<div class="form-group"><label class="form-label">ملاحظات</label><textarea name="notes" class="form-textarea" rows="3"></textarea></div>
</div>
<!-- Fee & Payment -->
<div class="card" style="padding:20px;margin-bottom:20px;background:#FFF7ED;border:2px solid #F59E0B;">
<h4 style="margin:0 0 15px;color:#D97706;">رسوم التنازل — تُرسل لطابور الخزينة</h4>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">المبلغ</label>
<input type="text" value="<?= money($waiver_fee) ?>" class="form-input" style="background:#F3F4F6;font-weight:700;font-size:16px;" readonly>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary" onclick="return confirm('سيتم تقديم طلب التنازل وإرسال رسوم <?= money($waiver_fee) ?> لطابور الخزينة. متأكد؟')">تقديم الطلب</button>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">إلغاء</a>
</form>
<?php else: ?>
<div style="padding:20px;text-align:center;">
<a href="/members/<?= (int) $member['id'] ?>/financials" class="btn btn-primary">عرض الحساب المالي</a>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">رجوع</a>
</div>
<?php endif; ?>
<script>
(function() {
var searchInput = document.getElementById('target-search');
if (!searchInput) return;
var resultsDiv = document.getElementById('target-results');
var selectedDiv = document.getElementById('target-selected');
var hiddenInput = document.getElementById('target-member-id');
......@@ -123,4 +189,4 @@ function clearTarget() {
document.getElementById('target-selected').style.display = 'none';
}
</script>
<?php $__template->endSection(); ?>
\ No newline at end of file
<?php $__template->endSection(); ?>
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>تنازل #<?= (int) $waiver['id'] ?><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$statusColor = match($waiver['status']) { 'completed' => '#059669', 'fee_paid' => '#2563EB', 'approved' => '#0284C7', 'rejected' => '#DC2626', default => '#D97706' };
$statusLabel = match($waiver['status']) { 'requested' => 'مقدم — في انتظار الاعتماد', 'approved' => 'معتمد — في انتظار الدفع', 'fee_paid' => 'تم الدفع — جاهز للإتمام', 'completed' => 'مكتمل — تم نقل العضوية', 'rejected' => 'مرفوض', default => $waiver['status'] };
?>
<!-- Status Banner -->
<div style="padding:12px 20px;background:<?= $statusColor ?>15;border:2px solid <?= $statusColor ?>;border-radius:8px;margin-bottom:20px;display:flex;align-items:center;gap:10px;">
<span style="font-size:20px;"></span>
<span style="font-weight:700;font-size:16px;color:<?= $statusColor ?>;"><?= $statusLabel ?></span>
<?php if ($waiver['approved_by_name']): ?>
<span style="margin-right:auto;font-size:12px;color:#6B7280;">بواسطة: <?= e($waiver['approved_by_name']) ?><?= arabic_date($waiver['approved_at'] ?? '') ?></span>
<?php endif; ?>
</div>
<!-- Main Info -->
<div class="card" style="padding:20px;margin-bottom:20px;">
<table style="width:100%;max-width:600px;font-size:14px;">
<tr><td style="padding:6px 0;color:#6B7280;width:35%;">المتنازل</td><td style="padding:6px 0;"><a href="/members/<?= (int) $waiver['source_member_id'] ?>" style="color:#0D7377;font-weight:600;"><?= e($waiver['source_name'] ?? '') ?></a></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">رقم العضوية</td><td style="padding:6px 0;font-weight:700;"><?= e($waiver['membership_number'] ?? '—') ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">الحالة</td><td style="padding:6px 0;font-weight:700;color:<?= match($waiver['status']) { 'completed' => '#059669', 'fee_paid' => '#2563EB', 'approved' => '#0284C7', 'rejected' => '#DC2626', default => '#D97706' } ?>;"><?= match($waiver['status']) { 'requested' => 'مقدم', 'approved' => 'معتمد — في انتظار الدفع', 'fee_paid' => 'تم الدفع', 'completed' => 'مكتمل', 'rejected' => 'مرفوض', default => $waiver['status'] } ?></td></tr>
<?php if ($waiver['target_member_id']): ?><tr><td style="padding:6px 0;color:#6B7280;">المتنازل إليه</td><td style="padding:6px 0;"><a href="/members/<?= (int) $waiver['target_member_id'] ?>" style="color:#0D7377;font-weight:600;"><?= e($waiver['target_name'] ?? '') ?> (عضو #<?= (int) $waiver['target_member_id'] ?>)</a></td></tr><?php endif; ?>
<tr><td style="padding:6px 0;color:#6B7280;">عدد التابعين الأصلي</td><td style="padding:6px 0;"><?= (int) ($waiver['original_dependent_count'] ?? 0) ?> (الحد الأقصى المسموح للمتنازل إليه)</td></tr>
<?php if ($waiver['new_dependent_count'] !== null): ?><tr><td style="padding:6px 0;color:#6B7280;">عدد تابعين المتنازل إليه</td><td style="padding:6px 0;"><?= (int) $waiver['new_dependent_count'] ?></td></tr><?php endif; ?>
<tr><td style="padding:6px 0;color:#6B7280;">سداد التجديد السنوي</td><td style="padding:6px 0;"><?= $waiver['annual_renewal_paid'] ? '<span style="color:#059669;">✅ تم السداد</span>' : '<span style="color:#D97706;">⏳ لم يُسدد بعد</span>' ?></td></tr>
<table style="width:100%;max-width:700px;font-size:14px;">
<tr><td style="padding:8px 0;color:#6B7280;width:30%;">المتنازل</td><td style="padding:8px 0;"><a href="/members/<?= (int) $waiver['source_member_id'] ?>" style="color:#0D7377;font-weight:600;"><?= e($waiver['source_name'] ?? '') ?></a></td></tr>
<tr><td style="padding:8px 0;color:#6B7280;">رقم العضوية</td><td style="padding:8px 0;font-weight:700;font-size:16px;"><?= e($waiver['membership_number'] ?? '—') ?></td></tr>
<?php if ($waiver['target_member_id']): ?>
<tr><td style="padding:8px 0;color:#6B7280;">المتنازل إليه</td><td style="padding:8px 0;"><a href="/members/<?= (int) $waiver['target_member_id'] ?>" style="color:#0D7377;font-weight:600;"><?= e($waiver['target_name'] ?? '') ?></a></td></tr>
<?php endif; ?>
<tr><td style="padding:8px 0;color:#6B7280;">تاريخ الطلب</td><td style="padding:8px 0;"><?= arabic_date($waiver['created_at'] ?? '') ?></td></tr>
<?php if ($waiver['board_decision_reference']): ?>
<tr><td style="padding:8px 0;color:#6B7280;">مرجع قرار مجلس الأمناء</td><td style="padding:8px 0;font-weight:600;"><?= e($waiver['board_decision_reference']) ?></td></tr>
<?php endif; ?>
<tr><td style="padding:8px 0;color:#6B7280;">المديونيات</td><td style="padding:8px 0;"><?= $waiver['debts_cleared'] ? '<span style="color:#059669;font-weight:600;">✅ تم التحقق — لا توجد مديونيات</span>' : '<span style="color:#D97706;">⏳ لم يتم التحقق</span>' ?></td></tr>
<?php if ($waiver['notes']): ?>
<tr><td style="padding:8px 0;color:#6B7280;">ملاحظات</td><td style="padding:8px 0;"><?= e($waiver['notes']) ?></td></tr>
<?php endif; ?>
</table>
</div>
<!-- Dependents Comparison -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:15px;color:#0369A1;">👥 مقارنة التابعين</h3>
</div>
<div style="padding:20px;">
<div style="display:grid;grid-template-columns:1fr auto 1fr;gap:15px;align-items:start;">
<!-- Source dependents -->
<div style="text-align:center;">
<div style="font-size:12px;color:#6B7280;margin-bottom:5px;">تابعين المتنازل (الأصلي)</div>
<div style="font-size:28px;font-weight:700;color:#0D7377;"><?= (int) ($waiver['original_dependent_count'] ?? 0) ?></div>
<?php if ($source_deps): ?>
<div style="font-size:11px;color:#6B7280;margin-top:4px;">
<?= $source_deps['spouses'] ?> زوجات — <?= $source_deps['children'] ?> أبناء — <?= $source_deps['temporary'] ?> مؤقتين
</div>
<?php endif; ?>
</div>
<!-- Arrow -->
<div style="text-align:center;padding-top:15px;font-size:24px;color:#6B7280;"></div>
<!-- Target dependents -->
<div style="text-align:center;">
<div style="font-size:12px;color:#6B7280;margin-bottom:5px;">تابعين المتنازل إليه</div>
<?php if ($target_deps): ?>
<?php $excess = $target_deps['total'] - (int) ($waiver['original_dependent_count'] ?? 0); ?>
<div style="font-size:28px;font-weight:700;color:<?= $excess > 0 ? '#DC2626' : '#059669' ?>;"><?= $target_deps['total'] ?></div>
<div style="font-size:11px;color:#6B7280;margin-top:4px;">
<?= $target_deps['spouses'] ?> زوجات — <?= $target_deps['children'] ?> أبناء — <?= $target_deps['temporary'] ?> مؤقتين
</div>
<?php if ($excess > 0): ?>
<div style="margin-top:6px;padding:4px 10px;background:#FEF2F2;border:1px solid #FECACA;border-radius:4px;display:inline-block;">
<span style="color:#DC2626;font-weight:700;font-size:13px;">+<?= $excess ?> تابع زائد</span>
</div>
<?php elseif ($excess <= 0): ?>
<div style="margin-top:6px;padding:4px 10px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:4px;display:inline-block;">
<span style="color:#059669;font-weight:600;font-size:13px;">ضمن الحد المسموح</span>
</div>
<?php endif; ?>
<?php else: ?>
<div style="font-size:28px;font-weight:700;color:#9CA3AF;"></div>
<div style="font-size:11px;color:#9CA3AF;">لم يُحدد المتنازل إليه بعد</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Fee Breakdown -->
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;align-items:center;gap:8px;">
......@@ -20,43 +88,139 @@
<h3 style="margin:0;color:#D97706;font-size:15px;">تفصيل الرسوم</h3>
</div>
<div style="padding:20px;">
<table style="width:100%;max-width:500px;font-size:14px;">
<table style="width:100%;max-width:600px;font-size:14px;">
<tr>
<td style="padding:8px 0;color:#6B7280;">قيمة العضوية وقت التنازل</td>
<td style="padding:8px 0;font-weight:600;direction:ltr;text-align:left;"><?= money($waiver['membership_value_at_waiver'] ?? '0') ?></td>
<td style="padding:8px 0;font-weight:600;text-align:left;"><?= money($waiver['membership_value_at_waiver'] ?? '0') ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">نسبة التنازل</td>
<td style="padding:8px 0;color:#6B7280;">نسبة رسوم التنازل</td>
<td style="padding:8px 0;font-weight:600;"><?= e($waiver['waiver_fee_percentage'] ?? '30') ?>%</td>
</tr>
<tr style="border-bottom:1px dashed #E5E7EB;">
<td style="padding:8px 0;color:#374151;">رسوم التنازل الأساسية</td>
<td style="padding:8px 0;font-weight:700;text-align:left;"><?= money($waiver['waiver_fee_amount'] ?? '0') ?></td>
</tr>
<?php if (bccomp($waiver['excess_fee_amount'] ?? '0', '0', 2) > 0): ?>
<tr style="background:#FEF2F2;">
<td style="padding:8px 0;color:#DC2626;">رسوم تابعين إضافيين (<?= (int) ($waiver['excess_dependent_count'] ?? 0) ?> عضو × <?= e($waiver['excess_fee_percentage'] ?? '0') ?>%)</td>
<td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($waiver['excess_fee_amount']) ?></td>
</tr>
<?php endif; ?>
<tr style="border-top:2px solid #0D7377;">
<td style="padding:12px 0;font-weight:700;font-size:16px;">رسوم التنازل (<?= e($waiver['waiver_fee_percentage'] ?? '30') ?>% من قيمة العضوية)</td>
<td style="padding:12px 0;font-weight:800;font-size:22px;color:#DC2626;direction:ltr;text-align:left;"><?= money($waiver['waiver_fee_amount'] ?? '0') ?></td>
<td style="padding:12px 0;font-weight:700;font-size:16px;">الإجمالي</td>
<td style="padding:12px 0;font-weight:800;font-size:20px;color:#0D7377;text-align:left;">
<?= money(bcadd($waiver['waiver_fee_amount'] ?? '0', $waiver['excess_fee_amount'] ?? '0', 2)) ?>
</td>
</tr>
</table>
</div>
</div>
<?php if (in_array($waiver['status'], ['requested', 'approved']) && bccomp($waiver['waiver_fee_amount'] ?? '0', '0', 2) > 0 && can('payment.collect')): ?>
<!-- Documents -->
<?php if (!empty($waiver['waiver_request_doc_path']) || !empty($waiver['target_form_doc_path'])): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;font-size:15px;color:#374151;">📄 المستندات المرفقة</h3>
</div>
<div style="padding:20px;display:flex;gap:20px;flex-wrap:wrap;">
<?php if (!empty($waiver['waiver_request_doc_path'])): ?>
<a href="/<?= e($waiver['waiver_request_doc_path']) ?>" target="_blank" style="display:flex;align-items:center;gap:8px;padding:10px 16px;background:#F3F4F6;border-radius:8px;text-decoration:none;color:#374151;">
<i data-lucide="file-text" style="width:20px;height:20px;color:#0D7377;"></i>
<span>استمارة طلب التنازل</span>
</a>
<?php endif; ?>
<?php if (!empty($waiver['target_form_doc_path'])): ?>
<a href="/<?= e($waiver['target_form_doc_path']) ?>" target="_blank" style="display:flex;align-items:center;gap:8px;padding:10px 16px;background:#F3F4F6;border-radius:8px;text-decoration:none;color:#374151;">
<i data-lucide="file-text" style="width:20px;height:20px;color:#0D7377;"></i>
<span>استمارة عضوية المتنازل إليه</span>
</a>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- Payment Section -->
<?php if (in_array($waiver['status'], ['requested', 'approved']) && bccomp(bcadd($waiver['waiver_fee_amount'] ?? '0', $waiver['excess_fee_amount'] ?? '0', 2), '0', 2) > 0 && can('payment.collect')): ?>
<div class="card" style="padding:20px;margin-bottom:20px;background:#FFF7ED;border:2px solid #F59E0B;">
<h4 style="margin:0 0 15px;color:#D97706;">💰 دفع رسوم التنازل (<?= e($waiver['waiver_fee_percentage'] ?? '30') ?>% من قيمة العضوية)</h4>
<h4 style="margin:0 0 15px;color:#D97706;">💰 دفع الرسوم</h4>
<form method="POST" action="/waivers/<?= (int) $waiver['id'] ?>/pay">
<?= csrf_field() ?>
<input type="hidden" name="amount" value="<?= e($waiver['waiver_fee_amount']) ?>">
<div style="display:flex;gap:10px;align-items:end;">
<div class="form-group"><label class="form-label">المبلغ</label><input type="text" value="<?= money($waiver['waiver_fee_amount']) ?>" class="form-input" style="background:#F3F4F6;font-weight:700;font-size:18px;" readonly></div>
<div class="form-group"><label class="form-label">طريقة الدفع</label><select name="payment_method" class="form-select"><option value="cash">نقدي</option><option value="visa">فيزا</option><option value="bank_transfer">تحويل</option></select></div>
<button type="submit" class="btn btn-primary" style="padding:10px 25px;" onclick="return confirm('تأكيد دفع <?= money($waiver['waiver_fee_amount']) ?>؟')">💰 ادفع</button>
<div style="display:flex;gap:10px;align-items:end;flex-wrap:wrap;">
<div class="form-group">
<label class="form-label">الإجمالي</label>
<input type="text" value="<?= money(bcadd($waiver['waiver_fee_amount'] ?? '0', $waiver['excess_fee_amount'] ?? '0', 2)) ?>" class="form-input" style="background:#F3F4F6;font-weight:700;font-size:18px;min-width:180px;" readonly>
</div>
<div class="form-group">
<label class="form-label">طريقة الدفع</label>
<select name="payment_method" class="form-select"><option value="cash">نقدي</option><option value="visa">فيزا</option><option value="bank_transfer">تحويل بنكي</option></select>
</div>
<button type="submit" class="btn btn-primary" style="padding:10px 25px;" onclick="return confirm('تأكيد دفع <?= money(bcadd($waiver['waiver_fee_amount'] ?? '0', $waiver['excess_fee_amount'] ?? '0', 2)) ?>؟')">💰 دفع</button>
</div>
</form>
</div>
<?php endif; ?>
<!-- Board Approval Section (with excess fee input) -->
<?php if ($waiver['status'] === 'requested' && can('waiver.approve')): ?>
<div class="card" style="padding:20px;margin-bottom:20px;background:#EFF6FF;border:2px solid #3B82F6;">
<h4 style="margin:0 0 15px;color:#1D4ED8;">🏛 اعتماد مجلس الأمناء</h4>
<form method="POST" action="/waivers/<?= (int) $waiver['id'] ?>/approve" id="approve-form">
<?= csrf_field() ?>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;">
<div class="form-group">
<label class="form-label">مرجع قرار مجلس الأمناء</label>
<input type="text" name="board_decision_reference" class="form-input" placeholder="مثال: قرار جلسة 2026/06/15">
</div>
<?php if ($target_deps && $target_deps['total'] > (int) ($waiver['original_dependent_count'] ?? 0)): ?>
<div class="form-group">
<label class="form-label" style="color:#DC2626;">نسبة رسوم التابعين الزائدين (%) <span style="color:#DC2626;">*</span></label>
<input type="number" name="excess_fee_percentage" class="form-input" step="0.01" min="0" max="100" placeholder="مثال: 30" style="border-color:#DC2626;">
<small style="color:#DC2626;">
يوجد <?= $target_deps['total'] - (int) $waiver['original_dependent_count'] ?> تابع زائد — النسبة × قيمة العضوية (<?= money($waiver['membership_value_at_waiver'] ?? '0') ?>) × عدد الزائدين
</small>
</div>
<?php else: ?>
<div class="form-group">
<label class="form-label">نسبة رسوم تابعين إضافية (%)</label>
<input type="number" name="excess_fee_percentage" class="form-input" step="0.01" min="0" max="100" placeholder="0 إذا لم يوجد زيادة">
<small style="color:#6B7280;">اتركه فارغاً إذا لم يوجد تابعين زائدين</small>
</div>
<?php endif; ?>
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary" onclick="return confirm('اعتماد طلب التنازل؟')">✅ اعتماد</button>
<button type="button" class="btn btn-outline" style="color:#DC2626;" onclick="document.getElementById('reject-modal').style.display='block'">❌ رفض</button>
</div>
</form>
</div>
<!-- Reject modal -->
<div id="reject-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:1000;display:none;align-items:center;justify-content:center;">
<div style="background:#fff;padding:30px;border-radius:12px;max-width:450px;width:90%;">
<h4 style="margin:0 0 15px;color:#DC2626;">❌ رفض طلب التنازل</h4>
<form method="POST" action="/waivers/<?= (int) $waiver['id'] ?>/reject">
<?= csrf_field() ?>
<div class="form-group">
<label class="form-label">سبب الرفض</label>
<textarea name="rejection_reason" class="form-textarea" rows="3" placeholder="أدخل سبب الرفض..."></textarea>
</div>
<div style="display:flex;gap:10px;margin-top:15px;">
<button type="submit" class="btn btn-primary" style="background:#DC2626;">تأكيد الرفض</button>
<button type="button" class="btn btn-outline" onclick="document.getElementById('reject-modal').style.display='none'">إلغاء</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
<!-- Complete Section -->
<?php if (in_array($waiver['status'], ['approved', 'fee_paid']) && can('waiver.approve')): ?>
<div class="card" style="padding:20px;margin-bottom:20px;background:#ECFDF5;border:2px solid #059669;">
<h4 style="margin:0 0 15px;color:#059669;">👤 تحديد المستفيد (المتنازل إليه)</h4>
<h4 style="margin:0 0 15px;color:#059669;">✅ إتمام التنازل ونقل العضوية</h4>
<form method="POST" action="/waivers/<?= (int) $waiver['id'] ?>/complete" id="complete-form">
<?= csrf_field() ?>
<?php if (!$waiver['target_member_id']): ?>
<div style="margin-bottom:15px;">
<label class="form-label">بحث عن المستفيد <span style="color:#DC2626;">*</span></label>
<div style="position:relative;">
......@@ -74,25 +238,32 @@
<button type="button" onclick="clearTarget()" style="background:none;border:none;color:#DC2626;cursor:pointer;font-size:18px;"></button>
</div>
</div>
<input type="hidden" name="target_member_id" id="target-member-id" value="<?= (int) ($waiver['target_member_id'] ?? 0) ?>">
<?php if (!empty($waiver['target_member_id']) && !empty($waiver['target_name'])): ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('target-name').textContent = <?= json_encode($waiver['target_name'], JSON_UNESCAPED_UNICODE) ?>;
document.getElementById('target-info').textContent = 'عضو #<?= (int) $waiver['target_member_id'] ?>';
document.getElementById('target-selected').style.display = 'block';
});
</script>
<?php else: ?>
<div style="padding:12px 16px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;margin-bottom:15px;">
<span style="font-weight:700;color:#166534;"><?= e($waiver['target_name'] ?? '') ?></span>
<span style="color:#6B7280;font-size:12px;margin-right:10px;">عضو #<?= (int) $waiver['target_member_id'] ?></span>
</div>
<?php endif; ?>
<input type="hidden" name="target_member_id" id="target-member-id" value="<?= (int) ($waiver['target_member_id'] ?? 0) ?>">
<div style="display:flex;gap:10px;align-items:center;">
<button type="submit" class="btn btn-primary" onclick="return validateComplete()">✅ إتمام التنازل ونقل العضوية</button>
<span style="font-size:12px;color:#6B7280;">أو <a href="/members/create?waiver_id=<?= (int) $waiver['id'] ?>" style="color:#0D7377;">أنشئ عضو جديد</a> ثم ارجع هنا</span>
<?php if (!$waiver['target_member_id']): ?>
<span style="font-size:12px;color:#6B7280;">أو <a href="/members/create" target="_blank" style="color:#0D7377;">أنشئ عضو جديد</a> ثم ارجع هنا</span>
<?php endif; ?>
</div>
</form>
</div>
<?php endif; ?>
<div style="margin-top:20px;">
<a href="/waivers" class="btn btn-outline">← رجوع للقائمة</a>
</div>
<?php if (in_array($waiver['status'], ['approved', 'fee_paid']) || (!$waiver['target_member_id'] && $waiver['status'] === 'requested')): ?>
<script>
(function() {
var searchInput = document.getElementById('target-search');
if (!searchInput) return;
var resultsDiv = document.getElementById('target-results');
var selectedDiv = document.getElementById('target-selected');
var hiddenInput = document.getElementById('target-member-id');
......@@ -151,20 +322,15 @@ function clearTarget() {
}
function validateComplete() {
if (!document.getElementById('target-member-id').value || document.getElementById('target-member-id').value === '0') {
var tid = document.getElementById('target-member-id');
if (!tid || !tid.value || tid.value === '0') {
alert('يجب تحديد المستفيد (المتنازل إليه) أولاً');
return false;
}
return confirm('⚠ إتمام التنازل — سيتم نقل العضوية رقم <?= e($waiver['membership_number'] ?? '') ?> للمستفيد. هل أنت متأكد؟');
return confirm('⚠ إتمام التنازل — سيتم نقل العضوية رقم <?= e($waiver['membership_number'] ?? '') ?> للمستفيد وأرشفة العضو الأصلي نهائياً. هل أنت متأكد؟');
}
</script>
<?php endif; ?>
<?php if ($waiver['status'] === 'requested'): ?>
<div style="margin-top:15px;display:flex;gap:10px;">
<form method="POST" action="/waivers/<?= (int) $waiver['id'] ?>/approve"><?= csrf_field() ?><button type="submit" class="btn btn-primary" onclick="return confirm('اعتماد التنازل؟')">✅ اعتماد مجلس الأمناء</button></form>
<form method="POST" action="/waivers/<?= (int) $waiver['id'] ?>/reject"><?= csrf_field() ?><button type="submit" class="btn btn-outline" style="color:#DC2626;" onclick="return confirm('رفض التنازل؟')">❌ رفض</button></form>
</div>
<?php endif; ?>
<script>document.addEventListener('DOMContentLoaded', function() { if (typeof lucide !== 'undefined') lucide.createIcons(); });</script>
<?php $__template->endSection(); ?>
\ No newline at end of file
<?php $__template->endSection(); ?>
<?php
declare(strict_types=1);
return [
'up' => "
ALTER TABLE waiver_requests
ADD COLUMN excess_dependent_count INT UNSIGNED NOT NULL DEFAULT 0 AFTER new_dependent_count,
ADD COLUMN excess_fee_percentage DECIMAL(5,2) NULL AFTER excess_dependent_count,
ADD COLUMN excess_fee_amount DECIMAL(15,2) NOT NULL DEFAULT 0.00 AFTER excess_fee_percentage,
ADD COLUMN waiver_request_doc_path VARCHAR(500) NULL AFTER notes,
ADD COLUMN target_form_doc_path VARCHAR(500) NULL AFTER waiver_request_doc_path,
ADD COLUMN debts_cleared TINYINT(1) NOT NULL DEFAULT 0 AFTER annual_renewal_paid;
",
'down' => "
ALTER TABLE waiver_requests
DROP COLUMN excess_dependent_count,
DROP COLUMN excess_fee_percentage,
DROP COLUMN excess_fee_amount,
DROP COLUMN waiver_request_doc_path,
DROP COLUMN target_form_doc_path,
DROP COLUMN debts_cleared;
",
];
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