• Mahmoud Aglan's avatar
    feat(settlements): settle a member's account instead of editing around it · e45bd6d7
    Mahmoud Aglan authored
    A club that ran on paper for years does not arrive in the system as a
    clean ledger. On the first academy live, 33 players were registered with
    an invoice raised and the "pay now" toggle left off — 25 of them in two
    data-entry evenings — and 27 of those are now carrying an unpaid
    registration month plus an unpaid September renewal. Nine paid for the
    federation card in instalments typed into free-text lines. Eight invoices
    were issued at zero because no price existed yet. Three people exist
    twice. None of that is a bug in one screen; it is a whole class of file
    that reality got ahead of.
    
    The desk had four tools that each did a slice: collect a payment, correct
    one invoice's amount, back-fill missing invoices, register someone who
    started months ago. None of them answers the question an operator has in
    front of a parent — this file is wrong in several ways at once, what do
    we do about all of it — so corrections were made wherever a screen
    allowed them and the ledger drifted further.
    
    SettlementService applies a reviewed set of corrections as one
    transaction and one record: money taken and never entered (on the day it
    was actually taken), a month closed for less than it was billed because
    the player joined halfway through, a month dropped entirely, a month
    nobody billed, a card or kit sold outside the system, a free-text line
    linked to the product it was really paying for, an agreed instalment
    plan, a payment sitting on the wrong month, and an overpayment held as
    wallet credit. Money moves through PaymentService so the ledger, the
    balance and the receipt all happen; stock through InventoryService; a
    waiver is written as the admin_override the roster already knows how to
    explain, leaving subtotal_amount alone so "650 of 900, discounted" still
    reads. Nothing calls auth() or session(): actor, branch and amounts are
    parameters.
    
    AccountAnomalyScanner finds the files rather than waiting for an argument
    at the desk — seven cases, worst first, each with the sentence that says
    what to check. SettlementWorklist lists them with a CSV export;
    AccountSettlementWizard puts one account on a page, proposes the
    corrections that fit what it found, shows exactly what will be collected,
    waived and billed, and demands a written reason before it writes
    anything.
    
    Both screens are gated on a new settlements.manage permission — waiving a
    month is the academy's call, and an owner should not need a platform
    administrator to make it — delivered by migration as well as seeder,
    since db:seed only runs on a first deploy.
    
    Two things the tests caught rather than production: Postgres refuses FOR
    UPDATE on an aggregate, so numbering settlements from max(id) would have
    rolled back a whole settlement the operator had already confirmed; and
    payment_plans_status_check has no 'partial', so a part-paid plan is
    active with the count saying how far along it is.
    
    Verified against a restored oc-sport tenant: 20 settlement cases and 7
    render/permission cases pass, including cross-participant access, a
    future date, an oversized payment, and a failing second action rolling
    the first one back. Full suite 298 tests, no failures.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    e45bd6d7
Name
Last commit
Last update
..
AccountAnomalyScanner.php Loading commit data...
CashSessionService.php Loading commit data...
ExpenseService.php Loading commit data...
InvoiceService.php Loading commit data...
LedgerAccountResolver.php Loading commit data...
ParticipantBillingService.php Loading commit data...
PaymentProofService.php Loading commit data...
PaymentService.php Loading commit data...
PaymobService.php Loading commit data...
RefundService.php Loading commit data...
SettlementService.php Loading commit data...
WalletService.php Loading commit data...