---
id: CAP-COM-ORD-001
title: Build a buyer’s cart
feature: FEAT-COM-ORD-01
product_area: commerce
module: orders
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes:
  - CAP-COM-CHK-001
  - CAP-COM-CHK-002
  - CAP-COM-CHK-003
  - CAP-COM-CHK-006
  - CAP-COM-CHK-008
  - CAP-COM-CHK-010
  - CAP-COM-CHK-011
  - CAP-COM-CHK-015
  - CAP-COM-CAT-034
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#2. Scope'
  - 'services-commerce:docs/milestones/COMM-M1-set-up-what-you-sell.md#D8'
  - 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#D13'
  - 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#A7'
  - 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#A17'
purpose: A clerk assembles the customer’s intended purchase before committing a sale.
outcomes:
  - 'The cart identifies buying, acting, billing and receiving parties and validates the intended items.'
  - Unfinished carts close and release their outstanding holds.
concepts:
  - CON-COM-ORD-001
actors:
  - ACT-COM-014
  - ACT-COM-017
  - ACT-COM-016
surfaces:
  - console
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: commerce.checkouts.create
    where_checked: api
    scope: tenant
  - key: commerce.checkouts.read
    where_checked: api
    scope: tenant
  - key: commerce.checkouts.update
    where_checked: api
    scope: tenant
  - key: commerce.orders.create
    where_checked: service
    scope: tenant
rules:
  - id: BR-CAP-COM-ORD-001-01
    proved_by: [ATS-COM-ORD-001-01, ATS-COM-ORD-001-18, ATS-COM-ORD-001-19, ATS-COM-ORD-001-20, ATS-COM-ORD-011-07, TST-COM-ORD-027]
    statement: >-
      Portal buyers work with carts belonging to their contact or naming them as the acting contact;
      authorized console staff can work with any cart in the workspace.
    kind: permission
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#refuses a member opening a checkout
      for another contact, 403
    origin: none
  - id: BR-CAP-COM-ORD-001-02
    proved_by: [ATS-COM-ORD-001-02, ATS-COM-ORD-001-15, ATS-COM-ORD-002-10, TST-COM-ORD-027]
    statement: >-
      Before an item is saved, validation reports all detected problems with sale availability, registered
      ownership, positive whole quantities and purchase limits, required item details, and supported currency.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#refuses a quantity past the product’s
      purchase limit: quantity_out_of_limits
    origin: none
  - id: BR-CAP-COM-ORD-001-03
    proved_by: [ATS-COM-ORD-001-03, TST-COM-ORD-024]
    statement: >-
      A cart requires record identifiers for its buyer, acting contact, bill-to party and item recipients, and
      rejects slugs with a field-specific error before writing.
    kind: validation
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkout-record-ids.e2e.spec.js#refuses a buyer that is a
      slug when the checkout is opened, 400 naming buyer_id, and writes nothing
    origin: 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#A17'
  - id: BR-CAP-COM-ORD-001-04
    proved_by: [ATS-COM-ORD-001-04, TST-COM-ORD-027]
    statement: >-
      Organization purchases use product purchasing roles first, store roles next and an active affiliation
      otherwise; staff with order-placing authority can bypass that check for a named contact, with the
      override recorded.
    kind: permission
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#lets a console user with
      commerce.orders.create buy for the organization naming any acting contact, and audits the override
    origin: 'services-commerce:docs/milestones/plans/COMM-M2-plan.md#D13'
    examples:
      positive: 'Authorized staff purchase for an organization using a named contact, with the override retained.'
      negative: A portal buyer without the required organizational purchasing role receives a refusal.
  - id: BR-CAP-COM-ORD-001-05
    proved_by: [ATS-COM-ORD-001-05, TST-COM-ORD-023]
    statement: >-
      Authorized staff can sell unpublished products and products outside the store assortment, while portal
      buyers receive an explanation and their cart remains unchanged.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkout-public-sale.e2e.spec.js#lets a console user
      holding commerce.orders.create open a checkout with the draft product and add the one outside the
      assortment
    origin: 'services-commerce:docs/milestones/COMM-M1-set-up-what-you-sell.md#D8'
  - id: BR-CAP-COM-ORD-001-06
    proved_by: [ATS-COM-ORD-001-06, ATS-COM-ORD-001-16, TST-COM-ORD-027, TST-COM-ORD-048]
    statement: >-
      Changes to an open cart advance its version and record the acting identity in its history, including
      actions by Rhythm support.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#adds after the last, changes the
      intent and clears what was advised, removes, each bumping the version
    origin: 'services-commerce:docs/milestones/COMM-M2-take-a-staff-order.md#M2-S2'
  - id: BR-CAP-COM-ORD-001-07
    proved_by: [ATS-COM-ORD-001-07, TST-COM-ORD-027, TST-COM-ORD-040]
    statement: >-
      Carts expire seven days after creation, and the minute sweep abandons expired open carts in bounded
      expiry order using a system action within each workspace.
    kind: lifecycle
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/integration/services/checkout/checkout-sweeper.integration.spec.js#abandoned
      each expired open checkout in its own tenant, with the system as the actor and expired as the reason
    origin: none
  - id: BR-CAP-COM-ORD-001-08
    proved_by: [ATS-COM-ORD-001-08, ATS-COM-ORD-001-17, TST-COM-ORD-027, TST-COM-ORD-029]
    statement: Abandonment prevents further cart edits and releases item holds through the applications that own them.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#moves an open checkout to abandoned,
      after which it no longer changes (409 checkout_not_open)
    origin: none
  - id: BR-CAP-COM-ORD-001-09
    proved_by: [ATS-COM-ORD-001-09, TST-COM-ORD-029]
    statement: >-
      An application can reserve a scarce item before placement, refuse competing buyers or an excessive
      extension, and release the reservation when the item is removed.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/holds-and-claims.e2e.spec.js#refuses another buyer the
      booth: 409 hold_unavailable with the participant’s reason, and their preview marks the item unavailable
    origin: none
  - id: BR-CAP-COM-ORD-001-10
    proved_by: [ATS-COM-ORD-001-11, TST-COM-ORD-029]
    statement: >-
      The reservation sweep marks an item stale when its hold expires and makes the reserved resource
      available again.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/holds-and-claims.e2e.spec.js#expires the ledger’s row,
      marks the item stale with hold_expired on the trail, and counts it
    origin: none
  - id: BR-CAP-COM-ORD-001-11
    proved_by: [ATS-COM-ORD-001-12, TST-COM-ORD-024]
    statement: 'A bundle child must refer to a parent already present in the cart, or validation rejects it.'
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkout-record-ids.e2e.spec.js#refuses an item whose
      parent_item_id names no item of the checkout, 400 envelope_invalid, and takes one that does
      (COM-ACC-0025)
    origin: none
  - id: BR-CAP-COM-ORD-001-12
    proved_by: [ATS-COM-ORD-001-13]
    statement: >-
      Process references can contain 500 characters on a cart, but application calls omit references exceeding
      the shared contract’s 128-character limit.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#refuses a process_ref of 501
      characters on create, 400
    origin: none
    note: >-
      The participant contract accepts a shorter reference than checkout, so the application call can lose a
      long value; finding 014 records the mismatch.
  - id: BR-CAP-COM-ORD-001-13
    proved_by: [ATS-COM-ORD-001-14, TST-COM-ORD-060]
    statement: 'A shipping choice records the selected strategy, and clearing that choice removes it from the cart.'
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/unit/checkout/shipping-choice.spec.js#writes null for a cleared
      choice, the property present so the update clears the column
    origin: none
  - id: BR-CAP-COM-ORD-001-14
    proved_by: [ATS-COM-ORD-001-10, TST-COM-ORD-043]
    statement: >-
      A cart can move from open to preparing, abandoned or cancelled, and from preparing back to open or
      onward to placed; terminal carts refuse further transitions, and placement requires an order link.
    enforced_at:
      - service
    provenance: observed
    source: 'services-commerce:modules/checkout/services/persistence/checkouts.ts:transitionCheckout'
    origin: none
    must_prove: true
    kind: lifecycle
scenarios:
  - id: SC-CAP-COM-ORD-001-01
    given: 'An organization customer orders shirts for a colleague, with Pat as its acting contact.'
    when: Staff create the organizational cart and select the colleague for shipping.
    then: The intended parties are recorded and the purchasing-role override appears in history.
    provenance: unknown
    source: FND-COM-ORD-032
  - id: SC-CAP-COM-ORD-001-02
    given: An open cart remains unfinished until its expiry passes.
    when: Expiry processing selects it on a minute run.
    then: It becomes abandoned and releases its holds.
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/integration/services/checkout/checkout-sweeper.integration.spec.js#abandoned
      each expired open checkout in its own tenant, with the system as the actor and expired as the reason
edge_cases:
  - id: SC-CAP-COM-ORD-001-03
    given: An existing item recipient needs to be removed.
    when: 'Submit both recipient fields as null, then try only one null field.'
    then: The paired nulls clear the recipient; a lone null is refused.
    provenance: test
    source: >-
      services-commerce:modules/checkout/tests/e2e/checkout-record-ids.e2e.spec.js#clears the recipient of an
      item changed with both fields null, keeps it when neither is given, and refuses one null alone
      (COM-ACC-0018)
constraints: []
depends_on: []
related:
  - CAP-COM-ORD-002
  - CAP-COM-ORD-003
  - CAP-COM-ORD-005
  - CAP-COM-ORD-011
journeys:
  - JNY-COM-ORD-001
  - JNY-COM-ORD-002
open_questions: []
evidence:
  - 'Examined reference: services-commerce:docs/milestones/COMM-M1-set-up-what-you-sell.md#D8'
  - 'Examined reference: services-commerce:docs/milestones/COMM-M2-take-a-staff-order.md#M2-S2'
  - 'Examined reference: services-commerce:docs/milestones/plans/COMM-M2-plan.md#2. Scope'
  - 'Examined reference: services-commerce:docs/milestones/plans/COMM-M2-plan.md#A17'
  - 'Examined reference: services-commerce:docs/milestones/plans/COMM-M2-plan.md#A7'
  - 'Examined reference: services-commerce:docs/milestones/plans/COMM-M2-plan.md#D13'
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkout-public-sale.e2e.spec.js#lets a
    console user holding commerce.orders.create open a checkout with the draft product and add the one outside
    the assortment
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkout-record-ids.e2e.spec.js#clears
    the recipient of an item changed with both fields null, keeps it when neither is given, and refuses one
    null alone (COM-ACC-0018)
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkout-record-ids.e2e.spec.js#refuses a
    buyer that is a slug when the checkout is opened, 400 naming buyer_id, and writes nothing
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkout-record-ids.e2e.spec.js#refuses
    an item whose parent_item_id names no item of the checkout, 400 envelope_invalid, and takes one that does
    (COM-ACC-0025)
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#adds after the
    last, changes the intent and clears what was advised, removes, each bumping the version
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#lets a console user
    with commerce.orders.create buy for the organization naming any acting contact, and audits the override
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#moves an open
    checkout to abandoned, after which it no longer changes (409 checkout_not_open)
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#refuses a member
    opening a checkout for another contact, 403
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#refuses a
    process_ref of 501 characters on create, 400
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/checkouts.e2e.spec.js#refuses a quantity
    past the product’s purchase limit: quantity_out_of_limits
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/holds-and-claims.e2e.spec.js#expires the
    ledger’s row, marks the item stale with hold_expired on the trail, and counts it
  - >-
    Examined reference: services-commerce:modules/checkout/tests/e2e/holds-and-claims.e2e.spec.js#refuses
    another buyer the booth: 409 hold_unavailable with the participant’s reason, and their preview marks the
    item unavailable
  - >-
    Examined reference:
    services-commerce:modules/checkout/tests/integration/services/checkout/checkout-sweeper.integration.spec.js#abandoned
    each expired open checkout in its own tenant, with the system as the actor and expired as the reason
  - >-
    Examined reference: services-commerce:modules/checkout/tests/unit/checkout/shipping-choice.spec.js#writes
    null for a cleared choice, the property present so the update clears the column
controls: [TC-IAM-006, 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-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-AVL-004, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002]
exercised_by: [FAS-COM-ORD-01-01, FAS-COM-ORD-01-02, FAS-COM-ORD-01-03, FAS-COM-ORD-01-04, FAS-COM-ORD-01-05]
suites: [TST-COM-ORD-023, TST-COM-ORD-024, TST-COM-ORD-027, TST-COM-ORD-029, TST-COM-ORD-040, TST-COM-ORD-043, TST-COM-ORD-048, TST-COM-ORD-060]
---

# Build a buyer’s cart

## Purpose

A clerk assembles the customer’s intended purchase before committing a sale. The cart keeps the parties and items together for review.

## How It Works

A clerk starts from the customer’s record and opens the composer. They establish the buyer, acting contact and billing party before building the item list. Commerce checks each submitted item and returns problems the clerk can address. The clerk can review limited availability and request a hold where the selling application supports it. History identifies changes and the people making them. Leaving the cart unfinished allows expiry processing to release it.

## Relationships

Checkout policy selection establishes the commercial context; preview reviews the items; placement commits the sale; record protection governs the caller’s access.
