---
id: DOM-COM-CAT-004
title: Price
realises: CON-COM-CAT-004
product_area: commerce
module: catalog
revision: 1
status: draft
design_intent: proposed
implementation_evidence: partially-observed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-COM-CAT-pricing
readable_by:
  - SVC-COM-CAT-catalog
  - SVC-COM-CAT-promotions
  - SVC-COM-CAT-recommendations
lifecycle: STATE-COM-CAT-005
custom_fields: none
identity: >-
  A calculated amount is an answer for a quote or checkout line rather than a separately stored price record.
  Its explanation identifies the source price row, audience, period and conversion. Audiences, schedules and
  periods use readable keys; purchasing settings and handler references are keyed by product.
attributes:
  - name: audiences
    kind: collection
    required: true
    computed: false
    notes: >-
      Audiences define qualifying groups, unique rank and normalized name, scope, public display and activity.
      Standard and Member are provisioned at ranks zero and ten.
  - name: schedules and periods
    kind: collection
    required: false
    computed: false
    notes: >-
      Schedules group contiguous start-inclusive, end-exclusive intervals in a named time zone, with outer
      open boundaries and a permanent first-sale stamp.
  - name: purchasing settings
    kind: collection
    required: false
    computed: false
    notes: >-
      Product choices select schedule, ranking strategy, promotion exclusion, priced party, qualification date
      and organization roles. Missing settings use defaults.
  - name: pricing function
    kind: reference
    required: false
    computed: false
    notes: 'A product and extension point may name one registered handler, used only while active.'
  - name: price grid
    kind: reference
    required: true
    computed: false
    notes: >-
      Accounting owns audience, period and currency price rows, including fixed, prohibited, percentage and
      conversion modes.
  - name: unit price and line total
    kind: computed
    required: true
    computed: true
    notes: 'One calculation sequence derives unit and line amounts, rounding exact half units away from zero.'
  - name: provenance and warnings
    kind: computed
    required: true
    computed: true
    notes: >-
      The response explains qualification, source rows, periods, conversion, staff assumptions and unpriced or
      context-dependent lines.
  - name: display prices
    kind: computed
    required: false
    computed: true
    notes: Visitors can see Standard and publicly displayed audience amounts without acquiring qualification.
  - name: special-price handovers
    kind: collection
    required: false
    computed: false
    notes: >-
      Accounting retains retired special-price handovers; the available realization function creates scoped
      audiences and stamps completion when invoked.
  - name: protection and version
    kind: computed
    required: true
    computed: true
    notes: 'Configuration records carry independent versions, protection and creator and latest-editor metadata.'
relationships:
  - to: DOM-COM-CAT-003
    cardinality: one price per product per line
    ownership: >-
      Accounting retains the product and price grid; Commerce retains the product’s purchasing settings and
      handler reference.
    cross_service: false
  - to: DOM-COM-CAT-001
    cardinality: many prices to one policy currency
    ownership: The checkout policy establishes the currency used by sale pricing.
    cross_service: false
  - to: DOM-COM-CAT-005
    cardinality: one price to many promotion alternatives
    ownership: Promotion evaluation applies discounts to the audience or Standard basis returned by pricing.
    cross_service: false
  - to: DOM-COM-CAT-006
    cardinality: one quote per offer candidate
    ownership: Recommendation candidates use the quote service without reserving the displayed price.
    cross_service: false
invariants:
  - id: INV-DOM-COM-CAT-004-01
    statement: >-
      Provisioning supplies Standard and Member; the service prevents their deletion and the database protects
      their kinds.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/e2e/pricing-audiences.e2e.spec.js#lists standard for everyone at
      rank 0 and member by eligibility at rank 10, built in, member shown to visitors, with no binding yet
  - id: INV-DOM-COM-CAT-004-02
    statement: Audiences require distinct nonnegative ranks and names distinct after case and spacing normalization.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/integration/services/persistence/pricing-audiences.integration.spec.js#refuses
      a create to the same name in other letter case and spacing, 409 audience_name_taken naming the existing
      audience
  - id: INV-DOM-COM-CAT-004-03
    statement: >-
      Price-row references block audience deletion; deactivation retains those rows but excludes the audience
      from selection.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/integration/services/persistence/pricing-audiences.integration.spec.js#refuses
      409 audience_in_use, naming the products and their count, and keeps the audience
  - id: INV-DOM-COM-CAT-004-04
    statement: >-
      At commit, schedule intervals must meet exactly, with an open start only on the first and an open end
      only on the last.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/integration/db/member-pricing-tables.table.integration.spec.js#refuses
      an overlap, a second open end and an open start that is not first
  - id: INV-DOM-COM-CAT-004-05
    statement: >-
      Used periods retain their name and start; a current end can move to now or later if adjacency remains
      valid, while elapsed periods refuse edits and first-sale stamps remain fixed.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/integration/db/member-pricing-tables.table.integration.spec.js#lets
      a sold-in current period end a week later, the next period starting where it ends
  - id: INV-DOM-COM-CAT-004-06
    statement: >-
      A referenced schedule cannot be disabled or removed, and purchasing settings cannot select an inactive
      schedule.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/integration/services/persistence/pricing-schedules.integration.spec.js#refuses
      with schedule_in_use, the count in the sentence, and the count and the first five product ids in the
      details
  - id: INV-DOM-COM-CAT-004-07
    statement: Periods belong to their workspace; the retained schedule request contract accepts no owner.
    enforced_at:
      - api
      - database
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/e2e/pricing-schedules.e2e.spec.js#answers 400 to an event owner
      (a schedule has none, COMM-M1 D2), to a property outside the contract and to a tenant_id other than the
      path’s
  - id: INV-DOM-COM-CAT-004-08
    statement: Calculation rounds to whole minor currency units and resolves exact halves away from zero.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/unit/engine/rounding.spec.js#rounds a negative half down, away
      from zero, where Math.round would answer -2
  - id: INV-DOM-COM-CAT-004-09
    statement: Quotes do not bind a price; placement calculates the amount again.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/unit/engine/quote-tolerance.spec.js#differs with the changes
      when a price moved, never answering the previewed price
  - id: INV-DOM-COM-CAT-004-10
    statement: >-
      Placement rechecks eligibility-priced lines transactionally and rejects lapsed qualification instead of
      silently selecting another price.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/integration/services/placement/audience-settlement.integration.spec.js#refuses
      the placement for a lapsed member, one audience_lapsed problem per line with the portal sentence,
      stamping nothing
  - id: INV-DOM-COM-CAT-004-11
    statement: A Standard currency prohibition excludes purchasing in that currency for every audience.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/unit/engine/step-3-base-price.spec.js#refuses a currency the
      product is prohibited in for everyone (a prohibit on standard)
  - id: INV-DOM-COM-CAT-004-12
    statement: Purchasing-settings replacement resets omitted values to defaults.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/e2e/product-purchasing-settings.e2e.spec.js#gives a setting the
      PUT leaves out its default, the whole record being stated
physical:
  - pricing_audiences
  - pricing_schedules
  - pricing_periods
  - product_purchasing_settings
  - product_extensions
evidence:
  - >-
    services-commerce at c8b66a98: modules/pricing/db/*.table.ts; services/persistence/pricing-audiences.ts,
    pricing-schedules.ts, product-purchasing-settings.ts, product-extensions.ts, catalog-products.ts,
    pricing-audience-handovers.ts; services/engine/engine.ts; services/inputs/pricing-inputs.ts;
    services/placement/audience-settlement.ts.
  - >-
    services-commerce at c8b66a98: docs/milestones/COMM-M1-set-up-what-you-sell.md (D1 to D7, D10, D14);
    docs/debt/pricing-schedules-owned-by-event.md.
  - 'react-console at baf6dae3: components/commerce/{audiences,pricing}/**, lib/commerce/pricing/live.ts.'
---

# Price

## What It Models

A price answers what an item costs for a buyer, currency and instant. Commerce calculates it from Accounting price rows and its own audiences, periods and purchasing choices. This object models the configuration and calculation result rather than a standing stored amount.

## Identity and Attributes

Audience and period keys let Accounting price rows refer to Commerce configuration. Purchasing settings and handler references are product-specific. Responses explain the chosen audience, period, row and conversion, including assumptions made during a staff preview.

## Relationships

The policy determines the sale currency. Accounting owns the underlying grid. Storefront and recommendation reads call the quote path; promotions assess discounts against its returned bases.

## Invariants

Audience ranks and normalized names remain unique. Schedule periods must be adjacent. A sold-in current period may still move its end to now or later, but its name and start stay fixed; elapsed periods refuse edits. Quotes bind nothing, and placement recalculates and rechecks qualification.

## How It Is Stored and Shared

Pricing owns the engine and configuration. Accounting owns price rows and handovers. Eligibility display transport can use Platform when configured, while membership facts and handler factories remain fixtures. No custom-field capability or change feed is declared.

## Open Design Questions

Special-price realization lacks a caller. Ownerless schedule APIs remain behind the approved owner-bound direction. Fixture pricing dependencies and the authoritative tax source require decisions before implementation preparation.
