top of page
Writer's pictureTabrez Ajaz

Work with Installing and upgrading codeunits in Dynamics 365 Business Central

Updated: Feb 21, 2022

Welcome Dynamics 365 BC Lovers,


In this article, I will explain how to use install or upgrade codeunits in D365 Business Central. Whenever we develop an extension, we also must think about installation and upgrading operations. Basically, when we install or upgrade an extension sometimes we need to populate new data in the existing setup table or need to restore existing data. These tasks can be easily achieved by implementing extension install and upgrade code logic. In this type of code, we can access the different extension properties(such as version, name, publisher, and dependencies) by using NavApp.GetCurrentModuleInfo() and NavApp.GetModuleInfo() methods.


Extension Install code:

Basically, Whenever we develop an extension there might be certain operations/tasks outside of the extension code itself that you won't run when an extension is installed. These operations could include, for example, populating empty records with data, service callbacks and telemetry, version checks, and messages to users. To do these types of operations, you write extension install code. Extension install code is run when: * An extension is installed for the first time. * An uninstalled version is installed again. This enables you to write different codes for initial installation and reinstallation.


The install logic can be written by creating an install codeunit, this is a codeunit and its SubType property should be set as Install.


An install codeunit has mainly supported two main system triggers on which we can add the install code.


Trigger

Description

OnInstallAppPerCompany()

Includes code for company-related operations. Runs once for each company in the database.

OnInstallAppPerDatabase()

Includes code for database-related operations. Runs once in the entire install process.


The install codeunit becomes an integral part of the extension version, which means you can have more than one install codeunit but their order of execution is not defined.


Install codeunit syntax: The following snippet/code illustrates the basic syntax and structure of an install codeunit:

codeunit [ObjectID] [ObjectNAME]
{
    Subtype=Install;

    trigger OnInstallAppPerCompany()
    begin
        // Code for company related operations
    end;

    trigger OnInstallAppPerDatabase()
    begin
        // Code for database related operations
    end;
}
 

Install codeunit example:

codeunit 80100 MyInstallCodeunit
{
    Subtype = Install;

    trigger OnInstallAppPerDatabase();
    var
        myAppInfo: ModuleInfo;
    begin
        // Get detail of currently executing module
        NavApp.GetCurrentModuleInfo(myAppInfo);

        // A 'DataVersion' of 0.0.0.0 indicates a 'fresh/new' install
        if myAppInfo.DataVersion = Version.Create(0, 0, 0, 0) then begin
            // Write logic to handle the fresh install - When extension installed for the first time 
            ManageFreshInstall;
        end
        else begin
            // If extension is not a fresh install, then we can Re-installing the same version of the extension
            ManageReinstall;
        end;
    end;

    local procedure ManageFreshInstall();
    begin
        // Perform task that need to be done when extension is installed for the first time for the tenant.        
        // You can also do initial data setup that used by the extension
    end;

    local procedure ManageReinstall();
    begin
        // Perform task when reinstalling the same version of this extension back on this tenant.
        // Can perform some update, patch or different tasks
    end;
}
 

The above example uses the OnInstallAppPerDatabase() trigger to check whether the data version of the previous extension is compatible for the upgrade. As we know each extension version has a set of properties that contain information about the extension, including AppVersion, DataVersion, Dependencies, Id, Name, and Publisher. These information can be very useful while installing. For example, one of the more important properties is the DataVersion property, which tells you what version of data you are dealing with. These properties are encapsulated in a ModuleInfo data type. We can access these properties through NavApp.GetCurrentModuleInfo() and NavApp.GetModuleInfo() methods.


Example:

codeunit 80105 StudentsRecordInstall
{
    Subtype = Install;

    trigger OnInstallAppPerCompany();
    var
        archivedVersion: Text;
        RecStudent: Record StudentDocMgtDemo;
    begin
        archivedVersion := NavApp.GetArchiveVersion;
        if archivedVersion = '1.0.0.0' then begin
            NavApp.RestoreArchiveData(Database::StudentDocMgtDemo);
            NavApp.DeleteArchiveData(Database::StudentDocMgtDemo);
        end;
        if RecStudent.IsEmpty() then
            InsertDefaultStudents();
        //Here we could fill the every Student in the database with the default value
        //SetDefaultSalesAndReceivablesSetup();
    end;

    // Insert some students records
    procedure InsertDefaultStudents();
    begin
        InsertStudentDetails('Stud1', 'Tabrez');
        InsertStudentDetails('Stud2', 'John');
        InsertStudentDetails('Stud3', 'Rock');
    end;

    // Create and insert a student record
    procedure InsertStudentDetails(ID: Code[30]; Name: Text[250]);
    var
        RecStudent: Record StudentDocMgtDemo;
    begin
        RecStudent.Init();
        RecStudent.ID := ID;
        RecStudent.Name := Name;
        RecStudent.Insert();
    end;
}
 

Upgrade Codeunit:

Basically, an upgrade is defined as enabling an extension that has a higher version number as defined in the app.json file, than the currently installed extension version. A new extension version should be considered the data from the previous version.


The upgrade logic can be written by creating an upgrade codeunit, this is a codeunit and its SubType property should be set as Upgrade.


An upgrade codeunit has several system triggers on which we can add data upgrade code, these triggers code are invoked when we run the data upgrade process on the new extension. An upgrade codeunit supports the following triggers:


Trigger

Description

OnCheckPreconditionsPerCompany() and OnCheckPreconditionsPerDatabase()

Useful for checking that certain requirements are met before running the upgrade.

OnUpgradePerCompany() and OnUpgradePerDatabase()

Used to perform the actual upgrade.

OnValidateUpgradePerCompany() and OnValidateUpgradePerDatabase()

Used to check that the upgrade was successful.


PerCompany triggers are executed once for every company in the database, while PerDatabase triggers are executed once in the entire upgrade process.


Upgrade codeunit syntax: The following snippet/code illustrates the basic syntax and structure of an install codeunit:

codeunit [ObjectID] [ObjectNAME]
{
    Subtype=Upgrade;

    trigger OnCheckPreconditionsPerCompany()
    begin
        // Write you conditional code logic to check company is OK to upgrade.
    end;

    trigger OnUpgradePerCompany()
    begin
        // Add your code logic for company related table upgrade tasks
    end;

    trigger OnValidateUpgradePerCompany()
    begin
        // Add code logic that confirms the upgrade was successful done for each company
    end;
}
 


Install codeunit example:

The following is an example of UpgradeCodeunit in which I will update the records of the student that is inserted previously:

codeunit 80006 StudentNameWithDeptUpgrade
{
    Subtype = Upgrade;

    trigger OnUpgradePerCompany();
    var
        RecStudent: Record StudentDocMgtDemo;
        Module: ModuleInfo;
    begin
        // Get information about the current module.
        NavApp.GetCurrentModuleInfo(Module);
        // In the new version, the BAD class is upgraded to WARNING
        if Module.DataVersion.Major = 1 then begin
            if RecStudent.FindSet() then
                repeat
                    RecStudent.Name := RecStudent.Name + ' - Department';
                    RecStudent.Modify();
                until RecStudent.Next() = 0;
        end;        
    end;
}
 

Here, in the OnUpgradePerCompany trigger, we get the currently executing module information. Then, if the currently-running extension is version 1 and you change the version as 2, we perform some upgrade operations on data, here I added ” – Department” text to existing records.

The best thing here you can also check the available dependencies preconditions before performing updates on the extension. So based on the dependencies you can perform any update in the system:

codeunit 80007 WithPreConditionsUpgrade
{
    Subtype = Upgrade;

    trigger OnCheckPreconditionsPerCompany()
    var
        Module: ModuleInfo;
    begin
        // Get information about the specific dependecy module.
        NavApp.GetModuleInfo('4af19591-2fc9-4f14-af9b-1493af8931ae', Module);
        if Module.DataVersion.Major < 5 then
            Error('Your app ' + Module.Name + ' - ' + Format(Module.AppVersion) + ' needs to be upgrade to version 5 first!');
    end;
}
 

I hope this article will help you when planning to write some logic on install or upgrade operations.


Stay Tuned!

829 views0 comments

Comments


bottom of page