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
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Please register or sign in to comment