Codeunit 483 Change Global Dimensions

App
Base Application
Namespace
Microsoft.Finance.Dimension
Versions
17-28

Procedures, 17Events, 11

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Finance/Dimension/ChangeGlobalDimensions.Codeunit.al1007 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.Finance.Dimension;

using Microsoft.Assembly.History;
using Microsoft.Bank.Ledger;
using Microsoft.Finance.GeneralLedger.Ledger;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Finance.VAT.Ledger;
using Microsoft.FixedAssets.Insurance;
using Microsoft.FixedAssets.Ledger;
using Microsoft.FixedAssets.Maintenance;
using Microsoft.HumanResources.Payables;
using Microsoft.Inventory.Counting.Document;
using Microsoft.Inventory.Counting.History;
using Microsoft.Inventory.Counting.Journal;
using Microsoft.Inventory.History;
using Microsoft.Inventory.Ledger;
using Microsoft.Projects.Project.Ledger;
using Microsoft.Projects.Project.WIP;
using Microsoft.Projects.Resources.Ledger;
using Microsoft.Purchases.History;
using Microsoft.Purchases.Payables;
using Microsoft.Sales.FinanceCharge;
using Microsoft.Sales.History;
using Microsoft.Sales.Receivables;
using Microsoft.Sales.Reminder;
using Microsoft.Warehouse.Ledger;
using System.Automation;
using System.Environment;
using System.Reflection;
using System.Utilities;

/// <summary>
/// Manages the process of changing global dimension assignments across all business data and ledger entries.
/// Provides comprehensive dimension restructuring with batch processing, parallel execution, and extensive validation.
/// </summary>
/// <remarks>
/// Supports sequential and parallel processing modes for large-scale dimension changes.
/// Includes table dependency analysis, session management, and rollback capabilities for dimension restructuring operations.
/// Integrates with Change Global Dim. Header and Log Entry tables for process tracking and error handling.
/// </remarks>
codeunit 483 "Change Global Dimensions"
{
    Permissions = TableData "G/L Entry" = rm,
                  TableData "VAT Entry" = rm,
                  TableData "Cust. Ledger Entry" = rm,
                  TableData "Vendor Ledger Entry" = rm,
                  TableData "Item Ledger Entry" = rm,
                  Tabledata "Approval Entry" = rm,
                  TableData "Sales Shipment Header" = rm,
                  TableData "Sales Shipment Line" = rm,
                  TableData "Sales Invoice Header" = rm,
                  TableData "Sales Invoice Line" = rm,
                  TableData "Sales Cr.Memo Header" = rm,
                  TableData "Sales Cr.Memo Line" = rm,
                  TableData "Purch. Rcpt. Header" = rm,
                  TableData "Purch. Rcpt. Line" = rm,
                  TableData "Purch. Inv. Header" = rm,
                  TableData "Purch. Inv. Line" = rm,
                  TableData "Purch. Cr. Memo Hdr." = rm,
                  TableData "Purch. Cr. Memo Line" = rm,
                  TableData "Job Ledger Entry" = rm,
                  TableData "Res. Ledger Entry" = rm,
                  TableData "Bank Account Ledger Entry" = rm,
                  TableData "Phys. Inventory Ledger Entry" = rm,
                  TableData "Issued Reminder Header" = rm,
                  TableData "Issued Reminder Line" = rm,
                  TableData "Reminder/Fin. Charge Entry" = rm,
                  TableData "Issued Fin. Charge Memo Header" = rm,
                  TableData "Issued Fin. Charge Memo Line" = rm,
                  TableData "Detailed Cust. Ledg. Entry" = rm,
                  TableData "Detailed Vendor Ledg. Entry" = rm,
                  TableData "Posted Assembly Header" = rm,
                  TableData "Posted Assembly Line" = rm,
                  TableData "Job WIP G/L Entry" = rm,
                  TableData "Employee Ledger Entry" = rm,
                  TableData "Detailed Employee Ledger Entry" = rm,
#if not CLEAN28
                  TableData Microsoft.Manufacturing.Document."Production Order" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Line" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Component" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Routing Line" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Capacity Need" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Routing Tool" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Routing Personnel" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Rtng Qlty Meas." = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Comment Line" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Rtng Comment Line" = rm,
                  TableData Microsoft.Manufacturing.Document."Prod. Order Comp. Cmt Line" = rm,
#endif
                  TableData "Invt. Receipt Header" = rm,
                  TableData "Invt. Receipt Line" = rm,
                  TableData "Invt. Shipment Header" = rm,
                  TableData "Invt. Shipment Line" = rm,
                  TableData "FA Ledger Entry" = rm,
                  TableData "Maintenance Ledger Entry" = rm,
                  TableData "Ins. Coverage Ledger Entry" = rm,
                  TableData "Value Entry" = rm,
                  TableData Microsoft.Manufacturing.Capacity."Capacity Ledger Entry" = rm,
#if not CLEAN28
                  TableData Microsoft.Service.Document."Service Header" = rm,
                  TableData Microsoft.Service.Document."Service Item Line" = rm,
                  TableData Microsoft.Service.Document."Service Line" = rm,
                  TableData Microsoft.Service.Ledger."Service Ledger Entry" = rm,
                  TableData Microsoft.Service.Contract."Service Contract Header" = rm,
                  TableData Microsoft.Service.Contract."Service Contract Line" = rm,
                  TableData Microsoft.Service.History."Service Invoice Line" = rm,
                  tabledata Microsoft.Service.History."Service Cr.Memo Header" = rm,
                  tabledata Microsoft.Service.History."Service Cr.Memo Line" = rm,
                  tabledata Microsoft.Service.History."Service Invoice Header" = rm,
                  tabledata Microsoft.Service.History."Service Shipment Header" = rm,
                  tabledata Microsoft.Service.History."Service Shipment Line" = rm,
                  TableData Microsoft.Service.Contract."Filed Service Contract Header" = rm,
#endif
                  TableData "Phys. Invt. Order Header" = rm,
                  TableData "Phys. Invt. Order Line" = rm,
                  TableData "Pstd. Phys. Invt. Order Hdr" = rm,
                  TableData "Pstd. Phys. Invt. Order Line" = rm,
                  TableData "Return Shipment Header" = rm,
                  TableData "Return Shipment Line" = rm,
                  TableData "Return Receipt Header" = rm,
                  TableData "Return Receipt Line" = rm,
                  TableData "Warehouse Entry" = rm;
    TableNo = "Change Global Dim. Log Entry";

    trigger OnRun()
    begin
        Rec.SendTraceTagOnTaskStart();
        if ChangeGlobalDimLogMgt.IsBufferClear() then
            ChangeGlobalDimLogMgt.FillBuffer();
        BindSubscription(ChangeGlobalDimLogMgt);
        if RunTask(Rec) then begin
            DeleteEntry(Rec);
            if ChangeGlobalDimLogMgt.IsBufferClear() then
                ResetState();
        end;
    end;

    var
        ChangeGlobalDimHeader: Record "Change Global Dim. Header";
        GeneralLedgerSetup: Record "General Ledger Setup";
        ChangeGlobalDimLogMgt: Codeunit "Change Global Dim. Log Mgt.";
        DepRecNo: Dictionary of [Integer, List of [Integer]]; // [TableId, [RecRefIndex, StartFromRecNo, CurrRecNo]]
        Window: Dialog;
        CloseActiveSessionsMsg: Label 'Close all other active sessions.';
        CloseSessionNotificationTok: Label 'A2C57B69-B056-4B3B-8D0F-C0D997145EE7', Locked = true;
        CurrRecord: Integer;
        NoOfRecords: Integer;
        ProgressMsg: Label 'Updating #1#####\@2@@@@@@@@@@', Comment = '#1-Table Id and Name;#2 - progress bar.';
        IsWindowOpen: Boolean;
        TagCategoryTxt: Label 'Change Global Dimensions', Locked = true;
        StartTraceTagMsg: Label 'Change Global Dimensions is started, parallel processing is on.';
        SequentialStartTraceTagMsg: Label 'Change Global Dimensions is started, parallel processing is off.';
        FinishTraceTagMsg: Label 'Change Global Dimensions is finished.';
        SessionListActionTxt: Label 'Session List';
        SessionUpdateRequiredMsg: Label 'All records were successfully updated. To apply the updates, close the General Ledger Setup page.';

    /// <summary>
    /// Resets the global dimension change state if all dimension change operations have been completed.
    /// Verifies completion status and initiates state reset automatically when all tables are processed.
    /// </summary>
    procedure ResetIfAllCompleted()
    begin
        ChangeGlobalDimLogMgt.FillBuffer();
        if ChangeGlobalDimLogMgt.AreAllCompleted() then
            ResetState();
    end;

    local procedure GetDelayInScheduling(): Integer
    begin
        // duration in milliseconds between scheduled jobs
        exit(100);
    end;

    /// <summary>
    /// Prepares the global dimension change operation by initializing tables and validating configuration.
    /// Sets up the dimension change environment with table analysis and dependency mapping.
    /// </summary>
    procedure Prepare()
    begin
        ChangeGlobalDimHeader.Get();
        if IsPrepareEnabled(ChangeGlobalDimHeader) and ChangeGlobalDimHeader."Parallel Processing" then
            if IsCurrentSessionActiveOnly() then
                PrepareTableList()
            else
                SendCloseSessionsNotification();
    end;

    local procedure PrepareTableList() IsListFilled: Boolean
    begin
        IsListFilled := InitTableList();
        if not IsListFilled then begin
            UpdateGLSetup();
            RefreshHeader();
        end;
    end;

    local procedure ProcessTableList()
    var
        ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
    begin
        ChangeGlobalDimLogEntry.Reset();
        ChangeGlobalDimLogEntry.SetFilter("Table ID", '>0');
        ChangeGlobalDimLogEntry.SetRange("Parent Table ID", 0);
        if IsWindowOpen then begin
            CurrRecord := 0;
            ChangeGlobalDimLogEntry.CalcSums("Total Records");
            NoOfRecords := ChangeGlobalDimLogEntry."Total Records";
        end;
        if ChangeGlobalDimLogEntry.FindSet(true) then
            repeat
                if IsWindowOpen then
                    Window.Update(1, StrSubstNo('%1 %2', ChangeGlobalDimLogEntry."Table ID", ChangeGlobalDimLogEntry."Table Name"));
                if RunTask(ChangeGlobalDimLogEntry) then
                    DeleteEntry(ChangeGlobalDimLogEntry);
            until ChangeGlobalDimLogEntry.Next() = 0;
        ResetIfAllCompleted();
    end;

    /// <summary>
    /// Removes the global dimension change header and all associated log entries.
    /// Cleans up dimension change configuration data after completion or cancellation.
    /// </summary>
    procedure RemoveHeader()
    var
        ChangeGlobalDimHeader: Record "Change Global Dim. Header";
        ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
    begin
        if ChangeGlobalDimLogEntry.IsEmpty() then
            ChangeGlobalDimHeader.DeleteAll();
    end;

    /// <summary>
    /// Resets the global dimension change state by clearing all log entries and cached data.
    /// Initializes the system for a new dimension change operation or after completion.
    /// </summary>
    procedure ResetState()
    var
        ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
    begin
        ChangeGlobalDimLogEntry.DeleteAll(true);
        ClearAll();
        ChangeGlobalDimLogMgt.ClearBuffer();
        RefreshHeader();
    end;

    /// <summary>
    /// Reruns failed dimension change operations for specified log entries.
    /// Reprocesses table updates that encountered errors during the initial dimension change operation.
    /// </summary>
    /// <param name="ChangeGlobalDimLogEntry">Log entry record to reprocess for dimension changes</param>
    procedure Rerun(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry")
    begin
        ChangeGlobalDimLogEntry.LockTable();
        ChangeGlobalDimLogEntry.UpdateStatus();
        if ChangeGlobalDimLogMgt.FillBuffer() then
            RerunEntry(ChangeGlobalDimLogEntry);
    end;

    local procedure RunTask(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry") Completed: Boolean
    begin
        ChangeGlobalDimLogEntry.SetSessionInProgress();
        if ChangeGlobalDimHeader."Parallel Processing" then
            Commit();
        Completed := ChangeDimsOnTable(ChangeGlobalDimLogEntry);
    end;

    /// <summary>
    /// Initiates the global dimension change process using the configured processing mode.
    /// Starts dimension change operations with either parallel or sequential processing based on configuration.
    /// </summary>
    procedure Start()
    begin
        ChangeGlobalDimHeader.Get();
        if IsStartEnabled() then begin
            SendTraceTagOn(StartTraceTagMsg);
            CompleteEmptyTables();
            UpdateGLSetup();
            ScheduleJobs(GetDelayInScheduling());
            RefreshHeader();
        end;
    end;

    /// <summary>
    /// Initiates global dimension change processing in sequential mode with progress tracking.
    /// Executes dimension changes table by table without parallel processing for controlled execution.
    /// </summary>
    procedure StartSequential()
    begin
        ChangeGlobalDimHeader.Get();
        if IsPrepareEnabled(ChangeGlobalDimHeader) and not ChangeGlobalDimHeader."Parallel Processing" then begin
            WindowOpen();
            if PrepareTableList() then begin
                SendTraceTagOn(SequentialStartTraceTagMsg);
                CompleteEmptyTables();
                UpdateGLSetup();
                ProcessTableList();
                RefreshHeader();
                SendTraceTagOn(FinishTraceTagMsg);
            end;
            WindowClose();
        end;
    end;

    local procedure CompleteEmptyTables()
    var
        ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
        RecRef: RecordRef;
    begin
        ChangeGlobalDimLogEntry.SetFilter("Table ID", '>0');
        ChangeGlobalDimLogEntry.SetRange("Total Records", 0);
        if ChangeGlobalDimLogEntry.FindSet(true) then
            repeat
                RecRef.Open(ChangeGlobalDimLogEntry."Table ID");
                RecRef.LockTable(true);
                ChangeGlobalDimLogEntry."Total Records" := RecRef.Count();
                if ChangeGlobalDimLogEntry."Total Records" = 0 then
                    DeleteEntry(ChangeGlobalDimLogEntry)
                else
                    ChangeGlobalDimLogEntry.Modify();
                RecRef.Close();
            until ChangeGlobalDimLogEntry.Next() = 0;
    end;

    /// <summary>
    /// Fills the internal buffer with dimension change log entries for processing coordination.
    /// Populates buffer data structures used for progress tracking and parallel processing management.
    /// </summary>
    procedure FillBuffer()
    begin
        ChangeGlobalDimLogMgt.FillBuffer();
    end;

    local procedure FindChildTables(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; var TempChildChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry" temporary): Boolean
    begin
        if ChangeGlobalDimLogEntry."Is Parent Table" then
            exit(ChangeGlobalDimLogMgt.FindChildTables(ChangeGlobalDimLogEntry."Table ID", TempChildChangeGlobalDimLogEntry));
    end;

    local procedure FindDependentTables(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; ParentChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; var DependentRecRef: array[7] of RecordRef): Boolean
    var
        TempChildChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry" temporary;
        RecRefIndex: Integer;
        TotalRecords: Integer;
    begin
        RecRefIndex := 0;
        if FindChildTables(ParentChangeGlobalDimLogEntry, TempChildChangeGlobalDimLogEntry) then
            repeat
                if ChangeGlobalDimLogEntry.Get(TempChildChangeGlobalDimLogEntry."Table ID") then begin
                    RecRefIndex += 1;
                    InitDependentRecNo(ChangeGlobalDimLogEntry."Table ID", RecRefIndex, ChangeGlobalDimLogEntry."Completed Records");
                    DependentRecRef[RecRefIndex].Open(ChangeGlobalDimLogEntry."Table ID");
                    DependentRecRef[RecRefIndex].LockTable(true);
                    ChangeGlobalDimLogEntry."Total Records" := DependentRecRef[RecRefIndex].Count();
                    ChangeGlobalDimLogEntry."Session ID" := SessionId();
                    ChangeGlobalDimLogEntry."Server Instance ID" := ServiceInstanceId();
                    ChangeGlobalDimLogEntry.Modify();
                    TotalRecords += ChangeGlobalDimLogEntry."Total Records";
                end;
            until TempChildChangeGlobalDimLogEntry.Next() = 0;
        if TotalRecords > 0 then begin
            ChangeGlobalDimLogEntry.Reset();
            ChangeGlobalDimLogEntry.SetRange("Parent Table ID", ChangeGlobalDimLogEntry."Parent Table ID");
            exit(ChangeGlobalDimLogEntry.Findset());
        end;
    end;

    /// <summary>
    /// Finds tables available for scheduling in parallel processing mode.
    /// Identifies root tables with records requiring dimension updates for job queue scheduling.
    /// </summary>
    /// <param name="ChangeGlobalDimLogEntry">Record to populate with tables available for scheduling</param>
    /// <returns>True if tables are found for scheduling, false otherwise</returns>
    procedure FindTablesForScheduling(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"): Boolean
    begin
        ChangeGlobalDimLogEntry.SetRange("Parent Table ID", 0);
        ChangeGlobalDimLogEntry.SetFilter("Total Records", '>0');
        exit(ChangeGlobalDimLogEntry.FindSet(true))
    end;

    local procedure GetMinCommitSize(): Integer
    begin
        // number of records that should be modified between COMMIT calls
        exit(10);
    end;

    local procedure CalcRecordsWithinCommit(TotalRecords: Integer) RecordsWithinCommit: Integer
    begin
        RecordsWithinCommit := Round(TotalRecords / 100, 1, '>');
        if RecordsWithinCommit < GetMinCommitSize() then
            RecordsWithinCommit := GetMinCommitSize();
    end;

    local procedure ChangeDimsOnTable(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry") Completed: Boolean
    var
        DependentChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
        RecRef: RecordRef;
        DependentRecRef: array[7] of RecordRef;
        CurrentRecNo: Integer;
        DependentRecNo: Integer;
        RecordsWithinCommit: Integer;
        StartedFromRecord: Integer;
        HasDependentTable: Boolean;
        DependentEntryCompleted: Boolean;
    begin
        RecRef.Open(ChangeGlobalDimLogEntry."Table ID");
        RecRef.LockTable(true);
        if not RecRef.IsEmpty() then begin
            Clear(DepRecNo);
            CurrentRecNo := ChangeGlobalDimLogEntry."Completed Records";
            StartedFromRecord := CurrentRecNo;
            ChangeGlobalDimLogEntry."Total Records" := RecRef.Count();
            OnChangeDimsOnTableOnBeforeCalcRecordsWithinCommit(ChangeGlobalDimLogEntry, ChangeGlobalDimLogMgt, ChangeGlobalDimHeader, Completed, StartedFromRecord, RecRef);
            RecordsWithinCommit := CalcRecordsWithinCommit(ChangeGlobalDimLogEntry."Total Records");
            if RecRef.FindSet(true) then begin
                HasDependentTable := FindDependentTables(DependentChangeGlobalDimLogEntry, ChangeGlobalDimLogEntry, DependentRecRef);
                if HasDependentTable then
                    repeat
                        DependentChangeGlobalDimLogEntry."Earliest Start Date/Time" := CurrentDateTime;
                        DependentChangeGlobalDimLogEntry.SetSessionInProgress();
                    until DependentChangeGlobalDimLogEntry.Next() = 0;
                if ChangeGlobalDimLogEntry."Completed Records" > 0 then
                    RecRef.Next(ChangeGlobalDimLogEntry."Completed Records");
                ChangeGlobalDimLogEntry."Earliest Start Date/Time" := CurrentDateTime;
                repeat
                    ChangeDimsOnRecord(ChangeGlobalDimLogEntry, RecRef);
                    CurrentRecNo += 1;

                    if HasDependentTable then
                        if DependentChangeGlobalDimLogEntry.FindSet() then
                            repeat
                                if DependentChangeGlobalDimLogEntry."Total Records" > 0 then
                                    ChangeDependentRecords(
                                        ChangeGlobalDimLogEntry, DependentChangeGlobalDimLogEntry,
                                        RecRef, DependentRecRef[GetDependentRecNo(DependentChangeGlobalDimLogEntry."Table ID", 1)]);
                            until DependentChangeGlobalDimLogEntry.Next() = 0;

                    if CurrentRecNo >= (ChangeGlobalDimLogEntry."Completed Records" + RecordsWithinCommit) then begin
                        if HasDependentTable then
                            if DependentChangeGlobalDimLogEntry.FindSet() then
                                repeat
                                    DependentRecNo := GetDependentRecNo(DependentChangeGlobalDimLogEntry."Table ID", 3);
                                    DependentChangeGlobalDimLogEntry.Update(DependentRecNo, GetDependentRecNo(DependentChangeGlobalDimLogEntry."Table ID", 2));
                                    Completed := UpdateWithCommit(ChangeGlobalDimLogEntry, CurrentRecNo, StartedFromRecord);
                                    if DependentRecNo > 0 then
                                        DependentRecRef[GetDependentRecNo(DependentChangeGlobalDimLogEntry."Table ID", 1)].LockTable();
                                until DependentChangeGlobalDimLogEntry.Next() = 0;
                        RecRef.LockTable();
                    end;
                    if IsWindowOpen then begin
                        CurrRecord += 1;
                        if CurrRecord mod Round(NoOfRecords / 100, 1, '>') = 1 then
                            Window.Update(2, Round(CurrRecord / NoOfRecords * 10000, 1));
                    end;
                until RecRef.Next() = 0;
            end;
            if HasDependentTable then
                if DependentChangeGlobalDimLogEntry.FindSet() then
                    repeat
                        DependentRecNo := GetDependentRecNo(DependentChangeGlobalDimLogEntry."Table ID", 3);
                        if DependentRecNo > 0 then begin
                            DependentRecRef[GetDependentRecNo(DependentChangeGlobalDimLogEntry."Table ID", 1)].Close();
                            DependentChangeGlobalDimLogEntry.Update(DependentRecNo, GetDependentRecNo(DependentChangeGlobalDimLogEntry."Table ID", 2));
                            if DependentChangeGlobalDimLogEntry.Status = DependentChangeGlobalDimLogEntry.Status::Completed then
                                DependentEntryCompleted := DeleteEntry(DependentChangeGlobalDimLogEntry);
                            if not DependentEntryCompleted then begin
                                DependentChangeGlobalDimLogEntry.Update(0, 0);
                                CurrentRecNo := 0; // set the parent to Incomplete
                            end;
                        end;
                    until DependentChangeGlobalDimLogEntry.Next() = 0;
            Completed := UpdateWithCommit(ChangeGlobalDimLogEntry, CurrentRecNo, StartedFromRecord);
        end;
        RecRef.Close();
    end;

    /// <summary>
    /// Retrieves the record number for a dependent table at the specified index.
    /// Returns the record number from the internal tracking collection for dependency management.
    /// </summary>
    /// <param name="TableId">Table identifier for the dependent table</param>
    /// <param name="Index">Index position in the dependent record list</param>
    /// <returns>Record number at the specified index position</returns>
    local procedure GetDependentRecNo(TableId: Integer; Index: Integer) RecNo: Integer;
    var
        RecNoList: List of [Integer];
    begin
        RecNoList := DepRecNo.Get(TableID);
        RecNo := RecNoList.Get(Index);
    end;

    local procedure InitDependentRecNo(TableId: Integer; RecRefIndex: Integer; RecNo: Integer)
    var
        RecNoList: List of [Integer];
    begin
        RecNoList.Add(RecRefIndex);
        RecNoList.Add(RecNo);
        RecNoList.Add(RecNo);
        DepRecNo.Add(TableId, RecNoList);
    end;

    local procedure SetDependentRecNo(TableId: Integer; Index: Integer; RecNo: Integer)
    var
        RecNoList: List of [Integer];
    begin
        RecNoList := DepRecNo.Get(TableID);
        RecNoList.Set(Index, RecNo);
        DepRecNo.Set(TableID, RecNoList);
    end;

    local procedure ChangeDimsOnRecord(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; var RecRef: RecordRef) Success: Boolean
    var
        GlobalDimFieldRef: array[2] of FieldRef;
        OldDimValueCode: array[2] of Code[20];
        IsHandled: Boolean;
    begin
        if (ChangeGlobalDimLogEntry."Change Type 1" = ChangeGlobalDimLogEntry."Change Type 1"::None) and (ChangeGlobalDimLogEntry."Change Type 2" = ChangeGlobalDimLogEntry."Change Type 2"::None) then
            exit(false);

        OnChangeDimsOnRecord(ChangeGlobalDimLogEntry, RecRef, IsHandled, Success);
        if IsHandled then
            exit(Success);

        if (ChangeGlobalDimLogEntry."Change Type 1" = ChangeGlobalDimLogEntry."Change Type 1"::Replace) and (ChangeGlobalDimLogEntry."Global Dim.2 Field No." = 0) then
            ChangeGlobalDimLogEntry."Change Type 1" := ChangeGlobalDimLogEntry."Change Type 1"::New;
        if (ChangeGlobalDimLogEntry."Change Type 2" = ChangeGlobalDimLogEntry."Change Type 2"::Replace) and (ChangeGlobalDimLogEntry."Global Dim.1 Field No." = 0) then
            ChangeGlobalDimLogEntry."Change Type 2" := ChangeGlobalDimLogEntry."Change Type 2"::New;

        if ChangeGlobalDimLogEntry."Global Dim.1 Field No." = 0 then
            ChangeGlobalDimLogEntry."Change Type 1" := ChangeGlobalDimLogEntry."Change Type 1"::None;
        if ChangeGlobalDimLogEntry."Global Dim.2 Field No." = 0 then
            ChangeGlobalDimLogEntry."Change Type 2" := ChangeGlobalDimLogEntry."Change Type 2"::None;

        ChangeGlobalDimLogEntry.GetFieldRefValues(RecRef, GlobalDimFieldRef, OldDimValueCode);
        ChangeGlobalDimLogEntry.ChangeDimOnRecord(RecRef, 1, GlobalDimFieldRef[1], OldDimValueCode[2]);
        ChangeGlobalDimLogEntry.ChangeDimOnRecord(RecRef, 2, GlobalDimFieldRef[2], OldDimValueCode[1]);
        Success := RecRef.Modify();
    end;

    local procedure ChangeDependentRecords(ParentChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; ParentRecRef: RecordRef; var RecRef: RecordRef)
    var
        GlobalDimFieldRef: array[2] of FieldRef;
        ParentKeyFieldRef: FieldRef;
        ParentDimValueCode: array[2] of Code[20];
        DimValueCode: array[2] of Code[20];
        ParentKeyValue: Variant;
        CurrentRecNo: Integer;
        IsHandled: Boolean;
    begin
        CurrentRecNo := GetDependentRecNo(ChangeGlobalDimLogEntry."Table ID", 3);

        ParentChangeGlobalDimLogEntry.GetFieldRefValues(ParentRecRef, GlobalDimFieldRef, ParentDimValueCode);
        ChangeGlobalDimLogEntry.GetPrimaryKeyFieldRef(ParentRecRef, ParentKeyFieldRef);
        ParentKeyValue := ParentKeyFieldRef.Value();

        ParentKeyFieldRef := RecRef.Field(2);
        ParentKeyFieldRef.SetRange(ParentKeyValue);
        if RecRef.FindSet(true) then
            repeat
                OnChangeDependentRecords(ChangeGlobalDimLogEntry, RecRef, IsHandled);
                if not IsHandled then begin
                    ChangeGlobalDimLogEntry.GetFieldRefValues(RecRef, GlobalDimFieldRef, DimValueCode);
                    GlobalDimFieldRef[1].Value(ParentDimValueCode[1]);
                    GlobalDimFieldRef[2].Value(ParentDimValueCode[2]);
                    RecRef.Modify();
                    CurrentRecNo += 1;
                end;
            until RecRef.Next() = 0;

        SetDependentRecNo(ChangeGlobalDimLogEntry."Table ID", 3, CurrentRecNo);
    end;

    local procedure RerunEntry(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry")
    begin
        if ChangeGlobalDimLogEntry.Status in [ChangeGlobalDimLogEntry.Status::" ", ChangeGlobalDimLogEntry.Status::Incomplete, ChangeGlobalDimLogEntry.Status::Scheduled] then begin
            ChangeGlobalDimLogEntry.SendTraceTagOnRerun();
            if ChangeGlobalDimLogEntry."Parent Table ID" <> 0 then
                RescheduleParentTable(ChangeGlobalDimLogEntry."Parent Table ID")
            else
                ScheduleJobForTable(ChangeGlobalDimLogEntry, CurrentDateTime + 2000);
        end;
    end;

    local procedure RescheduleParentTable(ParentTableID: Integer)
    var
        ParentChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
    begin
        ParentChangeGlobalDimLogEntry.Get(ParentTableID);
        ParentChangeGlobalDimLogEntry.Validate("Completed Records", 0);
        ScheduleJobForTable(ParentChangeGlobalDimLogEntry, CurrentDateTime + 100);
    end;

    local procedure ScheduleJobs(DeltaMsec: Integer)
    var
        ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
        StartTime: DateTime;
    begin
        if FindTablesForScheduling(ChangeGlobalDimLogEntry) then begin
            StartTime := CurrentDateTime + 1000;
            repeat
                StartTime += DeltaMsec;
                ScheduleJobForTable(ChangeGlobalDimLogEntry, StartTime);
            until ChangeGlobalDimLogEntry.Next() = 0;
        end;
    end;

    local procedure ScheduleJobForTable(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; StartNotBefore: DateTime)
    var
        DoNotScheduleTask: Boolean;
        TaskID: Guid;
    begin
        OnBeforeScheduleTask(ChangeGlobalDimLogEntry."Table ID", DoNotScheduleTask, TaskID);
        if DoNotScheduleTask then
            ChangeGlobalDimLogEntry."Task ID" := TaskID
        else begin
            ChangeGlobalDimLogEntry.CancelTask();
            ChangeGlobalDimLogEntry."Task ID" :=
              TASKSCHEDULER.CreateTask(
                CODEUNIT::"Change Global Dimensions", CODEUNIT::"Change Global Dim Err. Handler",
                true, CompanyName, StartNotBefore, ChangeGlobalDimLogEntry.RecordId);
        end;
        if IsNullGuid(ChangeGlobalDimLogEntry."Task ID") then
            ChangeGlobalDimLogEntry.Status := ChangeGlobalDimLogEntry.Status::" "
        else
            ChangeGlobalDimLogEntry.Status := ChangeGlobalDimLogEntry.Status::Scheduled;
        ChangeGlobalDimLogEntry."Earliest Start Date/Time" := StartNotBefore;
        ChangeGlobalDimLogEntry.Modify();
        ChangeGlobalDimLogEntry.SendTraceTagOnScheduling();
        if ChangeGlobalDimLogEntry."Is Parent Table" then
            ScheduleDependentTables(ChangeGlobalDimLogEntry);
    end;

    local procedure ScheduleDependentTables(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry")
    var
        DependentChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
    begin
        DependentChangeGlobalDimLogEntry.SetRange("Parent Table ID", ChangeGlobalDimLogEntry."Table ID");
        if DependentChangeGlobalDimLogEntry.FindSet() then
            repeat
                DependentChangeGlobalDimLogEntry."Task ID" := ChangeGlobalDimLogEntry."Task ID";
                DependentChangeGlobalDimLogEntry.Validate("Completed Records", 0);
                DependentChangeGlobalDimLogEntry.Status := ChangeGlobalDimLogEntry.Status;
                DependentChangeGlobalDimLogEntry.Modify();
            until DependentChangeGlobalDimLogEntry.Next() = 0;
    end;

    local procedure IsCurrentSessionActiveOnly() Result: Boolean
    var
        ActiveSession: Record "Active Session";
    begin
        OnCountingActiveSessions(Result);
        if Result then
            exit(true);
        // Ignore session types: Web Service,Client Service,NAS,Management Client
        ActiveSession.SetFilter(
          "Client Type", '<>%1&<>%2&<>%3&<>%4',
          ActiveSession."Client Type"::"Web Service", ActiveSession."Client Type"::"Client Service",
          ActiveSession."Client Type"::NAS, ActiveSession."Client Type"::"Management Client");
        ActiveSession.SetFilter("Session ID", '<>%1', SessionId());
        ActiveSession.SetRange("Server Instance ID", ServiceInstanceId());
        exit(ActiveSession.IsEmpty);
    end;

    /// <summary>
    /// Determines if dimension code configuration is enabled for modification.
    /// Checks buffer state to allow dimension code changes when no operations are in progress.
    /// </summary>
    /// <returns>True if dimension codes can be modified, false if operations are in progress</returns>
    procedure IsDimCodeEnabled(): Boolean
    begin
        exit(ChangeGlobalDimLogMgt.IsBufferClear());
    end;

    /// <summary>
    /// Determines if the preparation phase can be initiated for dimension changes.
    /// Validates configuration and system state to enable dimension change preparation.
    /// </summary>
    /// <param name="ChangeGlobalDimHeader">Header record containing dimension change configuration</param>
    /// <returns>True if preparation can proceed, false if prerequisites are not met</returns>
    procedure IsPrepareEnabled(var ChangeGlobalDimHeader: Record "Change Global Dim. Header"): Boolean
    begin
        exit(
              ((ChangeGlobalDimHeader."Change Type 1" <> ChangeGlobalDimHeader."Change Type 1"::None) or (ChangeGlobalDimHeader."Change Type 2" <> ChangeGlobalDimHeader."Change Type 2"::None)) and
              ChangeGlobalDimLogMgt.IsBufferClear());
    end;

    /// <summary>
    /// Determines if the dimension change process can be started.
    /// Validates that preparation is complete and no processing is currently active.
    /// </summary>
    /// <returns>True if dimension change can be started, false if prerequisites are not met</returns>
    procedure IsStartEnabled(): Boolean
    begin
        if ChangeGlobalDimLogMgt.IsBufferClear() then
            exit(false);
        exit(not ChangeGlobalDimLogMgt.IsStarted());
    end;

    /// <summary>
    /// Refreshes the global dimension change header with current status and progress information.
    /// Updates header record with latest processing state and statistical data.
    /// </summary>
    procedure RefreshHeader()
    begin
        if ChangeGlobalDimHeader.Get() then begin
            ChangeGlobalDimHeader.Refresh();
            ChangeGlobalDimHeader.Modify();
        end else begin
            ChangeGlobalDimHeader.Refresh();
            ChangeGlobalDimHeader.Insert();
        end
    end;

    /// <summary>
    /// Configures parallel processing mode for global dimension changes.
    /// Enables or disables parallel execution based on system capacity and processing requirements.
    /// </summary>
    /// <param name="NewParallelProcessing">True to enable parallel processing, false for sequential processing</param>
    procedure SetParallelProcessing(NewParallelProcessing: Boolean)
    begin
        ChangeGlobalDimHeader.Get();
        ChangeGlobalDimHeader."Parallel Processing" := NewParallelProcessing;
        ChangeGlobalDimHeader.Modify();
    end;

    /// <summary>
    /// Initializes the table list for dimension change operations with dependency analysis.
    /// Builds comprehensive list of tables requiring dimension updates with parent-child relationships.
    /// </summary>
    /// <returns>True if table list initialization succeeded, false if no tables require processing</returns>
    procedure InitTableList(): Boolean
    var
        ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry";
        TempAllObjWithCaption: Record AllObjWithCaption temporary;
        TempParentTableInteger: Record "Integer" temporary;
        TotalRecords: Integer;
    begin
        TotalRecords := 0;
        ChangeGlobalDimHeader.Get();
        ChangeGlobalDimLogEntry.LockTable();
        ChangeGlobalDimLogEntry.DeleteAll(true);
        if FindTablesWithDims(TempAllObjWithCaption) then begin
            OnInitTableListOnAfterFindTablesWithDims(ChangeGlobalDimHeader);
            repeat
                ChangeGlobalDimLogEntry.Init();
                ChangeGlobalDimLogEntry."Table ID" := TempAllObjWithCaption."Object ID";
                ChangeGlobalDimLogEntry."Table Name" := TempAllObjWithCaption."Object Name";
                ChangeGlobalDimLogEntry."Change Type 1" := ChangeGlobalDimHeader."Change Type 1";
                ChangeGlobalDimLogEntry."Change Type 2" := ChangeGlobalDimHeader."Change Type 2";
                FillTableData(ChangeGlobalDimLogEntry);
                TotalRecords += ChangeGlobalDimLogEntry."Total Records";
                TempParentTableInteger.Number := ChangeGlobalDimLogEntry."Parent Table ID";
                if TempParentTableInteger.Number <> 0 then
                    if TempParentTableInteger.Insert() then;
                OnInitTableListOnBeforeInsertChangeGlobalDimLogEntry(ChangeGlobalDimHeader, ChangeGlobalDimLogEntry);
                ChangeGlobalDimLogEntry.Insert();
            until TempAllObjWithCaption.Next() = 0;

            if TempParentTableInteger.FindSet() then
                repeat
                    if ChangeGlobalDimLogEntry.Get(TempParentTableInteger.Number) then begin
                        ChangeGlobalDimLogEntry."Is Parent Table" := true;
                        ChangeGlobalDimLogEntry.Modify();
                    end;
                until TempParentTableInteger.Next() = 0;
        end;
        if TotalRecords = 0 then
            ChangeGlobalDimLogEntry.DeleteAll(true);
        ChangeGlobalDimLogMgt.FillBuffer();
        exit(TotalRecords <> 0);
    end;

    local procedure TestDirectModifyPermission(var RecRef: RecordRef)
    var
        IsHandled: Boolean;
    begin
        OnBeforeTestDirectModifyPermission(RecRef, IsHandled);
        if IsHandled then
            exit;

        if RecRef.FindFirst() then
            RecRef.Modify();
    end;

    local procedure DeleteEntry(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"): Boolean
    begin
        if ChangeGlobalDimLogEntry.Delete() then begin
            ChangeGlobalDimLogMgt.ExcludeTable(ChangeGlobalDimLogEntry."Table ID");
            exit(true);
        end
    end;

    local procedure FillTableData(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry")
    var
        RecRef: RecordRef;
    begin
        RecRef.Open(ChangeGlobalDimLogEntry."Table ID");
        TestDirectModifyPermission(RecRef);
        ChangeGlobalDimLogEntry.FillData(RecRef);
        RecRef.Close();
        OnAfterFillTableData(ChangeGlobalDimLogEntry);
    end;

    local procedure FindTablesWithDims(var TempAllObjWithCaption: Record AllObjWithCaption temporary): Boolean
    var
        DimensionManagement: Codeunit DimensionManagement;
    begin
        DimensionManagement.DefaultDimObjectNoWithGlobalDimsList(TempAllObjWithCaption);
        DimensionManagement.GlobalDimObjectNoList(TempAllObjWithCaption);
        DimensionManagement.JobTaskDimObjectNoList(TempAllObjWithCaption);
        OnAfterGetObjectNoList(TempAllObjWithCaption);
        exit(TempAllObjWithCaption.FindSet());
    end;

    local procedure UpdateGLSetup()
    begin
        GeneralLedgerSetup.Get();
        GeneralLedgerSetup.Validate("Global Dimension 1 Code", ChangeGlobalDimHeader."Global Dimension 1 Code");
        GeneralLedgerSetup.Validate("Global Dimension 2 Code", ChangeGlobalDimHeader."Global Dimension 2 Code");
        GeneralLedgerSetup.Modify(true);

        UpdateDimValues();
        if ChangeGlobalDimHeader."Parallel Processing" then
            Commit();
    end;

    local procedure UpdateDimValues()
    var
        DimensionValue: Record "Dimension Value";
    begin
        DimensionValue.SetCurrentKey(Code, "Global Dimension No.");
        DimensionValue.SetRange("Global Dimension No.", 1, 2);
        DimensionValue.ModifyAll("Global Dimension No.", 0);
        DimensionValue.Reset();
        if ChangeGlobalDimHeader."Global Dimension 1 Code" <> '' then begin
            DimensionValue.SetRange("Dimension Code", ChangeGlobalDimHeader."Global Dimension 1 Code");
            DimensionValue.ModifyAll("Global Dimension No.", 1);
        end;
        if ChangeGlobalDimHeader."Global Dimension 2 Code" <> '' then begin
            DimensionValue.SetRange("Dimension Code", ChangeGlobalDimHeader."Global Dimension 2 Code");
            DimensionValue.ModifyAll("Global Dimension No.", 2);
        end;
    end;

    /// <summary>
    /// Gets the unique identifier for close sessions notification management.
    /// Returns consistent notification ID for session closure messaging during dimension changes.
    /// </summary>
    /// <param name="Id">Notification identifier for close sessions notification</param>
    procedure GetCloseSessionsNotificationID() Id: Guid
    begin
        Evaluate(Id, CloseSessionNotificationTok);
    end;

    local procedure PrepareNotification(var Notification: Notification; ID: Guid; Msg: Text)
    begin
        Notification.Id(ID);
        Notification.Recall();
        Notification.Message(Msg);
        Notification.Scope(NOTIFICATIONSCOPE::LocalScope);
    end;

    local procedure SendCloseSessionsNotification()
    var
        Notification: Notification;
    begin
        PrepareNotification(Notification, GetCloseSessionsNotificationID(), CloseActiveSessionsMsg);
        Notification.AddAction(SessionListActionTxt, CODEUNIT::"Change Global Dimensions", 'ShowActiveSessions');
        Notification.Send();
    end;

    local procedure SendTraceTagOn(TraceTagMessage: Text)
    begin
        Session.LogMessage('00001ZE', TraceTagMessage, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TagCategoryTxt);
    end;

    /// <summary>
    /// Displays the active sessions list for review during dimension change operations.
    /// Shows current active sessions that may interfere with dimension change processing.
    /// </summary>
    /// <param name="BlockNotification">Notification object triggering the session display</param>
    procedure ShowActiveSessions(BlockNotification: Notification)
    begin
        PAGE.Run(PAGE::"Concurrent Session List");
    end;

    /// <summary>
    /// Integration event raised after building the object list for dimension change operations.
    /// Enables customization of table list used for dimension processing.
    /// </summary>
    /// <param name="TempAllObjWithCaption">Temporary record containing objects for dimension processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterGetObjectNoList(var TempAllObjWithCaption: Record AllObjWithCaption temporary)
    begin
    end;

    /// <summary>
    /// Integration event raised when counting active sessions during dimension change operations.
    /// Enables custom logic for determining if only current session is active.
    /// </summary>
    /// <param name="IsCurrSessionActiveOnly">Set to true if only current session should be considered active</param>
    [IntegrationEvent(false, false)]
    local procedure OnCountingActiveSessions(var IsCurrSessionActiveOnly: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before scheduling task for parallel dimension change processing.
    /// Enables custom control over task scheduling and task identification.
    /// </summary>
    /// <param name="TableNo">Table number for which task is being scheduled</param>
    /// <param name="DoNotScheduleTask">Set to true to prevent scheduling of this task</param>
    /// <param name="TaskID">Task identifier for the scheduled dimension change operation</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeScheduleTask(TableNo: Integer; var DoNotScheduleTask: Boolean; var TaskID: Guid)
    begin
    end;

    local procedure UpdateWithCommit(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; CurrentRecNo: Integer; StartedFromRecord: Integer): Boolean
    begin
        if ChangeGlobalDimHeader."Parallel Processing" then
            exit(ChangeGlobalDimLogEntry.UpdateWithCommit(CurrentRecNo, StartedFromRecord));
        exit(ChangeGlobalDimLogEntry.UpdateWithoutCommit(CurrentRecNo, StartedFromRecord));
    end;

    local procedure WindowOpen()
    begin
        if GuiAllowed then begin
            Window.Open(ProgressMsg);
            IsWindowOpen := true;
        end;
    end;

    local procedure WindowClose()
    begin
        if IsWindowOpen then begin
            Window.Close();
            IsWindowOpen := false;

            Message(SessionUpdateRequiredMsg);
        end;
    end;

    /// <summary>
    /// Integration event raised before testing direct modify permission on table records.
    /// Enables custom permission checking logic for dimension change operations.
    /// </summary>
    /// <param name="RecRef">Record reference for permission testing</param>
    /// <param name="IsHandled">Set to true to bypass standard permission checking</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeTestDirectModifyPermission(var RecRef: RecordRef; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised when changing dependent records during dimension update operations.
    /// Enables custom processing of dependent table records during dimension changes.
    /// </summary>
    /// <param name="ChangeGlobalDimLogEntry">Log entry containing dimension change context</param>
    /// <param name="RecRef">Record reference for dependent record processing</param>
    /// <param name="IsHandled">Set to true to bypass standard dependent record processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnChangeDependentRecords(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; var RecRef: RecordRef; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised when changing dimensions on individual records.
    /// Enables custom dimension update logic for specific record types during dimension changes.
    /// </summary>
    /// <param name="ChangeGlobalDimLogEntry">Log entry containing dimension change context</param>
    /// <param name="RecRef">Record reference for dimension updates</param>
    /// <param name="IsHandled">Set to true to bypass standard dimension change processing</param>
    /// <param name="Success">Set to indicate success or failure of custom dimension processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnChangeDimsOnRecord(ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; var RecRef: RecordRef; var IsHandled: Boolean; var Success: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised after filling table data for dimension change operations.
    /// Enables post-processing of table data after initial dimension change setup.
    /// </summary>
    /// <param name="ChangeGlobalDimLogEntry">Log entry record after table data initialization</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterFillTableData(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnChangeDimsOnTableOnBeforeCalcRecordsWithinCommit(var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry"; var ChangeGlobalDimLogMgt: Codeunit "Change Global Dim. Log Mgt."; var ChangeGlobalDimHeader: Record "Change Global Dim. Header"; var Completed: Boolean; var StartedFromRecord: Integer; var RecRef: RecordRef)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnInitTableListOnAfterFindTablesWithDims(var ChangeGlobalDimHeader: Record "Change Global Dim. Header")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnInitTableListOnBeforeInsertChangeGlobalDimLogEntry(var ChangeGlobalDimHeader: Record "Change Global Dim. Header"; var ChangeGlobalDimLogEntry: Record "Change Global Dim. Log Entry")
    begin
    end;
}