Skip to main content

Inventory module

The stock console. SKU search → variant detail → Product Card editor → stock-movement ledger. Split out of the former two-face Products module in v0.7.69; the Purchasing side became its own Purchases module.

Tab labelInventory
Tab accent#0d9488 (teal)
Display order21 (right after Purchases at 20)
Foldersrc/modules/inventory/
Migrations shared018_products_catalog.sql, 019_inventory.sql, 022_products_perms_consolidate.sql, 024_rma.sql, 032_bundled_accessory_tax.sql

What it owns

src/modules/inventory/
index.ts — registers the module with the Kernel
view.ts — markup + styles for the stock console
client.ts — browser JS (search, variant detail, Product Card editor, movements)
api.ts — server-side routes (/api/m/inventory/*)

The inventory-movement engine lives at core/movements.ts — extracted to Core in v0.7.69 so Sales and Service can decrement stock through the same code path Inventory does. Every change to inventory.quantity_on_hand writes one immutable row to inventory_movements.

What it does

SKU search. Free-text across product name, variant SKU, barcode; returns matching variants with on-hand readout per row. Picking one loads the variant detail.

Variant detail — read-only. Rebuilt to prior-implementation Inventory-tab parity in v0.7.67. Sections:

SectionContent
HeroProduct name + variant descriptor (size / colour / year) + SKU + primary image
StockOn-hand · reorder point · on-order · low-stock flag (tabular numerals so the numbers align)
VendorsThe variant's supplier links (supplier_products — supplier, cost, primary flag). Rich vendor record (account #, address, website / B2B portal, logo, drop-ship) opens from a row click
IDsBarcode / UPC · aim_id · internal id
DescriptionFree-form product description + specs

Product Card editor. The full editable surface for a product + its variants — one dialog covering every field on every section. Replaces the older Face-A section-edit chain that was removed in v0.7.68 (that chain was orphaned once the Product Card superseded it). Save writes to the catalog + inventory rows; the audit chain captures the diff.

The three stock actions. The only way quantity_on_hand changes from this surface:

ActionProcessWhat it does
AdjustBIKE.L2-0046Signed delta with a reason; writes a movement_type='adjust' row via core/movements.ts
Cycle countBIKE.L2-0084Set the on-hand to the counted number; movement records the implied delta
Write-offBIKE.L2-0085Removes units with a reason (damage, theft, demo)

Every action goes through Core's movement engine — write the movement row, then update inventory.quantity_on_hand, then audit. The on-hand can never drift from the sum of its movements.

Stock-movement ledger — 7 analytical views (BIKE.L2-0149 + 0145). The ledger pane at the bottom carries a Tier-2 view Pill that selects one of seven views:

ViewShape
todayMovement-shaped: every movement in the last 24 hours
sold_todayMovement-shaped: sales-type movements today
last_receivedMovement-shaped: most-recent receive movements
top_sellersVariant-shaped: highest sale volume in the window
bottom_sellersVariant-shaped: lowest sale volume
out_of_stockVariant-shaped: quantity_on_hand <= 0
old_stockVariant-shaped: no movement in N days

/movements?view=X is the unified endpoint. The ledger <thead> + rows adapt per shape; variant rows click through back into the variant detail. Replaces the older movement-type filter dropdown.

Recent purchasers (BIKE.L2-0148). The variant detail surfaces a Recent purchasers section — who bought this SKU, newest first, with the sale_id for each. Click a row to open the sale in a read-only viewer (GET /api/m/sales/transaction).

Reorder dashboard (BIKE.L2-0088 / 0089). A low-stock surface that lists every variant at or below its reorder_point, with a per-row Add to Bucket action pre-quantitied to reorder_qty. Reachable regardless of low-stock count (v0.7.60 fix; empty state reads "All stocked above reorder point").

Print tag (BIKE.L2-0047). Variant detail footer Print tag opens a preview of the SKU tag — product name, price, a hand-rolled CODE39 barcode of the SKU, SKU text — then Print opens a print window.

Bundled-accessory tax rule (v0.7.81, migration 032). Accessories bundled with a bike at Sales time drop PST (BC PST Bulletin 204); standalone they carry both GST + PST. The tax_categories seed carries is_context_dependent + context_rule for the bundled case; Sales computePerLineTaxes reads the context at cart time.

Vendor RMA. Vendor returns (migration 024, BIKE.L2-0091) — rma header (status: draft / sent / received / closed / cancelled) + rma_lines (per-variant qty / reason / disposition). Operator UI ships from this module's Vendors surface.

Data model

Catalog (018). tax_categories (BC two-rate model + bundled-accessory context; migration 032 seeds the context rule), tax_rates, categories + subcategories, manufacturers, products, product_variants (the canonical SKU), product_barcodes.

Inventory (019). inventory_locations (single-shop seeds "Main Shop"), inventory (per variant per location: quantity_on_hand, reorder thresholds), inventory_movements (the immutable ledger — type, signed quantity, before / after on-hand, reason, originating reference).

Vendor RMA (024). rma + rma_lines.

Permissions

PermissionWhat it gates
screen.inventoryThe Inventory tab is visible at all
inventory.readSearch + variant detail + movement ledger
inventory.editProduct Card editor + Adjust / Cycle count / Write-off
inventory.rmaVendor RMA lifecycle

See also