---
id: ATS-ACC-PRD-010
scope: CAP-ACC-PRD-010
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-010-01
    title: Band ranges and percentage ceilings are validated
    level: unit
    proves:
      - BR-CAP-ACC-PRD-010-01
      - BR-CAP-ACC-PRD-010-02
    preconditions: A fixed-priced product supports quantity bands.
    steps: 'Try backward ranges, percentages above one hundred and a flat reduction of 150 minor units.'
    expected: Invalid boundaries fail and the monetary reduction succeeds.
    negative: true
    data_needs: A fixed-priced product supports quantity bands.
    bound_to:
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a percentage above
        100
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a range that ends
        before it starts, naming the row
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
        a percentage above 100
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a flat amount
        above 100, which is minor units
      - TST-ACC-PRD-066
      - TST-ACC-PRD-039
    provenance: test
    source: >-
      services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a percentage above
      100
    category: validation
    binding_scope:
      - suite: TST-ACC-PRD-066
        assertions:
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a percentage
            above 100
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a range that
            ends before it starts, naming the row
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a flat amount
            above 100, which is minor units
        provenance: test
        source: >-
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a percentage
          above 100; services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a
          range that ends before it starts, naming the row;
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
          a percentage above 100;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a flat amount
          above 100, which is minor units
      - suite: TST-ACC-PRD-039
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
            a percentage above 100
        provenance: test
        source: >-
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a percentage
          above 100; services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses a
          range that ends before it starts, naming the row;
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
          a percentage above 100;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#accepts a flat amount
          above 100, which is minor units
  - id: ATS-ACC-PRD-010-02
    title: Every band pair is checked
    level: unit
    proves:
      - BR-CAP-ACC-PRD-010-03
      - INV-DOM-ACC-PRD-001-11
    preconditions: A product supports disjoint quantity ranges.
    steps: 'Try shared endpoints, nesting and intersecting open ranges, then adjacent disjoint bands.'
    expected: Shared quantities fail and nonoverlapping adjacent bands succeed.
    negative: true
    data_needs: A product supports disjoint quantity ranges.
    bound_to:
      - >-
        services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
        overlapping bulk pricing ranges, naming the rows
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses two rules that
        overlap, naming both rows and their ranges
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#compares every pair, not
        only neighbours
      - >-
        services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds no overlap between
        ranges that only touch, the ends inclusive
      - >-
        services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#refuses overlapping bulk
        pricing ranges by the service’s rule, naming the rows
      - TST-ACC-PRD-039
      - TST-ACC-PRD-066
      - TST-ACC-PRD-027
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
      overlapping bulk pricing ranges, naming the rows
    category: invariant
    binding_scope:
      - suite: TST-ACC-PRD-039
        assertions:
          - >-
            services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
            overlapping bulk pricing ranges, naming the rows
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
          overlapping bulk pricing ranges, naming the rows;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses two rules that
          overlap, naming both rows and their ranges;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#compares every pair, not
          only neighbours; services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds
          no overlap between ranges that only touch, the ends inclusive;
          services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#refuses overlapping bulk
          pricing ranges by the service’s rule, naming the rows
      - suite: TST-ACC-PRD-066
        assertions:
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses two rules that
            overlap, naming both rows and their ranges
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#compares every pair,
            not only neighbours
          - >-
            services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds no overlap
            between ranges that only touch, the ends inclusive
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
          overlapping bulk pricing ranges, naming the rows;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses two rules that
          overlap, naming both rows and their ranges;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#compares every pair, not
          only neighbours; services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds
          no overlap between ranges that only touch, the ends inclusive;
          services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#refuses overlapping bulk
          pricing ranges by the service’s rule, naming the rows
      - suite: TST-ACC-PRD-027
        assertions:
          - >-
            services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#refuses overlapping
            bulk pricing ranges by the service’s rule, naming the rows
        provenance: test
        source: >-
          services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
          overlapping bulk pricing ranges, naming the rows;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#refuses two rules that
          overlap, naming both rows and their ranges;
          services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#compares every pair, not
          only neighbours; services-accounting:modules/products/tests/unit/rules/product-rules.spec.js#finds
          no overlap between ranges that only touch, the ends inclusive;
          services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#refuses overlapping bulk
          pricing ranges by the service’s rule, naming the rows
  - id: ATS-ACC-PRD-010-03
    title: An empty setting disables bands
    level: api
    proves:
      - BR-CAP-ACC-PRD-010-04
    preconditions: A product already has quantity bands.
    steps: Send an empty setting and then an empty array.
    expected: The setting clears bands and the array fails validation.
    negative: true
    data_needs: A product already has quantity bands.
    bound_to:
      - >-
        services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#switches every feature off
        with null
      - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112'
      - TST-ACC-PRD-027
      - TST-ACC-PRD-006
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#switches every feature off
      with null
    category: transition
    binding_scope:
      - suite: TST-ACC-PRD-027
        assertions:
          - >-
            services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#switches every feature
            off with null
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#switches every feature
          off with null;
          react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112
      - suite: TST-ACC-PRD-006
        assertions:
          - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112'
        provenance: test
        source: >-
          services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#switches every feature
          off with null;
          react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts#ACT-ACC-0112
  - id: ATS-ACC-PRD-010-04
    title: Discount collection accepts its maximum and refuses one more
    category: boundary
    level: api
    proves:
      - BR-CAP-ACC-PRD-010-04
    preconditions: A sellable fixed-priced product has a valid grid.
    steps: 'Replace with fifty disjoint bands, then try fifty-one bands.'
    expected: The maximum collection reads back in order; the larger replacement leaves it unchanged.
    negative: true
    data_needs: A sellable fixed-priced product has a valid grid.
    bound_to: []
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js#switches every feature off
      with null
    binding_scope: []
  - id: ATS-ACC-PRD-010-05
    title: Discount configuration survives a price correction
    category: interaction
    level: api
    proves:
      - BR-CAP-ACC-PRD-010-03
      - BR-CAP-ACC-PRD-008-12
    preconditions: A fixed-priced product carries two disjoint discount bands.
    steps: 'Replace its price grid while resending the bands, then submit a replacement with overlapping bands.'
    expected: >-
      The price correction retains the configured bands; the later invalid replacement preserves both grid and
      bands.
    negative: true
    data_needs: A fixed-priced product carries two disjoint discount bands.
    bound_to: []
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
      overlapping bulk pricing ranges, naming the rows;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      more than 500 prices (the bound of the grid)
    exercises:
      - CAP-ACC-PRD-010
      - CAP-ACC-PRD-008
    binding_scope: []
evidence:
  - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0112.spec.ts'
  - 'services-accounting:modules/products/db/product-bulk-pricing-rules.table.ts'
  - 'services-accounting:modules/products/services/rules/product-rules.ts'
  - 'services-accounting:modules/products/tests/e2e/product-features.e2e.spec.js'
  - >-
    services-accounting:modules/products/tests/integration/services/persistence/products.features.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: 'gap: FND-ACC-PRD-059'
  transition: covered
  validation: covered
  permission: 'gap: FND-ACC-PRD-059'
  edge: 'gap: FND-ACC-PRD-059'
  failure: 'gap: FND-ACC-PRD-059'
  boundary: covered
  concurrency: 'gap: FND-ACC-PRD-059'
  invariant: covered
  interaction: covered
coverage_notes:
  rule:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-059
  permission:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-059
  edge:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-059
  failure:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-059
  concurrency:
    reason: No dedicated scenario isolates this category for the capability.
    provenance: unknown
    source: FND-ACC-PRD-059
  remaining_obligations:
    reason: >-
      Exact maximum-size collections, percentage precision endpoints, product permissions and simultaneous
      edits need dedicated execution; purchase-time application is outside these suites. 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-059
  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-059
related_capabilities:
  - CAP-ACC-PRD-008
workflows:
  - WF-ACC-PRD-001
rule_coverage:
  - rule: BR-CAP-ACC-PRD-010-01
    scenarios:
      - ATS-ACC-PRD-010-01
  - rule: BR-CAP-ACC-PRD-010-02
    scenarios:
      - ATS-ACC-PRD-010-01
  - rule: BR-CAP-ACC-PRD-010-03
    scenarios:
      - ATS-ACC-PRD-010-02
      - ATS-ACC-PRD-010-05
  - rule: BR-CAP-ACC-PRD-010-04
    scenarios:
      - ATS-ACC-PRD-010-03
      - ATS-ACC-PRD-010-04
interaction_coverage:
  - scenario: ATS-ACC-PRD-010-05
    exercises:
      - CAP-ACC-PRD-010
      - CAP-ACC-PRD-008
    provenance: proposed
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
      overlapping bulk pricing ranges, naming the rows;
      services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
      more than 500 prices (the bound of the grid)
permission_coverage:
  - key: accounting.products.update
    where_checked: api
    scope: tenant
    scenarios: []
    finding: FND-ACC-PRD-059
    provenance: unknown
    source: >-
      rhythm-v2-docs:specs/accounting/products/planning/capabilities/CAP-ACC-PRD-010-offer-discounts-for-buying-in-quantity.md#permissions
invariant_coverage:
  - invariant: INV-DOM-ACC-PRD-001-11
    scenarios:
      - ATS-ACC-PRD-010-02
    finding: null
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#refuses
      overlapping bulk pricing ranges, naming the rows
---

# Configure discounts for quantity purchases: 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. Exact maximum-size collections, percentage precision endpoints, product permissions and simultaneous edits need dedicated execution; purchase-time application is outside these suites. Workflow edges not named by a scenario remain in this finding rather than gaining a binding from shared workflow membership.

## Interactions With Related Capabilities

Discount configuration survives a price correction. A fixed-priced product carries two disjoint discount bands. Replace its price grid while resending the bands, then submit a replacement with overlapping bands. The price correction retains the configured bands; the later invalid replacement preserves both grid and bands.
