---
id: CAP-COM-ORD-004
title: Use promotion codes before the sale
feature: FEAT-COM-ORD-01
product_area: commerce
module: orders
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes:
  - CAP-COM-PRO-020
  - CAP-COM-PRO-021
  - CAP-COM-PRO-022
  - CAP-COM-PRO-023
  - CAP-COM-CHK-005
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#A8'
  - 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#D10'
  - 'services-commerce:docs/milestones/COMM-M1-set-up-what-you-sell.md#D12'
purpose: Customers can request promotion savings while staff see which offers contribute to the reviewed sale.
outcomes:
  - Equivalent code entry produces one applied promotion.
  - Rejected offers give the customer a readable explanation.
concepts:
  - CON-COM-ORD-001
actors:
  - ACT-COM-014
  - ACT-COM-017
surfaces:
  - console
  - api
preconditions: []
postconditions: []
permissions:
  - key: commerce.checkouts.update
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-COM-ORD-004-01
    proved_by: [ATS-COM-ORD-001-19, ATS-COM-ORD-004-01, ATS-COM-ORD-005-19, ATS-COM-ORD-008-14, ATS-COM-ORD-014-19, ATS-COM-ORD-015-16, TST-COM-ORD-038]
    statement: >-
      Code matching normalizes case, spaces and compatible full-width characters, displays the configured code
      and retains the entered spelling after trimming the request text.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#applies the one code to each,
      full-width letters and an ideographic space included (D-REC-12), shown by its display code, the entered
      code kept as typed (trimmed at the boundary)
    origin: none
  - id: BR-CAP-COM-ORD-004-02
    proved_by: [ATS-COM-ORD-004-02, TST-COM-ORD-022, TST-COM-ORD-038]
    statement: Invalid promotion-code characters produce a readable validation refusal and are not stored on the cart.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkout-promotion-code-refusals.e2e.spec.js#refuses a code
      holding a NUL character, 400, where the database would refuse the text as a 500
    origin: none
  - id: BR-CAP-COM-ORD-004-03
    proved_by: [ATS-COM-ORD-004-03, TST-COM-ORD-038]
    statement: >-
      A promotion cannot apply twice through repeated entry or a different code for the same promotion; the
      refusal explains that it is already applied and enters the cart history.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#refuses the same code entered again,
      409 already_applied with the customer’s message
    origin: none
  - id: BR-CAP-COM-ORD-004-04
    proved_by: [ATS-COM-ORD-004-04, TST-COM-ORD-038]
    statement: >-
      A code with no qualifying items contributes no discount and remains in the placed snapshot as rejected
      with its reason.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#rejects the code nothing_qualifies,
      with the customer’s message, and takes nothing off
    origin: none
  - id: BR-CAP-COM-ORD-004-05
    proved_by: [ATS-COM-ORD-004-05, TST-COM-ORD-038]
    statement: >-
      A valid code that adds no savings consumes no redemption, including when earlier discounts already make
      the item free.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: 'services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#consumes no use of TENTH'
    origin: none
    note: >-
      The plan marks this promotion assumption for confirmation, and its recorded recommendation supplies the
      intent basis.
  - id: BR-CAP-COM-ORD-004-06
    proved_by: [ATS-COM-ORD-003-12, ATS-COM-ORD-004-06, ATS-COM-ORD-004-12, ATS-COM-ORD-004-13, ATS-COM-ORD-005-16, ATS-COM-ORD-015-13, TST-COM-ORD-038]
    statement: >-
      Placement records the applied promotion and code use together with the discount allocation, while
      previews consume no uses.
    kind: lifecycle
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#takes one use of the code and of the
      promotion
    origin: 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#A8'
  - id: BR-CAP-COM-ORD-004-07
    proved_by: [ATS-COM-ORD-004-07, TST-COM-ORD-037]
    statement: >-
      Eligibility-limited promotions evaluate their declared subject during preview and placement; an
      indeterminate result fails both matching and non-matching requirements.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/promotion-eligibility.e2e.spec.js#refuses it even for
      must_not_match: an indeterminate decision is not "does not match"
    origin: 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#D10'
  - id: BR-CAP-COM-ORD-004-08
    proved_by: [ATS-COM-ORD-004-08, TST-COM-ORD-038]
    statement: >-
      Portal buyers receive customer wording without staff-only explanations in their readable snapshot, while
      staff can also see operational details such as business-unit conflicts.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#keeps the staff message out of the
      price snapshot the portal member reads
    origin: none
  - id: BR-CAP-COM-ORD-004-09
    proved_by: [ATS-COM-ORD-004-09, TST-COM-ORD-038]
    statement: >-
      Removing an entered code recalculates the preview and records the removal; removal of an absent code
      returns not found.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkout-promotion-code-refusals.e2e.spec.js#answers 404 to
      the removal of a code the checkout does not carry, naming the code
    origin: none
  - id: BR-CAP-COM-ORD-004-10
    proved_by: [ATS-COM-ORD-004-10, TST-COM-ORD-020]
    statement: >-
      Automatic promotions enter pricing by priority without code entry, and an exclusive code displaces them
      only when it provides a better price.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/automatic-promotions.e2e.spec.js#lets VIP20 replace the
      automatic promotion when it leaves less to pay: $20.00
    origin: 'services-commerce:docs/milestones/COMM-M1-set-up-what-you-sell.md#D12'
    note: Catalog supplies promotion arithmetic; checkout applies the calculated result.
  - id: BR-CAP-COM-ORD-004-11
    proved_by: [ATS-COM-ORD-004-11, TST-COM-ORD-038]
    statement: A portal buyer cannot add a promotion code to a cart outside their permitted parties.
    kind: permission
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#refuses another member entering a
      code on a checkout that is not theirs, 403
    origin: none
scenarios:
  - id: SC-CAP-COM-ORD-004-01
    given: Jane’s book is priced at eighty dollars for the Member audience.
    when: Staff enter the twenty-five-percent early-bird code.
    then: The documented example reduces the book to sixty dollars and identifies the early-bird saving.
    provenance: documented
    source: 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#3. How it works for the user'
  - id: SC-CAP-COM-ORD-004-02
    given: Pat qualifies as a member.
    when: Staff enter the welcome code restricted to non-members.
    then: The code contributes no discount and the summary explains its refusal.
    provenance: documented
    source: 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#3. How it works for the user'
edge_cases: []
constraints: []
depends_on:
  - CAP-COM-ORD-003
related:
  - CAP-COM-ORD-005
  - CAP-COM-ORD-015
journeys:
  - JNY-COM-ORD-001
open_questions: []
evidence:
  - 'Examined reference: services-commerce:docs/milestones/COMM-M1-set-up-what-you-sell.md#D12'
  - 'Examined reference: services-commerce:docs/milestones/plans/COMM-M2-plan.md#3. How it works for the user'
  - 'Examined reference: services-commerce:docs/milestones/plans/COMM-M2-plan.md#A8'
  - 'Examined reference: services-commerce:docs/milestones/plans/COMM-M2-plan.md#D10'
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/automatic-promotions.e2e.spec.js#lets
    VIP20 replace the automatic promotion when it leaves less to pay: $20.00
  - >-
    Examined reference:
    services-commerce:modules/checkout/tests/e2e/checkout-promotion-code-refusals.e2e.spec.js#answers 404 to
    the removal of a code the checkout does not carry, naming the code
  - >-
    Examined reference:
    services-commerce:modules/checkout/tests/e2e/checkout-promotion-code-refusals.e2e.spec.js#refuses a code
    holding a NUL character, 400, where the database would refuse the text as a 500
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotion-eligibility.e2e.spec.js#refuses
    it even for must_not_match: an indeterminate decision is not "does not match"
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#applies the one
    code to each, full-width letters and an ideographic space included (D-REC-12), shown by its display code,
    the entered code kept as typed (trimmed at the boundary)
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#consumes no use of
    TENTH
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#keeps the staff
    message out of the price snapshot the portal member reads
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#refuses another
    member entering a code on a checkout that is not theirs, 403
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#refuses the same
    code entered again, 409 already_applied with the customer’s message
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#rejects the code
    nothing_qualifies, with the customer’s message, and takes nothing off
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/promotions.e2e.spec.js#takes one use of
    the code and of the promotion
controls: [TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-CRY-001, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-002, TC-PI-003, TC-PRV-001, TC-PRV-002]
exercised_by: [FAS-COM-ORD-01-02, FAS-COM-ORD-01-04]
suites: [TST-COM-ORD-020, TST-COM-ORD-022, TST-COM-ORD-037, TST-COM-ORD-038]
---

# Use promotion codes before the sale

## Purpose

Customers can request promotion savings while staff see which offers contribute to the reviewed sale. Refusals explain why the requested saving is unavailable.

## How It Works

The clerk enters the code supplied by the customer and requests an updated summary. Commerce matches the code and evaluates the offers against the purchase. The summary distinguishes applied savings from rejected offers. Staff explanations can include details that a portal buyer does not receive. Removing a code requests a fresh summary without that entry. Placement provides the final point at which accepted promotion uses become recorded redemptions.

## Relationships

Preview supplies eligible items and prices, while placement records the accepted redemption and voiding can release it.
