Manufacturing Orders
A Manufacturing Order (MO) is a single production run — it turns component materials into a finished good, moves through a controlled lifecycle, and, on completion, automatically posts the resulting stock movements to your Inventory.
flow '-' consume, flow '+'
produce), see Core Concepts & Data Flow.
What a Manufacturing Order does
An MO lets a planner declare “I want to produce X quantity of finished good Y (optionally following bill-of-materials Z)”, then tracks the run from planning to completion. Along the way ERPat:
- Auto-generates a human-readable reference (MO-00001, MO-00002, …).
- On Confirm, snapshots the BOM's component list — scaled to the planned quantity — onto the order, so later BOM edits never rewrite production history.
- On Complete, posts stock movements to core Inventory: it deducts each consumed component and adds the produced finished good.
The feature reads finished goods, components, units, and variants from the core Inventory tables, so it works even if the Inventory UI is off — but a finished good must have a default inventory variant before its output can be stocked.
The lifecycle
Every order flows left to right through Draft → Confirmed → In Progress → Done. A separate Cancelled state can be reached from any non-Done state. Done and Cancelled are terminal.
Status legend
actual_start_date set).Priority legend
Priority is a planning aid only — there is no scheduling or capacity logic behind it in this module. It defaults to Normal and is plain dropdown text, not a coloured badge.
State-by-state — what you can do
| State | Meaning | Allowed transitions |
|---|---|---|
| Draft | Newly created, not yet committed. Editable; components not yet snapshotted. | Confirm → Confirmed · Cancel → Cancelled · Delete (soft) · Edit allowed |
| Confirmed | Committed; BOM components snapshotted onto the order. Header + components editable. | Start Production → In Progress · Mark as Done → Done · Cancel → Cancelled · Edit allowed |
| In Progress | Production physically started. Header Edit is not offered, but component lines stay editable. | Mark as Done → Done · Cancel → Cancelled |
| Done | Completed; stock posted (stock_posted = 1), quantity produced and actual end recorded. Terminal. |
None — cannot be edited, cancelled, or completed again. |
| Cancelled | Abandoned. Terminal. No stock is reversed by cancellation. | None — cannot be confirmed, started, or completed; only viewable / deletable. |
Lifecycle actions in detail
Each action appears on the order's view page only when its guard passes, and all four require the
production_update permission. Confirm, Complete, and Cancel each show a JavaScript
confirm() dialog first; the page reloads on success.
Confirm Order Draft → Confirmed
Advances a Draft order to Confirmed and plans its materials. The order must
exist, be in status draft, and must have a BOM. If the order has no component
lines yet, the system snapshots each BOM component onto the order:
- Required Quantity
bom_component.quantity × (quantity_planned ÷ bom.quantity)- Consumed Quantity
- starts at
0 - Unit & Notes
- copied from the BOM component
- Result
- status becomes
confirmed
Start Production Confirmed → In Progress
Advances a Confirmed order to In Progress. The guard requires status
confirmed. It sets status to in_progress and stamps
actual_start_date = now if it was not already set. No inventory is posted.
Mark as Done (Complete) Confirmed / In Progress → Done
Completes the order and posts the stock movements. This is the moment inventory actually changes. In order:
-
Consume each stocked component
For every component line whose item has a default variant, ERPat writes an
item_inventoriesrow withflow '-'andqty = quantity_required, then sets that line'squantity_consumed = quantity_required. Components whose item has no default variant (e.g. services) are silently skipped — no stock is deducted and their Consumed stays 0. -
Produce the finished good
Writes one
item_inventoriesrow withflow '+'against the finished good's default variant, withqty =the posted quantity produced or, if none supplied, the planned quantity. -
Close the order
Sets
status = 'done', recordsquantity_produced,actual_end_date = now, andstock_posted = 1(alsoactual_start_date = nowif the order was never started).
“Complete this order? Components will be consumed and the finished good produced in inventory. This cannot be undone.”
On success: “Manufacturing order completed. Stock movements have been posted to inventory.” Completion is irreversible —
stock_posted is set to 1 and Done is terminal.
quantity_produced
parameter.
Cancel Order Any state except Done → Cancelled
Cancels an order that will not be produced. The guard refuses if status is done. It
simply sets status = 'cancelled' and does not reverse any already-posted stock.
Guards & validation messages
The lifecycle is protected by guards on the server. When a guard fails you get a specific message — know these so you can diagnose a blocked action quickly.
| Message key | Text | When it fires |
|---|---|---|
bom_required_to_confirm |
“Select a bill of materials before confirming the order.” | You click Confirm Order but the order has no BOM. A BOM is optional at creation but mandatory to confirm. |
mo_no_components_to_post |
“Add at least one component before completing the order.” | You click Mark as Done but the order has no component lines to consume. |
mo_finished_good_no_variant |
“The finished good has no inventory variant, so stock cannot be posted.” | On completion, when the finished good item has no default_variant_id. Set up the item's variant in Inventory first. |
mo_already_completed |
“This manufacturing order is already completed.” | You attempt to complete an order that is already Done / has stock_posted = 1. Guards against double-posting. |
mo_invalid_status_transition |
“This action is not allowed for the current order status.” | Any disallowed transition (wrong current status). Also returned by Save Component when the parent order isn't in an editable status. |
finished_good_required |
Missing required — the Finished Good field is required. | Saving an order without a Finished Good. The field is client-required (data-rule-required) and server-validated (item_id required|numeric). |
Save validation
| On | Server validates | Success / failure |
|---|---|---|
| Save order | id numeric, item_id required|numeric, quantity_planned required. (Finished Good & Quantity to Produce are client-required too.) |
Success: “Record saved.” Failure: “An error occurred.” |
| Save component | id numeric, manufacturing_order_id required|numeric, item_id required|numeric, quantity_required required. |
Success: “Record saved.” Failure: “An error occurred.” |
| Delete order | Standard delete confirmation dialog. | Success: “Record deleted.” Failure: “Record cannot be deleted.” |
| Delete component | Standard delete confirmation dialog. | Success: “Record deleted.” Failure: “Record cannot be deleted.” |
Order fields
These are the fields on the Add / Edit Manufacturing Order modal and the header of the view page. Editing the header is offered only while the order is Draft or Confirmed.
| Field | Meaning | Required | Notes |
|---|---|---|---|
reference — Reference |
Human-readable order number, e.g. MO-00001. Auto-generated from MAX(id)+1 as MO- + 5-digit zero-padded number. |
System | Static text in the create modal (pre-computes the next number); never user-editable; not shown when editing. Falls back to #id if empty. |
bom_id — Bill of Materials |
Optional link to an active BOM. Its components are snapshotted onto the order at Confirm; its operations show on the Operations tab. | Optional (but required to Confirm) | Select2 of active BOMs; label “BOM title (finished good title)”. Selecting a BOM auto-fills Finished Good and Unit. Stored NULL if blank. |
item_id — Finished Good |
The product this order produces; receives the flow '+' inventory movement on completion. |
Required | Select2 of active items with item_type='product'. Must have a default_variant_id to complete the order. |
quantity_planned — Quantity to Produce |
How many units of the finished good are planned. Drives the BOM-component scaling factor at Confirm and the default produced quantity at completion. | Required | Number input, step any, defaults to 1. Stored DECIMAL(15,2); passed through convert_number_to_decimal(). |
quantity_produced — Quantity Produced |
Actual output produced; set at completion (defaults to Quantity to Produce). 0 until Done. | System (set on Complete) | DECIMAL(15,2). Read-only in the UI; only the complete action can set it (via optional posted quantity_produced). |
unit_id — Unit |
Unit of measure for the finished good output. | Optional | Select2 from Units; label “title - value child_title” for child units. Falls back to the item's unit_id when posting stock. Stored NULL if blank. |
work_center_id — Work Center |
The production work center / resource assigned to the run. | Optional | Select2 of active work centers; label “code - title” (or just title). Display / reference only — no scheduling logic. Stored NULL if blank. |
priority — Priority |
Planning priority: Low, Normal, or High. | Optional | Select2; defaults to normal. VARCHAR(20). Display / planning aid only. |
status — Status |
Lifecycle state: draft, confirmed, in_progress, done, cancelled. | System | Defaults to draft on create. Changed only by lifecycle actions, never editable in the form. VARCHAR(20). |
planned_start_date — Planned Start |
Intended production start date. | Optional | HTML date input (DATE). 0000-00-00 treated as empty and shown as “-”. Used by list filters and status-summary date ranges. |
planned_end_date — Planned End |
Intended production end date. | Optional | HTML date input (DATE). 0000-00-00 treated as empty. |
actual_start_date — Actual Start |
Timestamp the order actually started production. | System | DATETIME. Set to now on Start Production (if empty); also set on Complete if never started. |
actual_end_date — Actual End |
Timestamp the order was completed. | System | DATETIME. Set to now on Complete. Drives output / consumption reports' date ranges. |
stock_posted — (internal) Stock Posted flag |
1 once completion has posted inventory movements; guards against double-posting. | System | TINYINT(1), default 0. Not shown in UI. Complete refuses if already 1. |
notes — Notes |
Free-text remarks about the order. | Optional | TEXT. Rendered with nl2br on the view page. |
created_by — Created By |
User who created the order (shown as legal name in the list). | System | Set to the logged-in user's id on create. |
created_at — (created timestamp) |
UTC creation time. | System | Set via get_current_utc_time() on create. |
bom_meta map). The help text under the BOM field reads
“Components are copied from the bill of materials when the order is confirmed.”
Components & Operations tabs
The order's view page has two AJAX tabs. The Components tab auto-opens shortly after the page loads.
The per-order snapshot of required materials — the component lines. When the order is
editable you can add, edit, and delete lines here. The Add Component button appears only
when you have production_update and the order status is
draft, confirmed, or in_progress.
| Column | Meaning | Required | Notes |
|---|---|---|---|
item_id — Component |
The material / item consumed by the order. | Required | Select2 of active product items (“- Select item -”). Its default_variant_id receives the flow '-' movement on completion. |
quantity_required — Required Quantity |
How much of the component this order needs; snapshotted from BOM (scaled) or entered manually. | Required | Number input, step any, DECIMAL(15,2). This exact amount is what is consumed on completion. |
quantity_consumed — Consumed Quantity |
Amount actually issued to production. 0 until completion. | System | On Complete it is set equal to Required Quantity for each stocked component. DECIMAL(15,2). |
unit_id — Unit |
Unit of measure for the component line. | Optional | Select2 from Units; falls back to the component item's unit_id when posting stock. |
notes — Notes |
Free-text remark on the component line. | Optional | VARCHAR(500). Copied from the BOM component note on snapshot. |
draft / confirmed / in_progress); if not you get
“This action is not allowed for the current order status.”
A read-only view of the routing operations inherited from the order's BOM. There is no add / edit here — operations are managed on the Bill of Materials.
| Column | Meaning | Required | Notes |
|---|---|---|---|
sequence — Sequence |
Order / step number of the routing operation (from BOM). | Read-only | Displayed as an integer, centered. |
title — Operation |
Name of the routing step (from BOM operations). | Read-only | Read-only display. |
work_center_title — Work Center |
Work center for that operation step. | Read-only | Shows “-” if none. |
duration_mins — Duration (minutes) |
Standard duration of the step in minutes. | Read-only | Right-aligned decimal. |
| notes — Notes | Remarks for the operation step. | Read-only | Empty / absent values render as “-”. |
The Manufacturing Orders list
The list is a DataTable (manufacturing-orders-table) with a refresh button and a
status filter dropdown. The Add Manufacturing Order button (top-right) is shown only
with the production_create permission.
| Column | Shows |
|---|---|
| Reference | The MO-##### number, linked to the order's view page. |
| Finished Good | The product being produced. |
| Bill of Materials | The linked BOM (if any). |
| Quantity to Produce | Right-aligned, shown with its unit. |
| Quantity Produced | Right-aligned; 0 until Done. |
| Status | Centered coloured label (Draft / Confirmed / In Progress / Done / Cancelled). |
| Planned Start | The planned start date. |
| Created By | Creator's legal name. |
| Options | View / Edit / Delete (each shown per its guard). |
The default sort is Reference descending (newest first). The Status filter (a Select2 with a leading “- Status -” blank option) reloads the table server-side, showing only orders in the chosen state.
SOP — run a make-to-stock production order
The end-to-end standard operating procedure for a full production run, from planning to posted stock.
-
Check the preconditions
The finished good item must exist and have a default inventory variant. If you'll use a BOM, an active BOM for that finished good (with components, and optionally operations) must exist. Component items to be stocked must also have default variants. See Getting Started → Prerequisites.
-
Create the order (Planner)
Go to Production → Manufacturing Orders and click Add Manufacturing Order. Select the BOM (auto-fills Finished Good + Unit), set Quantity to Produce, and set priority / dates / work center as needed. Click Save. The order is created as Draft with an auto reference like MO-00007.
-
Confirm to plan materials (Supervisor)
Open the Draft order and click Confirm Order — a BOM is mandatory to confirm. Accept the dialog “Confirm this order? Components will be planned from the bill of materials.” The BOM components are snapshotted onto the order, scaled to the planned quantity. The order becomes Confirmed.
-
Review & adjust the plan
Review the Components tab; adjust Required Quantities or add / remove component lines while the order is Draft / Confirmed / In Progress. Review the Operations tab (read-only routing from the BOM).
-
Start production (Operator)
When work begins, click Start Production (Confirmed only) to record the actual start time. The order becomes In Progress. This is optional — completion can also record the start time.
-
Complete & post stock (Operator / Supervisor)
When output is finished, click Mark as Done and accept “Complete this order? Components will be consumed and the finished good produced in inventory. This cannot be undone.” ERPat deducts each stocked component (
flow '-', qty = Required Quantity) and adds the finished good (flow '+', qty = Quantity Produced, defaulting to Quantity to Produce). The order becomes Done; Quantity Produced and Actual End are recorded, and each component's Consumed Quantity is set. -
(Or) Cancel if abandoned
If the run is abandoned before Done, click Cancel Order (any non-Done state) and accept “Cancel this manufacturing order?” Cancellation does not post or reverse stock.
How component quantities are computed at Confirm
-
Compute the factor
Factor = Quantity to Produce (MO) ÷ BOM output quantity (
bom.quantity, treated as 1 if 0 or missing). -
Scale each component
For each BOM component: snapshot Required Quantity = BOM component quantity × Factor.
-
Only if empty
The snapshot happens only if the order currently has zero component lines; if you already added lines manually, Confirm will not overwrite them.
-
Consumed starts at 0
Consumed Quantity starts at 0 and is filled in at completion.
Common use cases
flow '-', qty = Required Quantity) and adds the finished good
(flow '+', qty = Quantity Produced, defaulting to Quantity to Produce). Status
becomes Done; Quantity Produced and Actual End are recorded, each component's Consumed
Quantity is set.
Permissions
Reaching any Manufacturing Orders page requires that the Production module is enabled and you
hold the base production access.
| Permission | Grants |
|---|---|
production (module + access) | Reach any Manufacturing Orders page and View an order (no separate view permission key). |
production_create | Create a new manufacturing order (the Add button and save() on a new record). |
production_update | Edit orders, add / edit / delete component lines, and run all lifecycle actions (Confirm, Start, Complete, Cancel). |
production_delete | Soft-delete a manufacturing order (and cascade-delete its component lines). |
Gotchas & things to watch
- Snapshot is once-only. BOM components are copied at Confirm only if the order has zero component lines. Add any line manually first and the BOM will not be snapshotted at all.
- Silent skips. Completion consumes each line's Required quantity and sets Consumed = Required — but a component whose item has no default variant is silently skipped (no stock deducted, Consumed stays 0).
- Finished good needs a variant. Completion is blocked with “The finished good has no inventory variant, so stock cannot be posted.” if the item has no default variant. Set it up in Inventory first.
- No negative-stock guard. Completing can drive inventory negative if components are short — there is no availability check.
- Irreversible completion.
stock_postedis set to 1 and Done is terminal; re-completing is refused (“This manufacturing order is already completed.”). Cancelling a Done order is refused, and cancelling other states does not reverse posted stock. - Editing scope quirk. The header Edit button appears only in Draft / Confirmed, but component lines stay editable through In Progress. Delete Component does not re-check order status, so a line could be deletable even when Save is blocked.
- Delete cascades. Deleting an order soft-deletes it and deletes all its component lines — there is no undo in the UI.
- Reference gaps. References derive from
MAX(id)+1, so gaps appear if orders are deleted; the reference is fixed at creation and never renumbered. - Informational only. Work Center and Priority carry no scheduling / capacity logic in this module.