---
id: ATS-ACC-PRD-004
scope: CAP-ACC-PRD-004
product_area: accounting
module: products
revision: 3
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-ACC-PRD-004-01
    title: Stale corrections preserve the latest version
    level: api
    proves:
      - BR-CAP-ACC-PRD-004-01
    preconditions: A product has already advanced to version two.
    steps: Replace it using version one.
    expected: The stale request fails and version two remains intact.
    negative: true
    data_needs: A product has already advanced to version two.
    bound_to:
      - 'services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version'
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a replacement at a version the product has moved on from
      - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0114.spec.ts#ACT-ACC-0114'
      - TST-ACC-PRD-030
      - TST-ACC-PRD-040
      - TST-ACC-PRD-007
    provenance: test
    source: 'services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version'
    category: concurrency
    binding_scope:
      - suite: TST-ACC-PRD-030
        assertions:
          - 'services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version'
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a replacement at a version the product has moved on from;
          react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0114.spec.ts#ACT-ACC-0114
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a replacement at a version the product has moved on from
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a replacement at a version the product has moved on from;
          react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0114.spec.ts#ACT-ACC-0114
      - suite: TST-ACC-PRD-007
        assertions:
          - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0114.spec.ts#ACT-ACC-0114'
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a replacement at a version the product has moved on from;
          react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0114.spec.ts#ACT-ACC-0114
  - id: ATS-ACC-PRD-004-02
    title: Replacement retains only the intended exception
    level: unit
    proves:
      - BR-CAP-ACC-PRD-004-02
    preconditions: 'A product carries optional values, collections and owned confirmation content.'
    steps: Replace it with new collections while omitting optional settings and confirmation configuration.
    expected: 'Omitted ordinary values clear, collections are replaced and confirmation content remains unchanged.'
    negative: false
    data_needs: 'A product carries optional values, collections and owned confirmation content.'
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#keeps
        the confirmation e-mail and its template
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#answers
        the replaced record at version 2, the cleared fields left out and both collections replaced
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#answers
        the new features, each list replaced as a whole and the template merged
      - TST-ACC-PRD-039
      - TST-ACC-PRD-040
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#keeps
      the confirmation e-mail and its template
    category: rule
    binding_scope:
      - suite: TST-ACC-PRD-039
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#keeps
            the confirmation e-mail and its template
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#answers
            the new features, each list replaced as a whole and the template merged
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#keeps
          the confirmation e-mail and its template;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#answers
          the replaced record at version 2, the cleared fields left out and both collections replaced;
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#answers
          the new features, each list replaced as a whole and the template merged
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#answers
            the replaced record at version 2, the cleared fields left out and both collections replaced
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#keeps
          the confirmation e-mail and its template;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#answers
          the replaced record at version 2, the cleared fields left out and both collections replaced;
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#answers
          the new features, each list replaced as a whole and the template merged
  - id: ATS-ACC-PRD-004-03
    title: Replacement cannot change ownership coordinates
    level: api
    proves:
      - BR-CAP-ACC-PRD-004-03
    preconditions: An existing product has a stable kind and unit.
    steps: 'Submit another body identity, another unit and another kind separately.'
    expected: Each change is rejected and the original coordinates remain.
    negative: true
    data_needs: An existing product has a stable kind and unit.
    bound_to:
      - >-
        services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to a body whose id is
        not the path’s: an id never changes
      - >-
        services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a change of
        business unit
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a change of type, leaving the product unchanged
      - TST-ACC-PRD-030
      - TST-ACC-PRD-040
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to a body whose id is
      not the path’s: an id never changes
    category: validation
    binding_scope:
      - suite: TST-ACC-PRD-030
        assertions:
          - >-
            services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to a body whose id
            is not the path’s: an id never changes
          - >-
            services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a change of
            business unit
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to a body whose id
          is not the path’s: an id never changes;
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a change of
          business unit;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a change of type, leaving the product unchanged
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a change of type, leaving the product unchanged
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to a body whose id
          is not the path’s: an id never changes;
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a change of
          business unit;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a change of type, leaving the product unchanged
  - id: ATS-ACC-PRD-004-04
    title: Retired records require reactivation
    level: api
    proves:
      - BR-CAP-ACC-PRD-004-04
    preconditions: A product is retired.
    steps: Attempt a name correction.
    expected: The refusal instructs the caller to reactivate first.
    negative: true
    data_needs: A product is retired.
    bound_to:
      - >-
        services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to replacing a retired
        product
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        replacing a retired product
      - TST-ACC-PRD-030
      - TST-ACC-PRD-040
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to replacing a retired
      product
    category: transition
    binding_scope:
      - suite: TST-ACC-PRD-030
        assertions:
          - >-
            services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to replacing a
            retired product
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to replacing a
          retired product;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          replacing a retired product
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            replacing a retired product
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to replacing a
          retired product;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          replacing a retired product
  - id: ATS-ACC-PRD-004-05
    title: Unlocking must be explicit
    level: unit
    proves:
      - BR-CAP-ACC-PRD-004-05
    preconditions: A product has an update-and-delete lock.
    steps: 'Replace with the lock omitted, repeated and explicitly cleared.'
    expected: The first two fail; clearing the lock allows replacement and later deletion.
    negative: true
    data_needs: A product has an update-and-delete lock.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a locked product replaced without stating sys_locked, or with the same lock
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#unlocks
        a locked product replaced with sys_locked null, then edits and deletes it
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a replacement that
        omits sys_locked or resends the lock, naming the lock
      - >-
        services-accounting:modules/products/tests/unit/api/products-api-mapper.spec.js#keeps an explicit null
        on a product replacement
      - TST-ACC-PRD-040
      - TST-ACC-PRD-066
      - TST-ACC-PRD-044
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      a locked product replaced without stating sys_locked, or with the same lock
    category: permission
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a locked product replaced without stating sys_locked, or with the same lock
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#unlocks
            a locked product replaced with sys_locked null, then edits and deletes it
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a locked product replaced without stating sys_locked, or with the same lock;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#unlocks
          a locked product replaced with sys_locked null, then edits and deletes it;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a replacement
          that omits sys_locked or resends the lock, naming the lock;
          services-accounting:modules/products/tests/unit/api/products-api-mapper.spec.js#keeps an explicit
          null on a product replacement
      - suite: TST-ACC-PRD-066
        assertions:
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a replacement
            that omits sys_locked or resends the lock, naming the lock
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a locked product replaced without stating sys_locked, or with the same lock;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#unlocks
          a locked product replaced with sys_locked null, then edits and deletes it;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a replacement
          that omits sys_locked or resends the lock, naming the lock;
          services-accounting:modules/products/tests/unit/api/products-api-mapper.spec.js#keeps an explicit
          null on a product replacement
      - suite: TST-ACC-PRD-044
        assertions:
          - >-
            services-accounting:modules/products/tests/unit/api/products-api-mapper.spec.js#keeps an explicit
            null on a product replacement
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a locked product replaced without stating sys_locked, or with the same lock;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#unlocks
          a locked product replaced with sys_locked null, then edits and deletes it;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a replacement
          that omits sys_locked or resends the lock, naming the lock;
          services-accounting:modules/products/tests/unit/api/products-api-mapper.spec.js#keeps an explicit
          null on a product replacement
  - id: ATS-ACC-PRD-004-06
    title: Failed replacement restores collections
    level: unit
    proves:
      - BR-CAP-ACC-PRD-004-06
      - INV-DOM-ACC-PRD-001-07
    preconditions: A product contains prices and components.
    steps: Cause a validation refusal and database failures after collection deletion.
    expected: Every attempt leaves the original collections readable.
    negative: true
    data_needs: A product contains prices and components.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
        the old prices and components when a rule refuses the replacement
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
        the old prices and components when a new price is refused by the database after the old ones were
        removed
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
        the old prices and components when a new component is refused by the database after the old ones were
        removed
      - TST-ACC-PRD-040
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
      the old prices and components when a rule refuses the replacement
    category: failure
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
            the old prices and components when a rule refuses the replacement
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
            the old prices and components when a new price is refused by the database after the old ones were
            removed
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
            the old prices and components when a new component is refused by the database after the old ones
            were removed
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
          the old prices and components when a rule refuses the replacement;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
          the old prices and components when a new price is refused by the database after the old ones were
          removed;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
          the old prices and components when a new component is refused by the database after the old ones
          were removed
  - id: ATS-ACC-PRD-004-07
    title: Readers cannot correct products
    level: api
    proves:
      - BR-CAP-ACC-PRD-004-08
    preconditions: A caller has only product read authority.
    steps: Submit a replacement.
    expected: The request is denied without changing the product.
    negative: true
    data_needs: A caller has only product read authority.
    bound_to:
      - >-
        services-accounting:modules/products/tests/e2e/products-security.e2e.spec.js#is refused replacing a
        product
      - TST-ACC-PRD-028
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products-security.e2e.spec.js#is refused replacing a
      product
    category: permission
    binding_scope:
      - suite: TST-ACC-PRD-028
        assertions:
          - >-
            services-accounting:modules/products/tests/e2e/products-security.e2e.spec.js#is refused replacing
            a product
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/products-security.e2e.spec.js#is refused replacing a
          product
  - id: ATS-ACC-PRD-004-08
    title: The API disregards submitted derived values
    level: api
    proves:
      - BR-CAP-ACC-PRD-004-09
    preconditions: Workspace A owns the product.
    steps: 'Send workspace B in the body, then resend altered derived fields from A.'
    expected: The workspace mismatch fails and derived fields do not replace server values.
    negative: true
    data_needs: Workspace A owns the product.
    bound_to: []
    provenance: proposed
    source: FND-ACC-PRD-022
    category: boundary
    binding_scope: []
  - id: ATS-ACC-PRD-004-09
    title: Console edits retain hidden feature values
    level: browser
    proves:
      - BR-CAP-ACC-PRD-004-10
    preconditions: >-
      API configuration supplies quantity bands, a purchase ceiling, task instructions, a cancellation fee and
      notes.
    steps: Rename it in the editor and deactivate it from the list.
    expected: Those configured values remain unchanged through both writes.
    negative: false
    data_needs: >-
      API configuration supplies quantity bands, a purchase ceiling, task instructions, a cancellation fee and
      notes.
    bound_to:
      - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112'
      - TST-ACC-PRD-006
    provenance: test
    source: 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112'
    category: interaction
    binding_scope:
      - suite: TST-ACC-PRD-006
        assertions:
          - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112'
        provenance: test
        source: 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112'
    exercises:
      - CAP-ACC-PRD-004
      - CAP-ACC-PRD-010
      - CAP-ACC-PRD-012
      - CAP-ACC-PRD-013
  - id: ATS-ACC-PRD-004-10
    title: Editors exercise every supported product lock change
    category: transition
    level: api
    proves:
      - BR-CAP-ACC-PRD-004-05
    preconditions: An editor reads the current product before each lock change.
    steps: >-
      Save unlocked to delete-only and combined protection; save delete-only to unlocked and combined;
      explicitly reduce combined to each weaker setting.
    expected: >-
      Each supported replacement advances the version and leaves the requested protection; omission of
      combined protection refuses the replacement.
    negative: true
    data_needs: An editor reads the current product before each lock change.
    bound_to: []
    provenance: proposed
    source: >-
      rhythm-v2-docs:specs/accounting/products/design/state-models/STATE-ACC-PRD-002-record-lock.md#transitions;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      a locked product replaced without stating sys_locked, or with the same lock
    binding_scope: []
  - id: ATS-ACC-PRD-004-11
    title: Catalogue corrections and accounting use separate saved versions
    category: interaction
    level: api
    proves:
      - BR-CAP-ACC-PRD-004-01
      - BR-CAP-ACC-PRD-005-03
      - BR-CAP-ACC-PRD-008-12
      - BR-CAP-ACC-PRD-015-07
    preconditions: 'A product has a setup, a valid grid and an independently read version for each record.'
    steps: >-
      Retire and reactivate it, replace the grid at the latest product version, then update the setup using
      its unchanged setup version.
    expected: >-
      The grid and lifecycle use the product version; the independent setup save succeeds without overwriting
      the product.
    negative: true
    data_needs: 'A product has a setup, a valid grid and an independently read version for each record.'
    bound_to: []
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#reactivates
      an inactive product;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      more than 500 prices (the bound of the grid);
      services-accounting:modules/products/tests/e2e/product-accounting.e2e.spec.js#answers 400 to a
      replacement without the version read
    exercises:
      - CAP-ACC-PRD-004
      - CAP-ACC-PRD-005
      - CAP-ACC-PRD-008
      - CAP-ACC-PRD-015
    binding_scope: []
evidence:
  - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts'
  - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0114.spec.ts'
  - 'react-console:lib/accounting-config/product-form.ts'
  - 'services-accounting:modules/products/aws/api/products-api-mapper.ts'
  - 'services-accounting:modules/products/aws/api/products.routes.ts'
  - 'services-accounting:modules/products/services/persistence/products.ts'
  - 'services-accounting:modules/products/services/rules/product-rules.ts'
  - 'services-accounting:modules/products/tests/e2e/products-security.e2e.spec.js'
  - 'services-accounting:modules/products/tests/e2e/products.e2e.spec.js'
  - >-
    services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js
  - 'services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js'
  - 'services-accounting:modules/products/tests/unit/api/products-api-mapper.spec.js'
  - 'services-accounting:modules/products/tests/unit/rules/product-rules.spec.js'
  - >-
    rhythm-v2-docs:specs/accounting/products/design/workflows/WF-ACC-PRD-001-create-or-replace-a-product-with-its-owned-settings.md
  - >-
    rhythm-v2-docs:specs/accounting/products/design/workflows/WF-ACC-PRD-002-save-a-product-and-its-accounting-setup-from.md
coverage:
  rule: covered
  transition: covered
  validation: covered
  permission: covered
  edge: 'gap: FND-ACC-PRD-053'
  failure: covered
  boundary: covered
  concurrency: covered
  invariant: 'gap: FND-ACC-PRD-053'
  interaction: covered
coverage_notes:
  edge:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-053
  invariant:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-053
  remaining_obligations:
    reason: >-
      Simultaneous product and setup transactions, every lock reduction, all hidden availability fields
      through list status writes and derived-value input require dedicated proof. Workflow edges not named by
      a scenario remain in this finding rather than gaining a binding from shared workflow membership.
    provenance: unknown
    source: FND-ACC-PRD-053
  meaning:
    reason: >-
      Covered means a specification scenario exists in this category. Executable coverage and run results
      remain independent.
    provenance: documented
    source: 'rhythm-v2-docs:specs/CONVENTIONS.md#9.10 Capability Test Specification (`ATS`, under `design/tests/`)'
coverage_findings:
  - FND-ACC-PRD-053
related_capabilities:
  - CAP-ACC-PRD-005
  - CAP-ACC-PRD-008
  - CAP-ACC-PRD-015
workflows:
  - WF-ACC-PRD-001
  - WF-ACC-PRD-002
rule_coverage:
  - rule: BR-CAP-ACC-PRD-004-01
    scenarios:
      - ATS-ACC-PRD-004-01
      - ATS-ACC-PRD-004-11
  - rule: BR-CAP-ACC-PRD-004-02
    scenarios:
      - ATS-ACC-PRD-004-02
  - rule: BR-CAP-ACC-PRD-004-03
    scenarios:
      - ATS-ACC-PRD-004-03
  - rule: BR-CAP-ACC-PRD-004-04
    scenarios:
      - ATS-ACC-PRD-004-04
  - rule: BR-CAP-ACC-PRD-004-05
    scenarios:
      - ATS-ACC-PRD-004-05
      - ATS-ACC-PRD-004-10
  - rule: BR-CAP-ACC-PRD-004-06
    scenarios:
      - ATS-ACC-PRD-004-06
  - rule: BR-CAP-ACC-PRD-004-08
    scenarios:
      - ATS-ACC-PRD-004-07
  - rule: BR-CAP-ACC-PRD-004-09
    scenarios:
      - ATS-ACC-PRD-004-08
  - rule: BR-CAP-ACC-PRD-004-10
    scenarios:
      - ATS-ACC-PRD-004-09
interaction_coverage:
  - scenario: ATS-ACC-PRD-004-09
    exercises:
      - CAP-ACC-PRD-004
      - CAP-ACC-PRD-010
      - CAP-ACC-PRD-012
      - CAP-ACC-PRD-013
    provenance: test
    source: 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112'
  - scenario: ATS-ACC-PRD-004-11
    exercises:
      - CAP-ACC-PRD-004
      - CAP-ACC-PRD-005
      - CAP-ACC-PRD-008
      - CAP-ACC-PRD-015
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 409 to a stale version;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#reactivates
      an inactive product;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      more than 500 prices (the bound of the grid);
      services-accounting:modules/products/tests/e2e/product-accounting.e2e.spec.js#answers 400 to a
      replacement without the version read
state_coverage:
  - model: STATE-ACC-PRD-002
    from: unlocked
    to: delete-locked
    scenarios:
      - ATS-ACC-PRD-004-10
    finding: null
    provenance: proposed
    source: 'services-accounting:modules/products/aws/api/products-api-mapper.ts:toInput'
  - model: STATE-ACC-PRD-002
    from: unlocked
    to: update-and-delete-locked
    scenarios:
      - ATS-ACC-PRD-004-10
    finding: null
    provenance: proposed
    source: 'services-accounting:modules/products/aws/api/products-api-mapper.ts:toInput'
  - model: STATE-ACC-PRD-002
    from: delete-locked
    to: unlocked
    scenarios:
      - ATS-ACC-PRD-004-10
    finding: null
    provenance: proposed
    source: 'services-accounting:modules/products/services/rules/product-rules.ts:lockedReplaceProblem'
  - model: STATE-ACC-PRD-002
    from: delete-locked
    to: update-and-delete-locked
    scenarios:
      - ATS-ACC-PRD-004-10
    finding: null
    provenance: proposed
    source: 'services-accounting:modules/products/services/rules/product-rules.ts:lockedReplaceProblem'
  - model: STATE-ACC-PRD-002
    from: update-and-delete-locked
    to: unlocked
    scenarios:
      - ATS-ACC-PRD-004-10
    finding: null
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#unlocks
      a locked product replaced with sys_locked null, then edits and deletes it
  - model: STATE-ACC-PRD-002
    from: update-and-delete-locked
    to: delete-locked
    scenarios:
      - ATS-ACC-PRD-004-10
    finding: null
    provenance: proposed
    source: 'services-accounting:modules/products/services/rules/product-rules.ts:lockedReplaceProblem'
permission_coverage:
  - key: accounting.products.update
    where_checked: api
    scope: tenant
    scenarios:
      - ATS-ACC-PRD-004-05
      - ATS-ACC-PRD-004-07
    finding: null
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      a locked product replaced without stating sys_locked, or with the same lock;
      services-accounting:modules/products/tests/e2e/products-security.e2e.spec.js#is refused replacing a
      product
invariant_coverage:
  - invariant: INV-DOM-ACC-PRD-001-07
    scenarios:
      - ATS-ACC-PRD-004-06
    finding: null
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
      the old prices and components when a new price is refused by the database after the old ones were
      removed
---

# Save product corrections against the version read: Capability Test Specification

## Coverage

The frontmatter lists a proof scenario or finding for every required rule and records each applicable category. Scenario coverage describes what to check; bindings identify the assertions that already exist, and execution results remain unverified. Simultaneous product and setup transactions, every lock reduction, all hidden availability fields through list status writes and derived-value input require dedicated proof. Workflow edges not named by a scenario remain in this finding rather than gaining a binding from shared workflow membership.

## Interactions With Related Capabilities

Catalogue corrections and accounting use separate saved versions. A product has a setup, a valid grid and an independently read version for each record. Retire and reactivate it, replace the grid at the latest product version, then update the setup using its unchanged setup version. The grid and lifecycle use the product version; the independent setup save succeeds without overwriting the product.
