"UPDATE `{$depTable}` SET status = 'pending_payment', join_date = NULL, updated_at = NOW() WHERE member_id = ? AND status IN ('active','frozen') AND is_archived = 0",
[$memberId]
);
}
\App\Core\Logger::info("Reverted member #{$memberId} to payment_pending after membership payment void");
"UPDATE `{$depTable}` SET status = 'pending_payment', join_date = NULL, updated_at = NOW() WHERE member_id = ? AND status IN ('active','frozen') AND is_archived = 0",
[$memberId]
);
}
\App\Core\Logger::info("Reverted member #{$memberId} to payment_pending after payment request cancellation");
}
}
}
// Addition fee cancellation: revert the specific dependent
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND status = 'completed' AND is_voided = 0 LIMIT 1",
[$memberId,$entityType,$entityId]
);
if(!$otherValidRequest){
$db->update($entityType,[
'status'=>'pending_payment',
'join_date'=>null,
'updated_at'=>date('Y-m-d H:i:s'),
],'`id` = ? AND is_archived = 0',[$entityId]);
\App\Core\Logger::info("Reverted {$entityType} #{$entityId} to pending_payment after payment request cancellation");
}
}
}
// Separation/divorce/death/waiver fee cancellation: revert entity status
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1",
@@ -323,36 +323,52 @@ class DeathController extends Controller
...
@@ -323,36 +323,52 @@ class DeathController extends Controller
$member=$db->selectOne("SELECT * FROM members WHERE id = ?",[(int)$case['member_id']]);
$member=$db->selectOne("SELECT * FROM members WHERE id = ?",[(int)$case['member_id']]);
$spouse=$db->selectOne("SELECT * FROM spouses WHERE id = ?",[(int)$case['primary_spouse_id']]);
$spouse=$db->selectOne("SELECT * FROM spouses WHERE id = ?",[(int)$case['primary_spouse_id']]);
// Extract form data stored during fill-form step
$formData=[];
$formData=[];
if(!empty($case['notes'])){
if(!empty($case['notes'])){
$notesData=json_decode($case['notes'],true);
$notesData=json_decode($case['notes'],true);
$formData=$notesData['form_data']??[];
$formData=$notesData['form_data']??[];
}
}
// Create new member from form data with SAME membership number
$inheritedNumber=$member['membership_number'];
// Find the death_fee payment that was made for this case
$deathPayment=$db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type = 'death_fee' AND related_entity_type = 'death_cases' AND related_entity_id = ? AND is_voided = 0 ORDER BY id DESC LIMIT 1",
@@ -141,52 +141,45 @@ class MemberController extends Controller
...
@@ -141,52 +141,45 @@ class MemberController extends Controller
$qualification=$member->qualification_id?$db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?",[(int)$member->qualification_id]):null;
$qualification=$member->qualification_id?$db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?",[(int)$member->qualification_id]):null;
$spouses=[];$children=[];$temporaries=[];
$spouses=[];$children=[];$temporaries=[];
try{$spouses=$db->select("SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order",[(int)$id]);}catch(\Throwable$e){}
try{
try{$children=$db->select("SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order",[(int)$id]);}catch(\Throwable$e){}
$spouses=$db->select(
try{$temporaries=$db->select("SELECT * FROM temporary_members WHERE member_id = ? AND is_archived = 0 ORDER BY id",[(int)$id]);}catch(\Throwable$e){}
"SELECT s.*, pr.created_at AS due_date, p.payment_date
FROM spouses s
// Self-healing: fix dependents stuck at pending_payment after membership was paid
LEFT JOIN payment_requests pr ON pr.related_entity_type = 'spouses' AND pr.related_entity_id = s.id AND pr.payment_type = 'addition_fee' AND pr.is_voided = 0
if($member->status==='active'){
LEFT JOIN payments p ON p.id = s.activated_by_payment_id AND p.is_voided = 0
$membershipPaid=$db->selectOne(
WHERE s.member_id = ? AND s.is_archived = 0
"SELECT id FROM payments WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND is_voided = 0 LIMIT 1",
ORDER BY s.spouse_order",[(int)$id]
[(int)$id]
);
);
if($membershipPaid){
}catch(\Throwable$e){
$fixedAny=false;
try{$spouses=$db->select("SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order",[(int)$id]);}catch(\Throwable$e2){}
"SELECT id FROM `{$tbl}` WHERE member_id = ? AND status = 'pending_payment' AND is_archived = 0",
[(int)$id]
);
foreach($stuckas$dep){
$hasPending=$db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1",
[(int)$id,$tbl,(int)$dep['id']]
);
$hasCompletedPayment=$db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND status = 'completed' AND is_voided = 0 LIMIT 1",
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND status = 'cancelled' ORDER BY id DESC LIMIT 1",
"SELECT c.*, pr.created_at AS due_date, p.payment_date
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 payments p ON p.id = c.activated_by_payment_id AND p.is_voided = 0
WHERE c.member_id = ? AND c.is_archived = 0
ORDER BY c.child_order",[(int)$id]
);
}catch(\Throwable$e){
try{$children=$db->select("SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order",[(int)$id]);}catch(\Throwable$e2){}
}
try{
$temporaries=$db->select(
"SELECT t.*, pr.created_at AS due_date, p.payment_date
FROM temporary_members t
LEFT JOIN payment_requests pr ON pr.related_entity_type = 'temporary_members' AND pr.related_entity_id = t.id AND pr.payment_type = 'addition_fee' AND pr.is_voided = 0
LEFT JOIN payments p ON p.id = t.activated_by_payment_id AND p.is_voided = 0
WHERE t.member_id = ? AND t.is_archived = 0
ORDER BY t.id",[(int)$id]
);
}catch(\Throwable$e){
try{$temporaries=$db->select("SELECT * FROM temporary_members WHERE member_id = ? AND is_archived = 0 ORDER BY id",[(int)$id]);}catch(\Throwable$e2){}
}
// Reconcile membership status against payment source-of-truth
return['success'=>false,'error'=>'نوع الملحق غير صالح'];
}
$entity=$db->selectOne("SELECT id, member_id FROM `{$table}` WHERE id = ? AND is_archived = 0",[$entityId]);
if(!$entity){
return['success'=>true,'not_found'=>true];
}
$otherValid=$db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND is_voided = 0 AND id != ? LIMIT 1",
"SELECT id FROM `{$table}` WHERE member_id = ? AND status = 'pending_payment' AND is_archived = 0",
[$memberId]
);
foreach($pendingas$dep){
$hasSeparateRequest=$db->selectOne(
"SELECT id FROM payment_requests WHERE member_id = ? AND payment_type = 'addition_fee' AND related_entity_type = ? AND related_entity_id = ? AND status IN ('pending','processing') AND is_voided = 0 LIMIT 1",
[$memberId,$table,(int)$dep['id']]
);
if(!$hasSeparateRequest){
$db->update($table,[
'status'=>'active',
'join_date'=>date('Y-m-d'),
'activated_by_payment_id'=>$paymentId,
'updated_at'=>date('Y-m-d H:i:s'),
],'`id` = ?',[(int)$dep['id']]);
}
}
}
}
/**
* Deactivate ALL dependents for a member (used when member itself is deactivated).
"UPDATE `{$table}` SET status = 'pending_payment', join_date = NULL, activated_by_payment_id = NULL, updated_at = NOW() WHERE member_id = ? AND status IN ('active','frozen') AND is_archived = 0",
[$memberId]
);
}
}
/**
* Check if a dependent was activated as part of the membership fee (no separate addition_fee).
<divstyle="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">👤 الأعضاء المؤقتون (<?=count($temporaries)?>)</div>
<divstyle="font-size:12px;color:#0D7377;font-weight:700;margin-bottom:10px;text-transform:uppercase;">👤 الأعضاء المؤقتون (<?=count($temporaries)?>)</div>
// Find the separation_fee payment that was made for this transfer
$transferPayment=$db->selectOne(
"SELECT id 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",
<pstyle="margin:5px 0 0;font-size:12px;color:#6B7280;">العضوية الحالية بها <?=count($spouses)?> زوج/ة و <?=count($children)?> ابن/ابنة. إذا كان للمستلم ملحقين أكثر سيتم احتساب رسوم إضافية.</p>
<pstyle="font-size:13px;color:#6B7280;margin-bottom:15px;">سيتم احتساب رسوم الفصل/التحويل تلقائياً (نسبة من قيمة العضوية حسب سنوات الاكتساب + رسوم استمارة + اشتراك سنوي).</p>
<pstyle="font-size:13px;color:#6B7280;margin-bottom:15px;">سيتم احتساب رسوم الفصل/التحويل تلقائياً (نسبة من قيمة العضوية حسب سنوات الاكتساب + رسوم استمارة + اشتراك سنوي + رسوم ملحقين إضافيين إن وجدت).</p>