Inventory & Warehouse — Process flows
Architecture & lifecycle auto-generated from code (dispatch, CHECK status enums, SET status writes). Sequences & notes are authored.
Architecture
%%{init: {"theme":"base","themeVariables":{"darkMode":true,"background":"#1e1e1e","primaryColor":"#2d2d30","primaryBorderColor":"#569cd6","primaryTextColor":"#d4d4d4","lineColor":"#6796c6","secondaryColor":"#3a3d41","secondaryBorderColor":"#dcdcaa","tertiaryColor":"#252526","tertiaryBorderColor":"#569cd6","clusterBkg":"#252526","clusterBorder":"#569cd6","titleColor":"#569cd6","noteBkgColor":"#3a3d41","noteTextColor":"#dcdcaa"},"flowchart":{"useMaxWidth":false},"state":{"useMaxWidth":false}}}%%
flowchart LR
C["Client"] --> A["Pages Fn inventory<br/>bolt_session auth · company_id scope"]
A --> D["dispatch<br/>REST resources"]
D --> K["Kernel<br/>erp_document + journal<br/>atomic GL / stock"]
K --> DB[("D1 tables")]
D --> DB
DB -.-> T["item<br/>item_variant<br/>inventory_cycle_count<br/>inventory_cycle_count_line<br/>inventory_reservation<br/>inventory_pick_list<br/>inventory_pick_list_line"]
Lifecycle
Kernel document lifecycle (erp_document.docstatus)
Every money/stock-moving document in this module posts through the shared kernel, walking a fixed docstatus:
%%{init: {"theme":"base","themeVariables":{"darkMode":true,"background":"#1e1e1e","primaryColor":"#2d2d30","primaryBorderColor":"#569cd6","primaryTextColor":"#d4d4d4","lineColor":"#6796c6","secondaryColor":"#3a3d41","secondaryBorderColor":"#dcdcaa","tertiaryColor":"#252526","tertiaryBorderColor":"#569cd6","clusterBkg":"#252526","clusterBorder":"#569cd6","titleColor":"#569cd6","noteBkgColor":"#3a3d41","noteTextColor":"#dcdcaa"},"flowchart":{"useMaxWidth":false},"state":{"useMaxWidth":false}}}%%
stateDiagram-v2
[*] --> Draft: createDocument (docstatus 0)
Draft --> Submitted: submitDocument (docstatus 1, journal posted)
Submitted --> Cancelled: cancelDocument (docstatus 2, reversed)
Submitted --> [*]
Cancelled --> [*]
inventory_pick_list.status
States: draft (initial) · allocated · ready · in_progress · posted · cancelled
inventory_pick_ticket.status
States: queued (initial) · allocated · posted · cancelled
inventory_repost_request.status
States: queued (initial) · in_progress · completed · failed
Key flow: Goods Issue (submitIssue)
A goods-issue permanently relieves inventory value, so it moves the subledger and posts a balanced GL journal at the kernel-valued outbound cost (whenever that value is > 0).
%%{init: {"theme":"base","themeVariables":{"darkMode":true,"background":"#1e1e1e","primaryColor":"#2d2d30","primaryBorderColor":"#569cd6","primaryTextColor":"#d4d4d4","lineColor":"#6796c6","actorBkg":"#2d2d30","actorBorder":"#569cd6","actorTextColor":"#d4d4d4","signalColor":"#9cdcfe","signalTextColor":"#d4d4d4","noteBkgColor":"#3a3d41","noteTextColor":"#dcdcaa","labelBoxBkgColor":"#3a3d41","labelBoxBorderColor":"#dcdcaa"},"sequence":{"useMaxWidth":false}}}%%
sequenceDiagram
participant C as Client
participant F as Inventory Fn
participant S as Stock subledger
participant P as Removal policy
participant K as Kernel
participant G as GL journal
C->>F: POST issue/submit qty variant location
F->>F: auth + capability check (inventory/erp/manage)
F->>S: availability = onHand minus reserved
Note over F,S: reserved = WMS + sales hard reservations
alt qty > available
F-->>C: 400 insufficient ATP for issue
end
F->>P: resolveRemovalPolicy (returns strategy)
F->>S: pickLots order lots FIFO/FEFO/LIFO (CLOSEST degrades to FIFO here)
F->>S: unitCostForOutbound = value / qty
F->>K: createDocument WMS-ISSUE docstatus 0
F->>K: submitDocument stockLines + journal (journal only if amount > 0)
K->>K: idempotency fingerprint + posting-period lock
K->>S: insert qty_delta value_delta (negative)
K->>G: balanced Dr/Cr from reason (amount re-derived from stock)
K->>K: flip docstatus 0 to 1
K-->>F: document + stock_rows
F-->>C: ok strategy picks document stock_rows
Notes
- Purpose. One WMS endpoint covers all stock moves — transfer, adjustment, cycle-count, issue, putaway, pick, and reservation — plus a read surface (
GETon reservation / cycle-count(+line) / pick-list(+line) / putaway-rule / removal-policy / adjustment-reason /erp_stock_ledger_entry). Everything iscompany-scoped for multi-tenant isolation; the mutatingPOSTsurface is gated on capabilityinventory/erp/manage, the readGETsurface onmanage/erp/finance/accounting. - Kernel-mediated posting. Handlers never insert into
erp_stock_ledger_entryorerp_journal_entrythemselves. They build anintent(stockLines+ optionaljournal) and callpostKernelDocument→kernel.createDocument(createserp_documentatdocstatus 0) →kernel.submitDocument(flips0→1, writes subledger + GL). The kernel doc lifecycle is0 draft → 1 submitted → 2 cancelled, enforced by theerp_document_docstatus_lifecycletrigger (erp_document_immutable_after_submitlocks a submitted doc). - Append-only subledger + avg-cost valuation.
erp_stock_ledger_entryis immutable —BEFORE UPDATE/BEFORE DELETEtriggersRAISE(ABORT). On-hand and value are always derived:kernel.getOnHandsumsqty_delta/value_delta; average unit cost for outbound =value / qty. Lot ordering for a goods-issue is done inpickLots, which honors FIFO / FEFO / LIFO only — aCLOSESTremoval policy falls through to FIFO order in the issue path (CLOSEST is honored only instockQuantRows, the pick-list/putaway allocator). - Money-safety / idempotency.
idempotency_keydedupes replays with a fingerprint check — the same key reused for a different operation is rejected loudly (409) rather than returning a stale doc's rows (legacy NULL-fingerprint stock rows replay without the check). The posting-period lock is enforced kernel-wide on bothposting_dateandeffective_date, so a backdatedeffective_datecannot slip stock into a closed month. - Cross-module ATP.
reservedQtysubtracts both WMS reservations (erp_inventory_reservation, statesactive/partially_consumed) and live sales hard reservations (erp_sales_reservation_entry,reservation_kind='hard', unexpired), so WMS can never consume stock that Sales already promised. - Value integrity gotchas. Transfer receipts inherit the issue-leg's actual consumed cost (
value_from_issue) instead of a whole-pool average, keeping subledger value aligned with GL on FIFO transfers; journal magnitudes are re-derived by the kernel from the stock value movement (amount_from_stock). Cycle counts enforce maker/checker separation via thecounted_by <> approved_byDB CHECK (and a JS pre-check) and post a variance adjustment only whendelta != 0.