Batches
A batch is a single physical run of liquid through your equipment — the tank of mash, the kettle of beer, the blender of liqueur. It’s the primary traceability entity in vestl: every COA, every TTB record, every yield report, every lot of finished cases hangs off a batch.
Vocabulary check. A Batch Order (called a “manufacturing order” or “MO” in the schema) is the plan — what you intend to produce. A Batch (
production_batch) is the physical run — what actually happened on the floor. One batch order can spawn one or more batches (set bynumBatchesat order creation).
The lifecycle
Batch orders move through five statuses, shown left-to-right on the /batches kanban:
| Status | What it means | What advances it |
|---|---|---|
| Draft | Order is being authored. Quantity, formula, bottle BOM, case BOM are being picked. | Schedule the order (sets a start date and pins versions). |
| Scheduled | Versions pinned, work center reserved, ready to run when the slot opens. | Operator starts the run on the floor. |
| In progress | Liquid is on a tank or line right now. Lot genealogy is being recorded as ingredients are consumed. | Package the run. |
| Packaged | Liquid is in bottles, cases are built and palletized. Awaiting QC + compliance sign-off. | Close the order. |
| Closed | All artifacts signed off, costs posted, lot genealogy frozen. Read-only forever. | — |
Cancelled is a sixth status reachable from Draft or Scheduled only; once a run starts you cannot cancel without writing a deviation report.
The kanban enforces these transitions — drag a card from “Draft” to “Scheduled” works, drag from “Draft” straight to “In progress” doesn’t.
Pages
/batches — Kanban view
The default landing surface. Five columns matching the five statuses, with a horizon filter (this week / this + next / this quarter / all). Drag cards between columns to advance them.
Empty-column hints:
- Draft: “Orders start here — set quantity, pick formula + BOM, schedule to advance.”
- Scheduled: “Scheduled orders — ready to run when the work center opens up.”
- In progress: “Currently on a tank or line. Lot genealogy is being recorded.”
- Packaged: “Liquid in bottles, cases built. Awaiting compliance sign-off.”
- Closed: “All artifacts signed off. Costs posted. Lot genealogy frozen.”
/batches/new — Create a batch order
Wizard-style. Walk through:
- What are you making? — finished good (line-anchored or standalone)
- How many? — planned output cases, number of physical batches
- Which formula + BOM? — picks the family; vestl resolves the latest published version chain (formula → bottle BOM → case BOM)
- When? — scheduled start/end + work center assignment
Submitting creates a Draft order. You schedule it from the detail page or by dragging on the kanban.
/batches/[id] — Batch order detail
The full view: planned output, pinned formula/bottle BOM/case BOM versions, scheduled vs. actual times, the underlying physical batches, work-order tasks (routing steps), and the next-action button derived from current status.
The right rail clarifies the MO ↔ Batch distinction — the order on top, the physical batches it spawned underneath. Each batch has its own consumption records, yield, and (eventually) COA + TTB record.
Field reference (batch order)
Order number
Auto-generated, format MO-NNNN. Stable for the life of the order. Appears on every downstream artifact (consumption records, COAs, TTB batch records).
Name
Free-text label so operators can recognize a run at a glance (“Maybel’s Cream Liqueur — Spring 2026 batch”). Doesn’t replace the order number; it’s a human handle.
Target type
Formula or Bottle BOM. Determines whether the order produces bulk liquid (formula) or finished cases (bottle BOM → case BOM chain). Most orders target a bottle BOM so the run yields packaged cases directly.
Planned output cases
How many cases of the finished good you intend to produce. Drives MRP material requirements and scheduling. The actual yield is recorded per-batch on completion.
Number of batches
How many physical batches this order will spawn. A 10,000-case order might be split across 5 fermenter loads (5 batches) depending on tank capacity. Each batch gets its own batch number and its own COA.
Status
See “The lifecycle” above. Status drives which actions are available, which compliance records are required, and whether the row is editable.
Scheduled start / end
The window the order is reserved on its work center. Read by /schedule to render the calendar view and detect conflicts. Set when the order moves from Draft to Scheduled.
Actual start / end
The real start/end stamps recorded on the floor. Set when the operator transitions In progress → Packaged. Used by yield variance, cycle-time, and labor-cost reports.
Pinned versions (formula / bottle BOM / case BOM)
The exact version IDs this order is bound to. Resolved when the order is scheduled and frozen when status hits In progress or later. Even if the recipe is forked and a new version published mid-run, this order keeps its original pin so the batch record matches what was actually produced.
Assigned to
The operator (or production manager) responsible for executing the order. Optional — orders without an assignee show up in the unassigned queue on /dashboard.
Notes
Free-text. Use this for the things that don’t fit a field: the substitution authorized for an out-of-stock ingredient, the deviation note from a deviation report, the customer-facing context for a one-off run.
Field reference (production batch)
Batch number
Auto-generated, format B-NNNN. The batch number is what shows on bottles (via the lot it produces), on COAs, and on TTB batch records — it’s the customer-facing identifier of a specific physical run.
Status
Planned, In progress, Complete, or Rejected. Distinct from the parent order’s status — an order can be “In progress” while its underlying batch is still “Planned” (e.g. order opened, materials staged, but liquid hasn’t actually been pumped into the tank yet).
A Rejected batch is one that failed QC or compliance and was destroyed/reworked. Rejected batches stay on the books for traceability; their lot genealogy still resolves.
Actual yield (qty + UoM)
What you actually produced — usually in gallons of liquid or cases of finished product. Compared to the planned output (from the parent order × per-batch share) for yield variance reporting.
Completed at
Timestamp the batch was marked Complete. Drives compliance deadlines (TTB report cutoffs) and inventory-availability dates for downstream MOs.
Work-order tasks (routing)
Each batch order is broken into a list of work-order tasks corresponding to the formula’s process steps (mash, ferment, distill, blend, bottle, case). The task table on /batches/[id] shows:
- The step’s sequence number, name, and assigned work center
- Status:
Pending,In progress,Complete, orSkipped - Optionally, an actual temperature reading (°F) and gravity reading at completion — these flow into the COA
- The user who completed the task and when
Tasks unblock in sequence; you cannot start step 5 until steps 1–4 are complete (or explicitly skipped with a reason).
Consumption records (lot genealogy)
When a batch consumes an ingredient, vestl writes a batch_consumption_record with:
- The lot consumed (foreign key to
ingredient_lots) - The product
- Planned quantity vs. actual quantity (and a computed variance %)
- The user who recorded the consumption + timestamp
- An optional note for deviation context
These records are the legal traceability chain for TTB. From a finished bottle’s lot, you can walk back through the producing batch to every consumption record to every input lot to every supplier lot. vestl never deletes a consumption record — soft-delete only.
How batches connect to other surfaces
| You’re looking at | …it links to |
|---|---|
/batches/[id] | /formulas/[id]/version/[v] (pinned formula version), /bottle-boms/[id]/version/[v], /case-boms/[id]/version/[v] |
| Consumption record | /inventory/lots/[id] (the source lot) |
| Closed batch | /compliance/coa/[batchId] (Certificate of Analysis), /compliance/ttb/[batchId] (TTB Batch Record) |
| Sales order line | The MO this line is fulfilled from (set by MRP) |
| Finished case | The lot it shipped from → the batch that produced it |
The detail page has direct links for each of these — you should never need to copy/paste an ID between surfaces.
Compliance overlay
For products where compliance_regime = ttb:
- A TTB Batch Record is required before status can move to
Closed. The record references the formula’s TTB Formula # and proof-gallon math. - A Certificate of Analysis (COA) is required for label-claim ABV verification.
- The audit trail captures every status transition, every consumption, every task completion, and is append-only — no edits, no deletes.
For products where compliance_regime = none:
- No TTB record is required.
- A COA may still be required by the client, but vestl doesn’t gate closure on it.
The regime is set on the product, inherited by the batch, and frozen at batch creation. You cannot change a batch’s regime mid-run.