1 · A journal has one entry: Dr liability.customer.va_1.EUR 100.00. Valid?
1 · A journal has one entry: Dr liability.customer.va_1.EUR 100.00. Valid?
assertBalanced requires at least two entries: one entry can never balance.It is the first of three checks, before positivity and before per-currency balance, because it is the cheapest and most fundamental.2 · Dr EUR 100.00 and Cr USDC 108.00 at the prevailing rate. Balanced?
2 · Dr EUR 100.00 and Cr USDC 108.00 at the prevailing rate. Balanced?
3 · What is the balance tolerance?
3 · What is the balance tolerance?
4 · A €10 debit hits asset.float.bank.EUR and liability.customer.va_1.EUR. Same direction of effect?
4 · A €10 debit hits asset.float.bank.EUR and liability.customer.va_1.EUR. Same direction of effect?
entrySign() reads the NORMAL_BALANCE table and returns +1n or -1n accordingly.That table exists in exactly one place. Duplicated, the copies eventually disagree and the ledger silently mis-signs balances.5 · An entry was posted with the wrong amount. How do you fix it?
5 · An entry was posted with the wrong amount. How do you fix it?
BEFORE UPDATE OR DELETE trigger enforces this at the database level: ledger_entry is append-only: post a reversing journal instead.The record of what happened stays separate from the record of what was meant to happen, which is the whole content of an audit trail.6 · A customer has €1,000 posted and a €250 active hold. What can they spend?
6 · A customer has €1,000 posted and a €250 active hold. What can they spend?
available = posted − reserved, where reserved is the sum of active holds only: released and captured holds do not count.Showing them posted would let them spend money already committed elsewhere.7 · A journal fails the overdraft check. What is in the database?
7 · A journal fails the overdraft check. What is in the database?
8 · Why can the overdraft floor never be positive?
8 · Why can the overdraft floor never be positive?
CHECK (overdraft_floor <= 0) enforces it.Customer accounts are 0. So is liability.in_transit: driving it negative would mean paying out money nobody put in.9 · Someone opens psql and runs an UPDATE that leaves a journal unbalanced. Does it commit?
9 · Someone opens psql and runs an UPDATE that leaves a journal unbalanced. Does it commit?
UPDATE outright. And even via valid inserts, the balance constraint trigger is DEFERRABLE INITIALLY DEFERRED: it runs at COMMIT, so a transaction leaving any journal unbalanced in any currency cannot commit.This is deliberate duplication with the application check. An invariant depending on every future developer using the right class is a convention, and conventions erode.10 · A 1.5% fee on €33.33 is €0.49995. Where does 0.00995 go?
10 · A 1.5% fee on €33.33 is €0.49995. Where does 0.00995 go?
revenue.rounding.EUR, as its own entry.divRound returns the rounded value under an explicit mode; divResidual returns the exact leftover so it can be posted. The journal balances and the residual becomes a queryable number.The plausible wrong answer is “it rounds away”, which is an unrecorded transfer of value, executing continuously, that nobody wrote down as policy.11 · Journal J and its reversal are both posted. What are the balances?
11 · Journal J and its reversal are both posted. What are the balances?
reverseEntries is an involution: reversing twice returns the original. Both are in the property suite.This is why compensation is safe by construction rather than by careful coding, which matters because the unwind path runs when something has already gone wrong.12 · Compensation runs forwards instead of backwards. Does the trial balance detect it?
12 · Compensation runs forwards instead of backwards. Does the trial balance detect it?