---
id: STATE-ACC-TRX-001
title: Invoice lifecycle
object: DOM-ACC-TRX-001
product_area: accounting
module: transactions
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: not created
    meaning: The invoice has not been saved.
  - name: draft
    meaning: 'An editable, unnumbered bill under preparation.'
  - name: pro forma
    meaning: A numbered quotation awaiting receivable recognition.
  - name: open
    meaning: A recognised bill with outstanding debt.
  - name: paid
    meaning: The derived remaining debt is zero after settlement.
  - name: cancelled
    meaning: Credits have removed the billed value.
  - name: written off
    meaning: The unpaid remainder has been abandoned.
  - name: void
    meaning: Cancellation retains the document and offsets its recognised debt.
  - name: removed
    meaning: Deletion has removed the draft or unconverted quotation.
transitions:
  - from: not created
    to: draft
    trigger: save a new bill
    guard: >-
      Products must be active, sellable in the unit and priced in an enabled
      currency.
    side_effects:
      - save the invoice header and charges
      - append creation history
    who: Staff with invoice-create permission or the Commerce billing function.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/create-draft.integration.spec.js#it
      refuses an inactive product (product_not_sellable)
  - from: draft
    to: draft
    trigger: replace prepared content
    guard: 'The supplied version must match, preserving unit and currency.'
    side_effects:
      - replace the prepared header and charges
      - increment the record version
    who: Staff with invoice-create permission.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/update-draft.integration.spec.js#it
      refuses an update at a version the draft has moved past (stale)
  - from: draft
    to: pro forma
    trigger: issue a quotation
    guard: The quotation must contain a line.
    side_effects:
      - assign its invoice number
      - retain tax determination without the recognition freeze
      - leave accounting batch and receivable facts absent
    who: Staff with invoice-adjust permission or the Commerce billing function.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      gives it no batch, no accounting date and no recognition (FC-06)
  - from: draft
    to: open
    trigger: issue the real bill
    guard: >-
      Lines, an open period and an eligible batch are required, with tax address
      and service periods where applicable.
    side_effects:
      - assign its invoice number
      - protect recognised commercial content and tax policy
      - select the exchange rate
      - record each line's issued receivable
      - append issue history
    who: Staff with invoice-adjust permission or the Commerce billing function.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      refuses the issue when the unit has no assignment rules (batch_required),
      the draft unchanged
  - from: draft
    to: paid
    trigger: issue a bill with no value
    guard: The ordinary issue checks still apply.
    side_effects:
      - derive paid status from the zero balance
    who: Staff with invoice-adjust permission.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      is paid at once, its line with it
  - from: draft
    to: void
    trigger: cancel prepared billing
    guard: A cancellation explanation is required.
    side_effects:
      - retain the unnumbered draft
      - append cancellation history
    who: Staff with invoice-void permission.
    provenance: documented
    source: >-
      services-accounting:docs/architecture/accounting-2.0/05-lifecycles.md#1.
      The invoice
  - from: draft
    to: removed
    trigger: remove prepared billing
    guard: Deletion protection must permit removal.
    side_effects:
      - remove the header and its lines
    who: Staff with invoice-delete permission or the Commerce billing function.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      gives it no batch, no accounting date and no recognition (FC-06)
  - from: pro forma
    to: open
    trigger: recognise the quotation
    guard: 'The pro forma must be issued, live and not yet converted.'
    side_effects:
      - assign batch and accounting date
      - activate receivable recognition
      - record issued financial facts
    who: >-
      Staff with invoice-adjust permission, the Commerce billing function or the
      collection command.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/payments/receive-offline.integration.spec.js#it
      converts an unconverted pro forma the first payment names, in the
      payment’s batch, and pays it (Commerce’s first payment makes it real)
  - from: pro forma
    to: removed
    trigger: remove a quotation
    guard: The quotation must still be unconverted.
    side_effects:
      - remove the document and all its lines
    who: Staff with invoice-delete permission or the Commerce billing function.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      gives it no batch, no accounting date and no recognition (FC-06)
  - from: open
    to: open
    trigger: correct issued billing
    guard: >-
      Amendment, document or accounting dates and batch moves require unposted
      batching; repricing or removal requires unapplied lines.
    side_effects:
      - increment amendment revision
      - record amendment or date-correction facts
      - append history for the change
    who: Staff with the applicable invoice-adjust or update permission.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/document-edits/reassign-and-edit.integration.spec.js#it
      answers the invoice at revision 2 with the line added after the last, the
      line repriced and the totals re-derived
  - from: open
    to: paid
    trigger: complete settlement
    guard: The remaining line debt must reach zero.
    side_effects:
      - derive paid status from maintained amounts
    who: Database maintenance after allocation or credit application.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      raises the line’s amount_paid: a line settled by store credit reads open
      until fully paid, then paid
  - from: paid
    to: open
    trigger: release previously settled value
    guard: >-
      A refund reopening debt or an allocation, application or receipt reversal
      removes settled money.
    side_effects:
      - derive open status from the restored debt
    who: Database maintenance after the release command.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/payments/payment-reversals.integration.spec.js#it
      reverses the live allocation so the line owes again, and the payment
      item’s facts sum to zero (FC-16)
  - from: open
    to: cancelled
    trigger: credit all billed value
    guard: Credit reductions must cover the line value.
    side_effects:
      - derive cancelled status from maintained amounts
    who: Database maintenance after memo application.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/change-memos/credit-memos.integration.spec.js#it
      makes the credited line cancelled and leaves the invoice open with the
      other line’s 80.00
  - from: cancelled
    to: open
    trigger: undo a billed reduction
    guard: The memo must be issued and not void.
    side_effects:
      - apply opposing memo lines to restore debt
    who: Staff with credit-memo update permission.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/change-memos/credit-memos.integration.spec.js#it
      returns the invoice’s balances to what they were before the memo
  - from: open
    to: written off
    trigger: abandon the unpaid balance
    guard: No payment is applied and the write-off covers all remaining debt.
    side_effects:
      - derive written-off status from maintained amounts
    who: Staff with write-off create permission.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/write-offs/write-offs.integration.spec.js#it
      makes the whole line written_off and keeps the partly written-off line and
      the invoice open
  - from: written off
    to: open
    trigger: restore abandoned debt
    guard: The original write-off must remain eligible for reversal.
    side_effects:
      - apply a new opposite write-off
    who: Staff with write-off update permission.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/write-offs/write-offs.integration.spec.js#it
      answers the reversal: a second write-off, numbered next, its lines
      negating the first’s
  - from: open
    to: void
    trigger: cancel an issued bill
    guard: >-
      A reason, no applications or adjustments, an unposted batch and no update
      protection are required.
    side_effects:
      - mark lines void
      - offset remaining debt on the cancellation date
      - retain original amounts
    who: Staff with invoice-void permission.
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/invoices/invoice-drafts.ts;
      services-accounting:modules/transactions/services/invoices/invoice-issue.ts;
      services-accounting:modules/transactions/services/invoices/invoice-changes.ts;
      services-accounting:modules/transactions/services/invoices/invoice-amendments.ts;
      services-accounting:modules/transactions/db/invoices.table.ts;
      services-accounting:modules/transactions/db/invoice-lines.table.ts;
      services-accounting:modules/transactions/tests/integration/services/invoices/void.integration.spec.js#it
      appends an invoice_voided fact per line for minus the line’s balance,
      dated today
invalid:
  - from: void
    to: open
    why: Cancellation is final; replacement billing requires another invoice.
  - from: open
    to: draft
    why: Issue does not support returning the document to preparation.
  - from: open
    to: removed
    why: >-
      Recognised real invoices remain retained; deletion is restricted to drafts
      and unconverted quotations.
  - from: paid
    to: void
    why: Applied money or adjustments prevent cancellation of the invoice.
  - from: written off
    to: void
    why: >-
      The existing write-off must be reversed before invoice cancellation can be
      considered.
concurrency: >-
  Invoice commands and balance maintenance take ordered locks to serialize
  conflicting changes. Draft replacement additionally checks the read version,
  and competing issues recheck draft eligibility.
invariants:
  - INV-DOM-ACC-TRX-001-04
  - INV-DOM-ACC-TRX-001-06
  - INV-DOM-ACC-TRX-001-07
  - INV-DOM-ACC-TRX-001-08
  - INV-DOM-ACC-TRX-001-10
  - INV-DOM-ACC-TRX-001-11
  - INV-DOM-ACC-TRX-001-12
evidence:
  - >-
    services-accounting at f47cb263: services/invoices/invoice-drafts.ts,
    invoice-issue.ts, invoice-changes.ts, invoice-amendments.ts;
    db/invoices.table.ts; docs/architecture/accounting-2.0/05-lifecycles.md §1;
    02-decisions.md M1-1, A-4, A-14.
---
# Invoice lifecycle

## The Lifecycle

Invoice status combines command-controlled preparation and cancellation with amount-derived financial states. The quotation and removed nodes describe product conditions rather than additional stored invoice-status values.

```mermaid
stateDiagram-v2
  direction LR
  state "not created" as s0
  state "draft" as s1
  state "pro forma" as s2
  state "open" as s3
  state "paid" as s4
  state "cancelled" as s5
  state "written off" as s6
  state "void" as s7
  state "removed" as s8
  s0 --> s1 : save a new bill
  s1 --> s1 : replace prepared content
  s1 --> s2 : issue a quotation
  s1 --> s3 : issue the real bill
  s1 --> s4 : issue a bill with no value
  s1 --> s7 : cancel prepared billing
  s1 --> s8 : remove prepared billing
  s2 --> s3 : recognise the quotation
  s2 --> s8 : remove a quotation
  s3 --> s3 : correct issued billing
  s3 --> s4 : complete settlement
  s4 --> s3 : release previously settled value
  s3 --> s5 : credit all billed value
  s5 --> s3 : undo a billed reduction
  s3 --> s6 : abandon the unpaid balance
  s6 --> s3 : restore abandoned debt
  s3 --> s7 : cancel an issued bill
```

## Transitions Explained

The transition rows identify the initiating action, eligibility checks, resulting records and responsible caller. They retain the pinned evidence for each path.

## What Cannot Happen

The invalid-transition rows describe final-state protection and unsupported paths; command eligibility remains distinct from the declared state vocabulary.

## Concurrency and Timing

Invoice commands and balance maintenance take ordered locks to serialize conflicting changes. Draft replacement additionally checks the read version, and competing issues recheck draft eligibility. There is no scheduled invoice transition here; collection and correction commands drive the lifecycle.
