The first time I extended a Production Order graph, I assumed status transitions worked the way a Sales Order's do: a mostly linear sequence with a handful of documented statuses. Production orders are messier by design, because a real shop floor order can be planned, held, partially released, partially materialized, and partially completed all at once across different operations, and Acumatica's status model has to represent all of that simultaneously rather than as one linear field. Here's the lifecycle as it actually behaves, not as the training material simplifies it.
Order status vs operation status — two tracks, not one
A production order (AMProdItem in the Manufacturing module's DAC model) carries a document-level status, Planned, Released, Completed, Closed, Cancelled, On Hold, but each operation within that order (AMProdOper) tracks its own progress independently through material issue and labor/machine time entry. An order can sit at "Released" for weeks while individual operations move through in-process states as material gets issued and labor gets reported, operation by operation. Customizations that key off only the order-level status and ignore operation-level state consistently get shop floor progress wrong, because "Released" tells you almost nothing about how far through production the order actually is.
// Order status alone is not enough to know real progress
var order = PXSelect<AMProdItem,
Where<AMProdItem.orderType, Equal<Required<AMProdItem.orderType>>,
And<AMProdItem.prodOrderID, Equal<Required<AMProdItem.prodOrderID>>>>
.Select(Base, orderType, prodOrderID);
// Operation-level completion is where real progress lives
var operations = PXSelect<AMProdOper,
Where<AMProdOper.orderType, Equal<Required<AMProdOper.orderType>>,
And<AMProdOper.prodOrderID, Equal<Required<AMProdOper.prodOrderID>>>>>
.Select(Base, orderType, prodOrderID);
// Sum completed qty per operation against the order qty to get real % complete
Material issue and labor entry drive cost accumulation, not the status field
Costs accumulate onto a production order through material issue transactions (consuming components per the BOM, or over/under against it if actuals differ) and labor/machine time entry against operations, independent of what the order's headline status says. This means a production order's accumulated actual cost is a live, continuously moving number from the moment the first material gets issued, well before the order reaches "Completed." A common mistake in custom reporting: querying actual cost only for orders in Completed status, which misses substantial in-process cost sitting on orders still actively being worked, understating work-in-process value on any report run mid-cycle.
If the BOM or item is configured for backflush material issue, components get relieved from inventory automatically at operation completion or order completion rather than through an explicit material issue transaction. Custom logic that assumes material cost only posts via an explicit AMMTran issue document will silently miss backflushed consumption, understating WIP or overstating on-hand inventory for backflushed components. Check the item's issue method before writing any custom material cost logic, don't assume every component is manually issued.
Completion posts a finished good, and that's where standard-vs-actual variance surfaces
Completing a production order (fully or partially, Acumatica supports partial completions against an order that stays open for remaining quantity) receives the finished item into inventory at its calculated actual cost, or at standard cost with the difference posted to a variance account, depending on the item's costing method. This is the moment standard costing's whole point becomes visible: if actual material and labor cost diverged from the BOM's standard cost, that divergence becomes a material usage variance, a labor efficiency variance, or a rate variance, posted to distinct GL accounts rather than silently absorbed into inventory value.
AMProdEntry graph = PXGraph.CreateInstance<AMProdEntry>();
graph.ProdItemRecords.Current = graph.ProdItemRecords.Search<AMProdItem.prodOrderID>(orderType, prodOrderID);
AMProdMatl completion = new AMProdMatl(); // completion transactions use the
// production entry graph's completion
// view, not a generic INTran insert
graph.CompletionQty.Current.CompletedQty = partialQty;
graph.Save.Press();
Variance reporting only makes sense once you accept that a production order's actual cost and its standard cost are two genuinely different numbers tracked in parallel, and the report showing that gap (actual-vs-standard by order, by operation, by variance type) is one of the most requested manufacturing reports I build, because it's the number that tells a plant manager whether a specific work center is running efficiently or quietly bleeding margin.
Completed is not Closed, and the difference matters for period-end
A "Completed" production order can still accept cost adjustments, late labor entries, or corrections, it is not yet locked. "Closed" is the terminal state that finalizes cost and prevents further postings against the order. Running period-end close reports against orders that are Completed but not Closed can show cost figures that later shift once a late labor ticket gets entered and the order is finally closed, which is exactly the kind of "the numbers changed after I already reported them" problem that erodes finance's trust in manufacturing reporting. Any custom month-end reporting should filter explicitly by Closed status when the requirement is "final" cost, not just Completed.
Wrapping up
A production order's real progress lives at the operation level, not the order-level status field, and cost accumulates continuously from first material issue rather than appearing all at once on completion. Understand backflushing before writing custom material cost logic, treat Completed and Closed as genuinely different states for reporting purposes, and expect standard costing to surface real variance at completion rather than silently absorbing the gap between planned and actual. The lifecycle looks messy compared to a simple linear status field because a shop floor genuinely is messy, and the data model is honest about that.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.