Commit 910e8606 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(death): full board-approval workflow, mandatory docs, trustee fee, children-transfer fix

- New status flow: board_review → board_approved → pending_form_fill → completed
- Mandatory document uploads at case creation (death certificate + inheritance notice)
- Board approval step: configurable trustee fee (% of membership_value or flat amount)
- Payment request created only after board approval (not at case creation)
- Cashier bootstrap fixed: death_fee for primary_member now sets pending_form_fill
- Pre-completion validations: board approval, payment, both docs, wife form filled
- Children transfer bug fixed: sweep remaining children to primary + renumber child_order
- Source tracking: transferred_from_death_id on new member rows
- Death-origin badge in member show page
- Migration Phase_94_001: new columns on death_cases + members
Co-Authored-By: 's avatarClaude Sonnet 4.6 <noreply@anthropic.com>
parent e6708441
......@@ -148,8 +148,18 @@ EventBus::listen('payment_request.completed', function (array $data) {
];
if (isset($tableMap[$paymentType])) {
$cfg = $tableMap[$paymentType];
$newStatus = 'fee_paid';
// Death fee for primary_member: skip to pending_form_fill (wife must fill form next)
if ($paymentType === 'death_fee') {
$deathCase = $db->selectOne("SELECT deceased_type FROM death_cases WHERE id = ?", [$entityId]);
if ($deathCase && $deathCase['deceased_type'] === 'primary_member') {
$newStatus = 'pending_form_fill';
}
}
$db->update($cfg['table'], [
'status' => 'fee_paid',
'status' => $newStatus,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [$entityId]);
EventBus::dispatch($cfg['event'], [$cfg['key'] => $entityId, 'payment_id' => $paymentId]);
......
......@@ -18,10 +18,14 @@ class DeathCase extends Model
protected static array $fillable = [
'member_id', 'deceased_type', 'death_date',
'death_certificate_number', 'death_certificate_path',
'inheritance_notice_path',
'spouse_id', 'primary_spouse_id', 'secondary_spouses_json',
'primary_spouse_form_filled', 'child_id',
'children_assignment_json', 'transferred_to_member_id',
'same_membership_number', 'fee_amount',
'board_fee_method', 'board_fee_value', 'board_fee_amount',
'board_decision_reference', 'board_decision_date', 'board_notes',
'board_approved_by', 'board_approved_at', 'payment_request_id',
'archive_snapshot_id', 'workflow_instance_id', 'status', 'notes',
];
......
......@@ -10,4 +10,7 @@ return [
['POST', '/death/{id}/fill-form', 'Death\Controllers\DeathController@saveFillForm', ['auth', 'csrf'], 'transfer.initiate'],
['POST', '/death/{id}/pay', 'Death\Controllers\DeathController@pay', ['auth', 'csrf'], 'payment.collect'],
['POST', '/death/{id}/complete', 'Death\Controllers\DeathController@complete', ['auth', 'csrf'], 'transfer.approve'],
['POST', '/death/{id}/board-approve', 'Death\Controllers\DeathController@boardApprove', ['auth', 'csrf'], 'transfer.approve'],
['POST', '/death/{id}/board-reject', 'Death\Controllers\DeathController@boardReject', ['auth', 'csrf'], 'transfer.approve'],
['GET', '/death/{id}/document/{type}', 'Death\Controllers\DeathController@downloadDocument', ['auth'], 'transfer.view'],
];
\ No newline at end of file
......@@ -10,9 +10,31 @@
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">← العودة للعضو</a>
</div>
<form method="POST" action="/death/store/<?= (int) $member['id'] ?>">
<form method="POST" action="/death/store/<?= (int) $member['id'] ?>" enctype="multipart/form-data">
<?= csrf_field() ?>
<!-- Mandatory documents — shown/required only when primary_member is selected -->
<div id="docs-section" style="display:none;">
<div class="card" style="margin-bottom:20px;border:2px solid #DC2626;">
<div style="padding:15px 20px;border-bottom:1px solid #FCA5A5;background:#FEF2F2;">
<h3 style="margin:0;color:#DC2626;font-size:15px;">المستندات المطلوبة — إلزامية لنقل العضوية</h3>
<p style="margin:4px 0 0;font-size:12px;color:#6B7280;">PDF أو صورة (JPG/PNG) — لن يتم تسجيل الحالة بدونهما</p>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group">
<label class="form-label">شهادة الوفاة <span style="color:#DC2626;">*</span></label>
<input type="file" name="death_certificate" id="death_certificate" class="form-input" accept=".pdf,.png,.jpg,.jpeg">
<p style="font-size:11px;color:#9CA3AF;margin:4px 0 0;">PDF أو JPG أو PNG</p>
</div>
<div class="form-group">
<label class="form-label">إعلام الوراثة <span style="color:#DC2626;">*</span></label>
<input type="file" name="inheritance_notice" id="inheritance_notice" class="form-input" accept=".pdf,.png,.jpg,.jpeg">
<p style="font-size:11px;color:#9CA3AF;margin:4px 0 0;">PDF أو JPG أو PNG</p>
</div>
</div>
</div>
</div>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#1F2937;">بيانات الوفاة</h3></div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
......@@ -133,6 +155,8 @@ document.addEventListener('DOMContentLoaded', function() {
var hiddenChild = document.getElementById('hidden_child_id');
var primarySelect = document.getElementById('primary_spouse_id');
var docsSection = document.getElementById('docs-section');
deceasedType.addEventListener('change', function() {
var selected = this.options[this.selectedIndex];
hiddenSpouse.value = selected.dataset.spouseId || '';
......@@ -141,9 +165,15 @@ document.addEventListener('DOMContentLoaded', function() {
if (this.value === 'primary_member') {
primarySection.style.display = 'block';
feeSection.style.display = 'block';
docsSection.style.display = 'block';
document.getElementById('death_certificate').required = true;
document.getElementById('inheritance_notice').required = true;
} else {
primarySection.style.display = 'none';
feeSection.style.display = (this.value === 'spouse' || this.value === 'child') ? 'none' : 'block';
feeSection.style.display = 'none';
docsSection.style.display = 'none';
document.getElementById('death_certificate').required = false;
document.getElementById('inheritance_notice').required = false;
}
});
......
This diff is collapsed.
......@@ -111,6 +111,9 @@ $canEdit = can('member.edit') && (!$isLocked || ($isSuperAdmin ?? false));
<?php if (!empty($divorceTransfer)): ?>
<span style="background:#FEF3C7;padding:2px 8px;border-radius:10px;font-size:11px;">محوّل من عضوية: <a href="/members/<?= (int) $divorceTransfer['original_member_id'] ?>" style="color:#D97706;font-weight:700;"><?= e($divorceTransfer['original_membership_number']) ?></a> (<?= e($divorceTransfer['original_member_name'] ?? '') ?>)</span>
<?php endif; ?>
<?php if (!empty($member->transferred_from_death_id)): ?>
<span style="background:#DBEAFE;padding:2px 8px;border-radius:10px;font-size:11px;">أُنشئت من <a href="/death/<?= (int) $member->transferred_from_death_id ?>" style="color:#2563EB;font-weight:700;">حالة وفاة #<?= (int) $member->transferred_from_death_id ?></a></span>
<?php endif; ?>
<span>📋 استمارة: <strong style="color:#D97706;"><?= e($member->form_number ?? '—') ?></strong></span>
<span>🏢 <?= e($branchName) ?></span>
<span><?= $member->gender === 'male' ? '👨' : '👩' ?> <?= $member->getGenderLabel() ?></span>
......
<?php
declare(strict_types=1);
return [
'up' => "
ALTER TABLE death_cases
ADD COLUMN inheritance_notice_path VARCHAR(500) NULL AFTER death_certificate_path,
ADD COLUMN board_fee_method ENUM('percentage','fixed') NULL AFTER fee_amount,
ADD COLUMN board_fee_value DECIMAL(10,4) NULL AFTER board_fee_method,
ADD COLUMN board_fee_amount DECIMAL(15,2) NULL AFTER board_fee_value,
ADD COLUMN board_decision_reference VARCHAR(100) NULL AFTER board_fee_amount,
ADD COLUMN board_decision_date DATE NULL AFTER board_decision_reference,
ADD COLUMN board_notes TEXT NULL AFTER board_decision_date,
ADD COLUMN board_approved_by BIGINT UNSIGNED NULL AFTER board_notes,
ADD COLUMN board_approved_at TIMESTAMP NULL AFTER board_approved_by,
ADD COLUMN payment_request_id BIGINT UNSIGNED NULL AFTER board_approved_at;
ALTER TABLE members
ADD COLUMN transferred_from_death_id BIGINT UNSIGNED NULL AFTER transferred_from_divorce_id;
",
'down' => "
ALTER TABLE death_cases
DROP COLUMN inheritance_notice_path,
DROP COLUMN board_fee_method,
DROP COLUMN board_fee_value,
DROP COLUMN board_fee_amount,
DROP COLUMN board_decision_reference,
DROP COLUMN board_decision_date,
DROP COLUMN board_notes,
DROP COLUMN board_approved_by,
DROP COLUMN board_approved_at,
DROP COLUMN payment_request_id;
ALTER TABLE members
DROP COLUMN transferred_from_death_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