---
id: CAP-COM-ORD-018
title: Receive inventory and retain its cost
feature: FEAT-COM-ORD-07
product_area: commerce
module: orders
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - CAP-COM-INV-021
  - CAP-COM-INV-022
  - CAP-COM-INV-023
  - CAP-COM-INV-024
  - CAP-COM-INV-025
  - CAP-COM-INV-029
  - CAP-COM-INV-027
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-commerce:docs/migrations/purchasing/README.md#D-P8P-15. A costing method per business unit: FIFO,
    the default, or LIFO
purpose: >-
  Received and moved stock retains a cost basis that staff can reconcile with quantities and Accounting
  intake.
outcomes:
  - Layer values explain the cost assigned to stock on hand.
  - Accounting can consume distinct inventory and cost facts with one posting acknowledgement.
concepts:
  - CON-COM-ORD-006
actors:
  - ACT-COM-019
  - ACT-COM-015
surfaces:
  - api
  - job
preconditions: []
postconditions: []
permissions:
  - key: commerce.purchasing.create
    where_checked: api
    scope: tenant
  - key: commerce.purchasing.read
    where_checked: api
    scope: tenant
  - key: commerce.inventory-costing.read
    where_checked: api
    scope: tenant
  - key: commerce.inventory-costing.update
    where_checked: api
    scope: tenant
  - key: commerce.inventory-costing.create
    where_checked: api
    scope: tenant
  - key: commerce.inventory-costing.delete
    where_checked: api
    scope: tenant
  - key: commerce.inventory-costing.bulk
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-COM-ORD-018-01
    statement: >-
      Receipts replay by their business key without duplicate movements, and corrections use later movements
      rather than editing the receipt fact.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#answers
      the retried receipt as found under its key, writing nothing twice
    origin: none
  - id: BR-CAP-COM-ORD-018-02
    statement: >-
      Receipt quantities cannot exceed an open purchase line or enter an already received order, and a cost
      difference requires an explanation before any write.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#refuses
      a receipt beyond what the line has open (over_receipt) and a receipt against a received order, writing
      nothing
    origin: none
  - id: BR-CAP-COM-ORD-018-03
    statement: >-
      Receiving commits stock movements, landed-cost layers and capitalization facts together, then satisfies
      waiting backorders in age order within that transaction.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#writes,
      in the receipt’s transaction, the receipt movement naming the line, the layer of 60 @ 1000 and the
      capitalize_receipt fact of 60000
    origin: none
  - id: BR-CAP-COM-ORD-018-04
    statement: >-
      Quantity or value allocation divides landed cost to whole minor units with the final line taking the
      remainder, and each layer’s unit cost includes its allocated share.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/unit/cost/cost-rules.spec.js#shares by quantity to the minor
      unit, the last line taking the rounding remainder
    origin: none
  - id: BR-CAP-COM-ORD-018-05
    statement: >-
      Stock relief uses the business unit’s current FIFO or LIFO method, defaulting to FIFO, while later
      method changes leave earlier cost records intact.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#relieves
      a LIFO business unit’s 12 over two layers newer first: 10 @ 1200 from the newer, then 2 @ 1000 from the
      older, recorded lifo
    origin: >-
      services-commerce:docs/migrations/purchasing/README.md#D-P8P-15. A costing method per business unit:
      FIFO, the default, or LIFO
  - id: BR-CAP-COM-ORD-018-06
    statement: >-
      Unconfigured business units read default costing settings, and replacement requires the read version, an
      allowed method and agreement with the requested business unit.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/e2e/purchasing.e2e.spec.js#writes LIFO at version 0, replaces
      it with the record read sent back, and refuses the stale version 409
    origin: >-
      services-commerce:docs/migrations/purchasing/README.md#D-P8P-15. A costing method per business unit:
      FIFO, the default, or LIFO
  - id: BR-CAP-COM-ORD-018-07
    statement: Stock without sufficient layer coverage receives zero-cost relief with an uncosted exception.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#relieves
      units no layer covers at cost zero under no layer and raises uncosted_relief, so the books say so
    origin: none
  - id: BR-CAP-COM-ORD-018-08
    statement: Returned units reverse their original relief using its recorded cost and original layer position.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#restores
      5 returned units at the relieved cost and place: a restore row naming the relief it reverses, a layer at
      that cost dated with the original layer, a restore_cogs fact of 5 × that cost
    origin: none
  - id: BR-CAP-COM-ORD-018-09
    statement: >-
      Cost correction consumes the original layer’s remaining units into a new correction layer and records
      the value difference as a cost adjustment.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#moves
      the unconsumed remainder to a layer at the corrected cost, leaves the original consumed, and writes the
      cost_adjustment of the difference
    origin: none
  - id: BR-CAP-COM-ORD-018-10
    statement: >-
      Valuation sums the remaining layer values beside on-hand quantity and reports reconciliation, business
      unit and costing method.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/e2e/purchasing.e2e.spec.js#values the shirt L item at Σ the
      layers (3 at zero from the movement route, 10 × 1000, 10 × 1250 = 22500), reconciled with 23 on hand
    origin: none
  - id: BR-CAP-COM-ORD-018-11
    statement: >-
      Posting intake lists unposted inventory facts by date and accepts one Accounting reference, replaying
      the same reference and rejecting a conflicting one.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#lists
      the unposted facts by date, stamps them posted once under Accounting’s reference, and refuses another
      reference on a posted fact
    origin: none
  - id: BR-CAP-COM-ORD-018-12
    statement: >-
      Receipt, shipment, restore and stock-adjustment facts identify their inventory, clearing, cost-of-goods
      and adjustment account purposes for Accounting.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/unit/cost/cost-rules.spec.js#debits inventory and credits
      GRNI for a receipt, debits COGS and credits inventory for a handoff, the reverse for a restore
    origin: none
  - id: BR-CAP-COM-ORD-018-13
    statement: >-
      Reconciliation reports stock-to-layer mismatches and shipment or transfer movements missing relief,
      leaving correction to a separate action.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-commerce:modules/purchasing/services/valuation/inventory-valuation.ts:raiseCostFindings'
    origin: none
  - id: BR-CAP-COM-ORD-018-14
    statement: >-
      Transfers preserve covered cost in weighted destination layers with exact rounding, produce no posting
      fact and retain any uncovered quantity without a destination cost layer.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-commerce:modules/purchasing/services/cost/transfer-cost.ts:costTransfer'
    origin: none
  - id: BR-CAP-COM-ORD-018-15
    statement: >-
      Count or staff stock gains use the latest receipt cost, while losses and scrap relieve layers by the
      business unit method and write the corresponding posting fact.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-commerce:modules/purchasing/services/cost/costed-inventory-operations.ts'
    origin: none
scenarios:
  - id: SC-CAP-COM-ORD-018-01
    given: Existing backorders require sweatshirts that are not yet available.
    when: A supplier delivery is recorded for the waiting stock.
    then: Receipt fills the waiting commitments and subsequent shipping draws their cost from the received layers.
    provenance: unknown
    source: FND-COM-ORD-032
edge_cases: []
constraints: []
depends_on:
  - CAP-COM-ORD-017
related:
  - CAP-COM-ORD-016
journeys:
  - JNY-COM-ORD-006
open_questions:
  - OQ-COM-ORD-004
evidence:
  - >-
    Examined reference: services-commerce:docs/migrations/purchasing/README.md#D-P8P-15. A costing method per
    business unit: FIFO, the default, or LIFO
  - 'Examined reference: services-commerce:modules/purchasing/services/cost/costed-inventory-operations.ts'
  - 'Examined reference: services-commerce:modules/purchasing/services/cost/transfer-cost.ts:costTransfer'
  - >-
    Examined reference:
    services-commerce:modules/purchasing/services/valuation/inventory-valuation.ts:raiseCostFindings
  - >-
    Examined reference: services-commerce:modules/purchasing/tests/e2e/purchasing.e2e.spec.js#values the shirt
    L item at Σ the layers (3 at zero from the movement route, 10 × 1000, 10 × 1250 = 22500), reconciled with
    23 on hand
  - >-
    Examined reference: services-commerce:modules/purchasing/tests/e2e/purchasing.e2e.spec.js#writes LIFO at
    version 0, replaces it with the record read sent back, and refuses the stale version 409
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#answers
    the retried receipt as found under its key, writing nothing twice
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#lists
    the unposted facts by date, stamps them posted once under Accounting’s reference, and refuses another
    reference on a posted fact
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#moves
    the unconsumed remainder to a layer at the corrected cost, leaves the original consumed, and writes the
    cost_adjustment of the difference
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#refuses
    a receipt beyond what the line has open (over_receipt) and a receipt against a received order, writing
    nothing
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#relieves
    a LIFO business unit’s 12 over two layers newer first: 10 @ 1200 from the newer, then 2 @ 1000 from the
    older, recorded lifo
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#relieves
    units no layer covers at cost zero under no layer and raises uncosted_relief, so the books say so
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#restores
    5 returned units at the relieved cost and place: a restore row naming the relief it reverses, a layer at
    that cost dated with the original layer, a restore_cogs fact of 5 × that cost
  - >-
    Examined reference:
    services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#writes,
    in the receipt’s transaction, the receipt movement naming the line, the layer of 60 @ 1000 and the
    capitalize_receipt fact of 60000
  - >-
    Examined reference: services-commerce:modules/purchasing/tests/unit/cost/cost-rules.spec.js#debits
    inventory and credits GRNI for a receipt, debits COGS and credits inventory for a handoff, the reverse for
    a restore
  - >-
    Examined reference: services-commerce:modules/purchasing/tests/unit/cost/cost-rules.spec.js#shares by
    quantity to the minor unit, the last line taking the rounding remainder
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-CRY-001, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, 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]
---

# Receive inventory and retain its cost

## Purpose

Received and moved stock retains a cost basis that staff can reconcile with quantities and Accounting intake. Corrections remain traceable instead of overwriting earlier facts.

## How It Works

Staff record a delivery against its purchase order or as a direct receipt. The receiving operation updates quantities and establishes the cost basis together. Later shipment, return, transfer or stock adjustment refers to the retained cost layers. Controllers inspect valuation beside the quantity records and investigate reported differences. A cost correction creates a traceable successor layer. Accounting reads the resulting posting facts and acknowledges those it posts.

## Relationships

Purchasing supplies planned deliveries, the neighbouring inventory workflows supply movements, and Accounting consumes the resulting posting facts.
