Commit b143aca7 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(members): show separated/deceased children greyed out on parent profile

Children who were separated (فصل) or deceased now remain visible on the
parent member's profile instead of disappearing. They appear greyed out
with status labels (مفصول / متوفي) and for separated children, a link
to their new membership profile with the membership number.

The family tree count distinguishes active vs inactive children.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent ccd9b70a
...@@ -201,15 +201,19 @@ class MemberController extends Controller ...@@ -201,15 +201,19 @@ class MemberController extends Controller
} }
try { try {
$children = $db->select( $children = $db->select(
"SELECT c.*, TIMESTAMPDIFF(YEAR, c.date_of_birth, CURDATE()) as age_years, TIMESTAMPDIFF(MONTH, c.date_of_birth, CURDATE()) % 12 as age_months, pr.created_at AS due_date, p.payment_date "SELECT c.*, TIMESTAMPDIFF(YEAR, c.date_of_birth, CURDATE()) as age_years, TIMESTAMPDIFF(MONTH, c.date_of_birth, CURDATE()) % 12 as age_months,
pr.created_at AS due_date, p.payment_date,
tr.target_member_id as separated_member_id, tm.membership_number as separated_membership_number
FROM children c FROM children c
LEFT JOIN payment_requests pr ON pr.related_entity_type = 'children' AND pr.related_entity_id = c.id AND pr.payment_type = 'addition_fee' AND pr.is_voided = 0 LEFT JOIN payment_requests pr ON pr.related_entity_type = 'children' AND pr.related_entity_id = c.id AND pr.payment_type = 'addition_fee' AND pr.is_voided = 0
LEFT JOIN payments p ON p.id = c.activated_by_payment_id AND p.is_voided = 0 LEFT JOIN payments p ON p.id = c.activated_by_payment_id AND p.is_voided = 0
WHERE c.member_id = ? AND c.is_archived = 0 LEFT JOIN transfer_requests tr ON tr.child_id = c.id AND tr.status = 'completed'
ORDER BY c.child_order", [(int) $id] LEFT JOIN members tm ON tm.id = tr.target_member_id
WHERE c.member_id = ?
ORDER BY c.is_archived ASC, c.child_order", [(int) $id]
); );
} catch (\Throwable $e) { } catch (\Throwable $e) {
try { $children = $db->select("SELECT *, TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) as age_years, TIMESTAMPDIFF(MONTH, date_of_birth, CURDATE()) % 12 as age_months FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order", [(int) $id]); } catch (\Throwable $e2) {} try { $children = $db->select("SELECT *, TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) as age_years, TIMESTAMPDIFF(MONTH, date_of_birth, CURDATE()) % 12 as age_months FROM children WHERE member_id = ? ORDER BY is_archived ASC, child_order", [(int) $id]); } catch (\Throwable $e2) {}
} }
try { try {
$temporaries = $db->select( $temporaries = $db->select(
......
...@@ -966,7 +966,8 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done'])); ...@@ -966,7 +966,8 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done']));
<!-- FAMILY TREE — unified display of all dependants --> <!-- FAMILY TREE — unified display of all dependants -->
<!-- ═══════════════════════════════════════════════ --> <!-- ═══════════════════════════════════════════════ -->
<?php <?php
$totalDependants = count($spouses) + count($children) + count($temporaries ?? []); $activeChildrenCount = count(array_filter($children, fn($c) => empty($c['is_archived']) && !in_array($c['status'] ?? '', ['separated', 'deceased'])));
$totalDependants = count($spouses) + $activeChildrenCount + count($temporaries ?? []);
$categoryLabels = [ $categoryLabels = [
'parent' => 'والد/ة', 'special_needs' => 'ذوي احتياجات خاصة', 'unmarried_daughter' => 'ابنة غير متزوجة', 'parent' => 'والد/ة', 'special_needs' => 'ذوي احتياجات خاصة', 'unmarried_daughter' => 'ابنة غير متزوجة',
'sister' => 'شقيقة', 'stepchild' => 'ابن/ة زوج', 'orphan' => 'يتيم', 'disabled_sibling' => 'شقيق معاق', 'nanny' => 'مربية', 'sister' => 'شقيقة', 'stepchild' => 'ابن/ة زوج', 'orphan' => 'يتيم', 'disabled_sibling' => 'شقيق معاق', 'nanny' => 'مربية',
...@@ -1062,11 +1063,16 @@ $childClassLabels = ['included' => 'تابع مشمول', 'dependent_with_fee' = ...@@ -1062,11 +1063,16 @@ $childClassLabels = ['included' => 'تابع مشمول', 'dependent_with_fee' =
<?php if (!empty($children)): ?> <?php if (!empty($children)): ?>
<!-- Children --> <!-- Children -->
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f476; الأبناء (<?= count($children) ?>)</div> <?php
$activeChildren = array_filter($children, fn($c) => empty($c['is_archived']) && !in_array($c['status'] ?? '', ['separated', 'deceased']));
$inactiveChildren = array_filter($children, fn($c) => !empty($c['is_archived']) || in_array($c['status'] ?? '', ['separated', 'deceased']));
?>
<div style="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">&#x1f476; الأبناء (<?= count($activeChildren) ?><?= !empty($inactiveChildren) ? ' <span style="color:#6B7280;font-weight:500;">+ ' . count($inactiveChildren) . ' سابق</span>' : '' ?>)</div>
<?php <?php
$childrenOver25Male = array_filter($children, function($c) { $childrenOver25Male = array_filter($children, function($c) {
return ($c['gender'] ?? '') === 'male' return ($c['gender'] ?? '') === 'male'
&& ($c['status'] ?? '') === 'active' && ($c['status'] ?? '') === 'active'
&& empty($c['is_archived'])
&& (int) ($c['age_years'] ?? 0) >= 25; && (int) ($c['age_years'] ?? 0) >= 25;
}); });
?> ?>
...@@ -1101,10 +1107,18 @@ $childClassLabels = ['included' => 'تابع مشمول', 'dependent_with_fee' = ...@@ -1101,10 +1107,18 @@ $childClassLabels = ['included' => 'تابع مشمول', 'dependent_with_fee' =
$cFee = $c['addition_fee'] ?? '0.00'; $cFee = $c['addition_fee'] ?? '0.00';
$cBreakdown = !empty($c['fee_breakdown_json']) ? json_decode($c['fee_breakdown_json'], true) : null; $cBreakdown = !empty($c['fee_breakdown_json']) ? json_decode($c['fee_breakdown_json'], true) : null;
if (!is_array($cBreakdown)) $cBreakdown = null; if (!is_array($cBreakdown)) $cBreakdown = null;
$cIsSeparated = ($c['status'] ?? '') === 'separated';
$cIsDeceased = ($c['status'] ?? '') === 'deceased';
$cIsArchived = !empty($c['is_archived']) && !$cIsSeparated && !$cIsDeceased;
$cIsInactive = $cIsArchived || $cIsSeparated || $cIsDeceased;
?> ?>
<tr <?= $cBreakdown ? 'style="cursor:pointer;" onclick="toggleBillDetail(\'child-' . $cIdx . '\')"' : '' ?>> <tr <?= $cBreakdown && !$cIsInactive ? 'style="cursor:pointer;" onclick="toggleBillDetail(\'child-' . $cIdx . '\')"' : '' ?> <?= $cIsInactive ? 'style="opacity:0.5;background:#F9FAFB;"' : '' ?>>
<td><?= (int) $c['child_order'] ?></td> <td><?= (int) $c['child_order'] ?></td>
<td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td> <td style="font-weight:600;"><?= e($c['full_name_ar']) ?>
<?php if ($cIsSeparated && !empty($c['separated_membership_number'])): ?>
<a href="/members/<?= (int) $c['separated_member_id'] ?>" style="display:block;font-size:11px;color:#0D7377;font-weight:600;margin-top:2px;">عضوية #<?= e($c['separated_membership_number']) ?></a>
<?php endif; ?>
</td>
<td><span style="background:#E0F2FE;color:#0369A1;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;"><?= $childClassLabels[$c['classification'] ?? 'included'] ?? 'تابع' ?></span></td> <td><span style="background:#E0F2FE;color:#0369A1;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;"><?= $childClassLabels[$c['classification'] ?? 'included'] ?? 'تابع' ?></span></td>
<td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td> <td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td>
<td><?php <td><?php
...@@ -1119,22 +1133,38 @@ $childClassLabels = ['included' => 'تابع مشمول', 'dependent_with_fee' = ...@@ -1119,22 +1133,38 @@ $childClassLabels = ['included' => 'تابع مشمول', 'dependent_with_fee' =
<td style="font-size:12px;"><?= !empty($c['due_date']) ? e(substr($c['due_date'], 0, 10)) : '<span style="color:#9CA3AF;">—</span>' ?></td> <td style="font-size:12px;"><?= !empty($c['due_date']) ? e(substr($c['due_date'], 0, 10)) : '<span style="color:#9CA3AF;">—</span>' ?></td>
<td style="font-size:12px;"><?= !empty($c['payment_date']) ? e($c['payment_date']) : '<span style="color:#9CA3AF;">—</span>' ?></td> <td style="font-size:12px;"><?= !empty($c['payment_date']) ? e($c['payment_date']) : '<span style="color:#9CA3AF;">—</span>' ?></td>
<td style="font-weight:600;"><?php <td style="font-weight:600;"><?php
if ($cIsInactive) {
echo '<span style="color:#9CA3AF;">—</span>';
} else {
$fee = $c['addition_fee'] ?? '0.00'; $fee = $c['addition_fee'] ?? '0.00';
echo bccomp($fee, '0', 2) <= 0 ? '<span style="color:#059669;">مشمول</span>' : money($fee); echo bccomp($fee, '0', 2) <= 0 ? '<span style="color:#059669;">مشمول</span>' : money($fee);
if ($cBreakdown) echo ' <small style="color:#0D7377;font-size:10px;">&#x25BC;</small>'; if ($cBreakdown) echo ' <small style="color:#0D7377;font-size:10px;">&#x25BC;</small>';
}
?></td> ?></td>
<td><?php <td><?php if ($cIsDeceased): ?>
<span style="color:#DC2626;font-weight:600;font-size:12px;">&#x25cf; متوفي</span>
<?php elseif ($cIsSeparated): ?>
<span style="color:#6B7280;font-weight:600;font-size:12px;">&#x25cf; مفصول</span>
<?php elseif ($cIsArchived): ?>
<span style="color:#6B7280;font-weight:600;font-size:12px;">&#x25cf; محذوف</span>
<?php else:
$cKey = 'children:' . (int) $c['id']; $cKey = 'children:' . (int) $c['id'];
$cInQueue = isset($pendingAdditions[$cKey]); $cInQueue = isset($pendingAdditions[$cKey]);
$cInCombined = (($c['status'] ?? '') === 'pending_payment' && !empty($pendingMembership)); $cInCombined = (($c['status'] ?? '') === 'pending_payment' && !empty($pendingMembership));
if ($cInQueue || $cInCombined): ?> if ($cInQueue || $cInCombined): ?>
<span style="color:#D97706;font-weight:600;font-size:12px;">&#x1f4b3; في الخزينة</span> <span style="color:#D97706;font-weight:600;font-size:12px;">&#x1f4b3; في الخزينة</span>
<?php else: ?> <?php else: ?>
<span style="color:<?= match($c['status'] ?? '') { 'active' => '#059669', 'pending_payment' => '#D97706', 'frozen' => '#6B7280', 'separated' => '#6B7280', default => '#DC2626' } ?>;font-weight:600;font-size:12px;">&#x25cf; <?= match($c['status'] ?? '') { 'active' => 'نشط', 'pending_payment' => 'لم يتم السداد', 'frozen' => 'مجمد', 'separated' => 'منفصل', 'inactive' => 'غير نشط', default => $c['status'] ?? '—' } ?></span> <span style="color:<?= match($c['status'] ?? '') { 'active' => '#059669', 'pending_payment' => '#D97706', 'frozen' => '#6B7280', default => '#DC2626' } ?>;font-weight:600;font-size:12px;">&#x25cf; <?= match($c['status'] ?? '') { 'active' => 'نشط', 'pending_payment' => 'لم يتم السداد', 'frozen' => 'مجمد', 'inactive' => 'غير نشط', default => $c['status'] ?? '—' } ?></span>
<?php endif; ?> <?php endif;
endif; ?>
</td> </td>
<td style="white-space:nowrap;"> <td style="white-space:nowrap;">
<a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td> <?php if ($cIsSeparated && !empty($c['separated_member_id'])): ?>
<a href="/members/<?= (int) $c['separated_member_id'] ?>" class="btn btn-sm btn-outline" style="color:#0D7377;">الملف</a>
<?php elseif (!$cIsInactive): ?>
<a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a>
<?php endif; ?>
</td>
</tr> </tr>
<?php if ($cBreakdown): ?> <?php if ($cBreakdown): ?>
<tr id="bill-detail-child-<?= $cIdx ?>" style="display:none;"> <tr id="bill-detail-child-<?= $cIdx ?>" style="display:none;">
......
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