Today I’m starting a new blog and video series dedicated to the new certification exam for AL developers: MB-820.
The main topic of this blog is the Install and Upgrade subtype codeunits. Though, I will touch on DataTransfer data type and upgrading apps in BC just because the Microsoft lesson for MB-820 includes them:
Prepare for an easy application upgrade experience in Business Central – Training | Microsoft Learn
I have covered Install and Upgrade codeunits here for Dynamics NAV, but we are in a different world now and in this blog will look at current trends in upgrade and install codeunit by peaking at how Microsoft does it in AL and Business Central and then trying to replicate it in a sample app.
Microsoft Approach in Base Application
Let’s have a look at how Microsoft manages data upgrades in codeunit 104000 of Base Application extension:
trigger OnUpgradePerCompany()
begin
if not HybridDeployment.VerifyCanStartUpgrade(CompanyName()) then
exit;
ClearTemporaryTables();
UpdateGenJournalBatchReferencedIds();
UpdateJobs();
UpdateItemTrackingCodes();
UpgradeJobQueueEntries();
UpgradeNotificationEntries();
UpgradeVATReportSetup();
UpgradeStandardCustomerSalesCodes();
UpgradeStandardVendorPurchaseCode();
MoveLastUpdateInvoiceEntryNoValue();
CopyIncomingDocumentURLsIntoOneFiled();
...
And if we were to analyze one of the upgrade methods, we will notice 3 parts:
local procedure UpdateGenJournalBatchReferencedIds()
var
GenJournalBatch: Record "Gen. Journal Batch";
UpgradeTag: Codeunit "Upgrade Tag";
UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
begin
//part 1
// check if the current upgrade tag has been executed already
if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetBalAccountNoOnJournalAPIUpgradeTag()) then exit;
//part 2
// execute current update
if GenJournalBatch.FindSet() then
repeat
GenJournalBatch.UpdateBalAccountId();
if GenJournalBatch.Modify() then;
until GenJournalBatch.Next() = 0;
//part 3
//record current upgrade tag
UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetBalAccountNoOnJournalAPIUpgradeTag());
end;
The tag is defined in codeunit 9998 Upgrade Tag Definition:
internal procedure GetBalAccountNoOnJournalAPIUpgradeTag(): Code[250]
begin
exit('MS-275328-BalAccountNoOnJournalAPI-20180823');
end;
The idea is to record all tags as they get applied in each company so that if a new company is created, all previous updates could be executed while executed upgrades are skipped if they were already executed.
The upgrade tags are stored in the Upgrade Tags table as seen below:

What about Install codeunits?
Let’s look at the following use case. My extension will introduce a new setup table:
table 50135 "Custom Setup"
{
DataClassification = ToBeClassified;
fields
{
field(1; PK; Code[1])
{
DataClassification = ToBeClassified;
}
field(2; "No. of Retries"; Integer)
{
DataClassification = ToBeClassified;
}
field(3; "Default URL"; Code[50])
{
DataClassification = ToBeClassified;
}
field(4; "Type of app"; enum "App Type")
{
DataClassification = ToBeClassified;
}
}
keys
{
key(Key1; PK)
{
Clustered = true;
}
}
}
As a good ISV that we are we want to initialize the Custom Setup table with default values.
This can be done in an Install subtype codeunit like or close to what’s below:
codeunit 50135 InstallCU
{
Subtype = Install;
trigger OnInstallAppPerCompany()
var
ModuleInfo: ModuleInfo;
begin
NavApp.GetCurrentModuleInfo(ModuleInfo);
if ModuleInfo.DataVersion() = Version.Create(0, 0, 0, 0) then //new installation
InstallPerCompany()
else
//reinstallation
case true of
ModuleInfo.DataVersion() = Version.Create(1, 0, 0, 6):
//add reinstallation code for each version
ReInstallPerCompany();
end;
end;
trigger OnInstallAppPerDatabase()
var
begin
end;
local procedure InstallPerCompany()
var
CustomSetup: Record "Custom Setup";
begin
CustomSetup.PK := '';
CustomSetup."Default URL" := 'https://www.google.com';
CustomSetup."No. of Retries" := 300;
CustomSetup."Type of app" := CustomSetup."Type of app"::"ASP.NET MVC";
CustomSetup.Insert();
end;
local procedure ReInstallPerCompany()
var
CustomSetup: Record "Custom Setup";
begin
CustomSetup.Init();
CustomSetup.PK := '';
CustomSetup."Default URL" := 'https://www.google.com';
CustomSetup."No. of Retries" := 300;
CustomSetup."Type of app" := CustomSetup."Type of app"::"ASP.NET MVC";
if not CustomSetup.Insert() then
CustomSetup.Modify();
end;
}
What about Upgrade codeunits?
Overtime, we can change data types, or values in our setup tables.
For example, I changed an enum and an integer default value.
Initially:
enum 50135 "App Type"
{
Extensible = true;
value(0; "ASP.NET Web App")
{
}
value(1; "ASP.NET MVC")
{
}
}
While now:
enum 50135 "App Type"
{
Extensible = true;
value(0; "-NA")
{
}
value(1; "ASP.NET Web App")
{
}
value(2; "ASP.NET MVC")
{
}
value(3; "Ruby on Rails")
{
}
}
With each change introduced in a specific version we can add a new upgrade function just like below and just like in Base Application Upgrade codeunit:
codeunit 50136 UpgradeCU
{
// 1. Debug without publishing (use attach config)
// 2. Publish without debugging
Subtype = Upgrade;
trigger OnUpgradePerCompany()
begin
// Run upgrade code
UpgradeCustomSetup(); //upgrade Tag 1
UpgradeCustomSetupAgain();//upgrade Tag 2
end;
local procedure UpgradeCustomSetup()
var
CustomSetup: Record "Custom Setup";
ModuleInfo: ModuleInfo;
CustomCU: Codeunit "Custom CU";
UpgradeTagMgt: Codeunit "Upgrade Tag";
begin
// Check whether the tag has been used before, and if so, don't run upgrade code
if UpgradeTagMgt.HasUpgradeTag(CustomCU.GetCustomSetupTag()) then
exit;
if not NavApp.GetCurrentModuleInfo(ModuleInfo) then
Clear(ModuleInfo);
case true of
ModuleInfo.DataVersion() = Version.Create(1, 0, 0, 4):
begin
if CustomSetup.FindFirst() then begin
CustomSetup."Default URL" := 'https://www.google.ca';
CustomSetup."No. of Retries" := 300;
CustomSetup."Type of app" := CustomSetup."Type of app"::"-NA";
CustomSetup.Modify();
end;
end;
ModuleInfo.DataVersion() = Version.Create(1, 0, 0, 5):
begin
if CustomSetup.FindFirst() then begin
CustomSetup."Default URL" := 'https://www.google.jp';
CustomSetup."No. of Retries" := 300;
CustomSetup."Type of app" := CustomSetup."Type of app"::"Ruby on Rails";
CustomSetup.Modify();
end;
end;
end;
// Insert the upgrade tag in table 9999 "Upgrade Tags" for future reference
UpgradeTagMgt.SetUpgradeTag(CustomCU.GetCustomSetupTag());
end;
local procedure UpgradeCustomSetupAgain()
var
CustomSetup: Record "Custom Setup";
ModuleInfo: ModuleInfo;
CustomCU: Codeunit "Custom CU";
UpgradeTagMgt: Codeunit "Upgrade Tag";
begin
// Check whether the tag has been used before, and if so, don't run upgrade code
if UpgradeTagMgt.HasUpgradeTag(CustomCU.GetCustomSetupTag2()) then
exit;
if not NavApp.GetCurrentModuleInfo(ModuleInfo) then
Clear(ModuleInfo);
case true of
ModuleInfo.DataVersion() = Version.Create(1, 0, 0, 4):
begin
if CustomSetup.FindFirst() then begin
CustomSetup."Default URL" := 'https://www.google.ca';
CustomSetup."No. of Retries" := 300;
CustomSetup."Type of app" := CustomSetup."Type of app"::"-NA";
CustomSetup.Modify();
end;
end;
ModuleInfo.DataVersion() = Version.Create(1, 0, 0, 5):
begin
if CustomSetup.FindFirst() then begin
CustomSetup."Default URL" := 'https://www.google.se';
CustomSetup."No. of Retries" := 100;
CustomSetup."Type of app" := CustomSetup."Type of app"::"-NA";
CustomSetup.Modify();
end;
end;
end;
// Insert the upgrade tag in table 9999 "Upgrade Tags" for future reference
UpgradeTagMgt.SetUpgradeTag(CustomCU.GetCustomSetupTag2());
end;
}
DataTransfer data type
Microsoft introduced DataTransfer data type to improve the performance of bulk copy operations during upgrades. It works on sets of data instead of working with individual records.
Check this Microsoft Learn page to learn more: Transferring data between tables using DataTransfer – Business Central | Microsoft Learn
Or, you could visit my previous blog where I translated the DataTransfer data type functionality into SQL statements:
Keep extensions up-to-date
Business Central is not a one block does it all, but rather an assembly of lego pieces put together that work to achieve the behavior the customer wanted and paid for.
Some of these pieces of lego belong to Microsoft, and Microsoft makes available upgrades periodically. The customer and their partner is responsible to apply these updates.
There are also pieces belonging to your partner or the ISVs that sold the customer a specific app.
These ISVs are performing upgrades to their own pieces as well and these new pieces have to be applied.
These new pieces can be applied in Admin Center, by clicking on the environment where we want to apply the upgrade, and clicking on Apps:

And for each app left behind we can enforce the updates:

Some apps have dependencies, and in this case you need to take care of upgrading dependencies first before you apply them.
And that is more or less the MB-820 Prepare for an easy application upgrade.
Prepare for an easy application upgrade experience in Business Central – Training | Microsoft Learn
Want to see a demo of install and upgrade codeunits?
Check my Youtube video and subscribe:
Good luck with your studies!
— Buy my book —
https://leanpub.com/BCLedgerEntriesInsights
Download pdf sample.


