Skip to main content

Maintenance work order created

Fires once for every new Asset Management maintenance work order, when the work order has been created: D365 creates a work order in several steps (the header, its work order lines, then the first lifecycle state of its lifecycle model), and the event is raised as the transaction that creates the work order commits, so the payload describes the finished work order rather than a bare header row. Downstream systems can use it to open the job in a field-service, mobile-workforce or CMMS tool, alert maintenance planners, or start tracking cost and downtime for the new work order. Use the Scheduled, Started and Completed events for the rest of the work order's life.

Event classMGCLatticeMaintenanceWorkOrderCreatedBusinessEvent
Contract classMGCLatticeMaintenanceWorkOrderCreatedBusinessEventContract
CategoryAsset management
ModuleModuleAxapta::AssetManagement
Label IDsMaintenanceWorkOrderCreatedBusinessEventName / MaintenanceWorkOrderCreatedBusinessEventDescription
TriggerEntAssetWorkOrderTable Inserted data event queues the work order; the event is raised when the creating transaction commits (SysTransactionScope.ttsCommitting)

Trigger / Hook​

  • Handler class: MGCLatticeMaintenanceWorkOrderEventHandlers. The method EntAssetWorkOrderTable_onInserted is a table data-event handler decorated with [DataEventHandler(tableStr(EntAssetWorkOrderTable), DataEventType::Inserted)]; it does not send the event itself but queues the work order in the current transaction.
  • Method signature: public static void EntAssetWorkOrderTable_onInserted(Common _sender, DataEventArgs _e).
  • Firing condition: every insert of an EntAssetWorkOrderTable record, whatever creates it (the create work order dialog, a work order created from a maintenance request or a maintenance schedule, a related work order, a copied work order, a data import). There is no status or type filter, and the header is inserted once per work order, so the event is raised exactly once per work order.
  • When it is raised: just before the outermost transaction that inserted the header commits. The queued work order is read again at that moment and the payload is built from it, so everything the creating process did in that transaction is included: the work order lines (and with them the asset list, the functional location and the responsible worker D365 derives from the lines) and the first lifecycle state. The standard create work order dialog, maintenance schedule work order creation and work order copy create the header, the lines and the first state in one transaction.
  • Work orders whose lines are added later: when the header is saved in a transaction of its own and the lines are added afterwards (for example a data import that writes the header and the lines as separate records), the event describes the work order as it was when the header was committed: assetIds is [] and functionalLocation is empty. Lines added later do not raise the event again.
  • Fields that are empty at creation by design: scheduledStartDateTime, scheduledEndDateTime and scheduledWorkerPersonnelNumber, scheduledWorkerName are set by work order scheduling, and actualStartDateTime and actualEndDateTime by the Started and Completed lifecycle states, so they are normally empty here; the planned window of a new work order is in expectedStartDateTime and expectedEndDateTime.
  • The first lifecycle state a work order receives while it is created belongs to the creation: it does not raise the Scheduled, Started or Completed event, even if that state has Set actual start or Set actual end enabled or is the lifecycle model's scheduled state.
  • Transactions: a work order creation that rolls back raises nothing, and a work order inserted and deleted again in the same transaction raises nothing. send() writes the event to the business events commit log inside the committing transaction, and it is delivered after commit.
  • Enablement gate: guarded by BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeMaintenanceWorkOrderCreatedBusinessEvent)) both when the work order is queued and when the event is raised, so the event only sends when activated in the business events catalog.
  • No Lattice parameters, filters or batch jobs are involved.
[DataEventHandler(tableStr(EntAssetWorkOrderTable), DataEventType::Inserted)]
public static void EntAssetWorkOrderTable_onInserted(Common _sender, DataEventArgs _e)
{
EntAssetWorkOrderTable workOrderTable = _sender as EntAssetWorkOrderTable;

if (workOrderTable.RecId
&& BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeMaintenanceWorkOrderCreatedBusinessEvent)))
{
// Queued in the transaction scope; raisePendingCreatedEvents() raises it on ttsCommitting.
MGCLatticeMaintenanceWorkOrderEventHandlers::queueCreatedEvent(workOrderTable.DataAreaId, workOrderTable.RecId);
}
}

private static void raiseCreatedEvent(DataAreaId _dataAreaId, RecId _workOrderRecId)
{
changecompany(_dataAreaId)
{
EntAssetWorkOrderTable workOrderTable = EntAssetWorkOrderTable::findRecId(_workOrderRecId);

if (workOrderTable.RecId
&& BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeMaintenanceWorkOrderCreatedBusinessEvent)))
{
MGCLatticeMaintenanceWorkOrderCreatedBusinessEvent::newFromWorkOrderTable(workOrderTable).send();
}
}
}

Payload Contract​

One row per [DataMember], in declaration order. The fields are declared on MGCLatticeMaintenanceWorkOrderBusinessEventContractBase, which the four maintenance work order events share, and are read in its initializeFromWorkOrder() from the work order header, the records it references and its work order lines.

JSON fieldDisplay labelType / EDTSourceNotes
legalEntityLegal EntityDataAreaIdEntAssetWorkOrderTable.DataAreaIdCompany of the work order, in lower case
workOrderIdWork OrderEntAssetWorkOrderIDEntAssetWorkOrderTable.WorkOrderIdWork order number
workOrderTypeWork Order TypeEntAssetWorkOrderTypeIDEntAssetWorkOrderType::findRecId(EntAssetWorkOrderTable.WorkOrderType).WorkOrderTypeIdThe work order type, for example Corrective or Preventive
descriptionDescriptionDescriptionEntAssetWorkOrderTable.DescriptionWork order description
lifecycleStateLifecycle StateEntAssetLifecycleStateIdEntAssetWorkOrderLifecycleState::find(EntAssetWorkOrderTable.WorkOrderLifecycleState).WorkOrderLifecycleStateIdNormally the first state of the work order type's lifecycle model (New in the USMF demo data)
functionalLocationFunctional LocationEntAssetFunctionalLocationIDEntAssetFunctionalLocation::findRecID(EntAssetWorkOrderTable.FunctionalLocation).FunctionalLocationIdThe work order's functional location, which D365 sets from the lines when they all share one functional location (at the level set in Asset management parameters); empty otherwise
assetIdsAssetsList of EntAssetObjectIDEntAssetObjectTable.ObjectID of each EntAssetWorkOrderLine.Object, in line-number orderEvery distinct asset on the work order's lines, as a JSON array of strings; [] when the work order has no lines
responsibleWorkerPersonnelNumberResponsible Worker Personnel NumberHcmPersonnelNumberIdHcmWorker.PersonnelNumber of the responsible maintenance workerEmpty when no responsible worker is set
responsibleWorkerNameResponsible Worker NameHcmWorkerNameEntAssetWorker::find(EntAssetWorkOrderTable.ResponsibleWorker).workerName()Name of the worker responsible for the work order (from the maintenance request, or from the responsible-worker setup of the lines' assets and jobs); empty when none
scheduledWorkerPersonnelNumberScheduled Worker Personnel NumberHcmPersonnelNumberIdHcmWorker.PersonnelNumber of the scheduled maintenance workerEmpty when no worker is scheduled
scheduledWorkerNameScheduled Worker NameHcmWorkerNameEntAssetWorker::find(EntAssetWorkOrderTable.WorkerScheduled).workerName()Name of the worker all the work order's jobs are scheduled to; normally empty at creation
expectedStartDateTimeExpected Start Date TimeutcdatetimeEntAssetWorkOrderTable.ExpectedStart"\/Date(ms)\/" form; defaults to the moment the work order was created, or comes from the service level, request or maintenance schedule
expectedEndDateTimeExpected End Date TimeutcdatetimeEntAssetWorkOrderTable.ExpectedEndThe empty date-time (1900-01-01T00:00:00Z, "\/Date(-2208988800000)\/") when not set
scheduledStartDateTimeScheduled Start Date TimeutcdatetimeEntAssetWorkOrderTable.ScheduledStartSet by work order scheduling; normally the empty date-time at creation
scheduledEndDateTimeScheduled End Date TimeutcdatetimeEntAssetWorkOrderTable.ScheduledEndSame as scheduledStartDateTime
actualStartDateTimeActual Start Date TimeutcdatetimeEntAssetWorkOrderTable.ActualStartStamped by a lifecycle state with Set actual start; the empty date-time at creation
actualEndDateTimeActual End Date TimeutcdatetimeEntAssetWorkOrderTable.ActualEndStamped by a lifecycle state with Set actual end; the empty date-time at creation

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; it orders the events of one record, gaps are normal and permanent, see the README), correlationId (String - always Scope:Key; groups emissions that belong to one logical run and defaults to Event: plus a GUID unique to this emission) 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.

This event does not join a run: correlationId is unique to each emission.

Example Payload​

A corrective work order created with the create work order dialog for one asset.

{
"BusinessEventId": "MGCLatticeMaintenanceWorkOrderCreatedBusinessEvent",
"ControlNumber": 587902,
"EventId": "3C9D51A7-2E84-4B6F-9A1D-7F05C2E8B413",
"EventTime": "\/Date(1790243894000)\/",
"EventTimeIso8601": "2026-09-24T09:58:14.2941246Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{39F67208-C799-0FFE-C5BA-11F02CCB11B4}",
"ParentContextRecordSubjects": [],
"eventSequence": 48213,
"correlationId": "Event:d2b7e4a1-5c3f-4e8a-9b61-0a4f7c9e2d35",
"schemaVersion": 1,
"legalEntity": "usmf",
"workOrderId": "WO-000013",
"workOrderType": "Corrective",
"description": "Replace knives on pelletizer for line 2",
"lifecycleState": "New",
"functionalLocation": "PP-02-02-01",
"assetIds": ["PX-201"],
"responsibleWorkerPersonnelNumber": "",
"responsibleWorkerName": "",
"scheduledWorkerPersonnelNumber": "",
"scheduledWorkerName": "",
"expectedStartDateTime": "\/Date(1790243880000)\/",
"expectedEndDateTime": "\/Date(-2208988800000)\/",
"scheduledStartDateTime": "\/Date(-2208988800000)\/",
"scheduledEndDateTime": "\/Date(-2208988800000)\/",
"actualStartDateTime": "\/Date(-2208988800000)\/",
"actualEndDateTime": "\/Date(-2208988800000)\/"
}

Activation​

In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Maintenance work order created") or by filtering the Category column to Asset management. 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). No other setup is required.