- 12 Aug, 2026 7 commits
-
-
Mahmoud Aglan authored
The treasury queue page filtered by payment_type whitelist and didn't include sa_game_ticket, sa_pool_ticket, or pool_reservation — so those payment requests were created but never appeared in the cashier queue. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The issueTicket response was passing the raw Model object to json(), which could fail serialization. Now returns entry_id instead, and wraps recordEntry in try-catch for proper error reporting. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The service desk ticket form allows issuing tickets to walk-in guests without a carnet or membership, but the carnet_guest_entries table had NOT NULL + FK constraints on both columns, causing a 500 error. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The PoolReservationService already called PaymentRequestService::createRequest() but with member_id=0 and payment_type='pool_reservation' — which was silently rejected because 'pool_reservation' wasn't in the non-member whitelist. - Added 'pool_reservation' to PaymentRequestService whitelist - Added payment type label (حجز حارة سباحة) - Added payment completion handler → marks reservation as paid - Added void handler → reverts reservation to pending Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Game tickets now create payment requests (sa_game_ticket type) sent to the cashier sub-queue, matching pool ticket and booking patterns - Added session lifecycle actions: start, complete, cancel with UI buttons - Fixed session status bug: allow issuing tickets during 'in_progress' sessions - Fixed GuestEntryService crash when no carnet exists (skip UPDATE on id=0) - Added payment completion/void handlers for game and pool tickets - Extended PaymentRequestService whitelist for sa_game_ticket/sa_pool_ticket - Added comprehensive tutorial sections 9-14 covering entertainment workflows Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The ticket form and lookup in service-desk sent '_token' but the CSRF middleware expects '_csrf_token'. This caused HTTP 419 on every ticket issuance attempt. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Critical fixes: - submitFormPayment() now checks sa_players.registration_fee_paid as a safety net before creating any payment request - startRegistration() member_id fallback only used when NID matches the member's actual national_id (prevents NID-swap fee bypass) - lookupPlayer() validates NID-to-member consistency before proceeding - Existing registration lookup now includes 'pending_payment' status - Frontend clears membership_number when NID is manually changed after auto-fill Feature: - Player profile page now shows "Print Form" and "Generate Card" buttons for completing registration steps outside the wizard Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 11 Aug, 2026 12 commits
-
-
Mahmoud Aglan authored
- Convert migrations 103_007 and 103_008 to closures that check column existence before ALTER TABLE (fixes boot failure when columns were already added manually) - Add docs/tutorials/discounts-and-offers-guide.md Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Add file upload field on violation creation (image/PDF/doc, max 5MB) - Store proof_file path in violations table (new column) - Show proof file link (
📎 ) in violations index table - Auto-send monetary fines to cashier queue on imposition - Add manual "طابور الدفع" button on fines index for unpaid fines - Add sendToQueue controller method + route Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The base Model::create() auto-appends created_by when timestamps are enabled, but workflow_instances and workflow_transition_log were missing this column — causing PDOException on fine imposition. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Add qualification dropdown to first screen of child separation — the fee is a percentage of new membership value which varies by qualification - Pass selected qualification_code to SeparationFeeCalculator (was null) - Add /api/members/check-nid endpoint for real-time NID conflict detection - Add client-side NID conflict check on both form_fill and create pages - Block submission when active member has same NID (show clear error) - Warn (orange) when potential/inactive member exists (will auto-archive) - Server-side validation: reject if active NID conflict in store/saveFillForm - Validate qualification_code required for child_separation in store() - Add phone format validation on form_fill submit Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Transfer #14 failed because the national_id already existed on a potential (never-activated) member record. Now archives any conflicting potential/inactive/pending member with the same national_id before inserting the new active member. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
When a member dies in the same financial year they already have subscriptions for, don't charge the annual subscription again in the death fee — the man died before using it. Only earlier years' overdue subscriptions must be settled first. Changes: - calculateFamilyAnnualSub returns 0 if current FY member subscription already exists - createNewSubscriptions transfers existing FY subscriptions from deceased to new member instead of creating duplicate paid rows - Overdue check on create page excludes current FY (only blocks for previous years) - Views show "معفى — السنة المالية الحالية مغطاة" when current FY is covered Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Child/spouse death now auto-completes on creation (no manual "complete" click needed), which immediately archives the deceased and deletes their pending/overdue subscription rows - Child separation (TransferProcessor) now deletes the separated child's unpaid subscription rows from the parent's membership when the transfer completes - Spouse separation also removes their subscription rows Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Separation now follows: create → board approval → cashier payment → fill form → complete. After cashier confirms payment, status moves to pending_form_fill. A new form page collects personal data (name, NID, DOB, contact, address, qualification) before executing the transfer. TransferProcessor uses form_data from notes to create the new member record. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Children who were separated (فصل) or deceased now remain visible on the parent member's profile instead of disappearing. They appear greyed out with status labels (مفصول / متوفي) and for separated children, a link to their new membership profile with the membership number. The family tree count distinguishes active vs inactive children. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Add transferred_from_transfer_id column to track child separation origin - TransferProcessor sets the field when completing child separations - Member profile detects separation transfers and shows: parent name, separation date, actual paid fees (not full membership_value) - Financial summary shows "رسوم الفصل المدفوعة" for separated children - Transfer status labels: "requested" now shows "في انتظار الدفع" - AuditService: comprehensive Arabic labels for statuses, fields, and formatted values (payment_method, membership_type, classification, etc.) - Backfill migration links existing completed transfers to their target members Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Retroactive payments now fire payment.completed event so DailyCashMovementService records them in daily_cash_movements (treasury). Zero-amount installments (from bad data where down_payment = total_amount) no longer create empty payment records. Also simplifies overdue subscriptions section on member profile to a warning banner. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 10 Aug, 2026 1 commit
-
-
Mahmoud Aglan authored
1. Subscription badge: shows if active (paid), overdue, or awaiting renewal 2. Overdue table: lists all unpaid subscriptions with amounts needed to renew 3. Transfer origin card: shows who transferred, when, and actual amount paid 4. Financial summary: shows actual paid amount (not original membership_value) for transferred members 5. Payment history link for deceased member (accessible from transferee's page) 6. Redirect /members/{id} to archive page if member is archived Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 09 Aug, 2026 5 commits
-
-
Mahmoud Aglan authored
The subscriptions table has no is_voided column — the status filter ('pending', 'overdue') is sufficient. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Adds a calc_breakdown to the fee response showing acquisition_date, effective_transfer_date, exact diff (years/months/days), and each year range so staff can verify the calculation is correct. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The date under the status badge now uses membership_origin_date (the original first-ever date of the membership) instead of created_at (which for death/waiver transfers is just when the new record was created). Falls back to activated_at then created_at. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Shows a modal popup on death/create page listing overdue years and amounts. The user must go to subscriptions and settle outstanding balances before registering a death case. The death fee calculation itself remains unchanged. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
When membership transfers via death/waiver, the fee calculator now uses the original membership creation date instead of the transfer date. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 08 Aug, 2026 15 commits
-
-
Mahmoud Aglan authored
Creates a complete reservation system for freelance coaches and individuals booking pool lanes or session cards. Includes: - 3-step wizard (booker info → package selection → confirmation) - Overflow handling when participant count exceeds lane capacity - Reservations create real sa_groups records for Mirror grid integration - Session tracking (used/remaining) with manual + automatic decrement - Payment request integration with cashier module - List view with search/filter and detail view with progress tracker New tables: sa_pool_reservations, sa_groups.source_type/pool_reservation_id New programs: SWIM-LANE-RENTAL, SWIM-CARDS (program_type='rental') Updated pricing in sa_academy_pricing for lanes and cards Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
fix(sports): redesign assessment wizard — programs as expandable list with inline groups & centered modal - Programs display as vertical list items with chevron toggle - Clicking a program expands its groups panel directly beneath it - Group creation opens a centered modal popup instead of inline section - Fixed GroupQuickCreateApiController to return full group data (fees, coach, discipline) - Groups render with progress bars and proper fee display after creation Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Classification now shows: Discipline tabs → Program cards → Groups - Each group card shows progress bar (capacity %), coach, price, remaining slots - "Add Group" button creates a new group inline via API without leaving the page - New API: POST /api/sa/groups/quick-create for inline group creation - Controller now passes programs data to the view Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Route is POST /sa/groups/{id}, not /sa/groups/{id}/update. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Teams (Prep–General, 90-120min): 3-6x/week - Member: 750–900 EGP | Non-member: 1500–1800 EGP Masters (25+, 60-120min): 2-5x/week - Member: 800–1450 EGP | Non-member: 1600–2900 EGP Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Kids Academy (6mo–12y): Groups 5-1, Private, and Babys - Member: 480–1600 EGP | Non-member: 960–3200 EGP Adults Academy (13+): Groups 5-1 - Member: 600–1000 EGP | Non-member: 1200–2000 EGP Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Adds lane rental pricing for the Olympic pool per the official pricing table: - 25m lane: 500-1400 EGP (max 8 players) by entity type - 50m lane: 800-1700 EGP (max 12 players) by entity type Entity types: schools/gov, clubs/federations, other entities, foreign entities Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- SaMonthlySubscriptionJob: runs on days 1-3 of each month, auto-generates subscriptions for all active players using SubscriptionGeneratorService - Subscriptions index page now shows stats bar (total, paid, unpaid, overdue, collected amount, pending amount) for the selected month Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Shows player name, group, discipline, program, level, and monthly fee amount in a modal before the coach confirms the submission. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The classification query required status='completed' AND payment_status= 'paid', but the correct flow is: form fee → photo → disciplines → THEN coach classifies. Players were invisible because they're still 'in_progress' at that point. Fixed to: form_payment_status='paid' AND selected_disciplines not empty AND status IN ('in_progress','completed'). Also fixed the assess() method's guard from payment_status to form_payment_status. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
handleSubscriptionPaid() now finds the player's assessed registration and marks it as completed (payment_status='paid', status='completed'). Previously, paying the subscription only updated sa_subscriptions but never flowed back to sa_registrations, leaving the wizard card button permanently disabled. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
1. Player profile now shows a "طباعة الكارت" button (green) if the player has an active card, or "إنشاء الكارت" (outline) if they have a paid registration without a card yet. This provides a direct path to print/generate cards without re-entering the wizard. 2. Medical Board list (/medical-board) now has clickable player names that link to the player's profile (SA players → /sa/players/{id}, membership players → /members/{id}). Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
1. Renamed "تقييم اللاعبين" to "تصنيف وتعيين اللاعبين" throughout (sidebar, page titles, buttons) — better describes the actual action of classifying players into groups. 2. Assessment page now shows discipline tabs — clicking a discipline filters groups to only show those belonging to that activity. Player's selected disciplines are already visible at top. 3. Fixed medical certificate bug: when approving via the player profile edit form (updateMedical), the document's approval_status was not being synced. Now setting player to fit/conditional also marks the latest document as approved, and unfit marks it as rejected. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Step 4 now shows contextual messages: - Default: "waiting for coach assessment" - After assessment: "waiting for subscription payment" - After payment: enables card generation button Also refines the lookup block: only refuse entry when status is completed/assessed AND card_generated=1 (fully done). Players who still need to finish steps are redirected to resume. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Players who already completed the full registration (status=completed/assessed) are now refused at the first lookup screen with an error message instead of being redirected to their existing registration. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-