Commit a23798bd authored by Administrator's avatar Administrator

Update 4 files via Son of Anton

parent e93edd8a
......@@ -2,24 +2,18 @@
declare(strict_types=1);
return [
// Member CRUD
['GET', '/members', 'Members\Controllers\MemberController@index', ['auth'], 'member.view'],
['GET', '/members/create', 'Members\Controllers\MemberController@create', ['auth'], 'member.create'],
['POST', '/members', 'Members\Controllers\MemberController@store', ['auth'], 'member.create'],
['GET', '/members/search', 'Members\Controllers\MemberController@search', ['auth'], 'member.view'],
['GET', '/members/{id}', 'Members\Controllers\MemberController@show', ['auth'], 'member.view'],
['GET', '/members/{id}/edit', 'Members\Controllers\MemberController@edit', ['auth'], 'member.edit'],
['POST', '/members/{id}', 'Members\Controllers\MemberController@update', ['auth'], 'member.edit'],
['POST', '/members/{id}/status', 'Members\Controllers\MemberController@changeStatus', ['auth'], 'member.change_status'],
// Form fee payment
['POST', '/members/{id}/pay-form-fee', 'Members\Controllers\MemberController@payFormFee', ['auth'], 'member.edit'],
// Fill Form (استمارة) — blocked until form fee paid
['GET', '/members/{id}/fill-form', 'Members\Controllers\MemberController@fillForm', ['auth'], 'member.edit'],
['POST', '/members/{id}/fill-form', 'Members\Controllers\MemberController@saveFillForm', ['auth'], 'member.edit'],
// API endpoints
['POST', '/api/members/parse-nid', 'Members\Controllers\MemberApiController@parseNid', ['auth'], 'member.create'],
['POST', '/api/members/search', 'Members\Controllers\MemberApiController@search', ['auth'], 'member.view'],
['GET', '/members', 'Members\Controllers\MemberController@index', ['auth'], 'member.view'],
['GET', '/members/create', 'Members\Controllers\MemberController@create', ['auth'], 'member.create'],
['POST', '/members', 'Members\Controllers\MemberController@store', ['auth'], 'member.create'],
['GET', '/members/search', 'Members\Controllers\MemberController@search', ['auth'], 'member.view'],
['GET', '/members/{id}', 'Members\Controllers\MemberController@show', ['auth'], 'member.view'],
['GET', '/members/{id}/edit', 'Members\Controllers\MemberController@edit', ['auth'], 'member.edit'],
['POST', '/members/{id}', 'Members\Controllers\MemberController@update', ['auth'], 'member.edit'],
['POST', '/members/{id}/status', 'Members\Controllers\MemberController@changeStatus', ['auth'], 'member.change_status'],
['POST', '/members/{id}/pay-form-fee', 'Members\Controllers\MemberController@payFormFee', ['auth'], 'member.edit'],
['POST', '/members/{id}/pay-membership', 'Members\Controllers\MemberController@payMembership',['auth'], 'member.edit'],
['GET', '/members/{id}/fill-form', 'Members\Controllers\MemberController@fillForm', ['auth'], 'member.edit'],
['POST', '/members/{id}/fill-form', 'Members\Controllers\MemberController@saveFillForm', ['auth'], 'member.edit'],
['POST', '/api/members/parse-nid', 'Members\Controllers\MemberApiController@parseNid', ['auth'], 'member.create'],
['POST', '/api/members/search', 'Members\Controllers\MemberApiController@search', ['auth'], 'member.view'],
];
\ No newline at end of file
<?php
declare(strict_types=1);
namespace App\Modules\Members\Services;
use App\Core\App;
/**
* Calculates the TOTAL bill for a member including all additions.
* Used during initial creation to show accumulated charges before payment.
*/
final class BillingService
{
public static function getMemberBill(int $memberId): array
{
$db = App::getInstance()->db();
$member = $db->selectOne("SELECT * FROM members WHERE id = ? AND is_archived = 0", [$memberId]);
if (!$member) return [];
$membershipValue = $member['membership_value'] ?? '0.00';
$items = [];
// ── 1. Form Fee (505) ──
$formFeePaid = false;
try {
$ff = $db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 LIMIT 1",
[$memberId]
);
$formFeePaid = ($ff !== null);
} catch (\Throwable $e) {}
$items[] = [
'type' => 'form_fee',
'label' => 'رسوم استمارة عضوية (500 استمارة + 5 طابع شهداء)',
'amount' => '505.00',
'paid' => $formFeePaid,
'included' => false,
'category' => 'required',
];
// ── 2. Membership Value ──
$membershipPaid = false;
try {
$mp = $db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type IN ('membership_fee','down_payment') AND is_voided = 0 LIMIT 1",
[$memberId]
);
$membershipPaid = ($mp !== null);
} catch (\Throwable $e) {}
$qualName = '—';
if ($member['qualification_id']) {
$q = $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member['qualification_id']]);
$qualName = $q['name_ar'] ?? '—';
}
$items[] = [
'type' => 'membership_fee',
'label' => 'قيمة العضوية (' . $qualName . ')',
'amount' => $membershipValue,
'paid' => $membershipPaid,
'included' => false,
'category' => 'required',
];
// ── 3. Spouses ──
$spouses = [];
try {
$spouses = $db->select(
"SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order",
[$memberId]
);
} catch (\Throwable $e) {}
foreach ($spouses as $s) {
$order = (int) $s['spouse_order'];
$fee = $s['addition_fee'] ?? '0.00';
$isFirstFree = ($order === 1 && bccomp($fee, '0', 2) <= 0);
$items[] = [
'type' => 'spouse_fee',
'label' => 'زوجة #' . $order . ' — ' . $s['full_name_ar'],
'amount' => $isFirstFree ? '0.00' : $fee,
'paid' => false,
'included' => $isFirstFree,
'included_note' => $isFirstFree ? 'مشمولة في قيمة العضوية' : null,
'entity_type' => 'spouses',
'entity_id' => (int) $s['id'],
'category' => 'addition',
];
}
// ── 4. Children ──
$children = [];
try {
$children = $db->select(
"SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order",
[$memberId]
);
} catch (\Throwable $e) {}
foreach ($children as $c) {
$order = (int) $c['child_order'];
$fee = $c['addition_fee'] ?? '0.00';
$classification = $c['classification'] ?? 'included';
$isIncluded = ($classification === 'included' && bccomp($fee, '0', 2) <= 0);
$classLabel = match ($classification) {
'included' => 'مشمول (بدون رسوم)',
'dependent_with_fee' => 'تابع (برسوم)',
'temporary' => 'مؤقت',
default => $classification,
};
$items[] = [
'type' => 'child_fee',
'label' => ($c['gender'] === 'male' ? 'ابن' : 'ابنة') . ' #' . $order . ' — ' . $c['full_name_ar'] . ' (' . (int) ($c['age_years'] ?? 0) . ' سنة)',
'amount' => $fee,
'paid' => false,
'included' => $isIncluded,
'included_note' => $isIncluded ? 'مشمول — ' . $classLabel : $classLabel,
'entity_type' => 'children',
'entity_id' => (int) $c['id'],
'category' => 'addition',
];
}
// ── 5. Temporary Members ──
try {
$temps = $db->select(
"SELECT * FROM temporary_members WHERE member_id = ? AND is_archived = 0 ORDER BY id",
[$memberId]
);
foreach ($temps as $t) {
$items[] = [
'type' => 'temp_fee',
'label' => 'عضو مؤقت — ' . $t['full_name_ar'] . ' (' . $t['category'] . ')',
'amount' => $t['addition_fee'] ?? '0.00',
'paid' => false,
'included' => false,
'entity_type' => 'temporary_members',
'entity_id' => (int) $t['id'],
'category' => 'addition',
];
}
} catch (\Throwable $e) {}
// ── Check what's already paid ──
try {
$paidAdditions = $db->select(
"SELECT related_entity_type, related_entity_id FROM payments
WHERE member_id = ? AND payment_type = 'addition_fee' AND is_voided = 0",
[$memberId]
);
foreach ($paidAdditions as $pa) {
foreach ($items as &$item) {
if (($item['entity_type'] ?? '') === $pa['related_entity_type']
&& ($item['entity_id'] ?? 0) == $pa['related_entity_id']) {
$item['paid'] = true;
}
}
unset($item);
}
} catch (\Throwable $e) {}
// ── Calculate totals ──
$totalRequired = '0.00';
$totalPaid = '0.00';
$totalPending = '0.00';
$totalIncluded = '0.00';
foreach ($items as $item) {
if ($item['included']) {
$totalIncluded = bcadd($totalIncluded, $item['amount'], 2);
continue;
}
$totalRequired = bcadd($totalRequired, $item['amount'], 2);
if ($item['paid']) {
$totalPaid = bcadd($totalPaid, $item['amount'], 2);
} else {
$totalPending = bcadd($totalPending, $item['amount'], 2);
}
}
return [
'items' => $items,
'membership_value' => $membershipValue,
'total_required' => $totalRequired,
'total_paid' => $totalPaid,
'total_pending' => $totalPending,
'total_included' => $totalIncluded,
'form_fee_paid' => $formFeePaid,
'membership_paid' => $membershipPaid,
'all_paid' => (bccomp($totalPending, '0', 2) <= 0),
];
}
}
\ No newline at end of file
This diff is collapsed.
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