Production order scheduled
Fires when a production order reaches the Scheduled status (ProdStatus::Scheduled): when operations scheduling or job scheduling moves it there for the first time, or when firming a planned order creates it directly as Scheduled. From this point the order has planned start and end dates and capacity reservations. MES, capacity dashboards and labor-planning systems can use it to pick up the order before it is released to the shop floor.
| Event class | MGCLatticeProdOrderScheduledBusinessEvent |
| Contract class | MGCLatticeProdOrderScheduledBusinessEventContract |
| Category | Production control |
| Module | ModuleAxapta::ProductionControl |
| Label IDs | ProdOrderScheduledBusinessEventName / ProdOrderScheduledBusinessEventDescription |
| Trigger | ProdUpdScheduling.run() -> ProdStatus transitions into Scheduled; ReqTransPoMarkFirm.insertMovement() -> firmed production order inserted as Scheduled |
Trigger / Hook
- Extension classes:
MGCLatticeProdUpdScheduling_Class_Extension, decorated with[ExtensionOf(classStr(ProdUpdScheduling))], for scheduling, andMGCLatticeReqTransPoMarkFirm_Class_Extension, decorated with[ExtensionOf(classStr(ReqTransPoMarkFirm))], for firming. - Scheduling, CoC method signature:
public void run()(no parameters). It wraps the base scheduling class; both concrete scheduling classes,ProdUpdScheduling_Job(job scheduling) andProdUpdScheduling_Operation(operations scheduling), overriderun()and callsuper(), so the single hook covers both. - Scheduling, firing condition: before
next run(), the production order ID is read from the update's parameter record (this.parmParmBuffer().Id) and the order's current status is read from the database. Afternext run(), the order is re-read and the event is sent only when the status changed intoProdStatus::Scheduled(origStatus != ProdStatus::Scheduled && prodTable.ProdStatus == ProdStatus::Scheduled). The scheduling engine sets Scheduled only for an order that hasn't reached Scheduled yet (Created or Estimated). Rescheduling an order that is already Scheduled, Released or further along doesn't change its status, so it doesn't raise the event. - Scheduling, coverage: raised for every scheduling run that goes through
ProdUpdScheduling.run(). That includes the Schedule operations and Schedule jobs actions, and the scheduling step that the production status framework runs automatically when a later status update is requested for an unscheduled order (for example, releasing it). - Firming, CoC method signature:
public void insertMovement(Common _common). Firming a planned production order or planned batch order creates the production order directly in the Scheduled status (ReqTransPoMarkFirm.initializeProdTable()), without running the scheduling update, and inserts it throughinsertMovement(). - Firming, firing condition: after
next insertMovement(_common), the event is sent when the inserted record is a production order (ProdTable) that was saved (RecId) with the statusProdStatus::Scheduled. Production orders that firming creates for derived (sub-)production are inserted the same way and raise the event too. Firming isn't a scheduling run, so such an order never raises the event a second time: a later reschedule doesn't change its status. - Firming, timing: the event is sent as soon as the order is inserted, before firming copies its BOM and route and before the coverage group's automatic production status update runs. If that automatic update moves the order further (for example, to Started), the events it raises, such as Production order started, follow this one. A firmed order never passes through the Estimated status, so Production order estimated isn't raised for it.
- Transaction: the scheduling engine saves the result inside its own
ttsbegin/ttscommit(runScheduling()), and the event is sent afterrun()returns. Firming creates the order inside the firming transaction and the event is sent within it.BusinessEventsBase.send()only writes to the commit log inside the current transaction and delivery is asynchronous post-commit, so an event raised inside a transaction that rolls back (for example, a firming that fails after the order was inserted) is never delivered. - Enablement gate: guarded by
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeProdOrderScheduledBusinessEvent)), so the event only sends when activated in the business events catalog.
public void run()
{
// The activation check is a cached lookup and comes first, so an environment that has not activated the
// event pays no database read. next run() is always called.
boolean eventEnabled = BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeProdOrderScheduledBusinessEvent));
ProdId prodId;
ProdStatus origStatus;
// Capture the pre-status from the database BEFORE next so the event fires only on a genuine transition.
// The base class initializes its prodTable buffer inside run(), so read the persisted status using the
// production order id from the parm buffer.
if (eventEnabled)
{
prodId = this.parmParmBuffer().Id;
origStatus = ProdTable::find(prodId).ProdStatus;
}
next run();
if (eventEnabled)
{
// Re-read the committed record after next for the freshest status.
ProdTable prodTable = ProdTable::find(prodId);
if (origStatus != ProdStatus::Scheduled
&& prodTable.ProdStatus == ProdStatus::Scheduled)
{
MGCLatticeProdOrderScheduledBusinessEvent::newFromProdTable(prodTable).send();
}
}
}
Payload Contract
One row per [DataMember], in declaration order. All sources are fields on the ProdTable record (re-read after scheduling, or the order firming just inserted), mapped in initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | ProdTable.DataAreaId | Company of the production order. |
productionOrderId | Production Order ID | ProdId | ProdTable.ProdId | Production order number. |
itemId | Item Number | ItemId | ProdTable.ItemId | Item being produced. |
itemSearchName | Item Search Name | str | InventTable::find(ProdTable.ItemId).NameAlias | The item's search name, not its product name. |
productionStatus | Production Status | int | enum2int(ProdTable.ProdStatus) | ProdStatus as an integer; always 2 (Scheduled) for this event. |
productionStatusName | Production Status Name | str | enum2Symbol(enumNum(ProdStatus), enum2int(ProdTable.ProdStatus)) | ProdStatus symbol; always Scheduled for this event. |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listFor(ProdTable) | Keys external systems hold for the production order: one { externalSystem, externalKey } object per external system, ordered by external system, historical keys included; [] when it has none. See Cross-reference keys. |
Every Lattice event also carries the Lattice ordering and correlation envelope, inherited from MGCLatticeBusinessEventContractBase: eventSequence (Int64 - increasing across the whole catalog and all companies, allocated when the event is raised inside the business transaction rather than when it commits; it orders the events of one business record, gaps are permanent and normal, and it must not be used to re-sequence the whole stream), correlationId (String, always Scope:Key - groups emissions that belong to one logical run; Event:<GUID> unique to this emission when the event belongs to no run) and schemaVersion (Int - the payload shape version; 1 for this contract). D365 F&O also adds its standard envelope fields to every business event payload: BusinessEventId, BusinessEventLegalEntity, ContextRecordSubject, ControlNumber, EventId, EventTime, EventTimeIso8601, InitiatingUserAADObjectId, MajorVersion, MinorVersion, and ParentContextRecordSubjects. Do not rely on the order of members in the delivered JSON.
Example Payload
{
"BusinessEventId": "MGCLatticeProdOrderScheduledBusinessEvent",
"ControlNumber": 5637151703,
"EventId": "0F947521-73D8-4E48-BEE7-99A4D680C996",
"EventTime": "\/Date(1789981331000)\/",
"EventTimeIso8601": "2026-09-21T09:02:11.6937885Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{F5E014A8-70BA-5389-9C33-5831AF122B70}",
"ParentContextRecordSubjects": [],
"eventSequence": 48103,
"correlationId": "Event:2d490dbf-71c1-412e-8c4d-352a5104ef01",
"schemaVersion": 1,
"legalEntity": "usmf",
"productionOrderId": "P000108",
"itemId": "D0001",
"itemSearchName": "MidRangeSpeaker",
"productionStatus": 2,
"productionStatusName": "Scheduled",
"crossReferences": [
{
"externalSystem": "MES",
"externalKey": "WO-104522"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Production order scheduled") or by filtering the Category column to Production control. Select it and use Activate to bind it to a configured endpoint (for example, an Azure Service Bus queue, Event Grid topic, or HTTPS webhook).