Create the Customer Rewards Extension Management codeunit object
The Customer Rewards Extension Management codeunit encapsulates most of the logic and functionality that is required for the Customer Rewards extension. This codeunit contains examples of how you can use events to react to specific actions or behavior that occurs within your extension.
This extension includes a requirement to make a call to an external service or API to validate activation codes that are entered by the user. Typically, you would complete this verification by defining procedures that take in the activation code and then make calls to the API. Instead of using that approach, you can use events in AL.
To create a new codeunit in your project, create a new file with the name CustomerRewardsExtMgt.Codeunit.al.
Next, add the following code to the codeunit:
codeunit 50101 "Customer Rewards Ext. Mgt"
{
EventSubscriberInstance = StaticAutomatic;
// Determines if the extension is activated
procedure IsCustomerRewardsActivated(): Boolean;
var
ActivationCodeInformation: Record "Activation Code Information";
begin
if not ActivationCodeInformation.FindFirst() then
exit(false);
if (ActivationCodeInformation."Date Activated" <= Today) and (Today <= ActivationCodeInformation."Expiration Date") then
exit(true);
exit(false);
end;
// Opens the Customer Rewards Assisted Setup Guide
procedure OpenCustomerRewardsWizard();
var
CustomerRewardsWizard: Page "Customer Rewards Wizard";
begin
CustomerRewardsWizard.RunModal();
end;
// Opens the Reward Level page
procedure OpenRewardsLevelPage();
var
RewardsLevelList: Page "Rewards Level List";
begin
RewardsLevelList.Run();
end;
// Determines the corresponding reward level and returns it
procedure GetRewardLevel(RewardPoints: Integer) RewardLevelTxt: Text;
var
RewardLevel: Record "Reward Level";
MinRewardLevelPoints: Integer;
begin
RewardLevelTxt := NoRewardlevelTxt;
if RewardLevel.IsEmpty() then
exit;
RewardLevel.SetRange("Minimum Reward Points", 0, RewardPoints);
RewardLevel.SetCurrentKey("Minimum Reward Points"); // sorted in ascending order
if not RewardLevel.FindFirst() then
exit;
MinRewardLevelPoints := RewardLevel."Minimum Reward Points";
if RewardPoints >= MinRewardLevelPoints then begin
RewardLevel.Reset();
RewardLevel.SetRange("Minimum Reward Points", MinRewardLevelPoints, RewardPoints);
RewardLevel.SetCurrentKey("Minimum Reward Points"); // sorted in ascending order
RewardLevel.FindLast();
RewardLevelTxt := RewardLevel.Level;
end;
end;
// Activates Customer Rewards if activation code is validated successfully
procedure ActivateCustomerRewards(ActivationCode: Text): Boolean;
var
ActivationCodeInformation: Record "Activation Code Information";
begin
// raise event
OnGetActivationCodeStatusFromServer(ActivationCode);
exit(ActivationCodeInformation.Get(ActivationCode));
end;
// publishes event
[IntegrationEvent(false, false)]
procedure OnGetActivationCodeStatusFromServer(ActivationCode: Text);
begin
end;
// Subscribes to OnGetActivationCodeStatusFromServer event and handles it when the event is raised
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Rewards Ext Mgt", 'OnGetActivationCodeStatusFromServer', '', false, false)]
local procedure OnGetActivationCodeStatusFromServerSubscriber(ActivationCode: Text);
var
ActivationCodeInfo: Record "Activation Code Information";
ResponseText: Text;
Result: JsonToken;
JsonRepsonse: JsonToken;
begin
if not CanHandle() then
exit; // use the mock
// Get response from external service and update activation code information if successful
if (GetHttpResponse(ActivationCode, ResponseText)) then begin
JsonRepsonse.ReadFrom(ResponseText);
if (JsonRepsonse.SelectToken('ActivationResponse', Result)) then
if (Result.AsValue().AsText() = 'Success') then begin
if (ActivationCodeInfo.FindFirst()) then
ActivationCodeInfo.Delete();
ActivationCodeInfo.Init();
ActivationCodeInfo.ActivationCode := ActivationCode;
ActivationCodeInfo."Date Activated" := Today;
ActivationCodeInfo."Expiration Date" := CALCDATE('<1Y>', Today);
ActivationCodeInfo.Insert();
end;
end;
end;
// Helper method to make calls to a service to validate activation code
local procedure GetHttpResponse(ActivationCode: Text; var ResponseText: Text): Boolean;
begin
// You will typically make external calls / http requests to your service to validate the activation code
// here but for the sample extension we simply return a successful dummy response
if ActivationCode = '' then
exit(false);
ResponseText := DummySuccessResponseTxt;
exit(true);
end;
// Subscribes to the OnAfterReleaseSalesDoc event and increases reward points for the sell to customer in posted sales order
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Sales Document", 'OnAfterReleaseSalesDoc', '', false, false)]
local procedure OnAfterReleaseSalesDocSubscriber(VAR SalesHeader: Record "Sales Header"; PreviewMode: Boolean; LinesWereModified: Boolean);
var
Customer: Record Customer;
begin
if SalesHeader.Status <> SalesHeader.Status::Released then
exit;
Customer.Get(SalesHeader."Sell-to Customer No.");
Customer.RewardPoints += 1; // Add a point for each new sales order
Customer.Modify();
end;
// Checks if the current codeunit is allowed to handle Customer Rewards Activation requests rather than a mock.
local procedure CanHandle(): Boolean;
var
CustomerRewardsMgtSetup: Record "Customer Rewards Mgt Setup";
begin
if CustomerRewardsMgtSetup.Get() then
exit(CustomerRewardsMgtSetup."Cust. Rew. Ext. Mgt. Cod. ID" = CODEUNIT::"Customer Rewards Ext Mgt");
exit(false);
end;
var
DummySuccessResponseTxt: Label '{"ActivationResponse": "Success"}', Locked = true;
NoRewardlevelTxt: TextConst ENU = 'NONE';
}
The following sections examine the code from the codeunit, step by step.
The EventSubscriberInstance property specifies how event subscriber methods in a codeunit are bound to the codeunit instance and the events that they subscribe to. For this example, the property has been set to StaticAutomatic, which means that subscribers are statically bound to events and the codeunit instances are controlled by the system. This value is the default.
codeunit 50101 "Customer Rewards Ext Mgt"
{
EventSubscriberInstance = StaticAutomatic;
The OnGetActivationCodeStatusFromServer event publisher method has been defined that accepts the activation code that is entered by the user as a parameter. Additionally, the OnGetActivationCodeStatusFromServerSubscriber subscriber method has been defined to listen for and handle the event.
// publishes event
[IntegrationEvent(false, false)]
procedure OnGetActivationCodeStatusFromServer(ActivationCode: Text);
begin
end;
al-languageCopy
// Subscribes to OnGetActivationCodeStatusFromServer event and handles it when the event is raised
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Rewards Ext Mgt", 'OnGetActivationCodeStatusFromServer', '', false, false)]
local procedure OnGetActivationCodeStatusFromServerSubscriber(ActivationCode: Text);
var
ActivationCodeInfo: Record "Activation Code Information";
ResponseText: Text;
Result: JsonToken;
JsonRepsonse: JsonToken;
begin
if not CanHandle() then
exit; // use the mock
// Get response from external service and update activation code information if successful
if (GetHttpResponse(ActivationCode, ResponseText)) then begin
JsonRepsonse.ReadFrom(ResponseText);
if (JsonRepsonse.SelectToken('ActivationResponse', Result)) then
if (Result.AsValue().AsText() = 'Success') then begin
if (ActivationCodeInfo.FindFirst()) then
ActivationCodeInfo.Delete();
ActivationCodeInfo.Init();
ActivationCodeInfo.ActivationCode := ActivationCode;
ActivationCodeInfo."Date Activated" := Today;
ActivationCodeInfo."Expiration Date" := CALCDATE('<1Y>', Today);
ActivationCodeInfo.Insert();
end;
end;
end;
When the ActivateCustomerRewards procedure is run, the OnGetActivationCodeStatusFromServer event is raised.
// Activates Customer Rewards if activation code is validated successfully
procedure ActivateCustomerRewards(ActivationCode: Text): Boolean;
var
ActivationCodeInformation: Record "Activation Code Information";
begin
// raise event
OnGetActivationCodeStatusFromServer(ActivationCode);
exit(ActivationCodeInformation.Get(ActivationCode));
end;
Because the EventSubscriberInstance property for the codeunit is set to Static-Automatic by default, the OnGetActivationCodeStatusFromServerSubscriber procedure is called.
In this procedure, you'll handle the raised event by first checking if the current codeunit has been defined for handling this event.
if not CanHandle() then
exit; // use the mock
al-languageCopy
// Checks if the current codeunit is allowed to handle Customer Rewards Activation requests rather than a mock.
local procedure CanHandle(): Boolean;
var
CustomerRewardsMgtSetup: Record "Customer Rewards Mgt Setup";
begin
if CustomerRewardsMgtSetup.Get() then
exit(CustomerRewardsMgtSetup."Cust. Rew. Ext. Mgt. Cod. ID" = CODEUNIT::"Customer Rewards Ext Mgt");
exit(false);
end;
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.
// Get response from external service and update activation code information if successful
if (GetHttpResponse(ActivationCode, ResponseText)) then begin
JsonRepsonse.ReadFrom(ResponseText);
// Helper method to make calls to a service to validate activation code
local procedure GetHttpResponse(ActivationCode: Text; var ResponseText: Text): Boolean;
begin
// You will typically make external calls / http requests to your service to validate the activation code
// here but for the sample extension we simply return a successful dummy response
if ActivationCode = '' then
exit(false);
ResponseText := DummySuccessResponseTxt;
exit(true);
end;
By using events when the extension makes external calls to a service, you can mock the behavior of what happens when events are raised.
The IsCustomerRewardsActivated procedure looks for an activation code in the Activation Code Information table and also verifies if that code is active or expired.
// Determines if the extension is activated
procedure IsCustomerRewardsActivated(): Boolean;
var
ActivationCodeInformation: Record "Activation Code Information";
begin
if not ActivationCodeInformation.FindFirst() then
exit(false);
if (ActivationCodeInformation."Date Activated" <= Today) and (Today <= ActivationCodeInformation."Expiration Date") then
exit(true);
exit(false);
end;
This procedure is referenced in the Rewards Level List page and in the Customer List Ext. page extension.
The OpenCustomerRewardsWizard procedure runs the Customer Rewards Wizard page:
// Opens the Customer Rewards Assisted Setup Guide
procedure OpenCustomerRewardsWizard();
var
CustomerRewardsWizard: Page "Customer Rewards Wizard";
begin
CustomerRewardsWizard.RunModal();
end;
This page is created in a later module, so for now, it won't compile. You can comment out the procedure and remove the comments after you've tested the extension.
The OpenRewardsLevelPage procedure opens the Reward Level List page.
// Opens the Reward Level page
procedure OpenRewardsLevelPage();
var
RewardsLevelList: Page "Rewards Level List";
begin
RewardsLevelList.Run();
end;
This procedure is referenced from the following objects:
Customer List Extension page extension
Customer Rewards Wizard page
The GetRewardLevel procedure determines the corresponding reward level and then returns it.
// Determines the corresponding reward level and returns it
procedure GetRewardLevel(RewardPoints: Integer) RewardLevelTxt: Text;
var
RewardLevel: Record "Reward Level";
MinRewardLevelPoints: Integer;
begin
RewardLevelTxt := NoRewardlevelTxt;
if RewardLevel.IsEmpty() then
exit;
RewardLevel.SetRange("Minimum Reward Points", 0, RewardPoints);
RewardLevel.SetCurrentKey("Minimum Reward Points"); // sorted in ascending order
if not RewardLevel.FindFirst() then
exit;
MinRewardLevelPoints := RewardLevel."Minimum Reward Points";
if RewardPoints >= MinRewardLevelPoints then begin
RewardLevel.Reset();
RewardLevel.SetRange("Minimum Reward Points", MinRewardLevelPoints, RewardPoints);
RewardLevel.SetCurrentKey("Minimum Reward Points"); // sorted in ascending order
RewardLevel.FindLast();
RewardLevelTxt := RewardLevel.Level;
end;
end;
This procedure is referenced from the Customer Card Extension page extension.
Finally, the OnAfterReleaseSalesDocSubscriber procedure will run.
// Subscribes to the OnAfterReleaseSalesDoc event and increases reward points for the sell to customer in posted sales order
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Sales Document", 'OnAfterReleaseSalesDoc', '', false, false)]
local procedure OnAfterReleaseSalesDocSubscriber(VAR SalesHeader: Record "Sales Header"; PreviewMode: Boolean; LinesWereModified: Boolean);
var
Customer: Record Customer;
begin
if SalesHeader.Status <> SalesHeader.Status::Released then
exit;
Customer.Get(SalesHeader."Sell-to Customer No.");
Customer.RewardPoints += 1; // Add a point for each new sales order
Customer.Modify();
end;
The procedure is run whenever a sales document is released because the procedure is subscribed to the OnAfterReleaseSalesDoc event in the Release Sales Document codeunit.
The procedure will add a reward point to a customer for each new sales order. It increases reward points for each sale to a customer for each posted sales order.