Page 561 CrossIntercomp. Partner Setup

App
Base Application
Namespace
Microsoft.Intercompany.DataExchange
Versions
23-28
Source table
413

Procedures, 2

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Finance/Intercompany/DataExchange/CrossIntercompPartnerSetup.Page.al500 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.Intercompany.DataExchange;

using Microsoft.Intercompany.Partner;
using Microsoft.Intercompany.Setup;
using System.Environment;

/// <summary>
/// Wizard page for setting up cross-environment intercompany partner connections.
/// Guides users through API configuration, authentication setup, and partner validation for multi-environment scenarios.
/// </summary>
page 561 "CrossIntercomp. Partner Setup"
{
    Caption = 'IC Partner Cross-Environment Setup';
    PageType = NavigatePage;
    UsageCategory = None;
    SourceTable = "IC Partner";
    DeleteAllowed = false;
    SourceTableTemporary = true;
    InsertAllowed = false;

    layout
    {
        area(Content)
        {
            group(WelcomeTab)
            {
                ShowCaption = false;
                Visible = Step = Step::Welcome;
                group(Introduction)
                {
                    Caption = 'Welcome';
                    InstructionalText = 'This setup guide will help you create a connection to a partner company in a different environment. Before you continue, make sure that the partner has enabled this extension in their company. You will need some information about their environment.';
                }
                group(TermsAndConditions)
                {
                    Caption = 'Review the terms and conditions';
                    InstructionalText = 'By enabling this feature, you consent to your data being shared with a Microsoft service that might be outside of your organization''s selected geographic boundaries and might have different compliance and security standards than Microsoft Dynamics Business Central. Your privacy is important to us, and you can choose whether to share data with the service. To learn more, follow the link below.';

                    field(Consent; ConsentState)
                    {
                        ApplicationArea = All;
                        Caption = 'I accept';
                        ToolTip = 'Accept the terms and conditions.';

                        trigger OnValidate()
                        begin
                            NextEnabled := false;
                            if ConsentState then
                                NextEnabled := true;
                        end;
                    }
                    field(LearnMore; LearnMoreTok)
                    {
                        ApplicationArea = All;
                        Editable = false;
                        ShowCaption = false;
                        Caption = ' ';
                        ToolTip = 'View information about the privacy.';

                        trigger OnDrillDown()
                        begin
                            Hyperlink(PrivacyLinkTxt);
                        end;
                    }
                }
            }
            group(AuthenticationSetup_SaaSTab)
            {
                ShowCaption = false;
                Editable = true;
                Enabled = true;
                Visible = Step = Step::SaaS;
                group(CurrentCompanySaaSConnectionDetails)
                {
                    Caption = 'Your connection details';
                    InstructionalText = 'Provide some information about the partner''s company that you will link to. For reference, this is the information for your company.';

#pragma warning disable AA0218 // Ignore warning about missing tooltip
                    field(CCSaaSConnectionUrl; CurrentCompanyConnectionUrl)
                    {
                        Caption = 'Current Connection URL';
                        Editable = false;
                        ApplicationArea = Intercompany;
                    }
                    field(CCSaaSCompanyId; CurrentCompanyCompanyId)
                    {
                        Caption = 'Current Company ID';
                        Editable = false;
                        ApplicationArea = Intercompany;
                    }
                    field(CCSaaSIntercompanyId; CurrentCompanyIntercompanyId)
                    {
                        Caption = 'Intercompany ID';
                        Editable = false;
                        ApplicationArea = Intercompany;
                    }
                    field(CCSaaSCompanyName; CurrentCompanyName)
                    {
                        Caption = 'Company Name';
                        Editable = false;
                        ApplicationArea = Intercompany;
                    }
#pragma warning restore AA0218
                }
                group(PartnerSaaSConnectionDetails)
                {
                    Caption = 'Intercompany Partner''s connection details';
                    InstructionalText = 'Provide the information below to create an intercompany partner from a different environment.';

                    field(PartnerSaaSConnectionUrl; PartnerSaaSConnectionUrl)
                    {
                        Caption = 'IC Partner''s Connection URL';
                        ApplicationArea = Intercompany;
                        ToolTip = 'Specifies the connection URL for the intercompany partner''s environment.';
                        ShowMandatory = true;

                        trigger OnValidate()
                        begin
                            if (PartnerSaaSConnectionUrl <> '') and not ValidateDynamicsUrl(PartnerSaaSConnectionUrl) then
                                Error(UntrustedConnectionUrlErr);
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                    field(PartnerSaaSCompanyId; PartnerSaaSCompanyId)
                    {
                        Caption = 'IC Partner''s Company ID';
                        ApplicationArea = Intercompany;
                        ToolTip = 'Specifies the intercompany partner''s company ID in their environment.';
                        ShowMandatory = true;

                        trigger OnValidate()
                        begin
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                    field(PartnerSaaSCompanyIntercompanyId; Rec.Code)
                    {
                        Caption = 'IC Partner''s Intercompany ID';
                        ApplicationArea = Intercompany;
                        ToolTip = 'Specifies the intercompany partner''s intercompany identifier in their environment.';
                        ShowMandatory = true;

                        trigger OnValidate()
                        begin
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                    field(PartnerSaaSCompanyName; PartnerSaaSCompanyName)
                    {
                        Caption = 'IC Partner''s Company Name';
                        ApplicationArea = Intercompany;
                        ToolTip = 'Specifies the intercompany partner''s company name in their environment.';
                        ShowMandatory = true;

                        trigger OnValidate()
                        begin
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                }
                group(OAuth2ConnectionDetails)
                {
                    Caption = 'Authentication details';
                    InstructionalText = 'Provide information about the Microsoft Entra authentication application that will be used to connect with the partner.';

                    field(OAuth2ClientID; OAuth2ClientID)
                    {
                        ApplicationArea = Intercompany;
                        ExtendedDatatype = Masked;
                        Caption = 'Client ID';
                        ToolTip = 'Specifies the client ID of the Microsoft Entra authentication application.';
                        trigger OnValidate()
                        begin
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                    field(OAuth2ClientSecret; OAuth2ClientSecret)
                    {
                        ApplicationArea = Intercompany;
                        ExtendedDatatype = Masked;
                        Caption = 'Client Secret';
                        ToolTip = 'Specifies the client secret of the Microsoft Entra authentication application.';
                        trigger OnValidate()
                        begin
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                    field(OAuth2TokenEndpoint; OAuth2TokenEndpoint)
                    {
                        ApplicationArea = Intercompany;
                        ExtendedDatatype = URL;
                        Caption = 'Token Endpoint';
                        ToolTip = 'Specifies the OAuth 2.0 token endpoint of the Microsoft Entra authentication application. The endpoint indicates a directory to which tokens can be requested. In most scenarios this will be: https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token .';
                        trigger OnValidate()
                        begin
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                    field(OAuth2RedirectUrl; OAuth2RedirectUrl)
                    {
                        ApplicationArea = Intercompany;
                        ExtendedDatatype = URL;
                        Caption = 'Redirect URL';
                        ToolTip = 'Specifies the OAuth 2.0 redirect URL of the Microsoft Entra authentication application. In most scenarios this will be: https://businesscentral.dynamics.com/OAuthLanding.htm .';
                        // The connector always uses the environment's default redirect URL, so this field is hidden to avoid promising configurable behavior.
                        Visible = false;
                        trigger OnValidate()
                        begin
                            NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
                        end;
                    }
                }
            }
            group(TestConnectionTab)
            {
                ShowCaption = false;
                Visible = Step = Step::TestConnection;
                group(VerifyConnection)
                {
                    Caption = 'Verify connection';
                    InstructionalText = 'Before proceeding, let''s ensure that the connection to the partner is working correctly. Start a test connection to the partner''s company by selecting the ''Test Connection'' button.';
                    Visible = not FinishEnabled;
                }
            }
            group(FinishTab)
            {
                ShowCaption = false;
                Visible = Step = Step::Finish;
                group(AllDone)
                {
                    Caption = 'All done';
                    InstructionalText = 'You''re all set. Choose Finish to save your settings.';
                    Visible = FinishEnabled;
                }
            }
        }
    }
    actions
    {
        area(Processing)
        {
            action(TestConnection)
            {
                ApplicationArea = Intercompany;
                Caption = 'Test Connection';
                ToolTip = 'Test the connection to the partner''s company to check whether authentication is correctly set up.';
                Visible = TestConnectionEnabled;
                Image = InteractionTemplateSetup;
                InFooterBar = true;

                trigger OnAction()
                var
                    TempICPartner: Record "IC Partner" temporary;
                    CrossIntercompanyConnector: Codeunit "CrossIntercompany Connector";
                begin
                    FinishEnabled := false;
                    SaveConfigurationToTemporaryPartner(TempICPartner);
                    if CrossIntercompanyConnector.TestICPartnerSetup(TempICPartner) then
                        NextEnabled := true;
                end;
            }
            action(ActionBack)
            {
                ApplicationArea = Intercompany;
                Caption = 'Back';
                ToolTip = 'Go to previous page';
                Enabled = BackEnabled;
                Image = PreviousRecord;
                InFooterBar = true;

                trigger OnAction()
                begin
                    PreviousStep();
                end;
            }
            action(ActionNext)
            {
                ApplicationArea = Intercompany;
                Caption = 'Next';
                ToolTip = 'Go to the next page';
                Enabled = NextEnabled;
                Image = NextRecord;
                InFooterBar = true;

                trigger OnAction();
                var
                begin
                    NextStep();
                end;
            }
            action(ActionFinish)
            {
                ApplicationArea = Intercompany;
                Caption = 'Finish';
                ToolTip = 'Finish the setup';
                Enabled = FinishEnabled;
                Image = Approve;
                InFooterBar = true;

                trigger OnAction()
                var
                    TempICPartner: Record "IC Partner" temporary;
                    CrossIntercompanyConnector: Codeunit "CrossIntercompany Connector";
                begin
                    SaveConfigurationToTemporaryPartner(TempICPartner);
                    CrossIntercompanyConnector.FinishICPartnerSetup(TempICPartner);
                    Rec.TransferFields(TempICPartner, true);
                    Rec.Modify();
                    SkipOnCloseQuestion := true;
                    CurrPage.Close();
                end;
            }
        }
    }

    trigger OnOpenPage()
    var
        ICPartnerChangeMonitor: Codeunit "IC Partner Change Monitor";
    begin
        ICPartnerChangeMonitor.CheckHasPermissionsToChangeSensitiveFields();
        LoadSaaSDataForCurrentCompany();

        Step := Step::Welcome;
        ResetNavigation();
        ConsentState := false;
    end;

    trigger OnQueryClosePage(CloseAction: Action): Boolean
    begin
        if SkipOnCloseQuestion then
            exit(true);
        exit(Confirm(NotSetUpQst, false));
    end;

    var
        Step: Option Welcome,SaaS,TestConnection,Finish;
        NextEnabled, BackEnabled, FinishEnabled, TestConnectionEnabled, SkipOnCloseQuestion : Boolean;
        CurrentCompanyConnectionUrl, CurrentCompanyCompanyId, CurrentCompanyIntercompanyId, CurrentCompanyName : Text;
        PartnerSaaSCompanyName: Text[100];
        ConsentState: Boolean;

        [NonDebuggable]
        PartnerSaaSCompanyId, OAuth2ClientID : Guid;
        [NonDebuggable]
        PartnerSaaSConnectionUrl, OAuth2ClientSecret, OAuth2TokenEndpoint, OAuth2RedirectUrl : Text;

        NotSetUpQst: Label 'The setup for the connection to the intercompany partner''s environment isn''t complete. If you leave this guide, your settings will be deleted.\\Are you sure you want to exit?';
        LearnMoreTok: Label 'Privacy and Cookies';
        PrivacyLinkTxt: Label 'https://go.microsoft.com/fwlink/?linkid=521839';
        UntrustedConnectionUrlErr: Label 'The connection URL must be a trusted Business Central API URL.';


    local procedure LoadSaaSDataForCurrentCompany()
    var
        ICSetup: Record "IC Setup";
        Company: Record Company;
        CrossIntercompanyConnector: Codeunit "CrossIntercompany Connector";
    begin
        CurrentCompanyConnectionUrl := GetUrl(ClientType::Api);

        Company.Get(CompanyName());
        CurrentCompanyCompanyId := CrossIntercompanyConnector.RemoveCurlyBracketsAndUpperCases(Company.Id);

        ICSetup.Get();
        CurrentCompanyIntercompanyId := ICSetup."IC Partner Code";

        CurrentCompanyName := CompanyName();
    end;

    local procedure SaveConfigurationToTemporaryPartner(var TempICPartner: Record "IC Partner" temporary)
    var
        ICPartnerChangeMonitor: Codeunit "IC Partner Change Monitor";
        CompanyId, Auth2ClientId : SecretText;
    begin
        ICPartnerChangeMonitor.CheckHasPermissionsToChangeSensitiveFields();
        TempICPartner.Reset();
        TempICPartner.DeleteAll();
        TempICPartner.Init();

        CompanyId := Format(PartnerSaaSCompanyId);
        Auth2ClientId := Format(OAuth2ClientID);

        TempICPartner.Code := Rec.Code;
        TempICPartner.Name := PartnerSaaSCompanyName;
        TempICPartner."Inbox Details" := PartnerSaaSCompanyName;
        TempICPartner."Connection Url Key" := TempICPartner.SetSecret(TempICPartner."Connection Url Key", PartnerSaaSConnectionUrl);
        TempICPartner."Company Id Key" := TempICPartner.SetSecret(TempICPartner."Company Id Key", CompanyId);
        TempICPartner."Client Id Key" := TempICPartner.SetSecret(TempICPartner."Client Id Key", Auth2ClientId);
        TempICPartner."Client Secret Key" := TempICPartner.SetSecret(TempICPartner."Client Secret Key", OAuth2ClientSecret);
        TempICPartner."Token Endpoint Key" := TempICPartner.SetSecret(TempICPartner."Token Endpoint Key", OAuth2TokenEndpoint);
        TempICPartner."Redirect Url Key" := TempICPartner.SetSecret(TempICPartner."Redirect Url Key", OAuth2RedirectUrl);
        TempICPartner."Token Expiration Time" := CurrentDateTime;

        TempICPartner."Inbox Type" := Enum::Microsoft.Intercompany.Partner."IC Partner Inbox Type"::Database;
        TempICPartner."Data Exchange Type" := Enum::"IC Data Exchange Type"::API;

        TempICPartner.Insert();
    end;

    #region Visibility for sections
    local procedure ResetNavigation()
    begin
        BackEnabled := true;
        NextEnabled := true;
        FinishEnabled := false;
        TestConnectionEnabled := false;
        SkipOnCloseQuestion := false;

        case Step of
            Step::Welcome:
                ShowWelcomeTab();
            Step::SaaS:
                ShowAuthenticationSetupForSaaSTab();
            Step::TestConnection:
                ShowTestConnectionTab();
            Step::Finish:
                ShowFinishTab();
        end;
    end;

    local procedure PreviousStep()
    begin
        case Step of
            Step::SaaS:
                Step := Step::Welcome;
            Step::TestConnection:
                Step := Step::SaaS;
            Step::Finish:
                Step := Step::TestConnection;
        end;
        ResetNavigation();
    end;

    local procedure NextStep()
    begin
        case Step of
            Step::Welcome:
                Step := Step::SaaS;
            Step::SaaS:
                Step := Step::TestConnection;
            Step::TestConnection:
                Step := Step::Finish;
        end;
        ResetNavigation();
    end;

    local procedure ShowWelcomeTab()
    begin
        BackEnabled := false;
        NextEnabled := ConsentState;
    end;

    local procedure ShowAuthenticationSetupForSaaSTab()
    begin
        NextEnabled := CheckIfSaaSConnectionDetailsAreFilled();
    end;

    local procedure ShowTestConnectionTab()
    begin
        NextEnabled := false;
        TestConnectionEnabled := true;
    end;

    local procedure ShowFinishTab()
    begin
        NextEnabled := false;
        FinishEnabled := true;
    end;

    /// <summary>
    /// Validates whether all required SaaS connection details are properly configured for API connectivity.
    /// Checks partner connection URL, company ID, OAuth credentials, and token endpoint configuration.
    /// </summary>
    /// <returns>True if all SaaS connection details are filled and valid, false otherwise</returns>
    procedure CheckIfSaaSConnectionDetailsAreFilled(): Boolean
    var
        PartnerConnectionDetailsAreFilled: Boolean;
        OAuth2ClientIDDetailsAreFilled: Boolean;
    begin
        PartnerConnectionDetailsAreFilled := (PartnerSaaSConnectionUrl <> '') and (not IsNullGuid(PartnerSaaSCompanyId)) and (Rec.Code <> '') and (PartnerSaaSCompanyName <> '') and ValidateDynamicsUrl(PartnerSaaSConnectionUrl);
        OAuth2ClientIDDetailsAreFilled := (not IsNullGuid(OAuth2ClientID)) and (OAuth2ClientSecret <> '') and (OAuth2TokenEndpoint <> '');
        exit(PartnerConnectionDetailsAreFilled and OAuth2ClientIDDetailsAreFilled);
    end;

    internal procedure ValidateDynamicsUrl(Url: Text): Boolean
    var
        CrossIntercompanyConnector: Codeunit "CrossIntercompany Connector";
    begin
        if Url = '' then
            exit(true);

        exit(CrossIntercompanyConnector.IsDestinationUrlTrusted(Url));
    end;
    #endregion
}