Skip to main content

The seven sub-ledgers

A sub-ledger is where the operational detail of one business domain lives: the individual bills, the stock movements, the payroll lines, the facility drawdowns. The GL holds the control totals. GreatBook has seven of them, and the rule that makes them a system rather than seven systems is that every one posts through the same engine. Nothing writes the GL with raw SQL.

The seven

Three rules every sub-ledger obeys

1. It posts through LedgerService.post_entry. Each operation records its own sub-ledger row and posts its journal entry through the single writer, in the caller’s transaction. The row and the entry are one atomic fact. 2. Its balances are derived, never stored. A bill’s outstanding amount, a payment’s unapplied balance, the AP and AR control totals, the advance pools, the rail balances, stock on hand, the weighted-average unit cost, WIP, the facility balance, the VAT position: all computed from the rows plus the GL. There is no balance column anywhere to drift. See Nothing stored that can be derived. 3. A control-account leg and its sub-ledger row are one fact. Every path that credits AP control 2000 or debits AR control 1100 goes through settlement.record_obligation, the single obligation writer. A control credit with no sub-ledger row behind it is a balance the book cannot explain, and the Close checksums catch it.

How the families interlock

The sub-ledgers are not seven silos. They share the control accounts, so they reconcile as a whole. Four of those edges are worth naming, because each one is a checksum:
  • Costing labour is payroll labour. A production-direct employee’s employer cost flows to 1210 (VAS 154), which is exactly the labour the WIP balance consumes. Sub-ledger 3 feeds sub-ledger 2.
  • Payroll and factoring settle on rails. Net pay, PIT and social insurance bills, and factoring buyouts, all move through sub-ledger 1’s rails, so they reconcile against bank evidence like any other payment.
  • Factoring reads the obligation. Non-recourse factoring derecognises the receivable, so obligation.due goes to zero; recourse factoring is secured borrowing and the receivable stays on the books. The Close checksums police that distinction.
  • Notes move due, and move VAT. An adjustment note changes what is owed and the 133 or 3331 position, without editing the document it adjusts.

Ties to the dong

Sub-ledgers 1 to 6 reproduce worked v12 workbook data to the dong. That is the fidelity bar: not “the logic looks right” but “the same inputs produce the same numbers the workbook produced”.
Sub-ledger 7 is different, and says so. Intercompany, FX revaluation and equity are proposed only in the v12 workbook: the flows are specified but never worked, so there is no data to tie to. It is built from the schema’s field, enum and formula specification instead, which is what the workbook intends but never executed. That distinction is recorded rather than glossed, because “ties to worked data” and “built from spec” are different claims.

Where they are enforced

All seven live in backend/app/logics/finance/, one module each, plus derive.py for the balances and close_checks.py for the reconciliation surface.
Each module’s docstring carries its account legs as a table. Those tables are the source of the per-page tables in this section, so the documentation and the code cannot drift without the docstring drifting first.