Commit c8fae48c authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(waiver): per-individual fee assignment + fix 404 payment button

- New table `waiver_individual_fees` stores fee per person (not per category)
- Board approval screen shows each excess person as a separate card with:
  name, DOB, age, age category, relationship, independent fee type/rate
- Children 25+ flagged with warning and "فصل العضوية" button
- Live JS calculates per-person amounts and updates grand total instantly
- Fee breakdown section shows individual names when individual fees exist
- Fix: /members/{id}/financial → /payments/process/{id} (was 404)
- WaiverProcessor::getExcessIndividuals() identifies the specific excess persons
- WaiverProcessor::saveIndividualFees() persists per-person board decisions
- Age categories expanded: under_12, 12_to_16, 16_to_18, 18_to_25, 25_plus
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 83f24d7a
......@@ -161,6 +161,15 @@ class WaiverController extends Controller
$sourceDebtCheck = WaiverProcessor::checkDebtsComprehensive((int) $waiver['source_member_id']);
$targetDebtCheck = $waiver['target_member_id'] ? WaiverProcessor::checkDebtsComprehensive((int) $waiver['target_member_id']) : null;
// Per-individual excess persons for board fee assignment
$excessIndividuals = [];
if ($waiver['target_member_id'] && $comparison && $comparison['has_excess']) {
$excessIndividuals = WaiverProcessor::getExcessIndividuals((int) $waiver['target_member_id'], $originalDeps);
}
// Already-saved individual fees
$individualFees = WaiverProcessor::getIndividualFees((int) $id);
return $this->view('Waiver.Views.show', [
'waiver' => $waiver,
'source_deps' => $sourceDeps,
......@@ -171,6 +180,8 @@ class WaiverController extends Controller
'comparison' => $comparison,
'source_debt_check' => $sourceDebtCheck,
'target_debt_check' => $targetDebtCheck,
'excess_individuals' => $excessIndividuals,
'individual_fees' => $individualFees,
]);
}
......@@ -182,17 +193,14 @@ class WaiverController extends Controller
$employee = App::getInstance()->currentEmployee();
$boardRef = trim($request->post('board_decision_reference', ''));
$membershipValue = $waiver['membership_value_at_waiver'];
$membershipValue = (string) $waiver['membership_value_at_waiver'];
// Per-category fee configuration from board
$spouseFeeType = $request->post('spouse_fee_type', '') ?: null;
$spouseFeeRate = trim($request->post('spouse_fee_rate', ''));
$childFeeType = $request->post('child_fee_type', '') ?: null;
$childFeeRate = trim($request->post('child_fee_rate', ''));
$tempFeeType = $request->post('temporary_fee_type', '') ?: null;
$tempFeeRate = trim($request->post('temporary_fee_rate', ''));
$originalDeps = [
'spouses' => (int) ($waiver['original_spouses_count'] ?? 0),
'children' => (int) ($waiver['original_children_count'] ?? 0),
'temporary' => (int) ($waiver['original_temporary_count'] ?? 0),
];
// Calculate per-category excess
$excessSpouses = 0;
$excessChildren = 0;
$excessTemporary = 0;
......@@ -200,33 +208,51 @@ class WaiverController extends Controller
$childFeeTotal = '0.00';
$tempFeeTotal = '0.00';
$originalDeps = [
'spouses' => (int) ($waiver['original_spouses_count'] ?? 0),
'children' => (int) ($waiver['original_children_count'] ?? 0),
'temporary' => (int) ($waiver['original_temporary_count'] ?? 0),
];
if ($waiver['target_member_id']) {
$targetDeps = WaiverProcessor::countDependents((int) $waiver['target_member_id']);
$comparison = WaiverProcessor::compareDependents($originalDeps, $targetDeps);
$excessSpouses = $comparison['excess_spouses'];
$excessChildren = $comparison['excess_children'];
$excessTemporary = $comparison['excess_temporary'];
$feeConfig = [
'spouse_fee_type' => $spouseFeeType,
'spouse_fee_rate' => $spouseFeeRate,
'child_fee_type' => $childFeeType,
'child_fee_rate' => $childFeeRate,
'temporary_fee_type' => $tempFeeType,
'temporary_fee_rate' => $tempFeeRate,
// Collect per-individual fees from form (arrays indexed by person key)
$feeTypes = $request->post('ind_fee_type', []);
$feeRates = $request->post('ind_fee_rate', []);
$personTypes = $request->post('ind_person_type', []);
$personIds = $request->post('ind_person_id', []);
$personNames = $request->post('ind_person_name', []);
$personOrders = $request->post('ind_person_order', []);
$personAges = $request->post('ind_age_years', []);
$personAgeCodes = $request->post('ind_age_category_code', []);
$personDobs = $request->post('ind_date_of_birth', []);
$personRelations = $request->post('ind_relationship', []);
$personStatuses = $request->post('ind_status', []);
$feesData = [];
if (is_array($feeTypes)) {
foreach ($feeTypes as $idx => $fType) {
$feesData[] = [
'person_type' => $personTypes[$idx] ?? '',
'person_id' => (int) ($personIds[$idx] ?? 0),
'person_name' => $personNames[$idx] ?? '',
'person_order' => (int) ($personOrders[$idx] ?? 1),
'fee_type' => $fType ?: null,
'fee_rate' => $feeRates[$idx] ?? '0',
'age_years' => ($personAges[$idx] ?? '') !== '' ? (int) $personAges[$idx] : null,
'age_category_code' => $personAgeCodes[$idx] ?? null,
'date_of_birth' => $personDobs[$idx] ?? null,
'relationship' => $personRelations[$idx] ?? null,
'status' => $personStatuses[$idx] ?? null,
];
}
}
$fees = WaiverProcessor::calculateExcessFees($comparison, $feeConfig, (string) $membershipValue);
$spouseFeeTotal = $fees['spouse_fee_total'];
$childFeeTotal = $fees['child_fee_total'];
$tempFeeTotal = $fees['temporary_fee_total'];
if (!empty($feesData)) {
$totals = WaiverProcessor::saveIndividualFees((int) $id, $feesData, $membershipValue);
$spouseFeeTotal = $totals['spouse_total'];
$childFeeTotal = $totals['child_total'];
$tempFeeTotal = $totals['temporary_total'];
}
}
$totalExcessFee = bcadd(bcadd($spouseFeeTotal, $childFeeTotal, 2), $tempFeeTotal, 2);
......@@ -243,14 +269,8 @@ class WaiverController extends Controller
'excess_temporary_count' => $excessTemporary,
'excess_fee_percentage' => null,
'excess_fee_amount' => $totalExcessFee,
'spouse_fee_type' => $spouseFeeType,
'spouse_fee_rate' => ($spouseFeeRate !== '' && is_numeric($spouseFeeRate)) ? $spouseFeeRate : null,
'spouse_fee_total' => $spouseFeeTotal,
'child_fee_type' => $childFeeType,
'child_fee_rate' => ($childFeeRate !== '' && is_numeric($childFeeRate)) ? $childFeeRate : null,
'child_fee_total' => $childFeeTotal,
'temporary_fee_type' => $tempFeeType,
'temporary_fee_rate' => ($tempFeeRate !== '' && is_numeric($tempFeeRate)) ? $tempFeeRate : null,
'temporary_fee_total' => $tempFeeTotal,
'updated_at' => date('Y-m-d H:i:s'),
];
......
......@@ -361,6 +361,180 @@ final class WaiverProcessor
return bcmul($perUnit, (string) $excessCount, 2);
}
/**
* Identify excess dependents (individual persons) for per-person fee assignment.
* Compares target dependents vs original counts — returns the EXTRA individuals.
*/
public static function getExcessIndividuals(int $targetMemberId, array $originalCounts): array
{
$db = App::getInstance()->db();
$today = new \DateTime();
$excess = [];
// Spouses: if target has more than original, the LAST ones are considered excess
$spouses = $db->select(
"SELECT id, full_name_ar, national_id, status FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY id",
[$targetMemberId]
);
$allowedSpouses = $originalCounts['spouses'] ?? 0;
if (count($spouses) > $allowedSpouses) {
$excessSpouses = array_slice($spouses, $allowedSpouses);
$order = 1;
foreach ($excessSpouses as $s) {
$excess[] = [
'person_type' => 'spouse',
'person_id' => (int) $s['id'],
'person_name' => $s['full_name_ar'] ?? '',
'person_order' => $order++,
'national_id' => $s['national_id'] ?? '',
'status' => $s['status'] ?? 'active',
'age_years' => null,
'age_category' => null,
'age_category_code' => null,
'date_of_birth' => null,
'relationship' => null,
];
}
}
// Children: last N are excess
$children = $db->select(
"SELECT id, full_name_ar, national_id, date_of_birth, gender, relationship, classification, status
FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY date_of_birth",
[$targetMemberId]
);
$allowedChildren = $originalCounts['children'] ?? 0;
if (count($children) > $allowedChildren) {
$excessChildren = array_slice($children, $allowedChildren);
$order = 1;
foreach ($excessChildren as $c) {
$dob = !empty($c['date_of_birth']) ? new \DateTime($c['date_of_birth']) : null;
$ageYears = $dob ? $dob->diff($today)->y : null;
$ageCat = null;
$ageCatCode = null;
if ($ageYears !== null) {
if ($ageYears < 12) { $ageCat = 'أقل من 12 سنة'; $ageCatCode = 'under_12'; }
elseif ($ageYears < 16) { $ageCat = 'من 12 إلى أقل من 16 سنة'; $ageCatCode = '12_to_16'; }
elseif ($ageYears < 18) { $ageCat = 'من 16 إلى أقل من 18 سنة'; $ageCatCode = '16_to_18'; }
elseif ($ageYears < 25) { $ageCat = 'من 18 إلى أقل من 25 سنة'; $ageCatCode = '18_to_25'; }
else { $ageCat = '25 سنة فأكثر'; $ageCatCode = '25_plus'; }
}
$excess[] = [
'person_type' => 'child',
'person_id' => (int) $c['id'],
'person_name' => $c['full_name_ar'] ?? '',
'person_order' => $order++,
'national_id' => $c['national_id'] ?? '',
'status' => $c['status'] ?? 'active',
'age_years' => $ageYears,
'age_category' => $ageCat,
'age_category_code' => $ageCatCode,
'date_of_birth' => $c['date_of_birth'] ?? null,
'relationship' => $c['relationship'] ?? null,
'gender' => $c['gender'] ?? null,
'classification' => $c['classification'] ?? null,
];
}
}
// Temporary: last N are excess
$temps = $db->select(
"SELECT id, full_name_ar, national_id, status FROM temporary_members WHERE member_id = ? AND is_archived = 0 ORDER BY id",
[$targetMemberId]
);
$allowedTemps = $originalCounts['temporary'] ?? 0;
if (count($temps) > $allowedTemps) {
$excessTemps = array_slice($temps, $allowedTemps);
$order = 1;
foreach ($excessTemps as $t) {
$excess[] = [
'person_type' => 'temporary',
'person_id' => (int) $t['id'],
'person_name' => $t['full_name_ar'] ?? '',
'person_order' => $order++,
'national_id' => $t['national_id'] ?? '',
'status' => $t['status'] ?? 'active',
'age_years' => null,
'age_category' => null,
'age_category_code' => null,
'date_of_birth' => null,
'relationship' => null,
];
}
}
return $excess;
}
/**
* Save per-individual fees set by the board.
*/
public static function saveIndividualFees(int $waiverId, array $fees, string $membershipValue): array
{
$db = App::getInstance()->db();
// Clear old fees for this waiver
$db->delete('waiver_individual_fees', '`waiver_request_id` = ?', [$waiverId]);
$spouseTotal = '0.00';
$childTotal = '0.00';
$tempTotal = '0.00';
foreach ($fees as $fee) {
$feeType = $fee['fee_type'] ?? null;
$feeRate = $fee['fee_rate'] ?? '0';
$feeAmount = '0.00';
if ($feeType === 'percentage' && is_numeric($feeRate)) {
$feeAmount = bcdiv(bcmul($membershipValue, (string) $feeRate, 4), '100', 2);
} elseif ($feeType === 'fixed' && is_numeric($feeRate)) {
$feeAmount = number_format((float) $feeRate, 2, '.', '');
}
$db->insert('waiver_individual_fees', [
'waiver_request_id' => $waiverId,
'person_type' => $fee['person_type'],
'person_id' => (int) $fee['person_id'],
'person_name' => $fee['person_name'] ?? '',
'person_order' => (int) ($fee['person_order'] ?? 1),
'fee_type' => $feeType ?: null,
'fee_rate' => is_numeric($feeRate) ? $feeRate : null,
'fee_amount' => $feeAmount,
'age_years' => $fee['age_years'] ?? null,
'age_category' => $fee['age_category_code'] ?? $fee['age_category'] ?? null,
'date_of_birth' => $fee['date_of_birth'] ?? null,
'relationship' => $fee['relationship'] ?? null,
'status' => $fee['status'] ?? null,
'notes' => $fee['notes'] ?? null,
]);
if ($fee['person_type'] === 'spouse') $spouseTotal = bcadd($spouseTotal, $feeAmount, 2);
elseif ($fee['person_type'] === 'child') $childTotal = bcadd($childTotal, $feeAmount, 2);
elseif ($fee['person_type'] === 'temporary') $tempTotal = bcadd($tempTotal, $feeAmount, 2);
}
$grandTotal = bcadd(bcadd($spouseTotal, $childTotal, 2), $tempTotal, 2);
return [
'spouse_total' => $spouseTotal,
'child_total' => $childTotal,
'temporary_total' => $tempTotal,
'grand_total' => $grandTotal,
];
}
/**
* Load saved per-individual fees for a waiver.
*/
public static function getIndividualFees(int $waiverId): array
{
$db = App::getInstance()->db();
return $db->select(
"SELECT * FROM waiver_individual_fees WHERE waiver_request_id = ? ORDER BY person_type, person_order",
[$waiverId]
);
}
private static function getPersonLabel(?string $personType, string $personName): string
{
return match ($personType) {
......
......@@ -49,7 +49,7 @@
</tfoot>
</table>
<div style="margin-top:12px;display:flex;align-items:center;gap:12px;">
<a href="/members/<?= (int) $member['id'] ?>/financial" class="btn btn-outline" style="padding:8px 16px;font-size:13px;color:#DC2626;border-color:#DC2626;">
<a href="/payments/process/<?= (int) $member['id'] ?>" class="btn btn-outline" style="padding:8px 16px;font-size:13px;color:#DC2626;border-color:#DC2626;">
الانتقال إلى السداد ←
</a>
<span style="font-size:12px;color:#7F1D1D;">يجب سداد جميع المبالغ المستحقة (بما فيها مديونيات التابعين) قبل تقديم طلب التنازل.</span>
......@@ -188,7 +188,7 @@
</form>
<?php else: ?>
<div style="padding:20px;text-align:center;">
<a href="/members/<?= (int) $member['id'] ?>/financial" class="btn btn-primary" style="background:#DC2626;">الانتقال إلى السداد</a>
<a href="/payments/process/<?= (int) $member['id'] ?>" class="btn btn-primary" style="background:#DC2626;">الانتقال إلى السداد</a>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">رجوع</a>
</div>
<?php endif; ?>
......
......@@ -63,7 +63,7 @@ $statusIcon = match($waiver['status']) { 'requested' => '⏳', 'approved' => '
</tfoot>
</table>
<div style="margin-top:10px;text-align:left;">
<a href="/members/<?= (int) $waiver['source_member_id'] ?>/financial" class="btn btn-outline" style="padding:6px 14px;font-size:12px;color:#DC2626;border-color:#DC2626;">
<a href="/payments/process/<?= (int) $waiver['source_member_id'] ?>" class="btn btn-outline" style="padding:6px 14px;font-size:12px;color:#DC2626;border-color:#DC2626;">
الانتقال إلى السداد ←
</a>
</div>
......@@ -102,7 +102,7 @@ $statusIcon = match($waiver['status']) { 'requested' => '⏳', 'approved' => '
</tfoot>
</table>
<div style="margin-top:10px;text-align:left;">
<a href="/members/<?= (int) $waiver['target_member_id'] ?>/financial" class="btn btn-outline" style="padding:6px 14px;font-size:12px;color:#DC2626;border-color:#DC2626;">
<a href="/payments/process/<?= (int) $waiver['target_member_id'] ?>" class="btn btn-outline" style="padding:6px 14px;font-size:12px;color:#DC2626;border-color:#DC2626;">
الانتقال إلى السداد ←
</a>
</div>
......@@ -292,57 +292,72 @@ $statusIcon = match($waiver['status']) { 'requested' => '⏳', 'approved' => '
$childFeeTotal = $waiver['child_fee_total'] ?? '0.00';
$tempFeeTotal = $waiver['temporary_fee_total'] ?? '0.00';
$hasPerCategoryFees = bccomp(bcadd(bcadd($spouseFeeTotal, $childFeeTotal, 2), $tempFeeTotal, 2), '0', 2) > 0;
$hasIndividualFees = !empty($individual_fees);
?>
<?php if ($hasPerCategoryFees): ?>
<?php if (bccomp($spouseFeeTotal, '0', 2) > 0): ?>
<?php if ($hasIndividualFees): ?>
<!-- Per-individual fee breakdown -->
<?php
$indSpouses = array_filter($individual_fees, fn($f) => $f['person_type'] === 'spouse');
$indChildren = array_filter($individual_fees, fn($f) => $f['person_type'] === 'child');
$indTemps = array_filter($individual_fees, fn($f) => $f['person_type'] === 'temporary');
?>
<?php if (!empty($indSpouses)): ?>
<tr style="background:#FDF2F8;"><td colspan="3" style="padding:6px 0;color:#9D174D;font-weight:700;font-size:12px;">👩 رسوم الزوجات الإضافيات</td></tr>
<?php foreach ($indSpouses as $fee): ?>
<tr style="background:#FEF2F2;">
<td style="padding:8px 0;color:#DC2626;font-weight:600;">👩 رسوم زوجات إضافيات (<?= (int) ($waiver['excess_spouses_count'] ?? 0) ?>)</td>
<td style="padding:8px 0;font-size:12px;color:#7F1D1D;">
<?= (int) ($waiver['excess_spouses_count'] ?? 0) ?> x
<?php if (($waiver['spouse_fee_type'] ?? '') === 'percentage'): ?>
<?= e($waiver['spouse_fee_rate'] ?? '0') ?>% من <?= money($waiver['membership_value_at_waiver'] ?? '0') ?>
<?php else: ?>
<?= money($waiver['spouse_fee_rate'] ?? '0') ?> (مبلغ ثابت)
<?php endif; ?>
<td style="padding:6px 0;color:#DC2626;font-weight:600;font-size:13px;"><?= e($fee['person_name']) ?></td>
<td style="padding:6px 0;font-size:12px;color:#7F1D1D;">
<?= ($fee['fee_type'] ?? '') === 'percentage' ? e($fee['fee_rate'] ?? '0') . '% من قيمة العضوية' : money($fee['fee_rate'] ?? '0') . ' (ثابت)' ?>
</td>
<td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($spouseFeeTotal) ?></td>
<td style="padding:6px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($fee['fee_amount'] ?? '0') ?></td>
</tr>
<?php endforeach; ?>
<tr style="border-bottom:1px dashed #FECACA;"><td colspan="2" style="padding:4px 0;font-size:12px;color:#7F1D1D;font-weight:600;">إجمالي الزوجات</td><td style="padding:4px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($spouseFeeTotal) ?></td></tr>
<?php endif; ?>
<?php if (bccomp($childFeeTotal, '0', 2) > 0): ?>
<?php if (!empty($indChildren)): ?>
<tr style="background:#EFF6FF;"><td colspan="3" style="padding:6px 0;color:#1E40AF;font-weight:700;font-size:12px;">👶 رسوم الأبناء الإضافيين</td></tr>
<?php foreach ($indChildren as $fee): ?>
<tr style="background:#FEF2F2;">
<td style="padding:8px 0;color:#DC2626;font-weight:600;">👶 رسوم أبناء إضافيين (<?= (int) ($waiver['excess_children_count'] ?? 0) ?>)</td>
<td style="padding:8px 0;font-size:12px;color:#7F1D1D;">
<?= (int) ($waiver['excess_children_count'] ?? 0) ?> x
<?php if (($waiver['child_fee_type'] ?? '') === 'percentage'): ?>
<?= e($waiver['child_fee_rate'] ?? '0') ?>% من <?= money($waiver['membership_value_at_waiver'] ?? '0') ?>
<?php else: ?>
<?= money($waiver['child_fee_rate'] ?? '0') ?> (مبلغ ثابت)
<?php endif; ?>
<td style="padding:6px 0;color:#DC2626;font-weight:600;font-size:13px;"><?= e($fee['person_name']) ?> <?= $fee['age_years'] ? '(' . (int) $fee['age_years'] . ' سنة)' : '' ?></td>
<td style="padding:6px 0;font-size:12px;color:#7F1D1D;">
<?= ($fee['fee_type'] ?? '') === 'percentage' ? e($fee['fee_rate'] ?? '0') . '% من قيمة العضوية' : money($fee['fee_rate'] ?? '0') . ' (ثابت)' ?>
</td>
<td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($childFeeTotal) ?></td>
<td style="padding:6px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($fee['fee_amount'] ?? '0') ?></td>
</tr>
<?php endforeach; ?>
<tr style="border-bottom:1px dashed #FECACA;"><td colspan="2" style="padding:4px 0;font-size:12px;color:#7F1D1D;font-weight:600;">إجمالي الأبناء</td><td style="padding:4px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($childFeeTotal) ?></td></tr>
<?php endif; ?>
<?php if (bccomp($tempFeeTotal, '0', 2) > 0): ?>
<?php if (!empty($indTemps)): ?>
<tr style="background:#F0FDF4;"><td colspan="3" style="padding:6px 0;color:#166534;font-weight:700;font-size:12px;">👤 رسوم الأعضاء المؤقتين</td></tr>
<?php foreach ($indTemps as $fee): ?>
<tr style="background:#FEF2F2;">
<td style="padding:8px 0;color:#DC2626;font-weight:600;">👤 رسوم أعضاء مؤقتين إضافيين (<?= (int) ($waiver['excess_temporary_count'] ?? 0) ?>)</td>
<td style="padding:8px 0;font-size:12px;color:#7F1D1D;">
<?= (int) ($waiver['excess_temporary_count'] ?? 0) ?> x
<?php if (($waiver['temporary_fee_type'] ?? '') === 'percentage'): ?>
<?= e($waiver['temporary_fee_rate'] ?? '0') ?>% من <?= money($waiver['membership_value_at_waiver'] ?? '0') ?>
<?php else: ?>
<?= money($waiver['temporary_fee_rate'] ?? '0') ?> (مبلغ ثابت)
<?php endif; ?>
<td style="padding:6px 0;color:#DC2626;font-weight:600;font-size:13px;"><?= e($fee['person_name']) ?></td>
<td style="padding:6px 0;font-size:12px;color:#7F1D1D;">
<?= ($fee['fee_type'] ?? '') === 'percentage' ? e($fee['fee_rate'] ?? '0') . '% من قيمة العضوية' : money($fee['fee_rate'] ?? '0') . ' (ثابت)' ?>
</td>
<td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($tempFeeTotal) ?></td>
<td style="padding:6px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($fee['fee_amount'] ?? '0') ?></td>
</tr>
<?php endforeach; ?>
<tr style="border-bottom:1px dashed #FECACA;"><td colspan="2" style="padding:4px 0;font-size:12px;color:#7F1D1D;font-weight:600;">إجمالي المؤقتين</td><td style="padding:4px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($tempFeeTotal) ?></td></tr>
<?php endif; ?>
<?php elseif ($hasPerCategoryFees): ?>
<?php if (bccomp($spouseFeeTotal, '0', 2) > 0): ?>
<tr style="background:#FEF2F2;"><td style="padding:8px 0;color:#DC2626;font-weight:600;">👩 رسوم زوجات إضافيات (<?= (int) ($waiver['excess_spouses_count'] ?? 0) ?>)</td><td style="padding:8px 0;font-size:12px;color:#7F1D1D;"><?= (int) ($waiver['excess_spouses_count'] ?? 0) ?> شخص</td><td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($spouseFeeTotal) ?></td></tr>
<?php endif; ?>
<?php if (bccomp($childFeeTotal, '0', 2) > 0): ?>
<tr style="background:#FEF2F2;"><td style="padding:8px 0;color:#DC2626;font-weight:600;">👶 رسوم أبناء إضافيين (<?= (int) ($waiver['excess_children_count'] ?? 0) ?>)</td><td style="padding:8px 0;font-size:12px;color:#7F1D1D;"><?= (int) ($waiver['excess_children_count'] ?? 0) ?> شخص</td><td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($childFeeTotal) ?></td></tr>
<?php endif; ?>
<?php if (bccomp($tempFeeTotal, '0', 2) > 0): ?>
<tr style="background:#FEF2F2;"><td style="padding:8px 0;color:#DC2626;font-weight:600;">👤 رسوم أعضاء مؤقتين (<?= (int) ($waiver['excess_temporary_count'] ?? 0) ?>)</td><td style="padding:8px 0;font-size:12px;color:#7F1D1D;"><?= (int) ($waiver['excess_temporary_count'] ?? 0) ?> شخص</td><td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($tempFeeTotal) ?></td></tr>
<?php endif; ?>
<?php elseif (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) ?> عضو x <?= e($waiver['excess_fee_percentage'] ?? '0') ?>%)</td>
<td style="padding:8px 0;color:#DC2626;">رسوم تابعين إضافيين (<?= (int) ($waiver['excess_dependent_count'] ?? 0) ?> عضو)</td>
<td style="padding:8px 0;"></td>
<td style="padding:8px 0;font-weight:700;color:#DC2626;text-align:left;"><?= money($waiver['excess_fee_amount']) ?></td>
</tr>
......@@ -407,7 +422,7 @@ $statusIcon = match($waiver['status']) { 'requested' => '⏳', 'approved' => '
</div>
<?php endif; ?>
<!-- SECTION 7: Board Approval (with per-category fee inputs) -->
<!-- SECTION 7: Board Approval (per-individual fee assignment) -->
<?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>
......@@ -419,95 +434,198 @@ $statusIcon = match($waiver['status']) { 'requested' => '⏳', 'approved' => '
<input type="text" name="board_decision_reference" class="form-input" placeholder="مثال: قرار جلسة 2026/06/15">
</div>
<?php if ($target_deps && $comparison && $comparison['has_excess']): ?>
<?php if (!empty($excess_individuals)): ?>
<div style="padding:15px;background:#FEF2F2;border:1px solid #FECACA;border-radius:8px;margin-bottom:15px;">
<strong style="color:#DC2626;">⚠ يوجد تابعين زائدين — يجب تحديد الرسوم لكل فئة:</strong>
<p style="font-size:12px;color:#7F1D1D;margin:5px 0 0;">يمكن تحديد رسوم ثابتة (مبلغ لكل عنصر زائد) أو نسبة مئوية (من قيمة العضوية <?= money($waiver['membership_value_at_waiver'] ?? '0') ?> لكل عنصر زائد)</p>
<strong style="color:#DC2626;">⚠ يوجد تابعين زائدين — يجب تحديد الرسوم لكل فرد على حدة:</strong>
<p style="font-size:12px;color:#7F1D1D;margin:5px 0 0;">قيمة العضوية الحالية: <strong><?= money($waiver['membership_value_at_waiver'] ?? '0') ?></strong> — يمكن تحديد مبلغ ثابت أو نسبة من قيمة العضوية لكل شخص بشكل مستقل</p>
</div>
<!-- Spouse Excess Fee -->
<?php if ($comparison['excess_spouses'] > 0): ?>
<div style="padding:15px;background:#fff;border:1px solid #E5E7EB;border-radius:8px;margin-bottom:12px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<?php
$spouseExcess = array_filter($excess_individuals, fn($p) => $p['person_type'] === 'spouse');
$childExcess = array_filter($excess_individuals, fn($p) => $p['person_type'] === 'child');
$tempExcess = array_filter($excess_individuals, fn($p) => $p['person_type'] === 'temporary');
$indIdx = 0;
?>
<!-- Spouse individuals -->
<?php if (!empty($spouseExcess)): ?>
<div style="margin-bottom:20px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;padding:8px 12px;background:#FDF2F8;border-radius:6px;">
<span style="font-size:18px;">👩</span>
<strong style="color:#374151;">رسوم الزوجات الإضافيات</strong>
<span style="background:#FEF2F2;color:#DC2626;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700;">+<?= $comparison['excess_spouses'] ?> زوجة زائدة</span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="form-group">
<label class="form-label">نوع الرسوم <span style="color:#DC2626;">*</span></label>
<select name="spouse_fee_type" class="form-select" required onchange="updateFeePreview('spouse')">
<strong style="color:#9D174D;">الزوجات الإضافيات (<?= count($spouseExcess) ?>)</strong>
</div>
<?php foreach ($spouseExcess as $person): ?>
<div style="padding:14px;background:#fff;border:1px solid #E5E7EB;border-radius:8px;margin-bottom:10px;" class="ind-fee-card" data-idx="<?= $indIdx ?>">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;">
<strong style="color:#374151;font-size:14px;"><?= e($person['person_name']) ?></strong>
<?php if (!empty($person['national_id'])): ?>
<span style="font-size:11px;color:#6B7280;">رقم قومي: <?= e($person['national_id']) ?></span>
<?php endif; ?>
<span style="background:#F0FDF4;color:#059669;padding:2px 6px;border-radius:3px;font-size:11px;"><?= e($person['status'] ?? 'active') ?></span>
</div>
<input type="hidden" name="ind_person_type[]" value="spouse">
<input type="hidden" name="ind_person_id[]" value="<?= (int) $person['person_id'] ?>">
<input type="hidden" name="ind_person_name[]" value="<?= e($person['person_name']) ?>">
<input type="hidden" name="ind_person_order[]" value="<?= (int) $person['person_order'] ?>">
<input type="hidden" name="ind_age_years[]" value="">
<input type="hidden" name="ind_age_category_code[]" value="">
<input type="hidden" name="ind_date_of_birth[]" value="">
<input type="hidden" name="ind_relationship[]" value="">
<input type="hidden" name="ind_status[]" value="<?= e($person['status'] ?? '') ?>">
<div style="display:grid;grid-template-columns:1fr 1fr 120px;gap:10px;align-items:end;">
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">طريقة الاحتساب</label>
<select name="ind_fee_type[]" class="form-select" required onchange="calcIndFee(<?= $indIdx ?>)">
<option value="">— اختر —</option>
<option value="fixed">مبلغ ثابت لكل زوجة</option>
<option value="percentage">نسبة من قيمة العضوية لكل زوجة</option>
<option value="fixed">مبلغ ثابت</option>
<option value="percentage">نسبة من قيمة العضوية</option>
</select>
</div>
<div class="form-group">
<label class="form-label">القيمة <span style="color:#DC2626;">*</span></label>
<input type="number" name="spouse_fee_rate" id="spouse_fee_rate" class="form-input" step="0.01" min="0" placeholder="المبلغ أو النسبة" required oninput="updateFeePreview('spouse')">
<small style="color:#6B7280;" id="spouse_fee_hint"><?= $comparison['excess_spouses'] ?> زوجة x القيمة = ؟</small>
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">القيمة</label>
<input type="number" name="ind_fee_rate[]" class="form-input" step="0.01" min="0" required oninput="calcIndFee(<?= $indIdx ?>)" data-idx="<?= $indIdx ?>">
</div>
<div style="text-align:center;padding:8px 0;">
<span style="font-size:12px;color:#6B7280;">= </span>
<strong class="ind-fee-result" id="ind-result-<?= $indIdx ?>" style="color:#0D7377;">0.00</strong>
<span style="font-size:11px;color:#6B7280;"> ج.م</span>
</div>
</div>
</div>
<?php $indIdx++; endforeach; ?>
</div>
<?php endif; ?>
<!-- Child Excess Fee -->
<?php if ($comparison['excess_children'] > 0): ?>
<div style="padding:15px;background:#fff;border:1px solid #E5E7EB;border-radius:8px;margin-bottom:12px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<!-- Child individuals -->
<?php if (!empty($childExcess)): ?>
<div style="margin-bottom:20px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;padding:8px 12px;background:#EFF6FF;border-radius:6px;">
<span style="font-size:18px;">👶</span>
<strong style="color:#374151;">رسوم الأبناء الإضافيين</strong>
<span style="background:#FEF2F2;color:#DC2626;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700;">+<?= $comparison['excess_children'] ?> ابن زائد</span>
<strong style="color:#1E40AF;">الأبناء الإضافيون (<?= count($childExcess) ?>)</strong>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="form-group">
<label class="form-label">نوع الرسوم <span style="color:#DC2626;">*</span></label>
<select name="child_fee_type" class="form-select" required onchange="updateFeePreview('child')">
<?php foreach ($childExcess as $person): ?>
<?php
$catColor = match($person['age_category_code'] ?? '') {
'under_12' => '#059669', '12_to_16' => '#0284C7', '16_to_18' => '#D97706', '18_to_25' => '#EA580C', '25_plus' => '#DC2626', default => '#6B7280'
};
$is25Plus = ($person['age_category_code'] ?? '') === '25_plus';
?>
<div style="padding:14px;background:<?= $is25Plus ? '#FEF2F2' : '#fff' ?>;border:1px solid <?= $is25Plus ? '#FECACA' : '#E5E7EB' ?>;border-radius:8px;margin-bottom:10px;" class="ind-fee-card" data-idx="<?= $indIdx ?>">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap;">
<strong style="color:#374151;font-size:14px;"><?= e($person['person_name']) ?></strong>
<?php if ($person['date_of_birth']): ?>
<span style="font-size:11px;color:#6B7280;">م: <?= e($person['date_of_birth']) ?></span>
<?php endif; ?>
<?php if ($person['age_years'] !== null): ?>
<span style="font-size:12px;font-weight:600;color:#374151;">(<?= (int) $person['age_years'] ?> سنة)</span>
<?php endif; ?>
<span style="background:<?= $catColor ?>15;color:<?= $catColor ?>;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;"><?= e($person['age_category'] ?? '') ?></span>
<?php if ($person['relationship']): ?>
<span style="font-size:11px;color:#6B7280;"><?= $person['relationship'] === 'son' ? 'ابن' : 'ابنة' ?></span>
<?php endif; ?>
</div>
<?php if ($is25Plus): ?>
<div style="padding:8px 12px;background:#FEE2E2;border:1px solid #FECACA;border-radius:6px;margin-bottom:10px;display:flex;align-items:center;gap:8px;">
<span style="color:#DC2626;font-size:12px;font-weight:700;">⚠ تجاوز السن المسموح (25 سنة فأكثر) — قد يتطلب فصل العضوية</span>
<a href="/members/<?= (int) $waiver['target_member_id'] ?>" class="btn btn-outline" style="padding:4px 10px;font-size:11px;color:#DC2626;border-color:#DC2626;margin-right:auto;">فصل العضوية</a>
</div>
<?php endif; ?>
<input type="hidden" name="ind_person_type[]" value="child">
<input type="hidden" name="ind_person_id[]" value="<?= (int) $person['person_id'] ?>">
<input type="hidden" name="ind_person_name[]" value="<?= e($person['person_name']) ?>">
<input type="hidden" name="ind_person_order[]" value="<?= (int) $person['person_order'] ?>">
<input type="hidden" name="ind_age_years[]" value="<?= $person['age_years'] !== null ? (int) $person['age_years'] : '' ?>">
<input type="hidden" name="ind_age_category_code[]" value="<?= e($person['age_category_code'] ?? '') ?>">
<input type="hidden" name="ind_date_of_birth[]" value="<?= e($person['date_of_birth'] ?? '') ?>">
<input type="hidden" name="ind_relationship[]" value="<?= e($person['relationship'] ?? '') ?>">
<input type="hidden" name="ind_status[]" value="<?= e($person['status'] ?? '') ?>">
<div style="display:grid;grid-template-columns:1fr 1fr 120px;gap:10px;align-items:end;">
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">طريقة الاحتساب</label>
<select name="ind_fee_type[]" class="form-select" required onchange="calcIndFee(<?= $indIdx ?>)">
<option value="">— اختر —</option>
<option value="fixed">مبلغ ثابت لكل ابن</option>
<option value="percentage">نسبة من قيمة العضوية لكل ابن</option>
<option value="fixed">مبلغ ثابت</option>
<option value="percentage">نسبة من قيمة العضوية</option>
</select>
</div>
<div class="form-group">
<label class="form-label">القيمة <span style="color:#DC2626;">*</span></label>
<input type="number" name="child_fee_rate" id="child_fee_rate" class="form-input" step="0.01" min="0" placeholder="المبلغ أو النسبة" required oninput="updateFeePreview('child')">
<small style="color:#6B7280;" id="child_fee_hint"><?= $comparison['excess_children'] ?> ابن x القيمة = ؟</small>
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">القيمة</label>
<input type="number" name="ind_fee_rate[]" class="form-input" step="0.01" min="0" required oninput="calcIndFee(<?= $indIdx ?>)" data-idx="<?= $indIdx ?>">
</div>
<div style="text-align:center;padding:8px 0;">
<span style="font-size:12px;color:#6B7280;">= </span>
<strong class="ind-fee-result" id="ind-result-<?= $indIdx ?>" style="color:#0D7377;">0.00</strong>
<span style="font-size:11px;color:#6B7280;"> ج.م</span>
</div>
</div>
</div>
<?php $indIdx++; endforeach; ?>
</div>
<?php endif; ?>
<!-- Temporary Excess Fee -->
<?php if ($comparison['excess_temporary'] > 0): ?>
<div style="padding:15px;background:#fff;border:1px solid #E5E7EB;border-radius:8px;margin-bottom:12px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<!-- Temporary individuals -->
<?php if (!empty($tempExcess)): ?>
<div style="margin-bottom:20px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;padding:8px 12px;background:#F0FDF4;border-radius:6px;">
<span style="font-size:18px;">👤</span>
<strong style="color:#374151;">رسوم الأعضاء المؤقتين الإضافيين</strong>
<span style="background:#FEF2F2;color:#DC2626;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:700;">+<?= $comparison['excess_temporary'] ?> مؤقت زائد</span>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
<div class="form-group">
<label class="form-label">نوع الرسوم <span style="color:#DC2626;">*</span></label>
<select name="temporary_fee_type" class="form-select" required onchange="updateFeePreview('temporary')">
<strong style="color:#166534;">الأعضاء المؤقتون الإضافيون (<?= count($tempExcess) ?>)</strong>
</div>
<?php foreach ($tempExcess as $person): ?>
<div style="padding:14px;background:#fff;border:1px solid #E5E7EB;border-radius:8px;margin-bottom:10px;" class="ind-fee-card" data-idx="<?= $indIdx ?>">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;">
<strong style="color:#374151;font-size:14px;"><?= e($person['person_name']) ?></strong>
<?php if (!empty($person['national_id'])): ?>
<span style="font-size:11px;color:#6B7280;">رقم قومي: <?= e($person['national_id']) ?></span>
<?php endif; ?>
<span style="background:#F0FDF4;color:#059669;padding:2px 6px;border-radius:3px;font-size:11px;"><?= e($person['status'] ?? 'active') ?></span>
</div>
<input type="hidden" name="ind_person_type[]" value="temporary">
<input type="hidden" name="ind_person_id[]" value="<?= (int) $person['person_id'] ?>">
<input type="hidden" name="ind_person_name[]" value="<?= e($person['person_name']) ?>">
<input type="hidden" name="ind_person_order[]" value="<?= (int) $person['person_order'] ?>">
<input type="hidden" name="ind_age_years[]" value="">
<input type="hidden" name="ind_age_category_code[]" value="">
<input type="hidden" name="ind_date_of_birth[]" value="">
<input type="hidden" name="ind_relationship[]" value="">
<input type="hidden" name="ind_status[]" value="<?= e($person['status'] ?? '') ?>">
<div style="display:grid;grid-template-columns:1fr 1fr 120px;gap:10px;align-items:end;">
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">طريقة الاحتساب</label>
<select name="ind_fee_type[]" class="form-select" required onchange="calcIndFee(<?= $indIdx ?>)">
<option value="">— اختر —</option>
<option value="fixed">مبلغ ثابت لكل مؤقت</option>
<option value="percentage">نسبة من قيمة العضوية لكل مؤقت</option>
<option value="fixed">مبلغ ثابت</option>
<option value="percentage">نسبة من قيمة العضوية</option>
</select>
</div>
<div class="form-group">
<label class="form-label">القيمة <span style="color:#DC2626;">*</span></label>
<input type="number" name="temporary_fee_rate" id="temporary_fee_rate" class="form-input" step="0.01" min="0" placeholder="المبلغ أو النسبة" required oninput="updateFeePreview('temporary')">
<small style="color:#6B7280;" id="temporary_fee_hint"><?= $comparison['excess_temporary'] ?> مؤقت x القيمة = ؟</small>
<div class="form-group" style="margin:0;">
<label class="form-label" style="font-size:12px;">القيمة</label>
<input type="number" name="ind_fee_rate[]" class="form-input" step="0.01" min="0" required oninput="calcIndFee(<?= $indIdx ?>)" data-idx="<?= $indIdx ?>">
</div>
<div style="text-align:center;padding:8px 0;">
<span style="font-size:12px;color:#6B7280;">= </span>
<strong class="ind-fee-result" id="ind-result-<?= $indIdx ?>" style="color:#0D7377;">0.00</strong>
<span style="font-size:11px;color:#6B7280;"> ج.م</span>
</div>
</div>
</div>
<?php $indIdx++; endforeach; ?>
</div>
<?php endif; ?>
<!-- Fee Preview -->
<div id="fee-preview" style="padding:12px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;margin-bottom:15px;display:none;">
<strong style="color:#166534;">📊 معاينة الرسوم الإضافية:</strong>
<div id="fee-preview-content" style="margin-top:8px;font-size:13px;color:#166534;"></div>
<!-- Live Total Summary -->
<div id="fee-summary" style="padding:15px;background:#F0FDF4;border:2px solid #BBF7D0;border-radius:8px;margin-bottom:15px;">
<strong style="color:#166534;display:block;margin-bottom:8px;">📊 ملخص الرسوم الإضافية:</strong>
<div id="fee-summary-content" style="font-size:13px;color:#166534;">
<div id="sum-spouses" style="display:none;margin-bottom:4px;"></div>
<div id="sum-children" style="display:none;margin-bottom:4px;"></div>
<div id="sum-temporary" style="display:none;margin-bottom:4px;"></div>
<hr style="margin:8px 0;border:none;border-top:1px dashed #BBF7D0;">
<div style="font-weight:700;">إجمالي الرسوم الإضافية: <span id="sum-excess-total">0.00</span> ج.م</div>
<div style="font-weight:700;">+ رسوم التنازل الأساسية: <?= money($waiver['waiver_fee_amount'] ?? '0') ?></div>
<div style="font-weight:700;font-size:16px;color:#0D7377;margin-top:4px;">= الإجمالي النهائي: <span id="sum-grand-total"><?= money($waiver['waiver_fee_amount'] ?? '0') ?></span></div>
</div>
</div>
<?php else: ?>
<div style="padding:12px;background:#F0FDF4;border:1px solid #BBF7D0;border-radius:8px;margin-bottom:15px;">
<span style="color:#059669;font-weight:600;">✅ لا يوجد تابعين زائدين — لا تُفرض رسوم إضافية</span>
......@@ -539,71 +657,59 @@ $statusIcon = match($waiver['status']) { 'requested' => '⏳', 'approved' => '
</div>
</div>
<?php if ($target_deps && $comparison && $comparison['has_excess']): ?>
<?php if (!empty($excess_individuals)): ?>
<script>
var membershipValue = <?= (float) ($waiver['membership_value_at_waiver'] ?? 0) ?>;
var excessCounts = {spouse: <?= $comparison['excess_spouses'] ?>, child: <?= $comparison['excess_children'] ?>, temporary: <?= $comparison['excess_temporary'] ?>};
var waiverBaseFee = <?= (float) ($waiver['waiver_fee_amount'] ?? 0) ?>;
var totalCards = <?= count($excess_individuals) ?>;
function updateFeePreview(category) {
var typeEl = document.querySelector('[name="' + category + '_fee_type"]');
var rateEl = document.getElementById(category + '_fee_rate');
var hintEl = document.getElementById(category + '_fee_hint');
if (!typeEl || !rateEl || !hintEl) return;
function calcIndFee(idx) {
var card = document.querySelector('.ind-fee-card[data-idx="' + idx + '"]');
if (!card) return;
var typeEl = card.querySelector('select[name="ind_fee_type[]"]');
var rateEl = card.querySelector('input[name="ind_fee_rate[]"]');
var resultEl = document.getElementById('ind-result-' + idx);
if (!typeEl || !rateEl || !resultEl) return;
var type = typeEl.value;
var rate = parseFloat(rateEl.value) || 0;
var count = excessCounts[category] || 0;
var perUnit = 0;
var amount = 0;
if (type === 'percentage') {
perUnit = (membershipValue * rate / 100);
hintEl.textContent = count + ' x (' + rate + '% x ' + membershipValue.toLocaleString() + ') = ' + (perUnit * count).toLocaleString() + ' ج.م';
amount = membershipValue * rate / 100;
} else if (type === 'fixed') {
perUnit = rate;
hintEl.textContent = count + ' x ' + rate.toLocaleString() + ' = ' + (rate * count).toLocaleString() + ' ج.م';
} else {
hintEl.textContent = count + ' عضو x القيمة = ؟';
amount = rate;
}
updateTotalPreview();
resultEl.textContent = amount.toLocaleString('en', {minimumFractionDigits: 2, maximumFractionDigits: 2});
updateSummary();
}
function updateTotalPreview() {
var preview = document.getElementById('fee-preview');
var content = document.getElementById('fee-preview-content');
var lines = [];
var total = 0;
var categories = [
{key: 'spouse', label: '👩 زوجات', count: excessCounts.spouse},
{key: 'child', label: '👶 أبناء', count: excessCounts.child},
{key: 'temporary', label: '👤 مؤقتين', count: excessCounts.temporary}
];
categories.forEach(function(cat) {
if (cat.count <= 0) return;
var typeEl = document.querySelector('[name="' + cat.key + '_fee_type"]');
var rateEl = document.getElementById(cat.key + '_fee_rate');
if (!typeEl || !rateEl) return;
var type = typeEl.value;
var rate = parseFloat(rateEl.value) || 0;
if (!type || !rate) return;
var perUnit = type === 'percentage' ? (membershipValue * rate / 100) : rate;
var catTotal = perUnit * cat.count;
total += catTotal;
lines.push(cat.label + ': ' + cat.count + ' x ' + perUnit.toLocaleString() + ' = <strong>' + catTotal.toLocaleString() + ' ج.م</strong>');
function updateSummary() {
var cards = document.querySelectorAll('.ind-fee-card');
var totals = {spouse: 0, child: 0, temporary: 0};
var names = {spouse: [], child: [], temporary: []};
cards.forEach(function(card) {
var idx = parseInt(card.dataset.idx);
var resultEl = document.getElementById('ind-result-' + idx);
var amount = parseFloat((resultEl ? resultEl.textContent : '0').replace(/,/g, '')) || 0;
var personType = card.querySelector('input[name="ind_person_type[]"]').value;
var personName = card.querySelector('input[name="ind_person_name[]"]').value;
totals[personType] = (totals[personType] || 0) + amount;
if (amount > 0) names[personType].push(personName + ': ' + amount.toLocaleString('en', {minimumFractionDigits: 2}) + ' ج.م');
});
if (lines.length > 0) {
lines.push('<hr style="margin:6px 0;border:none;border-top:1px dashed #BBF7D0;">');
lines.push('<strong>إجمالي الرسوم الإضافية: ' + total.toLocaleString() + ' ج.م</strong>');
lines.push('<strong>+ رسوم التنازل الأساسية: <?= $waiver['waiver_fee_amount'] ?? '0' ?> ج.م</strong>');
lines.push('<strong style="font-size:15px;color:#0D7377;">= الإجمالي النهائي: ' + (total + <?= (float) ($waiver['waiver_fee_amount'] ?? 0) ?>).toLocaleString() + ' ج.م</strong>');
content.innerHTML = lines.join('<br>');
preview.style.display = 'block';
} else {
preview.style.display = 'none';
}
var sumSpouses = document.getElementById('sum-spouses');
var sumChildren = document.getElementById('sum-children');
var sumTemp = document.getElementById('sum-temporary');
if (totals.spouse > 0) { sumSpouses.innerHTML = '<strong>👩 الزوجات:</strong> ' + names.spouse.join(' | ') + ' = <strong>' + totals.spouse.toLocaleString('en', {minimumFractionDigits: 2}) + ' ج.م</strong>'; sumSpouses.style.display = 'block'; } else { sumSpouses.style.display = 'none'; }
if (totals.child > 0) { sumChildren.innerHTML = '<strong>👶 الأبناء:</strong> ' + names.child.join(' | ') + ' = <strong>' + totals.child.toLocaleString('en', {minimumFractionDigits: 2}) + ' ج.م</strong>'; sumChildren.style.display = 'block'; } else { sumChildren.style.display = 'none'; }
if (totals.temporary > 0) { sumTemp.innerHTML = '<strong>👤 المؤقتون:</strong> ' + names.temporary.join(' | ') + ' = <strong>' + totals.temporary.toLocaleString('en', {minimumFractionDigits: 2}) + ' ج.م</strong>'; sumTemp.style.display = 'block'; } else { sumTemp.style.display = 'none'; }
var excessTotal = totals.spouse + totals.child + totals.temporary;
document.getElementById('sum-excess-total').textContent = excessTotal.toLocaleString('en', {minimumFractionDigits: 2});
document.getElementById('sum-grand-total').textContent = (excessTotal + waiverBaseFee).toLocaleString('en', {minimumFractionDigits: 2}) + ' ج.م';
}
</script>
<?php endif; ?>
......
<?php
declare(strict_types=1);
return [
'up' => "
CREATE TABLE IF NOT EXISTS waiver_individual_fees (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
waiver_request_id BIGINT UNSIGNED NOT NULL,
person_type ENUM('spouse','child','temporary') NOT NULL,
person_id BIGINT UNSIGNED NOT NULL COMMENT 'FK to spouses/children/temporary_members',
person_name VARCHAR(200) NOT NULL,
person_order TINYINT UNSIGNED NOT NULL DEFAULT 1,
fee_type ENUM('fixed','percentage') NULL,
fee_rate DECIMAL(15,2) NULL COMMENT 'Fixed amount or percentage value',
fee_amount DECIMAL(15,2) NOT NULL DEFAULT 0.00 COMMENT 'Calculated fee for this person',
age_years INT UNSIGNED NULL COMMENT 'Age at time of waiver (children only)',
age_category VARCHAR(50) NULL COMMENT 'under_12, 12_to_16, 16_to_18, 18_to_25, 25_plus',
date_of_birth DATE NULL,
relationship VARCHAR(30) NULL COMMENT 'son/daughter for children',
status VARCHAR(50) NULL COMMENT 'active/frozen/etc',
notes TEXT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_waiver_request (waiver_request_id),
INDEX idx_person (person_type, person_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
",
'down' => "DROP TABLE IF EXISTS waiver_individual_fees;",
];
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