Skip to main content

Adjustments, FX and consolidation

Nothing in GreatBook is corrected by editing it. Every correction is a new entry - a note, an adjusting entry, or a reversal - and this page is what each of them posts.

Adjustment notes: four shapes

A credit or debit note references the original bill or invoice and changes its derived outstanding. The original document’s own figures stay frozen. due = total - allocated - advance_applied + Σ(debit notes) - Σ(credit notes). Notice that a note reverses the VAT leg too. A credit note that reduces a payable without reducing the input VAT claimed on it leaves the business declaring VAT on goods it no longer bought. Two guards check the note against what the original actually carried: Both source figures are auto-derived when not supplied - the quantity from the bill’s line detail, the rate from the document’s VAT register entry - and either may legitimately be absent, because not every document type carries both. A note that touches a stock account also posts its matching stock movement, the same rule as an inventory adjustment.

An accountant’s adjusting entry

The Accountant proposes only the eight typed adjusting-entry kinds, including accruals, releases, revaluation, depreciation, CIT and period-end carry operations. Business reversal is a separate named-human HTTP boundary and is not an Accountant adjustment kind. It never posts one: its proposal is staged at the Bookkeeper’s human posting gate, and nothing in the accountant package imports the ledger writer or opens a writable session. Before a proposal can reach that gate it is gate-checked, and the check refuses anything that would weaken the guarantees the document path makes:
A line’s currency - and an explicit exchange_rate when the proposer states one - is carried through, not normalised away. Dropping a currency here would re-create the par-posting defect on this path, because the engine reads an absent currency as “already in the base currency”. A carried currency is passed verbatim, never re-cased, because this validator is not told the org’s base currency and upper-casing blind to the base would make the engine treat an org’s own currency as foreign to itself.

FX revaluation at close

An open foreign-currency item is restated at the closing or settlement rate. difference = base × (closing_rate - booked_rate) The unrealized / realized distinction is the whole point: an unsettled item’s movement sits in equity, and only settlement moves it through the income statement.
One FX case the book has no machinery for, stated. A foreign obligation that has been partly settled has its remainder converted at the rate it was booked at. A settlement made at a different rate is a realised FX difference, and there is no automatic entry for it - so the control-account tie will show the difference rather than hide it. Reporting it is the honest failure; the machinery to book it is on the roadmap.

A reversal is typed compensation, not an eraser

The business-reversal service posts a new entry that swaps every line’s debit and credit on an explicitly reviewed date in an open period. It identifies and validates the native source object, then compensates its register state and GL effect in one transaction. A reader of the ledger therefore sees the mistake and the correction, in order, with the reason attached. That is strictly more information than an edit, which shows only the end state.

Intercompany and equity

Intercompany construction can create the two expected halves, but the current consolidation read treats them as independent evidence rather than claiming a proved pair. A missing or mismatched side remains visible for review; intercompany is not a supported typed-reversal family.

Where it is enforced

The typed reversal boundary is available through authenticated HTTP, the web review flow and typed MCP staging. Transactions, Close and posted Adjustments can stage an eligible event from server-read journal facts; the shared staged-workspace commit path reaches the canonical reversal service only after exact validation and confirmation. The public MCP ingress is current on registry 1.14.1, there is no dedicated reversal App, provider behavior is unexercised, and the Accountant cannot invoke reversal as an adjustment kind.

The test that would fail if it broke

backend/tests/test_adjustments.py covers the four shapes and both guards. langgraph_chat/agents/accountant/tests/test_posting_gate.py asserts statically that the accountant package has no write path, and that after routing a proposal the run’s next node is the gate and nothing has been written.