Skip to content

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 (GET on reservation / cycle-count(+line) / pick-list(+line) / putaway-rule / removal-policy / adjustment-reason / erp_stock_ledger_entry). Everything is company-scoped for multi-tenant isolation; the mutating POST surface is gated on capability inventory/erp/manage, the read GET surface on manage/erp/finance/accounting.
  • Kernel-mediated posting. Handlers never insert into erp_stock_ledger_entry or erp_journal_entry themselves. They build an intent (stockLines + optional journal) and call postKernelDocumentkernel.createDocument (creates erp_document at docstatus 0) → kernel.submitDocument (flips 0→1, writes subledger + GL). The kernel doc lifecycle is 0 draft → 1 submitted → 2 cancelled, enforced by the erp_document_docstatus_lifecycle trigger (erp_document_immutable_after_submit locks a submitted doc).
  • Append-only subledger + avg-cost valuation. erp_stock_ledger_entry is immutable — BEFORE UPDATE/BEFORE DELETE triggers RAISE(ABORT). On-hand and value are always derived: kernel.getOnHand sums qty_delta/value_delta; average unit cost for outbound = value / qty. Lot ordering for a goods-issue is done in pickLots, which honors FIFO / FEFO / LIFO only — a CLOSEST removal policy falls through to FIFO order in the issue path (CLOSEST is honored only in stockQuantRows, the pick-list/putaway allocator).
  • Money-safety / idempotency. idempotency_key dedupes 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 both posting_date and effective_date, so a backdated effective_date cannot slip stock into a closed month.
  • Cross-module ATP. reservedQty subtracts both WMS reservations (erp_inventory_reservation, states active/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 the counted_by <> approved_by DB CHECK (and a JS pre-check) and post a variance adjustment only when delta != 0.