Share This Post

Business Central Advanced Sample Extension

It is necessary to submit the tests with your extension in order to pass validation. This article builds an Business Central Advanced Sample Extension which is used as the foundation for writing a test.

This article will guide you through all the steps that you must go through to create the sample extension in AL. The end result can be published, installed and tested on the tenants. After you have built your extension, you must write the test for it.

This article illustrates the following tasks:

  • Developing a sample extension that uses code units, tables, card pages, list pages, navigate page (Assisted Setup) actions and events, and includes tool tips and links to context-sensitive Help.
  • Creating extension objects that can be used to modify page and table objects.
  • Initializing the database during the installation of the Business Central Advanced Sample Extension.
  • Developing a sample test that tests external calls to a service, events, permissions, actions, navigate page (Assisted Setup), and other modified pages.
  • Running the sample test using the Test Tool.

Business Central Advanced Sample Extension Prerequisites

To complete this, you will need:

  • The Dynamics 365 Business Central tenant.
  • Visual Studio Code.
  • The AL Language extension for Visual Studio Code.

D365 Business Central Advanced Sample Extension Overview

1. Customer Rewards Extension Overview

This sample extension enables the ability to set up any number of reward levels and the minimum number of rewards points required to attain that level. When the sample extension is installed, customers begin to follow one reward point per sales order.

When no reward levels are set up, the customers reward level is set to NONE even though the customer may possess reward points. To begin using the sample extension, the user must accept the extension terms and activate the extension by entering a valid activation code using the Customer Rewards Assisted Setup Wizard.

2. Developing the Sample Customer Rewards Extension

Following all the steps of this walkthrough allows you to publish the Business Central Advanced Sample Extension on your tenant and create a possible new feature for your customers. In the following section, you will be adding the objects that are needed for the Customer Rewards extension.

Customer Rewards Table Objects

First, we will get started with the table objects that store the data.

Reward Level table object

The following code appends a new table 50100 Reward Level for storing reward level information set up by the user. The table consists of two fields: Level and Minimum Reward Points.

table 50100 “Reward Level”
{
fields
{
field(1; Level; Text[20]) { }

field(2; “Minimum Reward Points”; Integer)
{
MinValue = 0;
NotBlank = true;

trigger OnValidate();
var
tempPoints: Integer;
RewardLevel: Record “Reward Level”;
begin
tempPoints := “Minimum Reward Points”;
RewardLevel.SetRange(“Minimum Reward Points”, tempPoints);
if RewardLevel.FindFirst then
Error(‘Minimum Reward Points must be unique’);
end;
}
}

keys
{
key(PK; Level)
{
Clustered = true;
}
key(“Minimum Reward Points”; “Minimum Reward Points”) { }
}

trigger OnInsert();
begin

Validate(“Minimum Reward Points”);
end;

trigger OnModify();
begin
Validate(“Minimum Reward Points”);
end;
}

Activation Code Information table object

Following code adds a new table 50101 Activation Code Information for storing the activation information for the Business Central Advanced Sample Extension. The table consists of three fields: Activation CodeDate Activated, and Expiration Date.

table 50101 “Activation Code Information”
{
fields
{
field(1; ActivationCode; Text[14])
{
Description = ‘Activation code used to activate Customer Rewards’;
}

field(2; “Date Activated”; Date)
{
Description = ‘Date Customer Rewards was activated’;
}

field(3; “Expiration Date”; Date)
{
Description = ‘Date Customer Rewards activation expires’;
}
}

keys
{
key(PK; ActivationCode)
{
Clustered = true;
}
}
}

Customer Rewards Mgt. Setup table object

The following code adds a new table 50102 Customer Rewards Mgt. Setup for storing information about the codeunit that should be used to handle events in the Business Central Advanced Sample Extension. This enables us to mock events in our sample test. The table contains two fields: Primary Key and Customer Rewards Ext. Mgt. Codeunit ID.

table 50102 “Customer Rewards Mgt. Setup”
{
fields
{
field(1; “Primary Key”; Code[10])
{
}

field(2; “Customer Rewards Ext. Mgt. Codeunit ID”; Integer)
{
TableRelation = “CodeUnit Metadata”.ID;
}
}

keys
{
key(PK; “Primary Key”)
{
Clustered = true;
}
}
}

Customer Rewards Table Extension Objects

Customer table extension object

The Customer table, like many other tables, is part of the Dynamics 365 Business Central service and it cannot be modified directly by developers.

To add additional fields or to alter the properties on this table, developers must create a new type of object; a table extension. The following code creates a table extension for the Customer table and adds the Reward Points field.

tableextension 50100 “CustomerTable Ext.” extends Customer
{
fields
{
field(10001; RewardPoints; Integer)
{
MinValue = 0;
}
}
}

Customer Rewards Page Objects

For each page object, you can describe the target Help page that describes the feature that the page object is part of. The ContextSensitiveHelpPage property on the page object works together with the link that is specified in the app.json file.

Customer Rewards Wizard page object

The following code adds the 50100 Customer Rewards Wizard page that enables the user to accept the terms for using the Business Central Advanced Sample Extension as well as activating the extension. The page consists of a welcome step, an activation step, and a finish step.

The welcome step has a checkbox for the Terms of Use that must be enabled. The activation step contains a text box where the activation code must be entered for validation. A valid activation code for this Business Central Advanced Sample Extension is any 14 character alphanumeric code.

page 50100 “Customer Rewards Wizard”
{
// Specifies that this page will be a navigate page.
PageType = NavigatePage;
Caption = ‘Customer Rewards assisted setup guide’;
ContextSensitiveHelpPage = ‘sales-rewards’;

layout
{
area(content)
{
group(MediaStandard)
{
Caption = ”;
Editable = false;
Visible = TopBannerVisible;

field(“MediaResourcesStandard.””Media Reference”””; MediaResourcesStandard.”Media Reference”)
{
ApplicationArea = All;
Editable = false;
ShowCaption = false;
}
}

group(FirstPage)
{
Caption = ”;
Visible = FirstPageVisible;

group(“Welcome”)
{
Caption = ‘Welcome’;
Visible = FirstPageVisible;

group(Introduction)
{
Caption = ”;
InstructionalText = ‘This Customer Rewards extension is a sample extension. It adds rewards tiers support for Customers.’;
Visible = FirstPageVisible;

field(Spacer1; ”)
{
ApplicationArea = All;
ShowCaption = false;
Editable = false;
MultiLine = true;
}
}

group(“Terms”)
{
Caption = ‘Terms of Use’;
Visible = FirstPageVisible;

group(Terms1)
{
Caption = ”;
InstructionalText = ‘By enabling the Customer Rewards extension…’;
Visible = FirstPageVisible;
}
}

group(Terms2)
{
Caption = ”;

field(EnableFeature; EnableCustomerRewards)
{
ApplicationArea = All;
MultiLine = true;
Editable = true;
Caption = ‘I understand and accept these terms.’;

trigger OnValidate();
begin
ShowFirstPage;
end;
}
}
}
}

group(SecondPage)
{
Caption = ”;
Visible = SecondPageVisible;

group(“Activation”)
{
Caption = ‘Activation’;
Visible = SecondPageVisible;

field(Spacer2; ”)
{
ApplicationArea = All;
ShowCaption = false;
Editable = false;
MultiLine = true;
}

group(ActivationMessage)
{
Caption = ”;
InstructionalText = ‘Enter your 14 digit activation code to continue’;
Visible = SecondPageVisible;

field(Activationcode; ActivationCode)
{
ApplicationArea = All;
ShowCaption = false;
Editable = true;
}
}
}
}

group(FinalPage)
{
Caption = ”;
Visible = FinalPageVisible;

group(“ActivationDone”)
{
Caption = ‘You”re done!’;
Visible = FinalPageVisible;

group(DoneMessage)
{
Caption = ”;
InstructionalText = ‘Click Finish to setup your rewards level and start using Customer Rewards.’;
Visible = FinalPageVisible;
}
}
}
}
}

actions
{
area(Processing)
{
action(ActionBack)
{
ApplicationArea = All;
Caption = ‘Back’;
Enabled = BackEnabled;
Visible = BackEnabled;
Image = PreviousRecord;
InFooterBar = true;

trigger OnAction();
begin
NextStep(true);
end;
}

action(ActionNext)
{
ApplicationArea = All;
Caption = ‘Next’;
Enabled = NextEnabled;
Visible = NextEnabled;
Image = NextRecord;
InFooterBar = true;

trigger OnAction();
begin
NextStep(false);
end;
}

action(ActionActivate)
{
ApplicationArea = All;
Caption = ‘Activate’;
Enabled = ActivateEnabled;
Visible = ActivateEnabled;
Image = NextRecord;
InFooterBar = true;

trigger OnAction();
var
CustomerRewardsExtMgt: Codeunit “Customer Rewards Ext. Mgt.”;
begin
if ActivationCode = ” then
Error(‘Activation code cannot be blank.’);

if Text.StrLen(ActivationCode) <> 14 then
Error(‘Activation code must have 14 digits.’);

if CustomerRewardsExtMgt.ActivateCustomerRewards(ActivationCode) then
NextStep(false)
else
Error(‘Activation failed. Please verify the activtion code you entered.’);
end;
}

action(ActionFinish)
{
ApplicationArea = All;
Caption = ‘Finish’;
Enabled = FinalPageVisible;
Image = Approve;
InFooterBar = true;

trigger OnAction();
begin
FinishAndEnableCustomerRewards
end;
}
}
}

trigger OnInit();
begin
LoadTopBanners;
end;

trigger OnOpenPage();
begin
Step := Step::First;
EnableControls;
end;

local procedure EnableControls();
begin
ResetControls;

case Step of
Step::First :
ShowFirstPage;

Step::Second :
ShowSecondPage;

Step::Finish :
ShowFinalPage;
END;
end;

local procedure NextStep(Backwards: Boolean);
begin
if Backwards then
Step := Step – 1
ELSE
Step := Step + 1;
EnableControls;
end;

local procedure FinishAndEnableCustomerRewards();
var
CustomerRewardsExtMgt: Codeunit “Customer Rewards Ext. Mgt.”;
begin
CurrPage.Close;
CustomerRewardsExtMgt.OpenRewardsLevelPage;
end;

local procedure ShowFirstPage();
begin
FirstPageVisible := true;
SecondPageVisible := false;
FinishEnabled := false;
BackEnabled := false;
ActivateEnabled := false;
NextEnabled := EnableCustomerRewards;
end;

local procedure ShowSecondPage();
begin
FirstPageVisible := false;
SecondPageVisible := true;
FinishEnabled := false;
BackEnabled := true;
NextEnabled := false;
ActivateEnabled := true;
end;

local procedure ShowFinalPage();
begin
FinalPageVisible := true;
BackEnabled := true;
NextEnabled := false;
ActivateEnabled := false;
end;

local procedure ResetControls();
begin
FinishEnabled := true;
BackEnabled := true;
NextEnabled := true;
ActivateEnabled := true;
FirstPageVisible := false;
SecondPageVisible := false;
FinalPageVisible := false;
end;

local procedure LoadTopBanners();
begin
if MediaRepositoryStandard.GET(‘AssistedSetup-NoText-400px.png’, FORMAT(CURRENTCLIENTTYPE))
then
if MediaResourcesStandard.GET(MediaRepositoryStandard.”Media Resources Ref”)
then
TopBannerVisible := MediaResourcesStandard.”Media Reference”.HASVALUE;
end;

var
MediaRepositoryStandard: Record 9400;
MediaResourcesStandard: Record 2000000182;
Step: Option First, Second, Finish;
ActivationCode: Text;
TopBannerVisible: Boolean;
FirstPageVisible: Boolean;
SecondPageVisible: Boolean;
FinalPageVisible: Boolean;
FinishEnabled: Boolean;
BackEnabled: Boolean;
NextEnabled: Boolean;
ActivateEnabled: Boolean;
EnableCustomerRewards: Boolean;
}

Rewards Level List page object

The following code adds the 50101 Rewards Level List page that enables the user to view, edit, or add new reward levels and their corresponding minimum required points. The code example includes tool tips for controls and a relative link to context-sensitive Help.

page 50101 “Rewards Level List”
{
PageType = List;
ContextSensitiveHelpPage = ‘sales-rewards’;
SourceTable = “Reward Level”;
SourceTableView = sorting (“Minimum Reward Points”) order(ascending);

layout
{
area(content)
{
repeater(Group)
{
field(Level; Level)
{
ApplicationArea = All;
Tooltip = ‘Specifies the level of reward that the customer has at this point.’;
}

field(“Minimum Reward Points”; “Minimum Reward Points”)
{
ApplicationArea = All;
Tooltip = ‘Specifies the number of points that customers must have to reach this level.’;
}
}
}
}

trigger OnOpenPage();
begin

if(not CustomerRewardsExtMgt.IsCustomerRewardsActivated) then
Error(NotActivatedTxt);
end;

var
CustomerRewardsExtMgt: Codeunit “Customer Rewards Ext. Mgt.”;
NotActivatedTxt: Label ‘Customer Rewards is not activated’;
}

We define an event publisher method OnGetActivationCodeStatusFromServer that accepts the activation code entered by the user as a parameter, and subscriber method OnGetActivationCodeStatusFromServerSubscriber to listen for and handle the event. When the ActivateCustomerRewards procedure is run, the OnGetActivationCodeStatusFromServer event is raised.

Because the EventSubscriberInstance property for the codeunit is set to Static-Automatic by default, the OnGetActivationCodeStatusFromServerSubscriber procedure is called. In this procedure, we handle the raised event by first checking if the current codeunit has been defined for handling this event.

If the codeunit can handle the event, the GetHttpResponse helper procedure is called to validate the activation code. Depending on the response, Customer Rewards is activated or not.

By using events when the Business Central Advanced Sample Extension makes external calls to a service, we are able to mock the behavior of what occurs when events are raised. This becomes particularly useful when writing tests for the extension.

At this point, the Customer Rewards sample extension can be published and installed on your sandbox. For more information on Microsoft Dynamics 365 Business Central Advanced Sample Extension, please contact us.

Share This Post

Leave a Reply

avatar
  Subscribe  
Notify of
Skip to toolbar