Create a Custom D365 Business Event
In the last two posts, we setup a D365 Business Events endpoint, we associated it with the actual event and then we enabled the business events to be processed. But what if you need a custom D365 business event to be triggered during a different process than the predefined Microsoft locations?
A custom business event is needed. During this post, I will show you the steps for creating a custom business event. In our case I will create a custom business event that sends the sales order status information, when a sales order packing slip is posted.
A custom business event requires three parts.
- A contract class that defines the details of the message to be sent.
- A Business Event class, that maps the data from the D365 object to the contract class.
- The chain of command method that creates the business event during the appropriate operation.
Create The Contract Class
The contract class defines all of the values that are sent in the business event message. In our case, we will create values for the legal entity, salesId and sales status.
In Visual Studio, create a new class and paste the following code.
/// <summary>
/// The data contract for a <c>rsmPackSlipPostedBusinessEvent</c>.
/// </summary>
[DataContract]
public final class rsmPackSlipPostedBusinessEventContract extends BusinessEventsContract
{
private SalesIdBase salesId;
private LegalEntityDataAreaId legalEntity;
private String50 salesStatus;
/// <summary>
/// Creates a <c>rsmPackSlipPostedBusinessEventContract</c> from a <c>CustInvoiceJour</c> record.
/// </summary>
/// <param name = "_custInvoiceJour">A <c>CustInvoiceJour</c> record.</param>
/// <returns>A <c>rsmPackSlipPostedBusinessEventContract</c>.</returns>
public static rsmPackSlipPostedBusinessEventContract newFromSalesTable(SalesTable _salesTable)
{
var contract = new rsmPackSlipPostedBusinessEventContract();
contract.initialize(_salesTable);
return contract;
}
private void initialize(SalesTable _salesTable)
{
CustParameters custParameters = CustParameters::find();
salesId = _salesTable.SalesId;
salesStatus = enum2Symbol(enumNum(SalesStatus), _salesTable.SalesStatus);
this.parmClientId(custParameters.rsmClientId);
this.parmClientSecret(custParameters.rsmClientSecret);
}
private void new()
{
}
[DataMember('SalesOrderId'), BusinessEventsDataMember("@AccountsReceivable:SalesOrderId")]
public SalesIdBase parmSaleOrderId(SalesIdBase _salesId = salesId)
{
salesId = _salesId;
return salesId;
}
[DataMember('SalesStatus'), BusinessEventsDataMember("@AccountsReceivable:SalesStatus")]
public String50 parmSalesStatus(String50 _salesStatus = salesStatus)
{
salesStatus = _salesStatus;
return salesStatus;
}
[DataMember('LegalEntity'), BusinessEventsDataMember("@AccountsReceivable:LegalEntity")]
public LegalEntityDataAreaId parmLegalEntity(LegalEntityDataAreaId _legalEntity = legalEntity)
{
legalEntity = _legalEntity;
return legalEntity;
}
[DataMember('ClientId'), BusinessEventsDataMember("@AccountsReceivable:ClientId")]
public SMTPPassword parmClientId(SMTPPassword _clientId = clientId)
{
clientId = _clientId;
return clientId;
}
[DataMember('ClientSecret'), BusinessEventsDataMember("@AccountsReceivable:ClientSecret")]
public SMTPPassword parmClientSecret(SMTPPassword _clientSecret = clientSecret)
{
clientSecret = _clientSecret;
return clientSecret;
}
}
Create The Business Event Class
The business event class maps the data from the D365 object to the business event contract class. In our case, we will map data from the salesTable record to our contract class.
In Visual Studio, create a new class and paste the following code.
/// <summary>
/// Packing slip posted business event.
/// </summary>
[BusinessEvents(classStr(rsmPackSlipPostedBusinessEventContract), 'AccountsReceivable:rsmPackSlipPostedBusinessEventName', 'AccountsReceivable:rsmPackSlipPostedBusinessEventDescription', ModuleAxapta::SalesOrder)]
public final class rsmPackSlipPostedBusinessEvent extends BusinessEventsBase
{
private SalesTable salesTable;
private SalesTable parmSalesTable(SalesTable _salesTable = salesTable)
{
salesTable = _salesTable;
return salesTable;
}
/// <summary>
/// Creates a <c>rsmPackSlipPostedBusinessEvent</c> from a <c>CustInvoiceJour</c> record.
/// </summary>
/// <param name = "_custInvoiceJour"> A <c>CustInvoiceJour</c> record.</param>
/// <returns>A <c>rsmPackSlipPostedBusinessEvent</c>.</returns>
public static rsmPackSlipPostedBusinessEvent newFromSalesTable(SalesTable _salesTable)
{
rsmPackSlipPostedBusinessEvent businessEvent = new rsmPackSlipPostedBusinessEvent();
businessEvent.parmSalesTable(_salesTable);
return businessEvent;
}
private void new()
{
}
[Wrappable(true), Replaceable(true)]
public BusinessEventsContract buildContract()
{
return rsmPackSlipPostedBusinessEventContract::newFromSalesTable(salesTable);
}
}
Call The Business Event
When the sales order packing slip is posted, we will call the business event.
First we need to find the correct spot in code where this action takes place. Fortunately I have already found this place. It is in the method endPost in the class SalesPackingSlipJournalPost.
In Visual Studio, create a new class, and paste in the following code.
[ExtensionOf(classStr(SalesPackingSlipJournalPost))]
final class rsmSalesPackingSlipJournalPost_Extension
{
protected void endPost()
{
next endPost();
// Send PackSlipPostedBusinessEvent
if(chainFormLetterContract.parmVersioningUpdateType() == VersioningUpdateType::Initial)
{
// Pack Slip posted
rsmPackSlipPostedBusinessEvent::newFromSalesTable(salesTable).send();
}
}
}
This uses chain of command to do the following:
- Call the base endPost logic.
- Instantiate the custom business event contract class and populate it using the available salesTable record that is a class variable.
- Call the send() method on the business event base class to create the business event record in the staging table. The business event batch job will actually process the record.
Build your solution.
Activate The Custom Business Event
Finally go to System administration > Setup > Business events > Business events catalog
On the form, click on Manage > Rebuild business event catalog. This will cause the system to see your newly created custom business event and add it to the grid shown on this form.
Now you are able to select and activate the Business Event and associate it with an endpoint, just like any other business event. You can follow the steps in the previous two blog articles for steps on how to:
- Setup the endpoint: https://dynamics365musings.com/how-to-setup-d365-business-events-part-1/
- Activate and enable the business event https://dynamics365musings.com/how-to-setup-d365-business-event-part-2/
Hello! Do you use Twitter? I’d like to folloiw you if that would be okay.
I’m definitely enjoying your blog andd lopk forward tooo new articles.
Very well worded!
Really interesting article, thank you. Do you happen to know if it is possible to activate the business event through x++ and not only through D365 UI?
Hi Peter, thanks for this! Trying to learn concepts by copying your code, however for the lines:
this.parmClientId(CustParameters.rsmClientId);
this.parmClientSecret(CustParameters.rsmClientSecret);
I am getting error msgs that the CustParameters table doesn’t contain those fields. Are you using a customized / extended CustParameters table?
Yes. I am using extended fields in this case. You can replace the references to custparameters with some hard coded values while testing. The client id and client secret have to be set up in the azure portal.
Thanks! Will it work if I just comment out the blocks dealing with clientId & clientSecret? Or can I replace them with other fields from CustParameters table? Sorry, very new to this, just trying to learn code flow at this point (do not have admin access to Azure portal anyway).
You can comment it out to make it compile. But it won’t actually work unless you set the client id and client secret.