Skip to main content

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​

  1. Security. The operations need the privilege MGCLatticeProductionServiceMaintain (duty MGCLatticeIntegrationServiceOperationsDuty, roles MGCLatticeIntegrationAdministratorRole and MGCLatticeIntegrationOperatorRole). getProductionOrderStatus needs MGCLatticeProductionServiceView (duty MGCLatticeIntegrationServiceQueryDuty, in all three integration roles). Reading LatticeProductionJournalHeaders comes with the duty MGCLatticeDataEntitiesView. See Security model.
  2. 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.
  3. 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.
  4. Component stock. On-hand for the components at the picking warehouse, or start, picking list and report as finished stop on missing stock.
  5. 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.

  1. Create the production order on Production control > Production orders > All production orders, or with a POST to the standard ProductionOrderHeaders entity, for example { "dataAreaId": "USMF", "ItemNumber": "D0002", "ScheduledQuantity": 100 }. Keep the ProductionOrderNumber (here P000245); its status is Created.

  2. Estimate, schedule and release with estimateProductionOrder, scheduleProductionOrder (job scheduling with the site's scheduling parameters) and releaseProductionOrder, one call each. All three take the same body (with its own RequestId) 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.

  3. Start with startProductionOrder. Omit StartQty (or send 0) 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" } }
  4. Post the picking list with postPickList while the order is started. Each line consumes against the order's BOM line for the item (send InventTransId, the BOM line's lot id, when the item has more than one open BOM line). Without InventDimId the 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 } ]
    }
    }
  5. 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 } ]
    }
    }
  6. Report as finished with reportAsFinished. Without EndJob the report is partial and needs a GoodQty or ErrorQty. EndJob true ends the job: the order becomes Reported as finished and its remaining quantity is cleared, so send it with the last report only. AcceptError true ends 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
    }
    }
  7. End with endProductionOrder; PostingDate is the calculation date of the end.

    { "_request": { "RequestId": "6f7a8b9c-0d1e-4f2a-b3c4-5d6e7f8a9b06", "LegalEntityId": "USMF", "ProdId": "P000245", "PostingDate": "2026-10-07" } }
  8. 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" }
    ]
    }

    DocumentStatus follows the order: Created, CostEstimated (Estimated), Scheduled, Released, StartedUp (Started), ReportedFinished (Reported as finished) and Completed (Ended).

How to verify​

  • Response. Success true, Status 3, DocumentId = the production order. postPickList, postRouteCard and reportAsFinished return the production journal they posted in PostedDocumentId (for example Pick list journal 000873 posted for production order P000245.); the other steps leave it empty.
  • Status. getProductionOrderStatus reports the DocumentStatus each step reaches, and the quantities started and reported as finished.
  • Journals. LatticeProductionJournalHeaders with ProductionJournalNumber = PostedDocumentId shows IsPosted Yes and the NumberOfLines you 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.
StepReachesInvalidStatus when
estimateProductionOrder, scheduleProductionOrder, releaseProductionOrder, startProductionOrder, reportAsFinishedEstimated, Scheduled, Released, Started, Reported as finishedThe parameters do not allow that update from the order's current status.
endProductionOrderEndedThe 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.

ErrorCodeTypical causeWhat to do
ValidationFailedProdId 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.
DocumentNotFoundProduction 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.
InvalidStatusSee the table above.Check the status, send the right step.
BusinessRuleD365 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.
PreviewUnsupportedPreviewMode trueNo production operation has a preview.
TransientRuntimeA runtime fault (IsRetryable true)Resend with the same RequestId.
DuplicateRequest / RequestInProgress / RequestNeedsReview / LicenseNotValidSee API referenceNew 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​