---
id: CAP-COM-CAT-007
title: Manage merchandise selections and sale availability
feature: FEAT-COM-CAT-02
product_area: commerce
module: catalog
revision: 1
status: draft
product_intent: proposed
implementation_evidence: partially-observed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-commerce:docs/milestones/walkthroughs/COMM-M1-handoff.md#What
    changed after your review (6)
  - >-
    services-commerce:docs/milestones/COMM-M1b-promotions-and-codes.md#Decisions
    (R6)
purpose: >-
  Administrators describe merchandise options while Commerce assesses whether
  requested lines can proceed.
outcomes:
  - >-
    Merchandise preparation retains atomic persistence with its Accounting
    product.
  - >-
    A buyer is does not sold stock that is not there except when backorders are
    allowed.
concepts:
  - CON-COM-CAT-003
actors:
  - ACT-COM-001
  - ACT-COM-004
  - ACT-COM-007
surfaces:
  - console
  - api
permissions:
  - key: commerce.merchandise.read
    where_checked: api
    scope: tenant
  - key: commerce.merchandise.create
    where_checked: api
    scope: tenant
  - key: commerce.merchandise.update
    where_checked: api
    scope: tenant
  - key: commerce.merchandise.delete
    where_checked: api
    scope: tenant
  - key: commerce.merchandise.bulk
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-COM-CAT-007-01
    proved_by: [ATS-COM-CAT-007-01, TST-COM-CAT-044, TST-COM-CAT-050]
    statement: >-
      Commerce permits one merchandise extension for an existing Accounting
      merchandise product and refuses unknown products, other product types and
      duplicate extensions.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/e2e/merchandise.e2e.spec.js#answers
      400 to a product the tenant has not, a selection off the sets, a SKU in
      use and a second extension, naming the rule
    origin: none
  - id: BR-CAP-COM-CAT-007-02
    proved_by: [ATS-COM-CAT-007-02, TST-COM-CAT-050]
    statement: >-
      Merchandise and Accounting product creation or replacement commit
      together, rolling back both when either validation fails.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/persistence/merchandise-products.queries.integration.spec.js#leaves
      no product when the database refuses the extension row
    origin: none
  - id: BR-CAP-COM-CAT-007-03
    proved_by: [ATS-COM-CAT-007-02, TST-COM-CAT-050]
    statement: >-
      Accounting refuses a merchandise price grid without a Standard price in
      the product’s base currency.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/persistence/merchandise-products.queries.integration.spec.js#refuses
      a grid without a standard price 400 with Accounting’s rule
    origin: COMM-M1 D6 (docs/milestones/COMM-M1-set-up-what-you-sell.md)
  - id: BR-CAP-COM-CAT-007-04
    proved_by: [ATS-COM-CAT-007-03, TST-COM-CAT-044, TST-COM-CAT-051]
    statement: >-
      Retirement changes the Accounting product’s status while retaining its
      merchandise extension, and a repeated retirement is refused.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/e2e/merchandise.e2e.spec.js#retires
      the product at its version, keeping the extension; a second retirement is
      409
    origin: none
  - id: BR-CAP-COM-CAT-007-05
    proved_by: [ATS-COM-CAT-007-03, TST-COM-CAT-044, TST-COM-CAT-051]
    statement: >-
      Stock references to merchandise variants prevent deletion of the
      merchandise extension.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/persistence/merchandise.integration.spec.js#refuses
      to delete an extension whose SKUs inventory items hold (409), deletes one
      that has none, and answers 404 for one the tenant has not
    origin: none
  - id: BR-CAP-COM-CAT-007-06
    proved_by: [ATS-COM-CAT-007-04, TST-COM-CAT-046, TST-COM-CAT-051]
    statement: >-
      Variant SKUs are unique within the workspace, each selection supplies
      exactly one permitted value per variant option set, and selections cannot
      repeat within a product.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/db/merchandise-tables.table.integration.spec.js#refuses
      one SKU twice in a tenant (merchandise_variants_sku_uq) and one selection
      twice for a product (merchandise_variants_selection_uq), while another
      tenant may use the SKU
    origin: none
  - id: BR-CAP-COM-CAT-007-07
    proved_by: [ATS-COM-CAT-007-12]
    statement: >-
      Replacement retains variant identities for retained SKUs and removes
      omitted SKUs, preserving inventory references to the retained variants.
    kind: invariant
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/persistence/merchandise.integration.spec.js#replaces
      the extension at its version: the kept SKU keeps its id, a new SKU is
      added, a dropped one goes
    origin: none
  - id: BR-CAP-COM-CAT-007-08
    proved_by: [ATS-COM-CAT-007-04, TST-COM-CAT-046, TST-COM-CAT-051]
    statement: >-
      Option sets distinguish variant values from modifiers: modifiers must use
      permitted values, variant sets have no modifier options, and empty sets
      are refused.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/persistence/merchandise.integration.spec.js#refuses
      a modifier option off the set’s values, a variant set with options, and an
      empty set, 400
    origin: none
  - id: BR-CAP-COM-CAT-007-09
    proved_by: [ATS-COM-CAT-007-05, TST-COM-CAT-052]
    statement: >-
      Availability compares requested quantity with tracked stock: sufficient
      stock above the threshold is available, a smaller remainder is limited,
      and shortfalls either become permitted backorders or refusals; untracked
      stock remains available.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/unit/participant/merchandise-availability.spec.js#is
      unavailable with what is left when stock is short and the product
      prohibits backorders: the line is refused
    origin: none
  - id: BR-CAP-COM-CAT-007-10
    proved_by: [ATS-COM-CAT-007-06, TST-COM-CAT-053]
    statement: >-
      Merchandise lines reject notes exceeding two hundred counted characters
      and selections that identify no variant.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/unit/participant/merchandise-payload.spec.js#limits
      a note to 200 characters, counted as the person sees them: an emoji is
      one, so 201 characters are refused and 200 are not (COM-ACC-0015)
    origin: none
  - id: BR-CAP-COM-CAT-007-11
    proved_by: [ATS-COM-CAT-007-07, ATS-COM-CAT-007-11]
    statement: >-
      The isolated pricing engine uses supplied variant prices and modifiers,
      but ordinary product loading supplies empty lists and does not connect
      those adjustments to quotes.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-commerce:modules/pricing/services/persistence/catalog-products.ts:toCatalogProduct
    origin: none
    note: >-
      The engine’s catalog read returns no variants and no modifiers at the
      pinned commit, so this applies exclusively in the engine’s unit tests.
  - id: BR-CAP-COM-CAT-007-12
    proved_by: [ATS-COM-CAT-007-08, TST-COM-CAT-044]
    statement: >-
      Merchandise reads require read permission, writes require administrator
      permissions, and the merchandise contract rejects custom fields.
    kind: permission
    enforced_at:
      - api
      - console
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/e2e/merchandise.e2e.spec.js#lets
      a reader list and read, and refuses it every write 403 permission_missing,
      leaving the records as they were
    origin: none
  - id: BR-CAP-COM-CAT-007-13
    proved_by: [ATS-COM-CAT-006-16, ATS-COM-CAT-007-10]
    statement: >-
      Staff manage merchandise and its listing in one item screen, and the
      former listing address redirects to that item.
    kind: policy
    enforced_at:
      - console
    must_prove: false
    provenance: test
    source: >-
      react-console:e2e/mock/merchandise.mock.spec.ts#the old Listings address
      opens the same item
    origin: >-
      COMM-M1 walkthrough ruling R1
      (docs/milestones/walkthroughs/COMM-M1-handoff.md)
  - id: BR-CAP-COM-CAT-007-14
    proved_by: [ATS-COM-CAT-007-09]
    statement: >-
      A full lock prevents replacing a merchandise extension, and either full or
      deletion-only locking prevents removing it.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-commerce:modules/merchandise/services/persistence/merchandise-extensions.ts
    origin: none
scenarios:
  - id: SC-CAP-COM-CAT-007-01
    given: One cap remains and backorders are prohibited
    when: Request two caps
    then: >-
      Availability refuses the line for insufficient stock and reports one
      remaining
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/participant/merchandise-participant.integration.spec.js#answers
      one cap too many unavailable, insufficient_stock, with what is left
  - id: SC-CAP-COM-CAT-007-02
    given: Tracked shirt stock is insufficient and backorders are enabled
    when: Request more shirts than available stock
    then: The line proceeds with the shortfall reported as a backorder
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/participant/merchandise-participant.integration.spec.js#answers
      the shirt short of stock ok with backorders allowed, the shortfall named
      as a warning and in the conditions, the SKU resolved from the selection
edge_cases:
  - id: SC-CAP-COM-CAT-007-03
    given: A new backordered SKU has no inventory row
    when: Run competing placement claims for that SKU
    then: Both claims succeed while sharing one inventory item
    provenance: test
    source: >-
      services-commerce:modules/merchandise/tests/integration/services/participant/backorder-race.integration.spec.js#leaves
      exactly one inventory item for the SKU
depends_on: []
related:
  - CAP-COM-CAT-006
  - CAP-COM-CAT-013
journeys: []
evidence:
  - >-
    Pinned service inputs: modules/merchandise/aws/api/merchandise.routes.ts and
    .schemas.ts; services/persistence/** (extensions, option sets, products);
    services/participant/** (availability, holds, payload);
    db/merchandise*.table.ts; tests (e2e, integration, unit)
  - >-
    Pinned console inputs: components/commerce/merchandise/**,
    lib/commerce/merchandise/** and tests, e2e/mock/merchandise.mock.spec.ts
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-CRY-001, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002]
exercised_by: [FAS-COM-CAT-02-01, FAS-COM-CAT-02-02, FAS-COM-CAT-02-03, FAS-COM-CAT-02-04, FAS-COM-CAT-02-05]
suites: [TST-COM-CAT-044, TST-COM-CAT-046, TST-COM-CAT-050, TST-COM-CAT-051, TST-COM-CAT-052, TST-COM-CAT-053]
---

# Manage merchandise selections and sale availability

## Purpose

Administrators describe merchandise options while Commerce assesses whether requested lines can proceed.

## How It Works

Staff create or edit merchandise together with its Accounting product. Variant sets and modifier sets describe the available choices for buyers. Commerce resolves a line selection and asks the availability logic to assess its quantity. Insufficient stock becomes a warning or refusal according to the configured backorder treatment. Stock references also matter when staff remove variants or merchandise. Retirement retains the extension while changing the product’s standing. The product should connect variant pricing inputs to the running product loader before relying on those adjustments outside isolated engine tests.

## Relationships

The prepare and publish product presentations capability supplies related configuration or assessment used in this work.

The calculate and explain a buyer’s item price capability supplies related configuration or assessment used in this work.
