---
id: DOM-COM-ORD-006
title: Purchase Order
realises: CON-COM-ORD-006
product_area: commerce
module: orders
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
owned_by: SVC-COM-ORD-purchasing
readable_by:
  - SVC-PFM-QRY-data-exports
lifecycle: STATE-COM-ORD-013
custom_fields: none
identity: >-
  Vendors, purchase orders and receipts have time-ordered identities. Purchase orders and receipts also
  receive workspace numbers; receipt keys identify replays and line numbers are local to a purchase order.
attributes:
  - name: vendor
    kind: reference
    required: true
    computed: false
    notes: 'Supplier details include name, normalized email, address, currency and active status.'
  - name: warehouse and currency
    kind: reference
    required: true
    computed: false
    notes: The destination warehouse and purchase currency.
  - name: status and instants
    kind: enum
    required: true
    computed: false
    notes: 'Draft, issued, partially received, received, closed or cancelled with the relevant lifecycle timestamps.'
  - name: lines
    kind: collection
    required: true
    computed: false
    notes: 'Product, SKU, price and ordered, received and cancelled quantities determine the unreceived remainder.'
  - name: receipts
    kind: collection
    required: false
    computed: false
    notes: >-
      Keyed purchase-order or direct deliveries retain freight allocation, condition, quantities, costs,
      cost-change explanations and acquisition-layer references.
  - name: cost layers
    kind: collection
    required: false
    computed: false
    notes: 'Layers preserve quantity, remaining units, cost, origin and the costing method recorded at creation.'
  - name: relieved and restored cost
    kind: collection
    required: false
    computed: false
    notes: >-
      Relief identifies stock leaving a layer; restoration identifies the original relief, movement and amount
      returned.
  - name: financial posting facts
    kind: collection
    required: false
    computed: false
    notes: >-
      Capitalization, relief, restoration, corrections and adjustments produce immutable financial facts with
      account purposes and eventual posting references.
  - name: business-unit costing setting
    kind: enum
    required: false
    computed: false
    notes: A versioned business-unit choice of FIFO or LIFO; unwritten settings read as FIFO.
relationships:
  - to: DOM-COM-ORD-002
    cardinality: Incoming stock can satisfy customer backorders and later shipments consume acquisition value.
    ownership: Inventory and shipping own physical allocation and delivery while Purchasing owns acquisition cost.
    cross_service: false
  - to: DOM-COM-ORD-005
    cardinality: Returned goods can reverse the acquisition cost consumed at shipment.
    ownership: The return reporter supplies the receipt and Purchasing persists restoration with it.
    cross_service: false
  - to: DOM-ACC-BKS-001
    cardinality: Each costing choice and posting fact identifies an Accounting business unit.
    ownership: Accounting retains business-unit and posted-journal ownership.
    cross_service: true
invariants:
  - id: INV-DOM-COM-ORD-006-01
    statement: Reusing a receipt key returns the recorded receipt without repeating its stock movement.
    enforced_at:
      - database
      - service
    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
  - id: INV-DOM-COM-ORD-006-02
    statement: Receiving requires an eligible order and quantities no greater than each line's unreceived remainder.
    enforced_at:
      - service
    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
  - id: INV-DOM-COM-ORD-006-03
    statement: >-
      Receipt movements, acquisition layers, posting facts, received quantities and backorder allocation share
      one commit.
    enforced_at:
      - service
      - database
    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
  - id: INV-DOM-COM-ORD-006-04
    statement: >-
      Remaining layer units cannot be negative or exceed the layer quantity, and receipt layers require their
      receipt-line reference.
    enforced_at:
      - database
    provenance: observed
    source: >-
      services-commerce:modules/purchasing/db/inventory-cost-layers.table.ts:inventory_cost_layers_remaining_check,
      inventory_cost_layers_receipt_line_check
  - id: INV-DOM-COM-ORD-006-05
    statement: Each relief or restoration total is its unit cost multiplied by its quantity.
    enforced_at:
      - database
    provenance: observed
    source: 'services-commerce:modules/purchasing/db/cost-relief.table.ts:cost_relief_total_check'
  - id: INV-DOM-COM-ORD-006-06
    statement: >-
      A source movement has one fact per posting kind; repeating the posted stamp must retain the same
      Accounting reference.
    enforced_at:
      - database
      - service
    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
  - id: INV-DOM-COM-ORD-006-07
    statement: A costing-method change governs future work without revising existing layers or relief rows.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/integration/services/receiving/purchasing-flow.integration.spec.js#rewrites
      nothing made before the switch: the FIFO relief rows read back unchanged, the receipt layers still
      record fifo
  - id: INV-DOM-COM-ORD-006-08
    statement: A referenced vendor cannot be removed while a purchase order or receipt uses it.
    enforced_at:
      - database
      - service
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/e2e/purchasing.e2e.spec.js#refuses a receipt against the
      received order 409, a receipt line with no quantity 400, and the vendor’s delete 409
  - id: INV-DOM-COM-ORD-006-09
    statement: Replacing a purchase order requires draft status and the version previously read.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-commerce:modules/purchasing/tests/e2e/purchasing.e2e.spec.js#replaces the draft at the version
      read with the record exactly as it was read, read-only line columns included, and answers the same lines
      one version on
  - id: INV-DOM-COM-ORD-006-10
    statement: >-
      Closed and cancelled purchase orders require their corresponding timestamps, which are absent in other
      statuses.
    enforced_at:
      - database
    provenance: observed
    source: >-
      services-commerce:modules/purchasing/db/purchase-orders.table.ts:purchase_orders_closed_check,
      purchase_orders_cancelled_check
physical:
  - vendors
  - purchase_orders
  - purchase_order_lines
  - inventory_receipts
  - inventory_receipt_lines
  - inventory_cost_layers
  - cost_relief
  - inventory_postings
  - business_unit_settings
evidence:
  - >-
    services-commerce at c8b66a98: modules/purchasing/db/*.table.ts; services/receiving/receive-stock.ts;
    services/cost/*.ts; services/valuation/inventory-valuation.ts; services/persistence/*.ts;
    aws/api/purchasing.routes.ts, purchasing.schemas.ts, purchasing-api-mapper.ts;
    tests/e2e/purchasing.e2e.spec.js;
    tests/integration/services/receiving/purchasing-flow.integration.spec.js,
    cost/lifo-costing.integration.spec.js.
---

# Purchase Order

## What It Models

A purchase order records stock requested from a supplier. Its related receipts and cost records explain acquisition value, later cost relief and restoration, and the facts Accounting needs to post. Vendor details and business-unit costing choices support that work.

## Identity and Attributes

Display numbers identify purchase orders and receipts for staff; receipt keys also identify repeated requests. Line quantities determine the outstanding remainder. Layers retain acquisition cost, and relief and restore records identify which cost was consumed or returned.

## Relationships

Receiving allocates sellable stock to backorders within the receipt transaction. Inventory movements and shipping call the cost boundary when stock leaves, changes or returns. Accounting consumes posting facts and supplies the posted reference.

## Invariants

Receipt replay, quantity limits and transactional stock-and-cost writes protect receiving. Database checks protect layer and relief arithmetic. Posting stamps reject conflicting references; method changes preserve historical costing, and referenced suppliers cannot be deleted.

## How It Is Stored and Shared

Purchasing owns commercial acquisition and cost records. Inventory and shipping own physical stock, warehouse, allocation and shipment records. Their in-process calls share transactions with costing. Moving purchasing into fulfilment remains an unresolved ownership choice.

## Open Design Questions

Cancelling every outstanding unit can label an untouched issued order received. Purchasing and costing have no console administration screens. Replacement schemas can clear omitted values, and the future ownership boundary remains undecided.
