---
id: TST-ACC-TRX-056
title: Issue 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/invoices/issue.integration.spec.js
implements:
  - ATS-ACC-TRX-001-07
  - ATS-ACC-TRX-001-08
  - ATS-ACC-TRX-001-09
  - ATS-ACC-TRX-001-11
  - ATS-ACC-TRX-001-12
implements_parts:
  - scenario: ATS-ACC-TRX-001-07
    assertion: it refuses a closed batch (batch_not_open)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      refuses a closed batch (batch_not_open)
  - scenario: ATS-ACC-TRX-001-07
    assertion: it refuses another unit’s batch (batch_not_open)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      refuses another unit’s batch (batch_not_open)
  - scenario: ATS-ACC-TRX-001-07
    assertion: 'it refuses the issue (period_not_open), the draft unchanged'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      refuses the issue (period_not_open), the draft unchanged
  - scenario: ATS-ACC-TRX-001-07
    assertion: >-
      it refuses the issue when the unit has no assignment rules
      (batch_required), the draft unchanged
    provenance: test
    source: >-
      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
  - scenario: ATS-ACC-TRX-001-07
    assertion: it issues in the active batch of the recurring batch the rules choose
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      issues in the active batch of the recurring batch the rules choose
  - scenario: ATS-ACC-TRX-001-08
    assertion: 'it numbers the tenant’s first invoice 1,000,000'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      numbers the tenant’s first invoice 1,000,000
  - scenario: ATS-ACC-TRX-001-08
    assertion: 'it numbers the next invoice 1,000,001'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      numbers the next invoice 1,000,001
  - scenario: ATS-ACC-TRX-001-09
    assertion: >-
      it resolves no account on a line, even of a product with an accounting
      setup: the setup’s tax class alone is settled
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      resolves no account on a line, even of a product with an accounting setup:
      the setup’s tax class alone is settled
  - scenario: ATS-ACC-TRX-001-09
    assertion: >-
      it resolves no account on a line of a product with no setup either (the
      unit’s defaults are the posting’s to read)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      resolves no account on a line of a product with no setup either (the
      unit’s defaults are the posting’s to read)
  - scenario: ATS-ACC-TRX-001-11
    assertion: 'it is paid at once, its line with it'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      is paid at once, its line with it
  - scenario: ATS-ACC-TRX-001-12
    assertion: 'it gives it no batch, no accounting date and no recognition (FC-06)'
    provenance: test
    source: >-
      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)
  - scenario: ATS-ACC-TRX-001-12
    assertion: >-
      it makes it a real invoice in the batch, its receivable recognized, its
      number kept
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      makes it a real invoice in the batch, its receivable recognized, its
      number kept
  - scenario: ATS-ACC-TRX-001-12
    assertion: it refuses converting it again (invoice_not_pro_forma)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js#it
      refuses converting it again (invoice_not_pro_forma)
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/invoices/issue.integration.spec.js
evidence:
  - provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/invoices/issue.integration.spec.js
    assertion_titles:
      - issueInvoice and convertProForma (integration)
      - issuing two drafts in a named open batch
      - 'numbers the tenant’s first invoice 1,000,000'
      - 'numbers the next invoice 1,000,001'
      - makes the invoice and its lines open
      - sets the accounting date to the invoice date
      - >-
        recognizes the receivable at the issue instant, by the caller, in the
        batch named
      - >-
        resolves no account on a line, even of a product with an accounting
        setup: the setup’s tax class alone is settled
      - >-
        resolves no account on a line of a product with no setup either (the
        unit’s defaults are the posting’s to read)
      - >-
        appends one invoice_issued fact per line, for the line’s total, dated
        the invoice date
      - 'records the issue document event, draft to open'
      - reads back what it answered
      - >-
        refuses issuing an issued invoice again (invoice_not_draft), the invoice
        unchanged
      - >-
        a change of the product’s setup after issue (FC-04: the issued line
        carries no accounts, so the change is what posts)
      - >-
        leaves the issued line without accounts still: nothing was frozen, the
        posting reads the setup as it stands then
      - >-
        leaves the revenue splits unresolved on both lines (null): the posting
        snapshots them
      - >-
        a product whose setup has revenue splits (D-03, superseded: nothing is
        frozen at issue)
      - >-
        freezes none of the setup’s splits on the line (the posting snapshots
        the splits as they stand when it posts)
      - >-
        answers the line without splits or accounts, at issue and when read
        back, with no posting instant
      - the batch named
      - refuses a closed batch (batch_not_open)
      - refuses another unit’s batch (batch_not_open)
      - refuses a batch that does not exist (not_found)
      - leaves the draft a draft after the refusals
      - a date no fiscal period covers
      - 'refuses the issue (period_not_open), the draft unchanged'
      - a pro forma
      - numbers it and makes it open
      - 'gives it no batch, no accounting date and no recognition (FC-06)'
      - appends no receivable fact for it (FC-06)
      - >-
        refuses converting an invoice that is not a pro forma
        (invoice_not_pro_forma)
      - converted
      - >-
        makes it a real invoice in the batch, its receivable recognized, its
        number kept
      - appends its invoice_issued facts
      - records the convert_pro_forma document event
      - refuses converting it again (invoice_not_pro_forma)
      - an empty draft
      - >-
        refuses issuing a draft whose lines were all removed
        (line_limit_exceeded), the draft unchanged
      - a zero-total invoice
      - 'is paid at once, its line with it'
      - appends its zero fact
      - no batch named
      - >-
        refuses the issue when the unit has no assignment rules
        (batch_required), the draft unchanged
      - with the unit’s assignment rules
      - issues in the active batch of the recurring batch the rules choose
    imports:
      - 'const { sql } = require(''drizzle-orm'');'
      - 'const { stage } = require(''@rhythmsoftware/macroservice-testing/stage'');'
      - >-
        const { TEST_NOW } =
        require('@rhythmsoftware/macroservice-testing/execution-context');
      - >-
        const { saveBatchAssignmentSettings } =
        require('../../../../../batches/services/persistence/batch-assignment-settings');
      - >-
        const { createRecurringBatchWithFirstBatch } =
        require('../../../../../batches/services/roll/first-batch');
      - >-
        } =
        require('../../../../../products/services/persistence/product-accounting-setups');
      - >-
        const { createProduct } =
        require('../../../../../products/services/persistence/products');
      - >-
        const { createDraft, updateDraft } =
        require('../../../../services/invoices/invoice-drafts');
      - >-
        const { convertProForma, issueInvoice } =
        require('../../../../services/invoices/invoice-issue');
      - >-
        const { getInvoice } =
        require('../../../../services/invoices/invoice-reads');
      - '} = require(''../../invoices-fixtures'');'
      - "\t\t\tworld.require();"
      - "\t\t\texpect(issuing.require().first.number).to.equal('1000000');"
      - "\t\t\texpect(issuing.require().second.number).to.equal('1000001');"
      - "\t\t\tconst { first } = issuing.require();"
      - "\t\t\texpect(issuing.require().first.accounting_date).to.equal('2026-01-10');"
      - "\t\t\tconst { first } = issuing.require();"
      - "\t\t\tconst [dues] = await lineRowsOf(TENANT, issuing.require().first.id);"
      - "\t\t\t\t(await frozenSplitsOf(issuing.require().first.id))[0],"
      - "\t\t\tconst [, gala] = await lineRowsOf(TENANT, issuing.require().first.id);"
      - "\t\t\tconst { first } = issuing.require();"
      - "\t\t\tconst events = await documentEventRowsOf(TENANT, issuing.require().first.id);"
      - "\t\t\texpect(await getInvoice(ctx(), issuing.require().first.id)).to.deep.equal(issuing.require().first);"
      - "\t\t\tconst { first } = issuing.require();"
      - "\t\t\t\tconst { first } = issuing.require();"
      - "\t\t\t\tconst [dues] = changing.require().rows;"
      - "\t\t\t\texpect(changing.require().splits).to.deep.equal([null, null]);"
      - "\t\t\t\tworld.require();"
      - "\t\t\tconst { atIssue, splits } = shared.require();"
      - "\t\t\tconst { invoice, read } = shared.require();"
      - "\t\t\t\tworld.require();"
      - "\t\t\texpect(refusing.require().closed).to.deep.equal({"
      - "\t\t\texpect(refusing.require().foreign).to.deep.equal({"
      - "\t\t\texpect(refusing.require().missing).to.deep.equal({"
      - "\t\t\tconst { target, after } = refusing.require();"
      - "\t\t\tworld.require();"
      - "\t\t\tworld.require();"
      - "\t\t\tconst issued = issuingProForma.require();"
      - "\t\t\tconst issued = issuingProForma.require();"
      - "\t\t\tconst issued = issuingProForma.require();"
      - "\t\t\t\tconst issued = issuingProForma.require();"
      - "\t\t\t\tconst issued = issuingProForma.require();"
      - "\t\t\t\tconst converted = converting.require();"
      - "\t\t\t\tconst converted = converting.require();"
      - "\t\t\t\tconst converted = converting.require();"
      - "\t\t\t\tconst converted = converting.require();"
      - "\t\t\tworld.require();"
      - "\t\t\tworld.require();"
      - "\t\t\tconst issued = issuingZero.require();"
      - "\t\t\texpect((await receivableEventRowsOf(TENANT, issuingZero.require().id)).map((row) => [row.event_type, row.amount])).to.deep.equal(["
      - "\t\t\tworld.require();"
      - "\t\t\t\t\tworld.require();"
      - "\t\t\t\tconst { active, issued } = assigning.require();"
    test_double_declarations: []
---

# Issue 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 ineligible posting context blocks issue; workspace invoice numbering advances independently; account selection waits for posting; monetary position determines standing; quote recognition retains document identity. 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.
