Skip to main content

Quarantine Review

Invoices reach a quarantine bill run by two paths — automatic (validation script) and manual (operator review). Both feed the same child quarantine BR and the same release flow.

Two ways into quarantine

1. Auto-quarantine (validation script)

When validation rejects an invoice — or flags it suspect — and the run's rejectAutoAction / suspectAutoAction is MOVE (default), the engine creates a child bill run holding the failing invoices:

  1. The invoicing job validates each invoice. The validation script (per-BR or inherited from the cycle) decides reject / suspect / pass.
  2. For each rejected or suspect invoice, when the corresponding auto-action is MOVE, the engine:
    • Lazily creates a child bill run with isQuarantine=true.
    • Copies scope and aggregation config from the origin BR; sets originBillingRun back-reference; resets amounts to zero.
    • Moves the failing invoices into the child BR via dedicated named queries, setting status to REJECTED or SUSPECT.
  3. The origin BR finishes with clean amounts. The child quarantine BR holds the problem invoices.

2. Manual quarantine (review before posting)

While a bill run sits at DRAFT_INVOICES or POSTINVOICED, the operator can review the draft invoices and selectively quarantine the bad ones before validating the rest:

POST /api/v1/billingRuns/{id}/quarantineInvoices
{ "invoiceIds": [12, 13], "description": "Carrefour amounts look wrong" }

Selected invoices move into a quarantine child BR (consolidated — repeated calls on one origin BR land in the same child). Status-gated: only valid in DRAFT_INVOICES / POSTINVOICED (409 otherwise). In the UI, the EditBillRun "Related Invoices" table becomes a checkbox multi-select with a Send to quarantine bulk action at those statuses.

Finding quarantine bill runs

The Bill Runs list page surfaces a Type column showing Quarantine of #N when isQuarantine=true, with the parent BR ID. Click the row to navigate to the dedicated quarantine review page.

In API terms, every BillingRunV1Response carries:

{
"id": 542,
"isQuarantine": true,
"originBillingRunId": 540,
"quarantine": {
"rejectedInvoiceCount": 2,
"suspectInvoiceCount": 1
}
}

Reviewing and releasing invoices

The quarantine review page lists the rejected and suspect invoices with their status, BA, and rejection reason. Each row offers three actions:

ActionWhat it does
RevalidateFlip the invoice status back to NEW and re-run validation. Successes attach to a fresh child BR (or a caller-supplied targetBillingRunId).
CancelDrop the invoice entirely. Underlying RTs stay BILLED.
Cancel and reopen RTsDrop the invoice and reopen its RTs so the next bill run picks them up.

API endpoint

POST /api/v1/billingRuns/{id}/releaseFromQuarantine

{
"invoiceIds": [12, 13, 14],
"action": "REVALIDATE",
"targetBillingRunId": null
}

Returns:

{
"quarantineBillingRunId": 542,
"action": "REVALIDATE",
"invoicesProcessed": 3,
"invoicesFailed": 0,
"targetBillingRunId": 543
}

Errors

CodeCause
400 MISSING_INVOICE_IDSinvoiceIds is empty.
400 INVALID_ACTIONaction is not one of REVALIDATE / CANCEL / CANCEL_RT.
404 BILLING_RUN_NOT_FOUNDBR id does not exist.
409BR is not a quarantine BR (isQuarantine=false).

Preventing quarantine

If you do not want auto-quarantine, set the auto-actions to something other than MOVE:

ActionEffect when validation rejects/suspects an invoice
MOVE (default)Move to a child quarantine BR.
CANCELDrop the invoice.
CANCEL_RTDrop the invoice and reopen its RTs.
AUTOMATIC_VALIDATIONTreat as valid and continue (suspect only).
MANUAL_ACTIONStop the engine and wait for an operator.

The "New Bill Run" form shows an inline warning under the run-options panel when either auto-action is set to MOVE, linking back here.