📒 Debt postings: written in the transaction script
Updated
For the analyst: obligations, payments and the general ledger.
The rule#
Which registers a document moves, with which dimensions, analytics, resources
and signs, is read in its transaction script in full. Every script that moves
Receivable / ReceivableByBasis or Payable / PayableByBasis builds its
RegisterMovementSpecs itself. A service may compute the NUMBERS (amount, tax,
functional amount via ISalesFx / IPurchaseFx, the basis document) — never
the shape of the movement. Similar movements in different documents are
expected, not copy-paste.
The two registers per side#
Each side keeps the debt twice:
| Register | Slice | Used by |
|---|---|---|
Receivable |
analytics Customer, SalesContract (both required); resources Amount, AmountFunc |
credit limit, FX revaluation, the GL |
ReceivableByBasis |
physical dimensions Customer, SalesContract, Basis (Guid); resource Amount |
allocation to orders, the 1C reconciliation |
Payable |
analytics Supplier, optional LegalEntity, optional SupplierContract; Amount, AmountFunc |
supplier balance, FX, the GL |
PayableByBasis |
physical Supplier, Basis; optional analytic SupplierContract; Amount |
allocation to orders and receipts |
The invariant is Receivable.Amount = Σ ReceivableByBasis.Amount per customer
× contract, and the same for payables per supplier. A script that writes only
the main register — or writes the companion with a different contract — breaks
the invariant without any error: both registers accept the movement, and the
difference shows only when someone compares the two totals. So every script
writes both specs next to each other, from the same amount and contract.
The two sides differ on purpose:
- Receivable always writes both analytics.
CustomerandSalesContractare required onReceivable; an empty contract is written asGuid.Empty. - Payable skips an empty optional analytic. An empty
LegalEntityorSupplierContractis not written at all, so the debt stays on the supplier without that slice.PayableByBasisskips an emptySupplierContractthe same way. - The companions have no functional resource.
AmountFuncexists only on the main register.
The shape#
// SalesReceivableTx
transactions.Add(new RegisterMovementSpec("Receivable")
.An(Analytics.Receivable.Customer, document.Customer)
.An(Analytics.Receivable.SalesContract, document.Contract)
.Res("Amount", amount)
.Res("AmountFunc", GetService<ISalesFx>().ToFunctional(amount, document.ExchangeRate)));
// Debt anchors on the order; an invoice without one anchors on itself.
var basis = document.SourceOrder != Guid.Empty ? document.SourceOrder : document.MetaId;
transactions.Add(new RegisterMovementSpec("ReceivableByBasis")
.Dim("Customer", document.Customer)
.Dim("SalesContract", document.Contract)
.Dim("Basis", basis)
.Res("Amount", amount));
| Movement | Writes | Used by |
|---|---|---|
| raise (+) / settle (−) | main register Amount + AmountFunc, companion Amount; empty basis = advance |
invoices, returns, notes, payments, refunds, debt offset / opening / write-off |
| revaluation | main register AmountFunc only |
ReceivableRevaluationTx, PayableRevaluationTx |
| basis transfer | companion only: −amount on the old basis, +amount on the new; with a target customer/supplier or contract (ToCustomer/ToSalesContract, ToSupplier/ToSupplierContract) also −/+ in Receivable/Payable (one currency, AmountFunc = amount), and the bases may be empty |
DebtTransfer*Tx |
Scripts that move debt#
| Model | Scripts |
|---|---|
| Sales | SalesReceivableTx, SalesReturnReceivableTx, SalesCreditNoteReceivableTx, SalesDebitNoteReceivableTx, CustomerPaymentTx, CustomerRefundTx, ReceivableRevaluationTx |
| Purchasing | GoodsReceiptPayableTx, PurchaseReturnPayableTx, PurchaseCreditNotePayableTx, SupplierInvoicePayableTx, VendorPaymentTx, VendorRefundTx, PayableRevaluationTx, TaxCalculationPayableSeamEventHandler (input VAT, posts through IRegisterMovementService) |
| Settlement | DebtOpening*Tx, DebtOffset*Tx, DebtWriteOff*Tx, DebtTransfer*Tx |
Traps#
- Write the companion in the same script, from the same variables. A new
document that moves debt and forgets
…ByBasisstill posts; nothing stops it except review and the allocation tests. - Pass the same contract you use everywhere else. A payment posted on another contract than the invoice leaves the invoice's slice open — in both registers.
- The physical dimensions of a companion need the full set. A balance read
with only some of
Customer,SalesContract,Basisreturns 0; sum the rows ofQueryBalancesAsyncinstead.
What's not there yet#
- No gate checks that a script writing
Receivablealso writesReceivableByBasis(same for payables). - Payable revaluation carries no contract:
PayableRevaluationlines revalue the supplier × legal entity slice.
Same topic for other readers: Analyst · User
Where next#
- Obligations, payments and the ledger for analysts
- Register totals layouts —
ReceivableByBasis,PayableByBasisand the per-contract layouts - Line tax:
LineTax