Commit 2ab35afb authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix(transfers): transfer full history and archived dependents on membership ownership change

Three issues fixed:
1. Deceased/archived dependents now transfer with membership (Death, Transfer, Waiver)
2. Archive snapshots (full membership history) follow the membership number on all transfer types
3. Missing Arabic labels added for transfer subtypes in Archive views
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 0a92504c
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<?php <?php
$entityTypeLabels = ['members' => 'أعضاء', 'employees' => 'موظفين', 'roles' => 'أدوار', 'spouses' => 'زوجات', 'children' => 'أبناء', 'temporary_members' => 'مؤقتين']; $entityTypeLabels = ['members' => 'أعضاء', 'employees' => 'موظفين', 'roles' => 'أدوار', 'spouses' => 'زوجات', 'children' => 'أبناء', 'temporary_members' => 'مؤقتين'];
$reasonLabels = ['transfer' => 'تحويل', 'separation' => 'فصل', 'divorce' => 'طلاق', 'death' => 'وفاة', 'waiver' => 'تنازل', 'status_change' => 'تغيير حالة', 'data_correction' => 'تصحيح بيانات', 'manual' => 'يدوي']; $reasonLabels = ['transfer' => 'تحويل', 'full_transfer' => 'تحويل كامل', 'separation' => 'فصل', 'child_separation' => 'فصل ابن', 'child_mandatory_25' => 'فصل إلزامي (25 سنة)', 'divorce' => 'طلاق', 'death' => 'وفاة', 'waiver' => 'تنازل', 'sports_conversion' => 'تحويل رياضي', 'cross_branch' => 'نقل بين فروع', 'status_change' => 'تغيير حالة', 'data_correction' => 'تصحيح بيانات', 'manual' => 'يدوي'];
?> ?>
<div class="card" style="margin-bottom:20px;padding:15px;"> <div class="card" style="margin-bottom:20px;padding:15px;">
<form method="GET" action="/archive" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;"> <form method="GET" action="/archive" style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;">
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<?php <?php
$reasonLabels = ['transfer' => 'تحويل', 'separation' => 'فصل', 'divorce' => 'طلاق', 'death' => 'وفاة', 'waiver' => 'تنازل', 'status_change' => 'تغيير حالة', 'data_correction' => 'تصحيح بيانات', 'manual' => 'يدوي']; $reasonLabels = ['transfer' => 'تحويل', 'full_transfer' => 'تحويل كامل', 'separation' => 'فصل', 'child_separation' => 'فصل ابن', 'child_mandatory_25' => 'فصل إلزامي (25 سنة)', 'divorce' => 'طلاق', 'death' => 'وفاة', 'waiver' => 'تنازل', 'sports_conversion' => 'تحويل رياضي', 'cross_branch' => 'نقل بين فروع', 'status_change' => 'تغيير حالة', 'data_correction' => 'تصحيح بيانات', 'manual' => 'يدوي'];
?> ?>
<?php if (!empty($isChainView)): ?> <?php if (!empty($isChainView)): ?>
......
...@@ -1030,12 +1030,18 @@ class DeathController extends Controller ...@@ -1030,12 +1030,18 @@ class DeathController extends Controller
} }
} }
// Sweep remaining children to primary // Sweep remaining active children to primary
$db->query( $db->query(
"UPDATE children SET member_id = ?, updated_at = ? WHERE member_id = ? AND is_archived = 0", "UPDATE children SET member_id = ?, updated_at = ? WHERE member_id = ? AND is_archived = 0",
[$newMemberId, date('Y-m-d H:i:s'), (int) $case['member_id']] [$newMemberId, date('Y-m-d H:i:s'), (int) $case['member_id']]
); );
// Also transfer archived/deceased children (preserve full family history)
$db->query(
"UPDATE children SET member_id = ?, updated_at = ? WHERE member_id = ? AND is_archived = 1",
[$newMemberId, date('Y-m-d H:i:s'), (int) $case['member_id']]
);
// Renumber child_order // Renumber child_order
$allChildren = $db->select( $allChildren = $db->select(
"SELECT id FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order, id", "SELECT id FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order, id",
...@@ -1063,12 +1069,18 @@ class DeathController extends Controller ...@@ -1063,12 +1069,18 @@ class DeathController extends Controller
if ($tempRow) $tempsMoved[] = $tempRow['full_name_ar']; if ($tempRow) $tempsMoved[] = $tempRow['full_name_ar'];
} }
} }
// Sweep remaining temps to primary // Sweep remaining active temps to primary
$db->query( $db->query(
"UPDATE temporary_members SET member_id = ?, updated_at = ? WHERE member_id = ? AND is_archived = 0", "UPDATE temporary_members SET member_id = ?, updated_at = ? WHERE member_id = ? AND is_archived = 0",
[$newMemberId, date('Y-m-d H:i:s'), (int) $case['member_id']] [$newMemberId, date('Y-m-d H:i:s'), (int) $case['member_id']]
); );
// Also transfer archived/deceased temps (preserve full family history)
$db->query(
"UPDATE temporary_members SET member_id = ?, updated_at = ? WHERE member_id = ? AND is_archived = 1",
[$newMemberId, date('Y-m-d H:i:s'), (int) $case['member_id']]
);
if (!empty($tempsMoved)) { if (!empty($tempsMoved)) {
$auditLog[] = 'تم نقل الأعضاء المؤقتين: ' . implode('، ', $tempsMoved); $auditLog[] = 'تم نقل الأعضاء المؤقتين: ' . implode('، ', $tempsMoved);
DeathAuditService::log($caseId, 'temps_transferred', 'تم نقل الأعضاء المؤقتين: ' . implode('، ', $tempsMoved), 'members', $newMemberId); DeathAuditService::log($caseId, 'temps_transferred', 'تم نقل الأعضاء المؤقتين: ' . implode('، ', $tempsMoved), 'members', $newMemberId);
...@@ -1080,6 +1092,12 @@ class DeathController extends Controller ...@@ -1080,6 +1092,12 @@ class DeathController extends Controller
} }
} }
// Transfer archived spouses (divorced/deceased) to preserve family history
$db->query(
"UPDATE spouses SET member_id = ?, updated_at = ? WHERE member_id = ? AND is_archived = 1",
[$newMemberId, date('Y-m-d H:i:s'), (int) $case['member_id']]
);
// STEP 3: Transfer active financial data to new membership // STEP 3: Transfer active financial data to new membership
$this->transferActiveData($db, (int) $case['member_id'], $newMemberId, $caseId); $this->transferActiveData($db, (int) $case['member_id'], $newMemberId, $caseId);
...@@ -1178,6 +1196,12 @@ class DeathController extends Controller ...@@ -1178,6 +1196,12 @@ class DeathController extends Controller
private function transferActiveData($db, int $deceasedMemberId, int $newMemberId, int $caseId): void private function transferActiveData($db, int $deceasedMemberId, int $newMemberId, int $caseId): void
{ {
// Transfer archive snapshots from deceased member to new member (full history)
$db->query(
"UPDATE archive_snapshots SET entity_id = ?, updated_at = NOW() WHERE entity_type = 'members' AND entity_id = ?",
[$newMemberId, $deceasedMemberId]
);
// Transfer active/unpaid installment plans (schedule + cheques follow via FK) // Transfer active/unpaid installment plans (schedule + cheques follow via FK)
$installments = $db->select( $installments = $db->select(
"SELECT id FROM installment_plans WHERE member_id = ? AND status IN ('active', 'partial')", "SELECT id FROM installment_plans WHERE member_id = ? AND status IN ('active', 'partial')",
......
...@@ -389,6 +389,17 @@ final class TransferProcessor ...@@ -389,6 +389,17 @@ final class TransferProcessor
$db->query("UPDATE spouses SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]); $db->query("UPDATE spouses SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]);
$db->query("UPDATE children SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]); $db->query("UPDATE children SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]);
$db->query("UPDATE temporary_members SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]); $db->query("UPDATE temporary_members SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 0", [$newMemberId, (int) $sourceMember['id']]);
// Also transfer archived dependents (preserve full family history)
$db->query("UPDATE spouses SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 1", [$newMemberId, (int) $sourceMember['id']]);
$db->query("UPDATE children SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 1", [$newMemberId, (int) $sourceMember['id']]);
$db->query("UPDATE temporary_members SET member_id = ?, updated_at = NOW() WHERE member_id = ? AND is_archived = 1", [$newMemberId, (int) $sourceMember['id']]);
// Transfer archive history (full membership history follows the number)
$db->query(
"UPDATE archive_snapshots SET entity_id = ?, updated_at = NOW() WHERE entity_type = 'members' AND entity_id = ?",
[$newMemberId, (int) $sourceMember['id']]
);
} }
// 7. Mark transfer request complete // 7. Mark transfer request complete
......
...@@ -82,6 +82,12 @@ final class WaiverProcessor ...@@ -82,6 +82,12 @@ final class WaiverProcessor
ArchiveService::recordNumberTransfer($waiver['membership_number'], 'waiver', 'members', (int) $waiver['target_member_id']); ArchiveService::recordNumberTransfer($waiver['membership_number'], 'waiver', 'members', (int) $waiver['target_member_id']);
// Transfer archive history from source to target (full membership history follows the number)
$db->query(
"UPDATE archive_snapshots SET entity_id = ?, updated_at = NOW() WHERE entity_type = 'members' AND entity_id = ?",
[(int) $waiver['target_member_id'], (int) $waiver['source_member_id']]
);
$db->update('waiver_requests', [ $db->update('waiver_requests', [
'archive_snapshot_id' => $snapshotId, 'archive_snapshot_id' => $snapshotId,
'status' => 'completed', 'status' => 'completed',
......
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