Split Billing — five native mechanisms
Billerang has five orthogonal native split levers built into the invoicing engine. None of them requires a validation script (validation scripts can only reject invoices, not redirect RTs). They are applied in this order:
| # | Mechanism | Where it lives | When it splits |
|---|---|---|---|
| 1. Per-IL split | billSeparately flag on Subscription / ServiceInstance / ChargeTemplate / ChargeInstance | Per entity | Auto-detected per BR scope; flagged units land in their own IL set inside the same invoice. |
| 2. Per-IL invoice type | AccountingArticle.invoiceType + AccountingArticle.invoiceTypeEl | Per article | Each IL routes to the article's InvoiceType. EL evaluated against the IL. |
| 3. Per-BC EL invoice type | BillingCycle.invoiceTypeEl | Per cycle | EL evaluated with billingRun + billingAccount + invoice as parameters → resolves an InvoiceType code per BR/BA combination. |
| 4. Per-BC static invoice type | BillingCycle.invoiceType | Per cycle | Static fallback when EL is empty or returns null. |
| 5. Per-BC split by payment method | BillingCycle.splitPerPaymentMethod | Per cycle | One invoice per payment method on the BA (e.g. one DIRECT_DEBIT invoice + one CHECK invoice for the same BA). |
Per-run default invoice type
A sixth knob lives directly on the BR: defaultInvoiceTypeCode (maps to BillingRun.invoiceType).
This is a per-run default, not a hard override. The article-level routing in mechanism #2 is evaluated before the BR-level fallback — articles can still produce multiple invoice types within the same run.
To force a single invoice type for the whole run, you would need to clear article-level routing (out of scope of the bill-run API).
Precedence chain in determineInvoiceType
For each IL, the engine picks an InvoiceType in this order:
BillingRun.invoiceType(thedefaultInvoiceTypeCodefrom the request)- Default prepaid / deposit / draft (special cases)
BillingCycle.invoiceTypeEl(per-BC EL)BillingCycle.invoiceType(per-BC static)- Default commercial invoice type
At the IL level (during aggregation), if AccountingArticle.invoiceTypeEl is set on the article assigned to the IL, that EL wins — short-circuiting the chain.
Worked examples
Example 1 — split by payment method
A BA has two payment methods: DIRECT_DEBIT (for the recurring fee) and CHECK (for usage charges). With BillingCycle.splitPerPaymentMethod=true, a single bill run produces two invoices:
Invoice INV_001234 (DIRECT_DEBIT, recurring fee) €99.99
Invoice INV_001235 (CHECK, usage charges) €34.50
Both invoices reference the same BA and bill run.
Example 2 — split by article invoice type
A merchant has two article codes:
Article "REIMB_3C" invoiceTypeEl = "#{il.amount < 0 ? 'REIMBURSEMENT_NOTE' : 'INVOICE'}"
Article "COMMISSION" invoiceTypeEl = "INVOICE"
When the bill run produces ILs for both articles, the engine creates two invoices per BA: one of type REIMBURSEMENT_NOTE (containing the negative-amount REIMB_3C lines) and one of type INVOICE (containing the positive-amount lines).
Example 3 — split by billSeparately
A subscription has three service instances, one of which is marked billSeparately=true. The engine adds serviceInstance.id to the GROUP BY only for that service instance — producing one extra IL set in the invoice for it. All three service instances still land in the same invoice document.
Example 4 — combining split mechanisms
The mechanisms compose:
2 payment methods (DIRECT_DEBIT, CHECK)
× 2 article-resolved invoice types (REIMBURSEMENT_NOTE, INVOICE)
× 1 active billSeparately at SERVICE_INSTANCE level (3 instances)
= up to 12 invoices per BA, with separate IL sets within each
In practice the number is much smaller because not every combination has billable RTs.
What about validation scripts?
Validation scripts run after invoices are created. They can:
- Pass an invoice (
status = VALIDATED) - Reject an invoice (
status = REJECTED) - Flag an invoice as suspect (
status = SUSPECT)
They cannot redirect RTs into different invoices — that decision is made earlier, during IL grouping and invoice creation. Use the five native mechanisms above for splitting, not validation scripts.