Inventory
Inventory is what you have on hand right now — every barrel of grain in the warehouse, every bottle in the case room, every drop in a tank. vestl tracks inventory by lot so you always know which specific receipt or production run a unit came from.
Core concepts
Lot. A specific receipt or production run of a product. Lots have a lot number, received date, quantity, location, and remaining quantity. Two batches of the same SKU received on different days are two separate lots.
Location. Where the inventory physically lives. Locations are typed: a tank, a warehouse, or generic storage. Tanks have capacity; warehouses don’t.
On-hand. The sum of remaining_quantity across all available lots of a product. Different from “received” because production batches consume from lots and decrement remaining.
Status. Each lot is available, on_hold, quarantine, consumed, or expired. Only available lots are pickable for production. on_hold and quarantine are visible but blocked from being consumed.
Where inventory comes from
A lot is created in three ways, and the lot type field records which:
- Purchased — created by a purchase receipt (
/purchasing/receipts). The receipt line that created the lot is linked back viapurchase_receipt_line_idso you can audit “where did this lot come from” with one click. - Produced — created when a production batch packages out. The batch’s MO/recipe/bottle BOM/case BOM are pinned to the lot, so traceability flows forward to the finished case.
- Adjusted — created by a manual
/inventory/adjustmentsentry (cycle-count discoveries, found inventory, write-ons). Adjusted lots require a reason note.
Most lots are purchased or produced. Adjusted lots are an exception path and should be rare.
Inventory pages
/inventory — On-hand by product
The main inventory landing page. Shows one row per product with the on-hand quantity rolled up across all available lots. Click into a row to see the lots that make up that on-hand number.
Filters: location, product type (finished goods only / raw materials only), low-stock (below reorder point — see /inventory/reorder).
/inventory/lots — Every lot, individually
The lot-level view. Use this when you need to find a specific lot (debugging traceability, looking up a lot that was flagged in QC, hunting for an expired lot to write off). Sortable by received date, expiry date, status.
/inventory/receive — Receive raw materials
Used for raw material receipts (finished goods are received by closing a production batch, not through this page). Two flows:
- PO-anchored receive — you have an open purchase order; pick the PO line and confirm quantity/lot/location. Most receipts go this way.
- Unplanned receive — material arrived without a PO (samples, returns, vendor errors). Requires a reason note in the lot’s
notesfield — operators want to know why later.
/inventory/adjustments
Manual quantity changes outside the receive/produce/consume flow. Cycle-count discrepancies, write-offs, found inventory. Every adjustment requires a reason and is audit-logged.
/inventory/transfers
Move inventory between locations. Common case: a tank has been emptied and the remainder was siphoned into a different tank — record the transfer so the lot’s location stays accurate.
/inventory/cycle-counts
Periodic physical counts. The page generates a count sheet for a location, you fill it in, and the system records adjustments for any variance.
/inventory/reorder
The reorder dashboard. Surfaces products that are below their reorder point (set per-product on the product detail page). MRP also feeds this view — anything MRP says you’ll need that you don’t have shows up here.
/inventory/reports/expiry-report
Lots that are within N days of their expiry date. Filter by lookahead window (7 / 30 / 90 days). Used for FEFO (first-expiry-first-out) picking and to prevent compliance issues with aged stock.
Field reference (lot detail)
Lot number
The identifier for this specific lot. For purchased lots, you can match it to your vendor’s lot/batch number; for produced lots, it’s auto-generated from the batch ID.
Supplier lot number
The vendor’s own lot/batch identifier from their packaging or COA. Optional, but strongly recommended for raw materials — TTB traceability requires being able to walk from a finished bottle back to the supplier lot of every ingredient.
Received date
The date the lot was actually received (not the PO date, not the day someone got around to entering it). Drives FEFO and aging reports.
Expiry date
When the lot becomes unusable. Optional for non-perishable materials. The expiry report at /inventory/reports/expiry-report flags lots within a configurable lookahead window.
Removal date
When the lot was fully consumed or written off. Set automatically when remaining_quantity hits zero.
Quantity / Remaining quantity
Quantity is what you received; remaining_quantity is what’s left after consumption. Production batches and adjustments decrement remaining; you cannot edit it directly.
Unit cost
The cost per UoM unit at the time of receipt. Used by inventory valuation reports and batch costing. Pulled from the PO line for purchased lots; computed from input costs for produced lots.
Location
Where the lot physically lives. Move it via /inventory/transfers.
Lot status
- Available — pickable for production. Default.
- On hold — visible but blocked from consumption. Use this when QC has flagged the lot pending an investigation.
- Quarantine — segregated for a compliance reason (failed QC, recall, regulatory hold). Requires a
quarantine_reasonnote. Cannot be consumed without manual unblock. - Consumed — fully drawn down to zero remaining.
- Expired — past expiry date, blocked from consumption.
Quarantine reason
Required when status is quarantine. Free-text, but should be specific enough to audit later (“Failed micro test 2026-04-12, retain for retest”). Visible in the audit trail.
Notes
Free-text. Especially useful for unplanned receives (“Vendor sent extra by mistake — kept per email with rep on 2026-04-15”).
Locations
Locations are configured at /settings/locations. Three types:
- Tank — has capacity. Used for liquid in process (mash tuns, fermenters, blending tanks). When capacity is set, the system warns if a transfer would exceed it.
- Warehouse — bulk dry-goods storage. No capacity tracking.
- Storage — generic location that doesn’t fit the other two (cold room, lab fridge, packaging staging).
A location can allows_negative if you want to permit negative on-hand for that location (rare; usually only for transit or virtual locations).
Inventory + production
A production batch reads inventory the same way you would: it picks available lots of each ingredient and decrements remaining_quantity as it consumes them. Lot selection follows FEFO by default — earliest-expiring lot first — but operators can override during batch creation if they need to clear a specific lot.
When a batch packages out, vestl creates a new produced lot for each finished SKU at the configured packaging location. The new lot’s traceability chain links forward through MO → batch → recipe/bottle BOM/case BOM, and backward through every consumed input lot.
Inventory + compliance
For TTB-regulated products, lot traceability is legally required. vestl never deletes a lot — consumed and expired lots stay on the books indefinitely so the trail from finished bottle to source ingredient can be reconstructed years later.
Quarantine status and quarantine reasons are also part of the audit trail. If a lot was held for a compliance reason and then released, the audit log shows the hold, the reason, the release, and who released it.