Docs/Inventory Ledger

Inventory Ledger

Organization-wide parts pool, append-only ledger, manual adjustments, and auto-credits / auto-deducts

Last updated: 2026-05-27

Inventory in S3Suite is a single organization-wide parts pool with an append-only ledger behind it. Every change to a part's on-hand count (manual adjustment, PO delivery, device build) writes a ledger row. The current on-hand number is just the materialised sum, so you can always answer "why does this part show 47 on hand?" by scrolling its history.

There is one inventory per organization. Projects, variants, and lots draw from the same shared pool. Negative stock is allowed by design - the system would rather show you a real shortfall than refuse to record a build.


Why a ledger model

Spreadsheet inventory drifts the moment two people edit it at once. A ledger model fixes that:

  • Every change is a row with actor, reason, delta, and the resulting balance
  • The on-hand number is a materialised count, not a hand-edited cell
  • Concurrent operators cannot overwrite each other; each writes a new row
  • "Who removed 50 of this part on Tuesday?" is answerable in two clicks
This is the discipline that makes inventory trustworthy across an engineering team and a manufacturing line working from the same pool.

Concepts

TermWhat it means
Inventory ItemA unique part number in your organization's pool. Compound unique on (organization, partNumber).
On handThe current materialised count. Computed from the sum of all ledger deltas. Allowed to go negative.
Ledger entryAn append-only row recording one mutation. Fields: source, delta, comment, actor, references.
SourceWhy the entry exists: manual-add, manual-remove, po-delivered, device-built.
Low only filterThe inventory list view's lowOnly toggle, narrowing to rows with onHand <= 0 (negative + exact-zero).

Quick start (2 minutes)

1. Open the inventory page

  1. From the sidebar, click Inventory (top-level, organization-scoped)
  2. The list shows every part in your org with current on-hand, last updated, and a search box

2. Add stock manually

  1. Click Adjust stock
  2. Enter the part number (existing or new - new parts are auto-created)
  3. Enter a positive delta (e.g. 100)
  4. Enter a comment (required) explaining the source (e.g. "Counted in stockroom 2026-05-27")
  5. Save
A new ledger entry is written with source manual-add. The on-hand count goes up by the delta.

3. Remove stock manually

Same flow, but enter a negative delta (e.g. -5). Source is recorded as manual-remove. The comment is required.

4. Inspect history

  1. Click any row to open the detail panel
  2. The overview tab shows the 10 most recent ledger entries
  3. The ledger tab paginates through every entry, newest first

Auto-credits from procurement

When a Purchase Order is marked delivered (Manufacturing → Procurement → PO detail → Mark delivered), every line that received parts auto-credits inventory:

  • Source: po-delivered
  • Refs: PO id, vendor name, lot id, BOM line index
  • One ledger row per delivered line
You do not need to manually add stock after a PO arrives - the procurement flow does it for you.
Note: The credit fires once per line, atomically. If two operators try to mark the same PO delivered at once, one wins; the other sees a 409 telling them the line moved underneath them.

Auto-deducts on device build

When a device is created against a lot (via the production station kiosk, or the Manufacturing → Lot → Add Device flow), the BOM lines for that lot's variant are read and a per-unit quantity is deducted from inventory:

  • Source: device-built
  • Refs: project id, variant id, lot id, device id and label, BOM line index
  • One ledger row per BOM line that contributed
  • Fires after the device record is saved; the auto-deduct does not block device creation
This is fire-and-forget. If a part has no inventory item yet, one is created with a starting balance of -quantity (the deduct is recorded; the shortfall is visible immediately in the low-only filter).
Tip: Open the inventory page with the lowOnly filter on after a long manufacturing run. Anything in red has been over-consumed and needs restocking before the next lot.

Workflow: investigating a shortfall

"BOM line says we need 250 of P-1234, the system shows 12. Where did 238 go?"

  1. Open Inventory, search for P-1234
  2. Click the row, open the Ledger tab
  3. Scroll the entries newest first. Each row shows source, delta, actor, comment, and timestamp
  4. Filter by source if you need to (e.g. only device-built to see which lots consumed it)
  5. The org-wide ledger view (under Inventory → org ledger) can also be filtered by partNumber, source, projectId, and date range
If the shortfall is real, place a procurement order (see the Procurement doc). If it is a counting error, add a manual-add adjustment with a comment explaining the recount.

Reference

Permissions

  • Read (/inventory list, detail, ledger): any authenticated org member
  • Adjust (POST /inventory/adjust): writer roles (Org Admin, Project Lead, Manufacturing Lead). Org Viewer cannot.
  • Super-admin users with no organization see an empty list (intentional - inventory is org-scoped)

Validation

  • partNumber is required, max 100 chars
  • delta is a non-zero integer (positive or negative)
  • comment is required, max 500 chars
  • Validation failures return 400 with details: string[] so the UI surfaces them via buildErrorMessage

Offline behaviour

  • Inventory mutations are not queueable. If the client is offline, the adjust button fails loudly instead of silently deferring.
  • Reason: silent deferral causes "the system says we have 500, build the lot" while the real server state has not moved. The user must retry deliberately when reconnected.

Common pitfalls

  • "On-hand shows -47." That is intentional. The system shows the real shortfall. Restock or place a PO; do not "correct" it with a manual add unless you actually have the parts in hand.
  • "I cannot delete a ledger entry." Correct. The ledger is append-only. If a row is wrong, add a reversing row with a comment explaining the correction.
  • "Two adjusts happened at the same time and the count looks off." Each adjust writes its own row atomically; on-hand reflects the sum. Open the ledger tab to verify both rows landed.

Related docs