feat(accounting): posting chains + accrual reconciler
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:
Claude Opus 5 <noreply@anthropic.com>
Showing
Please register or sign in to comment