Commit a23798bd authored by Administrator's avatar Administrator

Update 4 files via Son of Anton

parent e93edd8a
...@@ -10,424 +10,217 @@ use App\Core\App; ...@@ -10,424 +10,217 @@ use App\Core\App;
use App\Core\EventBus; use App\Core\EventBus;
use App\Modules\Members\Models\Member; use App\Modules\Members\Models\Member;
use App\Modules\Members\Services\NationalIdParser; use App\Modules\Members\Services\NationalIdParser;
use App\Modules\Members\Services\FormNumberGenerator; use App\Modules\Members\Services\MemberNumberGenerator;
use App\Modules\Members\Services\MemberSearchService; use App\Modules\Members\Services\MemberSearchService;
use App\Modules\Members\Services\BillingService;
use App\Modules\Payments\Services\PaymentService;
class MemberController extends Controller class MemberController extends Controller
{ {
public function index(Request $request): Response public function index(Request $request): Response
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$filters = [ $filters = [
'q' => trim((string) $request->get('q', '')), 'q' => trim((string) $request->get('q', '')),
'status' => $request->get('status', ''), 'status' => $request->get('status', ''),
'branch_id' => $request->get('branch_id', ''), 'branch_id' => $request->get('branch_id', ''),
'date_from' => $request->get('date_from', ''), 'date_from' => $request->get('date_from', ''),
'date_to' => $request->get('date_to', ''), 'date_to' => $request->get('date_to', ''),
]; ];
$page = max(1, (int) $request->get('page', 1)); $page = max(1, (int) $request->get('page', 1));
$perPage = 25; $perPage = 25;
$where = 'm.is_archived = 0'; $where = 'm.is_archived = 0';
$params = []; $params = [];
if ($filters['q'] !== '') { $where .= ' AND (m.full_name_ar LIKE ? OR m.national_id LIKE ? OR m.membership_number LIKE ? OR m.phone_mobile LIKE ? OR m.form_number LIKE ?)'; $s = '%' . $filters['q'] . '%'; $params = array_merge($params, [$s, $s, $s, $s, $s]); }
if ($filters['q'] !== '') { if ($filters['status'] !== '') { $where .= ' AND m.status = ?'; $params[] = $filters['status']; }
$where .= ' AND (m.full_name_ar LIKE ? OR m.national_id LIKE ? OR m.membership_number LIKE ? OR m.phone_mobile LIKE ? OR m.form_number LIKE ?)'; if ($filters['branch_id'] !== '') { $where .= ' AND m.branch_id = ?'; $params[] = (int) $filters['branch_id']; }
$s = '%' . $filters['q'] . '%'; if ($filters['date_from'] !== '') { $where .= ' AND m.created_at >= ?'; $params[] = $filters['date_from'] . ' 00:00:00'; }
$params = array_merge($params, [$s, $s, $s, $s, $s]); if ($filters['date_to'] !== '') { $where .= ' AND m.created_at <= ?'; $params[] = $filters['date_to'] . ' 23:59:59'; }
}
if ($filters['status'] !== '') {
$where .= ' AND m.status = ?';
$params[] = $filters['status'];
}
if ($filters['branch_id'] !== '') {
$where .= ' AND m.branch_id = ?';
$params[] = (int) $filters['branch_id'];
}
if ($filters['date_from'] !== '') {
$where .= ' AND m.created_at >= ?';
$params[] = $filters['date_from'] . ' 00:00:00';
}
if ($filters['date_to'] !== '') {
$where .= ' AND m.created_at <= ?';
$params[] = $filters['date_to'] . ' 23:59:59';
}
$countRow = $db->selectOne("SELECT COUNT(*) as cnt FROM members m WHERE {$where}", $params); $countRow = $db->selectOne("SELECT COUNT(*) as cnt FROM members m WHERE {$where}", $params);
$total = (int) ($countRow['cnt'] ?? 0); $total = (int) ($countRow['cnt'] ?? 0);
$offset = ($page - 1) * $perPage; $offset = ($page - 1) * $perPage;
$members = $db->select("SELECT m.*, b.name_ar as branch_name FROM members m LEFT JOIN branches b ON b.id = m.branch_id WHERE {$where} ORDER BY m.id DESC LIMIT {$perPage} OFFSET {$offset}", $params);
$members = $db->select(
"SELECT m.*, b.name_ar as branch_name
FROM members m
LEFT JOIN branches b ON b.id = m.branch_id
WHERE {$where}
ORDER BY m.id DESC
LIMIT {$perPage} OFFSET {$offset}",
$params
);
$pagination = \App\Core\Pagination::paginate($total, $perPage, $page); $pagination = \App\Core\Pagination::paginate($total, $perPage, $page);
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar"); $branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$statuses = Member::getStatusOptions(); return $this->view('Members.Views.index', ['members' => $members, 'branches' => $branches, 'statuses' => Member::getStatusOptions(), 'filters' => $filters, 'pagination' => $pagination]);
return $this->view('Members.Views.index', [
'members' => $members,
'branches' => $branches,
'statuses' => $statuses,
'filters' => $filters,
'pagination' => $pagination,
]);
} }
public function create(Request $request): Response public function create(Request $request): Response
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar"); $branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$nextFormNumber = FormNumberGenerator::next();
$formFee = FormNumberGenerator::getFormFee();
$needsStartNumber = ($nextFormNumber === null);
return $this->view('Members.Views.create', [ return $this->view('Members.Views.create', [
'branches' => $branches, 'branches' => $branches,
'nextFormNumber' => $nextFormNumber, 'nextFormNumber' => MemberNumberGenerator::next(),
'formFee' => $formFee, 'formFee' => MemberNumberGenerator::getFormFee(),
'needsStartNumber' => $needsStartNumber, 'needsStartNumber' => (MemberNumberGenerator::next() === null),
]); ]);
} }
public function store(Request $request): Response public function store(Request $request): Response
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$employee = App::getInstance()->currentEmployee();
// If super admin is setting the starting form number
$startNumber = $request->post('form_start_number'); $startNumber = $request->post('form_start_number');
if ($startNumber !== null && $startNumber !== '') { if ($startNumber !== null && $startNumber !== '') { MemberNumberGenerator::setStartNumber((int) $startNumber); }
$startNum = (int) $startNumber;
if ($startNum <= 0) {
return $this->redirect('/members/create')->withError('رقم بداية الاستمارات يجب أن يكون أكبر من صفر');
}
FormNumberGenerator::setStartNumber($startNum);
}
// Validate basic fields
$fullNameAr = trim((string) $request->post('full_name_ar', '')); $fullNameAr = trim((string) $request->post('full_name_ar', ''));
$nationalId = trim((string) $request->post('national_id', '')); $nationalId = trim((string) $request->post('national_id', ''));
$phoneMobile = trim((string) $request->post('phone_mobile', '')); $phoneMobile = trim((string) $request->post('phone_mobile', ''));
$branchId = (int) $request->post('branch_id', 0); $branchId = (int) $request->post('branch_id', 0);
$errors = []; $errors = [];
if ($fullNameAr === '' || mb_strlen($fullNameAr) < 5) { if ($fullNameAr === '' || mb_strlen($fullNameAr) < 5) $errors[] = 'الاسم بالعربي مطلوب (5 أحرف على الأقل)';
$errors[] = 'الاسم بالعربي مطلوب (5 أحرف على الأقل)'; if ($phoneMobile === '' || !preg_match('/^01[0125]\d{8}$/', $phoneMobile)) $errors[] = 'رقم المحمول غير صالح';
} if ($branchId <= 0) $errors[] = 'الفرع مطلوب';
if ($phoneMobile === '' || !preg_match('/^01[0125]\d{8}$/', $phoneMobile)) {
$errors[] = 'رقم المحمول غير صالح (01XXXXXXXXX)';
}
if ($branchId <= 0) {
$errors[] = 'الفرع مطلوب';
}
// Parse NID
$dob = null;
$ageYears = null;
$ageMonths = null;
$gender = null;
$govCode = null;
$idType = 'national_id';
$dob = $gender = $govCode = null; $ageYears = $ageMonths = null; $idType = 'national_id';
if ($nationalId !== '') { if ($nationalId !== '') {
if (strlen($nationalId) !== 14 || !ctype_digit($nationalId)) { $parsed = NationalIdParser::parse($nationalId);
$errors[] = 'الرقم القومي يجب أن يكون 14 رقم'; if (!$parsed['is_valid']) { $errors = array_merge($errors, $parsed['errors']); }
} else { else { $dob = $parsed['dob']; $ageYears = $parsed['age_years']; $ageMonths = $parsed['age_months']; $gender = $parsed['gender']; $govCode = $parsed['governorate_code']; }
$parsed = NationalIdParser::parse($nationalId); $dup = $db->selectOne("SELECT id, full_name_ar FROM members WHERE national_id = ? AND is_archived = 0", [$nationalId]);
if (!$parsed['is_valid']) { if ($dup) $errors[] = 'الرقم القومي مسجل بالفعل: ' . $dup['full_name_ar'];
$errors = array_merge($errors, $parsed['errors'] ?? ['الرقم القومي غير صالح']);
} else {
$dob = $parsed['dob'];
$ageYears = $parsed['age_years'];
$ageMonths = $parsed['age_months'];
$gender = $parsed['gender'];
$govCode = $parsed['governorate_code'];
$dup = $db->selectOne(
"SELECT id, full_name_ar, membership_number FROM members WHERE national_id = ? AND is_archived = 0",
[$nationalId]
);
if ($dup) {
$errors[] = 'الرقم القومي مسجل بالفعل: ' . $dup['full_name_ar'] . ' (' . ($dup['membership_number'] ?? 'بدون رقم') . ')';
}
}
}
} else { } else {
$dob = $request->post('date_of_birth', ''); $dob = $request->post('date_of_birth', ''); $gender = $request->post('gender', '');
$gender = $request->post('gender', ''); if (!$dob) $errors[] = 'تاريخ الميلاد مطلوب'; if (!$gender) $errors[] = 'النوع مطلوب';
if ($dob === '') $errors[] = 'تاريخ الميلاد مطلوب'; if ($dob) { $age = age_from_dob($dob); $ageYears = $age['years']; $ageMonths = $age['months']; }
if ($gender === '') $errors[] = 'النوع مطلوب';
if ($dob) {
$age = age_from_dob($dob);
$ageYears = $age['years'];
$ageMonths = $age['months'];
}
$idType = 'passport'; $idType = 'passport';
} }
if (!empty($errors)) { $session = App::getInstance()->session(); $session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors)); $session->flash('_old_input', $request->all()); return $this->redirect('/members/create'); }
if (!empty($errors)) { $formNumber = MemberNumberGenerator::next();
$session = App::getInstance()->session(); if (!$formNumber) return $this->redirect('/members/create')->withError('يجب تحديد رقم بداية الاستمارات');
$session->flash('_alerts', array_map(fn($e) => ['type' => 'error', 'message' => $e], $errors));
$session->flash('_old_input', $request->all());
return $this->redirect('/members/create');
}
$formNumber = FormNumberGenerator::next();
if ($formNumber === null) {
return $this->redirect('/members/create')->withError('يجب تحديد رقم بداية الاستمارات أولاً');
}
$member = Member::create([
'full_name_ar' => $fullNameAr,
'national_id' => $nationalId ?: null,
'passport_number' => $request->post('passport_number') ?: null,
'id_type' => $idType,
'date_of_birth' => $dob,
'age_years' => $ageYears,
'age_months' => $ageMonths,
'gender' => $gender,
'governorate_code' => $govCode,
'phone_mobile' => $phoneMobile,
'branch_id' => $branchId,
'nationality' => 'مصري',
'form_number' => (string) $formNumber,
'form_date' => date('Y-m-d'),
'status' => 'potential',
'membership_type' => 'working',
'member_category' => 'working_member',
]);
EventBus::dispatch('member.created', [
'member_id' => (int) $member->id,
'form_number' => (string) $formNumber,
]);
$formFee = FormNumberGenerator::getFormFee();
return $this->redirect('/members/' . $member->id) $member = Member::create(['full_name_ar' => $fullNameAr, 'national_id' => $nationalId ?: null, 'passport_number' => $request->post('passport_number') ?: null, 'id_type' => $idType, 'date_of_birth' => $dob, 'age_years' => $ageYears, 'age_months' => $ageMonths, 'gender' => $gender, 'governorate_code' => $govCode, 'phone_mobile' => $phoneMobile, 'branch_id' => $branchId, 'nationality' => 'مصري', 'form_number' => (string) $formNumber, 'form_date' => date('Y-m-d'), 'status' => 'potential', 'membership_type' => 'working', 'member_category' => 'working_member']);
->withSuccess('تم تسجيل العضو — استمارة رقم: ' . $formNumber . ' — يجب دفع رسوم الاستمارة ' . money($formFee) . ' لملء البيانات'); EventBus::dispatch('member.created', ['member_id' => (int) $member->id, 'form_number' => (string) $formNumber]);
return $this->redirect('/members/' . $member->id)->withSuccess('تم تسجيل العضو — استمارة رقم: ' . $formNumber);
} }
public function show(Request $request, string $id): Response public function show(Request $request, string $id): Response
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member || $member->is_archived) { if (!$member || $member->is_archived) return $this->redirect('/members')->withError('العضو غير موجود');
return $this->redirect('/members')->withError('العضو غير موجود');
}
$branch = $db->selectOne("SELECT name_ar FROM branches WHERE id = ?", [(int) $member->branch_id]); $branch = $db->selectOne("SELECT name_ar FROM branches WHERE id = ?", [(int) $member->branch_id]);
$qualification = $member->qualification_id $qualification = $member->qualification_id ? $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member->qualification_id]) : null;
? $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [(int) $member->qualification_id])
: null; $spouses = []; $children = [];
try { $spouses = $db->select("SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order", [(int) $id]); } catch (\Throwable $e) {}
// Check if form fee is paid try { $children = $db->select("SELECT * FROM children WHERE member_id = ? AND is_archived = 0 ORDER BY child_order", [(int) $id]); } catch (\Throwable $e) {}
$formFeePaid = false;
$formFeePayment = null; $bill = BillingService::getMemberBill((int) $id);
try {
$formFeePayment = $db->selectOne(
"SELECT p.*, r.receipt_number
FROM payments p
LEFT JOIN receipts r ON r.id = p.receipt_id
WHERE p.member_id = ? AND p.payment_type = 'form_fee' AND p.is_voided = 0
LIMIT 1",
[(int) $id]
);
$formFeePaid = ($formFeePayment !== null);
} catch (\Throwable $e) {}
// Load family data
$spouses = [];
$children = [];
try {
$spouses = $db->select(
"SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_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 $e) {}
// Load payments
$payments = [];
$totalPaid = '0.00';
try {
$payments = $db->select(
"SELECT p.*, r.receipt_number FROM payments p LEFT JOIN receipts r ON r.id = p.receipt_id
WHERE p.member_id = ? AND p.is_voided = 0 ORDER BY p.payment_date DESC LIMIT 10",
[(int) $id]
);
$totalPaidRow = $db->selectOne(
"SELECT COALESCE(SUM(amount), 0) as total FROM payments WHERE member_id = ? AND is_voided = 0",
[(int) $id]
);
$totalPaid = $totalPaidRow['total'] ?? '0.00';
} catch (\Throwable $e) {}
$notes = $member->getNotes();
$formFee = FormNumberGenerator::getFormFee();
$formFilled = ($member->qualification_id !== null && $member->qualification_id > 0); $formFilled = ($member->qualification_id !== null && $member->qualification_id > 0);
return $this->view('Members.Views.show', [ return $this->view('Members.Views.show', [
'member' => $member, 'member' => $member,
'branchName' => $branch['name_ar'] ?? '—', 'branchName' => $branch['name_ar'] ?? '—',
'qualificationName' => $qualification['name_ar'] ?? '—', 'qualificationName' => $qualification['name_ar'] ?? '—',
'spouses' => $spouses, 'spouses' => $spouses,
'children' => $children, 'children' => $children,
'payments' => $payments, 'bill' => $bill,
'totalPaid' => $totalPaid, 'formFee' => MemberNumberGenerator::getFormFee(),
'notes' => $notes, 'formFilled' => $formFilled,
'formFee' => $formFee,
'formFilled' => $formFilled,
'formFeePaid' => $formFeePaid,
'formFeePayment' => $formFeePayment,
]); ]);
} }
/**
* Pay form fee — quick inline payment
*/
public function payFormFee(Request $request, string $id): Response public function payFormFee(Request $request, string $id): Response
{ {
$db = App::getInstance()->db();
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member || $member->is_archived) { if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
return $this->redirect('/members')->withError('العضو غير موجود');
}
// Check if already paid $result = PaymentService::processPayment([
$existing = $db->selectOne( 'member_id' => (int) $id, 'amount' => MemberNumberGenerator::getFormFee(),
"SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0", 'payment_type' => 'form_fee', 'payment_method' => $request->post('payment_method', 'cash'),
[(int) $id] 'related_entity_type' => 'members', 'related_entity_id' => (int) $id,
); 'description' => 'رسوم استمارة عضوية رقم ' . ($member->form_number ?? ''),
if ($existing) { ]);
return $this->redirect('/members/' . $id)->withError('رسوم الاستمارة مدفوعة بالفعل'); if (!$result['success']) return $this->redirect('/members/' . $id)->withError($result['error']);
} return $this->redirect('/members/' . $id)->withSuccess('تم دفع رسوم الاستمارة — إيصال: ' . $result['receipt_number']);
}
public function payMembership(Request $request, string $id): Response
{
$db = App::getInstance()->db();
$member = Member::find((int) $id);
if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
$formFee = FormNumberGenerator::getFormFee(); $paymentType = trim((string) $request->post('payment_type', ''));
$employee = App::getInstance()->currentEmployee(); $amount = trim((string) $request->post('amount', '0'));
$paymentMethod = trim((string) $request->post('payment_method', 'cash')); $paymentMethod = trim((string) $request->post('payment_method', 'cash'));
$db->beginTransaction(); if (bccomp($amount, '0.01', 2) < 0) return $this->redirect('/members/' . $id)->withError('المبلغ غير صالح');
try {
// Create receipt number $result = PaymentService::processPayment([
$year = date('Y'); 'member_id' => (int) $id, 'amount' => $amount,
$prefix = 'REC-' . $year . '-'; 'payment_type' => $paymentType, 'payment_method' => $paymentMethod,
$lastReceipt = $db->selectOne( 'related_entity_type' => 'members', 'related_entity_id' => (int) $id,
"SELECT receipt_number FROM receipts WHERE receipt_number LIKE ? ORDER BY id DESC LIMIT 1", 'description' => ($paymentType === 'down_payment' ? 'مقدم تقسيط' : 'قيمة العضوية') . ' — استمارة ' . ($member->form_number ?? ''),
[$prefix . '%'] ]);
); if (!$result['success']) return $this->redirect('/members/' . $id)->withError($result['error']);
if ($lastReceipt) {
$parts = explode('-', $lastReceipt['receipt_number']); // ── Post-payment: Assign membership number + activate ──
$seq = (int) end($parts) + 1; $membershipNumber = MemberNumberGenerator::assign((int) $id);
} else { $member->update(['status' => 'active']);
$seq = 1;
// If installment, create plan
if ($paymentType === 'down_payment') {
$membershipValue = $member->membership_value ?? '0.00';
$remaining = bcsub($membershipValue, $amount, 2);
if (bccomp($remaining, '0', 2) > 0) {
$months = min(30, max(1, (int) $request->post('installment_months', 30)));
$interestRate = '22.00';
$totalInterest = bcdiv(bcmul($remaining, $interestRate, 4), '100', 2);
$totalWithInterest = bcadd($remaining, $totalInterest, 2);
$monthlyPayment = bcdiv($totalWithInterest, (string) $months, 2);
$planId = $db->insert('installment_plans', [
'member_id' => (int) $id, 'related_entity_type' => 'members', 'related_entity_id' => (int) $id,
'total_amount' => $membershipValue, 'down_payment' => $amount,
'remaining_balance' => $remaining, 'interest_rate' => $interestRate,
'total_interest' => $totalInterest, 'total_with_interest' => $totalWithInterest,
'number_of_months' => $months, 'monthly_payment' => $monthlyPayment,
'start_date' => date('Y-m-d'), 'status' => 'active',
'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
]);
$rem = $totalWithInterest;
for ($i = 1; $i <= $months; $i++) {
$principal = bcdiv($remaining, (string) $months, 2);
$interest = bcdiv($totalInterest, (string) $months, 2);
$instAmount = bcadd($principal, $interest, 2);
$rem = bcsub($rem, $instAmount, 2);
if (bccomp($rem, '0', 2) < 0) $rem = '0.00';
$db->insert('installment_schedule', [
'installment_plan_id' => $planId, 'installment_number' => $i,
'due_date' => date('Y-m-d', strtotime("+{$i} months")),
'amount' => $instAmount, 'principal' => $principal, 'interest' => $interest,
'remaining_after' => $rem, 'paid_amount' => '0.00', 'status' => 'pending',
'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s'),
]);
}
} }
$receiptNumber = $prefix . str_pad((string) $seq, 6, '0', STR_PAD_LEFT);
// Create payment
$paymentId = $db->insert('payments', [
'member_id' => (int) $id,
'payment_type' => 'form_fee',
'amount' => $formFee,
'currency' => 'EGP',
'payment_method' => $paymentMethod,
'payment_date' => date('Y-m-d'),
'received_by_employee_id' => $employee ? (int) $employee->id : null,
'related_entity_type' => 'members',
'related_entity_id' => (int) $id,
'notes' => 'رسوم استمارة عضوية رقم ' . ($member->form_number ?? ''),
'is_voided' => 0,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
'created_by' => $employee ? (int) $employee->id : null,
]);
// Create receipt
$receiptId = $db->insert('receipts', [
'receipt_number' => $receiptNumber,
'member_id' => (int) $id,
'payment_id' => $paymentId,
'receipt_type' => 'payment',
'amount' => $formFee,
'amount_in_words_ar' => number_to_arabic_words((float) $formFee),
'description_ar' => 'رسوم استمارة عضوية رقم ' . ($member->form_number ?? ''),
'issued_by_employee_id' => $employee ? (int) $employee->id : null,
'issued_at' => date('Y-m-d H:i:s'),
'is_voided' => 0,
'print_count' => 0,
'created_at' => date('Y-m-d H:i:s'),
]);
// Link receipt to payment
$db->update('payments', ['receipt_id' => $receiptId], '`id` = ?', [$paymentId]);
$db->commit();
EventBus::dispatch('payment.completed', [
'payment_id' => $paymentId,
'receipt_id' => $receiptId,
'member_id' => (int) $id,
'type' => 'form_fee',
'amount' => $formFee,
]);
return $this->redirect('/members/' . $id)
->withSuccess('تم دفع رسوم الاستمارة ' . money($formFee) . ' — إيصال رقم: ' . $receiptNumber . ' — يمكنك الآن ملء الاستمارة');
} catch (\Throwable $e) {
$db->rollBack();
return $this->redirect('/members/' . $id)->withError('فشل تسجيل الدفع: ' . $e->getMessage());
} }
EventBus::dispatch('member.activated', ['member_id' => (int) $id, 'membership_number' => $membershipNumber]);
$msg = '✅ تم السداد — رقم العضوية: ' . $membershipNumber . ' — إيصال: ' . $result['receipt_number'];
if ($paymentType === 'down_payment') $msg .= ' — تم إنشاء خطة تقسيط';
return $this->redirect('/members/' . $id)->withSuccess($msg);
} }
/**
* Fill form — BLOCKED if form fee not paid
*/
public function fillForm(Request $request, string $id): Response public function fillForm(Request $request, string $id): Response
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member || $member->is_archived) { if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
return $this->redirect('/members')->withError('العضو غير موجود'); $formFeePaid = $db->selectOne("SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 LIMIT 1", [(int) $id]);
} if (!$formFeePaid) return $this->redirect('/members/' . $id)->withError('⚠ يجب دفع رسوم الاستمارة أولاً');
// CHECK: Form fee must be paid
$formFeePaid = $db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 LIMIT 1",
[(int) $id]
);
if (!$formFeePaid) {
return $this->redirect('/members/' . $id)
->withError('⚠ يجب دفع رسوم الاستمارة (' . money(FormNumberGenerator::getFormFee()) . ') قبل ملء البيانات');
}
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order");
$governorates = $db->select("SELECT code, name_ar FROM governorates WHERE is_active = 1 ORDER BY name_ar");
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
return $this->view('Members.Views.fill-form', [ return $this->view('Members.Views.fill-form', [
'member' => $member, 'member' => $member,
'branches' => $branches, 'branches' => $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar"),
'qualifications' => $qualifications, 'qualifications' => $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order"),
'governorates' => $governorates, 'governorates' => $db->select("SELECT code, name_ar FROM governorates WHERE is_active = 1 ORDER BY name_ar"),
'countries' => $countries, 'countries' => $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar"),
]); ]);
} }
...@@ -435,176 +228,59 @@ class MemberController extends Controller ...@@ -435,176 +228,59 @@ class MemberController extends Controller
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member || $member->is_archived) { if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
return $this->redirect('/members')->withError('العضو غير موجود'); $formFeePaid = $db->selectOne("SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 LIMIT 1", [(int) $id]);
} if (!$formFeePaid) return $this->redirect('/members/' . $id)->withError('⚠ يجب دفع رسوم الاستمارة أولاً');
// CHECK: Form fee must be paid
$formFeePaid = $db->selectOne(
"SELECT id FROM payments WHERE member_id = ? AND payment_type = 'form_fee' AND is_voided = 0 LIMIT 1",
[(int) $id]
);
if (!$formFeePaid) {
return $this->redirect('/members/' . $id)
->withError('⚠ يجب دفع رسوم الاستمارة قبل ملء البيانات');
}
$data = $request->all();
unset($data['_csrf_token']);
$updateFields = [
'full_name_en', 'place_of_birth', 'nationality', 'religion',
'qualification_id', 'marital_status', 'id_issue_date', 'id_expiry_date',
'phone_home', 'phone_international', 'email',
'emergency_name', 'emergency_phone',
'residence_type', 'residence_address', 'landmark', 'floor', 'apartment',
'area', 'governorate', 'correspondence_address',
'employment_type', 'occupation', 'job_title', 'employment_date',
'business_address', 'office_phone', 'office_fax', 'business_activity',
'referral_source',
];
$data = $request->all(); unset($data['_csrf_token']);
$fields = ['full_name_en','place_of_birth','nationality','religion','qualification_id','marital_status','id_issue_date','id_expiry_date','phone_home','phone_international','email','emergency_name','emergency_phone','residence_type','residence_address','landmark','floor','apartment','area','governorate','correspondence_address','employment_type','occupation','job_title','employment_date','business_address','office_phone','office_fax','business_activity','referral_source'];
$update = []; $update = [];
foreach ($updateFields as $field) { foreach ($fields as $f) { if (array_key_exists($f, $data)) { $v = trim((string) ($data[$f] ?? '')); $update[$f] = $v === '' ? null : $v; } }
if (array_key_exists($field, $data)) { if (isset($update['qualification_id'])) $update['qualification_id'] = $update['qualification_id'] ? (int) $update['qualification_id'] : null;
$val = trim((string) ($data[$field] ?? ''));
$update[$field] = ($val === '') ? null : $val;
}
}
if (isset($update['qualification_id'])) {
$update['qualification_id'] = $update['qualification_id'] ? (int) $update['qualification_id'] : null;
}
// Calculate membership value based on branch + qualification
if (!empty($update['qualification_id'])) { if (!empty($update['qualification_id'])) {
try { $pricing = $db->selectOne("SELECT price FROM pricing_configs WHERE branch_id = ? AND qualification_id = ? AND membership_type = 'working' AND is_active = 1 AND effective_from <= CURDATE() AND (effective_to IS NULL OR effective_to >= CURDATE()) ORDER BY effective_from DESC LIMIT 1", [(int) $member->branch_id, (int) $update['qualification_id']]);
$pricing = $db->selectOne( if ($pricing) $update['membership_value'] = $pricing['price'];
"SELECT price FROM pricing_configs
WHERE branch_id = ? AND qualification_id = ? AND membership_type = 'working'
AND is_active = 1 AND effective_from <= CURDATE()
AND (effective_to IS NULL OR effective_to >= CURDATE())
ORDER BY effective_from DESC LIMIT 1",
[(int) $member->branch_id, (int) $update['qualification_id']]
);
if ($pricing) {
$update['membership_value'] = $pricing['price'];
}
} catch (\Throwable $e) {}
}
if ($member->status === 'potential') {
$update['status'] = 'under_review';
}
if (!empty($update)) {
$member->update($update);
} }
if ($member->status === 'potential') $update['status'] = 'under_review';
EventBus::dispatch('member.form_filled', [ if (!empty($update)) $member->update($update);
'member_id' => (int) $id, return $this->redirect('/members/' . $id)->withSuccess('تم ملء الاستمارة');
'form_number' => $member->form_number,
]);
return $this->redirect('/members/' . $id)
->withSuccess('تم ملء الاستمارة بنجاح — الحالة: تحت المراجعة');
} }
public function edit(Request $request, string $id): Response public function edit(Request $request, string $id): Response
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member || $member->is_archived) { if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
return $this->redirect('/members')->withError('العضو غير موجود'); return $this->view('Members.Views.edit', ['member' => $member, 'branches' => $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1"), 'qualifications' => $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order"), 'governorates' => $db->select("SELECT code, name_ar FROM governorates WHERE is_active = 1"), 'countries' => $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar")]);
}
$branches = $db->select("SELECT id, name_ar FROM branches WHERE is_active = 1 ORDER BY name_ar");
$qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order");
$governorates = $db->select("SELECT code, name_ar FROM governorates WHERE is_active = 1 ORDER BY name_ar");
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
return $this->view('Members.Views.edit', [
'member' => $member,
'branches' => $branches,
'qualifications' => $qualifications,
'governorates' => $governorates,
'countries' => $countries,
]);
} }
public function update(Request $request, string $id): Response public function update(Request $request, string $id): Response
{ {
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member || $member->is_archived) { if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
return $this->redirect('/members')->withError('العضو غير موجود'); $data = $request->all(); unset($data['_csrf_token']);
} $allowed = ['full_name_en','phone_home','phone_mobile','phone_international','email','emergency_name','emergency_phone','residence_type','residence_address','landmark','floor','apartment','area','governorate','correspondence_address','employment_type','occupation','job_title','employment_date','business_address','office_phone','office_fax','business_activity','referral_source','religion','marital_status'];
$data = $request->all();
unset($data['_csrf_token']);
$allowedFields = [
'full_name_en', 'phone_home', 'phone_mobile', 'phone_international',
'email', 'emergency_name', 'emergency_phone',
'residence_type', 'residence_address', 'landmark', 'floor', 'apartment',
'area', 'governorate', 'correspondence_address',
'employment_type', 'occupation', 'job_title', 'employment_date',
'business_address', 'office_phone', 'office_fax', 'business_activity',
'referral_source', 'religion', 'marital_status',
];
$update = []; $update = [];
foreach ($allowedFields as $field) { foreach ($allowed as $f) { if (array_key_exists($f, $data)) { $v = trim((string) ($data[$f] ?? '')); $update[$f] = $v === '' ? null : $v; } }
if (array_key_exists($field, $data)) { if (!empty($update)) $member->update($update);
$val = trim((string) ($data[$field] ?? '')); return $this->redirect('/members/' . $id)->withSuccess('تم تحديث البيانات');
$update[$field] = ($val === '') ? null : $val;
}
}
if (!empty($update)) {
$member->update($update);
}
return $this->redirect('/members/' . $id)->withSuccess('تم تحديث بيانات العضو');
} }
public function changeStatus(Request $request, string $id): Response public function changeStatus(Request $request, string $id): Response
{ {
$member = Member::find((int) $id); $member = Member::find((int) $id);
if (!$member) { if (!$member) return $this->redirect('/members')->withError('العضو غير موجود');
return $this->redirect('/members')->withError('العضو غير موجود');
}
$newStatus = trim((string) $request->post('status', '')); $newStatus = trim((string) $request->post('status', ''));
$validStatuses = array_keys(Member::getStatusOptions()); if (!in_array($newStatus, array_keys(Member::getStatusOptions()))) return $this->redirect('/members/' . $id)->withError('حالة غير صالحة');
if (!in_array($newStatus, $validStatuses)) { $old = $member->status; $member->update(['status' => $newStatus]);
return $this->redirect('/members/' . $id)->withError('حالة غير صالحة'); EventBus::dispatch('member.status_changed', ['member_id' => (int) $id, 'old' => $old, 'new' => $newStatus]);
} return $this->redirect('/members/' . $id)->withSuccess('تم تغيير الحالة');
$oldStatus = $member->status;
$member->update(['status' => $newStatus]);
EventBus::dispatch('member.status_changed', [
'member_id' => (int) $id,
'old_status' => $oldStatus,
'new_status' => $newStatus,
]);
return $this->redirect('/members/' . $id)
->withSuccess('تم تغيير الحالة إلى: ' . (Member::getStatusOptions()[$newStatus] ?? $newStatus));
} }
public function search(Request $request): Response public function search(Request $request): Response
{ {
$q = trim((string) $request->get('q', '')); $q = trim((string) $request->get('q', ''));
$results = []; return $this->view('Members.Views.search', ['query' => $q, 'results' => ($q !== '' && mb_strlen($q) >= 2) ? MemberSearchService::search($q, 50) : []]);
if ($q !== '' && mb_strlen($q) >= 2) {
$results = MemberSearchService::search($q, 50);
}
return $this->view('Members.Views.search', [
'query' => $q,
'results' => $results,
]);
} }
} }
\ No newline at end of file
...@@ -2,24 +2,18 @@ ...@@ -2,24 +2,18 @@
declare(strict_types=1); declare(strict_types=1);
return [ return [
// Member CRUD ['GET', '/members', 'Members\Controllers\MemberController@index', ['auth'], 'member.view'],
['GET', '/members', 'Members\Controllers\MemberController@index', ['auth'], 'member.view'], ['GET', '/members/create', 'Members\Controllers\MemberController@create', ['auth'], 'member.create'],
['GET', '/members/create', 'Members\Controllers\MemberController@create', ['auth'], 'member.create'], ['POST', '/members', 'Members\Controllers\MemberController@store', ['auth'], 'member.create'],
['POST', '/members', 'Members\Controllers\MemberController@store', ['auth'], 'member.create'], ['GET', '/members/search', 'Members\Controllers\MemberController@search', ['auth'], 'member.view'],
['GET', '/members/search', 'Members\Controllers\MemberController@search', ['auth'], 'member.view'], ['GET', '/members/{id}', 'Members\Controllers\MemberController@show', ['auth'], 'member.view'],
['GET', '/members/{id}', 'Members\Controllers\MemberController@show', ['auth'], 'member.view'], ['GET', '/members/{id}/edit', 'Members\Controllers\MemberController@edit', ['auth'], 'member.edit'],
['GET', '/members/{id}/edit', 'Members\Controllers\MemberController@edit', ['auth'], 'member.edit'], ['POST', '/members/{id}', 'Members\Controllers\MemberController@update', ['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}/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'],
// Form fee payment ['GET', '/members/{id}/fill-form', 'Members\Controllers\MemberController@fillForm', ['auth'], 'member.edit'],
['POST', '/members/{id}/pay-form-fee', 'Members\Controllers\MemberController@payFormFee', ['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'],
// Fill Form (استمارة) — blocked until form fee paid ['POST', '/api/members/search', 'Members\Controllers\MemberApiController@search', ['auth'], 'member.view'],
['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'],
]; ];
\ 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
...@@ -11,119 +11,229 @@ ...@@ -11,119 +11,229 @@
<?php <?php
$statusLabel = $member->getStatusLabel(); $statusLabel = $member->getStatusLabel();
$statusColor = $member->getStatusColor(); $statusColor = $member->getStatusColor();
$isInitialPhase = in_array($member->status, ['potential', 'under_review', 'interview_scheduled', 'accepted', 'payment_pending']);
?> ?>
<!-- ══════════════════════════════════════════════════════ --> <!-- ═══════════════════════════════════════ -->
<!-- STEP 1: Form Fee Not Paid — BIG RED BLOCKER --> <!-- STEP 1: Form Fee Not Paid -->
<!-- ══════════════════════════════════════════════════════ --> <!-- ═══════════════════════════════════════ -->
<?php if (!$formFeePaid && $member->status === 'potential'): ?> <?php if (!$bill['form_fee_paid'] && $member->status === 'potential'): ?>
<div class="card" style="margin-bottom:20px;padding:30px;background:linear-gradient(135deg, #FEF2F2, #FEE2E2);border:2px solid #DC2626;"> <div class="card" style="margin-bottom:20px;padding:30px;background:linear-gradient(135deg, #FEF2F2, #FEE2E2);border:2px solid #DC2626;">
<div style="text-align:center;margin-bottom:20px;"> <div style="text-align:center;margin-bottom:20px;">
<div style="font-size:48px;">🔒</div> <div style="font-size:48px;">🔒</div>
<h2 style="color:#DC2626;margin:10px 0 5px;">يجب دفع رسوم الاستمارة أولاً</h2> <h2 style="color:#DC2626;margin:10px 0 5px;">يجب دفع رسوم الاستمارة أولاً</h2>
<p style="color:#6B7280;font-size:14px;margin:0;">لا يمكن ملء بيانات الاستمارة قبل دفع الرسوم</p>
</div> </div>
<div style="max-width:500px;margin:0 auto;background:#fff;border-radius:12px;padding:25px;box-shadow:0 4px 12px rgba(0,0,0,0.1);"> <div style="max-width:500px;margin:0 auto;background:#fff;border-radius:12px;padding:25px;box-shadow:0 4px 12px rgba(0,0,0,0.1);">
<table style="width:100%;font-size:15px;margin-bottom:20px;"> <table style="width:100%;font-size:15px;margin-bottom:20px;">
<tr> <tr><td style="padding:8px 0;color:#6B7280;">رقم الاستمارة</td><td style="padding:8px 0;text-align:left;font-weight:700;font-size:20px;color:#0D7377;"><?= e($member->form_number ?? '—') ?></td></tr>
<td style="padding:8px 0;color:#6B7280;">رقم الاستمارة</td> <tr><td style="padding:8px 0;color:#6B7280;">رسوم الاستمارة</td><td style="padding:8px 0;text-align:left;font-weight:700;font-size:24px;color:#DC2626;"><?= money($formFee) ?></td></tr>
<td style="padding:8px 0;text-align:left;font-weight:700;font-size:20px;color:#0D7377;"><?= e($member->form_number ?? '—') ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">اسم العضو</td>
<td style="padding:8px 0;text-align:left;font-weight:600;"><?= e($member->full_name_ar) ?></td>
</tr>
<tr>
<td style="padding:8px 0;color:#6B7280;">رسوم الاستمارة</td>
<td style="padding:8px 0;text-align:left;font-weight:700;font-size:24px;color:#DC2626;"><?= money($formFee) ?></td>
</tr>
</table> </table>
<form method="POST" action="/members/<?= (int) $member->id ?>/pay-form-fee"> <form method="POST" action="/members/<?= (int) $member->id ?>/pay-form-fee">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="form-group" style="margin-bottom:15px;"> <div class="form-group" style="margin-bottom:15px;">
<label class="form-label">طريقة الدفع</label> <label class="form-label">طريقة الدفع</label>
<select name="payment_method" class="form-select" style="font-size:16px;"> <select name="payment_method" class="form-select"><option value="cash">نقدي 💵</option><option value="visa">فيزا 💳</option><option value="bank_transfer">تحويل بنكي 🏦</option></select>
<option value="cash">نقدي 💵</option>
<option value="visa">فيزا / بطاقة 💳</option>
<option value="bank_transfer">تحويل بنكي 🏦</option>
</select>
</div> </div>
<button type="submit" class="btn btn-primary" style="width:100%;padding:15px;font-size:18px;background:#059669;border-color:#059669;" onclick="return confirm('تأكيد دفع رسوم الاستمارة <?= e(money($formFee)) ?>؟')"> <button type="submit" class="btn btn-primary" style="width:100%;padding:15px;font-size:18px;background:#059669;border-color:#059669;" onclick="return confirm('تأكيد دفع <?= money($formFee) ?>؟')">💰 دفع رسوم الاستمارة</button>
💰 تسجيل دفع رسوم الاستمارة
</button>
</form> </form>
</div> </div>
</div> </div>
<!-- ══════════════════════════════════════════════════════ --> <!-- ═══════════════════════════════════════ -->
<!-- STEP 2: Fee Paid but Form Not Filled --> <!-- STEP 2: Fee Paid, Form Not Filled -->
<!-- ══════════════════════════════════════════════════════ --> <!-- ═══════════════════════════════════════ -->
<?php elseif ($formFeePaid && !$formFilled && in_array($member->status, ['potential', 'under_review'])): ?> <?php elseif ($bill['form_fee_paid'] && !$formFilled && $member->status === 'potential'): ?>
<div class="card" style="margin-bottom:20px;padding:25px;background:linear-gradient(135deg, #F0FDF4, #DCFCE7);border:2px solid #059669;"> <div class="card" style="margin-bottom:20px;padding:25px;background:#F0FDF4;border:2px solid #059669;">
<div style="display:flex;justify-content:space-between;align-items:center;"> <div style="display:flex;justify-content:space-between;align-items:center;">
<div> <div>
<h3 style="color:#059669;margin:0 0 8px;">✅ تم دفع رسوم الاستمارة — جاهز لملء البيانات</h3> <h3 style="color:#059669;margin:0 0 8px;">✅ تم دفع رسوم الاستمارة — الآن قم بملء البيانات</h3>
<p style="color:#6B7280;margin:0;font-size:14px;"> <p style="color:#6B7280;margin:0;font-size:14px;">استمارة رقم <strong><?= e($member->form_number) ?></strong></p>
استمارة رقم <strong style="font-size:18px;color:#0D7377;"><?= e($member->form_number) ?></strong>
— إيصال: <strong><?= e($formFeePayment['receipt_number'] ?? '—') ?></strong>
— المبلغ: <strong><?= money($formFee) ?></strong>
</p>
</div> </div>
<a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary" style="padding:15px 30px;font-size:18px;"> <a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary" style="padding:15px 30px;font-size:18px;">📝 ملء الاستمارة</a>
📝 ملء الاستمارة الآن
</a>
</div> </div>
</div> </div>
<?php endif; ?> <?php endif; ?>
<!-- Member Header Card --> <!-- Member Header -->
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;padding:25px;">
<div style="padding:25px;"> <div style="display:flex;justify-content:space-between;align-items:start;">
<div style="display:flex;justify-content:space-between;align-items:start;"> <div>
<div> <h2 style="margin:0 0 5px;color:#1A1A2E;font-size:22px;"><?= e($member->full_name_ar) ?></h2>
<h2 style="margin:0 0 5px;color:#1A1A2E;font-size:22px;"><?= e($member->full_name_ar) ?></h2> <?php if ($member->full_name_en): ?><div style="color:#6B7280;font-size:14px;margin-bottom:8px;"><?= e($member->full_name_en) ?></div><?php endif; ?>
<?php if ($member->full_name_en): ?> <div style="display:flex;gap:15px;flex-wrap:wrap;font-size:13px;color:#6B7280;margin-top:10px;">
<div style="color:#6B7280;font-size:14px;margin-bottom:8px;"><?= e($member->full_name_en) ?></div> <?php if ($member->membership_number): ?><span>🪪 عضوية: <strong style="color:#0D7377;font-size:16px;"><?= e($member->membership_number) ?></strong></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 if ($member->age_years): ?><span>🎂 <?= (int) $member->age_years ?> سنة</span><?php endif; ?>
</div>
</div>
<div style="text-align:left;">
<span style="display:inline-block;padding:6px 16px;border-radius:20px;font-weight:700;font-size:14px;color:#fff;background:<?= $statusColor ?>;"><?= e($statusLabel) ?></span>
<div style="margin-top:10px;font-size:12px;color:#9CA3AF;">تاريخ: <?= e(substr($member->created_at, 0, 10)) ?></div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════ -->
<!-- BILL / INVOICE — visible during initial phase -->
<!-- ═══════════════════════════════════════════════ -->
<?php if ($isInitialPhase && $formFilled): ?>
<div class="card" style="margin-bottom:20px;border:2px solid #0D7377;">
<div style="padding:15px 20px;background:#0D7377;color:#fff;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;">🧾 فاتورة العضوية</h3>
<div style="text-align:left;">
<div style="font-size:12px;opacity:0.8;">الإجمالي المطلوب</div>
<div style="font-size:28px;font-weight:700;"><?= money($bill['total_pending']) ?></div>
</div>
</div>
<div style="padding:20px;">
<table style="width:100%;font-size:14px;border-collapse:collapse;">
<thead>
<tr style="border-bottom:2px solid #E5E7EB;">
<th style="padding:10px;text-align:right;color:#6B7280;">البند</th>
<th style="padding:10px;text-align:left;color:#6B7280;width:140px;">المبلغ</th>
<th style="padding:10px;text-align:center;color:#6B7280;width:100px;">الحالة</th>
</tr>
</thead>
<tbody>
<?php foreach ($bill['items'] as $item): ?>
<tr style="border-bottom:1px solid #F3F4F6;">
<td style="padding:10px;">
<?= e($item['label']) ?>
<?php if (!empty($item['included_note'])): ?>
<small style="display:block;color:#059669;font-size:11px;"><?= e($item['included_note']) ?></small>
<?php endif; ?>
</td>
<td style="padding:10px;text-align:left;font-weight:600;direction:ltr;<?= $item['included'] ? 'color:#9CA3AF;text-decoration:line-through;' : '' ?>">
<?php if ($item['included']): ?>
<?= money($item['amount']) ?>
<?php else: ?>
<?= money($item['amount']) ?>
<?php endif; ?>
</td>
<td style="padding:10px;text-align:center;">
<?php if ($item['paid']): ?>
<span style="color:#059669;font-weight:700;">✅ مدفوع</span>
<?php elseif ($item['included']): ?>
<span style="color:#0284C7;font-size:12px;">مشمول</span>
<?php else: ?>
<span style="color:#DC2626;font-weight:600;">⏳ معلق</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<?php if (bccomp($bill['total_included'], '0', 2) > 0): ?>
<tr style="background:#EFF6FF;">
<td style="padding:10px;font-weight:600;color:#0284C7;">بنود مشمولة في قيمة العضوية</td>
<td style="padding:10px;text-align:left;color:#0284C7;"><?= money($bill['total_included']) ?></td>
<td></td>
</tr>
<?php endif; ?> <?php endif; ?>
<div style="display:flex;gap:15px;flex-wrap:wrap;font-size:13px;color:#6B7280;margin-top:10px;"> <tr style="background:#F0FDF4;">
<?php if ($member->membership_number): ?> <td style="padding:10px;font-weight:600;color:#059669;">إجمالي المدفوع</td>
<span>🪪 رقم العضوية: <strong style="color:#0D7377;font-size:16px;"><?= e($member->membership_number) ?></strong></span> <td style="padding:10px;text-align:left;font-weight:700;color:#059669;"><?= money($bill['total_paid']) ?></td>
<?php endif; ?> <td></td>
<span>📋 استمارة: <strong style="color:#D97706;"><?= e($member->form_number ?? '—') ?></strong></span> </tr>
<span>🏢 <?= e($branchName) ?></span> <tr style="background:#FEF2F2;">
<span><?= $member->gender === 'male' ? '👨' : '👩' ?> <?= $member->getGenderLabel() ?></span> <td style="padding:12px;font-weight:700;font-size:16px;color:#DC2626;">المتبقي للسداد</td>
<?php if ($member->age_years): ?> <td style="padding:12px;text-align:left;font-weight:700;font-size:20px;color:#DC2626;"><?= money($bill['total_pending']) ?></td>
<span>🎂 <?= (int) $member->age_years ?> سنة</span> <td></td>
<?php endif; ?> </tr>
<?php if ($formFeePaid): ?> </tfoot>
<span style="color:#059669;">✅ الرسوم مدفوعة</span> </table>
<?php else: ?> </div>
<span style="color:#DC2626;">❌ الرسوم غير مدفوعة</span>
<?php endif; ?> <!-- Add Family Before Paying -->
</div> <div style="padding:0 20px 15px;display:flex;gap:10px;flex-wrap:wrap;border-top:1px solid #E5E7EB;padding-top:15px;">
<span style="color:#6B7280;font-size:13px;padding:8px 0;">أضف أفراد الأسرة قبل الدفع:</span>
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">💍 إضافة زوجة</a>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">👶 إضافة ابن/ابنة</a>
</div>
<!-- Payment Section -->
<?php if (bccomp($bill['total_pending'], '0', 2) > 0 && in_array($member->status, ['accepted', 'payment_pending'])): ?>
<div style="padding:20px;background:#FFF7ED;border-top:2px solid #F59E0B;">
<h4 style="margin:0 0 15px;color:#D97706;">💰 اختر طريقة السداد</h4>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;">
<!-- Cash Full -->
<div style="background:#fff;border:2px solid #059669;border-radius:12px;padding:20px;">
<h5 style="margin:0 0 10px;color:#059669;">💵 كاش كامل</h5>
<p style="font-size:13px;color:#6B7280;margin:0 0 10px;">ادفع المبلغ كاملاً — بدون فوائد</p>
<div style="font-size:24px;font-weight:700;color:#059669;margin-bottom:15px;"><?= money($bill['total_pending']) ?></div>
<form method="POST" action="/members/<?= (int) $member->id ?>/pay-membership">
<?= csrf_field() ?>
<input type="hidden" name="payment_type" value="membership_fee">
<input type="hidden" name="amount" value="<?= e($bill['total_pending']) ?>">
<select name="payment_method" class="form-select" style="margin-bottom:10px;">
<option value="cash">نقدي</option>
<option value="visa">فيزا</option>
<option value="bank_transfer">تحويل بنكي</option>
</select>
<button type="submit" class="btn btn-primary" style="width:100%;background:#059669;border-color:#059669;" onclick="return confirm('تأكيد دفع <?= money($bill['total_pending']) ?>؟')">ادفع الآن</button>
</form>
</div> </div>
<div style="text-align:left;">
<span style="display:inline-block;padding:6px 16px;border-radius:20px;font-weight:700;font-size:14px;color:#fff;background:<?= $statusColor ?>;"> <!-- Installment -->
<?= e($statusLabel) ?> <div style="background:#fff;border:2px solid #0284C7;border-radius:12px;padding:20px;">
</span> <h5 style="margin:0 0 10px;color:#0284C7;">📅 تقسيط</h5>
<div style="margin-top:10px;font-size:12px;color:#9CA3AF;"> <p style="font-size:13px;color:#6B7280;margin:0 0 5px;">مقدم 25% على الأقل + باقي على أقساط</p>
تاريخ التسجيل: <?= e(substr($member->created_at, 0, 10)) ?> <p style="font-size:12px;color:#D97706;margin:0 0 10px;">فائدة 22% سنوياً — حتى 30 شهر</p>
</div> <?php
$minDown = bcdiv(bcmul($bill['total_pending'], '25', 2), '100', 2);
$remaining = bcsub($bill['total_pending'], $minDown, 2);
$interest = bcdiv(bcmul($remaining, '22', 2), '100', 2);
$totalWithInterest = bcadd($remaining, $interest, 2);
$monthlyEst = bcdiv($totalWithInterest, '30', 2);
?>
<table style="width:100%;font-size:13px;margin-bottom:10px;">
<tr><td style="color:#6B7280;">المقدم (25%)</td><td style="text-align:left;font-weight:600;"><?= money($minDown) ?></td></tr>
<tr><td style="color:#6B7280;">الباقي</td><td style="text-align:left;"><?= money($remaining) ?></td></tr>
<tr><td style="color:#6B7280;">الفائدة (22%)</td><td style="text-align:left;color:#D97706;"><?= money($interest) ?></td></tr>
<tr><td style="color:#6B7280;">القسط الشهري (~30 شهر)</td><td style="text-align:left;font-weight:600;"><?= money($monthlyEst) ?></td></tr>
</table>
<form method="POST" action="/members/<?= (int) $member->id ?>/pay-membership">
<?= csrf_field() ?>
<input type="hidden" name="payment_type" value="down_payment">
<div class="form-group" style="margin-bottom:10px;">
<label class="form-label" style="font-size:12px;">المقدم (الحد الأدنى <?= money($minDown) ?>)</label>
<input type="number" name="amount" value="<?= e($minDown) ?>" min="<?= e($minDown) ?>" step="0.01" class="form-input" style="direction:ltr;text-align:left;" required>
</div>
<div class="form-group" style="margin-bottom:10px;">
<label class="form-label" style="font-size:12px;">عدد الأشهر (حتى 30)</label>
<input type="number" name="installment_months" value="30" min="1" max="30" class="form-input" style="direction:ltr;text-align:left;">
</div>
<select name="payment_method" class="form-select" style="margin-bottom:10px;">
<option value="cash">نقدي</option>
<option value="visa">فيزا</option>
<option value="bank_transfer">تحويل بنكي</option>
</select>
<button type="submit" class="btn btn-primary" style="width:100%;background:#0284C7;border-color:#0284C7;" onclick="return confirm('تأكيد دفع المقدم؟')">ادفع المقدم</button>
</form>
</div> </div>
</div> </div>
<div style="margin-top:15px;padding:10px;background:#FEF2F2;border-radius:8px;font-size:12px;color:#DC2626;">
⚠️ مهلة السداد: 15 يوم من تاريخ القبول — بعدها تنتهي الاستمارة ويجب شراء استمارة جديدة
</div>
</div>
<?php elseif ($formFilled && in_array($member->status, ['under_review', 'interview_scheduled'])): ?>
<div style="padding:20px;background:#EFF6FF;border-top:2px solid #0284C7;">
<p style="margin:0;color:#0284C7;font-size:14px;">📋 الفاتورة جاهزة — في انتظار قرار مجلس الأمناء قبل السداد</p>
<p style="margin:5px 0 0;color:#6B7280;font-size:13px;">يمكنك إضافة أفراد الأسرة الآن وستُضاف رسومهم تلقائياً</p>
</div> </div>
<?php endif; ?>
</div> </div>
<?php endif; ?>
<!-- Info Grid --> <!-- Info Grid -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;">
<!-- Personal Info -->
<div class="card"> <div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;font-size:15px;">👤 البيانات الشخصية</h3></div>
<h3 style="margin:0;color:#0D7377;font-size:15px;">👤 البيانات الشخصية</h3>
</div>
<div style="padding:15px 20px;"> <div style="padding:15px 20px;">
<table style="width:100%;font-size:13px;"> <table style="width:100%;font-size:13px;">
<tr><td style="padding:5px 0;color:#6B7280;width:40%;">الرقم القومي</td><td style="padding:5px 0;direction:ltr;text-align:right;font-weight:600;"><?= e($member->national_id ?? '—') ?></td></tr> <tr><td style="padding:5px 0;color:#6B7280;width:40%;">الرقم القومي</td><td style="padding:5px 0;direction:ltr;text-align:right;font-weight:600;"><?= e($member->national_id ?? '—') ?></td></tr>
...@@ -132,29 +242,16 @@ $statusColor = $member->getStatusColor(); ...@@ -132,29 +242,16 @@ $statusColor = $member->getStatusColor();
<tr><td style="padding:5px 0;color:#6B7280;">الجنسية</td><td style="padding:5px 0;"><?= e($member->nationality ?? '—') ?></td></tr> <tr><td style="padding:5px 0;color:#6B7280;">الجنسية</td><td style="padding:5px 0;"><?= e($member->nationality ?? '—') ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">المؤهل</td><td style="padding:5px 0;"><?= e($qualificationName) ?></td></tr> <tr><td style="padding:5px 0;color:#6B7280;">المؤهل</td><td style="padding:5px 0;"><?= e($qualificationName) ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">الحالة الاجتماعية</td><td style="padding:5px 0;"><?= e($member->marital_status ?? '—') ?></td></tr> <tr><td style="padding:5px 0;color:#6B7280;">الحالة الاجتماعية</td><td style="padding:5px 0;"><?= e($member->marital_status ?? '—') ?></td></tr>
<tr><td style="padding:5px 0;color:#6B7280;">الديانة</td><td style="padding:5px 0;"><?= e($member->religion ?? '—') ?></td></tr> <?php if ($member->email): ?><tr><td style="padding:5px 0;color:#6B7280;">البريد</td><td style="padding:5px 0;direction:ltr;text-align:right;"><?= e($member->email) ?></td></tr><?php endif; ?>
<?php if ($member->email): ?>
<tr><td style="padding:5px 0;color:#6B7280;">البريد</td><td style="padding:5px 0;direction:ltr;text-align:right;"><?= e($member->email) ?></td></tr>
<?php endif; ?>
</table> </table>
</div> </div>
</div> </div>
<!-- Financial Summary -->
<div class="card"> <div class="card">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;font-size:15px;">💰 الملخص المالي</h3></div>
<h3 style="margin:0;color:#0D7377;font-size:15px;">💰 الملخص المالي</h3>
</div>
<div style="padding:15px 20px;"> <div style="padding:15px 20px;">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-bottom:15px;"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div style="background:#F0FDF4;padding:15px;border-radius:8px;text-align:center;"> <div style="background:#F0FDF4;padding:15px;border-radius:8px;text-align:center;"><div style="font-size:20px;font-weight:700;color:#059669;"><?= money($member->membership_value ?? '0') ?></div><div style="color:#6B7280;font-size:11px;">قيمة العضوية</div></div>
<div style="font-size:22px;font-weight:700;color:#059669;"><?= money($member->membership_value ?? '0') ?></div> <div style="background:#EFF6FF;padding:15px;border-radius:8px;text-align:center;"><div style="font-size:20px;font-weight:700;color:#0284C7;"><?= money($bill['total_paid']) ?></div><div style="color:#6B7280;font-size:11px;">إجمالي المدفوع</div></div>
<div style="color:#6B7280;font-size:12px;">قيمة العضوية</div>
</div>
<div style="background:#EFF6FF;padding:15px;border-radius:8px;text-align:center;">
<div style="font-size:22px;font-weight:700;color:#0284C7;"><?= money($totalPaid) ?></div>
<div style="color:#6B7280;font-size:12px;">إجمالي المدفوع</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -164,107 +261,80 @@ $statusColor = $member->getStatusColor(); ...@@ -164,107 +261,80 @@ $statusColor = $member->getStatusColor();
<div class="card" style="margin-bottom:20px;padding:15px;"> <div class="card" style="margin-bottom:20px;padding:15px;">
<h4 style="margin:0 0 10px;color:#6B7280;font-size:13px;">إجراءات سريعة</h4> <h4 style="margin:0 0 10px;color:#6B7280;font-size:13px;">إجراءات سريعة</h4>
<div style="display:flex;gap:10px;flex-wrap:wrap;"> <div style="display:flex;gap:10px;flex-wrap:wrap;">
<?php if ($formFeePaid && !$formFilled): ?> <?php if ($bill['form_fee_paid'] && !$formFilled): ?><a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary">📝 ملء الاستمارة</a><?php endif; ?>
<a href="/members/<?= (int) $member->id ?>/fill-form" class="btn btn-primary">📝 ملء الاستمارة</a>
<?php endif; ?>
<?php if ($formFilled): ?> <?php if ($formFilled): ?>
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-outline">💍 إضافة زوج/ة</a> <a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-outline">💍 إضافة زوجة</a>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-outline">👶 إضافة ابن/ة</a> <a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-outline">👶 إضافة ابن/ة</a>
<a href="/documents/upload/<?= (int) $member->id ?>" class="btn btn-outline">📁 رفع مستندات</a> <a href="/documents/upload/<?= (int) $member->id ?>" class="btn btn-outline">📁 رفع مستندات</a>
<?php endif; ?>
<?php if (in_array($member->status, ['under_review', 'interview_scheduled'])): ?>
<a href="/interviews/schedule/<?= (int) $member->id ?>" class="btn btn-outline">🗓️ تحديد مقابلة</a> <a href="/interviews/schedule/<?= (int) $member->id ?>" class="btn btn-outline">🗓️ تحديد مقابلة</a>
<a href="/installments/create/<?= (int) $member->id ?>" class="btn btn-outline">📅 خطة تقسيط</a>
<?php endif; ?> <?php endif; ?>
<?php if ($member->status === 'active'): ?> <?php if ($member->status === 'active'): ?>
<a href="/carnets/issue/<?= (int) $member->id ?>" class="btn btn-outline">🪪 إصدار كارنيه</a> <a href="/carnets/issue/<?= (int) $member->id ?>" class="btn btn-outline">🪪 إصدار كارنيه</a>
<a href="/payments/process/<?= (int) $member->id ?>" class="btn btn-outline">💰 تسجيل دفعة</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
<!-- Family: Spouses --> <!-- Spouses -->
<?php if (!empty($spouses)): ?> <?php if (!empty($spouses)): ?>
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">💍 الزوجات (<?= count($spouses) ?>)</h3> <h3 style="margin:0;color:#0D7377;font-size:15px;">💍 الزوجات (<?= count($spouses) ?>)</h3>
<a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">+ إضافة</a> <a href="/members/<?= (int) $member->id ?>/spouses/create" class="btn btn-sm btn-outline">+ إضافة</a>
</div> </div>
<div class="table-responsive"> <div class="table-responsive"><table class="data-table"><thead><tr><th>#</th><th>الاسم</th><th>الرقم القومي</th><th>الرسوم</th><th>الحالة</th><th>الإجراءات</th></tr></thead><tbody>
<table class="data-table"> <?php foreach ($spouses as $s): ?>
<thead><tr><th>#</th><th>الاسم</th><th>الرقم القومي</th><th>تاريخ الميلاد</th><th>الحالة</th><th>الإجراءات</th></tr></thead> <tr>
<tbody> <td><?= (int) $s['spouse_order'] ?></td>
<?php foreach ($spouses as $s): ?> <td style="font-weight:600;"><?= e($s['full_name_ar']) ?></td>
<tr> <td style="direction:ltr;text-align:right;font-size:12px;"><?= e($s['national_id'] ?? '—') ?></td>
<td><?= (int) $s['spouse_order'] ?></td> <td style="font-weight:600;"><?php
<td style="font-weight:600;"><?= e($s['full_name_ar']) ?></td> $fee = $s['addition_fee'] ?? '0.00';
<td style="direction:ltr;text-align:right;font-size:12px;"><?= e($s['national_id'] ?? '—') ?></td> if (bccomp($fee, '0', 2) <= 0 && (int) $s['spouse_order'] === 1) {
<td style="font-size:13px;"><?= e($s['date_of_birth']) ?></td> echo '<span style="color:#059669;">مشمولة</span>';
<td><span style="color:<?= $s['status'] === 'active' ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= $s['status'] === 'active' ? 'نشط' : $s['status'] ?></span></td> } else {
<td><a href="/members/<?= (int) $member->id ?>/spouses/<?= (int) $s['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td> echo money($fee);
</tr> }
<?php endforeach; ?> ?></td>
</tbody> <td><span style="color:<?= $s['status'] === 'active' ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= $s['status'] === 'active' ? 'نشط' : $s['status'] ?></span></td>
</table> <td><a href="/members/<?= (int) $member->id ?>/spouses/<?= (int) $s['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
</div> </tr>
<?php endforeach; ?>
</tbody></table></div>
</div> </div>
<?php endif; ?> <?php endif; ?>
<!-- Family: Children --> <!-- Children -->
<?php if (!empty($children)): ?> <?php if (!empty($children)): ?>
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;display:flex;justify-content:space-between;align-items:center;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">👨‍👩‍👧‍👦 الأبناء (<?= count($children) ?>)</h3> <h3 style="margin:0;color:#0D7377;font-size:15px;">👨‍👩‍👧‍👦 الأبناء (<?= count($children) ?>)</h3>
<a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">+ إضافة</a> <a href="/members/<?= (int) $member->id ?>/children/create" class="btn btn-sm btn-outline">+ إضافة</a>
</div> </div>
<div class="table-responsive"> <div class="table-responsive"><table class="data-table"><thead><tr><th>#</th><th>الاسم</th><th>النوع</th><th>السن</th><th>التصنيف</th><th>الرسوم</th><th>الإجراءات</th></tr></thead><tbody>
<table class="data-table"> <?php foreach ($children as $c): ?>
<thead><tr><th>#</th><th>الاسم</th><th>النوع</th><th>تاريخ الميلاد</th><th>السن</th><th>التصنيف</th><th>الحالة</th><th>الإجراءات</th></tr></thead> <tr>
<tbody> <td><?= (int) $c['child_order'] ?></td>
<?php foreach ($children as $c): ?> <td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td>
<tr> <td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td>
<td><?= (int) $c['child_order'] ?></td> <td><?= (int) ($c['age_years'] ?? 0) ?></td>
<td style="font-weight:600;"><?= e($c['full_name_ar']) ?></td> <td style="font-size:12px;"><?= match($c['classification'] ?? '') { 'included' => '<span style="color:#059669;">مشمول</span>', 'dependent_with_fee' => '<span style="color:#D97706;">برسوم</span>', default => e($c['classification'] ?? '') } ?></td>
<td><?= $c['gender'] === 'male' ? 'ذكر' : 'أنثى' ?></td> <td style="font-weight:600;"><?php
<td style="font-size:13px;"><?= e($c['date_of_birth']) ?></td> $fee = $c['addition_fee'] ?? '0.00';
<td><?= (int) ($c['age_years'] ?? 0) ?></td> echo bccomp($fee, '0', 2) <= 0 ? '<span style="color:#059669;">مشمول</span>' : money($fee);
<td style="font-size:12px;"><?= e($c['classification'] ?? '—') ?></td> ?></td>
<td><span style="color:<?= $c['status'] === 'active' ? '#059669' : '#DC2626' ?>;font-weight:600;"><?= $c['status'] === 'active' ? 'نشط' : $c['status'] ?></span></td> <td><a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td>
<td><a href="/members/<?= (int) $member->id ?>/children/<?= (int) $c['id'] ?>" class="btn btn-sm btn-outline">عرض</a></td> </tr>
</tr> <?php endforeach; ?>
<?php endforeach; ?> </tbody></table></div>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
<!-- Recent Payments -->
<?php if (!empty($payments)): ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;font-size:15px;">💳 آخر المدفوعات</h3>
</div>
<div class="table-responsive">
<table class="data-table">
<thead><tr><th>التاريخ</th><th>النوع</th><th>المبلغ</th><th>الإيصال</th></tr></thead>
<tbody>
<?php foreach ($payments as $p): ?>
<tr>
<td style="font-size:13px;"><?= e($p['payment_date']) ?></td>
<td style="font-size:13px;"><?= e(\App\Modules\Payments\Models\Payment::getPaymentTypeLabel($p['payment_type'])) ?></td>
<td style="font-weight:600;"><?= money($p['amount']) ?></td>
<td style="font-size:12px;direction:ltr;text-align:right;"><?= e($p['receipt_number'] ?? '—') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div> </div>
<?php endif; ?> <?php endif; ?>
<!-- Status Change --> <!-- Status Change -->
<div class="card" style="margin-bottom:20px;"> <div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"> <div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;font-size:15px;">🔄 تغيير الحالة</h3></div>
<h3 style="margin:0;color:#0D7377;font-size:15px;">🔄 تغيير الحالة</h3>
</div>
<div style="padding:15px 20px;"> <div style="padding:15px 20px;">
<form method="POST" action="/members/<?= (int) $member->id ?>/status" style="display:flex;gap:10px;align-items:center;"> <form method="POST" action="/members/<?= (int) $member->id ?>/status" style="display:flex;gap:10px;align-items:center;">
<?= csrf_field() ?> <?= csrf_field() ?>
...@@ -273,7 +343,7 @@ $statusColor = $member->getStatusColor(); ...@@ -273,7 +343,7 @@ $statusColor = $member->getStatusColor();
<option value="<?= e($val) ?>" <?= $member->status === $val ? 'selected' : '' ?>><?= e($label) ?></option> <option value="<?= e($val) ?>" <?= $member->status === $val ? 'selected' : '' ?>><?= e($label) ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<button type="submit" class="btn btn-outline" onclick="return confirm('تغيير حالة العضو؟')">تحديث الحالة</button> <button type="submit" class="btn btn-outline" onclick="return confirm('تغيير حالة العضو؟')">تحديث</button>
</form> </form>
</div> </div>
</div> </div>
......
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