Skip to content

Demand: Data Model

The Demand module stores two occurrence-carrying bitemporal tables — demand_item and production_delivery — each appending a version row per mutation rather than editing in place. Its subjects are a demand item (a card-cycle collation task: what’s wanted, how much, which kanban cards are attached, what’s been produced against it) and a production delivery (one recorded production event against a demand item). This page is the field-complete reference for both tables and their DTOs; see Data Models — Reading Guide for the shared envelope shapes referenced throughout.

production_delivery is demand’s own child table (demand_ref, same-module, no FK). demand_item carries three floating references out of the module: to the item it produces, to the kanban cards it collates (membership), and to inventory pools (a single holding_ref pool plus a per-member member_pools map — an interim Phase-3 bridge).

PlantUML diagram

Occurrence columns were renamed by V004 (kind→occ_kind, outcome→occ_outcome, reason→occ_reason); the Kotlin property names are unchanged.

ColumnTypeNullRole
idUUIDNOT NULL (PK)identity
tenant_idUUIDNOT NULLidentity
effective_as_oftimestamptzNOT NULLidentity
recorded_as_oftimestamptzNOT NULL, default now()identity
previousUUIDNULLidentity
eidUUIDNOT NULLidentity
retiredbooleanNOT NULL, default falsebookkeeping
bts_authorvarchar(255)NOT NULLbookkeeping
bts_author_subvarchar(255)NULLbookkeeping
bts_created_by_subvarchar(255)NULLbookkeeping
bts_notevarchar(512)NULLpayload
created_byvarchar(255)NOT NULL, default ‘system’bookkeeping
created_at_effectivetimestamptzNOT NULL, default now()bookkeeping
created_at_recordedtimestamptzNOT NULL, default now()bookkeeping
item_reference_entity_idUUIDNULLpayload
item_reference_item_namevarchar(255)NULLbookkeeping
item_reference_record_idUUIDNULLbookkeeping — reserved, unwritten; see Schema notes
item_reference_retiredbooleanNOT NULL, default falsebookkeeping
item_reference_provenance_updated_byvarchar(255)NULLbookkeeping
item_reference_provenance_updated_atbigintNULLbookkeeping
station_eidUUIDNOT NULLpayload — opaque, never resolved
occ_kind (was kind)varchar(64)NOT NULLpayload
occ_outcome (was outcome)varchar(16)NOT NULLpayload
occ_reason (was reason)varchar(64)NULL, set iff REFUSEDpayload
notification_idUUIDNULLpayload
statusvarchar(32)NOT NULLpayload
demand_qtyJSONBNOT NULLpayload
membershipJSONBNOT NULLpayload — full set of attached CardCycle eIds
holding_refUUIDNULLpayload — the holding InventoryPool eId, set by Start Production
payloadJSONBNULLpayload — per-kind relation-shaped payload (e.g. Distribute allocation)
member_poolsJSONBNOT NULL, default {}payload — member CardCycle eId → member InventoryPool eId, interim Phase-3 bridge

Unique constraints: demand_item_committed_previous — UNIQUE partial index on (previous) WHERE occ_outcome = 'COMMITTED', the P2-H1 committed-successor fork guard.

Demand’s second occurrence-carrying table (PDEV-1528). Same envelope shape as demand_item; occurrence columns renamed identically by V004.

ColumnTypeNullRole
idUUIDNOT NULL (PK)identity
tenant_idUUIDNOT NULLidentity
effective_as_oftimestamptzNOT NULLidentity
recorded_as_oftimestamptzNOT NULL, default now()identity
previousUUIDNULLidentity
eidUUIDNOT NULLidentity
retiredbooleanNOT NULL, default falsebookkeeping
bts_authorvarchar(255)NOT NULLbookkeeping
bts_author_subvarchar(255)NULLbookkeeping
bts_created_by_subvarchar(255)NULLbookkeeping
bts_notevarchar(512)NULLpayload
created_byvarchar(255)NOT NULL, default ‘system’bookkeeping
created_at_effectivetimestamptzNOT NULL, default now()bookkeeping
created_at_recordedtimestamptzNOT NULL, default now()bookkeeping
demand_refUUIDNOT NULLidentity — same-module parent link
item_eidUUIDNOT NULLpayload
quantityJSONBNOT NULLpayload — immutable at Create
source_handlevarchar(128)NULLpayload — never set; see Schema notes
occ_kind (was kind, widened)varchar(64)NOT NULLpayload
occ_outcome (was outcome)varchar(16)NOT NULLpayload
occ_reason (was reason)varchar(64)NULL, set iff REFUSEDpayload
statusvarchar(16)NOT NULLpayload — CREATED / REVOKED

Unique constraints: production_delivery_committed_previous — the same P2-H1 fork guard, UNIQUE partial index on (previous) WHERE occ_outcome = 'COMMITTED'.

FieldTypeStored or computedNotes
eIdEntityIdStored← eid
itemItemReferenceStoredthe item_reference_* cluster
stationEIdUUIDStored
kindDemandOccurrenceKindStored← occ_kind
outcomeDemandOutcomeStored← occ_outcome
reasonString?Stored
notificationIdUUID?Stored
statusDemandStatusStored
demandQtyGeneralizedQuantityStored← demand_qty
membershipSet<UUID>Stored
holdingRefUUID?Stored
payloadJsonElement?Stored
memberPoolsMap<UUID,UUID>Stored
FieldTypeStored or computedNotes
eIdEntityIdStored
demandRefUUIDStored
itemEIdUUIDStored
quantityGeneralizedQuantityStored
sourceHandleString?Storednever populated in practice — see Schema notes
kindProductionDeliveryOccurrenceKindStored
outcomeDemandOutcomeStored
reasonString?Stored
statusProductionDeliveryStatusStored

DemandView (internal read projection — not directly serialized by any endpoint)

Section titled “DemandView (internal read projection — not directly serialized by any endpoint)”
FieldTypeStored or computedNotes
itemDemandItemComputedDemandItemPayload.toView()
stateDemandStateComputedstatus, demandQty, membership, holding re-typed from the row
rIdDemandItemRIdComputedthe row’s own rId
atTimeCoordinatesComputedthe row’s own bitemporal coordinates
FieldTypeNotes
occurrenceOccurrenceDtosee Data Models — Reading Guide
recordEntityRecord<DemandItemPayload, DemandItemMetadata>full row projection, no hand-rolled view
ColumnTargetKindNotes
item_reference_entity_idItemfloatingguard-time liveness check re-reads by eId on every genesis
item_reference_item_name, _retired, _provenance_*Itemvalue-snapshotonly entity_id + item_name populated as of Increment 3; retired/provenance await observer-driven sync
item_reference_record_idItem version rowreserved version-pin slotnever populated — see Schema notes
station_eidresources.station.Stationfloating in shape, opaque in practicenever resolved; pinned to == tenantId as validation only
membership (JSONB set)Kanban Cards — CardCyclefloatingeach entry is a card’s own eId; re-resolved on every read, never pinned
member_pools (map values)Inventory Poolsfloatingmember eId → pool eId; re-resolved each call
holding_refInventory Poolsfloatingminted once by Start Production, re-resolved by eId on every content read
production_delivery.item_eidItemfloatingcompared against demand_item’s item for the RWrongItem guard; never fetched as a record
production_delivery.source_handle(external producing process)not a modeled referenceopaque, unresolved reconciliation handle

production_delivery.demand_ref is a same-module parent-child link (1:N, stores the DemandItem’s eId, no FK). Note: RecordProductionRequest.deliveryPool is read and merged into the holding pool but isn’t stored anywhere — no persisted relationship exists for that leg once the merge completes.

Mounted under /v1/operations/demand/demand-item/.... Follows the request/response call-shape convention in The Orders v2 Call Shape.

MethodPathPurposeMain refusals
POSTcreateCreate a card-less demand task
POSTadjust-qtySet the advisory intent total
POSTreset-qtySnap intent to members’ sum
POSTrelease / reopenOPEN ↔ RELEASED
POSTcancelOPEN, no cards → CANCELED
POSTdeleteTombstone a terminal task
POSTcreate-with-cycleCreate from its first card
POSTadd-cycle / remove-cycleAdjust card membership
POSTdetach-withdrawnReconcile a withdrawn attachment (manual repair)
POSTstart-productionMint the holding pool
POSTrecord-productionRecord a production deliverywrong item
POSTrevoke-deliveryTerminally revoke a recorded delivery
POSTdistributeAllocate accumulated production to members
POSTcompleteIN_PROCESS → COMPLETE
GETfor?item&stationLive demand items for an (item, station) pair
GETholding-card/{cardEId}?effectiveasof&recordedasofThe live demand holding a member card404 if none
GET{eId}?effectiveasof&recordedasofA demand item as of a coordinatetombstoned = 404
GET{eId}/log?page&sizeOccurrence log
GET{eId}/deliveries?effectiveasof&recordedasofProduction deliveries recorded against this item

Emits ProductionRecorded/ProductionRevoked for Orders to subscribe to. Listens for kanban’s WITHDRAW/REQUEST events to drive the detachWithdrawn reconciliation, and (Increment 5, default-off) a periodic law-probe runner that can self-heal via the same detachWithdrawn capability.

  • demand_item.item_reference_record_id is reserved but unwritten. The would-be version pin is declared and read but never populated by either creation path (DemandServiceImpl.create() or DemandMembershipSagas.createWithCycle()).
  • production_delivery.source_handle is never set. No writer populates it on any code path found — it’s an opaque reconciliation handle that no law reads.