ZuloOne
English
Sign in
Menu

📒 Debt postings: written in the transaction script

Updated

← Developer wiki

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. Customer and SalesContract are required on Receivable; an empty contract is written as Guid.Empty.
  • Payable skips an empty optional analytic. An empty LegalEntity or SupplierContract is not written at all, so the debt stays on the supplier without that slice. PayableByBasis skips an empty SupplierContract the same way.
  • The companions have no functional resource. AmountFunc exists 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 …ByBasis still 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, Basis returns 0; sum the rows of QueryBalancesAsync instead.

What's not there yet#

  • No gate checks that a script writing Receivable also writes ReceivableByBasis (same for payables).
  • Payable revaluation carries no contract: PayableRevaluation lines revalue the supplier × legal entity slice.

Same topic for other readers: Analyst · User

Where next#

Was this page helpful?