Codeunit 9081 Check Gen. Jnl. Line. Backgr.

App
Base Application
Namespace
Microsoft.Finance.GeneralLedger.Journal
Versions
17-28

Procedures, 1Events, 2

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Finance/GeneralLedger/Journal/CheckGenJnlLineBackgr.Codeunit.al264 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.GeneralLedger.Journal;

using Microsoft.Utilities;
using System.Utilities;

/// <summary>
/// Provides background validation of general journal lines for real-time error detection and user feedback.
/// Performs comprehensive line validation checks without blocking the user interface during data entry.
/// </summary>
/// <remarks>
/// Background processing component for journal line validation. Integrates with the journal error management system
/// to provide immediate feedback on validation issues while users work with journal entries.
/// Key features: Asynchronous validation processing, real-time error reporting, document balance checking, comprehensive business rule validation.
/// Integration: Works with journal error factboxes and background error handling framework for seamless user experience.
/// </remarks>
codeunit 9081 "Check Gen. Jnl. Line. Backgr."
{
    trigger OnRun()
    var
        TempErrorMessage: Record "Error Message" temporary;
        Args: Dictionary of [Text, Text];
        Results: Dictionary of [Text, Text];
    begin
        Args := Page.GetBackgroundParameters();

        RunCheck(Args, TempErrorMessage);

        PackErrorMessagesToResults(TempErrorMessage, Results);

        Page.SetBackgroundTaskResult(Results);
    end;

    var
        BackgroundErrorHandlingMgt: Codeunit "Background Error Handling Mgt.";
        DocumentOutOfBalanceErr: Label 'Document No. %1 is out of balance by %2', Comment = '%1 - document number, %2 = amount';
        RecurringDocumentOutOfBalanceErr: Label 'Document No. %1 is out of balance.', Comment = '%1 - document number';
        RecurringLineIsExpiredErr: Label 'The posting date %1 must be less or equal the expiration date %2 in the document %3.', Comment = '%1 - posting date, %2 - expiration date, %3 - document number';
        RecurringLineIsOutOfWorkdateErr: Label 'The posting date %1 must be less or equal the working date %2 in the document %3.', Comment = '%1 - posting date, %2 - working date, %3 - document number';

    /// <summary>
    /// Executes comprehensive validation checks for general journal lines based on provided parameters.
    /// Performs document balance validation, recurring journal checks, and integration with standard line validation routines.
    /// </summary>
    /// <param name="Args">Dictionary containing error handling parameters for batch and line filtering</param>
    /// <param name="TempErrorMessage">Temporary error message record to collect and return validation errors</param>
    /// <remarks>
    /// Supports both full batch checking and targeted document validation based on parameter settings.
    /// Integrates with background error handling framework for real-time journal validation feedback.
    /// </remarks>
    procedure RunCheck(Args: Dictionary of [Text, Text]; var TempErrorMessage: Record "Error Message" temporary)
    var
        GenJnlLine: Record "Gen. Journal Line";
        TempGenJnlLine: Record "Gen. Journal Line" temporary;
        ErrorHandlingParameters: Record "Error Handling Parameters";
    begin
        ErrorHandlingParameters.FromArgs(Args);

        GenJnlLine.SetRange("Journal Template Name", ErrorHandlingParameters."Journal Template Name");
        GenJnlLine.SetRange("Journal Batch Name", ErrorHandlingParameters."Journal Batch Name");
        if ErrorHandlingParameters."Full Batch Check" then
            CheckGenJnlLines(GenJnlLine, TempErrorMessage)
        else
            if ErrorHandlingParameters."Line Modified" then begin
                CheckDocument(GenJnlLine, ErrorHandlingParameters."Previous Document No.", ErrorHandlingParameters."Previous Posting Date", TempErrorMessage);

                if ErrorHandlingParameters.IsGenJnlDocumentChanged() then
                    CheckDocument(GenJnlLine, ErrorHandlingParameters."Document No.", ErrorHandlingParameters."Posting Date", TempErrorMessage);
            end;

        BackgroundErrorHandlingMgt.GetDeletedDocumentsFromArgs(Args, TempGenJnlLine);
        if TempGenJnlLine.FindSet() then
            repeat
                CheckDocument(GenJnlLine, TempGenJnlLine."Document No.", TempGenJnlLine."Posting Date", TempErrorMessage);
            until TempGenJnlLine.Next() = 0;
    end;

    local procedure CheckDocument(var GenJnlLine: Record "Gen. Journal Line"; DocumentNo: Code[20]; PostingDate: Date; var TempErrorMessage: Record "Error Message" temporary)
    begin
        GenJnlLine.SetRange("Document No.", DocumentNo);
        GenJnlLine.SetRange("Posting Date", PostingDate);
        CheckGenJnlLines(GenJnlLine, TempErrorMessage);
    end;

    local procedure CheckGenJnlLines(var GenJnlLine: Record "Gen. Journal Line"; var TempErrorMessage: Record "Error Message" temporary)
    begin
        if GenJnlLine.FindSet() then
            repeat
                CheckGenJnlLine(GenJnlLine, TempErrorMessage);
            until GenJnlLine.Next() = 0;
    end;

    local procedure CheckGenJnlLine(var GenJnlLine: Record "Gen. Journal Line"; var TempErrorMessage: Record "Error Message" temporary)
    begin
        RunGenJnlCheckLineCodeunit(GenJnlLine, TempErrorMessage);
        CheckRecurringGenJnlLine(GenJnlLine, TempErrorMessage);
        CheckGenJnlLineDocBalance(GenJnlLine, TempErrorMessage);

        OnAfterCheckGenJnlLine(GenJnlLine, TempErrorMessage);
    end;

    [ErrorBehavior(ErrorBehavior::Collect)]
    local procedure RunGenJnlCheckLineCodeunit(GenJnlLine: Record "Gen. Journal Line"; var TempErrorMessage: Record "Error Message" temporary)
    var
        TempLineErrorMessage: Record "Error Message" temporary;
        GenJnlCheckLine: Codeunit "Gen. Jnl.-Check Line";
        ErrorMessageMgt: Codeunit "Error Message Management";
    begin
        GenJnlCheckLine.SetLogErrorMode(true);

        if GenJnlCheckLine.Run(GenJnlLine) then begin
            GenJnlCheckLine.GetErrors(TempLineErrorMessage);
            ErrorMessageMgt.CollectErrors(TempLineErrorMessage);
            CopyErrorsToBuffer(TempLineErrorMessage, TempErrorMessage);
        end else begin
            InsertTempLineErrorMessage(
                TempLineErrorMessage,
                GenJnlLine.RecordId(),
                0,
                GetLastErrorText(),
                false);

            CopyErrorsToBuffer(TempLineErrorMessage, TempErrorMessage);
        end;
    end;

    local procedure CheckRecurringGenJnlLine(var GenJnlLine: Record "Gen. Journal Line"; var TempErrorMessage: Record "Error Message" temporary)
    var
        TempLineErrorMessage: Record "Error Message" temporary;
        Description: Text;
    begin
        if GenJnlLine."Recurring Method" = GenJnlLine."Recurring Method"::" " then
            exit;

        if (GenJnlLine."Expiration Date" <> 0D) and (GenJnlLine."Expiration Date" < GenJnlLine."Posting Date") then begin
            Description := StrSubstNo(RecurringLineIsExpiredErr, GenJnlLine."Posting Date", GenJnlLine."Expiration Date", GenJnlLine."Document No.");
            InsertTempLineErrorMessage(
                    TempLineErrorMessage,
                    GenJnlLine.RecordId(),
                    GenJnlLine.FieldNo("Expiration Date"),
                    Description,
                    false);
            CopyErrorsToBuffer(TempLineErrorMessage, TempErrorMessage);
        end;

        if GenJnlLine."Posting Date" > WorkDate() then begin
            Description := StrSubStNo(RecurringLineIsOutOfWorkdateErr, GenJnlLine."Posting Date", WorkDate(), GenJnlLine."Document No.");
            InsertTempLineErrorMessage(
                TempLineErrorMessage,
                GenJnlLine.RecordId(),
                GenJnlLine.FieldNo("Posting Date"),
                Description,
                false);
            CopyErrorsToBuffer(TempLineErrorMessage, TempErrorMessage);
        end;
    end;

    local procedure CheckGenJnlLineDocBalance(GenJnlLine: Record "Gen. Journal Line"; var TempErrorMessage: Record "Error Message" temporary)
    var
        TempLineErrorMessage: Record "Error Message" temporary;
        DocumentBalance: Decimal;
        Description: Text;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckGenJnlLineDocBalance(GenJnlLine, TempErrorMessage, IsHandled);
        if IsHandled then
            exit;

        GenJnlLine.SetRange("Journal Template Name", GenJnlLine."Journal Template Name");
        GenJnlLine.SetRange("Journal Batch Name", GenJnlLine."Journal Batch Name");
        DocumentBalance := GenJnlLine.GetDocumentBalance(GenJnlLine);
        if DocumentBalance <> 0 then begin
            if GenJnlLine."Recurring Method" <> GenJnlLine."Recurring Method"::" " then
                Description := StrSubstNo(RecurringDocumentOutOfBalanceErr, GenJnlLine."Document No.")
            else
                Description := StrSubstNo(DocumentOutOfBalanceErr, GenJnlLine."Document No.", DocumentBalance);
            InsertTempLineErrorMessage(
                TempLineErrorMessage,
                GenJnlLine.RecordId(),
                GenJnlLine.FieldNo("Balance (LCY)"),
                Description,
                DocumentBalanceErrorExists(Description, TempErrorMessage));

            TempErrorMessage.Reset();
            CopyErrorsToBuffer(TempLineErrorMessage, TempErrorMessage);
        end;
    end;

    local procedure DocumentBalanceErrorExists(Description: Text; var TempErrorMessage: Record "Error Message" temporary): Boolean
    begin
        TempErrorMessage.SetRange("Message", Description);
        exit(not TempErrorMessage.IsEmpty());
    end;

    local procedure InsertTempLineErrorMessage(var TempLineErrorMessage: Record "Error Message" temporary; ContextRecordId: RecordId; ContextFieldNo: Integer; Description: Text; Duplicate: Boolean)
    begin
        TempLineErrorMessage."Record ID" := ContextRecordId;
        TempLineErrorMessage."Field Number" := ContextFieldNo;
        TempLineErrorMessage."Table Number" := Database::"Gen. Journal Line";
        TempLineErrorMessage."Context Record ID" := ContextRecordId;
        TempLineErrorMessage."Context Field Number" := ContextFieldNo;
        TempLineErrorMessage."Context Table Number" := Database::"Gen. Journal Line";
        TempLineErrorMessage."Message" := CopyStr(Description, 1, MaxStrLen(TempLineErrorMessage."Message"));
        TempLineErrorMessage.Duplicate := Duplicate;
        TempLineErrorMessage.Insert();
    end;

    local procedure CopyErrorsToBuffer(var TempLineErrorMessage: Record "Error Message" temporary; var TempErrorMessage: Record "Error Message" temporary)
    var
        ID: Integer;
    begin
        if TempErrorMessage.FindLast() then;
        ID := TempErrorMessage.ID + 1;

        if TempLineErrorMessage.FindSet() then
            repeat
                TempErrorMessage.TransferFields(TempLineErrorMessage);
                TempErrorMessage.SetErrorCallStack(TempLineErrorMessage.GetErrorCallStack());
                TempErrorMessage.ID := ID;
                TempErrorMessage.Insert();
                ID += 1;
            until TempLineErrorMessage.Next() = 0;
    end;

    local procedure PackErrorMessagesToResults(var TempErrorMessage: Record "Error Message" temporary; var Results: Dictionary of [Text, Text])
    var
        ErrorMessageMgt: Codeunit "Error Message Management";
        JSON: Text;
    begin
        if TempErrorMessage.FindSet() then
            repeat
                JSON := ErrorMessageMgt.ErrorMessage2JSON(TempErrorMessage);
                Results.Add(Format(TempErrorMessage.ID), JSON);
            until TempErrorMessage.Next() = 0;
    end;

    /// <summary>
    /// Integration event that occurs after checking a general journal line during background validation.
    /// Allows additional custom validation logic and error message collection.
    /// </summary>
    /// <param name="GenJnlLine">The general journal line that was validated.</param>
    /// <param name="TempErrorMessage">Error message record for collecting validation errors.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterCheckGenJnlLine(var GenJnlLine: Record "Gen. Journal Line"; var TempErrorMessage: Record "Error Message" temporary)
    begin
    end;

    /// <summary>
    /// Integration event that occurs before checking general journal line document balance validation.
    /// Allows custom handling of document balance validation logic during background processing.
    /// </summary>
    /// <param name="GenJnlLine">The general journal line being validated for document balance.</param>
    /// <param name="TempErrorMessage">Error message record for collecting validation errors.</param>
    /// <param name="IsHandled">Set to true to skip standard document balance validation.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckGenJnlLineDocBalance(GenJnlLine: Record "Gen. Journal Line"; var TempErrorMessage: Record "Error Message"; var IsHandled: Boolean)
    begin
    end;
}