Dynamics 365 Business Central: Allow user selection of Role Center (Profile) for each company (Multiple roles for one user) – Customization

Dynamics 365 Business Central

Hi, Readers.
Today I would like to talk about an interesting question I saw on Twitter earlier, is it possible to allow user selection of Role Center (Profile) for each company in Business Central.

First, let’s look at the custom function I implemented: (Not the perfect solution)
The latest version: W1 21.4 (Platform 21.0.52534.53435 + Application 21.4.52563.52602)

Test Video:

The Role Center is the user’s entry point and home page for Dynamics 365 Business Central. You can develop several different Role Centers, where each Role Center is customized to the profile of the intended users. For example, you could have Role Centers that target the different levels within an organization, such business owners, department leads, and information workers. (A Role Center is defined by page that has the PageType property set to RoleCenter)

As an administrator, you can configure user settings in Business Central, similar to how individual users can manage their own preferences in the My Settings page.

The User Settings list shows the current settings for each user. To view or edit individual users, choose the View or Edit action.

My Settings page:

PS: How to define default Role Center (Profile) for users and How to bulk edit user roles/profiles (Customization)

However, the standard functionality of Business Central is one-to-one. Only one Role (Profile) can be set up for one user. This is discussed similarly in both MS Yammer and MSIdea.

So is it possible if customized?
I did a brief research and found a simple way. But I personally feel that this is not a perfect solution. The key point is to reset the user’s default Role (Profile) in the code after opening the company. This time the company name is already available. And we just create a relational table to manage it.

The following is the event I need to use this time, it is relatively low risk.

New isolated event OnCompanyOpenCompleted: This event is raised after sign-in and company open have completed. Any subscribers to this event can fail but will not block the sign-in process.

More details: Business Central 2022 wave 1 (BC20) new features: Isolated events and New isolated event OnCompanyOpenCompleted

The main method is the one below.
codeunit 9170 “Conf./Personalization Mgt.” -> SetCurrentProfile

Well, first, I create a table and page to manage the relationship between users, companies and Roles (Profiles). (Note set DataPerCompany = false)
PS: How to find out available Roles/Profiles via AL (About the Available Roles/Profiles Lookup)

Then create a new list page.

For example,

Then add the control code in the event mentioned above.

When this step is done, the user will be automatically assigned to a new Profile (Role) when logging in for the first time. But then I realized a problem. When changing companies, the system will not automatically re-read the Profile (Role) every time, and sometimes the reading is incomplete. Since there is no Global Event for Change Company in the system (maybe I missed something), I can only make some changes to the My Settings page (
User Settings (9204, StandardDialog)).

But the fields displayed on this page are all variables, and the source table of this page is temporary. So we can only think of a way on its original table. The following is the solution I thought of. When changing the company, modify the user’s default Profile (Role).

Finally, when opening the My Settings page, sometimes Role does not display the correct Profile (Role), and the previous one will be displayed, so I simply added the following code.

That’s all the customization I’ve done. As for why I don’t think this is a perfect solution, one of the biggest reasons is Business Central 2022 wave 2 (BC21) new features: Switch companies across environments. When using this method to switch companies, you must close BC, and then log in again, the new Profile (Role) will take effect.

Well, while this may seem a bit complicated, hopefully it can give you some hints. Give it a try!!!😁

Source Code: Github (Please note that the source code is for reference only, you can improve it according to your own needs and for the convenience of copying, I have concentrated the code in an AL file, please manage it separately in your project.)

table 50100 ZYUsersRoles
{
    DataClassification = CustomerContent;
    DataPerCompany = false;

    fields
    {
        field(1; UserID; Code[20])
        {
            Caption = 'User ID';
            TableRelation = User."User Name";
            ValidateTableRelation = false;
            DataClassification = CustomerContent;
        }
        field(2; CompanyName; Text[25])
        {
            Caption = 'Company Name';
            TableRelation = Company.Name;
            DataClassification = CustomerContent;
        }
        field(3; ProfileID; Code[30])
        {
            Caption = 'Profile ID';
            DataClassification = CustomerContent;

            trigger OnLookup()
            var
                TempAllProfile: Record "All Profile" temporary;
            begin
                PopulateProfiles(TempAllProfile);
                if Page.RunModal(Page::Roles, TempAllProfile) = Action::LookupOK then begin
                    ProfileID := TempAllProfile."Profile ID";
                end;
            end;
        }
    }

    keys
    {
        key(PK; UserID, CompanyName)
        {
            Clustered = true;
        }
    }

    var
        DescriptionFilterTxt: Label 'Navigation menu only.';
        UserCreatedAppNameTxt: Label '(User-created)';

    local procedure PopulateProfiles(var TempAllProfile: Record "All Profile" temporary)
    var
        AllProfile: Record "All Profile";
    begin
        TempAllProfile.Reset();
        TempAllProfile.DeleteAll();
        AllProfile.SetRange(Enabled, true);
        AllProfile.SetFilter(Description, '<> %1', DescriptionFilterTxt);
        if AllProfile.FindSet() then
            repeat
                TempAllProfile := AllProfile;
                if IsNullGuid(TempAllProfile."App ID") then
                    TempAllProfile."App Name" := UserCreatedAppNameTxt;
                TempAllProfile.Insert();
            until AllProfile.Next() = 0;
    end;
}

page 50102 "ZY Users Roles"
{
    ApplicationArea = All;
    Caption = 'ZY Users Roles';
    PageType = List;
    SourceTable = ZYUsersRoles;
    UsageCategory = Lists;
    DelayedInsert = true;

    layout
    {
        area(content)
        {
            repeater(General)
            {
                field(UserID; Rec.UserID)
                {
                    ToolTip = 'Specifies the value of the User ID field.';
                }
                field(CompanyName; Rec.CompanyName)
                {
                    ToolTip = 'Specifies the value of the Company Name field.';
                }
                field(ProfileID; Rec.ProfileID)
                {
                    ToolTip = 'Specifies the value of the Profile ID field.';
                }
            }
        }
    }
}

codeunit 50100 UsersProfilesHandle
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Company Triggers", 'OnCompanyOpenCompleted', '', false, false)]
    local procedure OnCompanyOpenCompleted();
    var
        ConfPersonMgt: Codeunit "Conf./Personalization Mgt.";
        AllProfile: Record "All Profile";
        ZYUsersRoles: Record ZYUsersRoles;
    begin
        if ZYUsersRoles.Get(UserId, CompanyName) then begin
            AllProfile.Reset();
            AllProfile.SetRange("Profile ID", ZYUsersRoles.ProfileID);
            if AllProfile.FindFirst() then
                ConfPersonMgt.SetCurrentProfile(AllProfile);
        end;
    end;
}

pageextension 50115 UserSettingsExt extends "User Settings"
{
    trigger OnOpenPage()
    begin
        CurrPage.Update();
    end;
}

tableextension 50115 UserSettingsExt extends "User Settings"
{
    trigger OnBeforeModify()
    var
        ZYUsersRoles: Record ZYUsersRoles;
    begin
        if Rec.Company <> xRec.Company then
            if ZYUsersRoles.Get("User ID", Rec.Company) then begin
                Rec."Profile ID" := ZYUsersRoles.ProfileID;
            end;
    end;
}

PS: Please do not try to use SessionSettings.RequestSessionUpdate method in OnCompanyOpenCompleted event, it will cause the problem that BC cannot be opened in the current version.
More details: SessionSettings Data Type (A complex data type)

END

Hope this will help.

Thanks for your reading.

ZHU

コメント

Copied title and URL