Skip to main content

Adjustment notes

Sub-ledger 6 was introduced by migration 0008; the current issuance authority is migration 0068_adjustment_note_proposals.

The idea

A supplier reduces a bill. A customer is credited for a return. A quantity was wrong. In a mutable system you edit the document. In GreatBook the original bill or invoice is never edited; a note references it and changes its derived due. This is the clearest place where immutability and derived balances pay off together. Because due is derived rather than stored, a note does not have to reach into the original and change a number. It adds a row, and the derivation accounts for it.
A CREDIT note lowers what is outstanding. A DEBIT note raises it.

Every new note is staged for a second person

AdjustmentNoteProposalService.propose derives the full note plan and stores it without posting. The proposal names its target obligation, credit/debit direction, source discrepancy basis and reference, exact decimal amounts, currency, proposer, reason, request key, and content fingerprint. Approval requires a different named person. The service normalizes identities before comparing them, so case, surrounding whitespace, or the email form cannot turn one person into two. Agent, service, placeholder, and same-person approvers are refused. The approval and note issue happen in one transaction. approved is therefore not a durable resting state: either the note, immutable journal, proposal attribution, and native links all commit, or the approval rolls back with the failed post. The original bill or invoice remains unchanged. Migration 0068 repeats the load-bearing rules in the database:
  • an approved or posted proposal must name a decider distinct from its proposer;
  • a staged proposal has no decider and every decided proposal does;
  • every proposal carries both a discrepancy basis and the specific source reference;
  • amounts are non-negative and total = amount_pretax + vat_amount;
  • historical notes are not backfilled with invented issuer or proposal evidence.
The engine service and migration are shipped, but this documentation cut did not find or exercise a public end-to-end adjustment-note proposal UI or API. The two-person issuance control is documented as an engine fact, not as a working production workflow claim.

The four shapes

Notice that every shape moves the VAT leg too. A credit note that reduces a bill also reduces the recoverable input VAT, so the VAT register and the 133 position stay tied. A note that touches a stock account also posts a matching stock movement, so the costing sub-ledger stays consistent with what was actually returned.

What a note may not do

Three checks constrain a note against the document it adjusts: Plus ADJ-CATALOG (the direction and target type are in the catalog), ADJ-ORPHAN (no note against a document that does not exist), ADJ-AMOUNT (amount matches quantity times price) and ADJ-STOCK-COUNTERLEG (a stock-touching note posted its movement). Two explainable balances report the net adjustment impact on AP and on AR.

Where it is enforced

The test that would fail if it broke

backend/tests/test_adjustments.py covers all four shapes and the constraint checks, and ties to the v12 worked data: a credit note moves a bill’s due to 2,805,000 with the original document frozen, and a note against an invoice produces a negative due of 100 where the customer overpaid. backend/tests/test_adjustment_note_authority.py covers distinct-human approval, exact plan re-derivation, stale state, database constraints, replay, atomicity, and deliberately null historical attribution.
A negative due is a legitimate outcome, not a bug: it is an overpayment. The check surface reports it rather than clamping it to zero, because clamping loses the fact that money is owed back.

Adjustment notes are not adjusting entries

Two similarly-named things, and they are different.

What goes wrong without it

Editing the original is the alternative, and it breaks three things at once: the hash chain, the audit trail, and any downstream artifact that already quoted the old figure. The softer failure is a “correction” booked as a fresh unrelated entry. It nets to the right total and nothing links it to what it corrected, so the supplier statement reconciliation shows one bill you disagree about and one credit you cannot explain.