1. 26 Jun, 2026 3 commits
    • Mahmoud Aglan's avatar
      fix(payments): actually update subscription/fine/installment records when paid from process page · 9044313e
      Mahmoud Aglan authored
      The generic payment process page (linked from waiver "الانتقال إلى السداد") was
      recording payments in the payments table but never updating the source records
      (subscriptions.paid_amount, fines.paid_amount, installment_schedule.paid_amount).
      This caused the debt check to keep showing debts as unpaid after payment.
      
      Added handleSubscriptionPayment, handleFinePayment, and handleInstallmentPayment
      methods that mark the underlying records as paid (oldest first).
      
      Also fixed waiver debt check using wrong fines status ('pending' instead of
      'imposed'/'appeal_upheld') which is what fines actually use.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      9044313e
    • Mahmoud Aglan's avatar
      feat(waiver): per-individual fee assignment + fix 404 payment button · c8fae48c
      Mahmoud Aglan authored
      - New table `waiver_individual_fees` stores fee per person (not per category)
      - Board approval screen shows each excess person as a separate card with:
        name, DOB, age, age category, relationship, independent fee type/rate
      - Children 25+ flagged with warning and "فصل العضوية" button
      - Live JS calculates per-person amounts and updates grand total instantly
      - Fee breakdown section shows individual names when individual fees exist
      - Fix: /members/{id}/financial → /payments/process/{id} (was 404)
      - WaiverProcessor::getExcessIndividuals() identifies the specific excess persons
      - WaiverProcessor::saveIndividualFees() persists per-person board decisions
      - Age categories expanded: under_12, 12_to_16, 16_to_18, 18_to_25, 25_plus
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      c8fae48c
    • Mahmoud Aglan's avatar
      feat(waiver): Round 2 UX — comprehensive debt check, children details, payment in target's name · 83f24d7a
      Mahmoud Aglan authored
      - checkDebtsComprehensive() returns per-person breakdown (name, type, debt_type, period, amount)
      - getDependentDetails() calculates age, DOB, age category for children
      - New sendToCashier route creates payment request in TARGET member's name (buyer pays)
      - Detailed receipt breakdown with both member names and per-category fees
      - show.php: per-person debt table, children comparison, status indicators, go-to-payment button
      - create.php: detailed debt display with person labels, children age table
      - Status flow: requested → approved → send to cashier → fee_paid → complete
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      83f24d7a
  2. 23 Jun, 2026 1 commit
  3. 22 Jun, 2026 5 commits
  4. 21 Jun, 2026 3 commits
  5. 19 Jun, 2026 1 commit
    • Mahmoud Aglan's avatar
      Fix waiver duplicate key error + wrong fee calculation + member search name mismatch · 6c3db858
      Mahmoud Aglan authored
      TKT-71: Fixed duplicate entry for membership_number by reversing operation
      order in WaiverProcessor — source member's number is now NULLed BEFORE
      assigning it to target member, preventing unique constraint violation.
      
      TKT-71: Waiver fee now calculated from current pricing_configs value instead
      of the member's stored membership_value (old/historical price).
      
      TKT-69: Member search duplicate check now shows the entity type (spouse/child)
      and the parent member's number, clarifying which person is being shown.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      6c3db858
  6. 17 Jun, 2026 3 commits
    • Mahmoud Aglan's avatar
      Remove child fees from divorce transfer — fees only on new additions post-separation · 42f0ccc6
      Mahmoud Aglan authored
      Per bylaws clarification: children transferred during divorce separation move
      without fees. Age-based fees (15%/20%/25%/30%) apply only when the new
      independent member adds NEW children after the separation is complete.
      
      - Removed child fee calculation from boardApprove flow
      - Children selection is now transfer-only (no fee columns)
      - Updated fee preview JS to exclude child fees
      - Fee breakdown shows "transferred without fees" for moved children
      - Total = percentage_fee + form_fee (570) + annual_subscription only
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      42f0ccc6
    • Mahmoud Aglan's avatar
      Use current membership value from pricing_configs for divorce fee calculation · 94e1f426
      Mahmoud Aglan authored
      Instead of using the stored member.membership_value (which may be outdated),
      the divorce module now fetches the current active price from pricing_configs
      based on the member's branch and qualification at the time of the request.
      
      - Added DivorceFeeCalculator::getCurrentMembershipValue() method
      - Updated boardApprove, show, and create to use current pricing
      - Views now display "قيمة العضوية الحالية" with source indicator
      - Payment breakdown shows which source was used for the calculation
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      94e1f426
    • Mahmoud Aglan's avatar
      Fix divorce fee calculation: add both_working case, restore child <12 to 15%, unify 50% · e64c8865
      Mahmoud Aglan authored
      - Added 'both_working' case type detection via spouse.classification field
        (annual subscription only, 0% separation fee per bylaws)
      - Restored DIVORCE_CHILD_UNDER_12 percentage from 5% back to 15% per bylaws
      - Unified joined_after suggested percentage to 50% regardless of spouse order
      - Updated show.php to display both_working scenario in board panel
      - Updated architecture map with corrected business rules
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      e64c8865
  7. 13 Jun, 2026 9 commits
  8. 12 Jun, 2026 7 commits
    • Mahmoud Aglan's avatar
      Separate dev fee from subscription total: single 35 EGP added at invoice level · e381d4e9
      Mahmoud Aglan authored
      Business rule: مصاريف تنمية is a single flat 35 EGP fee per family per year,
      NOT per person, NOT included in discountable base, added AFTER all calculations.
      
      Changes:
      - Generator: total_amount = base - discount (dev fee stored separately, not in total)
      - Migration: subtracts dev_fee from total_amount for existing unpaid rows
      - payYear(): adds single dev fee on top of subscription+fine totals at payment
      - View: dev fee shown as separate line below the subscription subtotal, before
        the invoice grand total. Table columns simplified (no per-row dev fee column)
      
      Calculation order:
      1. Sum all family subscription bases
      2. Apply year discount (e.g. 50% for 2023/2024) to subscription amounts
      3. Add single 35 EGP dev fee as standalone charge at the end
      4. Add fines (on subscription only, not dev fee)
      = Final invoice total
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      e381d4e9
    • Mahmoud Aglan's avatar
      Implement unified family payment: single button pays entire year · 2d63c655
      Mahmoud Aglan authored
      - New payYear() controller action: processes all unpaid subscriptions for a
        member's financial year in one transaction with a single receipt
      - Route: POST /members/{memberId}/subscriptions/{year}/pay
      - View: replaced per-row pay buttons with a single "سداد اشتراك بالكامل" button
        at the bottom of each year section showing total due
      - Itemized table remains for transparency (shows each person's breakdown)
      - Added discount_amount column to the table display
      - FIFO still enforced: only oldest unpaid year's button is active
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      2d63c655
    • Mahmoud Aglan's avatar
      Fix discount calculation: مصاريف تنمية excluded from discountable base · cd3c2724
      Mahmoud Aglan authored
      Business rule: the 35 EGP development fee is a flat non-discountable surcharge
      added AFTER all discounts are applied. Discount applies only to base_amount.
      
      Correct formula: discount = base × pct, total = (base - discount) + dev_fee
      Previous (wrong): discount = (base + dev_fee) × pct
      
      For member 51 (2023/2024): 410×50% = 205 discount, total = 205 + 35 = 240
      (was incorrectly: 445×50% = 222.50, total = 222.50)
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      cd3c2724
    • Mahmoud Aglan's avatar
      Apply mandated 50% discount for 2023/2024 subscriptions · 2e65dd2e
      Mahmoud Aglan authored
      - Migration: retroactively applies 50% discount to all unpaid 2023/2024
        subscription rows (discount on gross total = base + dev fee)
      - SubscriptionGenerator: year-specific discount now applied on per-row total
        (base + dev_fee) and stored in discount_amount column, not just on base rate
      
      Business rule: SUBSCRIPTION_YEAR_ADJUSTMENT_2023 mandates 50% reduction on
      the final subscription total for all person types in that fiscal year.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      2e65dd2e
    • Mahmoud Aglan's avatar
      Fix cron job: delegate to OverdueFineApplicator, enforce consecutive-year drop rule · fc2128c5
      Mahmoud Aglan authored
      - OverdueFineJob now delegates fine calculation/distribution/drop to
        OverdueFineApplicator::run() which already does proportional distribution
      - Added reinstatement expiry call (12-month window enforcement)
      - Only marks subscriptions overdue when financial_year < current (grace period)
      - SubscriptionCalculator: drop check now verifies 5 CONSECUTIVE unpaid years
        instead of just checking if oldest overdue >= 5 (prevents false drops when
        a member paid middle years)
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      fc2128c5
    • Mahmoud Aglan's avatar
      Fix fine distribution: proportionally split across subscription rows per year · 5287a41a
      Mahmoud Aglan authored
      Previously the year-level fine (e.g. 897 EGP) was stamped identically on
      every row in the year, tripling the displayed fine. Now distributes
      proportionally based on each row's total_amount relative to the year total.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      5287a41a
    • Mahmoud Aglan's avatar
      Overhaul subscription display: FIFO enforcement, fine calculations, year grouping · 194da186
      Mahmoud Aglan authored
      - Add FIFO payment validation: must pay oldest year first before newer years
      - Add OverdueFineApplicator::applyForMember() for on-demand fine recalculation
      - Rewrite view with year-grouped sections, fine breakdown panels, totals
      - Add data migration to fix corrupted rows (paid_amount with no payment_id)
      - Show fine calculation details (percentage × base = amount, from rules engine)
      - Disable pay buttons for non-oldest years with Arabic tooltip
      - Summary cards showing total debt, fines, and years overdue
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      194da186
  9. 11 Jun, 2026 2 commits
  10. 10 Jun, 2026 1 commit
    • Mahmoud Aglan's avatar
      Fix membership fee calculations, subscription escalation, and over-25 children freeze · 0dd4abfc
      Mahmoud Aglan authored
      - TemporaryFeeCalculator: resolve current pricing_configs price instead of stored
        membership_value, matching SpouseFeeCalculator and ChildFeeCalculator behaviour
      - SubscriptionGenerator: add per-dependent dedup guards (spouse/child/temporary)
        to prevent duplicate subscription rows on repeated batch runs
      - Phase_89_001 migration: idempotent fix for subscription late-fine escalation rules
        (10/50/100/200/300% over 5 years, correct from the broken seed 100/200/300%)
      - Members show view: add warning banner listing active male children aged 25+ with
        direct freeze button, surfacing the existing freeze route that was never linked
      Co-Authored-By: 's avatarClaude Sonnet 4.6 <noreply@anthropic.com>
      0dd4abfc
  11. 08 Jun, 2026 1 commit
    • Mahmoud Aglan's avatar
      Fix children age display: compute dynamically from DOB, auto-separate at 25 · 110d7771
      Mahmoud Aglan authored
      - MemberController: SELECT TIMESTAMPDIFF for age_years/age_months in children query
      - children-table.php: compute age from DOB in view (fallback for static column)
      - show.php (member): same dynamic age + add 'separated' status translation
      - show.php (child): compute age from DOB dynamically, fix 25+ threshold check
      - AgeMonitorJob: add daily age recalculation for all children, change auto-freeze
        to auto-separate (status='separated', classification='separated')
      - AutoFreezeService: update processAutoFreeze() to separate instead of just freeze
      - DB fixes: corrected DOBs from NIDs, updated all ages, separated 3 children >= 25
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      110d7771
  12. 07 Jun, 2026 1 commit
    • Mahmoud Aglan's avatar
      Fix transfer fee calculation and remove dev fee from dependents · 7e3ac060
      Mahmoud Aglan authored
      - SeparationFeeCalculator: handle NULL qualification_id with fallback
        pricing lookup, use actual membership_type instead of hardcoded 'working'
      - SeparationFeeCalculator companion surcharge: use current pricing_configs
        instead of stale member.membership_value
      - SubscriptionGeneratorJob: set development_fee=0.00 for spouse/child/temp
        (matching SubscriptionGenerator service that was already fixed)
      - RetroactiveMembershipService: default dev fee to 0.00 for non-member types
      - RetroactiveWizardController: same default logic for missing form fields
      - Retroactive wizard JS: generate 0.00 dev fee for dependent subscriptions
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      7e3ac060
  13. 03 Jun, 2026 2 commits
    • Mahmoud Aglan's avatar
      Fix fee calculation: handle NULL qualification_id in pricing lookup · c8353344
      Mahmoud Aglan authored
      Members with qualification_id=NULL (old retroactive entries) were falling
      back to the stored membership_value (e.g. 119,800) instead of using the
      current pricing (150,000). Now when qualification is NULL, we look up the
      minimum price for that branch+type, which gives the correct base value.
      
      Also fixed 26 existing subscription records in live DB: removed dev fee
      (35.00) from spouse/child/temp subscriptions and recalculated totals.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      c8353344
    • Mahmoud Aglan's avatar
      Fix fee calculations: use current pricing, remove dev fee for dependents · 6873f19e
      Mahmoud Aglan authored
      Business rule fixes per ticket #68:
      
      1. Spouse/child fee percentages now use CURRENT membership value from
         pricing_configs (150k/225k/300k) instead of old stored value at creation
      
      2. Spouse annual subscription = 492 (SVC_ANNUAL_SPOUSE) WITHOUT dev fee
         Was incorrectly using child rate (222) + dev fee (35) = 257
      
      3. Child/temp annual subscription = 222 WITHOUT dev fee
         Was incorrectly adding 35 dev fee = 257
      
      4. Late marriage penalty: now calculated from LATER of (marriage_date,
         member_activated_at) to TODAY — not from marriage to member creation
      
      5. SubscriptionGenerator batch: spouse/child/temp subscriptions no longer
         include development_fee (set to 0.00). Only member keeps dev fee.
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      6873f19e
  14. 01 Jun, 2026 1 commit
    • Mahmoud Aglan's avatar
      Overhaul coach create form: full validation, NID auto-parse, edge cases · e1f1837a
      Mahmoud Aglan authored
      Frontend:
      - Client-side NID parser (no API call) — extracts DOB, gender, age, governorate
      - Shows green badge with parsed info (governorate, gender, age)
      - Red highlight + Arabic message on every invalid field
      - Validates: code, name, employment_type, payment_model (non-academy),
        academy_id (academy), NID length, email format, phone format
      - Hides payment/rate fields for academy coaches (not needed)
      - Shows note explaining academy coaches follow salary system
      - Gender/DOB auto-locked when NID is valid, manual otherwise
      - Age displayed under DOB field
      
      Backend:
      - Strips non-digits from NID input
      - Uppercase code automatically
      - Checks NID uniqueness against existing coaches
      - Validates email with filter_var
      - Academy coach requires academy_id selected
      - payment_model defaults to 'salary' for academy, 'per_session' fallback
      - max_groups minimum 1
      - Success message includes coach name
      Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
      e1f1837a