---
id: DOM-ACC-TRX-004
title: Credit
realises: CON-ACC-TRX-004
product_area: accounting
module: transactions
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-ACC-TRX-transactions
lifecycle: STATE-ACC-TRX-011
custom_fields: none
identity: >-
  A credit has its own time-ordered identity and workspace sequence number. Each
  application is a separately identified, numbered and batched record.
attributes:
  - name: 'customer, business unit and currency'
    kind: reference
    required: true
    computed: false
    notes: >-
      Names the beneficiary and accounting scope directly or inherits them from
      the source receipt.
  - name: source
    kind: enum
    required: true
    computed: false
    notes: >-
      Explains whether the value was granted manually or derived from a payment,
      overpayment or memo.
  - name: reason
    kind: text
    required: true
    computed: false
    notes: Keeps a required explanation that later edits cannot erase.
  - name: liability and expense accounts
    kind: reference
    required: false
    computed: false
    notes: Selects unit liability and expense accounts for a manual grant.
  - name: amount
    kind: money
    required: true
    computed: false
    notes: >-
      Preserves the original value and limits receipt-derived credit to
      available receipt money.
  - name: 'applied, refunded and reserved'
    kind: money
    required: true
    computed: true
    notes: >-
      Tracks spent, returned and settlement-held value through database
      maintenance.
  - name: balance
    kind: money
    required: true
    computed: true
    notes: 'Calculates the unspent, unrefunded and unreserved amount, bounded at zero.'
  - name: status
    kind: enum
    required: true
    computed: true
    notes: >-
      The status trigger derives open or used from value less applications and
      refunds, excluding reservations; fully held credit can remain open with
      zero available balance. Void is explicit and no worker sets expired.
  - name: expiry date and order-type restriction
    kind: date
    required: false
    computed: false
    notes: >-
      Limits direct applications by time and order category; settlement
      currently bypasses these checks.
  - name: 'date, accounting date and batch'
    kind: date
    required: true
    computed: false
    notes: Locates the credit in its posting batch and dates.
  - name: applications · lines
    kind: collection
    required: false
    computed: false
    notes: >-
      Records pending reservations, applied value, cancellation and reversal in
      independent application documents.
relationships:
  - to: DOM-ACC-TRX-001
    cardinality: many-to-many through applications
    ownership: >-
      Applications owned by the credit add to the destination invoice line's
      paid amount.
    cross_service: false
  - to: DOM-ACC-TRX-003
    cardinality: many-to-one
    ownership: A payment-backed credit transfers available money from its source receipt.
    cross_service: false
  - to: DOM-ACC-TRX-005
    cardinality: many-to-one
    ownership: The customer account includes available credit as a negative open item.
    cross_service: false
  - to: DOM-ACC-TRX-006
    cardinality: one-to-one
    ownership: >-
      Credits and application documents each use the transaction-record
      structure.
    cross_service: false
invariants:
  - id: INV-DOM-ACC-TRX-004-01
    statement: >-
      Manual credit requires a beneficiary, unit, currency, explanation and
      unit-specific liability and expense accounts.
    enforced_at:
      - api
      - service
      - database
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/e2e/credits.e2e.spec.js#it
      refuses a manual credit without its accounts, and a payment’s credit
      without its payment (400)
  - id: INV-DOM-ACC-TRX-004-02
    statement: >-
      Receipt-derived credit preserves its source customer's unit and currency
      and cannot draw more than the receipt's available money.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses more than the payment holds unapplied (over_application)
  - id: INV-DOM-ACC-TRX-004-03
    statement: >-
      Available credit is original value minus applied, refunded and reserved
      value and cannot be negative.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses more than the credit holds (insufficient_credit), the database’s
      CHECK, writing nothing
  - id: INV-DOM-ACC-TRX-004-04
    statement: >-
      Credit reaches invoice debt through separately numbered and batched
      applications.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      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
  - id: INV-DOM-ACC-TRX-004-05
    statement: Credit application targets belong to the credit's customer.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses a credit of another customer than the invoice’s bill-to
      (credit_customer_mismatch)
  - id: INV-DOM-ACC-TRX-004-06
    statement: >-
      The direct application command enforces expiry and order category, while
      settlement does not perform those eligibility checks.
    enforced_at:
      - service
    provenance: observed
    source: >-
      services-accounting:modules/transactions/services/credits/credit-commands.ts:requireCreditUsable;
      services-accounting:modules/transactions/services/settlements/settlement-commands.ts:settle
  - id: INV-DOM-ACC-TRX-004-07
    statement: >-
      Locking serializes competing applications so they cannot spend the same
      credit twice.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      serializes two concurrent applications of one credit: one succeeds, the
      other is refused by the database
  - id: INV-DOM-ACC-TRX-004-08
    statement: >-
      Reversing an application requires a reason and one retained opposing
      application.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      marks the first reversed and gives the money back to the credit, which is
      open again
  - id: INV-DOM-ACC-TRX-004-09
    statement: >-
      Void is available before posting only when no credit has been spent, held
      or refunded.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses voiding a credit with money applied (credit_has_applications)
  - id: INV-DOM-ACC-TRX-004-10
    statement: >-
      The application role cannot replace application lines or maintained credit
      amounts.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses an UPDATE and a DELETE of an application line
  - id: INV-DOM-ACC-TRX-004-11
    statement: >-
      Credit tables have no custom-field storage and the module registration
      omits the custom-field capability declaration.
    enforced_at:
      - api
      - service
    provenance: documented
    source: >-
      services-accounting:docs/milestones/plans/ACCT-M2-plan.md#7a. Entity
      capabilities (R2-ARCH-001)
physical:
  - credits
  - credit_applications
  - credit_application_lines
  - credits_v
  - credit_applications_v
evidence:
  - >-
    services-accounting at f47cb263: modules/transactions/db/credits.table.ts,
    credit-applications.table.ts, credit-application-lines.table.ts,
    payment-vocabularies.ts (credit statuses and sources); services/credits/*.
  - >-
    services-accounting at f47cb263:
    docs/architecture/accounting-2.0/02-decisions.md (M2-2, M2-11, A-9),
    05-lifecycles.md §3, 06-integrity-model.md §2 and §3.
  - >-
    services-accounting:db/migrations/20261017174829_payments_core.sql:derive_credit_status;
    db/migrations/20261017192623_payments_operations.sql (reservation
    maintenance does not replace status derivation)
---
# Credit

## What It Models

Credit represents value held for a customer and the application documents that spend it. Its source distinguishes a manual grant from value originating in an earlier receipt or cancellation.

## Identity and Attributes

Credit attributes describe the grant, accounting scope and remaining value. Application records provide the posting and lifecycle context for each use of that value.

## Relationships

Applications link credit to billed lines, while payment-backed grants link it to collected money. The customer account reads the available balance without owning the credit.

## Invariants

The invariant rows separate financial bounds from use restrictions. Settlement currently omits restrictions that direct application checks, creating a recorded implementation conflict.

## How It Is Stored and Shared

Transactions owns grants and applications. Staff operate through credit permissions, and Commerce can apply or create credit through the separately granted service functions.

## Open Design Questions

Expiry does not update the stored status, and settlement bypasses expiry and order restrictions. A standalone grant from a memo is also absent despite that source being represented in the vocabulary.
