AL, Business Central, Codeunit, Dataverse, Events, Extension, Integration, Procedures, Subscription, VS Code

Create Integration Codeunit for Business Central & Dataverse Integration

This is the Seventh post in the series. If you want to go to previous post click here.

From the series of steps this post is dedicated to Step-6:

As a Sixth Step we will Create Integration Codeunit in Business Central

codeunit 50120 CDSDataverseEvent

Add these Procedures

local procedure LookupCDSProspect(SavedCRMId: Guid; var CRMId: Guid; IntTableFilter: Text): Boolean
        CDSProspect: Record "CDS cr95d_Prospects";
        OriginalCDSProspect: Record "CDS cr95d_Prospects";
        OriginalCDSProspectList: Page "CDS Prospect List";
        if not IsNullGuid(CRMId) then begin
            if CDSProspect.Get(CRMId) then
            if not IsNullGuid(SavedCRMId) then
                if OriginalCDSProspect.Get(SavedCRMId) then

        if OriginalCDSProspectList.RunModal = ACTION::LookupOK then begin
            CRMId := CDSProspect.cr95d_ProspectsId;

local procedure AddEntityTableMapping(CRMEntityTypeName: Text; TableID: Integer; var TempNameValueBuffer: Record "Name/Value Buffer" temporary)
        TempNameValueBuffer.ID := TempNameValueBuffer.Count + 1;
        TempNameValueBuffer.Name := CopyStr(CRMEntityTypeName, 1, MaxStrLen(TempNameValueBuffer.Name));
        TempNameValueBuffer.Value := Format(TableID);

local procedure InsertIntegrationTableMapping(var IntegrationTableMapping: Record "Integration Table Mapping"; MappingName: Code[20]; TableNo: Integer; IntegrationTableNo: Integer; IntegrationTableUIDFieldNo: Integer; IntegrationTableModifiedFieldNo: Integer; TableConfigTemplateCode: Code[10]; IntegrationTableConfigTemplateCode: Code[10]; SynchOnlyCoupledRecords: Boolean)
        IntegrationTableMapping.CreateRecord(MappingName, TableNo, IntegrationTableNo, IntegrationTableUIDFieldNo, IntegrationTableModifiedFieldNo, TableConfigTemplateCode, IntegrationTableConfigTemplateCode, SynchOnlyCoupledRecords, IntegrationTableMapping.Direction::Bidirectional, 'CDS');

procedure InsertIntegrationFieldMapping(IntegrationTableMappingName: Code[20]; TableFieldNo: Integer; IntegrationTableFieldNo: Integer; SynchDirection: Option; ConstValue: Text; ValidateField: Boolean; ValidateIntegrationTableField: Boolean)
        IntegrationFieldMapping: Record "Integration Field Mapping";
        IntegrationFieldMapping.CreateRecord(IntegrationTableMappingName, TableFieldNo, IntegrationTableFieldNo, SynchDirection,
            ConstValue, ValidateField, ValidateIntegrationTableField);

To Add Event Subscriptions, Use the new Shift+Alt+E shortcut in the AL code editor to invoke a list of all events.

Search for Event OnGetCDSTableNo“CRM Setup Defaults”

[EventSubscriber(ObjectType::Codeunit, Codeunit::"CRM Setup Defaults", 'OnGetCDSTableNo', '', false, false)]

local procedure OnGetCDSTableNo(BCTableNo: Integer; var CDSTableNo: Integer; var handled: Boolean);
        if BCTableNo = DATABASE::"Prospect" then begin
            CDSTableNo := DATABASE::"CDS cr95d_Prospects";
            handled := true;

Search for Event OnLookupCRMTables“Lookup CRM Tables”

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Lookup CRM Tables", 'OnLookupCRMTables', '', false, false)]

local procedure OnLookupCRMTables(CRMTableID: Integer; NAVTableId: Integer; SavedCRMId: Guid; var CRMId: Guid; IntTableFilter: Text; var Handled: Boolean);
        if CRMTableID = Database::"CDS cr95d_Prospects" then
            Handled := LookupCDSProspect(SavedCRMId, CRMId, IntTableFilter);

Search for Event OnAddEntityTableMapping“CRM Setup Defaults”

[EventSubscriber(ObjectType::Codeunit, Codeunit::"CRM Setup Defaults", 'OnAddEntityTableMapping', '', false, false)]

local procedure OnAddEntityTableMapping(var TempNameValueBuffer: Record "Name/Value Buffer");
        AddEntityTableMapping('Prospect', DATABASE::"CDS cr95d_Prospects", TempNameValueBuffer);

Search for Event OnAfterResetConfiguration “CDS Setup Defaults”

[EventSubscriber(ObjectType::Codeunit, Codeunit::"CDS Setup Defaults", 'OnAfterResetConfiguration', '', false, false)]

local procedure OnAfterResetConfiguration(CDSConnectionSetup: Record "CDS Connection Setup");
        IntegrationTableMapping: Record "Integration Table Mapping";
        IntegrationFieldMapping: Record "Integration Field Mapping";
        CDSProspect: Record "CDS cr95d_Prospects";
        Prospect: Record "Prospect";
            IntegrationTableMapping, 'Prospect',
            DATABASE::"Prospect", DATABASE::"CDS cr95d_Prospects", CDSProspect.FieldNo(cr95d_ProspectsId), CDSProspect.FieldNo(ModifiedOn), '', '', true);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo("No."), CDSProspect.FieldNo(cr95d_ProspectsId), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo(Name), CDSProspect.FieldNo(cr95d_ProspectName), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo(Probability), CDSProspect.FieldNo(cr95d_Probability), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo("Contract Amount"), CDSProspect.FieldNo(cr95d_ContractAmount), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo("Contract Amount (Base)"), CDSProspect.FieldNo(cr95d_contractamount_Base), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo(Stage), CDSProspect.FieldNo(cr95d_Stage), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo("Forecast Revenue"), CDSProspect.FieldNo(cr95d_ForcastedRevenue), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

        InsertIntegrationFieldMapping('Prospect', Prospect.FieldNo("Forecast Revenue (Base)"), CDSProspect.FieldNo(cr95d_forcastedrevenue_Base), IntegrationFieldMapping.Direction::Bidirectional, '', true, false);

Now you are good to proceed with Next Step.

You can jump to Next Step from here.

AL, BC18, Business Central, Code, Codeunit, Combinations, Development Tips, Dimension, Dynamics 365, Environment, Events, Extension, Extension Package, How To, Online, Page, Sandbox, Subscription, Table, Tip & Tricks, VS Code

Walkthrough Extension Development in Online Sandbox – Business Central

I have my Sandbox environment as below:

Details of the Sandbox as below:

Connecting VS code to above environment.

When you try to Publish the extension, it will ask you to authenticate.

Copy the Link and open in the browser and paste the code in the box as shown below.

Next it will ask for your Online Instance User Id & Password provide it and on confirmation close the page. It will start deploying the extension.

In this walkthrough I am using below scenario:

Requirement is we need to be able to define some code for dimension combinations. Let me say it will be Sales Code, you can choose name of your choice, this is not Salesperson code.

I am assuming these dimensions will follow the sequence as defined on my General Ledger Setup as below:

On Sales Order & Invoice user should be able to select this Sales Code and dimensions should be populated on order accordingly.

For tracking purpose this Sales Code should flow to Posted Sales Invoice and Customer Ledger.

So, Let’s Start with the development process:

Step-1 We will Create the Table

Here is the code for LookupDimValue Function, it will set filter for Dimension Code on Dimension Value table, as per the Dimension No passed. (1 is for Shortcut Dimension defined on General Ledger Setup, similarly for other 8 dimensions)

Step-2 Next, we will create the Page for this Setup

Step-3 Next, we will add the Sales Code field to all required Tables & Pages

Here is the code for AddDim Function. It is assumed that only combination provided in Sales Code Setup will be used. If you have defined Default dimensions or Combinations, those need to be preserved else this code will overwrite them. You will have to find the Data Set Entry, store them in temporary table used in below code and then add all the dimensions from the setup.

When you Select Sales Code on the Order or Invoice it will populate all the dimensions defined in the Setup.

Code for other Tables & Pages

Step-4 Next, we need to take care to flow the Sales Code to the Ledger and Posted documents.

For posted documents we need not to worry it will flow automatically provided we have defined the fields on same Id.

However, for ledger we will require to use Events to pass the data to their destinations. In this case we are only passing to Customer Ledger Entry. For this we will create a Codeunit.

To Add Event Subscriptions, Use the new Shift+Alt+E shortcut in the AL code editor to invoke a list of all events.

Search for the even you are looking for.

When pressing Enter to select an event entry, an event subscriber for the event will be inserted at the cursor position in the active AL code editor window.

Here is the Codeunit Code:

This is not the final code; much more can be done or need to be done before it can be delivered to customer for their use. Purpose of this walkthrough was to demo the way we can customize the solution using extensions and publishing to Online tenant in Sandbox.

Hope you enjoyed the information. Will come with more similar information in my next posts. Till then keep exploring, learning and sharing your knowledge with others.

Remain safe, take care of your loved ones, put your mask, maintain safe distance and don’t forget to get vaccinated.

AL, Business Central, C/AL, Development Tips, Events, Extension Package, How To, Information, Modern Development Tool, Publisher, Raise, Subscriber, Tip & Tricks, V2, Visual Studio Code

Event driven Programming – Business Central

You can use events to design your application. Below are the benefits of using this model.

  1. You can lower the cost of code modifications and upgrades.
  2. You can customize functionality without modifying the original application.
  3. Your program will react to specific actions or behaviours of original application.


The following table describes all the different event types:

Event types Description
BusinessEvent Specifies the method to be business type event publisher.
IntegrationEvent Specifies the method to be integration type event publisher.
Global Global events are predefined system events.
Trigger Trigger events are published by the runtime.

You program events in the application to run customized behaviour when they occur.


What are Events?

A thing that happens. Event is declared by an AL method, which is referred to as event publisher function. Publisher method have only signature only and does not execute any code.

Publisher is the object that contains event publisher methods that declares the event. It serves as hook-up point in application, where subscribers use these points to extend the functionality, without even making any changes to the base application.

Only publishing an event do nothing in application, these events must be raised for subscribers to respond.

Especially Business and Integration type events must be published and raised, you need to create event publisher functions and add them to the objects manually.

Trigger events which occurs on Table & Page operations, are automatically published and raised by system at runtime, so no coding is required to publish them.

Subscriber is an AL method that subscribes to even publisher method, and logic to handle the event is implement.


Creating an event publisher method to publish business and integration events

Creating event publisher method is similar to other methods you define in AL. In addition some specific properties and few restrictions.

  • Can not include any code except comments.
  • You cannot define return values, variables or text constants.

You can define event publisher in any objects new or in existing objects and of any type of objects like codeunit, page or table.

If you define even as local then it will not be available for subscribers.


local procedure MyProcedure()




local procedure MyProcedure()



Feel free to add as much of parameters and of any type as required. However it is advised not to include unnecessary parameters to Business events.

Raising Events

You need to modify the application to raise the event where ever it is needed. You call the event publisher method, same way you call any other methods in AL.

When the execution hits the evet publisher method, all event subscriber method that subscribe to the event are executed. Limitation will be you can not specify the order in which subscriber method will run, subscribers will be picked one at a time and in random order.


Subscriber Method

You can create new or use existing codeunits to define subscriber methods.

[EventSubscriber(ObjectType::Codeunit, Codeunit::, ‘OnSomeEvent’, ‘ElementName’, SkipOnMissingLicense, SkipOnMissingPermission)]

local procedure MyProcedure()



Add code to the method for handling the event.

Don’t worry at this point we will go through complete process programmatically in our next upcoming posts.

How to find which event to subscribe, and where to write our code.

Similar to earlier days we used Code Coverage, same way we have Event Recorder in Business Central.

Search for Event Recorder in RTC or alternatively you can launch from VS Code from Command Palate AL: Open Events Recorder.

Let’s look at a small example of finding Events.

I want to know what all events hit or available to subscribe when Sales-Order is Re-Opened.

Step-1: Open the Event Recorder and Click on Start.


Step-2: Perform Sales-Order -> Reopen

Step-3: Click on Stop.


Step-4: Scan from the list of events that you find suitable, to know how to subscribe to that event you can find AL Code. (Get AL Snippet)


All the recorded events display in the order they were called. The Event Recorder page provides information on the events that were raised including the details whether the raised events were trigger events or custom events. The custom events are either Business Events or Integration Events.

You can identify the Event types, additionally, you can discover which object types and methods raised the events with the details like calling methods, object types, and object names.

Readiness to Event:

  • Redesign your on prem to an event-based approach in C/AL.
  • This will prove to be best preparation for moving to VS Code AL extension.
  • You will be able to find any issues in your code that you need to refactor.
  • Next step will be to Lift your on prem product to VS Code AL extension.
  • Then extension can be published as a MSDY365 Business Central app.

Now you know about basics of Events. Understand Publisher, Raising Events and Subscribing to Events. Also how to trace and find suitable events to subscribe for your customization over base application.

Before we end the post let’s have a Recap to Events


  • Announcement by the application
  • Function without code
  • Exposes the event to the outside


  • Specifies exactly when the event happens
  • Call to the Publisher Function


  • React to the event
  • Must be in codeunit, tableextension or pageextension.

Note: Raise order specified in code, Subscribe order undefined.

We will look into practical approach in our next post.


AL, Assisted Setup, Business Central, Development Tips, Extension Package, How To, Information, Tip & Tricks, V2, Visual Studio Code

Creating Assisted Setup in AL for Business Central

Today we will see how we can add our Wizard Page to Assisted Setup.

Continuing from previous post this is the second part as both are inter related.

If you have not gone through earlier post see here how to create Wizard Page for Business Central. Creating a Wizard Page in AL for Business Central

Let’s have some overview why we need assisted setup:

What is Assisted Setup?

  • List of setup scenarios, presented in list form.
  • Uses Wizard Page with relevant options.
  • Display Status of activity, completed or incomplete.
  • Predefined set of setup scenarios. Like E-Mail Setup, Cash Flow Forecast, Approval Workflows, Connection to other entities like CRM connection etc.
  • Possible to add new custom setup scenarios.


Today we will see how we can add our Wizard Page for Company Information Setup into this List, and how the Status is updated in this list.

So further not going into theory let’s jump to practical Approach.

To add a New Assisted Setup:

  • 2 tables are involved Assisted Setup & Aggregated Assisted Setup.
  • Need to Subscribe to Event OnRegisteredAssistedSetup and call AddExtensionAssistedSetup.
  • Wizard should update the status in the Assisted Setup table.
  • Subscribe to Event OnUpdateAssistedSetupStatus to store the updated status.
  • Alternatively can determine actual status based on data.


Create New Codeunit on available ID and unique name.

Code will be similar to below:


Save the file and publish your Extension.

Now you should be able to locate your Wizard as entry ‘Setup Company Information’


Click to Run your Company Information Setup Wizard.


Complete your Setup by entering information and Click Finish.


If you have completed all the step and provided information, your Status should show Completed.

You may need to re-open the Assisted Setup page.

It was just to give you an idea how we can get this done.

You will require to perform other some more steps to get it functioning smooth.

Sometime later will come up with more details.

Explore the existing Setups and can get more insight on the same.

I have given you the start explore learn and reach to conclusion.

See you again with some other topic in my upcoming post. Till then keep exploring and keep learning.

Corfu Navision 2016, Development Tips, Events, How To

Implementing Events in Navision 2016

Recall from my earlier post for description of Events – Events in C/AL Navision 2016

Here in this post we will see how to implement the same.

This could be helpful in situation where throughout the system you wish to have similar behaviour on specific condition. In general we will have to write code in every object with same set of logic, variables, and functions and so on.

Although still in this case too we will require to add code to every object, but only one time.

Think of you need to enhance or make changes in behaviour again you will have to go through each objects and do the required change. But using this feature we can keep it centralized and manage from single place, without going through each objects again.

Here below I am taking example one explained by Microsoft help basic concept, will come up with my version with some more effective usage later sometime.

When users change the address of a customer, you want to check that the address does not include invalid characters, which in this walkthrough is a plus sign (+).

To accomplish this, you will publish an event that is raised when the Address field on page 21 Customer Card is changed.

To handle the event when it is raised, you will add an event subscriber function that includes logic that checks the address value and returns a message to the user if it contains a plus sign.

Publishing the Event

To publish an event, you create a C/AL function that is set up to be an event publisher. An event publisher function can be added in any object, such as a codeunit, page, or table.

In this procedure we will add the event publisher function to a new codeunit, in which you can potentially add more event publisher functions for other events later.

Because you might want to change this event implementation in the future, you decide to create an integration event type.

The event publisher requires a single text parameter for handling the address of the customer.

Create a new codeunit

The codeunit for the event publisher I have created 50000 – Event Publisher.

Now we will add the event publisher function to publish the event.

To create the event publisher function to publisher the event

  • Define a function OnAddressLineChanged.
  • Open the properties for the OnAddressLineChanged function, select the function, and then in the View menu, choose Properties. Set the properties as follows:
  • Set the Local property to No.

Setting this property makes the function available to be called from the other objects.

  • Set the Event property to Publisher. This makes the function an event publisher.
  • Set the EventType property to Integration.
  • Close the Properties


  • Add a local parameter to the function for the address of the customer as described in the following steps:
  • On the Functions tab, select the OnAddressLineChanged function, and then choose the Locals

The C/AL Locals window opens.

  • On the Parameters tab, in the Name field, enter line.
  • Set the DataType field to Text.
  • Set the Length field to 100.



An event publisher function cannot have a return value, variables, or text constants; otherwise you will not be able to compile the function.

The new function appears in the C/AL Editor with the following signature:

[IntegrationEvent] OnAddressLineChanged(line : Text[100])

You can now raise the event in the application.

Raising the Event

After creating the event publisher function to publish the event, now we will add code to the application to raise the event where it is required.

In this case, the event will raise when the Address field is changed on the page 21 Customer Card.

Therefore, we will add code to the Address – OnValidate() trigger in C/AL code of the page. Raising an event basically involves calling the event publisher function that publishes the event.

To raise the event

  • In the development environment, open page 21 Customer Card as follows:
  • Add a C/AL variable that specifies the object that publishes the event. In this case, the event publisher object is codeunit 50000 Event Publisher, which contains the event publisher function OnAddressLineChanged that you created in the previous procedure.


  • In C/AL code, add the following code on the Address – OnValidate() trigger to raise the event:


This calls the event publisher function to raise the event.

  • Save and compile the changes to the page.

The event can now be subscribed to and handled.

Subscribing to and Handling an Event

Once an event has been published you can add code to the application that subscribes to and handles the event when it is raised.

For example, in this case when a user changes the address of a customer (the event), you want code that checks that the value does not contain a plus sign.

Subscribing to and handling an event is accomplished by creating a C/AL function that is set up as an event subscriber and subscribes to a specific event (defined by an event publisher function). The event subscription function contains the application logic for handling the raised event.

In this case, we will create an event subscriber function that subscribes to the OnAddressLineChanged function in codeunit 50000 Event Publisher.

Unlike an event publisher function, an event subscriber function can only reside in a codeunit object. This procedure will add the event subscriber function to a new codeunit, in which you can potentially add more event subscriber functions for other events later.

To create a new codeunit

  • In the development environment, create a new codeunit that has the ID 50001 and the name Event Subscriber.

To create the event subscriber function to subscribe to and handle the event

  • Create Function CheckAddressLine.


  • Choose Properties. Set the properties as follows:
  • Set the Event property to Subscriber to make the function an event subscriber.
  • Set the EventPublisherObject property to Codeunit My Publishers.

This is the codeunit that contains the event publisher function (OnAddressLineChanged) that you want to subscribe to.

  • In the EventFunction property, select the OnAddressLineChanged integration event.

This field reads all the published events in the event publisher object.


When you get a message that asks whether you want to overwrite the edited function’s signature, choose Yes to continue.

  • A local parameter that has the name line and the data type Text has been automatically added to the new CheckAddressLine
  • The new function appears in the C/AL Editor with the following signature:

LOCAL [EventSubscriber] CheckAddressLine(line : Text[100])

You can now add code to handle the event.

  • To handle the event, add the following code to the CheckAddressLine function in the C/AL editor:

IF (STRPOS(line, ‘+’) > 0) THEN BEGIN

ERROR(‘Cannot use a plus sign (+) in the address [‘ + line + ‘]’);


This code checks the value of the Address field on page 21 Customer Card when is has been changed and returns a message if the value contains a plus sign.

Viewing the New Event Subscription

After you create an event subscriber, you can view information about it in page 9510 Event Subscriptions. This page provides information about all the current event subscriptions in the application. You can open this page directly from the development environment or from a Microsoft Dynamics NAV client.

To view the event subscription from the development environment

  • On the Tools menu, choose Debugger, and then choose Event Subscriptions.


To view the event subscription from a Microsoft Dynamics NAV client

  • Start the Microsoft Dynamics NAV client.
  • In the Search box, enter Sessions, and then choose the related link.


The Even Subscription Window will look like:

Testing the Event

To test the event implementation, you can run page 21 Customer Card from the development environment.

  • In the Address field, add a plus sign, and then choose the OK

The following message appears:

Cannot use a plus sign (+) in the address [].

[] contains the value of the Address field.

I will come up with more details on this topic later in my future posts.

Corfu Navision 2016, Events, Information

Events in C/AL Navision 2016

By implementing events in C/AL code, you can design applications to react to specific actions or behaviour that occur.

Events enable you to separate customized functionality from the application business logic.

By using events in the application where customizations are typically made, you can lower the cost of code modifications and upgrades to the original application.

Events can be used for different purposes, such as generating notifications when certain behaviour occurs or the state of an entity changes, distributing information, and integrating with external systems and applications.

How Events Work

There are three major participants involved in events: the event, a publisher and a subscriber.

An event is the declaration of the occurrence or change in the application. An event is declared by a C/AL function, which is referred to as an event publisher function. An event publisher function is comprised of a signature only and does not execute any code.

A publisher is the object that contains event publisher function that declares the event. The publisher exposes an event in the application to subscribers, essentially providing them with a hook-up point in the application. An event is raised by adding logic to the application that calls into the publisher to invoke the event (the event publisher function). There are three different event types: business, integration, and trigger events.

Business and integration type events must be explicitly declared and published, which means that you must create event publisher functions and add them to objects manually. On the other hand, trigger events, which occur on table and page operations, are published and raised implicitly by the Microsoft Dynamics NAV runtime. Therefore, no coding is required to publish them.

A subscriber listens for and handles a published event. A subscriber is a C/AL function that subscribes to a specific event publisher function and includes the logic for handling the event. When an event is raised, the subscriber function is a called and it code is run.

How to Implement Events

Implementing events in Microsoft Dynamics NAV consists of the following tasks:

  1. Publish the event. For business and integration events, create and configure a function in an application object to be an event publisher function.
  2. Raise the event. Add code that calls the event publisher function..
  3. Subscribe to the event. At the consumer end, add one or more subscriber functions that subscribe to published events when they are raised.

Will come up with how to practically use it in my upcoming posts.