How to : generate an external trigger after the completion of BYODW export using the business event in Dynamics 365 Finance and Operations
In BI projects for Dynamics 365 Finance and Operations, the most common requirement is the trigger when the BYODW batch job completes the data population. Usually, to start another process on exported data.
After some research, we found the best solution for this is to create a custom business event. Business events in Dynamics 365 are robust and flexible enough to be used for many different scenarios.
To understand the Business events framework, please go through https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/business-events/home-page
The business events framework provides different endpoints for the consumption of the trigger.
After some research, we found the best solution for this is to create a custom business event. Business events in Dynamics 365 are robust and flexible enough to be used for many different scenarios.
To understand the Business events framework, please go through https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/business-events/home-page
The business events framework provides different endpoints for the consumption of the trigger.
- Azure Service Bus Queue
- Azure Service Bus Topic
- Azure Event Grid
- Azure Event Hub
- HTTPS
- Microsoft Power Automate
Below is the custom code required for the generation of events.
We need 4 classes:
1. BYODWBusinessEvent
2. BYODWBusinessEventContract
3. DMFPackageExporter_Extension (This is for manual execution)
4. DMFExportEntityWriter_Extension (This is for a batch execution)
4. DMFExportEntityWriter_Extension (This is for a batch execution)
1. BYODWBusinessEvent class extends the BusinessEventsBase class.
/// <summary>
/// BYODW business event.
/// </summary>
[BusinessEvents(classStr(BYODWBusinessEventContract), 'BYODW Business Event', 'BYODW Business Event', ModuleAxapta::DIXF)]
public final class BYODWBusinessEvent extends BusinessEventsBase
{
private DMFExecution dmfExecution;
private DMFExecution parmDMFExecution(DMFExecution _dmfExecution = dmfExecution)
{
dmfExecution = _dmfExecution;
return dmfExecution;
}
/// <summary>
/// Creates a <c>BYODWBusinessEvent</c> from a <c>DMFExecution</c> record.
/// </summary>
/// <param name = "_transferTable"> A <c>DMFExecution</c> record.</param>
/// <returns>A <c>BYODWBusinessEvent</c>.</returns>
public static BYODWBusinessEvent newFromDMFExecution(DMFExecution _dmfExecution)
{
BYODWBusinessEvent businessEvent = new BYODWBusinessEvent();
businessEvent.parmDMFExecution(_dmfExecution);
return businessEvent;
}
private void new()
{
}
[Wrappable(true), Replaceable(true)]
public BusinessEventsContract buildContract()
{
return BYODWBusinessEventContract::newFromDMFExecution(dmfExecution);
}
}
2. Contract class for BYODWBusinessEvent extends the BusinessEventsContract class.
/// <summary>
/// The data contract for a <c>BYODWBusinessEventContract</c>.
/// </summary>
[DataContract]
public final class BYODWBusinessEventContract extends BusinessEventsContract
{
private DMFExecutionSummaryStatus dmfExecutionSummaryStatus;
private EndDateTime endDateTime;
private DMFExecutionId dmfExecutionId;
private DMFDefinitionGroupName dmfDefinitionGroupName;
/// <summary>
/// Creates a <c>BYODWBusinessEventContract</c> from a <c>InventTransferTable</c> record.
/// </summary>
/// <param name = "_transferOrder">DMFExecutionId</param>
/// <returns>A <c>BYODWBusinessEventContract</c>.</returns>
public static BYODWBusinessEventContract newFromDMFExecution(DMFExecution _dmfExecution)
{
var contract = new BYODWBusinessEventContract();
contract.initialize(_dmfExecution);
return contract;
}
private void initialize(DMFExecution _dmfExecution)
{
dmfExecutionSummaryStatus = _dmfExecution.GetExecutionSummaryStatus();
endDateTime = _dmfExecution.getExecutionEndDateTime();
dmfExecutionId = _dmfExecution.ExecutionId;
dmfDefinitionGroupName = _dmfExecution.DefinitionGroup;
}
[DataMember('DMFExecutionSummaryStatus'), BusinessEventsDataMember("DMFExecutionSummaryStatus")]
public DMFExecutionSummaryStatus parmDMFExecutionSummaryStatus(DMFExecutionSummaryStatus _dmfExecutionSummaryStatus = dmfExecutionSummaryStatus)
{
dmfExecutionSummaryStatus = _dmfExecutionSummaryStatus;
return dmfExecutionSummaryStatus;
}
[DataMember('EndDateTime'), BusinessEventsDataMember("EndDateTime")]
public EndDateTime parmEndDateTime(EndDateTime _endDateTime = endDateTime)
{
endDateTime = _endDateTime;
return endDateTime;
}
[DataMember('DMFExecutionId'), BusinessEventsDataMember("DMFExecutionId")]
public DMFExecutionId parmDMFExecutionId(DMFExecutionId _dmfExecutionId = dmfExecutionId)
{
dmfExecutionId = _dmfExecutionId;
return dmfExecutionId;
}
[DataMember('DMFDefinitionGroupName'), BusinessEventsDataMember("DMFDefinitionGroupName")]
public DMFDefinitionGroupName parmDMFDefinitionGroupName(DMFDefinitionGroupName _dmfDefinitionGroupName = dmfDefinitionGroupName)
{
dmfDefinitionGroupName = _dmfDefinitionGroupName;
return dmfDefinitionGroupName;
}
}
3. DMFPackageExporter_Extension which generates the trigger in manual execution
/// <summary>
/// Extension class for DMFPackageExporter
/// </summary>
[ExtensionOf(classstr(DMFPackageExporter))]
final class DMFPackageExporter_Extension
{
/// <summary>
/// Export a package for the specified definition group
/// </summary>
/// <param name = "packageName">Name of package to generate</param>
/// <param name = "packageUrl">Url to upload the package to</param>
/// <param name = "definitionGroupId">Definition group name</param>
/// <param name = "executionId">Job id</param>
/// <param name = "reExecute">Indicate if data from target to staging is to be moved</param>
/// <param name = "legalEntityId">Legal entity context for the operation</param>
/// <param name = "createPackage">Specify if only the files are needed or if the package is also to be generated</param>
/// <param name="entitySyncVersion">List of entity and the associated sync version range. This is only required for rerun scenario for older executions. Not enabled for AX UI</param>
/// <param name="previewCount">Preview count for export.</param>
/// <param name="_messageId">Message ID.</param>
/// <returns>ExecutionId for preoject execution.</returns>
public DMFExecutionId exportToPackageV2(DMFPackageName packageName, SharedServiceUnitURL packageUrl,
DMFDefinitionGroupName definitionGroupId, DMFExecutionId executionId, boolean reExecute,
DataAreaName legalEntityId, boolean createPackage, Map entitySyncVersion, int previewCount, str _messageId)
{
DMFExecutionId localDMFExecutionId = next exportToPackageV2(packageName,packageUrl, definitionGroupId, executionId, reExecute,
legalEntityId, createPackage, entitySyncVersion, previewCount, _messageId);
BYODWBusinessEvent businessEvent;
DMFExecution localDMFExecution = DMFExecution::find(localDMFExecutionId);
DMFDefinitionGroup dmfDefinitionGroup = DMFDefinitionGroup::find(localDMFExecution.DefinitionGroup);
DMFExecutionSummaryStatus DMFExecutionSummaryStatus = localDMFExecution.GetExecutionSummaryStatus();
switch(DMFExecutionSummaryStatus)
{
case DMFExecutionSummaryStatus::Succeeded:
businessEvent= BYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.BusinessEventSucess)
{
businessEvent.send();
}
break;
case DMFExecutionSummaryStatus::PartiallySucceeded:
businessEvent = BYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.BusinessEventPartialSucess)
{
businessEvent.send();
}
break;
case DMFExecutionSummaryStatus::Failed:
businessEvent = BYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.BusinessEventFail)
{
businessEvent.send();
}
break;
}
return localDMFExecutionId;
}
}
/// <summary>
/// Extension class for DMFExportEntityWriter for the business event trigger when export is happening in Batch mode
/// </summary>
[ExtensionOf(classstr(DMFExportEntityWriter))]
final class PgonDMFExportEntityWriter_Extension
{
public void run()
{
next run();
PgonBYODWBusinessEvent businessEvent;
DMFExecution localDMFExecution = DMFExecution::find(this.parmExecutionId());
DMFDefinitionGroup dmfDefinitionGroup = DMFDefinitionGroup::find(localDMFExecution.DefinitionGroup);
DMFExecutionSummaryStatus DMFExecutionSummaryStatus = localDMFExecution.GetExecutionSummaryStatus();
switch(DMFExecutionSummaryStatus)
{
case DMFExecutionSummaryStatus::Succeeded:
businessEvent= PgonBYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.PgonBusinessEventSucess)
{
businessEvent.send();
}
break;
case DMFExecutionSummaryStatus::PartiallySucceeded:
businessEvent = PgonBYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.PgonBusinessEventPartialSucess)
{
businessEvent.send();
}
break;
case DMFExecutionSummaryStatus::Failed:
businessEvent = PgonBYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.PgonBusinessEventFail)
{
businessEvent.send();
}
break;
}
}
}
4. DMFExportEntityWriter_Extension which generates the trigger in batch execution
/// Extension class for DMFExportEntityWriter for the business event trigger when export is happening in Batch mode
/// </summary>
[ExtensionOf(classstr(DMFExportEntityWriter))]
final class PgonDMFExportEntityWriter_Extension
{
public void run()
{
next run();
PgonBYODWBusinessEvent businessEvent;
DMFExecution localDMFExecution = DMFExecution::find(this.parmExecutionId());
DMFDefinitionGroup dmfDefinitionGroup = DMFDefinitionGroup::find(localDMFExecution.DefinitionGroup);
DMFExecutionSummaryStatus DMFExecutionSummaryStatus = localDMFExecution.GetExecutionSummaryStatus();
switch(DMFExecutionSummaryStatus)
{
case DMFExecutionSummaryStatus::Succeeded:
businessEvent= PgonBYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.PgonBusinessEventSucess)
{
businessEvent.send();
}
break;
case DMFExecutionSummaryStatus::PartiallySucceeded:
businessEvent = PgonBYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.PgonBusinessEventPartialSucess)
{
businessEvent.send();
}
break;
case DMFExecutionSummaryStatus::Failed:
businessEvent = PgonBYODWBusinessEvent::newFromDMFExecution(localDMFExecution);
if(businessEvent && dmfDefinitionGroup.PgonBusinessEventFail)
{
businessEvent.send();
}
break;
}
}
}
We also added the parameters in data projects as follows:
Now we just need to configure the business events in System administrator -> Setup -> Business events - > Business events catalog
We had configured the Azure Service Bus to capture the event and trigger 3rd party code to process the data further. We can configure Microsoft Power automate (FLOW) to process the event as well.
Comments
Post a Comment