Scenario: Run a production order from estimate to end
Goal. Drive a D365 production order through its life from an external system (an MES, a shop floor terminal, a planning tool): estimate, schedule, release and start it, issue its components, report the time spent on its operations and the finished quantity, and end it. After every step you can ask D365 for the order's status, so your system always knows where the order stands.
When to use. Production is planned in D365 but executed or tracked elsewhere, and D365 must reflect each status change and posting. To put component stock on hand before production, use a movement journal (Inventory journals).
Prerequisites
- Security. The operations need the privilege
MGCLatticeProductionServiceMaintain(dutyMGCLatticeIntegrationServiceOperationsDuty, rolesMGCLatticeIntegrationAdministratorRoleandMGCLatticeIntegrationOperatorRole).getProductionOrderStatusneedsMGCLatticeProductionServiceView(dutyMGCLatticeIntegrationServiceQueryDuty, in all three integration roles). ReadingLatticeProductionJournalHeaderscomes with the dutyMGCLatticeDataEntitiesView. See Security model. - The product. A released product with an active, approved BOM and route at the production site. USMF
D0002(Cabinet) has both at site 1; its components are picked from warehouse 11. - Production parameters. Production control > Setup > Production control parameters: the Status tab decides which status updates are allowed from each status (see What InvalidStatus means at each step below). Production control > Setup > Production control parameters by site: the picking list, route card and report as finished journal names, the scheduling parameters, and the start parameters (automatic BOM and route consumption at start). The number sequences for production orders and production journals must exist.
- Component stock. On-hand for the components at the picking warehouse, or start, picking list and report as finished stop on missing stock.
- Lattice. A valid Lattice license. To send your own order or item keys (
ProdIdExternalKey,ItemIdExternalKey,DocumentIdExternalKey), register an external system (Use your own keys).
Steps
Every operation belongs to MGCLatticeProductionOrderPostingService in the service group
MGCLatticeProductionServiceGroup:
POST https://<your-environment>/api/services/MGCLatticeProductionServiceGroup/MGCLatticeProductionOrderPostingService/<operation>.
Send a new RequestId for each step.
-
Create the production order on Production control > Production orders > All production orders, or with a
POSTto the standardProductionOrderHeadersentity, for example{ "dataAreaId": "USMF", "ItemNumber": "D0002", "ScheduledQuantity": 100 }. Keep theProductionOrderNumber(hereP000245); its status is Created. -
Estimate, schedule and release with
estimateProductionOrder,scheduleProductionOrder(job scheduling with the site's scheduling parameters) andreleaseProductionOrder, one call each. All three take the same body (with its ownRequestId) and no date:{ "_request": { "RequestId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c01", "LegalEntityId": "USMF", "UserContext": "Admin", "ProdId": "P000245" } }Release, start and report as finished also run the earlier updates an order has not had yet when the parameters allow it (releasing an unscheduled order schedules it first), so you can skip a step your process does not use.
-
Start with
startProductionOrder. OmitStartQty(or send0) to start the quantity not started yet; the start applies the site's start parameters, which may consume BOM and route automatically.{ "_request": { "RequestId": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d02", "LegalEntityId": "USMF", "ProdId": "P000245", "PostingDate": "2026-10-07" } } -
Post the picking list with
postPickListwhile the order is started. Each line consumes against the order's BOM line for the item (sendInventTransId, the BOM line's lot id, when the item has more than one open BOM line). WithoutInventDimIdthe line consumes from the BOM line's own dimension.{"_request": {"RequestId": "3c4d5e6f-7a8b-4c9d-8e1f-2a3b4c5d6e03","LegalEntityId": "USMF","ProdId": "P000245","PostingDate": "2026-10-07","Lines": [ { "ItemId": "M0005", "Qty": 1 } ]}} -
Post a route card with
postRouteCard: hours and quantities per operation of the order's route. When the start parameters already consumed the route at start (as in USMF), report hours only, or the quantity is reported twice.{"_request": {"RequestId": "4d5e6f7a-8b9c-4d0e-9f2a-3b4c5d6e7f04","LegalEntityId": "USMF","ProdId": "P000245","PostingDate": "2026-10-07","Lines": [ { "OprNum": 10, "Hours": 0.5, "GoodQty": 0, "ErrorQty": 0 } ]}} -
Report as finished with
reportAsFinished. WithoutEndJobthe report is partial and needs aGoodQtyorErrorQty.EndJobtrueends the job: the order becomes Reported as finished and its remaining quantity is cleared, so send it with the last report only.AcceptErrortrueends the job even when route jobs or BOM lines have not been reported as finished.{"_request": {"RequestId": "5e6f7a8b-9c0d-4e1f-a2b3-4c5d6e7f8a05","LegalEntityId": "USMF","ProdId": "P000245","PostingDate": "2026-10-07","GoodQty": 1, "ErrorQty": 0, "EndJob": true, "AcceptError": true}} -
End with
endProductionOrder;PostingDateis the calculation date of the end.{ "_request": { "RequestId": "6f7a8b9c-0d1e-4f2a-b3c4-5d6e7f8a9b06", "LegalEntityId": "USMF", "ProdId": "P000245", "PostingDate": "2026-10-07" } } -
Check the status after any step with
getProductionOrderStatus(same URL pattern,DocumentId= the production order):{ "_request": { "RequestId": "7a8b9c0d-1e2f-4a3b-c4d5-6e7f8a9b0c07", "LegalEntityId": "USMF", "DocumentId": "P000245" } }{"Success": true,"ErrorCode": 0,"Message": "Status retrieved for production order P000245.","DocumentId": "P000245","DocumentType": "ProductionOrder","DocumentStatus": "StartedUp","LastModifiedDateTimeUtc": "2026-10-07T09:14:52Z","StatusDetails": [{ "Name": "ProdStatus", "Value": "StartedUp" },{ "Name": "ItemId", "Value": "D0002" },{ "Name": "QtySched", "Value": "100.0000" },{ "Name": "QtyStartedUp", "Value": "100.0000" },{ "Name": "QtyReportFinished", "Value": "0.0000" },{ "Name": "QtyErrorCur", "Value": "0.0000" }]}DocumentStatusfollows the order:Created,CostEstimated(Estimated),Scheduled,Released,StartedUp(Started),ReportedFinished(Reported as finished) andCompleted(Ended).
How to verify
- Response.
Successtrue,Status3,DocumentId= the production order.postPickList,postRouteCardandreportAsFinishedreturn the production journal they posted inPostedDocumentId(for examplePick list journal 000873 posted for production order P000245.); the other steps leave it empty. - Status.
getProductionOrderStatusreports theDocumentStatuseach step reaches, and the quantities started and reported as finished. - Journals.
LatticeProductionJournalHeaderswithProductionJournalNumber=PostedDocumentIdshowsIsPostedYesand theNumberOfLinesyou sent. - In D365. Production control > Production orders > All production orders: the order's Production status.
- Call history and events. System administration > Inquiries > Lattice > Services > Service call log. The production order estimated, scheduled, started, reported as finished and ended events, and the production pick list posted and route card posted events, notify subscribers (React to business events).
What InvalidStatus means at each step
Each status update runs the standard D365 update. The Status tab of Production control parameters
lists, for each status, which updates are allowed. When it does not allow the update from the order's
current status, D365 skips it; Lattice never reports a skipped update as done but answers InvalidStatus
(ErrorCode 3) with the operation and the order's status in Errors:
Production order P000245 was not updated: <operation> is not allowed for an order in status <status>.
Check the order status and the status update settings in the production control parameters.
| Step | Reaches | InvalidStatus when |
|---|---|---|
estimateProductionOrder, scheduleProductionOrder, releaseProductionOrder, startProductionOrder, reportAsFinished | Estimated, Scheduled, Released, Started, Reported as finished | The parameters do not allow that update from the order's current status. |
endProductionOrder | Ended | The order is not Reported as finished (Production order P000245 must be in Reported as finished status to end. Current status: StartedUp.), or the parameters do not allow ending it. |
postPickList, postRouteCard | (no status change) | Never; D365's refusal of a journal (for example on an ended order) is BusinessRule. |
Call getProductionOrderStatus to see the current status, then either send the step the order is ready
for or have the parameters changed.
When it goes wrong
Message is a summary, for example
Pick list posting failed due to validation or business rule constraints.; Errors carries the reason.
| ErrorCode | Typical cause | What to do |
|---|---|---|
ValidationFailed | ProdId is required.; a partial report with no quantity (Report as finished needs a GoodQty or an ErrorQty, or EndJob to end the job.); no lines; a pick line with Qty of 0 or less, an unknown InventDimId, a lot that is not a BOM line of the order, or an item with several open BOM lines; a route card line without OprNum, without hours or quantities, or for an operation not on the route (Route card line 1: operation 99 is not on the route of production order P000245.) | Fix the request; nothing was posted. |
DocumentNotFound | Production order P000245 was not found.; Item 'X' on the pick list line was not found. | Check the order number and item in the legal entity. |
InvalidStatus | See the table above. | Check the status, send the right step. |
BusinessRule | D365 refused the update or journal: components without on-hand, Job ... has not been reported as finished (end job without AcceptError), a journal on an ended order, Journal X was not posted. | Fix the cause named in Errors, then resend. |
PreviewUnsupported | PreviewMode true | No production operation has a preview. |
TransientRuntime | A runtime fault (IsRetryable true) | Resend with the same RequestId. |
DuplicateRequest / RequestInProgress / RequestNeedsReview / LicenseNotValid | See API reference | New RequestId / retry later / check the order first / renew the license. |
Retries and recovery. A successful step resent with the same RequestId returns its recorded result
without running again. A picking list or route card whose posting failed is deleted, so it does not block
later status changes and a resend with the same RequestId creates it anew. If a status update is cut off
mid-run, a resend with the same RequestId does not run it twice: it answers RequestInProgress while the
first attempt may still be running and RequestNeedsReview once it stopped without a result. Check the
order with getProductionOrderStatus; an administrator then closes the open request on System
administration > Inquiries > Lattice > Service idempotency log with Mark succeeded (the
update happened) or Release claim (it did not, so the same RequestId can be sent again). See
Idempotency reconcile runbook.
Reference
- API reference: section 5, Production Order Posting Service; section 20, Status Query Operations
- Endpoint payload specification:
MGCLatticeProductionOrderPostingService - Security model: Production Control privileges
- Data entity reference:
LatticeProductionJournalHeaders - Cross-reference keys, Business events
- Inventory journals, Track documents and read data, Batch and async operations