Codeunit 2580 Dimension Correction Mgt, source in 29

Source29

src/Layers/W1/BaseApp/Finance/Dimension/Correction/DimensionCorrectionMgt.Codeunit.al1695 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.Correction;

using Microsoft.CostAccounting.Setup;
using Microsoft.Finance.Dimension;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Ledger;
using System.Environment.Configuration;
using System.Text;
using System.Threading;
using System.Utilities;

/// <summary>
/// Manages dimension correction operations including validation, execution, and supporting data generation.
/// Provides comprehensive functionality for correcting dimension errors in posted general ledger entries
/// with validation, job scheduling, and status management capabilities.
/// </summary>
codeunit 2580 "Dimension Correction Mgt"
{
    trigger OnRun()
    begin

    end;

    /// <summary>
    /// Validates that no blocked dimensions are used in the dimension correction changes.
    /// Checks all changes to ensure dimensions and dimension values are not blocked for use.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to validate</param>
    procedure ValidateNoBlockedDimensionsUsed(var DimensionCorrection: Record "Dimension Correction")
    var
        DimCorrectionChange: Record "Dim Correction Change";
    begin
        DimCorrectionChange.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
        DimCorrectionChange.SetFilter("Change Type", '<>%1', DimCorrectionChange."Change Type"::"No Change");
        if not DimCorrectionChange.FindSet() then
            exit;

        repeat
            VerifyIfDimensionCanBeChanged(DimCorrectionChange);
        until DimCorrectionChange.Next() = 0;
    end;

    /// <summary>
    /// Generates supporting tables and data structures for dimension correction processing.
    /// Creates target dimension set IDs and prepares temporary buffers for validation and execution.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to generate data for</param>
    /// <param name="TempDimCorrectionSetBuffer">Temporary buffer to populate with correction set data</param>
    procedure GenerateSupportingTables(var DimensionCorrection: Record "Dimension Correction"; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary)
    var
        DimensionCorrectionEntryLog: Record "Dim Correction Entry Log";
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
    begin
        DimCorrectSelectionCriteria.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
        if DimCorrectSelectionCriteria.IsEmpty() then
            Error(NoSelectionCriteriaFoundErr);

        if not DimensionCorrection."Generated Set IDs" then begin
            Session.LogMessage('0000EHF', StrSubstNo(StartingGenerateDimensionSetIdsLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
            GenerateTargetDimensionSetIds(DimensionCorrection);
            Session.LogMessage('0000EHG', StrSubstNo(CompletedGenerateDimensionSetIdsLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
        end;

        if not LoadTempDimCorrectionSetBuffer(DimensionCorrection."Entry No.", TempDimCorrectionSetBuffer) then
            Error(NoChangesFoundErr);

        if not DimensionCorrection."Generated Selected Entries" then begin
            Session.LogMessage('0000EKA', StrSubstNo(StartingGenerateSelectedEntriesLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
            GenerateSelectedEntries(DimensionCorrection, TempDimCorrectionSetBuffer);
            Session.LogMessage('0000EKB', StrSubstNo(CompletedGenerateSelectedEntriesLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
        end;

        DimensionCorrectionEntryLog.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
        if DimensionCorrectionEntryLog.IsEmpty() then
            Error(NoLedgerEntriesFoundErr);
    end;

    /// <summary>
    /// Verifies that the dimension correction can be validated based on its current status.
    /// Checks if the dimension correction is in a valid state for validation processing.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to verify</param>
    procedure VerifyCanValidateDimensionCorrection(var DimensionCorrection: Record "Dimension Correction")
    begin
        if not (DimensionCorrection.Status in [DimensionCorrection.Status::Completed, DimensionCorrection.Status::Draft, DimensionCorrection.Status::Failed, DimensionCorrection.Status::"Validaton in Process"]) then
            Error(CannotValidateDimensionCorrectionErr, DimensionCorrection.Status);
    end;

    /// <summary>
    /// Validates that blocked dimensions and dimension values are not used in the correction.
    /// Performs validation with telemetry logging to ensure data integrity.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to validate</param>
    procedure ValidateBlockedNotUsed(var DimensionCorrection: Record "Dimension Correction")
    begin
        Session.LogMessage('0000EHD', StrSubstNo(StartingValidateDimensionCorrectionChangesTelemetryLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
        ValidateNoBlockedDimensionsUsed(DimensionCorrection);
        Session.LogMessage('0000EHE', StrSubstNo(CompletedValidateDimensionCorrectionChangesTelemetryLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
    end;

    /// <summary>
    /// Validates dimension sets for the correction to ensure dimension combination rules are followed.
    /// Checks dimension combinations and restrictions, counting any validation errors encountered.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to validate</param>
    /// <param name="TempDimCorrectionSetBuffer">Temporary buffer containing dimension sets to validate</param>
    /// <param name="ErrorCount">Variable to receive the count of validation errors found</param>
    procedure ValidateDimensionSets(var DimensionCorrection: Record "Dimension Correction"; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary; var ErrorCount: Integer)
    var
        DimensionManagement: Codeunit DimensionManagement;
        ErrorMessageManagement: Codeunit "Error Message Management";
        ErrorMessage: Text[250];
    begin
        if DimensionCorrection.Status = DimensionCorrection.Status::"Validaton in Process" then
            DimensionManagement.SetCollectErrorsMode();

        TempDimCorrectionSetBuffer.FindSet();
        repeat
            if not DimensionManagement.CheckDimIDComb(TempDimCorrectionSetBuffer."Target Set ID") then
                if DimensionCorrection.Status <> DimensionCorrection.Status::"Validaton in Process" then begin
                    Commit();
                    ErrorMessageManagement.GetLastError(ErrorMessage);
                    Error(ErrorMessage);
                end else begin
                    ErrorCount += 1;
                    if ErrorCount > GetMaximumNumberOfValidationErrors() then
                        UpdateValidationStatusAndThrowErrorIfFailed(DimensionCorrection, ErrorCount);
                end;
        until TempDimCorrectionSetBuffer.Next() = 0;
    end;

    /// <summary>
    /// Validates dimension changes across all selected entries for a dimension correction.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to validate</param>
    /// <param name="TempDimCorrectionSetBuffer">Temporary buffer containing correction set data</param>
    /// <param name="ErrorCount">Returns the number of validation errors encountered</param>
    procedure ValidateDimensionChanges(var DimensionCorrection: Record "Dimension Correction"; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary; var ErrorCount: Integer)
    var
        LastDimCorrectionEntryLog: Record "Dim Correction Entry Log";
        DimCorrectionEntryLog: Record "Dim Correction Entry Log";
        UpdateCounter: Integer;
    begin
        Session.LogMessage('0000EK5', StrSubstNo(StartingValidateDimensionChangesForEntriesLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
        ErrorCount := 0;
        UpdateCounter := 0;

        if DimensionCorrection."Last Validated Entry No." > 0 then begin
            LastDimCorrectionEntryLog.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
            LastDimCorrectionEntryLog.SetFilter("Start Entry No.", '<=%1', DimensionCorrection."Last Validated Entry No.");
            LastDimCorrectionEntryLog.SetFilter("End Entry No.", '>%1', DimensionCorrection."Last Validated Entry No.");
            if LastDimCorrectionEntryLog.FindFirst() then
                ValidateDimensionChanges(DimensionCorrection."Last Validated Entry No." + 1, LastDimCorrectionEntryLog."End Entry No.", UpdateCounter, TempDimCorrectionSetBuffer, DimensionCorrection, ErrorCount);
        end;

        DimCorrectionEntryLog.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
        DimCorrectionEntryLog.SetFilter("Start Entry No.", '>=%1', DimensionCorrection."Last Validated Entry No.");
        DimCorrectionEntryLog.SetCurrentKey("Start Entry No.");
        DimCorrectionEntryLog.Ascending(true);

        if DimCorrectionEntryLog.FindSet() then
            repeat
                ValidateDimensionChanges(DimCorrectionEntryLog."Start Entry No.", DimCorrectionEntryLog."End Entry No.", UpdateCounter, TempDimCorrectionSetBuffer, DimensionCorrection, ErrorCount);
            until DimCorrectionEntryLog.Next() = 0;

        DimensionCorrection."Validated Selected Entries" := true;
        DimensionCorrection.Modify();
        Commit();
        Session.LogMessage('0000EK6', StrSubstNo(CompletedValidateDimensionChangesForEntriesLbl, DimensionCorrection."Entry No."), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
    end;

    local procedure ValidateDimensionChanges(StartEntryNo: Integer; EndEntryNo: Integer; var UpdateCounter: Integer; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary; var DimensionCorrection: Record "Dimension Correction"; var ErrorCount: Integer)
    var
        GLEntry: Record "G/L Entry";
        DimensionManagement: Codeunit DimensionManagement;
        StartDateTime: DateTime;
        TableID: array[10] of Integer;
        No: array[10] of Code[20];
    begin
        StartDateTime := CurrentDateTime();
        GLEntry.SetFilter("Entry No.", '>=%1&<=%2', StartEntryNo, EndEntryNo);

        GLEntry.SetLoadFields("Entry No.", "Dimension Set ID", "G/L Account No.");
        if GLEntry.IsEmpty() then
            exit;
        if DimensionCorrection.Status = DimensionCorrection.Status::"Validaton in Process" then
            DimensionManagement.SetCollectErrorsMode();

        GLEntry.FindSet();
        repeat
            UpdateCounter += 1;

            No[1] := GLEntry."G/L Account No.";
            TableID[1] := Database::"G/L Account";
            TempDimCorrectionSetBuffer.Get(DimensionCorrection."Entry No.", GLEntry."Dimension Set ID");
            if not DimensionManagement.CheckDimValuePosting(TableID, No, TempDimCorrectionSetBuffer."Target Set ID") then
                if DimensionCorrection.Status <> DimensionCorrection.Status::"Validaton in Process" then begin
                    DimensionCorrection."Last Validated Entry No." := GLEntry."Entry No.";
                    DimensionCorrection.Modify();
                    Commit();
                    Error(InvalidDimensionCorrectionErr, GLEntry."Entry No.", DimensionManagement.GetDimValuePostingErr());
                end else begin
                    ErrorCount += 1;
                    if ErrorCount > GetMaximumNumberOfValidationErrors() then
                        UpdateValidationStatusAndThrowErrorIfFailed(DimensionCorrection, ErrorCount);
                end;

            if UpdateCounter >= GetCommitCount() then begin
                UpdateCounter := 0;
                DimensionCorrection."Last Validated Entry No." := GLEntry."Entry No.";
                DimensionCorrection.Modify();
                Commit();
                Session.LogMessage('0000EK7', StrSubstNo(CommitedValidateGLEntriesLbl, DimensionCorrection."Entry No.", Format(CurrentDateTime() - StartDateTime), GetCommitCount()), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
                StartDateTime := CurrentDateTime();
            end;
        until GLEntry.Next() = 0;

        if UpdateCounter > 0 then begin
            DimensionCorrection."Last Validated Entry No." := GLEntry."Entry No.";
            DimensionCorrection.Modify();
            Commit();
        end;
    end;

    /// <summary>
    /// Verifies whether a specific dimension can be changed based on blocking setup.
    /// </summary>
    /// <param name="DimCorrectionChange">Dimension correction change record to verify</param>
    procedure VerifyIfDimensionCanBeChanged(var DimCorrectionChange: Record "Dim Correction Change")
    var
        DimCorrecitonBlocked: Record "Dim Correction Blocked Setup";
    begin
        if DimCorrecitonBlocked.Get(DimCorrectionChange."Dimension Code") then
            Error(CannotChangeDimensionCodeBlockedErr, DimCorrectionChange."Dimension Code");

        OnAfterVerifyIfDimensionCanBeChanged(DimCorrectionChange);
    end;

    /// <summary>
    /// Deletes all validation error messages associated with a dimension correction.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to delete validation errors for</param>
    procedure DeleteValidationErrors(var DimensionCorrection: Record "Dimension Correction")
    var
        ErrorMessage: Record "Error Message";
    begin
        if not IsNullGuid(DimensionCorrection."Validation Errors Register ID") then begin
            ErrorMessage.SetRange("Register ID", DimensionCorrection."Validation Errors Register ID");
            ErrorMessage.DeleteAll();
        end;
        Clear(DimensionCorrection."Validation Errors Register ID");
    end;

    /// <summary>
    /// Updates validation status and throws an error if validation failed.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to update status for</param>
    /// <param name="ErrorCount">Number of validation errors encountered</param>
    procedure UpdateValidationStatusAndThrowErrorIfFailed(var DimensionCorrection: Record "Dimension Correction"; ErrorCount: Integer)
    begin
        DimensionCorrection."Validated At" := CurrentDateTime();
        DimensionCorrection."Validated Selected Entries" := ErrorCount = 0;
        DimensionCorrection.Modify();

        if ErrorCount > 0 then begin
            DimensionCorrection.SetValidateDimensionChangesText(GetValidationStatusText(DimensionCorrection, StrSubstNo(ValidationFailedErr, ErrorCount)));
            DimensionCorrection.Modify();
            Commit();
            Error(ValidationFailedErr, ErrorCount);
        end else begin
            DimensionCorrection.SetValidateDimensionChangesText(GetValidationStatusText(DimensionCorrection, ''));
            DimensionCorrection.Modify();
            Commit();
        end;
    end;

    /// <summary>
    /// Generates selected entries for dimension correction based on selection criteria.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to generate entries for</param>
    /// <param name="TempDimCorrectionSetBuffer">Temporary buffer to store generated correction set data</param>
    procedure GenerateSelectedEntries(var DimensionCorrection: Record "Dimension Correction"; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary)
    var
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        TempExcludedEntriesInteger: Record Integer temporary;
    begin
        if not GetSelectionCriteria(DimensionCorrection."Entry No.", DimCorrectSelectionCriteria) then
            exit;

        LoadExcludedEntries(DimensionCorrection, TempExcludedEntriesInteger);

        repeat
            GenerateSelectedEntries(DimCorrectSelectionCriteria, DimensionCorrection, TempDimCorrectionSetBuffer, TempExcludedEntriesInteger);
        until DimCorrectSelectionCriteria.Next() = 0;

        DimensionCorrection."Generated Selected Entries" := true;
        DimensionCorrection.Modify();
        Commit();
    end;

    local procedure LoadExcludedEntries(DimensionCorrection: Record "Dimension Correction"; var TempExcludedEntriesInteger: Record Integer temporary)
    var
        ExlcudedDimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        GLEntry: Record "G/L Entry";
        ExcludedEntriesView: Text;
    begin
        ExlcudedDimCorrectSelectionCriteria.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
        ExlcudedDimCorrectSelectionCriteria.SetRange("Filter Type", ExlcudedDimCorrectSelectionCriteria."Filter Type"::Excluded);
        if ExlcudedDimCorrectSelectionCriteria.IsEmpty() then
            exit;

        ExlcudedDimCorrectSelectionCriteria.FindSet();
        GLEntry.SetLoadFields("Entry No.");

        repeat
            ExlcudedDimCorrectSelectionCriteria.GetSelectionFilter(ExcludedEntriesView);
            GLEntry.SetView(ExcludedEntriesView);
            if not GLEntry.IsEmpty() then
                if GLEntry.FindSet() then
                    repeat
                        TempExcludedEntriesInteger.Number := GLEntry."Entry No.";
                        if TempExcludedEntriesInteger.Insert() then;
                    until GLEntry.Next() = 0;
        until ExlcudedDimCorrectSelectionCriteria.Next() = 0;
    end;

    local procedure GenerateSelectedEntries(var DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria"; var DimensionCorrection: Record "Dimension Correction"; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary; var TempExcludedEntriesInteger: Record Integer temporary)
    var
        TempInteger: Record Integer temporary;
        GLEntry: Record "G/L Entry";
        SelectionFilter: Text;
        CommitCounter: Integer;
    begin
        DimCorrectSelectionCriteria.GetSelectionFilter(SelectionFilter);
        if DimCorrectSelectionCriteria."Last Entry No." <> 0 then begin
            GLEntry.FilterGroup(4);
            GLEntry.SetFilter("Entry No.", '>%1', DimCorrectSelectionCriteria."Last Entry No.");
            GLEntry.FilterGroup(0);
        end;

        GLEntry.SetView(SelectionFilter);

        GLEntry.SetLoadFields("Entry No.");
        if GLEntry.IsEmpty() then
            exit;

        GLEntry.FindSet();

        TempInteger.SetCurrentKey(Number);
        TempInteger.Ascending(true);

        repeat
            if IsGLEntryForUpdate(GLEntry, TempDimCorrectionSetBuffer, DimensionCorrection."Entry No.", TempExcludedEntriesInteger) then begin
                if not TempInteger.Get(GLEntry."Entry No.") then begin
                    TempInteger.Number := GLEntry."Entry No.";
                    TempInteger.Insert();
                end;

                CommitCounter += 1;
                if (CommitCounter > GetDictionarySizeLimit()) then begin
                    WriteSelectedEntriesCriteriaToTables(TempInteger, DimensionCorrection);
                    DimCorrectSelectionCriteria."Last Entry No." := GLEntry."Entry No.";
                    TempInteger.DeleteAll();
                    CommitCounter := 0;
                end;
            end;
        until GLEntry.Next() = 0;

        if (CommitCounter > 0) then begin
            WriteSelectedEntriesCriteriaToTables(TempInteger, DimensionCorrection);
            DimCorrectSelectionCriteria."Last Entry No." := GLEntry."Entry No.";
            Commit();
        end;
    end;

    /// <summary>
    /// Determines if a G/L entry should be updated during dimension correction.
    /// </summary>
    /// <param name="GLEntry">G/L entry to check for update eligibility</param>
    /// <param name="TempDimCorrectionSetBuffer">Temporary buffer containing correction set data</param>
    /// <param name="DimensionCorrectionEntryNo">Dimension correction entry number</param>
    /// <param name="TempExcludedEntriesInteger">Temporary buffer containing excluded entry numbers</param>
    /// <returns>True if the G/L entry should be updated, false otherwise</returns>
    procedure IsGLEntryForUpdate(var GLEntry: Record "G/L Entry"; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary; DimensionCorrectionEntryNo: Integer; var TempExcludedEntriesInteger: Record Integer temporary): boolean
    begin
        if not TempDimCorrectionSetBuffer.Get(DimensionCorrectionEntryNo, GLEntry."Dimension Set ID") then
            exit(false);

        if GLEntry."Dimension Set ID" = TempDimCorrectionSetBuffer."Target Set ID" then
            exit(false);

        if TempExcludedEntriesInteger.Get(GLEntry."Entry No.") then
            exit(false);

        exit(true);
    end;

    local procedure WriteSelectedEntriesCriteriaToTables(var TempInteger: Record Integer temporary; var DimensionCorrection: Record "Dimension Correction")
    var
        StartEntryNo: Integer;
        LastEntryNo: Integer;
        AddedEntries: Integer;
    begin
        TempInteger.FindSet();
        StartEntryNo := TempInteger.Number;
        LastEntryNo := TempInteger.Number;

        repeat
            if LastEntryNo + 1 < TempInteger.Number then begin
                AddedEntries += UpdateDimCorrectionEntryLog(StartEntryNo, LastEntryNo, DimensionCorrection."Entry No.");
                StartEntryNo := TempInteger.Number;
            end;

            LastEntryNo := TempInteger.Number;
        until TempInteger.Next() = 0;

        // Add last entry
        AddedEntries += UpdateDimCorrectionEntryLog(StartEntryNo, LastEntryNo, DimensionCorrection."Entry No.");

        DimensionCorrection."Total Selected Ledger Entries" += AddedEntries;
        DimensionCorrection.Modify();
    end;

    local procedure UpdateDimCorrectionEntryLog(StartEntryNo: Integer; LastEntryNo: Integer; DimensionCorrectionEntryNo: Integer): Integer
    var
        PrevoiusDimCorrectionEntryLog: Record "Dim Correction Entry Log";
        NextDimCorrectionEntryLog: Record "Dim Correction Entry Log";
        DimCorrectionEntryLog: Record "Dim Correction Entry Log";
        PreviousExists: Boolean;
        NextExists: Boolean;
        AddedEntries: Integer;
    begin
        AddedEntries := 0;
        PrevoiusDimCorrectionEntryLog.SetRange("Dimension Correction Entry No.", DimensionCorrectionEntryNo);
        PrevoiusDimCorrectionEntryLog.SetFilter("Start Entry No.", '<=%1', StartEntryNo);
        PrevoiusDimCorrectionEntryLog.SetFilter("End Entry No.", '>=%1', StartEntryNo - 1);
        PreviousExists := PrevoiusDimCorrectionEntryLog.FindFirst();

        NextDimCorrectionEntryLog.SetRange("Dimension Correction Entry No.", DimensionCorrectionEntryNo);
        NextDimCorrectionEntryLog.SetFilter("Start Entry No.", '<=%1', LastEntryNo + 1);
        NextDimCorrectionEntryLog.SetFilter("End Entry No.", '>=%1', LastEntryNo);
        NextExists := NextDimCorrectionEntryLog.FindFirst();

        if PreviousExists then begin
            if NextExists then begin
                if (NextDimCorrectionEntryLog."Start Entry No." = PrevoiusDimCorrectionEntryLog."Start Entry No.") and (NextDimCorrectionEntryLog."End Entry No." = PrevoiusDimCorrectionEntryLog."End Entry No.") then
                    exit(AddedEntries);

                AddedEntries := NextDimCorrectionEntryLog."Start Entry No." - PrevoiusDimCorrectionEntryLog."End Entry No." - 1;
                PrevoiusDimCorrectionEntryLog.Rename(PrevoiusDimCorrectionEntryLog."Dimension Correction Entry No.", PrevoiusDimCorrectionEntryLog."Start Entry No.", NextDimCorrectionEntryLog."End Entry No.");
                NextDimCorrectionEntryLog.Delete();
                exit(AddedEntries);
            end;
            if PrevoiusDimCorrectionEntryLog."End Entry No." >= LastEntryNo then
                exit(AddedEntries);

            AddedEntries := LastEntryNo - PrevoiusDimCorrectionEntryLog."End Entry No.";
            PrevoiusDimCorrectionEntryLog.Rename(PrevoiusDimCorrectionEntryLog."Dimension Correction Entry No.", PrevoiusDimCorrectionEntryLog."Start Entry No.", LastEntryNo);
            exit(AddedEntries);
        end;

        if NextExists then begin
            AddedEntries := NextDimCorrectionEntryLog."Start Entry No." - StartEntryNo;
            NextDimCorrectionEntryLog.Rename(NextDimCorrectionEntryLog."Dimension Correction Entry No.", StartEntryNo, NextDimCorrectionEntryLog."End Entry No.");
            exit(AddedEntries)
        end;

        AddedEntries := LastEntryNo - StartEntryNo + 1;
        DimCorrectionEntryLog."Dimension Correction Entry No." := DimensionCorrectionEntryNo;
        DimCorrectionEntryLog."Start Entry No." := StartEntryNo;
        DimCorrectionEntryLog."End Entry No." := LastEntryNo;
        DimCorrectionEntryLog.Insert(true);
        exit(AddedEntries)
    end;

    /// <summary>
    /// Retrieves the target dimension correction set buffer for a specific G/L entry.
    /// </summary>
    /// <param name="TempDimCorrectionSetBuffer">Temporary buffer containing correction set data</param>
    /// <param name="DimensionCorrection">Dimension correction record</param>
    /// <param name="GLEntry">G/L entry to find target correction set for</param>
    /// <returns>True if target correction set buffer found, false otherwise</returns>
    procedure GetTargetDimCorrectionSetBuffer(var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary; var DimensionCorrection: Record "Dimension Correction"; var GLEntry: Record "G/L Entry"): Boolean
    begin
        TempDimCorrectionSetBuffer.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
        TempDimCorrectionSetBuffer.SetRange("Target Set ID", GLEntry."Dimension Set ID");
        if not TempDimCorrectionSetBuffer.FindFirst() then
            exit(false);

        if not TempDimCorrectionSetBuffer."Multiple Target Set ID" then
            exit(true);

        repeat
            if TempDimCorrectionSetBuffer.ContainsLedgerEntry(GLEntry."Entry No.") then
                exit(true);
        until TempDimCorrectionSetBuffer.Next() = 0;
        exit(false);
    end;

    /// <summary>
    /// Generates target dimension set IDs for dimension correction based on correction changes.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to generate target set IDs for</param>
    procedure GenerateTargetDimensionSetIds(var DimensionCorrection: Record "Dimension Correction")
    var
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        DimCorrectionSetBuffer: Record "Dim Correction Set Buffer";
        SameTargetDimCorrectionSetBuffer: Record "Dim Correction Set Buffer";
        TempDimCorrectionChange: Record "Dim Correction Change" temporary;
        DimensionSetIDs: List of [Integer];
        DimensionSetID: Integer;
        NewDimensionSetID: Integer;
        CommitCounter: Integer;
    begin
        if DimensionCorrection."Generated Set IDs" then
            exit;

        if not GetSelectionCriteria(DimensionCorrection."Entry No.", DimCorrectSelectionCriteria) then
            exit;

        if not GetDimCorrectionChanges(DimensionCorrection."Entry No.", TempDimCorrectionChange) then
            exit;

        DimensionCorrection."Started Correction" := true;
        DimensionCorrection.Modify();

        CommitCounter := 0;
        repeat
            DimCorrectSelectionCriteria.GetDimensionSetIds(DimensionSetIDs);
            foreach DimensionSetID in DimensionSetIDs do
                if not DimCorrectionSetBuffer.Get(DimensionCorrection."Entry No.", DimensionSetID) then
                    if TransformDimensionSet(DimensionSetID, TempDimCorrectionChange, NewDimensionSetID) then
                        if (NewDimensionSetID <> DimensionSetID) then begin
                            DimCorrectionSetBuffer."Dimension Correction Entry No." := DimensionCorrection."Entry No.";
                            DimCorrectionSetBuffer."Dimension Set ID" := DimensionSetID;
                            DimCorrectionSetBuffer."Target Set ID" := NewDimensionSetID;
                            SameTargetDimCorrectionSetBuffer.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
                            SameTargetDimCorrectionSetBuffer.SetRange("Target Set ID", DimCorrectionSetBuffer."Target Set ID");
                            if SameTargetDimCorrectionSetBuffer.FindFirst() then begin
                                DimCorrectionSetBuffer."Multiple Target Set ID" := true;
                                if SameTargetDimCorrectionSetBuffer."Multiple Target Set ID" = false then begin
                                    SameTargetDimCorrectionSetBuffer."Multiple Target Set ID" := true;
                                    SameTargetDimCorrectionSetBuffer.Modify();
                                end;
                            end;

                            DimCorrectionSetBuffer.Insert();
                            IncrementAndCommitIfNeeded(CommitCounter);
                        end;
        until DimCorrectSelectionCriteria.Next() = 0;

        DimensionCorrection.GetBySystemId(DimensionCorrection.SystemId);
        DimensionCorrection."Generated Set IDs" := true;
        DimensionCorrection.Modify();
    end;

    /// <summary>
    /// Creates a dimension correction from selected G/L register entries.
    /// </summary>
    /// <param name="GLRegister">G/L register entries to create correction from</param>
    /// <param name="DimensionCorrection">Dimension correction record to populate</param>
    procedure CreateCorrectionFromGLRegister(var GLRegister: Record "G/L Register"; var DimensionCorrection: Record "Dimension Correction")
    var
        LastDimensionCorrection: Record "Dimension Correction";
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        GLEntry: Record "G/L Entry";
        GLEntryRecordRef: RecordRef;
        NewEntryNo: Integer;
    begin
        GLRegister.FindSet();

        NewEntryNo := 1;
        if LastDimensionCorrection.FindLast() then
            NewEntryNo := LastDimensionCorrection."Entry No." + 1;

        DimensionCorrection."Entry No." := NewEntryNo;
        DimensionCorrection.Insert(true);

        repeat
            GLEntry.SetRange("Entry No.", GLRegister."From Entry No.", GLRegister."To Entry No.");
            TransferSelectionFilterToRecordRef(GLEntry, GLEntryRecordRef);
            InsertNewDimCorrectSelectionCriteria(GLEntryRecordRef, DimCorrectSelectionCriteria."Filter Type"::Manual, DimCorrectSelectionCriteria, NewEntryNo);
            Clear(DimCorrectSelectionCriteria);
        until GLRegister.Next() = 0;

        ReloadDimensionChangesTable(NewEntryNo);
    end;

    /// <summary>
    /// Creates a dimension correction from manually selected G/L entries.
    /// </summary>
    /// <param name="GLEntry">G/L entries to create correction from</param>
    /// <param name="DimensionCorrection">Dimension correction record to populate</param>
    procedure CreateCorrectionFromSelection(var GLEntry: Record "G/L Entry"; var DimensionCorrection: Record "Dimension Correction")
    var
        LastDimensionCorrection: Record "Dimension Correction";
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        GLEntryRecordRef: RecordRef;
        NewEntryNo: Integer;
    begin
        NewEntryNo := 1;
        if LastDimensionCorrection.FindLast() then
            NewEntryNo := LastDimensionCorrection."Entry No." + 1;

        DimensionCorrection."Entry No." := NewEntryNo;
        DimensionCorrection.Insert(true);

        TransferSelectionFilterToRecordRef(GLEntry, GLEntryRecordRef);
        InsertNewDimCorrectSelectionCriteria(GLEntryRecordRef, DimCorrectSelectionCriteria."Filter Type"::Manual, DimCorrectSelectionCriteria, NewEntryNo);
        ReloadDimensionChangesTable(NewEntryNo);
    end;

    /// <summary>
    /// Creates a dimension correction from G/L entries based on filter criteria.
    /// </summary>
    /// <param name="GLEntry">G/L entries with applied filters to create correction from</param>
    /// <param name="DimensionCorrection">Dimension correction record to populate</param>
    procedure CreateCorrectionFromFilter(var GLEntry: Record "G/L Entry"; var DimensionCorrection: Record "Dimension Correction")
    var
        LastDimensionCorrection: Record "Dimension Correction";
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        GLEntryRecordRef: RecordRef;
        NewEntryNo: Integer;
    begin
        NewEntryNo := 1;
        if LastDimensionCorrection.FindLast() then
            NewEntryNo := LastDimensionCorrection."Entry No." + 1;

        DimensionCorrection."Entry No." := NewEntryNo;
        DimensionCorrection.Insert(true);

        GLEntryRecordRef.GetTable(GLEntry);
        InsertNewDimCorrectSelectionCriteria(GLEntryRecordRef, DimCorrectSelectionCriteria."Filter Type"::"Custom Filter", DimCorrectSelectionCriteria, NewEntryNo);
        ReloadDimensionChangesTable(NewEntryNo);
    end;

    /// <summary>
    /// Inserts new dimension correction selection criteria based on record filters.
    /// </summary>
    /// <param name="MainRecordRef">Record reference with filters to extract criteria from</param>
    /// <param name="DimCorrectSelectionCriteriaFilterType">Filter type for the selection criteria</param>
    /// <param name="DimCorrectSelectionCriteria">Selection criteria record to populate</param>
    /// <param name="DimensionCorrectionEntryNo">Dimension correction entry number</param>
    procedure InsertNewDimCorrectSelectionCriteria(var MainRecordRef: RecordRef; DimCorrectSelectionCriteriaFilterType: Option; var DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria"; DimensionCorrectionEntryNo: Integer)
    var
        DimensionCorrectionMgt: Codeunit "Dimension Correction Mgt";
        DimensionSetIds: List of [Integer];
    begin
        DimCorrectSelectionCriteria."Dimension Correction Entry No." := DimensionCorrectionEntryNo;
        DimCorrectSelectionCriteria."Filter Type" := DimCorrectSelectionCriteriaFilterType;
        DimCorrectSelectionCriteria.SetSelectionFilter(MainRecordRef);
        DimCorrectSelectionCriteria.Insert(true);
        DimensionCorrectionMgt.CalculateDimensionSetIds(DimensionSetIds, DimCorrectSelectionCriteria);
        DimCorrectSelectionCriteria.SetDimensionSetIds(DimensionSetIds);
        DimCorrectSelectionCriteria.Modify(true);
    end;

    /// <summary>
    /// Generates a filter text for selected dimension set IDs from temporary dimension set entries.
    /// </summary>
    /// <param name="TempDimensionSetEntry">Temporary dimension set entries containing selected dimensions</param>
    /// <returns>Filter text representing the selected dimension set IDs</returns>
    procedure GetSelectedDimensionSetIDsFilter(var TempDimensionSetEntry: Record "Dimension Set Entry" temporary): Text
    var
        TempFoundDimensionSetIDInteger: Record "Integer" temporary;
        SelectedDimensionSetFilter: Text;
        LastAddedNumber: Integer;
        CurrentNumber: Integer;
    begin
        GetSelectedDimensionSetIDs(TempDimensionSetEntry, TempFoundDimensionSetIDInteger);
        TempFoundDimensionSetIDInteger.SetCurrentKey(Number);
        TempFoundDimensionSetIDInteger.Ascending(true);
        if not TempFoundDimensionSetIDInteger.FindSet() then
            exit('');

        LastAddedNumber := TempFoundDimensionSetIDInteger.Number;
        SelectedDimensionSetFilter += Format(LastAddedNumber);
        CurrentNumber := LastAddedNumber;

        repeat
            if TempFoundDimensionSetIDInteger.Number > CurrentNumber + 1 then begin
                if LastAddedNumber <> CurrentNumber then
                    SelectedDimensionSetFilter += '..' + Format(CurrentNumber) + '|' + Format(TempFoundDimensionSetIDInteger.Number)
                else
                    SelectedDimensionSetFilter += '|' + Format(TempFoundDimensionSetIDInteger.Number);

                LastAddedNumber := TempFoundDimensionSetIDInteger.Number;
            end;
            CurrentNumber := TempFoundDimensionSetIDInteger.Number;
        until TempFoundDimensionSetIDInteger.Next() = 0;

        if LastAddedNumber <> CurrentNumber then
            SelectedDimensionSetFilter += '..' + Format(CurrentNumber);

        exit(SelectedDimensionSetFilter);
    end;

    /// <summary>
    /// Retrieves selected dimension set IDs based on temporary dimension set entries.
    /// </summary>
    /// <param name="TempDimensionSetEntry">Temporary dimension set entries to find matching set IDs for</param>
    /// <param name="TempFoundDimensionSetIDInteger">Temporary integer buffer to store found dimension set IDs</param>
    procedure GetSelectedDimensionSetIDs(var TempDimensionSetEntry: Record "Dimension Set Entry" temporary; var TempFoundDimensionSetIDInteger: Record "Integer" temporary)
    var
        DimensionSetEntry: Record "Dimension Set Entry";
    begin
        if not TempDimensionSetEntry.FindFirst() then
            exit;

        TempFoundDimensionSetIDInteger.SetCurrentKey(Number);
        TempFoundDimensionSetIDInteger.Ascending(true);

        DimensionSetEntry.SetCurrentKey("Dimension Set ID");
        DimensionSetEntry.Ascending(true);

        repeat
            DimensionSetEntry.SetRange("Dimension Code", TempDimensionSetEntry."Dimension Code");
            if TempDimensionSetEntry."Dimension Value Code" <> '' then
                DimensionSetEntry.SetFilter("Dimension Value Code", TempDimensionSetEntry."Dimension Value Code");
            if DimensionSetEntry.FindFirst() then begin
                TempFoundDimensionSetIDInteger.Number := DimensionSetEntry."Dimension Set ID";
                TempFoundDimensionSetIDInteger.Insert();
                TempFoundDimensionSetIDInteger.Mark(true);
            end;
            DimensionSetEntry.SetFilter("Dimension Set ID", '>%1', DimensionSetEntry."Dimension Set ID");
        until DimensionSetEntry.Next() = 0;

        TempFoundDimensionSetIDInteger.MarkedOnly(true);

        if not TempFoundDimensionSetIDInteger.FindFirst() then
            exit;

        if TempDimensionSetEntry.Next() = 0 then
            exit;

        repeat
            repeat
                DimensionSetEntry.SetRange("Dimension Set ID", TempFoundDimensionSetIDInteger.Number);
                DimensionSetEntry.SetRange("Dimension Code", TempDimensionSetEntry."Dimension Code");
                if TempDimensionSetEntry."Dimension Value Code" <> '' then
                    DimensionSetEntry.SetRange("Dimension Value Code", TempDimensionSetEntry."Dimension Value Code");
                if DimensionSetEntry.IsEmpty() then
                    TempFoundDimensionSetIDInteger.Mark(false);
            until TempFoundDimensionSetIDInteger.Next() = 0;

            if not TempFoundDimensionSetIDInteger.FindFirst() then
                exit;
        until TempDimensionSetEntry.Next() = 0;
    end;

    local procedure TransformDimensionSet(DimensionSetID: Integer; var TempDimCorrectionChange: Record "Dim Correction Change" temporary; var NewDimensionSetID: Integer): Boolean
    var
        TempDimensionSetEntry: Record "Dimension Set Entry" temporary;
        TempNewDimensionSetEntry: Record "Dimension Set Entry" temporary;
        DimensionManagement: Codeunit "DimensionManagement";
        Changed: Boolean;
        EntryExist: Boolean;
    begin
        DimensionManagement.GetDimensionSet(TempDimensionSetEntry, DimensionSetId);

        TempDimCorrectionChange.Reset();
        if not TempDimCorrectionChange.FindFirst() then
            exit(false);

        repeat
            case TempDimCorrectionChange."Change Type" of
                TempDimCorrectionChange."Change Type"::Add,
                TempDimCorrectionChange."Change Type"::Change:
                    begin
                        EntryExist := TempDimensionSetEntry.Get(DimensionSetID, TempDimCorrectionChange."Dimension Code");
                        TempDimensionSetEntry."Dimension Code" := TempDimCorrectionChange."Dimension Code";
                        TempDimensionSetEntry."Dimension Value ID" := TempDimCorrectionChange."New Value ID";
                        TempDimensionSetEntry."Dimension Value Code" := CopyStr(TempDimCorrectionChange."New Value", 1, MaxStrLen(TempDimensionSetEntry."Dimension Value Code"));
                        if EntryExist then
                            TempDimensionSetEntry.Modify()
                        else
                            TempDimensionSetEntry.Insert();

                        Changed := true;
                    end;
                TempDimCorrectionChange."Change Type"::Remove:
                    if TempDimensionSetEntry.Get(DimensionSetID, TempDimCorrectionChange."Dimension Code") then begin
                        TempDimensionSetEntry.Delete();
                        Changed := true;
                    end;
            end;
        until TempDimCorrectionChange.Next() = 0;

        if Changed then
            if TempDimensionSetEntry.FindFirst() then begin
                repeat
                    TempNewDimensionSetEntry.TransferFields(TempDimensionSetEntry, true);
                    TempNewDimensionSetEntry."Dimension Set ID" := 0;
                    OnTransformDimensionSetOnBeforeInsertTempNewDimensionSetEntry(TempNewDimensionSetEntry);
                    TempNewDimensionSetEntry.Insert(true);
                until TempDimensionSetEntry.Next() = 0;

                NewDimensionSetID := DimensionManagement.GetDimensionSetID(TempNewDimensionSetEntry);
            end;

        exit(Changed);
    end;

    /// <summary>
    /// Transfers selection filter from G/L Entry record to a record reference for filter processing.
    /// </summary>
    /// <param name="GLEntry">G/L entry record with selection filters applied</param>
    /// <param name="GLEntryRecordRef">Record reference to transfer filters to</param>
    procedure TransferSelectionFilterToRecordRef(var GLEntry: Record "G/L Entry"; var GLEntryRecordRef: RecordRef)
    var
        SelectionFilterManagement: Codeunit SelectionFilterManagement;
        EntryNoFieldRef: FieldRef;
        FilteredText: Text;
    begin
        GLEntryRecordRef.GetTable(GLEntry);
        FilteredText := SelectionFilterManagement.GetSelectionFilter(GLEntryRecordRef, GLEntry.FieldNo("Entry No."));
        GLEntryRecordRef.ClearMarks();
        EntryNoFieldRef := GLEntryRecordRef.Field(GLEntry.FieldNo("Entry No."));
        EntryNoFieldRef.SetFilter(FilteredText);
    end;

    /// <summary>
    /// Retrieves dimension correction changes for a specific dimension correction entry.
    /// </summary>
    /// <param name="DimensionCorrectionEntryNo">Dimension correction entry number to get changes for</param>
    /// <param name="TempDimCorrectionChange">Temporary record to store correction changes</param>
    /// <returns>True if correction changes were found, false otherwise</returns>
    procedure GetDimCorrectionChanges(DimensionCorrectionEntryNo: Integer; var TempDimCorrectionChange: Record "Dim Correction Change" temporary): Boolean
    var
        DimCorrectionChange: Record "Dim Correction Change";
    begin
        DimCorrectionChange.SetRange("Dimension Correction Entry No.", DimensionCorrectionEntryNo);
        DimCorrectionChange.SetFilter("Change Type", '<>%1', DimCorrectionChange."Change Type"::"No Change");
        if not DimCorrectionChange.FindSet() then
            exit(false);

        repeat
            TempDimCorrectionChange.TransferFields(DimCorrectionChange, true);
            TempDimCorrectionChange.Insert();
        until DimCorrectionChange.Next() = 0;

        exit(true);
    end;

    /// <summary>
    /// Retrieves selection criteria for a dimension correction entry.
    /// </summary>
    /// <param name="DimensionCorrectionEntryNo">Dimension correction entry number to get selection criteria for</param>
    /// <param name="DimCorrectSelectionCriteria">Record to store selection criteria</param>
    /// <returns>True if selection criteria were found, false otherwise</returns>
    procedure GetSelectionCriteria(DimensionCorrectionEntryNo: Integer; var DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria"): Boolean
    begin
        DimCorrectSelectionCriteria.SetRange("Dimension Correction Entry No.", DimensionCorrectionEntryNo);
        DimCorrectSelectionCriteria.SetFilter("Filter Type", '<>%1', DimCorrectSelectionCriteria."Filter Type"::Excluded);
        exit(DimCorrectSelectionCriteria.FindSet());
    end;

    /// <summary>
    /// Calculates dimension set IDs based on selection criteria filters.
    /// </summary>
    /// <param name="DimensionSetIds">List to store calculated dimension set IDs</param>
    /// <param name="DimCorrectSelectionCriteria">Selection criteria containing filters</param>
    procedure CalculateDimensionSetIds(var DimensionSetIds: List of [Integer]; var DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria")
    var
        GLEntry: Record "G/L Entry";
        SelectionFilter: Text;
    begin
        Clear(DimensionSetIds);
        DimCorrectSelectionCriteria.GetSelectionFilter(SelectionFilter);
        GLEntry.FilterGroup(2);
        GLEntry.SetView(SelectionFilter);
        GLEntry.FilterGroup(0);
        GLEntry.SetCurrentKey("Dimension Set ID", "Entry No.");
        GLEntry.Ascending(true);
        if not GLEntry.FindFirst() then
            exit;

        repeat
            if not IsEntryExclued(GLEntry, DimCorrectSelectionCriteria."Dimension Correction Entry No.") then begin
                DimensionSetIds.Add(GLEntry."Dimension Set ID");
                GLEntry.SetFilter("Dimension Set ID", '>%1', GLEntry."Dimension Set ID");
            end;
        until GLEntry.Next() = 0;
    end;

    /// <summary>
    /// Reloads dimension changes table by recalculating changes based on current selection criteria.
    /// </summary>
    /// <param name="DimCorrectionEntryNo">Dimension correction entry number to reload changes for</param>
    procedure ReloadDimensionChangesTable(DimCorrectionEntryNo: Integer)
    var
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        DimCorrectionChange: Record "Dim Correction Change";
        DimensionCodeValue: Dictionary of [Code[20], List of [Integer]];
    begin
        DimCorrectionChange.SetRange("Dimension Correction Entry No.", DimCorrectionEntryNo);
        DimCorrectionChange.SetFilter("Change Type", '<>%1', DimCorrectionChange."Change Type"::"No Change");
        if not DimCorrectionChange.IsEmpty() then
            Message(ChangesWereResetMsg);

        DimCorrectionChange.SetRange("Change Type");
        DimCorrectionChange.DeleteAll(true);

        DimCorrectSelectionCriteria.SetRange("Dimension Correction Entry No.", DimCorrectionEntryNo);
        if not DimCorrectSelectionCriteria.FindSet() then
            exit;

        GetDimensionCodeValues(DimensionCodeValue, DimCorrectSelectionCriteria);
        UpdateDimCorrectionChanges(DimensionCodeValue, DimCorrectionEntryNo);
    end;

    /// <summary>
    /// Loads dimension correction set buffer data into a temporary record.
    /// </summary>
    /// <param name="DimensionCorrectionEntryNo">Dimension correction entry number to load data for</param>
    /// <param name="TempDimCorrectionSetBuffer">Temporary buffer to store correction set data</param>
    /// <returns>True if correction set buffer data was loaded, false otherwise</returns>
    procedure LoadTempDimCorrectionSetBuffer(DimensionCorrectionEntryNo: Integer; var TempDimCorrectionSetBuffer: Record "Dim Correction Set Buffer" temporary): Boolean
    var
        DimCorrectionSetBuffer: Record "Dim Correction Set Buffer";
    begin
        DimCorrectionSetBuffer.SetRange("Dimension Correction Entry No.", DimensionCorrectionEntryNo);
        if not DimCorrectionSetBuffer.FindSet() then
            exit(false);

        repeat
            TempDimCorrectionSetBuffer.TransferFields(DimCorrectionSetBuffer, true);
            TempDimCorrectionSetBuffer.Insert();
            if DimCorrectionSetBuffer."Multiple Target Set ID" then begin
                TempDimCorrectionSetBuffer.SetLedgerEntries(DimCorrectionSetBuffer.GetSetLedgerEntries());
                TempDimCorrectionSetBuffer.Modify();
            end;
        until DimCorrectionSetBuffer.Next() = 0;

        exit(true);
    end;

    /// <summary>
    /// Schedules a job queue entry to run dimension correction.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction to schedule for execution</param>
    /// <returns>True if job was successfully scheduled, false otherwise</returns>
    procedure ScheduleRunJob(var DimensionCorrection: Record "Dimension Correction"): Boolean

    var
        JobQueueEntry: Record "Job Queue Entry";
    begin
        CreateUniqueJobQueue(Codeunit::"Dim Correction Run", DimensionCorrection, JobQueueEntry);
        Commit();

        if ScheduleJobViaUI(JobQueueEntry, '') then begin
            SetStatusInProgress(DimensionCorrection);
            DimensionCorrection."Last Job Queue Entry ID" := JobQueueEntry.ID;
            DimensionCorrection.Modify();
            exit(true);
        end;

        exit(false);
    end;

    /// <summary>
    /// Schedules a job queue entry to undo dimension correction.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction to schedule for undo</param>
    /// <returns>True if undo job was successfully scheduled, false otherwise</returns>
    procedure ScheduleUndoJob(var DimensionCorrection: Record "Dimension Correction"): Boolean
    var
        JobQueueEntry: Record "Job Queue Entry";
    begin
        CreateUniqueJobQueue(Codeunit::"Dimension Correction Undo", DimensionCorrection, JobQueueEntry);
        Commit();
        if ScheduleJobViaUI(JobQueueEntry, UndoDimensionCorrectionLbl) then begin
            SetUndoStatusInProgress(DimensionCorrection);
            DimensionCorrection."Last Job Queue Entry ID" := JobQueueEntry.ID;
            DimensionCorrection.Modify();
            exit(true);
        end;
        exit(false);
    end;

    /// <summary>
    /// Schedules a job queue entry to validate dimension correction.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction to schedule for validation</param>
    /// <returns>True if validation job was successfully scheduled, false otherwise</returns>
    procedure ScheduleValidationJob(var DimensionCorrection: Record "Dimension Correction"): Boolean
    var
        JobQueueEntry: Record "Job Queue Entry";
    begin
        CreateUniqueJobQueue(Codeunit::"Dim Correction Validate", DimensionCorrection, JobQueueEntry);
        Commit();
        if ScheduleJobViaUI(JobQueueEntry, ValidateDimensionCorrectionLbl) then begin
            DeleteValidationErrors(DimensionCorrection);
            ClearValidationFields(DimensionCorrection);
            SetValidatingStatusInProgress(DimensionCorrection);
            DimensionCorrection."Last Job Queue Entry ID" := JobQueueEntry.ID;
            DimensionCorrection.Modify();
            exit(true);
        end else
            exit(false);
    end;

    /// <summary>
    /// Schedules a job queue entry to update analysis views after dimension correction.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction to schedule analysis view updates for</param>
    /// <returns>True if analysis view update job was successfully scheduled, false otherwise</returns>
    procedure ScheduleUpdateAnalysisViews(var DimensionCorrection: Record "Dimension Correction"): Boolean
    var
        JobQueueEntry: Record "Job Queue Entry";
    begin
        CreateUniqueJobQueue(Codeunit::"Dim Corr Analysis View", DimensionCorrection, JobQueueEntry);
        Commit();
        if GuiAllowed then
            exit(ScheduleJobViaUI(JobQueueEntry, UpdateAnalysisViewsLbl));

        JobQueueEntry.SetStatus(JobQueueEntry.Status::Ready);
        exit(true);
    end;

    local procedure ScheduleJobViaUI(var JobQueueEntry: Record "Job Queue Entry"; NewCaption: Text): Boolean
    var
        DimCorrectionSchedule: Page "Dim Correction Schedule";
    begin
        if NewCaption <> '' then
            DimCorrectionSchedule.SetNewCaption(NewCaption);

        DimCorrectionSchedule.SetRecord(JobQueueEntry);
        if DimCorrectionSchedule.RunModal() in [Action::Cancel, Action::LookupCancel] then
            exit(false);

        exit(true);
    end;

    /// <summary>
    /// Updates the status of a dimension correction based on job queue entry status.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to update status for</param>
    procedure UpdateStatus(var DimensionCorrection: Record "Dimension Correction")
    var
        JobQueueEntry: Record "Job Queue Entry";
    begin
        if not (DimensionCorrection.Status in [DimensionCorrection.Status::"In Process", DimensionCorrection.Status::"Undo in Process", DimensionCorrection.Status::"Validaton in Process"]) then
            exit;

        if not JobQueueEntry.Get(DimensionCorrection."Last Job Queue Entry ID") then begin
            DimensionCorrection.Status := DimensionCorrection.Status::Failed;
            DimensionCorrection.Modify();
            exit;
        end;

        if JobQueueEntry.Status = JobQueueEntry.Status::Error then begin
            DimensionCorrection.Status := DimensionCorrection.Status::Failed;
            DimensionCorrection."Error Message" := JobQueueEntry."Error Message";
            DimensionCorrection.Modify();
            exit;
        end;

        if JobQueueEntry.Status = JobQueueEntry.Status::Finished then begin
            DimensionCorrection.Status := DimensionCorrection.Status::Completed;
            Clear(DimensionCorrection."Error Message");
            DimensionCorrection.Modify();
            exit;
        end;
    end;


    /// <summary>
    /// Updates the analysis view status for dimension correction based on job queue entry status.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to update analysis view status for</param>
    procedure UpdateAnalysisViewStatus(var DimensionCorrection: Record "Dimension Correction")
    var
        JobQueueEntry: Record "Job Queue Entry";
    begin
        if not (DimensionCorrection."Update Analysis Views Status" = DimensionCorrection."Update Analysis Views Status"::"In Process") then
            exit;

        if not JobQueueEntry.Get(DimensionCorrection."Update Analysis View Job ID") then begin
            DimensionCorrection."Update Analysis Views Status" := DimensionCorrection."Update Analysis Views Status"::Failed;
            DimensionCorrection.Modify();
            exit;
        end;

        if JobQueueEntry.Status = JobQueueEntry.Status::Error then begin
            DimensionCorrection."Update Analysis Views Status" := DimensionCorrection."Update Analysis Views Status"::Failed;
            DimensionCorrection.SetUpdateAnalysisViewErrorMessage(JobQueueEntry."Error Message");
            DimensionCorrection.Modify();
            exit;
        end;

        if JobQueueEntry.Status = JobQueueEntry.Status::Finished then begin
            DimensionCorrection."Update Analysis Views Status" := DimensionCorrection."Update Analysis Views Status"::Completed;
            Clear(DimensionCorrection."Update Analysis Views Error");
            DimensionCorrection.Modify();
            exit;
        end;
    end;

    local procedure CreateUniqueJobQueue(CodeunitID: Integer; var DimensionCorrection: Record "Dimension Correction"; var JobQueueEntry: Record "Job Queue Entry")
    var
        JobQueueExist: Boolean;
    begin
        JobQueueEntry.SetRange("Object Type to Run", JobQueueEntry."Object Type to Run"::Codeunit);
        JobQueueEntry.SetRange("Object ID to Run", CodeunitID);

        JobQueueExist := JobQueueEntry.FindFirst();
        if JobQueueExist then begin
            if JobQueueEntry.Status = JobQueueEntry.Status::"In Process" then
                Error(JobQueueIsRunningErr);

            JobQueueEntry.Delete(true);
            Clear(JobQueueEntry);
        end;

        JobQueueEntry."Object Type to Run" := JobQueueEntry."Object Type to Run"::Codeunit;
        JobQueueEntry."Object ID to Run" := CodeunitID;
        JobQueueEntry."Maximum No. of Attempts to Run" := 1;
        JobQueueEntry."Recurring Job" := false;
        JobQueueEntry."Record ID to Process" := DimensionCorrection.RecordId;
        JobQueueEntry.Status := JobQueueEntry.Status::"On Hold";
        JobQueueEntry."Job Queue Category Code" := JobQueueCategoryCodeTxt;
        Clear(JobQueueEntry."Error Message");
        Clear(JobQueueEntry."Error Message Register Id");
        JobQueueEntry.Description := CopyStr(StrSubstNo(JobQueueEntryDescTxt, DimensionCorrection."Entry No."), 1, MaxStrLen(JobQueueEntry.Description));
        JobQueueEntry.Insert(true);
    end;

    /// <summary>
    /// Verifies that a dimension correction job can be started by checking for conflicts with other running jobs.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction to verify for job start</param>
    procedure VerifyCanStartJob(DimensionCorrection: Record "Dimension Correction")
    var
        InProgressDimensionCorrection: Record "Dimension Correction";
    begin
        if not DimensionCorrection.Completed then
            if DimensionCorrection.Status = DimensionCorrection.Status::"In Process" then
                Error(JobAlreadyInProgressErr);

        if DimensionCorrection.Completed then
            if DimensionCorrection.Status = DimensionCorrection.Status::"Undo In Process" then
                Error(JobAlreadyInProgressErr);

        InProgressDimensionCorrection.SetFilter(Status, '%1|%2|%3', DimensionCorrection.Status::"In Process", DimensionCorrection.Status::"Undo in Process", DimensionCorrection.Status::"Validaton in Process");
        InProgressDimensionCorrection.SetLoadFields("Entry No.", Status);
        if InProgressDimensionCorrection.FindFirst() then
            UpdateStatus(InProgressDimensionCorrection);

        if InProgressDimensionCorrection.FindFirst() then
            Error(AnotherJobAlreadyInProgressErr, InProgressDimensionCorrection."Entry No.");
    end;

    /// <summary>
    /// Sets dimension correction status to in progress and clears error information.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to set in progress</param>
    procedure SetStatusInProgress(var DimensionCorrection: Record "Dimension Correction")
    begin
        DimensionCorrection.Status := DimensionCorrection.Status::"In Process";
        Clear(DimensionCorrection."Error Message");
        DeleteValidationErrors(DimensionCorrection);

        if DimensionCorrection."Validated At" <> 0DT then
            ClearValidationFields(DimensionCorrection);

        DimensionCorrection.Modify();
        Commit();
    end;

    /// <summary>
    /// Sets dimension correction status to undo in progress and clears error information.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to set undo in progress</param>
    procedure SetUndoStatusInProgress(var DimensionCorrection: Record "Dimension Correction")
    begin
        DimensionCorrection.Status := DimensionCorrection.Status::"Undo in Process";
        Clear(DimensionCorrection."Error Message");
        DeleteValidationErrors(DimensionCorrection);
        if DimensionCorrection."Validated At" <> 0DT then
            ClearValidationFields(DimensionCorrection);

        DimensionCorrection.Modify();
        Commit();
    end;

    /// <summary>
    /// Sets dimension correction status to validation in progress and clears validation fields.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to set validation in progress</param>
    procedure SetValidatingStatusInProgress(var DimensionCorrection: Record "Dimension Correction")
    begin
        DimensionCorrection.Status := DimensionCorrection.Status::"Validaton in Process";
        Clear(DimensionCorrection."Error Message");
        Clear(DimensionCorrection."Last Validated Entry No.");
        DeleteValidationErrors(DimensionCorrection);
        DimensionCorrection.Modify();
        Commit();
    end;

    /// <summary>
    /// Gets formatted validation status text for dimension correction.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction to get validation status for</param>
    /// <param name="ErrorMessage">Error message text to include if validation failed</param>
    /// <returns>Formatted validation status text</returns>
    procedure GetValidationStatusText(var DimensionCorrection: Record "Dimension Correction"; ErrorMessage: Text): Text
    begin
        if DimensionCorrection."Validated At" = 0DT then
            exit('');

        if DimensionCorrection."Validated Selected Entries" then begin
            if DimensionCorrection.Completed then
                exit(StrSubstNo(ValidationUndoPassedLbl, DimensionCorrection."Validated At"));

            exit(StrSubstNo(ValidationDraftPassedLbl, DimensionCorrection."Validated At"));
        end;

        exit(StrSubstNo(ValidationFailedLbl, DimensionCorrection."Validated At", ErrorMessage));
    end;

    /// <summary>
    /// Creates a draft copy of an existing dimension correction with all selection criteria.
    /// </summary>
    /// <param name="DimensionCorrection">Original dimension correction to copy from</param>
    /// <param name="NewDimensionCorrection">New dimension correction record to populate</param>
    procedure CopyToDraft(var DimensionCorrection: Record "Dimension Correction"; var NewDimensionCorrection: Record "Dimension Correction")
    var
        DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        NewDimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
        SelectionFilter: Text;
        DimensionSetIds: List of [Integer];
    begin
        NewDimensionCorrection.Description := DimensionCorrection.Description;
        NewDimensionCorrection.Insert(true);

        DimCorrectSelectionCriteria.SetRange("Dimension Correction Entry No.", DimensionCorrection."Entry No.");
        if not DimCorrectSelectionCriteria.FindSet() then
            exit;

        repeat
            Clear(NewDimCorrectSelectionCriteria);
            NewDimCorrectSelectionCriteria.TransferFields(DimCorrectSelectionCriteria);
            NewDimCorrectSelectionCriteria."Dimension Correction Entry No." := NewDimensionCorrection."Entry No.";
            DimCorrectSelectionCriteria.GetSelectionFilter(SelectionFilter);
            NewDimCorrectSelectionCriteria.SetSelectionFilter(SelectionFilter);
            NewDimCorrectSelectionCriteria."Entry No." := 0;
            NewDimCorrectSelectionCriteria."Last Entry No." := 0;
            NewDimCorrectSelectionCriteria.Insert(true);

            CalculateDimensionSetIds(DimensionSetIds, NewDimCorrectSelectionCriteria);
            NewDimCorrectSelectionCriteria.SetDimensionSetIds(DimensionSetIds);
            NewDimCorrectSelectionCriteria.Modify(true);
        until DimCorrectSelectionCriteria.Next() = 0;

        ReloadDimensionChangesTable(NewDimensionCorrection."Entry No.");
    end;

    local procedure ClearValidationFields(var DimensionCorrection: Record "Dimension Correction")
    begin
        Clear(DimensionCorrection."Validated At");
        Clear(DimensionCorrection."Validated Selected Entries");
        Clear(DimensionCorrection."Validation Errors Register ID");
        Clear(DimensionCorrection."Last Validated Entry No.");
        Clear(DimensionCorrection."Error Message");
        Clear(DimensionCorrection."Validation Message");
    end;

    local procedure GetDimensionCodeValues(var DimensionCodeValue: Dictionary of [Code[20], List of [Integer]]; var DimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria")
    var
        DimensionSetIdList: List of [Integer];
        DimensionSetId: Integer;
        ProcessedDimensionSetIds: List of [Integer];
    begin
        if not DimCorrectSelectionCriteria.FindSet() then
            exit;

        repeat
            DimCorrectSelectionCriteria.GetDimensionSetIds(DimensionSetIdList);

            foreach DimensionSetId in DimensionSetIdList do
                if not ProcessedDimensionSetIds.Contains(DimensionSetId) then begin
                    AddDimensionSetIDToDictionary(DimensionSetId, DimensionCodeValue);
                    ProcessedDimensionSetIds.Add(DimensionSetId);
                end;
        until DimCorrectSelectionCriteria.Next() = 0;
    end;

    local procedure AddDimensionSetIDToDictionary(DimensionsetId: Integer; var DimensionCodeValue: Dictionary of [Code[20], List of [Integer]])
    var
        DimensionSetEntry: Record "Dimension Set Entry";
        DimensionValues: List of [Integer];
    begin
        DimensionSetEntry.SetRange("Dimension Set ID", DimensionSetId);
        if DimensionSetEntry.FindSet() then
            repeat
                Clear(DimensionValues);
                if not DimensionCodeValue.ContainsKey(DimensionSetEntry."Dimension Code") then
                    DimensionCodeValue.Add(DimensionSetEntry."Dimension Code", DimensionValues)
                else
                    DimensionCodeValue.Get(DimensionSetEntry."Dimension Code", DimensionValues);

                if not DimensionValues.Contains(DimensionSetEntry."Dimension Value ID") then
                    DimensionValues.Add(DimensionSetEntry."Dimension Value ID");
            until DimensionSetEntry.Next() = 0;
    end;

    local procedure UpdateDimCorrectionChanges(var DimensionCodeValue: Dictionary of [Code[20], List of [Integer]]; DimensionCorrectionEntryNo: Integer): Boolean
    var
        DimCorrectionChange: Record "Dim Correction Change";
        DimensionSetEntry: Record "Dimension Set Entry";
        DimensionCode: Code[20];
        DimensionValues: List of [Integer];
        EntryExists: Boolean;
    begin
        if DimensionCodeValue.Keys().Count() = 0 then
            exit;

        foreach DimensionCode in DimensionCodeValue.Keys() do begin
            DimensionValues := DimensionCodeValue.Get(DimensionCode);

            Clear(DimCorrectionChange);
            EntryExists := DimCorrectionChange.Get(DimensionCorrectionEntryNo, DimensionCode);
            DimCorrectionChange."Dimension Correction Entry No." := DimensionCorrectionEntryNo;
            DimCorrectionChange."Dimension Code" := DimensionCode;
            DimCorrectionChange."Dimension Value Count" := DimensionValues.Count();
            DimCorrectionChange.SetDimensionValues(DimensionValues);
            if DimCorrectionChange."Dimension Value Count" = 1 then begin
                DimensionSetEntry.SetRange("Dimension Value ID", DimensionValues.Get(1));
                if DimensionSetEntry.FindFirst() then
                    DimCorrectionChange."Dimension Value" := DimensionSetEntry."Dimension Value Code"
                else
                    DimCorrectionChange."Dimension Value" := Format(DimensionValues.Get(1));
            end;

            if EntryExists then
                DimCorrectionChange.Modify(true)
            else
                DimCorrectionChange.Insert(true);
        end;
    end;

    /// <summary>
    /// Determines if a G/L entry is excluded from dimension correction based on exclusion criteria.
    /// </summary>
    /// <param name="GLEntry">G/L entry to check for exclusion</param>
    /// <param name="DimensionCorrectionEntryNo">Dimension correction entry number containing exclusion criteria</param>
    /// <returns>True if entry is excluded, false otherwise</returns>
    procedure IsEntryExclued(var GLEntry: Record "G/L Entry"; DimensionCorrectionEntryNo: Integer): Boolean
    var
        ExcludedDimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria";
    begin
        ExcludedDimCorrectSelectionCriteria.SetRange("Dimension Correction Entry No.", DimensionCorrectionEntryNo);
        ExcludedDimCorrectSelectionCriteria.SetRange("Filter Type", ExcludedDimCorrectSelectionCriteria."Filter Type"::Excluded);
        exit(IsEntryExclued(GLEntry, ExcludedDimCorrectSelectionCriteria));
    end;

    /// <summary>
    /// Determines if a G/L entry is excluded based on specific exclusion selection criteria.
    /// </summary>
    /// <param name="GLEntry">G/L entry to check for exclusion</param>
    /// <param name="ExcludedDimCorrectSelectionCriteria">Exclusion selection criteria to evaluate against</param>
    /// <returns>True if entry is excluded, false otherwise</returns>
    procedure IsEntryExclued(var GLEntry: Record "G/L Entry"; var ExcludedDimCorrectSelectionCriteria: Record "Dim Correct Selection Criteria"): Boolean
    var
        TempGLEntry: Record "G/L Entry" temporary;
        SelectionFilter: Text;
    begin
        if not ExcludedDimCorrectSelectionCriteria.FindSet() then
            exit;

        TempGLEntry.Copy(GLEntry);
        TempGLEntry.Insert();
        repeat
            ExcludedDimCorrectSelectionCriteria.GetSelectionFilter(SelectionFilter);
            TempGLEntry.SetView(SelectionFilter);
            if not TempGLEntry.IsEmpty() then
                exit(true);
        until ExcludedDimCorrectSelectionCriteria.Next() = 0;

        exit(false);
    end;

    /// <summary>
    /// Increments a counter and commits database changes if the commit threshold is reached.
    /// </summary>
    /// <param name="Counter">Counter to increment and check against commit threshold</param>
    procedure IncrementAndCommitIfNeeded(var Counter: Integer)
    begin
        Counter += 1;
        if Counter >= GetCommitCount() then begin
            Commit();
            Session.LogMessage('0000EHT', StrSubstNo(CommitingTelemetryLbl, GetCommitCount()), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', DimensionCorrectionTok);
            Counter := 0;
        end;
    end;

    local procedure GetDictionarySizeLimit(): Integer
    begin
        exit(100000);
    end;

    /// <summary>
    /// Gets the maximum number of validation errors allowed before stopping validation.
    /// </summary>
    /// <returns>Maximum number of validation errors (50000)</returns>
    procedure GetMaximumNumberOfValidationErrors(): Integer
    begin
        exit(50000);
    end;

    /// <summary>
    /// Gets the number of operations after which database changes should be committed.
    /// </summary>
    /// <returns>Commit count threshold</returns>
    procedure GetCommitCount(): Integer;
    var
        Handled: Boolean;
        CommitCount: Integer;
    begin
        OnGetCommitCount(Handled, CommitCount);
        if Handled then
            exit(CommitCount);

        exit(1000);
    end;

    /// <summary>
    /// Gets the maximum number of G/L entries to show in preview mode.
    /// </summary>
    /// <returns>Preview G/L entries limit</returns>
    procedure GetPreviewGLEntriesLimit(): Integer
    var
        Handled: Boolean;
        PreviewCount: Integer;
    begin
        OnGetPreviewGLEntriesLimit(Handled, PreviewCount);
        if Handled then
            exit(PreviewCount);

        exit(20000);
    end;

    /// <summary>
    /// Gets the maximum number of filter conditions allowed in selection criteria.
    /// </summary>
    /// <returns>Filter conditions limit</returns>
    procedure GetFilterConditionsLimit(): Integer;
    var
        Handled: Boolean;
        FilterCount: Integer;
    begin
        OnGetFilterCount(Handled, FilterCount);
        if Handled then
            exit(FilterCount);

        exit(2000);
    end;

    [EventSubscriber(ObjectType::Table, Database::"Job Queue Entry", 'OnAfterFinalizeRun', '', false, false)]
    local procedure HandleJobQueueFailures(JobQueueEntry: Record "Job Queue Entry")
    var
        DimensionCorrection: Record "Dimension Correction";
        ErrorMessage: Record "Error Message";
        LastErrorMessage: Text[2048];
    begin
        if JobQueueEntry."Error Message" = '' then
            exit;

        if not DimensionCorrection.Get(JobQueueEntry."Record ID to Process") then
            exit;

        DimensionCorrection."Validation Errors Register ID" := JobQueueEntry."Error Message Register Id";

        if not IsNullGuid(JobQueueEntry."Error Message Register Id") then begin
            ErrorMessage.SetRange("Register ID", DimensionCorrection."Validation Errors Register ID");
            if ErrorMessage.FindLast() then
                LastErrorMessage := ErrorMessage."Message"
            else
                LastErrorMessage := JobQueueEntry."Error Message";
        end;

        if (JobQueueEntry."Object Type to Run" <> JobQueueEntry."Object Type to Run"::Codeunit) then
            exit;

        if not (JobQueueEntry."Object ID to Run" in [Codeunit::"Dim Correction Run", Codeunit::"Dimension Correction Undo", Codeunit::"Dim Corr Analysis View", Codeunit::"Dim Correction Validate"]) then
            exit;

        if JobQueueEntry."Object ID to Run" in [Codeunit::"Dim Correction Run", Codeunit::"Dimension Correction Undo"] then begin
            DimensionCorrection.Status := DimensionCorrection.Status::Failed;
            DimensionCorrection."Error Message" := LastErrorMessage;
            DimensionCorrection.Modify(true);
            exit;
        end;

        if JobQueueEntry."Object Type to Run" = Codeunit::"Dim Corr Analysis View" then begin
            DimensionCorrection."Update Analysis Views Status" := DimensionCorrection."Update Analysis Views Status"::Failed;
            DimensionCorrection.SetUpdateAnalysisViewErrorMessage(LastErrorMessage);
            DimensionCorrection.Modify();
            exit;
        end;

        if JobQueueEntry."Object ID to Run" = Codeunit::"Dim Correction Validate" then begin
            if DimensionCorrection.Status = DimensionCorrection.Status::"Validaton in Process" then
                if DimensionCorrection.Completed then
                    DimensionCorrection.Status := DimensionCorrection.Status::Completed
                else
                    DimensionCorrection.Status := DimensionCorrection.Status::Draft;

            DimensionCorrection."Validated Selected Entries" := false;
            DimensionCorrection."Validated At" := CurrentDateTime();
            DimensionCorrection.SetValidateDimensionChangesText(GetValidationStatusText(DimensionCorrection, LastErrorMessage));
            DimensionCorrection.Modify();
        end;
    end;

    /// <summary>
    /// Sets the analysis views update status to completed and clears error messages.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction record to mark analysis views update as completed</param>
    procedure SetUpdateAnalysisViewsCompleted(var DimensionCorrection: Record "Dimension Correction")
    begin
        DimensionCorrection."Update Analysis Views Status" := DimensionCorrection."Update Analysis Views Status"::Completed;
        Clear(DimensionCorrection."Update Analysis Views Error");
        DimensionCorrection.Modify();
    end;

    /// <summary>
    /// Shows notification to update cost accounting manually after dimension correction.
    /// </summary>
    procedure ShowNotificationUpdateCashFlowAccounting()
    var
        MyNotifications: Record "My Notifications";
        CostAccountingSetup: Record "Cost Accounting Setup";
        ShowUpdateCostAccountingManually: Notification;
    begin
        if not CostAccountingSetup.Get() then
            exit;

        if not CostAccountingSetup."Auto Transfer from G/L" then
            exit;

        if MyNotifications.Get(UserId, GetUpdateCostAccountingNotificationID()) then
            if not MyNotifications.Enabled then
                exit;

        ShowUpdateCostAccountingManually.Id := GetUpdateCostAccountingNotificationID();
        if ShowUpdateCostAccountingManually.Recall() then;
        ShowUpdateCostAccountingManually.Message := UpdateCostAccountingManuallyMsg;
        ShowUpdateCostAccountingManually.Scope := NOTIFICATIONSCOPE::LocalScope;
        ShowUpdateCostAccountingManually.AddAction(DontShowAgainTxt, CODEUNIT::"Dimension Correction Mgt", 'DontShowAgainUpdateCostAccounting');
        ShowUpdateCostAccountingManually.Send();
    end;

    /// <summary>
    /// Disables the cost accounting update notification to prevent future display.
    /// </summary>
    /// <param name="UpdateCostAcountingNotification">Notification being dismissed</param>
    procedure DontShowAgainUpdateCostAccounting(UpdateCostAcountingNotification: Notification)
    var
        MyNotifications: Record "My Notifications";
    begin
        if not MyNotifications.Disable(GetUpdateCostAccountingNotificationID()) then
            MyNotifications.InsertDefault(
              GetUpdateCostAccountingNotificationID(), DimensionCorrectionCostAccountingNotificationNameTxt, DimensionCorrectionCostAccountingNotificationDescriptionTxt, false);
    end;

    local procedure GetUpdateCostAccountingNotificationID(): Guid
    begin
        exit('0bf7a209-d730-485b-9ad8-14dc1e756eaf');
    end;

    /// <summary>
    /// Verifies that a dimension correction can be undone by checking if dimension sets are still valid.
    /// </summary>
    /// <param name="DimensionCorrection">Dimension correction to verify for undo capability</param>
    procedure VerifyCanUndoDimensionCorrection(var DimensionCorrection: Record "Dimension Correction")
    begin
        DimensionCorrection.CalcFields(Invalidated);
        if DimensionCorrection.Invalidated then
            Error(DimensionSetIsInvalidErr);
    end;

    /// <summary>
    /// Verifies that a draft dimension correction entry can be modified based on its current status.
    /// </summary>
    /// <param name="DimensionCorrectionEntryNo">Dimension correction entry number to verify for modification</param>
    procedure VerifyCanModifyDraftEntry(DimensionCorrectionEntryNo: Integer)
    var
        DimensionCorrection: Record "Dimension Correction";
    begin
        if DimensionCorrectionEntryNo = 0 then
            Error(InsertDimensionCorrectionFirstErr);

        DimensionCorrection.Get(DimensionCorrectionEntryNo);
        if not (DimensionCorrection.Status in [DimensionCorrection.Status::Failed, DimensionCorrection.Status::Draft]) then
            Error(ModifyingDraftDimensionCorrectionNotAllowedErr, DimensionCorrection.Status);

        if DimensionCorrection."Started Correction" then
            Error(ModifyingDraftDimensionCorrectionNotAllowedReopenErr);
    end;

    /// <summary>
    /// Integration event allowing customization of preview G/L entries limit.
    /// </summary>
    /// <param name="Handled">Set to true to override default limit</param>
    /// <param name="Limit">Custom preview entries limit to use</param>
    [IntegrationEvent(false, false)]
    local procedure OnGetPreviewGLEntriesLimit(var Handled: Boolean; var Limit: Integer)
    begin
    end;

    /// <summary>
    /// Integration event allowing customization of commit count threshold.
    /// </summary>
    /// <param name="Handled">Set to true to override default commit count</param>
    /// <param name="CommitCount">Custom commit count threshold to use</param>
    [IntegrationEvent(false, false)]
    local procedure OnGetCommitCount(var Handled: Boolean; var CommitCount: Integer)
    begin
    end;

    /// <summary>
    /// Integration event allowing customization of filter conditions limit.
    /// </summary>
    /// <param name="Handled">Set to true to override default filter count</param>
    /// <param name="FilterCount">Custom filter conditions limit to use</param>
    [IntegrationEvent(false, false)]
    local procedure OnGetFilterCount(var Handled: Boolean; var FilterCount: Integer)
    begin
    end;

    /// <summary>
    /// Integration event raised after verifying if dimension can be changed.
    /// </summary>
    /// <param name="DimCorrectionChange">Dimension correction change record that was verified</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterVerifyIfDimensionCanBeChanged(var DimCorrectionChange: Record "Dim Correction Change")
    begin
    end;

    /// <summary>
    /// Integration event raised before inserting temporary new dimension set entry during dimension transformation.
    /// </summary>
    /// <param name="TempNewDimensionSetEntry">Temporary dimension set entry about to be inserted</param>
    [IntegrationEvent(false, false)]
    local procedure OnTransformDimensionSetOnBeforeInsertTempNewDimensionSetEntry(var TempNewDimensionSetEntry: Record "Dimension Set Entry" temporary)
    begin
    end;

    var
        CannotValidateDimensionCorrectionErr: Label 'You cannot validate dimension corrections that have the %1 status.', Comment = '%1 Status of dimension correction';
        ValidateDimensionCorrectionLbl: Label 'Validate Dimension Correction';
        UpdateCostAccountingManuallyMsg: Label 'A dimension correction may have affected reports for cost accounting. To help ensure the reports are accurate, recreate the cost accounting allocations.';
        DimensionCorrectionCostAccountingNotificationNameTxt: Label 'Warn Update Cost Accounting After Dimension Correction';
        DimensionCorrectionCostAccountingNotificationDescriptionTxt: Label 'Notifies users that a dimension correction may have affected reports for cost accounting, and that they may need to recreate allocations.';
        DontShowAgainTxt: Label 'Do not show again';
        ValidationFailedLbl: Label 'The validation of the dimension corrections that ran on %1 found the following errors: %2.', Comment = '%1 - Date time of last validation, %2 - Error message';
        ValidationUndoPassedLbl: Label 'Validation was successful on %1. You can undo the dimension correction.', Comment = '%1 - Date time of last validation';
        ValidationDraftPassedLbl: Label 'The dimension correction was validated on %1.', Comment = '%1 - Date time of last validation';
        ValidationFailedErr: Label '%1 entries have validation errors on dimension changes. To review the errors, choose the Show Errors action.', Comment = '%1 count of validation errors';
        JobQueueIsRunningErr: Label 'The job queue entry is already running. Stop the existing job queue entry to schedule a new one.';
        JobQueueEntryDescTxt: Label 'Dimension Correction - %1.', Comment = '%1 - Unique number of the correction';
        JobAlreadyInProgressErr: Label 'There is a job already in progress.';
        AnotherJobAlreadyInProgressErr: Label 'Dimension correction %1 is in progress and must complete before you can start another correction.', Comment = '%1 - Entry No of another dimension correction';
        ChangesWereResetMsg: Label 'Changes to the dimensions were reset because ledger entries were updated. We recommend that you change dimensions after selecting all ledger entries.';
        CannotChangeDimensionCodeBlockedErr: Label 'Dimension %1 cannot be used because it is blocked for the correction.', Comment = '%1 code of the dimension';
        JobQueueCategoryCodeTxt: Label 'DIMCORRECT', Locked = true;
        DimensionSetIsInvalidErr: Label 'Cannot undo the dimension correction because other corrections were made more recently.';
        ModifyingDraftDimensionCorrectionNotAllowedErr: Label 'You cannot change a dimension correction while it is in %1 state.', Comment = '%1 Name of the state';
        ModifyingDraftDimensionCorrectionNotAllowedReopenErr: Label 'You need to reopen the dimension correction to do changes. Alternatevelly you can schedule a new run.';
        InsertDimensionCorrectionFirstErr: Label 'You must create Dimension Correction first, for example by entering description';
        CommitingTelemetryLbl: Label 'Commiting after %1 entries processed.', Locked = true, Comment = '%1 Number of entries procesed';
        UndoDimensionCorrectionLbl: Label 'Undo Dimension Correction';
        UpdateAnalysisViewsLbl: Label 'Update Analysis Views';
        StartingValidateDimensionChangesForEntriesLbl: Label 'Starting Validate Dimension Changes for Entries, Dimension Correction - %1', Locked = true, Comment = '%1 - Number of Dimension Correction';
        CompletedValidateDimensionChangesForEntriesLbl: Label 'Completed Validate Dimension Changes for Entries, Dimension Correction - %1', Locked = true, Comment = '%1 - Number of Dimension Correction';
        InvalidDimensionCorrectionErr: Label 'The dimension correction for Entry %1 is invalid. Error message: %2', Comment = '%1 - Ledger entry number, %2 Error message.';
        CommitedValidateGLEntriesLbl: Label 'Commited G/L Entries validation. Dimension Correction Entry No.: %1, Time from last commit: %2. Number of entries iterated: %3.', Locked = true, Comment = '%1 Dimension Correction Entry No., %2 - Time passed between commits, %3 Number, %4 Time validating dimensions';
        StartingGenerateDimensionSetIdsLbl: Label 'Starting Generate Dimension Correction Set IDs, Dimension Correction Entry No.: %1.', Locked = true, Comment = '%1 Dimension Correction Entry No.';
        CompletedGenerateDimensionSetIdsLbl: Label 'Completed Generate Dimension Correction Set IDs, Dimension Correction Entry No.: %1.', Locked = true, Comment = '%1 Dimension Correction Entry No.';
        NoLedgerEntriesFoundErr: Label 'No ledger entries were found for this dimension correction';
        NoSelectionCriteriaFoundErr: Label 'No selection criteria was found for this dimension correction.';
        NoChangesFoundErr: Label 'No dimension values need to be updated.';
        StartingGenerateSelectedEntriesLbl: Label 'Starting Generate Selected Entries, Dimension Correction Entry No.: %1.', Locked = true, Comment = '%1 Dimension Correction Entry No.';
        CompletedGenerateSelectedEntriesLbl: Label 'Completed Generate Selected Entries, Dimension Correction Entry No.: %1.', Locked = true, Comment = '%1 Dimension Correction Entry No.';
        StartingValidateDimensionCorrectionChangesTelemetryLbl: Label 'Starting Validate Dimension Correction Changes, Dimension Correction Entry No.: %1', Locked = true, Comment = '%1 Dimension Correction Entry No.';
        CompletedValidateDimensionCorrectionChangesTelemetryLbl: Label 'Completed Validate Dimension Correction Changes, Dimension Correction Entry No.: %1.', Locked = true, Comment = '%1 Dimension Correction Entry No., %2 number of updated entries';
        DimensionCorrectionTok: Label 'DimensionCorrection', Locked = true;
}