---
id: ATS-COM-CAT-014
scope: CAP-COM-CAT-014
product_area: commerce
module: catalog
revision: 1
status: proposed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-COM-CAT-014-01
    title: Promotion kind changes respect existing codes and lifecycle
    level: api
    proves:
      - BR-CAP-COM-CAT-014-01
    preconditions: A coded draft and active automatic promotion exist
    steps: >-
      Convert the coded draft, change the active kind and add a code to the
      automatic promotion
    expected: Each prohibited operation fails
    negative: true
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        to make a draft with a code automatic, 409
        automatic_promotion_has_no_codes
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#makes
        a draft with no code automatic, and refuses the change once it is
        active, 409 kind_not_editable
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        a code, 409 automatic_promotion_has_no_codes
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
      to make a draft with a code automatic, 409
      automatic_promotion_has_no_codes
  - id: ATS-COM-CAT-014-02
    title: Offer validation rejects incompatible or excessive parts
    level: unit
    proves:
      - BR-CAP-COM-CAT-014-02
      - BR-CAP-COM-CAT-014-03
    preconditions: A promotion creation request is available
    steps: >-
      Combine parts with an order offer, supply twenty-one parts, omit
      percentage parts, exceed one hundred percent and omit per-unit currency
    expected: Each invalid offer receives a validation refusal
    negative: true
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/unit/promotions/promotion-parts-rules.spec.js#refuses
        parts together with a promotion-level kind
      - >-
        services-commerce:modules/promotions/tests/unit/promotions/promotion-parts-rules.spec.js#refuses
        twenty-one parts, 400 promotion_invalid
      - >-
        services-commerce:modules/promotions/tests/unit/promotions/promotion-parts-rules.spec.js#refuses
        percent_off as the promotion’s own kind without parts
      - >-
        services-commerce:modules/promotions/tests/unit/promotions/promotion-parts-rules.spec.js#refuses
        a percentage above 100 %, naming the part
      - >-
        services-commerce:modules/promotions/tests/unit/promotions/promotion-parts-rules.spec.js#refuses
        a per-unit part without a currency
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/unit/promotions/promotion-parts-rules.spec.js#refuses
      parts together with a promotion-level kind
  - id: ATS-COM-CAT-014-03
    title: Selling areas validate part targets
    level: api
    proves:
      - BR-CAP-COM-CAT-014-03
    preconditions: The merchandise provider is registered
    steps: Select an unknown area and a nonmerchandise product
    expected: Both part requests identify their invalid target
    negative: true
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        an area nobody offers, 400 promotion_invalid naming the part
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        a product that is not merchandise, 400 promotion_invalid naming the part
        and the product
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
      an area nobody offers, 400 promotion_invalid naming the part
  - id: ATS-COM-CAT-014-04
    title: Promotion validation returns all detected problems
    level: api
    proves:
      - BR-CAP-COM-CAT-014-04
    preconditions: 'A request contains an invalid cap, interval and currency'
    steps: Submit the request once
    expected: One refusal reports every detected issue
    negative: true
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#names
        every problem of a sloppy promotion in one 400 promotion_invalid, the
        zero cap among them (Q07, journey 0203)
      - >-
        services-commerce:modules/promotions/tests/unit/pricing/promotion-shape.spec.js#refuses
        a stated cap of zero, as the database does, and admits a cap of one and
        a minimum of zero (Q07)
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#names
      every problem of a sloppy promotion in one 400 promotion_invalid, the zero
      cap among them (Q07, journey 0203)
  - id: ATS-COM-CAT-014-05
    title: Code and automatic eligibility have different homes
    level: api
    proves:
      - BR-CAP-COM-CAT-014-05
    preconditions: Both promotion kinds can be created
    steps: Set promotion eligibility on each and include legacy scope
    expected: >-
      Code-promotion eligibility fails, automatic eligibility succeeds and
      legacy scope has no effect
    negative: true
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        a promotion-level eligibility rule on a code-claimed promotion, 400 in
        the code’s words
      - >-
        services-commerce:modules/promotions/tests/unit/promotions/promotion-parts-rules.spec.js#admits
        the rule on an automatic promotion
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#accepts
        a scope of any members and ignores it: never an error, nothing stored
        (Revision 2)
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
      a promotion-level eligibility rule on a code-claimed promotion, 400 in the
      code’s words
  - id: ATS-COM-CAT-014-06
    title: Audience combination defaults on and can be changed
    level: api
    proves:
      - BR-CAP-COM-CAT-014-06
    preconditions: A new promotion omits the combination setting
    steps: Create it and replace the setting
    expected: The default is enabled and the replacement is readable
    negative: false
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#defaults
        "combine with audience prices" to yes when the body leaves it out (D12)
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#changes
        "combine with audience prices" and reads it back (D12)
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#defaults
      "combine with audience prices" to yes when the body leaves it out (D12)
  - id: ATS-COM-CAT-014-07
    title: Lifecycle transitions validate states and retain reasons
    level: api
    proves:
      - BR-CAP-COM-CAT-014-09
      - AS-JNY-COM-CAT-004-01
    preconditions: Fixtures cover each promotion state
    steps: Exercise valid transitions and unsupported moves
    expected: Valid moves appear in history and invalid moves identify their states
    negative: true
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#pauses
        an active promotion, resumes a paused one, and activates a paused one
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#ends
        an active promotion, and archives an ended one and a draft
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#names
        both states in the refusal
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#records
        every transition on the history, newest first, with the reason given
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#pauses
      an active promotion, resumes a paused one, and activates a paused one
  - id: ATS-COM-CAT-014-08
    title: Activation requires operational configuration
    level: api
    proves:
      - BR-CAP-COM-CAT-014-10
    preconditions: 'Drafts lack active codes, parts or selected eligibility rules'
    steps: Attempt activation of each
    expected: Each refusal identifies its missing prerequisite
    negative: true
    data_needs: none
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        to activate a coded promotion with no active code, 409 no_active_code
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        to activate a promotion whose parts were removed, 409 offer_required
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
        activating an effect with no rule chosen yet, 409
        eligibility_rule_required
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions.e2e.spec.js#refuses
      to activate a coded promotion with no active code, 409 no_active_code
  - id: ATS-COM-CAT-014-09
    title: Launch authority is independent of preparation authority
    level: api
    proves:
      - BR-CAP-COM-CAT-014-11
      - AS-JNY-COM-CAT-004-02
    preconditions: Separate callers hold manage and activate permissions
    steps: 'Attempt preparation, pause, activation and resumption'
    expected: >-
      Management cannot launch or resume, and activation authority alone cannot
      pause
    negative: true
    data_needs: Personas by key
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions-security.e2e.spec.js#cannot
        activate a draft, 403 permission_missing
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions-security.e2e.spec.js#cannot
        resume a paused promotion, 403 permission_missing
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions-security.e2e.spec.js#activates
        a draft and resumes a paused promotion
      - >-
        services-commerce:modules/promotions/tests/e2e/promotions-security.e2e.spec.js#cannot
        pause a promotion, 403 permission_missing
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotions-security.e2e.spec.js#cannot
      activate a draft, 403 permission_missing
  - id: ATS-COM-CAT-014-10
    title: Console actions reflect the separate launch grant
    level: unit
    proves:
      - BR-CAP-COM-CAT-014-11
    preconditions: 'Read-only, coordinator and administrator fixtures exist'
    steps: Render promotion actions for each
    expected: >-
      Coordinators receive preparation and pause actions without Activate or
      Resume
    negative: false
    data_needs: none
    bound_to:
      - >-
        react-console:components/commerce/__tests__/permission-gating.test.tsx#lets
        a coordinator prepare and pause but not launch or resume
        (commerce.promotions.activate)
      - >-
        react-console:components/commerce/__tests__/permission-gating.test.tsx#offers
        a read-only person no pencil and no lifecycle
    provenance: test
    source: >-
      react-console:components/commerce/__tests__/permission-gating.test.tsx#lets
      a coordinator prepare and pause but not launch or resume
      (commerce.promotions.activate)
  - id: ATS-COM-CAT-014-11
    title: Used promotions retain economic terms while allowing maintenance
    level: api
    proves:
      - BR-CAP-COM-CAT-014-12
    preconditions: A promotion already has redemptions
    steps: 'Change its benefit, extend dates, change limits and duplicate after ending'
    expected: >-
      Locked benefit changes fail; permitted maintenance saves and the duplicate
      is editable
    negative: true
    data_needs: Planted redemptions
    bound_to:
      - >-
        services-commerce:modules/promotions/tests/e2e/promotion-redemptions.e2e.spec.js#refuses
        the new benefit value, 409 promotion_locked
      - >-
        services-commerce:modules/promotions/tests/e2e/promotion-redemptions.e2e.spec.js#names
        the column and offers to end the promotion and duplicate it
      - >-
        services-commerce:modules/promotions/tests/e2e/promotion-redemptions.e2e.spec.js#extends
        the window
      - >-
        services-commerce:modules/promotions/tests/e2e/promotion-redemptions.e2e.spec.js#changes
        the benefit value of its duplicate, a draft never redeemed
    provenance: test
    source: >-
      services-commerce:modules/promotions/tests/e2e/promotion-redemptions.e2e.spec.js#refuses
      the new benefit value, 409 promotion_locked
  - id: ATS-COM-CAT-014-12
    title: Promotion view reflects a coordinator’s saved edit
    level: browser
    proves:
      - AS-JNY-COM-CAT-004-03
    preconditions: The browser uses mocked promotions
    steps: 'Create a category offer, view it and change its benefit to fifteen percent'
    expected: The view displays the revised offer
    negative: false
    data_needs: Console mock
    bound_to:
      - >-
        react-console:e2e/mock/promotions.mock.spec.ts#a new promotion is
        created for a category, shown on its view, and an edit to 15% comes back
        to the view
    provenance: test
    source: >-
      react-console:e2e/mock/promotions.mock.spec.ts#a new promotion is created
      for a category, shown on its view, and an edit to 15% comes back to the
      view
evidence:
  - 'Pinned service inputs: test sources in scenario bindings'
  - 'Pinned console inputs: console sources in scenario bindings'
---

# Prepare and control a promotion’s lifecycle: Acceptance Test Set

## Coverage

| Rule | Proof Scenario or Gap |
|---|---|
| `BR-CAP-COM-CAT-014-01` | `ATS-COM-CAT-014-01` |
| `BR-CAP-COM-CAT-014-02` | `ATS-COM-CAT-014-02` |
| `BR-CAP-COM-CAT-014-03` | `ATS-COM-CAT-014-02`, `ATS-COM-CAT-014-03` |
| `BR-CAP-COM-CAT-014-04` | `ATS-COM-CAT-014-04` |
| `BR-CAP-COM-CAT-014-05` | `ATS-COM-CAT-014-05` |
| `BR-CAP-COM-CAT-014-06` | `ATS-COM-CAT-014-06` |
| `BR-CAP-COM-CAT-014-09` | `ATS-COM-CAT-014-07` |
| `BR-CAP-COM-CAT-014-10` | `ATS-COM-CAT-014-08` |
| `BR-CAP-COM-CAT-014-11` | `ATS-COM-CAT-014-09`, `ATS-COM-CAT-014-10` |
| `BR-CAP-COM-CAT-014-12` | `ATS-COM-CAT-014-11` |
