Receivables and revenue
Receivables mirror payables, one control account to the other side. Every rule that holds for AP holds here: the entry, the sub-ledger obligation and the tax register line are one fact, written in one transaction.An AR invoice
Revenue is credited at the net, output VAT is a separate credit, and the AR control account is debited at the total.
Written alongside it: the AR obligation in
obligations (kind: "invoice", party_kind: "customer", keyed by client_code and the invoice’s own number), and - when VAT was charged - the direction: "output" register line in tax_entries against GL 2100.
The obligation’s amount_base is read back off the posted AR leg, not computed from a rate the caller may not have supplied.
That is what lets close_checks.ar_control_tie compare base against base.
Output VAT is the twin of input VAT, and the same pair of Close checksums reads it back: the register against the GL balance, and each line’s
tax_amount == ROUND(base_amount × rate).
The output-VAT per-line checksum is one GreatBook added that the source workbook never had.A receipt clears it
The receipt records a settlement row on its rail plus allocation rows saying which invoice it paid and how much of it.
Nothing about the invoice is edited: its
due is derived as total - allocated - advance_applied, adjusted by any credit or debit notes against it.
Which GL account a rail lands on is a property of the rail, and each rail reconciles against its own external source rather than against one global cash figure.
The nine rails and their accounts are on Settlement, rails and the AP/AR spine.
Customer advances and deposits
The non-cash application is a real journal entry with no rail movement behind it.
The Close list carries
Σ|advance pool| as a must-be-zero checksum precisely to catch an advance that was received and never applied.
A deposit held is supposed to be a non-zero liability, so it is reported as an explainable balance rather than a break.
Reporting a legitimate balance as a break trains people to ignore breaks, which is why the Close list has two kinds of row.
Adjustment notes change the derived due, never the invoice
The original invoice is never edited. A note references it, posts its own entry, and moves the derived outstanding.due = total - allocated - advance_applied + Σ(debit notes) - Σ(credit notes).
Two checks guard a note against what the original actually carried: its quantity gap may not exceed the original’s quantity, and its VAT rate must match the original’s.
Both are described in full on Adjustment notes, with the other two shapes on Adjustments, FX and consolidation.
Factoring: the distinction is derecognition
Selling a receivable and borrowing against one look identical in the bank account and are opposite in the ledger.
Getting this backwards either hides a liability or reports revenue-backed cash twice.
The Close list polices it as its own family, tying to the AR sub-ledger.
Intercompany invoices mirror
The two are expected to eliminate on consolidation.
Current consolidation reads keep each side as independent evidence, so a missing or mismatched half stays visible rather than being treated as a proved pair.
Accrued and deferred revenue
Both are moved by the Accountant’s adjusting entries at close, through the human-approved posting gate like every other entry - never by a direct write.
Where it is enforced
The test that would fail if it broke
backend/tests/test_settlement.py and backend/tests/test_close_checks.py drive the AR spine and its tie-outs against a real migrated Postgres.
backend/tests/test_loans.py covers the recourse versus non-recourse split, including that a non-recourse advance takes the invoice’s derived due to zero.
Related
- Payables and input VAT - the mirror family, in full
- Payments, rails and treasury - what a receipt’s rail row means
- Adjustment notes - the immutability feature that replaces editing a document
- Loans, treasury and factoring - the facility lifecycle