• DevPilot's avatar
    feat(accounting): posting chains + accrual reconciler · 1bc26cc1
    DevPilot authored
    Two gaps closed, both of which let real money go unrecorded.
    
    CHAINS — money moving between accounts across several entries
    
    The allocation wizard splits one amount inside one entry. It had no
    answer for the same money moving through a sequence of entries as
    separate actions happen: cash into a safe, settled to main, banked.
    Each hop must clear the account the previous one filled, and nothing
    enforced that.
    
    Concretely broken: every cash collection debited one global account
    regardless of which safe took it, then the settlement credited an
    unmapped `treasury:sub_cash` pointer that fell back to الصندوق بالدولار
    — an account no collection had ever touched. The sub-safes were also
    pointed at the USD/EUR cash boxes, so 972,791 EGP of pound takings sat
    in foreign-currency accounts.
    
    A chain step now declares where it leaves money and which earlier step
    it clears; the counter side is derived by re-resolving that step's rule
    against the same document, so a chain cannot be authored that fails to
    net to zero. Each safe owns a GL account, resolved through one service
    both the engine and the chain call. Guards refuse a hop whose two sides
    resolve to the same account, or whose type puts both on the same side.
    
    The historic misposting is corrected by a reviewable journal entry —
    not by rewriting posted history — and cannot be posted twice.
    
    ACCRUALS — obligations the ledger was never told about
    
    24 revenue streams were marked `needs_code`. 18 are now booked, finding
    1,316 claims worth 835,167 EGP that were nowhere in the accounts.
    
    Built as a scanner over the source tables rather than event dispatches
    in twelve modules: an event can be missed or misnamed — the coach
    payroll listener was bound to a name nothing dispatched — while a
    scanner is self-healing, retroactive and idempotent.
    
    Critically, collection here posts revenue directly, so accruing without
    releasing would double-count on every future payment. Each accrual is
    released when its document is paid, by mirroring the same rule.
    
    Outflows that had no entry at all are wired too: staff loans (an asset,
    not an expense), end of service, coach fees, goods receipt (against a
    new clearing account, so the invoice does not book inventory twice),
    depreciation per asset category, stock variances, asset disposal, and
    fine waivers.
    
    6 streams are deliberately left unbooked — no amount or no counterparty
    recorded, so any figure would be a guess. They are listed on screen
    with what each one needs.
    
    Verified against a scratch copy of production: trial balance nets to
    0.00, no unbalanced entries, no postings to header accounts, every pass
    idempotent.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    1bc26cc1
Name
Last commit
Last update
..
Controllers Loading commit data...
Models Loading commit data...
Services Loading commit data...
Views Loading commit data...
AccountCodes.php Loading commit data...
JournalServiceAdapter.php Loading commit data...
Routes.php Loading commit data...
bootstrap.php Loading commit data...