Billing·Advanced·12 min·Updated 2026-10-05
From order to invoice: where billing actually starts, and why it silently does not
Between an activated order and a posted invoice sit an orchestrator, a context definition, four org settings and a set of order-line fields nobody tells you are required. Each one fails without an error.
The shape of the chain
An activated order does not become an invoice directly. It is decomposed into fulfilment, it produces billing schedules, those schedules are picked up by an invoice run, and the resulting draft invoice is posted into an accounting period. Five handovers, and the failure modes are concentrated at the first and the last.
Salesforce's orchestration layer for the fulfilment side is the Dynamic Revenue Orchestrator, which decomposes a sales transaction into fulfilment lines and maintains fulfilment assets and their state periods. It has its own object family and its own invocable actions, and it is worth reading the official overview before designing around it.
Four prerequisites, none of which reports anything
This is the section worth the whole article. On one implementation, orders activated cleanly and no billing schedule was ever created. There was no error, no flow failure email, and nothing in the invoice run to look at — the run simply found nothing to invoice.
What had to be true, all of it invisible until the first invoice fails:
- The order save behaviour release update. Found under the archived tab of release updates, which is not where anyone looks for something still required.
- The billing context definition extended and activated, then declared in billing settings as a context service mapping. Extending it is not enough; activating it is not enough; it has to be declared.
- The correct intra-context mapping. The schedule-group mapping, not the order mapping. The two are adjacent in the picklist and one of them produces no schedules and no error.
- The billing defaults — legal entity, billing treatment, tax treatment. Missing defaults do not block activation; they block output.
The order line fields that are required without being required
Once the org is configured, the data has its own version of the same trap. A billing
profile produces nothing without a bill-to contact, a payment term, and a delivery address.
And the order line needs PeriodBoundary,
PeriodBoundaryStartMonth, PeriodBoundaryDay and
BillingFrequency2.
None of those four is surfaced as required. The record saves. The order activates. Nothing bills.
At the scale of one test order this is an afternoon. At the scale of a migration it is the whole project: an import of thousands of accounts will pass every validation and bill nothing at all. Prove the chain on a single record before loading the rest — all the way to a posted invoice, not just to an activated order.
The payment term is on the order
The payment term that determines the due date lives on the order, not on the billing profile. We looked for it on the profile first, because that is where the documentation suggested it would be, and the field was simply not on the object when checked by describe.
The general lesson applies well beyond this field. On these newer objects, the public documentation runs behind the schema. Check with a describe before concluding that something needs a custom field — and equally, before concluding that a documented field exists.
The flow criterion that never fired
On one org the handover from order to billing schedule went through a flow. The flow
filtered on Status = "Activated". The org runs in French, where that value
renders as Activé. The criterion never matched. The flow never fired. No schedule
was ever created.
Nothing failed — and that is the point. A flow that does not act is indistinguishable from a flow that acted and found nothing to do. There is no error to find.
Generalise it: any flow criterion comparing against a displayed value is suspect on a localised org. Compare against API names.
What the invoice does not carry
Once invoices are produced, the next surprise is how little they hold. The invoice has
no country field. Its billing-profile reference is null.
And Invoice.BillingAccountId points at Account, not at the
BillingAccount object — a naming trap that sends ledger rules looking in entirely the wrong
place.
On the line, InvoiceLine.TaxCode is always empty, posted or
not. The real tax code lives on InvoiceLineTax, or has to be brought across by
a formula from the treatment.
The consequence for design: anything you intend to route, report or reconcile on has to be brought down onto the line deliberately, usually as a formula field. Discovering this after writing forty ledger assignment rules is expensive, because those rules cannot be edited once their criteria are inserted.
Posting
Invoice.Status is not writeable. Posting goes through the standard post
action rather than an update, and the accounting period has to be open — remembering that a
period cannot exceed one year, so “one open period” means one per financial
year, not one per month.
What a working chain looks like
On one implementation the end-to-end chain was proved with a single invoice carrying the correct VAT: a net amount, twenty percent tax, and a gross total that matched to the cent, with ledger entries attributed by a named assignment rule. That one invoice validated the context mapping, the billing defaults, the order line fields, the tax chain and the ledger routing simultaneously.
It is worth building that single proof deliberately and early. It is the only artefact that tests all five handovers at once.
Salesforce components
Select one to open its detail.
What this is based on
- Every prerequisite listed here was discovered by failure on a real implementation, at API version 67, and fixed. The behaviours are reported as observed, not as documented.
- The orchestration layer is documented by Salesforce; the failure modes around it are not.