Appearance
Delegation and Interaction Contracts
This document explains how complex operations cross module boundaries. The owning module controls the user-facing document and final status. A delegated module performs a bounded responsibility and returns IDs, codes, balances, or account data to the owner. Unless stated otherwise, delegated commands use the caller's unit-of-work context so one failure prevents the whole operation from committing.
Contract vocabulary
| Term | Meaning |
|---|---|
| Owner | Module that owns the source document, user action, and final state. |
| Delegate | Module called through an internal/public API to perform a specialized effect. |
| Preflight | Read/check performed before any final commit, such as account, period, stock, or module availability. |
| Linked document | Separate auditable document created by a delegate, such as Stock Out or Payment In. |
| Commit boundary | Point at which all owner/delegate changes become durable together. |
| Compensation | Reversal/unpost behavior used after a successful prior commit. It is not a substitute for atomic rollback during initial Post. |
Sales Invoice Post
Owner: Sales
Delegates: General Settings, Inventory, Accounting, Finance
Primary result: Posted Sales Invoice with commercial journal, optional Stock Out, due balances, reconciliation, and credit effects.
| Step | Responsible module | Delegated operation | Returned/changed data | Failure effect |
|---|---|---|---|---|
| 1 | Sales | Load Draft invoice with customer, lines, taxes, balances, salesperson, and source data | Complete aggregate | Missing/non-Draft stops. |
| 2 | Sales/Business Owner context | Check Accounting, Inventory, and Finance module availability | Capability flags | Required inaccessible module stops or changes only explicitly supported optional behavior. |
| 3 | General Settings | Resolve current company currency, tax definitions, period, and configuration | Currency/tax/account data | Missing tax/account/closed period stops. |
| 4 | Inventory | Resolve revenue accounts per item | Item Revenue -> Category Revenue -> Inventory Settings Revenue | Any line without account stops. |
| 5 | Sales | Build commercial journal | Receivable debit, discount debit, revenue/tax credits/debits | Imbalance or missing customer accounts stops. |
| 6 | Accounting | AddJournal with SaveToContext=false | Journal ID/code | Journal validation failure stops. |
| 7 | Inventory | AddPostedStockOut for non-service lines | Stock Out ID/code, inventory journal ID/code | Insufficient stock, tracking, UOM, warehouse, COGS, or journal failure stops. |
| 8 | Finance | Auto-reconcile eligible Payment In availability | Reconciliation results/new due balances | Invalid linked balance stops according to handler behavior. |
| 9 | Sales | Apply invoice to customer and salesperson credit; set linked IDs; mark Posted | Updated credit, balances, status | Credit/business-rule failure stops. |
| 10 | Shared unit of work | Commit all changes | Durable invoice, journals, stock, balances, audit | No partial owner/delegate result may remain. |
Ownership after Post
- Sales owns invoice totals, customer/salesperson credit, due balances, returns, and commercial reporting.
- Inventory owns Stock Out, warehouse quantity/cost layers, COGS journal, and item reports.
- Accounting owns journal validity and GL reporting but retains source links to Sales/Inventory.
- Finance owns later Payment In and reconciliation records.
- General Settings owns tax/currency/period master data, not the posted snapshot.
Sales Return Post
Sales validates the source invoice and remaining returnable quantity, resolves customer Receivable and Sales Return accounts, asks General Settings for tax definitions, creates the commercial reversal journal through Accounting, delegates Stock In to Inventory for storable lines, restores customer/salesperson credit, updates the source invoice return status, links all generated documents, and commits once. Inventory returns Stock In/journal IDs; Sales does not directly mutate stock tables.
Purchase Invoice Post
Owner: Purchase
Delegates: General Settings, Inventory, Accounting
Primary result: Posted Purchase Invoice with payable journal, optional Landed Cost, Stock In, vendor due balances, and inventory valuation.
| Step | Responsible module | Delegated operation | Returned/changed data |
|---|---|---|---|
| 1 | Purchase | Load Draft invoice/vendor/lines/payment term and validate source/order quantities | Valid purchase aggregate |
| 2 | Purchase | Resolve Vendor Payable: vendor then vendor category | Payable account |
| 3 | General Settings | Resolve tax, currency, period, and sequence | Posting context |
| 4 | Inventory | Post/validate Landed Cost for the purchase invoice | Expense, VAT, vendor-account groups and allocated cost by item variant |
| 5 | Inventory | Resolve service expense/cost center: item then item category | Expense/cost-center data |
| 6 | Purchase | Build purchase journal | Transit/service/tax/landed debits and vendor credits |
| 7 | Accounting | Add purchase journal without independent commit | Journal ID/code |
| 8 | Inventory | Add and post Stock In for non-service lines, applying landed allocations | Stock In and inventory journal IDs/codes; quantity and costs |
| 9 | Purchase | Create vendor due schedule, link records, mark Posted | Payable/subledger state |
| 10 | Shared unit of work | Commit | Invoice, landed cost, journals, stock, vendor balances become durable together |
Purchase owns payable/source/order status. Inventory owns stock and landed allocation records. Accounting owns balanced journals. A failure in any delegate prevents Purchase from reaching Posted.
Purchase Return Post
Purchase validates the posted source and remaining quantities, resolves Vendor Payable, asks Inventory for current return stock cost and landed-cost expense data, delegates a posted Stock Out, builds the purchase-return commercial journal through Accounting, updates source return status, links Stock Out/journals, and commits atomically. Inventory rejects insufficient stock or invalid tracking; Purchase rejects over-return.
Payment In Post and Reconciliation
Owner: Finance
Delegates: Sales, Fixed Assets, Accounting, General Settings
Primary result: Posted cash/bank receipt, journal, subledger transaction, and optional reconciliations.
- Finance validates Draft, payment hub/method, amount, currency/rate, party/details, commission/tax, and period.
- Sales supplies/owns Customer Receivable hierarchy and later customer invoice/opening-balance reconciliation effects.
- Purchase/vendor data supplies Vendor Payable hierarchy where the receipt detail targets a vendor.
- Finance builds the cash journal and asks Accounting to add it.
- Finance creates Bank or Treasury transaction header/details and sequence.
- Finance auto-reconciles eligible customer payment detail; Sales/Fixed Assets/opening balances are updated through their owning contracts.
- Finance marks Payment In Posted and commits journal, cash subledger, payment availability, and target balances together.
Payment In Unpost delegation
- Finance preloads Payment, bank/treasury movements, reconciliations, and linked targets.
- General Settings confirms source and linked periods are open.
- Sales validates and undoes Sales Invoice, Customer Opening Balance, customer credit, salesperson credit, and payment-intent effects.
- Fixed Assets validates and restores Asset Sales Invoice balances.
- Accounting validates source journal identity and creates the reversal.
- Finance restores Payment detail availability, soft-deletes Bank/Treasury movements, changes source state, and commits all reversals together.
Any missing/changed/closed linked target blocks unpost; Finance must not reverse cash while leaving a customer/asset balance applied.
Payment Out Post and Unpost
Finance owns Payment Out, resolves party account through Sales/Purchase master data or explicit Other GL, validates source Bank/Treasury balance, delegates journal creation to Accounting, creates the Bank/Treasury subledger movement, marks Posted, and commits. Unpost validates the open period and journal, asks Accounting for a reversal, soft-deletes the subledger movements, restores any linked balance effects, and commits once.
Fund Transfer Post
Finance owns one Fund Transfer but delegates internally to two finance transaction legs:
- Post source Payment Out: Debit Fund Transfer Contra, Credit source Bank/Treasury.
- Post target Payment In: Debit target Bank/Treasury, Credit Fund Transfer Contra.
- Link both legs to the Fund Transfer and mark it Posted.
- Commit both legs together. A failure in the target leg must roll back the source leg.
Stock Transfer Post
Owner: Inventory Stock Transfer
Delegates: Inventory Stock Out, Inventory Stock In, Accounting
- Resolve transfer cost from issued-warehouse stock using configured Weighted Average or Last Purchase method.
- Delegate posted Stock Out with
SaveToContext=false; it reduces source stock and creates the outbound Goods-in-Transit journal. - Delegate posted Stock In with
SaveToContext=false; it increases target stock and creates the inbound Goods-in-Transit journal. - Store both stock IDs/codes/journal links on the transfer.
- Update linked Transfer Request to Partially Transferred or Done.
- Mark transfer Posted and commit the two stock legs, two journals, request status, and transfer together.
Inventory Reconciliation Post
Inventory Count owns the physical/system comparison. For shortages it delegates a posted Stock Out; for surpluses it delegates a posted Stock In. Those delegates own stock and journal effects. The count stores their IDs/codes, releases the warehouse count lock, records reconciliation date, marks Posted, and commits. Zero-variance rows create no delegate document.
POS Session Post
Owner: Sales POS Session
Delegates: Inventory and Finance
The session aggregates POS invoices/returns and delegates session Stock Out, Stock In, Payment In, and Payment Out using the selected branch/treasury context. Only after all four categories succeed does Sales mark the POS Session Posted and commit. This is a batch orchestration boundary; missing accounts, stock, cash, or one invalid child document blocks the session close.
Fixed Asset Purchase/Sale/Return
- Fixed Assets owns asset status, cost, accumulated depreciation, net book value, location, and lifecycle.
- Purchase supplies Vendor Payable resolution for asset purchase/return.
- Sales supplies Customer Receivable resolution and payment reconciliation for asset sale/return.
- General Settings supplies period, currency, tax, and sequence.
- Accounting validates and stores the asset journal.
- Finance later owns Payment In/Out; Fixed Assets owns how reconciliations change asset invoice balances.
The owner marks an asset transaction Posted only after all delegated account, tax, journal, and balance operations succeed.
ETA/ZATCA submission
Sales/General Settings owns the submission request and history. It reads already-posted Sales documents and master-data official codes, delegates token/signature/authority communication to the integration service, stores authority request/response/status, and does not call Sales/Inventory/Accounting Post again. Retry uses stored external identity to prevent duplicate authority documents.
Year close
General Settings owns financial-year state and delegates financial calculation/journal creation to Accounting. Accounting requires the current-year earnings account and produces the closing effect. The year is not closed if Accounting is unavailable or the closing journal fails.
Failure and ownership rules
- The owner is responsible for preflight orchestration and final status.
- The delegate is responsible for validating its own invariants even if the owner already checked them.
- Returned IDs/codes must be stored by the owner for audit and unpost.
SaveToContext=falsemeans the delegate must not independently commit; the owner commits the shared operation.- After a successful commit, correction uses explicit unpost/reversal/return workflows, not deletion or direct table edits.
- External integration failure after ERP commit changes integration status, not ERP Posted state, unless an approved compensation workflow says otherwise.