Private Preview: Business Events the newest integration with Business Central

Private Preview: Business Events the newest integration with Business Central

Note:
This feature is still in private preview and has his limitation.
You can see all details here (also how you can be part of the private preview):
d365bcdv/samples/Business Events/Private Preview at main · microsoft/d365bcdv · GitHub

Next the already integrations with Dataverse (Data Sync, Virtual Entities and Data Events) there is a fourth integration: Business Events.

With Business Events you can respond to an action inside Business Central. So for example if a Purchase Invoice is posted or when a Sales Quote is transferred to a Sales Order. You van add your own Business Events through AL code. In the example below I react on a event in the standard code. Bot you can also implement it in your own code!

(source: d365bcdv/samples/Business Events/Private Preview at main · microsoft/d365bcdv · GitHub)

So first you can extend the enum “EventCategory”:

enumextension 50110 MyEventCategory extends EventCategory
{
    value(50110; "My Inventory Events")
    {
        Caption = 'Inventory Events';
    }
}

After this you can add your Business Event in AL like below example when a Transfer Order is shipped or Recieved:

Codeunit 50110 MyBusinessEvents
{
    var
        EventCategory: Enum EventCategory;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"TransferOrder-Post (Yes/No)", 'OnAfterPost', '', false, false)]
    local procedure OnAfterPostTransferOrder(var TransHeader: Record "Transfer Header"; Selection: Option " ",Shipment,Receipt)
    var
        Url: Text[250];
        TransferReceiptApiUrlTok: Label 'v2.0/companies(%1)/transferReceiptHeader(%2)', Locked = true;
        TransferShipmentApiUrlTok: Label 'v2.0/companies(%1)/transferShipmentHeader(%2)', Locked = true;
        Company: Record Company;
        TransferReceiptHeader: Record "Transfer Receipt Header";
        TransferShipmentHeader: Record "Transfer Shipment Header";
    begin

        Company.Get(CompanyName); //Because Company Id is not available yet

        if Selection = Selection::Shipment then begin
            TransferShipmentHeader.SetRange("Transfer Order No.", TransHeader."No.");
            TransferShipmentHeader.FindLast();
            Url := GetBaseUrl() + StrSubstNo(TransferShipmentApiUrlTok, GetCompanyId(), TrimId(TransferShipmentHeader.SystemId));
            MyBusinessEventTransferOrderPostedShipment(TransferShipmentHeader.SystemId, Url, Company.SystemId, TransferShipmentHeader."Direct Transfer");
        end;
        if Selection = Selection::Receipt then begin
            TransferReceiptHeader.SetRange("Transfer Order No.", TransHeader."No.");
            TransferReceiptHeader.FindLast();
            Url := GetBaseUrl() + StrSubstNo(TransferReceiptApiUrlTok, GetCompanyId(), TrimId(TransferReceiptHeader.SystemId));
            MyBusinessEventTransferOrderPostedReceipt(TransferReceiptHeader.SystemId, Url, Company.SystemId, TransHeader."Direct Transfer");
        end;
    end;

    [ExternalBusinessEvent('TransferOrderPostedShipment', 'Transfer Order Posted for Shipment', 'This business event is triggered when a Transfer Order is posted', EventCategory::"My Inventory Events")]
    local procedure MyBusinessEventTransferOrderPostedShipment(TrasferOrderId: Guid; Url: text[250]; CompanyId: Guid; DirectTransfer: Boolean)
    begin
    end;

    [ExternalBusinessEvent('TransferOrderPostedShipmentReceipt', 'Transfer Order Posted for Receipt', 'This business event is triggered when a Transfer Order is posted', EventCategory::"My Inventory Events")]
    local procedure MyBusinessEventTransferOrderPostedReceipt(TrasferOrderId: Guid; Url: text[250]; CompanyId: Guid; DirectTransfer: Boolean)
    begin
    end;

    local procedure GetBaseUrl(): text
    begin
        exit(GetUrl(CLIENTTYPE::Api));
    end;

    local procedure GetCompanyId(): text
    var
        Company: Record Company;
    begin
        Company.Get(CompanyName);
        exit(TrimId(Company.SystemId));
    end;

    local procedure TrimId(Id: Guid): Text
    begin
        exit(DelChr(Format(Id), '<>', '{}'));
    end;
}

The beauty of this is that you can define your own parameters that will send into the body. In this case we are sending the following parameters:
– TransferOrderId
– DirectTransfer
– CompanyID (this is beacuse the company isn’t there by default. That is a know issue but you can get it by adding this to your Business Event).
– URL
– CompanyCode

When you do a get on api/microsoft/runtime/v1.0/externalbusinesseventdefinitions you can also see it:

"value": [
        {
            "appId": "cd388a4b-3b9e-4756-aea6-d17f72d6e970",
            "name": "TransferOrderPostedShipment",
            "payload": "[{\"Index\":0,\"Name\":\"TrasferOrderId\",\"Type\":\"Guid\"},{\"Index\":1,\"Name\":\"Url\",\"Type\":\"Text[250]\"},{\"Index\":2,\"Name\":\"CompanyId\",\"Type\":\"Guid\"},{\"Index\":3,\"Name\":\"DirectTransfer\",\"Type\":\"Boolean\"}]",
            "displayName": "Transfer Order Posted for Shipment",
            "description": "This business event is triggered when a Transfer Order is posted",
            "category": "My Inventory Events",
            "appName": "BusinessEventDataverse",
            "appPublisher": "Default publisher",
            "appVersion": "1.0.0.0"
        },
        {
            "appId": "cd388a4b-3b9e-4756-aea6-d17f72d6e970",
            "name": "TransferOrderPostedShipmentReceipt",
            "payload": "[{\"Index\":0,\"Name\":\"TrasferOrderId\",\"Type\":\"Guid\"},{\"Index\":1,\"Name\":\"Url\",\"Type\":\"Text[250]\"},{\"Index\":2,\"Name\":\"CompanyId\",\"Type\":\"Guid\"},{\"Index\":3,\"Name\":\"DirectTransfer\",\"Type\":\"Boolean\"}]",
            "displayName": "Transfer Order Posted for Receipt",
            "description": "This business event is triggered when a Transfer Order is posted",
            "category": "My Inventory Events",
            "appName": "BusinessEventDataverse",
            "appPublisher": "Default publisher",
            "appVersion": "1.0.0.0"
        }
    ]

Now you can subscribe to your Business Event in two ways:

External app

When you want to subscribe to your external app you first have to subscribe your app to the Business Event. That is possible to do a Post with the following body:

POST https://api.businesscentral.dynamics.com/v2.0/<tenant-ID>/<environment>/api/microsoft/runtime/v1.0/externaleventsubscriptions

{
   "companyName": "CRONUS NL",
   "eventName": "TransferOrderPostedShipment",
   "appId": "cd388a4b-3b9e-4756-aea6-d17f72d6e970",
   "notificationUrl": "https://prod-94.westeurope.logic.azure.com:443/workflows/0d6f47c012ec492ab809de453d23cdc3/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=Ry_7MyF9C2AJswEDqZk8pHmK-QspBFGTzdDZJaYAahU"
}

You can also view all your subscriptions:

GET https://api.businesscentral.dynamics.com/v2.0/<tenantId>/<environment>/api/microsoft/runtime/v1.0/externaleventsubscriptions

Result:
{
    "@odata.context": "https://api.businesscentral.dynamics.com/v2.0/<tenantId>/<environment>/api/microsoft/runtime/v1.0/$metadata#externaleventsubscriptions",
    "value": [
        {
            "@odata.etag": "W/\"JzE5OzE5MDUyNjM0MjcyNjg0MzU1MjUxOzAwOyc=\"",
            "id": "2473052d-db1c-4144-bb1c-2f4fbdaed067",
            "timestamp": 105709,
            "appId": "cd388a4b-3b9e-4756-aea6-d17f72d6e970",
            "eventName": "CustomerBlocked",
            "companyName": "",
            "userId": "89f9aa77-a97b-4ec6-9ea1-54a2065b8c74",
            "notificationUrl": "https://org13abfcc3.crm4.dynamics.com/api/data/V9.0/PostRuntimeIntegrationExternalEvent",
            "lastModifiedDateTime": "2023-04-17T11:52:59Z",
            "clientState": "",
            "subscriptionType": "Dataverse",
            "systemCreatedAt": "2023-04-17T11:52:59.38Z",
            "systemCreatedBy": "89f9aa77-a97b-4ec6-9ea1-54a2065b8c74",
            "systemModifiedAt": "2023-04-17T11:52:59.38Z",
            "systemModifiedBy": "89f9aa77-a97b-4ec6-9ea1-54a2065b8c74"
        },
        {
            "@odata.etag": "W/\"JzE5OzE1NDYxMjMzMTk2NTk2MzExMjkxOzAwOyc=\"",
            "id": "e95073fb-8f6a-450b-89bf-7dd4e480a368",
            "timestamp": 106667,
            "appId": "cd388a4b-3b9e-4756-aea6-d17f72d6e970",
            "eventName": "TransferOrderPostedShipment",
            "companyName": "",
            "userId": "89f9aa77-a97b-4ec6-9ea1-54a2065b8c74",
            "notificationUrl": "https://org13abfcc3.crm4.dynamics.com/api/data/V9.0/PostRuntimeIntegrationExternalEvent",
            "lastModifiedDateTime": "2023-04-17T19:23:44Z",
            "clientState": "",
            "subscriptionType": "Dataverse",
            "systemCreatedAt": "2023-04-17T19:23:44.537Z",
            "systemCreatedBy": "89f9aa77-a97b-4ec6-9ea1-54a2065b8c74",
            "systemModifiedAt": "2023-04-17T19:23:44.537Z",
            "systemModifiedBy": "89f9aa77-a97b-4ec6-9ea1-54a2065b8c74"
        },
        {
            "@odata.etag": "W/\"JzE5Ozk1MjUxOTM2OTA4NDcyNzE4OTMxOzAwOyc=\"",
            "id": "09113fe9-3865-4dbd-a7f3-f779ee5daea9",
            "timestamp": 107543,
            "appId": "cd388a4b-3b9e-4756-aea6-d17f72d6e970",
            "eventName": "TransferOrderPostedShipment",
            "companyName": "CRONUS NL",
            "userId": "d09dc8df-efb3-4fd4-b6fb-6dc42319950c",
            "notificationUrl": "https://prod-94.westeurope.logic.azure.com:443/workflows/0d6f47c012ec492ab809de453d23cdc3/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=Ry_7MyF9C2AJswEDqZk8pHmK-QspBFGTzdDZJaYAahU",
            "lastModifiedDateTime": "2023-04-18T08:56:42Z",
            "clientState": "",
            "subscriptionType": "Webhook",
            "systemCreatedAt": "2023-04-18T08:56:42.6Z",
            "systemCreatedBy": "d09dc8df-efb3-4fd4-b6fb-6dc42319950c",
            "systemModifiedAt": "2023-04-18T08:56:42.6Z",
            "systemModifiedBy": "d09dc8df-efb3-4fd4-b6fb-6dc42319950c"
        }
    ]
}

You can see that the Dataverse subscription is “automatically” created and it has a different subscriptionType.

When you receive the webhook the body is as follow:

{
  "value": [
    {
      "appId": "cd388a4b-3b9e-4756-aea6-d17f72d6e970",
      "eventName": "TransferOrderPostedShipment",
      "timestamp": "2023-04-18T08:59:18.5700000Z",
      "initiatingUserAADObjectId": "2773155c-d08c-4739-b7f0-1ceef2d7e49f",
      "companyName": "CRONUS NL",
      "payload": {
        "TrasferOrderId": "55ee654a-c7dd-ed11-a7c7-000d3a64eef5",
        "Url": "https://api.businesscentral.dynamics.com/v2.0/f347666b-e7a9-4dbb-a45f-7836c70d3024/Test/api/v2.0/companies(A1B2E55E-F9C9-ED11-94CA-6045BD8E515F)/transferShipmentHeader(2301EA86-F9C9-ED11-94CA-6045BD8E515F)",
        "CompanyId": "a1b2e55e-f9c9-ed11-94ca-6045bd8e515f",
        "DirectTransfer": false
      },
      "clientState": ""
    }
  ]
}

Dataverse

As shown above you can also interact with Dataverse on your Business Events.
Before that you have to install the Virtual Table for Business Central App in your Dataverse and refresh the Business Event from the “Business Central Virtual Data Source Configuration” table:

When you have done that you can use the trigger “When an action is performed”:

And with the values you can get the record from Business Central and do many more things.

Note:
The users that can fire the Business Event must have the “EXT. EVENTS – SUBSCR” Permission Set assigned!

2 COMMENTS

Leave a Reply

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.