Commit ccd9b70a authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix(transfers): show separation origin on child profile and translate audit labels to Arabic

- Add transferred_from_transfer_id column to track child separation origin
- TransferProcessor sets the field when completing child separations
- Member profile detects separation transfers and shows: parent name,
  separation date, actual paid fees (not full membership_value)
- Financial summary shows "رسوم الفصل المدفوعة" for separated children
- Transfer status labels: "requested" now shows "في انتظار الدفع"
- AuditService: comprehensive Arabic labels for statuses, fields, and
  formatted values (payment_method, membership_type, classification, etc.)
- Backfill migration links existing completed transfers to their target members
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent cb2f1384
This diff is collapsed.
......@@ -319,9 +319,28 @@ class MemberController extends Controller
[(int) $id]
);
// Check if this member was created via child separation transfer
$separationTransfer = null;
if (!empty($member->transferred_from_transfer_id)) {
$separationTransfer = $db->selectOne(
"SELECT tr.id, tr.source_member_id, tr.total_fee, tr.separation_fee, tr.transfer_type,
tr.completed_at as transfer_date, tr.effective_transfer_date,
m.full_name_ar as source_member_name, m.membership_number as source_number
FROM transfer_requests tr
LEFT JOIN members m ON m.id = tr.source_member_id
WHERE tr.id = ?",
[(int) $member->transferred_from_transfer_id]
);
}
// Transfer fee payment (for separated children / waiver-acquired members)
$transferFeePayment = null;
if (!empty($member->transferred_from_waiver_id)) {
if (!empty($member->transferred_from_transfer_id) && $separationTransfer) {
$transferFeePayment = $db->selectOne(
"SELECT amount, payment_date FROM payments WHERE member_id = ? AND payment_type = 'separation_fee' AND related_entity_type = 'transfer_requests' AND related_entity_id = ? AND is_voided = 0 ORDER BY id DESC LIMIT 1",
[(int) $separationTransfer['source_member_id'], (int) $member->transferred_from_transfer_id]
);
} elseif (!empty($member->transferred_from_waiver_id)) {
$transferFeePayment = $db->selectOne(
"SELECT amount, payment_date FROM payments WHERE member_id = ? AND payment_type = 'waiver_fee' AND is_voided = 0 ORDER BY id DESC LIMIT 1",
[(int) $id]
......@@ -365,6 +384,7 @@ class MemberController extends Controller
'subscriptionStatus' => $subscriptionStatus,
'overdueSubscriptions' => $overdueSubscriptions,
'transferFeePayment' => $transferFeePayment,
'separationTransfer' => $separationTransfer,
]);
}
......
......@@ -162,7 +162,7 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<!-- ═══════════════════════════════════════════════ -->
<!-- TRANSFER ORIGIN DETAILS -->
<!-- ═══════════════════════════════════════════════ -->
<?php if (!empty($deathTransfer) || !empty($waiverTransfer) || !empty($divorceTransfer)): ?>
<?php if (!empty($deathTransfer) || !empty($waiverTransfer) || !empty($divorceTransfer) || !empty($separationTransfer)): ?>
<div class="card" style="margin-bottom:20px;padding:20px;background:#F0F9FF;border:1px solid #BAE6FD;">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
<span style="font-size:18px;">🔄</span>
......@@ -190,6 +190,14 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<tr><td style="padding:6px 0;color:#6B7280;">المبلغ المدفوع فعلياً</td><td style="padding:6px 0;font-weight:700;color:#059669;font-size:15px;"><?= number_format((float) $transferFeePayment['amount'], 2) ?> ج.م</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الدفع</td><td style="padding:6px 0;"><?= e($transferFeePayment['payment_date'] ?? '—') ?></td></tr>
<?php endif; ?>
<?php elseif (!empty($separationTransfer)): ?>
<tr><td style="padding:6px 0;color:#6B7280;width:160px;">فُصل من عضوية</td><td style="padding:6px 0;font-weight:600;"><a href="/members/<?= (int) $separationTransfer['source_member_id'] ?>" style="color:#14B8A6;"><?= e($separationTransfer['source_member_name'] ?? '—') ?></a> (<?= e($separationTransfer['source_number'] ?? '') ?>)</td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الفصل</td><td style="padding:6px 0;font-weight:600;"><?= e(substr($separationTransfer['transfer_date'] ?? '', 0, 10)) ?></td></tr>
<tr><td style="padding:6px 0;color:#6B7280;">رسوم الفصل المدفوعة</td><td style="padding:6px 0;font-weight:700;color:#059669;font-size:15px;"><?= number_format((float) ($transferFeePayment['amount'] ?? $separationTransfer['total_fee'] ?? 0), 2) ?> ج.م</td></tr>
<?php if (!empty($transferFeePayment['payment_date'])): ?>
<tr><td style="padding:6px 0;color:#6B7280;">تاريخ الدفع</td><td style="padding:6px 0;"><?= e($transferFeePayment['payment_date']) ?></td></tr>
<?php endif; ?>
<tr><td style="padding:6px 0;color:#6B7280;">تفاصيل الطلب</td><td style="padding:6px 0;"><a href="/transfers/<?= (int) $separationTransfer['id'] ?>" style="color:#0D7377;font-weight:600;">عرض طلب الفصل #<?= (int) $separationTransfer['id'] ?></a></td></tr>
<?php endif; ?>
</table>
</div>
......@@ -754,10 +762,13 @@ $hasIncomplete = !empty(array_filter($missingSteps, fn($s) => !$s['done']));
<div style="padding:15px 20px;">
<div style="display:grid;grid-template-columns:<?= !empty($specialDiscount) ? '1fr 1fr 1fr' : '1fr 1fr' ?>;gap:15px;">
<?php
$isTransferred = !empty($member->transferred_from_death_id) || !empty($member->transferred_from_waiver_id) || !empty($member->transferred_from_divorce_id);
$isTransferred = !empty($member->transferred_from_death_id) || !empty($member->transferred_from_waiver_id) || !empty($member->transferred_from_divorce_id) || !empty($member->transferred_from_transfer_id);
$displayValue = $member->membership_value ?? '0';
$valueLabel = 'قيمة العضوية';
if ($isTransferred && !empty($transferFeePayment)) {
if (!empty($member->transferred_from_transfer_id) && !empty($separationTransfer)) {
$displayValue = $transferFeePayment['amount'] ?? $separationTransfer['total_fee'] ?? $displayValue;
$valueLabel = 'رسوم الفصل المدفوعة';
} elseif ($isTransferred && !empty($transferFeePayment)) {
$displayValue = $transferFeePayment['amount'];
$valueLabel = 'المبلغ المدفوع عند الانتقال';
} elseif ($isTransferred && !empty($deathTransfer['paid_amount'])) {
......
......@@ -185,6 +185,8 @@ final class TransferProcessor
}
}
$newMemberData['transferred_from_transfer_id'] = $transferRequestId;
$newMemberId = $db->insert('members', $newMemberData);
// 5. Record number chain
......
......@@ -21,7 +21,7 @@
<td style="font-size:13px;"><?= match($r['transfer_type'] ?? '') { 'child_separation' => 'فصل ابن', 'child_mandatory_25' => 'تحويل وجوبي (25)', 'sports_conversion' => 'تحويل رياضي', 'cross_branch' => 'تحويل بين فروع', default => $r['transfer_type'] ?? '' } ?></td>
<td style="font-weight:600;"><?= money($r['separation_fee'] ?? '0') ?></td>
<td style="font-weight:600;color:#0D7377;"><?= e($r['new_membership_number'] ?? '—') ?></td>
<td><span style="color:<?= match($r['status'] ?? '') { 'completed' => '#059669', 'approved' => '#0284C7', default => '#D97706' } ?>;font-weight:600;"><?= match($r['status'] ?? '') { 'requested' => 'مقدم', 'approved' => 'معتمد', 'completed' => 'مكتمل', 'rejected' => 'مرفوض', default => $r['status'] ?? '' } ?></span></td>
<td><span style="color:<?= match($r['status'] ?? '') { 'completed' => '#059669', 'fee_paid' => '#2563EB', 'approved' => '#0284C7', 'rejected' => '#DC2626', default => '#D97706' } ?>;font-weight:600;"><?= match($r['status'] ?? '') { 'requested' => 'في انتظار الدفع', 'approved' => 'معتمد', 'fee_paid' => 'تم الدفع', 'completed' => 'مكتمل', 'rejected' => 'مرفوض', default => $r['status'] ?? '' } ?></span></td>
<td style="font-size:12px;"><?= e(substr($r['created_at'] ?? '', 0, 10)) ?></td>
<td><a href="/transfers/<?= (int) $r['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
</tr>
......
......@@ -24,7 +24,7 @@ if ($transfer['transfer_type'] === 'full_transfer' && !empty($transfer['notes'])
<tr><td style="padding:8px 0;color:#6B7280;">الزوج/ة</td><td style="padding:8px 0;font-weight:600;"><?= e($transfer['spouse_name']) ?></td></tr>
<?php endif; ?>
<tr><td style="padding:8px 0;color:#6B7280;">النوع</td><td style="padding:8px 0;font-weight:600;"><?= e($typeLabels[$transfer['transfer_type']] ?? $transfer['transfer_type']) ?></td></tr>
<tr><td style="padding:8px 0;color:#6B7280;">الحالة</td><td style="padding:8px 0;font-weight:700;color:<?= match($transfer['status']) { 'completed' => '#059669', 'fee_paid' => '#2563EB', 'approved' => '#0284C7', 'rejected' => '#DC2626', default => '#D97706' } ?>;"><?= match($transfer['status']) { 'completed' => 'مكتمل', 'fee_paid' => 'تم الدفع', 'approved' => 'معتمد', 'rejected' => 'مرفوض', default => 'مقدّم' } ?></td></tr>
<tr><td style="padding:8px 0;color:#6B7280;">الحالة</td><td style="padding:8px 0;font-weight:700;color:<?= match($transfer['status']) { 'completed' => '#059669', 'fee_paid' => '#2563EB', 'approved' => '#0284C7', 'rejected' => '#DC2626', default => '#D97706' } ?>;"><?= match($transfer['status']) { 'completed' => 'مكتمل', 'fee_paid' => 'تم الدفع', 'approved' => 'معتمد — في انتظار الدفع', 'rejected' => 'مرفوض', 'requested' => 'في انتظار الدفع', default => $transfer['status'] } ?></td></tr>
<?php if ($transfer['new_membership_number']): ?><tr><td style="padding:8px 0;color:#6B7280;">رقم العضوية (نفس الرقم)</td><td style="padding:8px 0;font-weight:700;color:#0D7377;font-size:20px;"><?= e($transfer['new_membership_number']) ?></td></tr><?php endif; ?>
<?php if (!empty($transfer['form_number'])): ?><tr><td style="padding:8px 0;color:#6B7280;">رقم الاستمارة</td><td style="padding:8px 0;font-weight:700;color:#1A1A2E;"><?= e($transfer['form_number']) ?></td></tr><?php endif; ?>
<?php if ($transfer['source_companions_count'] !== null): ?>
......
<?php
declare(strict_types=1);
return [
'up' => "ALTER TABLE members ADD COLUMN transferred_from_transfer_id BIGINT UNSIGNED NULL AFTER transferred_from_waiver_id;
UPDATE members m
JOIN transfer_requests tr ON tr.target_member_id = m.id AND tr.status = 'completed'
SET m.transferred_from_transfer_id = tr.id
WHERE m.transferred_from_transfer_id IS NULL",
'down' => "ALTER TABLE members DROP COLUMN transferred_from_transfer_id",
];
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