Kanban Cards: Data Model
The Kanban Cards module stores one table, kanban_card: a physical replenishment card, its serial number, the item it’s tied to, its quantity and physical location, and its lifecycle and print-lifecycle state. This page is the field-complete reference for that table and its DTOs; see Data Models — Reading Guide for the shared envelope shapes referenced throughout, and for why this module predates the occurrence pattern the rest of the stack uses.
Entities and relationships
Section titled “Entities and relationships”kanban_card is bitemporal like every other module’s tables, but has no *OccurrenceKind enum — state transitions run through two flat event-type enums instead (see Schema notes). The card’s own eid doubles as CardCycleId, the phantom-typed reference Demand uses for card membership; there is no separate card-cycle table.
Stored shape
Section titled “Stored shape”kanban_card
Section titled “kanban_card”The bitemporal-coordinate and bookkeeping columns come from a shared base class rather than being declared directly in this module’s table object; their names and types are confirmed from the SQL migrations.
| Column | Type | Null | Role |
|---|---|---|---|
id | UUID | NOT NULL (PK) | identity |
tenant_id | UUID | NOT NULL | identity |
effective_as_of | timestamp | NOT NULL | identity |
recorded_as_of | timestamp | NOT NULL, default now() (since V004) | identity |
previous | UUID | NULL, FK → kanban_card(id) | identity |
eid | UUID | NOT NULL | identity |
retired | boolean | NOT NULL, default false | bookkeeping |
bts_author (was author) | varchar(255) | NOT NULL | bookkeeping |
kanban_card_sn | varchar(255) | NOT NULL | payload — natural identifier; see Schema notes |
item_reference_entity_id | UUID | NULL | payload |
item_reference_item_name | varchar(255) | NULL | bookkeeping |
item_reference_record_id | UUID | NULL | bookkeeping — version-pin, used on tombstone resolution |
card_quantity_amount | double precision | NULL | payload |
card_quantity_unit | varchar(255) | NULL | payload |
physical_locator_facility | varchar(255) | NULL | payload |
physical_locator_department | varchar(255) | NULL | payload |
physical_locator_location | varchar(255) | NULL | payload |
physical_locator_sub_location | varchar(255) | NULL | payload |
last_event_at_time_effective | bigint | NULL | payload |
last_event_at_time_recorded | bigint | NULL | payload |
last_event_kanban_card_event_type | varchar(255) | NULL | payload |
last_event_from_where_facility/department/location/sub_location | varchar(255) | NULL | payload |
last_event_to_where_facility/department/location/sub_location | varchar(255) | NULL | payload |
last_event_author | varchar(255) | NULL | payload |
status | varchar(255) | NULL | payload — computed projection, see Projected shape |
last_print_event_at_time_effective | bigint | NULL | payload |
last_print_event_at_time_recorded | bigint | NULL | payload |
last_print_event_kanban_card_event_type | varchar(255) | NULL | payload |
last_print_event_author | varchar(255) | NULL | payload |
print_status | varchar(255) | NULL | payload — computed projection |
created_by | varchar(255) | NOT NULL | bookkeeping |
created_at_effective | timestamp | NOT NULL | bookkeeping |
created_at_recorded | timestamp | NOT NULL | bookkeeping |
notes | varchar(8192) | NULL | payload — seeded from the item’s cardNotesDefault, then independently editable |
bts_author_sub (was oidc_sub) | varchar(255) | NULL | bookkeeping |
bts_note (was note) | varchar(512) | NULL | payload |
bts_created_by_sub | varchar(255) | NULL | bookkeeping |
item_reference_retired | boolean | NOT NULL, default false | bookkeeping — denormalized item-deleted flag (PDEV-808) |
item_reference_provenance_updated_by | varchar(255) | NULL | bookkeeping |
item_reference_provenance_updated_at | bigint | NULL | bookkeeping |
Primary key: id. Unique constraints: none — see Schema notes. Foreign keys: fk_kanban_card_previous__id (previous → kanban_card(id), RESTRICT).
Other bookkeeping object: kanban_card_serial_number, a Postgres SEQUENCE (not a column) that mints kanban_card_sn values.
Projected shape
Section titled “Projected shape”KanbanCard (persisted payload / API record body)
Section titled “KanbanCard (persisted payload / API record body)”| Field | Type | Stored or computed | Notes |
|---|---|---|---|
eId | EntityId | Stored | |
serialNumber | String | Stored | |
item | ItemReference | Stored | floating ref + denormalized retired/provenance |
cardQuantity | Quantity.Value? | Stored | |
locator | PhysicalLocator.Value? | Stored | |
lastEvent | KanbanCardEvent? | Stored | |
status | KanbanCardStatus? | Computed | the lifecycle transition table, from current status + the incoming event type; also seeded directly at creation |
lastPrintEvent | KanbanCardPrintEvent? | Stored | |
printStatus | KanbanCardPrintStatus? | Computed | same pattern, print-lifecycle transition table |
notes | String? | Stored | reconciled on item change |
KanbanCardDetails (read model, details/print endpoints)
Section titled “KanbanCardDetails (read model, details/print endpoints)”Same base fields as KanbanCard, plus:
| Field | Type | Stored or computed | Notes |
|---|---|---|---|
rId | UUID? | Stored | the bitemporal version row id |
lookupUrlId | String? | Computed | base64/gzip-encoded UrlLookup(cardId, tenantId, rId) |
item | ItemReference | Computed | overrides the card’s cached reference with the resolved Item record’s current state |
itemDetails | Item.Entity | Computed | resolved via an asOf or tombstone read |
KanbanCardSummary (aggregate DTO, summary/* endpoints)
Section titled “KanbanCardSummary (aggregate DTO, summary/* endpoints)”| Field | Type | Stored or computed | Notes |
|---|---|---|---|
eId | UUID | Computed | synthetic — a summary row is a group, not a card |
item | ItemReference | Computed | GROUP BY classifier |
quantity | List<Quantity> | Computed | SUM(card_quantity_amount) grouped by (status, item, unit) |
status | KanbanCardStatus | Stored | GROUP BY classifier |
KanbanCardStateChange / KanbanCardPrintStateChange (lifecycle-event response envelopes)
Section titled “KanbanCardStateChange / KanbanCardPrintStateChange (lifecycle-event response envelopes)”| Field | Type | Notes |
|---|---|---|
event | KanbanCardEvent / KanbanCardPrintEvent | built for this call |
previous | EntityRecord<KanbanCard, KanbanCardMetadata> | pre-mutation record |
current | EntityRecord<KanbanCardDetails, KanbanCardMetadata> | post-mutation, composed |
Cross-module references
Section titled “Cross-module references”| Column | Target | Kind | Notes |
|---|---|---|---|
item_reference_entity_id | Item | floating | primary pointer, resolved via itemService.getAsOf |
item_reference_record_id | Item version row | version-pin | used only when item_reference_retired = true, to resolve the frozen tombstone version |
item_reference_retired, item_reference_provenance_* | Item | value-snapshot | refreshed on item update/delete notifications (PDEV-808) |
physical_locator_* | none | plain value object | no separate Facility/SubLocation entity exists — free-text columns |
| — | Inventory Pools | not referenced | searched, no references found |
card’s own eid (as CardCycleId) | Demand — membership | floating, consumer-side | the card’s eid is the cycle id; no cycle table exists in this module |
previous is the bitemporal version chain (self-reference, same entity), not a cross-module link.
API surface
Section titled “API surface”Mounted as the kanban-card resource. Standard CRUD (create/readByEid/readByRId/update/delete/query/history/bulk) is framework-generated; only the custom routes are listed here.
Kanban does not follow the shared call-shape convention — it is the documented exception to it. Card events are POST .../{eId}/event/{event} with no body and a single optional effectiveasof query parameter — no recordedasof; system time is the server’s on this route — and an event response returns {event, previous}: the card’s status before the event, not after. See The Orders v2 Call Shape, which records why — this module predates the convention.
| Method | Path | Purpose |
|---|---|---|
| POST | summary/generate | Cards grouped by item/status |
| POST | summary/requested/generate, summary/in-process/generate | Status-restricted summaries |
| GET | details/{eId} | Extended details of one card |
| POST | details/generate, details/by-status/{status} | Query cards, return details |
| GET | details/by-page/{page-id} | Cursor-paged details continuation |
| GET | for-item/{itemEId} | Cards linked to one item |
| POST | cards-by-items/by-items | Cards for several items, grouped |
| POST | print-cards/print-card | Print (Documint render) a set of cards |
| PUT | {eId}/annotate/notes | Update a card’s notes |
| POST | {eId}/event/{request|accept|shelve|start-processing|complete-processing|fulfill|receive|use|deplete|withdraw|production-failure} | Card lifecycle events |
| POST | {eId}/event/unmark | Only print-lifecycle event exposed as its own route |
Notifications
Section titled “Notifications”Listens for item new/update/delete notifications — creates a default card on a new item, refreshes linked cards on update, stamps item_reference_retired on delete (PDEV-808). Emits NewEntity<KanbanCard,...> on card creation. No UpdateEntity/DeleteEntity emission was found for KanbanCard itself — card mutations go through the common-module base update(), whose own notification behavior couldn’t be confirmed from this module’s code.
Schema notes
Section titled “Schema notes”- No database unique constraint on
kanban_card_sn, despite it acting as the card’s natural identifier — uniqueness, if enforced at all, isn’t a DB-level guarantee here. - This module predates the occurrence-kind pattern. There is no
KanbanCardOccurrenceKindenum; state transitions run through two flat event-type enums (KanbanCardEvent,KanbanCardPrintEvent) that store only the single most-recent event inlast_event_*/last_print_event_*columns, not a full occurrence log. Compare this todemand,procurement.order, andresources.inventory, each of which has its own*OccurrenceKindenum — kanban is the one sibling module that hasn’t adopted the pattern. Bringing it onto the same shape as its siblings is planned follow-on work.
Copyright: © Arda Systems 2025-2026, All rights reserved