Skip to main content

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 classMGCLatticeDMFExecutionCompletedBusinessEvent
Contract classMGCLatticeDMFExecutionCompletedBusinessEventContract
CategorySystem administration
ModuleModuleAxapta::SystemAdministration
Label IDsDmfExecutionCompletedBusinessEventName / DmfExecutionCompletedBusinessEventDescription
TriggerDMFDefinitionGroupExecution.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 signature public void update(container recordCount) (the platform method is update(container recordCount = conNull())). After next update(recordCount), the event sends when orig().TargetStatus was not terminal and the new TargetStatus is Finished, Error, or Canceled (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() (in DMFEntityDbExporter::updateExecutionHistoryOnCompletionV2()), which bypasses update(). Extension class MGCLatticeDMFEntityDbExporter_Class_Extension, decorated with [ExtensionOf(classStr(DMFEntityDbExporter))], wraps public 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 terminal TargetStatus (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 through update() 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) or DMFEntityWriterTasksStatus (parallel tasks), both via update();
    • Copy data to target, run from the execution history with or without batch (also DMFEntityWriter);
    • file exports, which record their result in DMFGenerateSSISPackage via update();
    • 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 overrides update() the statement runs record by record through the row hook - and the platform's clean-up of jobs stuck in Executing.
  • Imports run without batch (Import instead of Import in batch): the target step runs inside DMFGenerateSSISPackage.generateStagingDataInner(), which writes the entity's final status with doUpdate() first and only then calls update() on the same record. By then orig() 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 through update(); and a target step that fails with an unhandled error is reported as Failed through the error handler's update(). 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() and update_recordset under skipDataMethods(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 fieldDisplay labelType / EDTSourceNotes
legalEntityLegal EntityDataAreaIdDMFDefinitionGroupExecution.CompanyThe 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
executionIdExecution IDDMFExecutionIdDMFDefinitionGroupExecution.ExecutionIdSame for every entity of one run; use it to group emissions
definitionGroupDefinition GroupDMFDefinitionGroupNameDMFDefinitionGroupExecution.DefinitionGroupThe data project name
entityEntityDMFEntityNameDMFDefinitionGroupExecution.EntityData entity name as shown in the data project
sourceSourceDMFSourceNameDMFDefinitionGroupExecution.SourceSource data format
outcomeOutcomestrDerived in deriveOutcome() from TargetStatus and the error countsError -> "Failed"; Canceled -> "Canceled"; Finished with any staging or target errors -> "PartiallyFailed"; Finished with none -> "Succeeded"
targetStatusTarget StatusintDMFDefinitionGroupExecution.TargetStatus (as integer)DMFBatchJobStatus: 3 Error, 4 Finished, 8 Canceled
targetStatusNameTarget Status NamestrTargetStatus (enum symbol)"Error", "Finished", or "Canceled"
stagingStatusStaging StatusintDMFDefinitionGroupExecution.StagingStatus (as integer)DMFBatchJobStatus: 0 NotStarted, 1 Waiting, 2 Executing, 3 Error, 4 Finished, 5 Ready, 6 NotRun, 7 Canceling, 8 Canceled, 9 Hold
stagingStatusNameStaging Status NamestrStagingStatus (enum symbol)
recordCountRecord CountintDMFDefinitionGroupExecution.NoOfRecords
targetNewCountTarget New CountintDMFDefinitionGroupExecution.NumOfTargetNewRecords created in the target
targetUpdatedCountTarget Updated CountintDMFDefinitionGroupExecution.NumOfTargetUpdatedRecords updated in the target
targetDeletedCountTarget Deleted CountintDMFDefinitionGroupExecution.NumOfTargetDeletedRecords deleted in the target
stagingErrorCountStaging Error CountintDMFDefinitionGroupExecution.StagingErrorCount
targetErrorCountTarget Error CountintDMFDefinitionGroupExecution.TargetErrorCount
totalEntitiesInExecutionTotal Entities In ExecutionintCount of DMFDefinitionGroupExecution rows with the same ExecutionIdNumber 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.