DMF execution completed
Fires when an entity in a Data management (DMF) data project execution finishes its target step - its target status becomes Finished, Error, or Canceled. It carries the execution ID, data project, entity, the company, a derived outcome (Succeeded, PartiallyFailed, Failed, or Canceled), and the record and error counts, so an integration chain can react to a finished import or export instead of polling the execution history. The event fires per entity: a data project with several entities produces one emission for each. Batch imports, copies to target, file exports and bring-your-own-database exports raise it; an import run without batch mostly doesn't (see below).
| Event class | MGCLatticeDMFExecutionCompletedBusinessEvent |
| Contract class | MGCLatticeDMFExecutionCompletedBusinessEventContract |
| Category | System administration |
| Module | ModuleAxapta::SystemAdministration |
| Label IDs | DmfExecutionCompletedBusinessEventName / DmfExecutionCompletedBusinessEventDescription |
| Trigger | DMFDefinitionGroupExecution.update() -> TargetStatus moves from a non-terminal to a terminal value; DMFEntityDbExporter::generateEntityDBDataV2() -> a bring-your-own-database export wrote a terminal TargetStatus |
Trigger / Hook
Two send sites, because DMF records an entity's result in two different ways.
- Row update. Extension class
MGCLatticeDMFDefinitionGroupExecution_Table_Extension, decorated with[ExtensionOf(tableStr(DMFDefinitionGroupExecution))]; CoC method signaturepublic void update(container recordCount)(the platform method isupdate(container recordCount = conNull())). Afternext update(recordCount), the event sends whenorig().TargetStatuswas not terminal and the newTargetStatusisFinished,Error, orCanceled(MGCLatticeDMFExecutionCompletedBusinessEvent::isTerminal()). It fires once per entity row, on the transition into a terminal status. - Bring-your-own-database (BYOD) export. A BYOD export writes its result with
doUpdate()(inDMFEntityDbExporter::updateExecutionHistoryOnCompletionV2()), which bypassesupdate(). Extension classMGCLatticeDMFEntityDbExporter_Class_Extension, decorated with[ExtensionOf(classStr(DMFEntityDbExporter))], wrapspublic static void generateEntityDBDataV2(DMFDefinitionGroupEntity, DMFExecutionId, DMFRefreshType, NoYes, int64)- the method both BYOD export paths call for one entity. It reads the entity's execution row before the export and again afterwards (also when the export fails with an error), and sends the event once if the export wrote the row and left a terminalTargetStatus(isNewCompletion(): the row version changed and the status is terminal). A re-run of an entity that failed before is reported again; an entity the export skips because it already finished isn't. While the export runs, the row-update hook leaves that entity's row to the wrapper (MGCLatticeDmfByodExportContext), so a failure the exporter writes throughupdate()isn't reported twice.
Which runs raise it, verified against the platform code:
- Raised:
- the target step of batch imports, which records its result through
DMFEntityWriter.updateExecutionRecord()(single task) orDMFEntityWriterTasksStatus(parallel tasks), both viaupdate(); - Copy data to target, run from the execution history with or without batch (also
DMFEntityWriter); - file exports, which record their result in
DMFGenerateSSISPackageviaupdate(); - bring-your-own-database exports, through the export wrapper;
- cancellations by the import and export task schedulers - those use
update_recordset, but because the platform table overridesupdate()the statement runs record by record through the row hook - and the platform's clean-up of jobs stuck in Executing.
- the target step of batch imports, which records its result through
- Imports run without batch (Import instead of Import in batch): the target step runs inside
DMFGenerateSSISPackage.generateStagingDataInner(), which writes the entity's final status withdoUpdate()first and only then callsupdate()on the same record. By thenorig()already holds the terminal status, so a completed import of a regular entity isn't reported. Two exceptions: a composite entity reports one row, the one the platform then updates throughupdate(); and a target step that fails with an unhandled error is reported asFailedthrough the error handler'supdate(). Run imports in batch when you rely on this event. - Not raised: the status the platform copies onto the other rows of a composite entity, written with
doupdate()andupdate_recordsetunderskipDataMethods(true); only the row the platform updates directly fires. - Timing: the row-update hook runs inside the platform's transaction, so the event is delivered only after that transaction commits. The BYOD wrapper sends after the export's own transactions have committed.
- Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeDMFExecutionCompletedBusinessEvent)), checked at both send sites.
public void update(container recordCount)
{
DMFBatchJobStatus origTargetStatus = this.orig().TargetStatus;
next update(recordCount);
if (!MGCLatticeDMFExecutionCompletedBusinessEvent::isTerminal(origTargetStatus)
&& MGCLatticeDMFExecutionCompletedBusinessEvent::isTerminal(this.TargetStatus)
&& !MGCLatticeDmfByodExportContext::coversRow(this))
{
if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeDMFExecutionCompletedBusinessEvent)))
{
MGCLatticeDMFExecutionCompletedBusinessEvent::newFromDefinitionGroupExecution(this).send();
}
}
}
public static void generateEntityDBDataV2(
DMFDefinitionGroupEntity _definitionGroupEntity, DMFExecutionId _executionId,
DMFRefreshType _refreshType, NoYes _skipStaging, int64 _syncVersion)
{
DMFDefinitionGroupExecution before;
MGCLatticeDmfByodExportContext scope;
// Cached activation check first: when the event is not activated the export runs with no extra read.
if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeDMFExecutionCompletedBusinessEvent)))
{
before = MGCLatticeDMFExecutionCompletedBusinessEvent::findExecution(
_definitionGroupEntity.DefinitionGroup, _definitionGroupEntity.Entity, _executionId);
scope = MGCLatticeDmfByodExportContext::construct(
_definitionGroupEntity.DefinitionGroup, _definitionGroupEntity.Entity, _executionId);
}
try
{
next generateEntityDBDataV2(_definitionGroupEntity, _executionId, _refreshType, _skipStaging, _syncVersion);
}
finally
{
if (scope)
{
scope.Dispose();
MGCLatticeDMFExecutionCompletedBusinessEvent::sendIfCompletedSince(before);
}
}
}
Payload Contract
One row per [DataMember], in declaration order. Sources are the DMFDefinitionGroupExecution row that just completed, mapped in initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | DMFDefinitionGroupExecution.Company | The company the entity was imported into or exported from, in lower case (for example "usmf"). The execution table is shared across companies, so it has no DataAreaId; DMF records the company in this field |
executionId | Execution ID | DMFExecutionId | DMFDefinitionGroupExecution.ExecutionId | Same for every entity of one run; use it to group emissions |
definitionGroup | Definition Group | DMFDefinitionGroupName | DMFDefinitionGroupExecution.DefinitionGroup | The data project name |
entity | Entity | DMFEntityName | DMFDefinitionGroupExecution.Entity | Data entity name as shown in the data project |
source | Source | DMFSourceName | DMFDefinitionGroupExecution.Source | Source data format |
outcome | Outcome | str | Derived in deriveOutcome() from TargetStatus and the error counts | Error -> "Failed"; Canceled -> "Canceled"; Finished with any staging or target errors -> "PartiallyFailed"; Finished with none -> "Succeeded" |
targetStatus | Target Status | int | DMFDefinitionGroupExecution.TargetStatus (as integer) | DMFBatchJobStatus: 3 Error, 4 Finished, 8 Canceled |
targetStatusName | Target Status Name | str | TargetStatus (enum symbol) | "Error", "Finished", or "Canceled" |
stagingStatus | Staging Status | int | DMFDefinitionGroupExecution.StagingStatus (as integer) | DMFBatchJobStatus: 0 NotStarted, 1 Waiting, 2 Executing, 3 Error, 4 Finished, 5 Ready, 6 NotRun, 7 Canceling, 8 Canceled, 9 Hold |
stagingStatusName | Staging Status Name | str | StagingStatus (enum symbol) | |
recordCount | Record Count | int | DMFDefinitionGroupExecution.NoOfRecords | |
targetNewCount | Target New Count | int | DMFDefinitionGroupExecution.NumOfTargetNew | Records created in the target |
targetUpdatedCount | Target Updated Count | int | DMFDefinitionGroupExecution.NumOfTargetUpdated | Records updated in the target |
targetDeletedCount | Target Deleted Count | int | DMFDefinitionGroupExecution.NumOfTargetDeleted | Records deleted in the target |
stagingErrorCount | Staging Error Count | int | DMFDefinitionGroupExecution.StagingErrorCount | |
targetErrorCount | Target Error Count | int | DMFDefinitionGroupExecution.TargetErrorCount | |
totalEntitiesInExecution | Total Entities In Execution | int | Count of DMFDefinitionGroupExecution rows with the same ExecutionId | Number of entity rows in the run; lets a subscriber tell when it has received every entity's emission |
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 doesn't call correlateTo, so correlationId is unique to each emission; group the entities of one run by executionId.
Example Payload
One entity of a batch import that finished with two target errors:
{
"BusinessEventId": "MGCLatticeDMFExecutionCompletedBusinessEvent",
"ControlNumber": 5637151266,
"EventId": "47A1E0C9-8D35-4B72-A6F4-0E9B3C5D2A18",
"EventTime": "\/Date(1790268499000)\/",
"EventTimeIso8601": "2026-09-24T16:48:19.3554080Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{86E0085A-983E-92F8-DF04-BD39A26D8AB6}",
"ParentContextRecordSubjects": [],
"eventSequence": 48822,
"correlationId": "Event:7f0c2b94-6a1e-4d38-b5c7-e9d2a04f6b81",
"schemaVersion": 1,
"legalEntity": "usmf",
"executionId": "CustomerImport-2026-09-24T16:45:02-5B1F4C0E9A2D4E7B8C3F6A1D0E9B2C7F",
"definitionGroup": "CustomerImport",
"entity": "Customers V3",
"source": "CSV-Unicode",
"outcome": "PartiallyFailed",
"targetStatus": 4,
"targetStatusName": "Finished",
"stagingStatus": 4,
"stagingStatusName": "Finished",
"recordCount": 250,
"targetNewCount": 212,
"targetUpdatedCount": 36,
"targetDeletedCount": 0,
"stagingErrorCount": 0,
"targetErrorCount": 2,
"totalEntitiesInExecution": 1
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("DMF execution completed") or by filtering the Category column to System administration. 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 Lattice setup is needed. Run the imports you depend on in batch (Import in batch, a recurring data job, or a batch Copy data to target): an import run without batch doesn't report a successful completion.