Part 5 · 2 chapters · ~12 min
Reconciliation and Breaks
Three-way reconciliation between the ledger, bank statements and processor reports, normalising formats, exact, fuzzy and many-to-one matching, break types and ageing, operations tooling, and continuous reconciliation.
11
Matching the ledger with the world
code
-- exact matching on reference and amount, recording the rule INSERT INTO recon_matches (ledger_posting_id, statement_line_id, rule) SELECT p.id, s.id, 'exact_ref_amount' FROM postings p JOIN journal_entries e ON e.id = p.entry_id JOIN statement_lines s ON s.reference = e.external_ref AND s.amount_minor = -p.amount_minor WHERE p.account_id = :bank_settlement_account AND s.value_date = :day AND NOT EXISTS (SELECT 1 FROM recon_matches m WHERE m.statement_line_id = s.id);
RECONCILIATION
match internal records against external statements; every mismatch is a break with an owner
swipe the figure sideways, or tap expand for full screen
1/5
three sources
Our ledger, the partner bank's statement (often MT940, CSV or an API) and processor settlement reports describe the same money from different sides.
three views of the same moneyours, the bank's, the processor's
12
Breaks, ageing and operations
| break type | typical cause | resolution |
|---|---|---|
| in bank, not in ledger | an inbound transfer we never credited (webhook lost) | credit the customer with a linked entry |
| in ledger, not in bank | an "unknown" payout that actually failed | reverse the posting, release or refund |
| amount differs | bank charged a fee we did not model | post the fee as an expense; update the fee model |
| timing | value date on the next day | auto-resolves next run; alert if it does not |
Breaks need an operations UI: filter by type and age, see both sides, apply a resolution that posts the correct entries with an audit note, and track break count and value as daily metrics. A growing break balance is an incident, even if no customer has complained yet.