Commit 7d252da8 authored by Administrator's avatar Administrator

Update 2 files via Son of Anton

parent 17e8255e
......@@ -22,7 +22,6 @@ class SpouseController extends Controller
return $this->redirect('/members')->withError('العضو غير موجود');
}
// BLOCK: membership_value must be set
$membershipValue = $member['membership_value'] ?? '0.00';
if (bccomp($membershipValue, '0.01', 2) < 0) {
return $this->redirect('/members/' . $memberId)
......@@ -33,10 +32,12 @@ class SpouseController extends Controller
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
$spouseOrder = Spouse::getNextOrder((int) $memberId);
// Pre-calculate fee estimate for display
// 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'),
'nationality' => 'مصري',
'marriage_date' => date('Y-m-d'),
]);
return $this->view('Spouses.Views.create', [
......@@ -44,6 +45,7 @@ class SpouseController extends Controller
'qualifications' => $qualifications,
'countries' => $countries,
'spouseOrder' => $spouseOrder,
'spouseGender' => $spouseGender,
'feeEstimate' => $feeEstimate,
]);
}
......@@ -56,7 +58,6 @@ class SpouseController extends Controller
return $this->redirect('/members')->withError('العضو غير موجود');
}
// BLOCK: membership_value must be set
$membershipValue = $member['membership_value'] ?? '0.00';
if (bccomp($membershipValue, '0.01', 2) < 0) {
return $this->redirect('/members/' . $memberId)
......@@ -70,9 +71,11 @@ class SpouseController extends Controller
if (empty(trim($data['full_name_ar'] ?? ''))) $errors[] = 'اسم الزوج/ة مطلوب';
if (empty($data['date_of_birth'] ?? '')) $errors[] = 'تاريخ الميلاد مطلوب';
if (empty($data['gender'] ?? '')) $errors[] = 'النوع مطلوب';
if (empty($data['marriage_date'] ?? '')) $errors[] = 'تاريخ الزواج مطلوب';
// ── Gender validation: only male/female, must be opposite of member ──
$requiredSpouseGender = ($member['gender'] === 'male') ? 'female' : 'male';
// Max spouse check
$existingCount = Spouse::countActiveForMember((int) $memberId);
$maxSpouses = ($member['gender'] === 'male') ? 4 : 1;
......@@ -91,6 +94,14 @@ class SpouseController extends Controller
$data['age_years'] = $parsed['age_years'];
$data['age_months'] = $parsed['age_months'];
$data['gender'] = $parsed['gender'];
// Validate gender from NID matches required
if ($parsed['gender'] !== $requiredSpouseGender) {
$memberGenderAr = $member['gender'] === 'male' ? 'ذكر' : 'أنثى';
$requiredAr = $requiredSpouseGender === 'male' ? 'ذكر' : 'أنثى';
$parsedAr = $parsed['gender'] === 'male' ? 'ذكر' : 'أنثى';
$errors[] = "العضو {$memberGenderAr} — يجب أن يكون الزوج/ة {$requiredAr} — الرقم القومي يشير إلى {$parsedAr}";
}
}
// Cannot be the member themselves
......@@ -103,6 +114,9 @@ class SpouseController extends Controller
if ($dup) {
$errors[] = 'الرقم القومي مسجل بالفعل: ' . ($dup['data']['full_name_ar'] ?? '');
}
} else {
// No NID — force correct gender
$data['gender'] = $requiredSpouseGender;
}
// Calculate age
......@@ -140,7 +154,6 @@ class SpouseController extends Controller
}
$spouseOrder = $feeCalc['spouse_order'] ?? Spouse::getNextOrder((int) $memberId);
$joinDate = date('Y-m-d');
$spouse = Spouse::create([
'member_id' => (int) $memberId,
......@@ -152,7 +165,7 @@ class SpouseController extends Controller
'date_of_birth' => $data['date_of_birth'],
'age_years' => (int) ($data['age_years'] ?? 0),
'age_months' => (int) ($data['age_months'] ?? 0),
'gender' => $data['gender'],
'gender' => $requiredSpouseGender,
'nationality' => $data['nationality'] ?? 'مصري',
'religion' => $data['religion'] ?? null,
'qualification_id' => ($data['qualification_id'] ?? '') !== '' ? (int) $data['qualification_id'] : null,
......@@ -161,7 +174,7 @@ class SpouseController extends Controller
'work_phone' => $data['work_phone'] ?? null,
'mobile' => $data['mobile'] ?? null,
'marriage_date' => $data['marriage_date'],
'join_date' => $joinDate,
'join_date' => date('Y-m-d'),
'classification' => 'working',
'addition_fee' => $feeCalc['total_fee'] ?? '0.00',
'status' => 'active',
......@@ -174,10 +187,6 @@ class SpouseController extends Controller
'fee' => $feeCalc['total_fee'] ?? '0.00',
]);
// Build success message with full breakdown
$breakdown = $feeCalc['breakdown'] ?? [];
$breakdownText = !empty($breakdown) ? "\n" . implode("\n", $breakdown) : '';
$msg = 'تم إضافة الزوج/الزوجة بنجاح';
$msg .= ' — الترتيب: #' . $spouseOrder;
$msg .= ' — الرسوم: ' . money($feeCalc['total_fee'] ?? '0');
......@@ -195,10 +204,8 @@ class SpouseController extends Controller
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة');
}
$db = App::getInstance()->db();
$member = $db->selectOne("SELECT * FROM members WHERE id = ?", [(int) $memberId]);
return $this->view('Spouses.Views.show', ['member' => $member, 'spouse' => $spouse]);
}
......@@ -208,17 +215,13 @@ class SpouseController extends Controller
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة');
}
$db = App::getInstance()->db();
$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");
$countries = $db->select("SELECT nationality_ar FROM countries WHERE is_active = 1 ORDER BY name_ar");
return $this->view('Spouses.Views.edit', [
'member' => $member,
'spouse' => $spouse,
'qualifications' => $qualifications,
'countries' => $countries,
'member' => $member, 'spouse' => $spouse,
'qualifications' => $qualifications, 'countries' => $countries,
]);
}
......@@ -228,10 +231,8 @@ class SpouseController extends Controller
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة');
}
$data = $request->all();
unset($data['_csrf_token']);
$updateData = [];
foreach (['full_name_en', 'occupation', 'work_address', 'work_phone', 'mobile', 'religion'] as $field) {
if (array_key_exists($field, $data)) {
......@@ -242,11 +243,7 @@ class SpouseController extends Controller
if (isset($data['qualification_id'])) {
$updateData['qualification_id'] = ($data['qualification_id'] !== '') ? (int) $data['qualification_id'] : null;
}
if (!empty($updateData)) {
$spouse->update($updateData);
}
if (!empty($updateData)) $spouse->update($updateData);
return $this->redirect("/members/{$memberId}")->withSuccess('تم تحديث بيانات الزوج/ة');
}
......@@ -256,29 +253,18 @@ class SpouseController extends Controller
if (!$spouse || (int) $spouse->member_id !== (int) $memberId) {
return $this->redirect("/members/{$memberId}")->withError('بيانات الزوج/ة غير موجودة');
}
$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();
$db = App::getInstance()->db();
$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,
'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]);
EventBus::dispatch('spouse.removed', [
'member_id' => (int) $memberId,
'spouse_id' => (int) $id,
'reason' => $reason,
]);
EventBus::dispatch('spouse.removed', ['member_id' => (int) $memberId, 'spouse_id' => (int) $id, 'reason' => $reason]);
return $this->redirect("/members/{$memberId}")->withSuccess('تم إزالة الزوج/ة');
}
}
\ No newline at end of file
<?php $__template->layout('Layout.main'); ?>
<?php $__template->section('title'); ?>إضافة زوج/زوجة — <?= e($member['full_name_ar']) ?><?php $__template->endSection(); ?>
<?php $__template->section('title'); ?>إضافة زوج/ة — <?= e($member['full_name_ar']) ?><?php $__template->endSection(); ?>
<?php $__template->section('content'); ?>
<?php
$memberGenderAr = $member['gender'] === 'male' ? 'ذكر' : 'أنثى';
$spouseGenderAr = $spouseGender === 'male' ? 'زوج' : 'زوجة';
$spouseGenderFullAr = $spouseGender === 'male' ? 'ذكر' : 'أنثى';
?>
<div class="card" style="margin-bottom:15px;padding:15px;display:flex;justify-content:space-between;align-items:center;">
<div>
<strong>العضو:</strong> <?= e($member['full_name_ar']) ?>
<strong>العضو:</strong> <?= e($member['full_name_ar']) ?> (<?= $memberGenderAr ?>)
&nbsp;|&nbsp; <strong>رقم العضوية:</strong> <?= e($member['membership_number'] ?? 'لم يُحدد') ?>
&nbsp;|&nbsp; <strong>ترتيب الزوج/الزوجة:</strong> #<?= (int) $spouseOrder ?>
&nbsp;|&nbsp; <strong>قيمة العضوية:</strong> <?= money($member['membership_value'] ?? '0') ?>
&nbsp;|&nbsp; <strong>ترتيب ال<?= $spouseGenderAr ?>:</strong> #<?= (int) $spouseOrder ?>
</div>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">← العودة للعضو</a>
</div>
<form method="POST" action="/members/<?= (int) $member['id'] ?>/spouses" id="spouse-form">
<!-- 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">
<?= csrf_field() ?>
<div class="card" style="margin-bottom:20px;">
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;"><h3 style="margin:0;color:#0D7377;">بيانات الزوج/الزوجة</h3></div>
<div style="padding:15px 20px;border-bottom:1px solid #E5E7EB;">
<h3 style="margin:0;color:#0D7377;">بيانات ال<?= $spouseGenderAr ?></h3>
</div>
<div style="padding:20px;display:grid;grid-template-columns:1fr 1fr;gap:15px;">
<div class="form-group" style="grid-column:1/-1;">
<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="10" 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 class="form-group">
<label class="form-label">الاسم بالإنجليزي</label>
<input type="text" name="full_name_en" value="<?= e(old('full_name_en')) ?>" class="form-input">
<input type="text" name="full_name_en" value="<?= e(old('full_name_en')) ?>" class="form-input" style="direction:ltr;text-align:left;">
</div>
<div class="form-group">
<label class="form-label">الرقم القومي</label>
<input type="text" name="national_id" id="spouse_nid" value="<?= e(old('national_id')) ?>" class="form-input" maxlength="14" pattern="\d{14}" style="direction:ltr;text-align:left;" placeholder="14 رقم">
<div id="nid-feedback" style="margin-top:5px;font-size:12px;"></div>
<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="hidden" name="gender" value="<?= e($spouseGender) ?>">
<small style="color:#6B7280;">يُحدد تلقائياً — العضو <?= $memberGenderAr ?> فالزوج/ة <?= $spouseGenderFullAr ?></small>
</div>
<div class="form-group">
<label class="form-label">رقم جواز السفر</label>
<input type="text" name="passport_number" value="<?= e(old('passport_number')) ?>" class="form-input" style="direction:ltr;text-align:left;">
<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 رقم">
<div id="spouse-nid-feedback" style="margin-top:5px;font-size:12px;"></div>
</div>
<div class="form-group">
<label class="form-label">تاريخ الميلاد <span style="color:#DC2626;">*</span></label>
......@@ -40,25 +61,21 @@
</div>
<div class="form-group">
<label class="form-label">السن</label>
<input type="text" id="spouse_age" class="form-input" style="background:#F3F4F6;" readonly>
<input type="hidden" name="age_years" id="spouse_age_years" value="<?= e(old('age_years')) ?>">
<input type="hidden" name="age_months" id="spouse_age_months" value="<?= e(old('age_months')) ?>">
<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_months" id="spouse_age_months">
</div>
<div class="form-group">
<label class="form-label">النوع <span style="color:#DC2626;">*</span></label>
<select name="gender" id="spouse_gender" class="form-select" required>
<option value="">-- اختر --</option>
<option value="male" <?= old('gender') === 'male' ? 'selected' : '' ?>>ذكر</option>
<option value="female" <?= old('gender') === 'female' ? 'selected' : '' ?>>أنثى</option>
</select>
<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')) ?>">
</div>
<div class="form-group">
<label class="form-label">الجنسية <span style="color:#DC2626;">*</span></label>
<label class="form-label">الجنسية</label>
<select name="nationality" class="form-select">
<option value="مصري" selected>مصري</option>
<?php foreach ($countries as $c): ?>
<?php if ($c['nationality_ar'] !== 'مصري'): ?>
<option value="<?= e($c['nationality_ar']) ?>" <?= old('nationality') === $c['nationality_ar'] ? 'selected' : '' ?>><?= e($c['nationality_ar']) ?></option>
<option value="<?= e($c['nationality_ar']) ?>"><?= e($c['nationality_ar']) ?></option>
<?php endif; ?>
<?php endforeach; ?>
</select>
......@@ -67,27 +84,26 @@
<label class="form-label">الديانة</label>
<select name="religion" class="form-select">
<option value="">-- اختر --</option>
<option value="muslim" <?= old('religion') === 'muslim' ? 'selected' : '' ?>>مسلم</option>
<option value="christian" <?= old('religion') === 'christian' ? 'selected' : '' ?>>مسيحي</option>
<option value="other" <?= old('religion') === 'other' ? 'selected' : '' ?>>أخرى</option>
<option value="muslim">مسلم/ة</option>
<option value="christian">مسيحي/ة</option>
</select>
</div>
<div class="form-group">
<label class="form-label">المؤهل الدراسي</label>
<label class="form-label">المؤهل</label>
<select name="qualification_id" class="form-select">
<option value="">-- اختر --</option>
<?php foreach ($qualifications as $q): ?>
<option value="<?= (int) $q['id'] ?>" <?= old('qualification_id') == $q['id'] ? 'selected' : '' ?>><?= e($q['name_ar']) ?></option>
<option value="<?= (int) $q['id'] ?>"><?= e($q['name_ar']) ?></option>
<?php endforeach; ?>
</select>
</div>
<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">
</div>
<div class="form-group">
<label class="form-label">رقم المحمول</label>
<input type="tel" name="mobile" value="<?= e(old('mobile')) ?>" class="form-input" style="direction:ltr;text-align:left;" maxlength="11">
<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">
</div>
<div class="form-group">
<label class="form-label">تليفون العمل</label>
......@@ -95,18 +111,14 @@
</div>
<div class="form-group" style="grid-column:1/-1;">
<label class="form-label">عنوان العمل</label>
<input type="text" name="work_address" value="<?= e(old('work_address')) ?>" class="form-input">
</div>
<div class="form-group">
<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>
<textarea name="work_address" class="form-textarea" rows="2"><?= e(old('work_address')) ?></textarea>
</div>
</div>
</div>
<div style="display:flex;gap:10px;">
<button type="submit" class="btn btn-primary">إضافة الزوج/الزوجة</button>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline">إلغاء</a>
<button type="submit" class="btn btn-primary" style="padding:12px 30px;font-size:16px;">💍 إضافة ال<?= $spouseGenderAr ?></button>
<a href="/members/<?= (int) $member['id'] ?>" class="btn btn-outline" style="padding:12px 20px;">إلغاء</a>
</div>
</form>
......@@ -117,16 +129,17 @@
document.addEventListener('DOMContentLoaded', function() {
var nidInput = document.getElementById('spouse_nid');
var dobInput = document.getElementById('spouse_dob');
var ageDisplay = document.getElementById('spouse_age');
var ageDisplay = document.getElementById('spouse_age_display');
var ageYears = document.getElementById('spouse_age_years');
var ageMonths = document.getElementById('spouse_age_months');
var genderSelect = document.getElementById('spouse_gender');
var feedback = document.getElementById('nid-feedback');
var feedback = document.getElementById('spouse-nid-feedback');
var requiredGender = '<?= e($spouseGender) ?>';
nidInput.addEventListener('input', function() {
var val = this.value.replace(/\D/g, '');
this.value = val;
if (val.length === 14) {
feedback.innerHTML = '<span style="color:#0284C7;">جاري التحقق...</span>';
var formData = new FormData();
formData.append('national_id', val);
var csrfToken = document.querySelector('input[name="_csrf_token"]');
......@@ -141,18 +154,38 @@ document.addEventListener('DOMContentLoaded', function() {
ageDisplay.value = p.age_years + ' سنة و ' + p.age_months + ' شهر';
ageYears.value = p.age_years;
ageMonths.value = p.age_months;
genderSelect.value = p.gender;
feedback.innerHTML = '<span style="color:#059669;">✓ الرقم القومي صالح</span>';
feedback.innerHTML = '<span style="color:#059669;">✓ صالح — ' + (p.gender === 'male' ? 'ذكر' : 'أنثى') + '</span>';
// Check gender matches required
if (p.gender !== requiredGender) {
var reqAr = requiredGender === 'male' ? 'ذكر' : 'أنثى';
var gotAr = p.gender === 'male' ? 'ذكر' : 'أنثى';
feedback.innerHTML = '<span style="color:#DC2626;">✖ الرقم القومي يشير إلى ' + gotAr + ' — يجب أن يكون ' + reqAr + '</span>';
}
if (data.duplicate) {
feedback.innerHTML += '<br><span style="color:#DC2626;">✖ الرقم مسجل بالفعل: ' + data.duplicate.full_name_ar + '</span>';
feedback.innerHTML += '<br><span style="color:#DC2626;">✖ مسجل: ' + data.duplicate.full_name_ar + '</span>';
}
} else {
feedback.innerHTML = '<span style="color:#DC2626;">✖ ' + (p.errors ? p.errors.join(' | ') : 'رقم غير صالح') + '</span>';
feedback.innerHTML = '<span style="color:#DC2626;">✖ ' + (p.errors ? p.errors.join(' | ') : 'غير صالح') + '</span>';
}
})
.catch(function() { feedback.innerHTML = '<span style="color:#DC2626;">خطأ في الاتصال</span>'; });
} else {
feedback.innerHTML = '';
.catch(function() { feedback.innerHTML = '<span style="color:#DC2626;">خطأ</span>'; });
} else { feedback.innerHTML = ''; }
});
dobInput.addEventListener('change', function() {
if (this.value && !nidInput.value) {
var dob = new Date(this.value);
var now = new Date();
var years = now.getFullYear() - dob.getFullYear();
var months = now.getMonth() - dob.getMonth();
if (months < 0 || (months === 0 && now.getDate() < dob.getDate())) { years--; months += 12; }
if (now.getDate() < dob.getDate()) months--;
if (months < 0) months = 0;
ageDisplay.value = years + ' سنة و ' + months + ' شهر';
ageYears.value = years;
ageMonths.value = months;
}
});
});
......
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