---
id: TST-ACC-TRX-037
title: Credits suite
kind: integration
product_area: accounting
module: transactions
revision: 1
status: draft
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
repository: services-accounting
files:
  - >-
    modules/transactions/tests/integration/services/credits/credits.integration.spec.js
implements:
  - ATS-ACC-TRX-011-01
  - ATS-ACC-TRX-011-02
  - ATS-ACC-TRX-011-03
  - ATS-ACC-TRX-011-04
  - ATS-ACC-TRX-011-05
  - ATS-ACC-TRX-011-06
  - ATS-ACC-TRX-011-07
  - ATS-ACC-TRX-011-08
implements_parts:
  - scenario: ATS-ACC-TRX-011-01
    assertion: it refuses an account of another unit (business_unit_mismatch)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses an account of another unit (business_unit_mismatch)
  - scenario: ATS-ACC-TRX-011-01
    assertion: it refuses an amount in another currency (currency_mismatch)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses an amount in another currency (currency_mismatch)
  - scenario: ATS-ACC-TRX-011-01
    assertion: >-
      it numbers the tenant’s first credit 1,000,000, open, with its accounts
      and its whole balance
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      numbers the tenant’s first credit 1,000,000, open, with its accounts and
      its whole balance
  - scenario: ATS-ACC-TRX-011-02
    assertion: 'it takes the money off the payment: credited out 2000, unapplied 1000'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      takes the money off the payment: credited out 2000, unapplied 1000
  - scenario: ATS-ACC-TRX-011-02
    assertion: 'it is the payment’s customer, unit and currency, open with its balance'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      is the payment’s customer, unit and currency, open with its balance
  - scenario: ATS-ACC-TRX-011-02
    assertion: it refuses more than the payment holds unapplied (over_application)
    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)
  - scenario: ATS-ACC-TRX-011-03
    assertion: 'it uses the credit up: applied 5000, balance 0, status used'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      uses the credit up: applied 5000, balance 0, status used
  - scenario: ATS-ACC-TRX-011-03
    assertion: it refuses applying a used credit (credit_not_open)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses applying a used credit (credit_not_open)
  - scenario: ATS-ACC-TRX-011-03
    assertion: >-
      it refuses more than the credit holds (insufficient_credit), the
      database’s CHECK, writing nothing
    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
  - scenario: ATS-ACC-TRX-011-03
    assertion: >-
      it serializes two concurrent applications of one credit: one succeeds, the
      other is refused by the 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
  - scenario: ATS-ACC-TRX-011-04
    assertion: >-
      it numbers the application 1,000,000, applied, for the whole credit: 4000
      then 1000 by pay priority
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      numbers the application 1,000,000, applied, for the whole credit: 4000
      then 1000 by pay priority
  - scenario: ATS-ACC-TRX-011-04
    assertion: >-
      it raises the line’s amount_paid: a line settled by store credit reads
      open until fully paid, then paid
    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
  - scenario: ATS-ACC-TRX-011-04
    assertion: >-
      it applies one credit across two invoices in one application: a line of
      each, the balances of both lowered
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      applies one credit across two invoices in one application: a line of each,
      the balances of both lowered
  - scenario: ATS-ACC-TRX-011-05
    assertion: >-
      it refuses a credit of another customer than the invoice’s bill-to
      (credit_customer_mismatch)
    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)
  - scenario: ATS-ACC-TRX-011-06
    assertion: >-
      it refuses a credit that expired before the application date
      (credit_expired)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses a credit that expired before the application date (credit_expired)
  - scenario: ATS-ACC-TRX-011-06
    assertion: it applies a credit on the day it expires
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      applies a credit on the day it expires
  - scenario: ATS-ACC-TRX-011-06
    assertion: >-
      it refuses a credit restricted to another order type than the invoice’s
      (credit_order_type_mismatch)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses a credit restricted to another order type than the invoice’s
      (credit_order_type_mismatch)
  - scenario: ATS-ACC-TRX-011-06
    assertion: it applies a restricted credit to an invoice of its order type
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      applies a restricted credit to an invoice of its order type
  - scenario: ATS-ACC-TRX-011-07
    assertion: >-
      it answers a second application naming the first, its lines negating the
      first’s
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      answers a second application naming the first, its lines negating the
      first’s
  - scenario: ATS-ACC-TRX-011-07
    assertion: >-
      it marks the first reversed and gives the money back to the credit, which
      is open again
    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
  - scenario: ATS-ACC-TRX-011-07
    assertion: it refuses reversing it again (application_not_live)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      refuses reversing it again (application_not_live)
  - scenario: ATS-ACC-TRX-011-08
    assertion: it voids an unused credit with the reason and nets its facts to nothing
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      voids an unused credit with the reason and nets its facts to nothing
  - scenario: ATS-ACC-TRX-011-08
    assertion: it gives a payment’s credit back to the payment
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js#it
      gives a payment’s credit back to the payment
  - scenario: ATS-ACC-TRX-011-08
    assertion: it refuses voiding a credit with money applied (credit_has_applications)
    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)
environment: local
implementation_state: implemented
latest_result: not-run
latest_run_at: null
latest_run_ref: null
conformance: not-verified
estimated_minutes: 3
isolation: >-
  Suite-marked records in the prepared local test database; fixture teardown
  removes owned records.
provenance: test
source: >-
  services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js
evidence:
  - provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/credits/credits.integration.spec.js
    assertion_titles:
      - the credit commands (integration)
      - a manual credit of 5000
      - >-
        numbers the tenant’s first credit 1,000,000, open, with its accounts and
        its whole balance
      - >-
        writes its credit_issued fact of minus the total and the create document
        event
      - refuses an account of another unit (business_unit_mismatch)
      - refuses an amount in another currency (currency_mismatch)
      - a credit from a payment’s unapplied money
      - 'takes the money off the payment: credited out 2000, unapplied 1000'
      - 'is the payment’s customer, unit and currency, open with its balance'
      - >-
        mirrors the money’s move in the facts: plus on the payment item, minus
        on the credit item
      - refuses more than the payment holds unapplied (over_application)
      - applying the manual credit
      - >-
        numbers the application 1,000,000, applied, for the whole credit: 4000
        then 1000 by pay priority
      - >-
        raises the line’s amount_paid: a line settled by store credit reads open
        until fully paid, then paid
      - >-
        replays the same key and body as the same application, writing no more
        lines (FC-19)
      - >-
        refuses the same key with another body (idempotency_key_reused): the
        application is found by its key and its request fingerprint
      - >-
        applies one credit across two invoices in one application: a line of
        each, the balances of both lowered
      - 'uses the credit up: applied 5000, balance 0, status used'
      - >-
        mirrors each line in the credit item’s facts, which sum to minus the
        balance
      - records the apply document event
      - refuses applying a used credit (credit_not_open)
      - >-
        refuses more than the credit holds (insufficient_credit), the database’s
        CHECK, writing nothing
      - >-
        serializes two concurrent applications of one credit: one succeeds, the
        other is refused by the database
      - refusing a credit that does not fit the invoice (DEF-ACT-0851)
      - >-
        refuses a credit of another customer than the invoice’s bill-to
        (credit_customer_mismatch)
      - >-
        refuses a credit that expired before the application date
        (credit_expired)
      - applies a credit on the day it expires
      - >-
        refuses a credit restricted to an order type the invoice does not carry
        (credit_order_type_mismatch)
      - >-
        refuses a credit restricted to another order type than the invoice’s
        (credit_order_type_mismatch)
      - applies a restricted credit to an invoice of its order type
      - reversing the application
      - >-
        answers a second application naming the first, its lines negating the
        first’s
      - >-
        marks the first reversed and gives the money back to the credit, which
        is open again
      - >-
        answers the reversed application with who reversed it, when, why, and
        the reversing application (DEF-ACT-0317)
      - reopens the line and keeps the facts in step with the balances
      - refuses reversing it again (application_not_live)
      - voiding
      - voids an unused credit with the reason and nets its facts to nothing
      - gives a payment’s credit back to the payment
      - refuses voiding a credit with money applied (credit_has_applications)
      - refuses a credit from a voided payment (payment_not_live)
      - the application ledger is insert-only for the application role
      - refuses an UPDATE and a DELETE of an application line
      - refuses an UPDATE of the credit’s maintained amounts
    imports:
      - 'const { sql } = require(''drizzle-orm'');'
      - >-
        const { expectDatabaseRefusal } =
        require('@rhythmsoftware/macroservice-testing/refusals');
      - 'const { stage } = require(''@rhythmsoftware/macroservice-testing/stage'');'
      - >-
        const { applyCredit, createCredit, reverseCreditApplication, voidCredit
        } = require('../../../../services/credits/credit-commands');
      - >-
        const { getCreditApplication } =
        require('../../../../services/credits/credit-reads');
      - 'const { draftRequest } = require(''../../invoices-fixtures'');'
      - >-
        const { createDraft } =
        require('../../../../services/invoices/invoice-drafts');
      - >-
        const { issueInvoice } =
        require('../../../../services/invoices/invoice-issue');
      - >-
        const { voidPayment } =
        require('../../../../services/payments/payment-allocate');
      - >-
        const { receivePayment } =
        require('../../../../services/payments/payment-receive');
      - '} = require(''../../payments-fixtures'');'
      - "\t\tworld.require();"
      - "\t\tconst { invoice } = world.require();"
      - "\t\t\tworld.require();"
      - "\t\t\tconst credit = created.require();"
      - "\t\t\tconst credit = created.require();"
      - "\t\t\tconst { payment } = made.require();"
      - "\t\t\tconst { payment, credit } = made.require();"
      - "\t\t\tconst { payment, credit } = made.require();"
      - "\t\t\tconst { payment } = made.require();"
      - "\t\t\tconst { invoice, application } = applied.require();"
      - "\t\t\tconst { invoice } = applied.require();"
      - "\t\t\tconst { invoice, credit, application } = applied.require();"
      - "\t\t\tconst { invoice, credit } = applied.require();"
      - "\t\t\tconst { invoice } = applied.require();"
      - "\t\t\tconst { credit } = applied.require();"
      - "\t\t\tconst { credit } = applied.require();"
      - "\t\t\tconst { application } = applied.require();"
      - "\t\t\tconst { invoice, credit } = applied.require();"
      - "\t\t\tconst { invoice } = applied.require();"
      - "\t\t\tconst { invoice } = world.require();"
      - "\t\t\tconst { invoice } = world.require();"
      - "\t\t\tconst { invoice } = world.require();"
      - "\t\t\tworld.require();"
      - "\t\t\tconst { invoice } = world.require();"
      - "\t\t\tconst { application } = applied.require();"
      - "\t\t\tconst { application } = applied.require();"
      - "\t\t\tconst { reversal } = reversed.require();"
      - "\t\t\tconst { application, credit } = applied.require();"
      - "\t\t\tconst { application } = applied.require();"
      - "\t\t\tconst { reversal } = reversed.require();"
      - "\t\t\tconst { invoice, credit } = applied.require();"
      - "\t\t\tconst { application } = applied.require();"
      - "\t\t\tconst { payment, credit } = made.require();"
      - "\t\t\tconst { invoice } = world.require();"
      - "\t\t\tconst { application } = applied.require();"
      - "\t\t\tconst { credit } = applied.require();"
    test_double_declarations: []
---

# Credits suite

## Verification Boundary

The suite exercises transaction commands and database protections in the local prepared test environment. Registration records the existing executable file; it does not report a run or establish product approval.

## What This Suite Proves

Its cited assertions contribute evidence for manual credit requires compatible accounts; credit creation transfers available receipt value; credit spending remains bounded under concurrency; one application can pay several invoices; credit cannot cross customers; expiry boundary and order restriction are enforced; application reversal restores held value once; unused credit can be voided safely. The structured assertion bindings identify which parts of combined scenarios it executes.

## What It Does Not Prove

Local commands and route invocations do not establish deployed routing, live processor guarantees or the complete staff browser experience. An unbound feature chain remains proposed, and this registration carries no execution result.

## Real Dependencies and Test Doubles

The suite uses the imported commands, route handlers or persistence code against the local test database. Fixture helpers supply accounting context; any stepped clock, dummy processor or substituted external client is retained in the evidence declarations.

## Must Not Be Doubled

The command or persistence behavior under assertion and its relevant database protections must remain real. Substituting the final financial result removes evidence of transactional effects.
