Commit ec96e7ed authored by Administrator's avatar Administrator

Update 3 files via Son of Anton

parent ef7860c0
...@@ -22,31 +22,25 @@ class SpouseController extends Controller ...@@ -22,31 +22,25 @@ class SpouseController extends Controller
return $this->redirect('/members')->withError('العضو غير موجود'); return $this->redirect('/members')->withError('العضو غير موجود');
} }
$membershipValue = $member['membership_value'] ?? '0.00'; $currentCount = Spouse::countActiveForMember((int) $memberId);
if (bccomp($membershipValue, '0.01', 2) < 0) { $maxAllowed = Spouse::getMaxSpouses($member['gender']);
return $this->redirect('/members/' . $memberId)
->withError('⚠ يجب ملء الاستمارة وتحديد المؤهل أولاً لحساب قيمة العضوية قبل إضافة زوج/ة'); if ($currentCount >= $maxAllowed) {
$msg = $member['gender'] === 'female'
? 'العضوة لديها زوج مسجل بالفعل — لا يمكن إضافة أكثر من زوج واحد'
: 'تم الوصول للحد الأقصى من الزوجات (' . $maxAllowed . ')';
return $this->redirect("/members/{$memberId}")->withError($msg);
} }
$qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order"); $qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order");
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
$spouseOrder = Spouse::getNextOrder((int) $memberId);
// Spouse gender is the OPPOSITE of member gender
$spouseGender = ($member['gender'] === 'male') ? 'female' : 'male';
$feeEstimate = SpouseFeeCalculator::calculate((int) $memberId, [
'nationality' => 'مصري',
'marriage_date' => date('Y-m-d'),
]);
return $this->view('Spouses.Views.create', [ return $this->view('Spouses.Views.create', [
'member' => $member, 'member' => $member,
'spouseOrder' => $currentCount + 1,
'qualifications' => $qualifications, 'qualifications' => $qualifications,
'countries' => $countries, 'requiredGender' => Spouse::getRequiredSpouseGender($member['gender']),
'spouseOrder' => $spouseOrder, 'maxAllowed' => $maxAllowed,
'spouseGender' => $spouseGender, 'currentCount' => $currentCount,
'feeEstimate' => $feeEstimate,
]); ]);
} }
...@@ -58,32 +52,37 @@ class SpouseController extends Controller ...@@ -58,32 +52,37 @@ class SpouseController extends Controller
return $this->redirect('/members')->withError('العضو غير موجود'); return $this->redirect('/members')->withError('العضو غير موجود');
} }
$membershipValue = $member['membership_value'] ?? '0.00';
if (bccomp($membershipValue, '0.01', 2) < 0) {
return $this->redirect('/members/' . $memberId)
->withError('⚠ يجب تحديد قيمة العضوية قبل إضافة زوج/ة');
}
$data = $request->all(); $data = $request->all();
unset($data['_csrf_token']); unset($data['_csrf_token']);
$errors = []; $errors = [];
if (empty(trim($data['full_name_ar'] ?? ''))) $errors[] = 'اسم الزوج/ة مطلوب'; if (empty(trim($data['full_name_ar'] ?? ''))) $errors[] = 'اسم الزوج/الزوجة مطلوب';
if (empty($data['date_of_birth'] ?? '')) $errors[] = 'تاريخ الميلاد مطلوب'; if (empty($data['date_of_birth'] ?? '')) $errors[] = 'تاريخ الميلاد مطلوب';
if (empty($data['gender'] ?? '')) $errors[] = 'النوع مطلوب';
if (empty($data['marriage_date'] ?? '')) $errors[] = 'تاريخ الزواج مطلوب'; if (empty($data['marriage_date'] ?? '')) $errors[] = 'تاريخ الزواج مطلوب';
// ── Gender validation: only male/female, must be opposite of member ── // ── Gender & count validation ──
$requiredSpouseGender = ($member['gender'] === 'male') ? 'female' : 'male'; $requiredGender = Spouse::getRequiredSpouseGender($member['gender']);
$maxAllowed = Spouse::getMaxSpouses($member['gender']);
$currentCount = Spouse::countActiveForMember((int) $memberId);
// Max spouse check if ($currentCount >= $maxAllowed) {
$existingCount = Spouse::countActiveForMember((int) $memberId); $errors[] = $member['gender'] === 'female'
$maxSpouses = ($member['gender'] === 'male') ? 4 : 1; ? 'العضوة لديها زوج مسجل بالفعل — لا يمكن إضافة أكثر من زوج واحد'
if ($existingCount >= $maxSpouses) { : 'تم الوصول للحد الأقصى من الزوجات (' . $maxAllowed . ')';
$errors[] = 'تم بلوغ الحد الأقصى لعدد الزوجات (' . $maxSpouses . ')';
} }
// Parse NID if provided if (!empty($data['gender']) && $data['gender'] !== $requiredGender) {
$errors[] = $member['gender'] === 'female'
? 'يجب أن يكون الزوج ذكراً'
: 'يجب أن تكون الزوجة أنثى';
}
// Force the correct gender regardless of what was submitted
$data['gender'] = $requiredGender;
// ── NID parsing ──
$nid = trim($data['national_id'] ?? ''); $nid = trim($data['national_id'] ?? '');
if ($nid !== '') { if ($nid !== '') {
$parsed = NationalIdParser::parse($nid); $parsed = NationalIdParser::parse($nid);
...@@ -93,49 +92,36 @@ class SpouseController extends Controller ...@@ -93,49 +92,36 @@ class SpouseController extends Controller
$data['date_of_birth'] = $parsed['dob']; $data['date_of_birth'] = $parsed['dob'];
$data['age_years'] = $parsed['age_years']; $data['age_years'] = $parsed['age_years'];
$data['age_months'] = $parsed['age_months']; $data['age_months'] = $parsed['age_months'];
$data['gender'] = $parsed['gender'];
// Validate gender from NID matches required // Validate NID gender matches required gender
if ($parsed['gender'] !== $requiredSpouseGender) { if ($parsed['gender'] !== $requiredGender) {
$memberGenderAr = $member['gender'] === 'male' ? 'ذكر' : 'أنثى'; $genderLabel = $requiredGender === 'male' ? 'ذكر' : 'أنثى';
$requiredAr = $requiredSpouseGender === 'male' ? 'ذكر' : 'أنثى'; $errors[] = "الرقم القومي يشير إلى نوع مختلف — المطلوب: {$genderLabel}";
$parsedAr = $parsed['gender'] === 'male' ? 'ذكر' : 'أنثى';
$errors[] = "العضو {$memberGenderAr} — يجب أن يكون الزوج/ة {$requiredAr} — الرقم القومي يشير إلى {$parsedAr}";
} }
$data['gender'] = $parsed['gender'];
} }
// Cannot be the member themselves
if ($nid === ($member['national_id'] ?? '')) { if ($nid === ($member['national_id'] ?? '')) {
$errors[] = 'لا يمكن إضافة العضو نفسه كزوج/ة'; $errors[] = 'لا يمكن إضافة العضو نفسه كزوج/زوجة';
} }
// Check duplicate $dup = Spouse::nidExistsElsewhere($nid);
$dup = Spouse::nidExistsForOtherMember($nid, (int) $memberId);
if ($dup) { if ($dup) {
$errors[] = 'الرقم القومي مسجل بالفعل: ' . ($dup['data']['full_name_ar'] ?? ''); $errors[] = 'الرقم القومي مسجل بالفعل: ' . ($dup['data']['full_name_ar'] ?? '');
} }
} else {
// No NID — force correct gender
$data['gender'] = $requiredSpouseGender;
} }
// Calculate age // ── Age calculation ──
if (!empty($data['date_of_birth']) && empty($data['age_years'])) { if (!empty($data['date_of_birth']) && empty($data['age_years'])) {
$age = age_from_dob($data['date_of_birth']); $age = age_from_dob($data['date_of_birth']);
$data['age_years'] = $age['years']; $data['age_years'] = $age['years'];
$data['age_months'] = $age['months']; $data['age_months'] = $age['months'];
} }
// Both must be at least 18 at marriage // ── Minimum age check (18+) ──
if (!empty($data['date_of_birth']) && !empty($data['marriage_date'])) { if ((int) ($data['age_years'] ?? 0) < 18) {
$dobTs = strtotime($data['date_of_birth']); $errors[] = 'الحد الأدنى للسن 18 سنة';
$marriageTs = strtotime($data['marriage_date']);
if ($marriageTs && $dobTs) {
$ageAtMarriage = (int) (($marriageTs - $dobTs) / (365.25 * 86400));
if ($ageAtMarriage < 18) {
$errors[] = 'يجب أن يكون عمر الزوج/ة 18 سنة على الأقل وقت الزواج';
}
}
} }
if (!empty($errors)) { if (!empty($errors)) {
...@@ -145,15 +131,9 @@ class SpouseController extends Controller ...@@ -145,15 +131,9 @@ class SpouseController extends Controller
return $this->redirect("/members/{$memberId}/spouses/create"); return $this->redirect("/members/{$memberId}/spouses/create");
} }
// Calculate fee // ── Fee calculation ──
$feeCalc = SpouseFeeCalculator::calculate((int) $memberId, $data); $spouseOrder = $currentCount + 1;
$feeCalc = SpouseFeeCalculator::calculate((int) $memberId, $spouseOrder);
if (!empty($feeCalc['error'])) {
return $this->redirect("/members/{$memberId}/spouses/create")
->withError($feeCalc['error']);
}
$spouseOrder = $feeCalc['spouse_order'] ?? Spouse::getNextOrder((int) $memberId);
$spouse = Spouse::create([ $spouse = Spouse::create([
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
...@@ -165,10 +145,10 @@ class SpouseController extends Controller ...@@ -165,10 +145,10 @@ class SpouseController extends Controller
'date_of_birth' => $data['date_of_birth'], 'date_of_birth' => $data['date_of_birth'],
'age_years' => (int) ($data['age_years'] ?? 0), 'age_years' => (int) ($data['age_years'] ?? 0),
'age_months' => (int) ($data['age_months'] ?? 0), 'age_months' => (int) ($data['age_months'] ?? 0),
'gender' => $requiredSpouseGender, 'gender' => $data['gender'],
'nationality' => $data['nationality'] ?? 'مصري', 'nationality' => $data['nationality'] ?? 'مصري',
'religion' => $data['religion'] ?? null, 'religion' => $data['religion'] ?? null,
'qualification_id' => ($data['qualification_id'] ?? '') !== '' ? (int) $data['qualification_id'] : null, 'qualification_id' => !empty($data['qualification_id']) ? (int) $data['qualification_id'] : null,
'occupation' => $data['occupation'] ?? null, 'occupation' => $data['occupation'] ?? null,
'work_address' => $data['work_address'] ?? null, 'work_address' => $data['work_address'] ?? null,
'work_phone' => $data['work_phone'] ?? null, 'work_phone' => $data['work_phone'] ?? null,
...@@ -176,36 +156,32 @@ class SpouseController extends Controller ...@@ -176,36 +156,32 @@ class SpouseController extends Controller
'marriage_date' => $data['marriage_date'], 'marriage_date' => $data['marriage_date'],
'join_date' => date('Y-m-d'), 'join_date' => date('Y-m-d'),
'classification' => 'working', 'classification' => 'working',
'addition_fee' => $feeCalc['total_fee'] ?? '0.00', 'addition_fee' => $feeCalc['fee'] ?? '0.00',
'status' => 'active', 'status' => 'active',
]); ]);
EventBus::dispatch('spouse.added', [ EventBus::dispatch('spouse.added', [
'member_id' => (int) $memberId, 'member_id' => (int) $memberId,
'spouse_id' => (int) $spouse->id, 'spouse_id' => (int) $spouse->id,
'spouse_order' => $spouseOrder, 'spouse_order'=> $spouseOrder,
'fee' => $feeCalc['total_fee'] ?? '0.00', 'fee' => $feeCalc['fee'] ?? '0.00',
]); ]);
$msg = 'تم إضافة الزوج/الزوجة بنجاح'; $genderWord = $requiredGender === 'male' ? 'الزوج' : 'الزوجة';
$msg .= ' — الترتيب: #' . $spouseOrder;
$msg .= ' — الرسوم: ' . money($feeCalc['total_fee'] ?? '0');
if (!empty($feeCalc['rule_applied'])) {
$msg .= ' (' . $feeCalc['rule_applied'] . ')';
}
return $this->redirect("/members/{$memberId}") return $this->redirect("/members/{$memberId}")
->withSuccess($msg); ->withSuccess("تم إضافة {$genderWord} — الترتيب: #{$spouseOrder} — الرسوم: " . money($feeCalc['fee'] ?? '0.00'));
} }
public function show(Request $request, string $memberId, string $id): Response public function show(Request $request, string $memberId, string $id): Response
{ {
$spouse = Spouse::find((int) $id); $spouse = Spouse::find((int) $id);
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) { if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة'); return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/الزوجة غير موجودة');
} }
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$member = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $memberId]); $member = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $memberId]);
return $this->view('Spouses.Views.show', ['member' => $member, 'spouse' => $spouse]); return $this->view('Spouses.Views.show', ['member' => $member, 'spouse' => $spouse]);
} }
...@@ -213,15 +189,17 @@ class SpouseController extends Controller ...@@ -213,15 +189,17 @@ class SpouseController extends Controller
{ {
$spouse = Spouse::find((int) $id); $spouse = Spouse::find((int) $id);
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) { if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة'); return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/الزوجة غير موجودة');
} }
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$member = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $memberId]); $member = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $memberId]);
$qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order"); $qualifications = $db->select("SELECT id, name_ar FROM qualifications WHERE is_active = 1 ORDER BY sort_order");
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
return $this->view('Spouses.Views.edit', [ return $this->view('Spouses.Views.edit', [
'member' => $member, 'spouse' => $spouse, 'member' => $member,
'qualifications' => $qualifications, 'countries' => $countries, 'spouse' => $spouse,
'qualifications' => $qualifications,
]); ]);
} }
...@@ -229,10 +207,12 @@ class SpouseController extends Controller ...@@ -229,10 +207,12 @@ class SpouseController extends Controller
{ {
$spouse = Spouse::find((int) $id); $spouse = Spouse::find((int) $id);
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) { if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة'); return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/الزوجة غير موجودة');
} }
$data = $request->all(); $data = $request->all();
unset($data['_csrf_token']); unset($data['_csrf_token']);
$updateData = []; $updateData = [];
foreach (['full_name_en', 'occupation', 'work_address', 'work_phone', 'mobile', 'religion'] as $field) { foreach (['full_name_en', 'occupation', 'work_address', 'work_phone', 'mobile', 'religion'] as $field) {
if (array_key_exists($field, $data)) { if (array_key_exists($field, $data)) {
...@@ -240,31 +220,46 @@ class SpouseController extends Controller ...@@ -240,31 +220,46 @@ class SpouseController extends Controller
$updateData[$field] = ($val === '' || $val === null) ? null : $val; $updateData[$field] = ($val === '' || $val === null) ? null : $val;
} }
} }
if (isset($data['qualification_id'])) { if (!empty($data['qualification_id'])) {
$updateData['qualification_id'] = ($data['qualification_id'] !== '') ? (int) $data['qualification_id'] : null; $updateData['qualification_id'] = (int) $data['qualification_id'];
} }
if (!empty($updateData)) $spouse->update($updateData);
return $this->redirect("/members/{$memberId}")->withSuccess('تم تحديث بيانات الزوج/ة'); if (!empty($updateData)) {
$spouse->update($updateData);
}
return $this->redirect("/members/{$memberId}")->withSuccess('تم تحديث بيانات الزوج/الزوجة');
} }
public function archive(Request $request, string $memberId, string $id): Response public function archive(Request $request, string $memberId, string $id): Response
{ {
$spouse = Spouse::find((int) $id); $spouse = Spouse::find((int) $id);
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) { if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة'); return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/الزوجة غير موجودة');
} }
$reason = trim((string) $request->post('reason', '')); $reason = trim((string) $request->post('reason', ''));
if ($reason === '') return $this->redirect("/members/{$memberId}")->withError('يجب إدخال سبب الإزالة'); if ($reason === '') {
return $this->redirect("/members/{$memberId}")->withError('يجب إدخال سبب الإزالة');
}
$employee = App::getInstance()->currentEmployee(); $employee = App::getInstance()->currentEmployee();
$db = App::getInstance()->db(); $db = App::getInstance()->db();
$db->update('spouses', [ $db->update('spouses', [
'is_archived' => 1, 'archived_at' => date('Y-m-d H:i:s'), 'is_archived' => 1,
'archived_at' => date('Y-m-d H:i:s'),
'archived_by' => $employee ? (int) $employee->id : null, 'archived_by' => $employee ? (int) $employee->id : null,
'status' => 'inactive', 'updated_at' => date('Y-m-d H:i:s'), 'status' => 'inactive',
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $id]); ], '`id` = ?', [(int) $id]);
EventBus::dispatch('spouse.removed', ['member_id' => (int) $memberId, 'spouse_id' => (int) $id, 'reason' => $reason]); EventBus::dispatch('spouse.removed', [
return $this->redirect("/members/{$memberId}")->withSuccess('تم إزالة الزوج/ة'); 'member_id' => (int) $memberId,
'spouse_id' => (int) $id,
'reason' => $reason,
]);
return $this->redirect("/members/{$memberId}")->withSuccess('تم إزالة الزوج/الزوجة');
} }
} }
\ No newline at end of file
...@@ -16,22 +16,18 @@ class Spouse extends Model ...@@ -16,22 +16,18 @@ class Spouse extends Model
protected static array $fillable = [ protected static array $fillable = [
'member_id', 'spouse_order', 'full_name_ar', 'full_name_en', 'member_id', 'spouse_order', 'full_name_ar', 'full_name_en',
'national_id', 'passport_number', 'date_of_birth', 'age_years', 'age_months', 'national_id', 'passport_number', 'date_of_birth',
'gender', 'nationality', 'religion', 'qualification_id', 'age_years', 'age_months', 'gender', 'nationality', 'religion',
'occupation', 'work_address', 'work_phone', 'mobile', 'qualification_id', 'occupation', 'work_address', 'work_phone', 'mobile',
'marriage_date', 'join_date', 'classification', 'marriage_date', 'join_date', 'classification', 'addition_fee',
'addition_fee', 'fee_receipt_number', 'status', 'photo_path', 'fee_receipt_number', 'status', 'photo_path',
]; ];
public static function getForMember(int $memberId): array public static function getForMember(int $memberId): array
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
return $db->select( return $db->select(
"SELECT s.*, q.name_ar as qualification_name "SELECT * FROM spouses WHERE member_id = ? AND is_archived = 0 ORDER BY spouse_order ASC",
FROM spouses s
LEFT JOIN qualifications q ON q.id = s.qualification_id
WHERE s.member_id = ? AND s.is_archived = 0
ORDER BY s.spouse_order ASC",
[$memberId] [$memberId]
); );
} }
...@@ -51,45 +47,30 @@ class Spouse extends Model ...@@ -51,45 +47,30 @@ class Spouse extends Model
return self::countActiveForMember($memberId) + 1; return self::countActiveForMember($memberId) + 1;
} }
public static function nidExistsForOtherMember(string $nid, int $excludeMemberId, ?int $excludeSpouseId = null): ?array public static function nidExistsElsewhere(string $nid, ?int $excludeSpouseId = null): ?array
{ {
$db = App::getInstance()->db(); $db = App::getInstance()->db();
// Check members table
$memberDup = $db->selectOne( $memberDup = $db->selectOne(
"SELECT id, full_name_ar, membership_number FROM members WHERE national_id = ? AND id != ? AND is_archived = 0", "SELECT id, full_name_ar, membership_number FROM members WHERE national_id = ? AND is_archived = 0",
[$nid, $excludeMemberId] [$nid]
); );
if ($memberDup) { if ($memberDup) return ['type' => 'member', 'data' => $memberDup];
return ['type' => 'member', 'data' => $memberDup];
}
// Check spouses table $sql = "SELECT s.id, s.full_name_ar, m.membership_number FROM spouses s JOIN members m ON m.id = s.member_id WHERE s.national_id = ? AND s.is_archived = 0";
$sql = "SELECT s.id, s.full_name_ar, s.member_id, m.membership_number
FROM spouses s
JOIN members m ON m.id = s.member_id
WHERE s.national_id = ? AND s.is_archived = 0";
$params = [$nid]; $params = [$nid];
if ($excludeSpouseId) { if ($excludeSpouseId) {
$sql .= " AND s.id != ?"; $sql .= " AND s.id != ?";
$params[] = $excludeSpouseId; $params[] = $excludeSpouseId;
} }
$spouseDup = $db->selectOne($sql, $params); $spouseDup = $db->selectOne($sql, $params);
if ($spouseDup) { if ($spouseDup) return ['type' => 'spouse', 'data' => $spouseDup];
return ['type' => 'spouse', 'data' => $spouseDup];
}
// Check children table
$childDup = $db->selectOne( $childDup = $db->selectOne(
"SELECT c.id, c.full_name_ar, c.member_id, m.membership_number "SELECT c.id, c.full_name_ar, m.membership_number FROM children c JOIN members m ON m.id = c.member_id WHERE c.national_id = ? AND c.is_archived = 0",
FROM children c
JOIN members m ON m.id = c.member_id
WHERE c.national_id = ? AND c.is_archived = 0",
[$nid] [$nid]
); );
if ($childDup) { if ($childDup) return ['type' => 'child', 'data' => $childDup];
return ['type' => 'child', 'data' => $childDup];
}
return null; return null;
} }
...@@ -97,9 +78,9 @@ class Spouse extends Model ...@@ -97,9 +78,9 @@ class Spouse extends Model
public function getClassificationLabel(): string public function getClassificationLabel(): string
{ {
return match ($this->classification) { return match ($this->classification) {
'working' => 'عضو عامل', 'working' => 'عامل',
'dependent' => 'عضو تابع', 'seasonal' => 'موسمي',
default => $this->classification, default => $this->classification ?? 'عامل',
}; };
} }
...@@ -108,24 +89,28 @@ class Spouse extends Model ...@@ -108,24 +89,28 @@ class Spouse extends Model
return match ($this->status) { return match ($this->status) {
'active' => 'نشط', 'active' => 'نشط',
'inactive' => 'غير نشط', 'inactive' => 'غير نشط',
'separated' => 'منفصل', 'divorced' => 'مطلق/ة',
'deceased' => 'متوفى', 'deceased' => 'متوفى/ة',
default => $this->status, 'transferred' => 'منقول/ة',
default => $this->status ?? '',
}; };
} }
public function getMemberName(): string /**
* Get the max number of spouses allowed based on member gender.
*/
public static function getMaxSpouses(string $memberGender): int
{ {
$db = App::getInstance()->db(); // Female member: max 1 husband
$row = $db->selectOne("SELECT full_name_ar FROM members WHERE id = ?", [$this->member_id]); // Male member: max 4 wives (as per regulations)
return $row['full_name_ar'] ?? '—'; return $memberGender === 'female' ? 1 : 4;
} }
public function getQualificationName(): string /**
* Get the required spouse gender based on member gender.
*/
public static function getRequiredSpouseGender(string $memberGender): string
{ {
if (!$this->qualification_id) return '—'; return $memberGender === 'female' ? 'male' : 'female';
$db = App::getInstance()->db();
$row = $db->selectOne("SELECT name_ar FROM qualifications WHERE id = ?", [$this->qualification_id]);
return $row['name_ar'] ?? '—';
} }
} }
\ No newline at end of file
<?php $__template->layout('Layout.main'); ?> <?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>إضافة زوج/ة<?= e($member['full_name_ar']) ?><?php $__template->endSection(); ?> <?php $__template->section('title'); ?>إضافة <?= $member['gender'] === 'female' ? 'زوج' : 'زوجة' ?><?= e($member['full_name_ar']) ?><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?> <?php $__template->section('content'); ?>
<?php <?php
$memberGenderAr = $member['gender'] === 'male' ? 'ذكر' : 'أنثى'; $isFemale = ($member['gender'] === 'female');
$spouseGenderAr = $spouseGender === 'male' ? 'زوج' : 'زوجة'; $spouseLabel = $isFemale ? 'الزوج' : 'الزوجة';
$spouseGenderFullAr = $spouseGender === 'male' ? 'ذكر' : 'أنثى'; $genderLabel = $isFemale ? 'ذكر' : 'أنثى';
$genderValue = $requiredGender;
?> ?>
<div class="card" style="margin-bottom:15px;padding:15px;display:flex;justify-content:space-between;align-items:center;"> <div class="card" style="margin-bottom:15px;padding:15px;display:flex;justify-content:space-between;align-items:center;">
<div> <div>
<strong>العضو:</strong> <?= e($member['full_name_ar']) ?> (<?= $memberGenderAr ?>) <strong>العضو/ة:</strong> <?= e($member['full_name_ar']) ?>
&nbsp;|&nbsp; <strong>رقم العضوية:</strong> <?= e($member['membership_number'] ?? 'لم يُحدد') ?> &nbsp;|&nbsp; <strong>رقم العضوية:</strong> <?= e($member['membership_number'] ?? 'لم يُحدد') ?>
&nbsp;|&nbsp; <strong>قيمة العضوية:</strong> <?= money($member['membership_value'] ?? '0') ?> &nbsp;|&nbsp; <strong>ترتيب <?= $spouseLabel ?>:</strong> #<?= (int) $spouseOrder ?>
&nbsp;|&nbsp; <strong>ترتيب ال<?= $spouseGenderAr ?>:</strong> #<?= (int) $spouseOrder ?> <?php if ($isFemale): ?>
&nbsp;|&nbsp; <span style="background:#EFF6FF;color:#0284C7;padding:3px 10px;border-radius:4px;font-size:12px;font-weight:600;">👤 يُسمح بزوج واحد فقط</span>
<?php else: ?>
&nbsp;|&nbsp; <span style="color:#6B7280;font-size:13px;">(<?= $currentCount ?>/<?= $maxAllowed ?>)</span>
<?php endif; ?>
</div> </div>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">← العودة للعضو</a> <a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">← العودة للعضو</a>
</div> </div>
<!-- Fee Estimate -->
<?php if (!empty($feeEstimate) && empty($feeEstimate['error'])): ?>
<div class="card" style="margin-bottom:15px;padding:15px;background:#EFF6FF;border:1px solid #BFDBFE;">
<strong style="color:#0284C7;">💰 تقدير الرسوم:</strong>
<?php foreach ($feeEstimate['breakdown'] ?? [] as $line): ?>
<div style="font-size:13px;color:#4B5563;margin-top:3px;"><?= e($line) ?></div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form method="POST" action="/members/<?= (int) $member['id'] ?>/spouses"> <form method="POST" action="/members/<?= (int) $member['id'] ?>/spouses">
<?= csrf_field() ?> <?= csrf_field() ?>
<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;">بيانات ال<?= $spouseGenderAr ?></h3> <h3 style="margin:0;color:#0D7377;">بيانات <?= $spouseLabel ?></h3>
</div> </div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;"> <div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group" style="grid-column:1/-1;"> <div class="form-group" style="grid-column:1/-1;">
<label class="form-label">الاسم بالكامل (عربي) <span style="color:#DC2626;">*</span></label> <label class="form-label">الاسم بالكامل (عربي) <span style="color:#DC2626;">*</span></label>
<input type="text" name="full_name_ar" value="<?= e(old('full_name_ar')) ?>" class="form-input" required minlength="5" maxlength="200"> <input type="text" name="full_name_ar" value="<?= e(old('full_name_ar')) ?>" class="form-input" required minlength="5" maxlength="200">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">الاسم بالإنجليزي</label> <label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" value="<?= e(old('full_name_en')) ?>" class="form-input" style="direction:ltr;text-align:left;"> <input type="text" name="full_name_en" value="<?= e(old('full_name_en')) ?>" class="form-input">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">النوع</label> <label class="form-label">النوع</label>
<input type="text" value="<?= $spouseGenderFullAr ?>" class="form-input" style="background:#F3F4F6;font-weight:700;color:#0D7377;" readonly disabled> <input type="text" value="<?= e($genderLabel) ?>" class="form-input" style="background:#F3F4F6;font-weight:600;color:#0D7377;" disabled>
<input type="hidden" name="gender" value="<?= e($spouseGender) ?>"> <input type="hidden" name="gender" value="<?= e($genderValue) ?>">
<small style="color:#6B7280;">يُحدد تلقائياً — العضو <?= $memberGenderAr ?> فالزوج/ة <?= $spouseGenderFullAr ?></small> <small style="color:#6B7280;font-size:11px;">يتم تحديده تلقائياً بناءً على نوع العضو/ة</small>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">الرقم القومي</label> <label class="form-label">الرقم القومي</label>
<input type="text" name="national_id" id="spouse_nid" value="<?= e(old('national_id')) ?>" class="form-input" maxlength="14" style="direction:ltr;text-align:left;font-size:16px;letter-spacing:1px;" placeholder="14 رقم"> <input type="text" name="national_id" id="spouse_nid" value="<?= e(old('national_id')) ?>" class="form-input" maxlength="14" style="direction:ltr;text-align:left;">
<div id="spouse-nid-feedback" style="margin-top:5px;font-size:12px;"></div> <div id="spouse-nid-feedback" style="margin-top:5px;font-size:12px;"></div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">تاريخ الميلاد <span style="color:#DC2626;">*</span></label> <label class="form-label">تاريخ الميلاد <span style="color:#DC2626;">*</span></label>
<input type="date" name="date_of_birth" id="spouse_dob" value="<?= e(old('date_of_birth')) ?>" class="form-input" required> <input type="date" name="date_of_birth" id="spouse_dob" value="<?= e(old('date_of_birth')) ?>" class="form-input" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">السن</label> <label class="form-label">السن</label>
<input type="text" id="spouse_age_display" class="form-input" style="background:#F3F4F6;" readonly> <input type="text" id="spouse_age_display" class="form-input" style="background:#F3F4F6;" readonly>
<input type="hidden" name="age_years" id="spouse_age_years"> <input type="hidden" name="age_years" id="spouse_age_years">
<input type="hidden" name="age_months" id="spouse_age_months"> <input type="hidden" name="age_months" id="spouse_age_months">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">تاريخ الزواج <span style="color:#DC2626;">*</span></label> <label class="form-label">تاريخ الزواج <span style="color:#DC2626;">*</span></label>
<input type="date" name="marriage_date" value="<?= e(old('marriage_date')) ?>" class="form-input" required max="<?= e(date('Y-m-d')) ?>"> <input type="date" name="marriage_date" value="<?= e(old('marriage_date')) ?>" class="form-input" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">الجنسية</label> <label class="form-label">الجنسية</label>
<select name="nationality" class="form-select"> <input type="text" name="nationality" value="<?= e(old('nationality', 'مصري')) ?>" class="form-input">
<option value="مصري" selected>مصري</option>
<?php foreach ($countries as $c): ?>
<?php if ($c['nationality_ar'] !== 'مصري'): ?>
<option value="<?= e($c['nationality_ar']) ?>"><?= e($c['nationality_ar']) ?></option>
<?php endif; ?>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label class="form-label">الديانة</label>
<select name="religion" class="form-select">
<option value="">-- اختر --</option>
<option value="muslim">مسلم/ة</option>
<option value="christian">مسيحي/ة</option>
</select>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">المؤهل</label> <label class="form-label">المؤهل</label>
<select name="qualification_id" class="form-select"> <select name="qualification_id" class="form-select">
<option value="">-- اختر --</option> <option value="">-- اختر --</option>
<?php foreach ($qualifications as $q): ?> <?php foreach ($qualifications as $q): ?>
<option value="<?= (int) $q['id'] ?>"><?= e($q['name_ar']) ?></option> <option value="<?= (int) $q['id'] ?>" <?= old('qualification_id') == $q['id'] ? 'selected' : '' ?>><?= e($q['name_ar']) ?></option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">المهنة</label> <label class="form-label">الوظيفة</label>
<input type="text" name="occupation" value="<?= e(old('occupation')) ?>" class="form-input"> <input type="text" name="occupation" value="<?= e(old('occupation')) ?>" class="form-input">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">المحمول</label> <label class="form-label">رقم المحمول</label>
<input type="tel" name="mobile" value="<?= e(old('mobile')) ?>" class="form-input" style="direction:ltr;text-align:left;" placeholder="01XXXXXXXXX"> <input type="tel" name="mobile" value="<?= e(old('mobile')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">تليفون العمل</label> <label class="form-label">تليفون العمل</label>
<input type="tel" name="work_phone" value="<?= e(old('work_phone')) ?>" class="form-input" style="direction:ltr;text-align:left;"> <input type="tel" name="work_phone" value="<?= e(old('work_phone')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div> </div>
<div class="form-group" style="grid-column:1/-1;"> <div class="form-group" style="grid-column:1/-1;">
<label class="form-label">عنوان العمل</label> <label class="form-label">عنوان العمل</label>
<textarea name="work_address" class="form-textarea" rows="2"><?= e(old('work_address')) ?></textarea> <textarea name="work_address" class="form-textarea" rows="2"><?= e(old('work_address')) ?></textarea>
...@@ -117,8 +110,8 @@ $spouseGenderFullAr = $spouseGender === 'male' ? 'ذكر' : 'أنثى'; ...@@ -117,8 +110,8 @@ $spouseGenderFullAr = $spouseGender === 'male' ? 'ذكر' : 'أنثى';
</div> </div>
<div style="display:flex;gap:10px;"> <div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:16px;">💍 إضافة ال<?= $spouseGenderAr ?></button> <button type="submit" class="btn btn-primary">إضافة <?= $spouseLabel ?></button>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline" style="padding:12px 20px;">إلغاء</a> <a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">إلغاء</a>
</div> </div>
</form> </form>
...@@ -133,13 +126,12 @@ document.addEventListener('DOMContentLoaded', function() { ...@@ -133,13 +126,12 @@ document.addEventListener('DOMContentLoaded', function() {
var ageYears = document.getElementById('spouse_age_years'); var ageYears = document.getElementById('spouse_age_years');
var ageMonths = document.getElementById('spouse_age_months'); var ageMonths = document.getElementById('spouse_age_months');
var feedback = document.getElementById('spouse-nid-feedback'); var feedback = document.getElementById('spouse-nid-feedback');
var requiredGender = '<?= e($spouseGender) ?>'; var requiredGender = '<?= e($genderValue) ?>';
nidInput.addEventListener('input', function() { nidInput.addEventListener('input', function() {
var val = this.value.replace(/\D/g, ''); var val = this.value.replace(/\D/g, '');
this.value = val; this.value = val;
if (val.length === 14) { if (val.length === 14) {
feedback.innerHTML = '<span style="color:#0284C7;">جاري التحقق...</span>';
var formData = new FormData(); var formData = new FormData();
formData.append('national_id', val); formData.append('national_id', val);
var csrfToken = document.querySelector('input[name="_csrf_token"]'); var csrfToken = document.querySelector('input[name="_csrf_token"]');
...@@ -154,13 +146,12 @@ document.addEventListener('DOMContentLoaded', function() { ...@@ -154,13 +146,12 @@ document.addEventListener('DOMContentLoaded', function() {
ageDisplay.value = p.age_years + ' سنة و ' + p.age_months + ' شهر'; ageDisplay.value = p.age_years + ' سنة و ' + p.age_months + ' شهر';
ageYears.value = p.age_years; ageYears.value = p.age_years;
ageMonths.value = p.age_months; ageMonths.value = p.age_months;
feedback.innerHTML = '<span style="color:#059669;">✓ صالح — ' + (p.gender === 'male' ? 'ذكر' : 'أنثى') + '</span>'; feedback.innerHTML = '<span style="color:#059669;">✓ صالح</span>';
// Check gender matches required // Check gender mismatch
if (p.gender !== requiredGender) { if (p.gender !== requiredGender) {
var reqAr = requiredGender === 'male' ? 'ذكر' : 'أنثى'; var needed = requiredGender === 'male' ? 'ذكر' : 'أنثى';
var gotAr = p.gender === 'male' ? 'ذكر' : 'أنثى'; feedback.innerHTML += '<br><span style="color:#DC2626;">✖ الرقم القومي يشير إلى نوع مختلف — المطلوب: ' + needed + '</span>';
feedback.innerHTML = '<span style="color:#DC2626;">✖ الرقم القومي يشير إلى ' + gotAr + ' — يجب أن يكون ' + reqAr + '</span>';
} }
if (data.duplicate) { if (data.duplicate) {
......
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