Commit de24ec03 authored by Fares's avatar Fares

feat(sports): require qualification on sports member creation for fee calculation

Sports membership fee is 50% of the membership price tier which is
determined by the qualification. Added a qualification dropdown that
appears when membership type is 'sports', with server-side validation
requiring it. The qualification_id is saved on the member record so
the billing and sports registration can correctly calculate the fee.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 02b693a7
......@@ -69,8 +69,10 @@ class MemberController extends Controller
{
$db = App::getInstance()->db();
$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");
return $this->view('Members.Views.create', [
'branches' => $branches,
'qualifications' => $qualifications,
'nextFormNumber' => MemberNumberGenerator::next(),
'formFee' => MemberNumberGenerator::getFormFee(),
'needsStartNumber' => (MemberNumberGenerator::next() === null),
......@@ -97,6 +99,7 @@ class MemberController extends Controller
if ($fullNameAr === '' || mb_strlen($fullNameAr) < 5) $errors[] = 'الاسم بالعربي مطلوب (5 أحرف على الأقل)';
if ($phoneMobile === '' || !preg_match('/^01[0125]\d{8}$/', $phoneMobile)) $errors[] = 'رقم المحمول غير صالح';
if ($branchId <= 0) $errors[] = 'الفرع مطلوب';
if ($membershipType === 'sports' && empty($request->post('qualification_id'))) $errors[] = 'المؤهل مطلوب للعضوية الرياضية';
$dob = $gender = $govCode = null; $ageYears = $ageMonths = null; $idType = 'national_id';
if ($nationalId !== '') {
......@@ -140,7 +143,8 @@ class MemberController extends Controller
if ($membershipType === 'foreign') {
$nationality = 'أجنبي';
}
$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' => $nationality, 'form_number' => (string) $formNumber, 'form_date' => date('Y-m-d'), 'status' => 'potential', 'membership_type' => $membershipType, 'member_category' => $membershipType . '_member']);
$qualificationId = (int) $request->post('qualification_id', 0) ?: null;
$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' => $nationality, 'form_number' => (string) $formNumber, 'form_date' => date('Y-m-d'), 'status' => 'potential', 'membership_type' => $membershipType, 'member_category' => $membershipType . '_member', 'qualification_id' => $qualificationId]);
$photoResult = PhotoUploadService::upload($photoFile, 'members', (int) $member->id);
if ($photoResult) {
$member->update(['photo_path' => $photoResult['path']]);
......
......@@ -145,6 +145,18 @@
<small id="membership-type-hint" style="color:#6B7280;font-size:12px;margin-top:4px;display:block;"></small>
</div>
<!-- Qualification (for sports/working) -->
<div class="form-group" id="qualification-group" style="display:none;">
<label class="form-label">المؤهل / Qualification <span style="color:#DC2626;">*</span></label>
<select name="qualification_id" id="qualification_id" class="form-select" style="font-size:16px;">
<option value="">-- اختر المؤهل --</option>
<?php foreach ($qualifications as $q): ?>
<option value="<?= (int) $q['id'] ?>" <?= old('qualification_id') == $q['id'] ? 'selected' : '' ?>><?= e($q['name_ar']) ?></option>
<?php endforeach; ?>
</select>
<small style="color:#6B7280;font-size:12px;">يحدد فئة سعر العضوية</small>
</div>
<!-- Toggle for non-Egyptian -->
<div class="form-group" style="grid-column:1/-1;">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;color:#6B7280;">
......@@ -269,10 +281,12 @@ document.addEventListener('DOMContentLoaded', function() {
// Membership type switching logic
var typeSelect = document.getElementById('membership_type');
var typeHint = document.getElementById('membership-type-hint');
var qualGroup = document.getElementById('qualification-group');
var qualSelect = document.getElementById('qualification_id');
var typeHints = {
'working': 'العضو الأساسي — الحد الأدنى 21 سنة — يسمح بإضافة تابعين',
'seasonal': 'عضوية موسمية — بدون حد أدنى للسن — رسوم حسب المدة',
'sports': 'عضوية رياضية — لاعب مسجل باتحاد لعبة',
'sports': 'عضوية رياضية — لاعب مسجل باتحاد لعبة — الرسوم 50% من قيمة العضوية',
'honorary': 'عضوية شرفية — بقرار مجلس أمناء — بدون رسوم',
'foreign': 'عضو أجنبي — الحد الأدنى 21 سنة — جواز سفر مطلوب'
};
......@@ -283,6 +297,13 @@ document.addEventListener('DOMContentLoaded', function() {
noNidToggle.checked = true;
noNidToggle.dispatchEvent(new Event('change'));
}
if (val === 'sports') {
qualGroup.style.display = '';
qualSelect.required = true;
} else {
qualGroup.style.display = 'none';
qualSelect.required = false;
}
}
typeSelect.addEventListener('change', updateTypeHint);
updateTypeHint();
......
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