---
id: ATS-ACC-PRD-011
scope: CAP-ACC-PRD-011
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-011-01
    title: Kinds without bundling reject components
    level: unit
    proves:
      - BR-CAP-ACC-PRD-011-01
    preconditions: Donation and Legacy Products exist.
    steps: Add a component to each.
    expected: Both requests fail.
    negative: true
    data_needs: Donation and Legacy Products exist.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        components on a Donation Product
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        components on a Legacy Product
      - >-
        services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to bundled products on
        a Legacy Product
      - TST-ACC-PRD-040
      - TST-ACC-PRD-030
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      components on a Donation Product
    category: validation
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            components on a Donation Product
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            components on a Legacy Product
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          components on a Donation Product;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          components on a Legacy Product;
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to bundled products
          on a Legacy Product
      - suite: TST-ACC-PRD-030
        assertions:
          - >-
            services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to bundled
            products on a Legacy Product
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          components on a Donation Product;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          components on a Legacy Product;
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 to bundled products
          on a Legacy Product
  - id: ATS-ACC-PRD-011-02
    title: Bundle components must be valid and distinct
    level: unit
    proves:
      - BR-CAP-ACC-PRD-011-02
    preconditions: The fixtures include products from two units and an unsellable kind.
    steps: 'Try foreign-unit, unsellable, missing, self and repeated components.'
    expected: Every invalid relationship fails.
    negative: true
    data_needs: The fixtures include products from two units and an unsellable kind.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a component of another business unit, naming both units
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a component whose type is not sellable
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a component the tenant does not have
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a product including itself
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        the same component twice
      - TST-ACC-PRD-040
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      a component of another business unit, naming both units
    category: validation
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a component of another business unit, naming both units
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a component whose type is not sellable
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a component the tenant does not have
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a product including itself
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            the same component twice
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a component of another business unit, naming both units;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a component whose type is not sellable;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a component the tenant does not have;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a product including itself;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          the same component twice
  - id: ATS-ACC-PRD-011-03
    title: Retired components distinguish retained from new
    level: unit
    proves:
      - BR-CAP-ACC-PRD-011-03
    preconditions: A bundle holds a component later retired and another retired product exists.
    steps: Resave the held component and try adding the other retired product.
    expected: The held relationship remains and the new one fails.
    negative: true
    data_needs: A bundle holds a component later retired and another retired product exists.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#keeps
        a retired component the bundle already holds when a replacement resends it, and refuses a new retired
        one
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a retired component
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#keeps a retired component
        the bundle already holds
      - TST-ACC-PRD-040
      - TST-ACC-PRD-066
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#keeps
      a retired component the bundle already holds when a replacement resends it, and refuses a new retired
      one
    category: transition
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#keeps
            a retired component the bundle already holds when a replacement resends it, and refuses a new
            retired one
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a retired component
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#keeps
          a retired component the bundle already holds when a replacement resends it, and refuses a new
          retired one;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a retired component;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#keeps a retired
          component the bundle already holds
      - suite: TST-ACC-PRD-066
        assertions:
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#keeps a retired
            component the bundle already holds
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#keeps
          a retired component the bundle already holds when a replacement resends it, and refuses a new
          retired one;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a retired component;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#keeps a retired
          component the bundle already holds
  - id: ATS-ACC-PRD-011-04
    title: Indirect self-inclusion fails
    level: unit
    proves:
      - BR-CAP-ACC-PRD-011-04
    preconditions: A already includes B.
    steps: Add A to B.
    expected: The cycle is rejected.
    negative: true
    data_needs: A already includes B.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        closing a cycle: B including A when A includes B
      - >-
        services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 when a part would
        include the bundle that includes it
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds a bundle reached
        again through its components
      - TST-ACC-PRD-040
      - TST-ACC-PRD-030
      - TST-ACC-PRD-066
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      closing a cycle: B including A when A includes B
    category: invariant
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            closing a cycle: B including A when A includes B
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          closing a cycle: B including A when A includes B;
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 when a part would
          include the bundle that includes it;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds a bundle reached
          again through its components
      - suite: TST-ACC-PRD-030
        assertions:
          - >-
            services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 when a part would
            include the bundle that includes it
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          closing a cycle: B including A when A includes B;
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 when a part would
          include the bundle that includes it;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds a bundle reached
          again through its components
      - suite: TST-ACC-PRD-066
        assertions:
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds a bundle reached
            again through its components
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          closing a cycle: B including A when A includes B;
          services-accounting:modules/products/tests/e2e/products.e2e.spec.js#answers 400 when a part would
          include the bundle that includes it;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds a bundle reached
          again through its components
  - id: ATS-ACC-PRD-011-05
    title: Component allocations require explicit prices
    level: unit
    proves:
      - BR-CAP-ACC-PRD-011-05
    preconditions: An existing bundle can take a component.
    steps: Submit one component without a price and another priced zero.
    expected: The omitted allocation fails without writes and zero succeeds.
    negative: true
    data_needs: An existing bundle can take a component.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        a component sent without a price, naming it, and stores nothing (ACCT-M1 D10.1, DEF-ACT-0107)
      - 'services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a price of 0'
      - TST-ACC-PRD-040
      - TST-ACC-PRD-066
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      a component sent without a price, naming it, and stores nothing (ACCT-M1 D10.1, DEF-ACT-0107)
    category: validation
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            a component sent without a price, naming it, and stores nothing (ACCT-M1 D10.1, DEF-ACT-0107)
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a component sent without a price, naming it, and stores nothing (ACCT-M1 D10.1, DEF-ACT-0107);
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a price of 0
      - suite: TST-ACC-PRD-066
        assertions:
          - 'services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a price of 0'
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          a component sent without a price, naming it, and stores nothing (ACCT-M1 D10.1, DEF-ACT-0107);
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a price of 0
  - id: ATS-ACC-PRD-011-06
    title: Quantity-weighted allocations fit bundle prices
    level: unit
    proves:
      - BR-CAP-ACC-PRD-011-06
    preconditions: A bundle costs 5000 minor units and has member-price variants.
    steps: 'Try exact totals, quantity-driven excess and member allocations exceeding or lacking backing.'
    expected: Exact totals pass and excess allocations fail.
    negative: true
    data_needs: A bundle costs 5000 minor units and has member-price variants.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#totals
        each component price times its quantity (ACCT-M1 D12 A2)
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        component prices that add up to more than the product price, in Rhythm 1.0’s words
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        component member prices that add up to more than the product member price, in Rhythm 1.0’s words
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        component member prices when the product has no member price
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#accepts
        component prices that add up to exactly the product price
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#multiplies each price and
        member price by its quantity
      - TST-ACC-PRD-040
      - TST-ACC-PRD-066
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#totals
      each component price times its quantity (ACCT-M1 D12 A2)
    category: rule
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#totals
            each component price times its quantity (ACCT-M1 D12 A2)
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            component prices that add up to more than the product price, in Rhythm 1.0’s words
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            component member prices that add up to more than the product member price, in Rhythm 1.0’s words
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            component member prices when the product has no member price
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#accepts
            component prices that add up to exactly the product price
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#totals
          each component price times its quantity (ACCT-M1 D12 A2);
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          component prices that add up to more than the product price, in Rhythm 1.0’s words;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          component member prices that add up to more than the product member price, in Rhythm 1.0’s words;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          component member prices when the product has no member price;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#accepts
          component prices that add up to exactly the product price;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#multiplies each price
          and member price by its quantity
      - suite: TST-ACC-PRD-066
        assertions:
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#multiplies each price
            and member price by its quantity
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#totals
          each component price times its quantity (ACCT-M1 D12 A2);
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          component prices that add up to more than the product price, in Rhythm 1.0’s words;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          component member prices that add up to more than the product member price, in Rhythm 1.0’s words;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          component member prices when the product has no member price;
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#accepts
          component prices that add up to exactly the product price;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#multiplies each price
          and member price by its quantity
  - id: ATS-ACC-PRD-011-07
    title: Bundle size is bounded
    level: unit
    proves:
      - BR-CAP-ACC-PRD-011-08
    preconditions: A product can take component fixtures.
    steps: Submit fifty-one components.
    expected: The request fails its collection limit.
    negative: true
    data_needs: A product can take component fixtures.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
        more than 50 bundled products
      - TST-ACC-PRD-040
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      more than 50 bundled products
    category: boundary
    binding_scope:
      - suite: TST-ACC-PRD-040
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
            more than 50 bundled products
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
          more than 50 bundled products
  - id: ATS-ACC-PRD-011-08
    title: Console corrections preserve bundles
    level: browser
    proves:
      - BR-CAP-ACC-PRD-011-09
    preconditions: An API-configured bundle exists.
    steps: Open and save it through the console.
    expected: Bundle controls stay hidden and components remain unchanged.
    negative: false
    data_needs: An API-configured bundle exists.
    bound_to:
      - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0110.spec.ts#ACT-ACC-0110'
      - TST-ACC-PRD-005
    provenance: test
    source: 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0110.spec.ts#ACT-ACC-0110'
    category: interaction
    binding_scope:
      - suite: TST-ACC-PRD-005
        assertions:
          - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0110.spec.ts#ACT-ACC-0110'
        provenance: test
        source: 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0110.spec.ts#ACT-ACC-0110'
    exercises:
      - CAP-ACC-PRD-011
      - CAP-ACC-PRD-004
  - id: ATS-ACC-PRD-011-09
    title: Bundle retirement and setup allocation retain independent safeguards
    category: interaction
    level: api
    proves:
      - BR-CAP-ACC-PRD-011-03
      - BR-CAP-ACC-PRD-011-06
      - BR-CAP-ACC-PRD-005-02
      - BR-CAP-ACC-PRD-006-01
      - BR-CAP-ACC-PRD-016-06
    preconditions: A bundle includes an active product and has financial shares.
    steps: >-
      Retire the included product, resave the relationship, try to delete the component, and attempt setup
      shares above the bundle standard price.
    expected: >-
      The held component remains valid and protected from deletion; excessive financial shares refuse the
      setup save.
    negative: true
    data_needs: A bundle includes an active product and has financial shares.
    bound_to: []
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#keeps
      a retired component the bundle already holds when a replacement resends it, and refuses a new retired
      one; services-accounting:modules/products/services/rules/product-rules.ts:listPrice;
      services-accounting:modules/products/services/rules/product-rules.ts:bundleTotalsProblem;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#retires
      the product at version 2, changing nothing else;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      deleting a product another product bundles, 409, and keeps it;
      services-accounting:modules/products/services/rules/product-rules.ts:listPrice;
      services-accounting:modules/products/services/rules/product-rules.ts:revenueSplitsProblem
    exercises:
      - CAP-ACC-PRD-011
      - CAP-ACC-PRD-005
      - CAP-ACC-PRD-006
      - CAP-ACC-PRD-016
    binding_scope: []
evidence:
  - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0110.spec.ts'
  - 'react-console:components/accounting-config/products/product-section-types.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.e2e.spec.js'
  - 'services-accounting:modules/products/tests/integration/services/persistence/products.integration.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
coverage:
  rule: covered
  transition: covered
  validation: covered
  permission: 'gap: FND-ACC-PRD-060'
  edge: 'gap: FND-ACC-PRD-060'
  failure: 'gap: FND-ACC-PRD-060'
  boundary: covered
  concurrency: 'gap: FND-ACC-PRD-060'
  invariant: covered
  interaction: covered
coverage_notes:
  permission:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-060
  edge:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-060
  failure:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-060
  concurrency:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-060
  remaining_obligations:
    reason: >-
      Cross-product cycle races, full allocation interactions and exact maximum-size success require
      execution; existing validation does not lock the whole component graph. 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-060
  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-060
related_capabilities:
  - CAP-ACC-PRD-005
  - CAP-ACC-PRD-006
  - CAP-ACC-PRD-016
workflows:
  - WF-ACC-PRD-001
rule_coverage:
  - rule: BR-CAP-ACC-PRD-011-01
    scenarios:
      - ATS-ACC-PRD-011-01
  - rule: BR-CAP-ACC-PRD-011-02
    scenarios:
      - ATS-ACC-PRD-011-02
  - rule: BR-CAP-ACC-PRD-011-03
    scenarios:
      - ATS-ACC-PRD-011-03
      - ATS-ACC-PRD-011-09
  - rule: BR-CAP-ACC-PRD-011-04
    scenarios:
      - ATS-ACC-PRD-011-04
  - rule: BR-CAP-ACC-PRD-011-05
    scenarios:
      - ATS-ACC-PRD-011-05
  - rule: BR-CAP-ACC-PRD-011-06
    scenarios:
      - ATS-ACC-PRD-011-06
      - ATS-ACC-PRD-011-09
  - rule: BR-CAP-ACC-PRD-011-08
    scenarios:
      - ATS-ACC-PRD-011-07
  - rule: BR-CAP-ACC-PRD-011-09
    scenarios:
      - ATS-ACC-PRD-011-08
interaction_coverage:
  - scenario: ATS-ACC-PRD-011-08
    exercises:
      - CAP-ACC-PRD-011
      - CAP-ACC-PRD-004
    provenance: test
    source: 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0110.spec.ts#ACT-ACC-0110'
  - scenario: ATS-ACC-PRD-011-09
    exercises:
      - CAP-ACC-PRD-011
      - CAP-ACC-PRD-005
      - CAP-ACC-PRD-006
      - CAP-ACC-PRD-016
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#keeps
      a retired component the bundle already holds when a replacement resends it, and refuses a new retired
      one; services-accounting:modules/products/services/rules/product-rules.ts:listPrice;
      services-accounting:modules/products/services/rules/product-rules.ts:bundleTotalsProblem;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#retires
      the product at version 2, changing nothing else;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      deleting a product another product bundles, 409, and keeps it;
      services-accounting:modules/products/services/rules/product-rules.ts:listPrice;
      services-accounting:modules/products/services/rules/product-rules.ts:revenueSplitsProblem
permission_coverage:
  - key: accounting.products.update
    where_checked: api
    scope: tenant
    scenarios: []
    finding: FND-ACC-PRD-060
    provenance: unknown
    source: >-
      rhythm-v2-docs:specs/accounting/products/planning/capabilities/CAP-ACC-PRD-011-sell-products-together-as-a-bundle.md#permissions
invariant_coverage:
  - invariant: INV-DOM-ACC-PRD-001-08
    scenarios:
      - ATS-ACC-PRD-011-04
    finding: FND-ACC-PRD-060
    provenance: proposed
    source: >-
      services-accounting:modules/products/services/persistence/products.ts:assertProductRules,
      readBundleGraph
    note: >-
      The mapped scenario exercises the related behavior; the complete invariant statement still needs
      dedicated proof.
---

# Offer a collection of products as a bundle: 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. Cross-product cycle races, full allocation interactions and exact maximum-size success require execution; existing validation does not lock the whole component graph. Workflow edges not named by a scenario remain in this finding rather than gaining a binding from shared workflow membership.

## Interactions With Related Capabilities

Bundle retirement and setup allocation retain independent safeguards. A bundle includes an active product and has financial shares. Retire the included product, resave the relationship, try to delete the component, and attempt setup shares above the bundle standard price. The held component remains valid and protected from deletion; excessive financial shares refuse the setup save.
