Codeunit 407 Adjust Gen. Journal Balance

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

Events, 3

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Finance/GeneralLedger/Journal/AdjustGenJournalBalance.Codeunit.al198 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.Finance.Currency;

/// <summary>
/// Provides functionality to insert LCY rounding correction lines in general journals to balance foreign currency transactions.
/// Calculates and creates rounding adjustment entries when foreign currency amounts don't balance perfectly in local currency.
/// </summary>
/// <remarks>
/// Core functionality for multi-currency journal posting accuracy. Ensures journal batches balance correctly in local currency
/// when foreign currency transactions create minor rounding differences during currency conversion.
/// Key features: Automatic rounding detection, correction line creation, currency-specific rounding account assignment.
/// Integration: Uses currency setup rounding accounts and general ledger posting groups for correction entries.
/// </remarks>
codeunit 407 "Adjust Gen. Journal Balance"
{
    TableNo = "Gen. Journal Line";

    trigger OnRun()
    var
        GenJnlLine: Record "Gen. Journal Line";
        PrevGenJnlLine: Record "Gen. Journal Line";
        CorrectionEntry: Boolean;
        TotalAmountLCY: Decimal;
    begin
        TempCurrTotalBuffer.DeleteAll();
        GenJnlLine.SetRange("Journal Template Name", Rec."Journal Template Name");
        GenJnlLine.SetRange("Journal Batch Name", Rec."Journal Batch Name");

        OnRunOnBeforeGenJnlLineFind(GenJnlLine);
        if not GenJnlLine.Find('-') then
            exit;
        PrevGenJnlLine := GenJnlLine;
        CorrectionEntry := true;
        TotalAmountLCY := 0;
        repeat
            if (GenJnlLine."Posting Date" <> PrevGenJnlLine."Posting Date") or
               (GenJnlLine."Document No." <> PrevGenJnlLine."Document No.")
            then begin
                if CheckCurrBalance() and (TotalAmountLCY <> 0) then begin
                    PrevGenJnlLine.Correction := CorrectionEntry;
                    InsertCorrectionLines(GenJnlLine, PrevGenJnlLine);
                end;
                TotalAmountLCY := 0;
                TempCurrTotalBuffer.DeleteAll();
                CorrectionEntry := true;
                PrevGenJnlLine := GenJnlLine;
            end;
            TotalAmountLCY := TotalAmountLCY + GenJnlLine."Amount (LCY)";
            if GenJnlLine."Bal. Account No." = '' then begin
                if TempCurrTotalBuffer.Get(GenJnlLine."Currency Code") then begin
                    TempCurrTotalBuffer."Total Amount" :=
                      TempCurrTotalBuffer."Total Amount" + GenJnlLine.Amount;
                    TempCurrTotalBuffer."Total Amount (LCY)" :=
                      TempCurrTotalBuffer."Total Amount (LCY)" + GenJnlLine."Amount (LCY)";
                    TempCurrTotalBuffer.Modify();
                end else begin
                    TempCurrTotalBuffer."Currency Code" := GenJnlLine."Currency Code";
                    TempCurrTotalBuffer."Total Amount" := GenJnlLine.Amount;
                    TempCurrTotalBuffer."Total Amount (LCY)" := GenJnlLine."Amount (LCY)";
                    TempCurrTotalBuffer.Insert();
                end;
                CorrectionEntry := CorrectionEntry and GenJnlLine.Correction;
            end;

            if GenJnlLine."Document Type" <> PrevGenJnlLine."Document Type" then
                GenJnlLine."Document Type" := GenJnlLine."Document Type"::" ";
            if GenJnlLine."Business Unit Code" <> PrevGenJnlLine."Business Unit Code" then
                GenJnlLine."Business Unit Code" := '';
            if GenJnlLine."Reason Code" <> PrevGenJnlLine."Reason Code" then
                GenJnlLine."Reason Code" := '';
            if GenJnlLine."Recurring Method" <> PrevGenJnlLine."Recurring Method" then
                GenJnlLine."Recurring Method" := GenJnlLine."Recurring Method"::" ";
            if GenJnlLine."Recurring Frequency" <> PrevGenJnlLine."Recurring Frequency" then
                Evaluate(GenJnlLine."Recurring Frequency", '<>');

            PrevGenJnlLine := GenJnlLine;
        until GenJnlLine.Next() = 0;

        Clear(PrevGenJnlLine);

        if CheckCurrBalance() and (TotalAmountLCY <> 0) then begin
            GenJnlLine.Correction := CorrectionEntry;
            InsertCorrectionLines(PrevGenJnlLine, GenJnlLine);
        end;
    end;

    var
        TempCurrTotalBuffer: Record "Currency Total Buffer" temporary;

#pragma warning disable AA0074
#pragma warning disable AA0470
        Text000: Label 'The program cannot find a key between line number %1 and line number %2.';
        Text002: Label 'Rounding correction for %1';
#pragma warning restore AA0470
#pragma warning restore AA0074

    local procedure CheckCurrBalance(): Boolean
    var
        InBalance: Boolean;
    begin
        InBalance := true;
        if TempCurrTotalBuffer.Find('-') then
            repeat
                InBalance := InBalance and (TempCurrTotalBuffer."Total Amount" = 0)
            until (not InBalance) or (TempCurrTotalBuffer.Next() = 0);
        exit(InBalance);
    end;

    local procedure InsertCorrectionLines(var GenJnlLine2: Record "Gen. Journal Line"; var PrevGenJnlLine2: Record "Gen. Journal Line")
    var
        Currency: Record Currency;
        NewGenJnlLine: Record "Gen. Journal Line";
    begin
        NewGenJnlLine := PrevGenJnlLine2;

        TempCurrTotalBuffer.SetFilter("Currency Code", '<>%1', '');
        TempCurrTotalBuffer.SetRange("Total Amount", 0);

        if TempCurrTotalBuffer.Find('-') then
            repeat
                Currency.Get(TempCurrTotalBuffer."Currency Code");
                NewGenJnlLine.Init();
                if GenJnlLine2."Line No." = 0 then
                    NewGenJnlLine."Line No." := NewGenJnlLine."Line No." + 10000
                else
                    if GenJnlLine2."Line No." >= NewGenJnlLine."Line No." + 2 then
                        NewGenJnlLine."Line No." := (NewGenJnlLine."Line No." + GenJnlLine2."Line No.") div 2
                    else
                        Error(
                          Text000,
                          PrevGenJnlLine2."Line No.",
                          GenJnlLine2."Line No.");
                NewGenJnlLine."Document Type" := PrevGenJnlLine2."Document Type";
                NewGenJnlLine."Account Type" := NewGenJnlLine."Account Type"::"G/L Account";
                NewGenJnlLine.Correction := PrevGenJnlLine2.Correction;
                if NewGenJnlLine.Correction xor (TempCurrTotalBuffer."Total Amount (LCY)" <= 0) then
                    NewGenJnlLine.Validate("Account No.", Currency.GetConvLCYRoundingDebitAccount())
                else
                    NewGenJnlLine.Validate("Account No.", Currency.GetConvLCYRoundingCreditAccount());
                NewGenJnlLine."Posting Date" := PrevGenJnlLine2."Posting Date";
                NewGenJnlLine."Document No." := PrevGenJnlLine2."Document No.";
                NewGenJnlLine.Description := StrSubstNo(Text002, TempCurrTotalBuffer."Currency Code");
                NewGenJnlLine.Validate(Amount, -TempCurrTotalBuffer."Total Amount (LCY)");
                NewGenJnlLine."Source Code" := PrevGenJnlLine2."Source Code";
                NewGenJnlLine."Business Unit Code" := PrevGenJnlLine2."Business Unit Code";
                NewGenJnlLine."Reason Code" := PrevGenJnlLine2."Reason Code";
                NewGenJnlLine."Recurring Method" := PrevGenJnlLine2."Recurring Method";
                NewGenJnlLine."Recurring Frequency" := PrevGenJnlLine2."Recurring Frequency";
                NewGenJnlLine."Posting No. Series" := PrevGenJnlLine2."Posting No. Series";
                OnBeforeGenJnlLineInsert(NewGenJnlLine, GenJnlLine2, PrevGenJnlLine2);
                if TempCurrTotalBuffer."Total Amount (LCY)" <> 0 then begin
                    OnInsertCorrectionLinesOnBeforeNewGenJnlLineInsert(GenJnlLine2, PrevGenJnlLine2, NewGenJnlLine);
                    NewGenJnlLine.Insert();
                end;
            until TempCurrTotalBuffer.Next() = 0;
    end;

    /// <summary>
    /// Integration event raised before inserting currency rounding correction lines into the general journal.
    /// Enables custom modification of correction line data before insertion.
    /// </summary>
    /// <param name="NewGenJnlLine">Correction journal line being prepared for insertion</param>
    /// <param name="GenJnlLine2">Current journal line context</param>
    /// <param name="PrevGenJnlLine2">Previous journal line used as template</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeGenJnlLineInsert(var NewGenJnlLine: Record "Gen. Journal Line"; GenJnlLine2: Record "Gen. Journal Line"; PrevGenJnlLine2: Record "Gen. Journal Line")
    begin
    end;

    /// <summary>
    /// Integration event raised in correction line insertion process before the new correction line is inserted.
    /// Provides final opportunity to modify correction line data based on journal context.
    /// </summary>
    /// <param name="GenJnlLine2">Current journal line context for correction</param>
    /// <param name="PrevGenJnlLine2">Previous journal line used as template</param>
    /// <param name="NewGenJnlLine">Correction line ready for insertion</param>
    [IntegrationEvent(false, false)]
    local procedure OnInsertCorrectionLinesOnBeforeNewGenJnlLineInsert(var GenJnlLine2: Record "Gen. Journal Line"; var PrevGenJnlLine2: Record "Gen. Journal Line"; var NewGenJnlLine: Record "Gen. Journal Line")
    begin
    end;

    /// <summary>
    /// Integration event raised in the main process before finding general journal lines for balance adjustment.
    /// Enables custom filtering or setup of journal line selection criteria.
    /// </summary>
    /// <param name="GenJnlLine">General journal line record with filters applied</param>
    [IntegrationEvent(false, false)]
    local procedure OnRunOnBeforeGenJnlLineFind(var GenJnlLine: Record "Gen. Journal Line")
    begin
    end;
}