---
id: CAP-COM-FUL-001
title: Choose delivery conditions for sold items
feature: FEAT-COM-FUL-01
product_area: commerce
module: fulfillment
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - CAP-COM-REG-010
  - CAP-COM-REG-011
  - CAP-COM-REG-012
  - CAP-COM-REG-013
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-commerce:docs/migrations/obligation-register/README.md#D-P4-17
    (Q05, Andrew 2026-10-19)
purpose: Administrators define when a sale is ready for delivery.
outcomes:
  - >-
    Delivery respects the financial and approval conditions selected by the
    customer.
  - Later policy edits do not silently alter existing orders.
concepts:
  - CON-COM-FUL-002
  - CON-COM-FUL-001
actors:
  - ACT-COM-009
  - ACT-COM-008
surfaces:
  - api
preconditions: []
postconditions: []
permissions:
  - key: commerce.fulfillment-policies.read
    where_checked: api
    scope: tenant
  - key: commerce.fulfillment-policies.create
    where_checked: api
    scope: tenant
  - key: commerce.fulfillment-policies.update
    where_checked: api
    scope: tenant
  - key: commerce.fulfillment-policies.delete
    where_checked: api
    scope: tenant
  - key: commerce.fulfillment-policies.apply
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-COM-FUL-001-01
    proved_by: [ATS-COM-FUL-001-01, TST-COM-FUL-019, TST-COM-FUL-020, TST-COM-FUL-033]
    statement: >-
      A delivery policy selects one financial condition—immediate, full payment,
      minimum amount, minimum percentage, paid deposit or none—and carries
      approval, readiness, reversal, bank-debit and parent-line settings; amount
      and percentage fields are required only for their matching conditions.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-policies.e2e.spec.js#refuses
      a policy the contract refuses (400 naming the field), a second policy of
      one scope (400), a reader’s create (403) and the other tenant’s read (404)
    origin: none
    examples:
      positive: minimum percent with 50 is accepted
      negative: minimum amount with no amount is refused naming the field
  - id: BR-CAP-COM-FUL-001-02
    proved_by: [ATS-COM-FUL-001-02, TST-COM-FUL-025, TST-COM-FUL-033]
    statement: >-
      Each product, ordering profile or workspace default has at most one
      policy, and the workspace default alone omits a scope reference.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/integration/services/persistence/fulfillment-policies.integration.spec.js#refuses
      a second tenant default and a second policy of one scope, 400 naming the
      replacement
    origin: none
  - id: BR-CAP-COM-FUL-001-03
    proved_by: [ATS-COM-FUL-001-03, TST-COM-FUL-020, TST-COM-FUL-053]
    statement: >-
      Policy selection uses the active product policy, then the active
      ordering-profile policy, then the active workspace default, then the
      built-in full-payment policy with approved terms allowed and reversals
      sent for review.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/unit/policies/policy-resolution.spec.js#takes
      the product’s policy over the ordering profile’s and the tenant default’s
    origin: none
  - id: BR-CAP-COM-FUL-001-04
    proved_by: [ATS-COM-FUL-001-04, ATS-COM-FUL-001-10, ATS-COM-FUL-002-11, TST-COM-FUL-020, TST-COM-FUL-053]
    statement: >-
      Placement copies the selected policy, version and origin onto the
      obligation, preserving that gate when the configuration is subsequently
      changed or removed.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-policies.e2e.spec.js#freezes
      the product policy on the obligation with the row’s version and source,
      and the replacement changes nothing on it
    origin: none
  - id: BR-CAP-COM-FUL-001-05
    proved_by: [ATS-COM-FUL-001-04, TST-COM-FUL-020, TST-COM-FUL-053]
    statement: >-
      Preparation adds any application-required approval or readiness condition
      to the configured policy and cannot remove a configured safeguard.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/unit/policies/policy-resolution.spec.js#ORs
      the participant’s approval and prerequisite onto the policy, and never
      waives what the policy asks
    origin: none
  - id: BR-CAP-COM-FUL-001-06
    proved_by: [ATS-COM-FUL-001-01, TST-COM-FUL-019, TST-COM-FUL-020, TST-COM-FUL-033]
    statement: >-
      A minimum-payment amount is retained unchanged in the order currency,
      permits zero and rejects negative values with the affected field
      identified.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-paid-amount.e2e.spec.js#refuses
      a negative amount 400 naming financial_amount_minor
    origin: >-
      Q05, Andrew 2026-10-19:
      services-commerce:docs/migrations/obligation-register/README.md#D-P4-17
  - id: BR-CAP-COM-FUL-001-07
    proved_by: [ATS-COM-FUL-001-05, TST-COM-FUL-033]
    statement: >-
      Policy replacement requires the current version, and a locked policy
      rejects replacement and deletion.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/integration/services/persistence/fulfillment-policies.integration.spec.js#replaces
      a policy at the version read, refuses a stale version 409, and refuses a
      locked record
    origin: none
  - id: BR-CAP-COM-FUL-001-08
    proved_by: [ATS-COM-FUL-001-06, ATS-COM-FUL-001-11, ATS-COM-FUL-002-12, ATS-COM-FUL-005-08, TST-COM-FUL-020]
    statement: >-
      Policy application records the staff reason and intervention before
      examining a bounded page of pending obligations in scope, replaces gates
      that differ and leaves matching gates unchanged on repetition.
    kind: lifecycle
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-policies.e2e.spec.js#re-freezes
      the pending obligation to the replaced policy with a receipt, records the
      intervention, and re-freezes nothing on a repeat
    origin: none
  - id: BR-CAP-COM-FUL-001-09
    proved_by: [ATS-COM-FUL-001-07, ATS-COM-FUL-001-08, TST-COM-FUL-040]
    statement: >-
      Policy application excludes authorized and blocked obligations and
      preserves any approval or readiness requirement in the existing gate.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-commerce:modules/fulfillment/services/policies/retroactive-policy.ts:applyPolicyRetroactively
    origin: >-
      D-P4-05 (build decision, not a product ruling):
      services-commerce:docs/migrations/obligation-register/README.md#D-P4-05
  - id: BR-CAP-COM-FUL-001-10
    proved_by: [ATS-COM-FUL-001-09, TST-COM-FUL-020]
    statement: >-
      Policy reads require read authority; creation, replacement, deletion and
      application require their respective administrator permissions, with
      application treated as sensitive.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: observed
    source: 'services-commerce:modules/fulfillment/aws/api/fulfillment.routes.ts'
    origin: none
  - id: BR-CAP-COM-FUL-001-11
    proved_by: [ATS-COM-FUL-001-03, TST-COM-FUL-020, TST-COM-FUL-053]
    statement: >-
      Removing all configured policies makes the built-in policy the fallback
      for newly sold lines.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-policies.e2e.spec.js#deletes
      the policies, after which the effective policy is the built-in default
    origin: none
scenarios:
  - id: SC-CAP-COM-FUL-001-01
    given: A buyer selects a product with a 300.00 minimum-payment policy.
    when: Place the order.
    then: >-
      Its obligation retains that threshold and cannot pass the financial
      condition below 300.00.
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-paid-amount.e2e.spec.js#freezes
      the product’s amount on the obligation
  - id: SC-CAP-COM-FUL-001-02
    given: 'Policies exist for the product, ordering profile and workspace.'
    when: Request the effective policy for a new product line.
    then: The answer selects the product policy and identifies its origin.
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-policies.e2e.spec.js#answers
      the effective policy: the product’s over the ordering profile’s over the
      tenant default’s
edge_cases:
  - id: SC-CAP-COM-FUL-001-03
    given: A waiting obligation retains an earlier product-policy version.
    when: Replace configuration without applying it to existing work.
    then: The saved obligation gate remains unchanged.
    provenance: test
    source: >-
      services-commerce:modules/fulfillment/tests/e2e/fulfillment-policies.e2e.spec.js#freezes
      the product policy on the obligation with the row’s version and source,
      and the replacement changes nothing on it
constraints: []
depends_on: []
related:
  - CAP-COM-FUL-002
  - CAP-COM-FUL-005
journeys:
  - JNY-COM-FUL-001
open_questions:
  - OQ-COM-FUL-001
evidence:
  - repository: services-commerce
    paths:
      - modules/fulfillment/tests/e2e/fulfillment-policies.e2e.spec.js
      - >-
        modules/fulfillment/tests/integration/services/persistence/fulfillment-policies.integration.spec.js
      - modules/fulfillment/tests/unit/policies/policy-resolution.spec.js
      - modules/fulfillment/tests/e2e/fulfillment-paid-amount.e2e.spec.js
      - modules/fulfillment/services/policies/retroactive-policy.ts
      - modules/fulfillment/aws/api/fulfillment.routes.ts
  - repository: react-console
    paths: []
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-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002]
exercised_by: [FAS-COM-FUL-01-01, FAS-COM-FUL-01-04, FAS-COM-FUL-01-05]
suites: [TST-COM-FUL-019, TST-COM-FUL-020, TST-COM-FUL-025, TST-COM-FUL-033, TST-COM-FUL-040, TST-COM-FUL-053]
---

# Choose delivery conditions for sold items

## Purpose

Administrators define when a sale is ready for delivery.

## How It Works

An administrator configures delivery policies through the service. Staff choose the scope and the financial, approval and readiness conditions appropriate to the sale. The selected policy becomes part of the obligation when the order is placed. Staff inspecting an order can distinguish that saved policy from today's configuration. Applying a replacement policy is a separate intervention with a recorded reason and a bounded result. Large applications need particular care because the implementation has no continuation cursor. The product should provide a policy editor and an impact preview before application.

## Relationships

- Authorize delivery from current order facts supplies or consumes related delivery records.
- Understand and intervene in an order's delivery supplies or consumes related delivery records.
