Codeunit 11 Gen. Jnl.-Check Line

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

Procedures, 31Events, 41Obsolete, 5

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Finance/GeneralLedger/Journal/GenJnlCheckLine.Codeunit.al1758 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.Bank.BankAccount;
using Microsoft.CostAccounting.Setup;
using Microsoft.CRM.Campaign;
using Microsoft.CRM.Team;
using Microsoft.Finance.Deferral;
using Microsoft.Finance.Dimension;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Finance.SpendRequest;
using Microsoft.Finance.VAT.Calculation;
using Microsoft.Finance.VAT.Setup;
using Microsoft.FixedAssets.Journal;
using Microsoft.Foundation.PaymentTerms;
using Microsoft.Foundation.Period;
using Microsoft.HumanResources.Employee;
using Microsoft.Intercompany.BankAccount;
using Microsoft.Intercompany.GLAccount;
using Microsoft.Intercompany.Partner;
using Microsoft.Projects.Project.Job;
using Microsoft.Purchases.Payables;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
using Microsoft.Sales.Receivables;
using System.Environment.Configuration;
using System.Security.User;
using System.Utilities;

/// <summary>
/// Provides comprehensive validation and checking functionality for general journal lines before posting.
/// Performs critical business logic validation including account validation, dimension checking, and posting setup verification.
/// </summary>
/// <remarks>
/// Core validation engine for journal line integrity including account existence, dimension validation, and posting setup verification.
/// Key validations: Account and balancing account validation, dimension consistency, VAT setup verification, and business rule enforcement.
/// Extensibility: Integration events enable custom validation rules and business logic checks throughout the validation process.
/// </remarks>
codeunit 11 "Gen. Jnl.-Check Line"
{
    Permissions = tabledata "General Posting Setup" = rimd,
                  tabledata "Cost Accounting Setup" = R,
                  tabledata "G/L Account" = r,
                  tabledata "Spend Request" = r,
                  tabledata "Payment Terms" = R;
    TableNo = "Gen. Journal Line";

    trigger OnRun()
    begin
        RunCheck(Rec);
    end;

    var
        GLSetup: Record "General Ledger Setup";
        GenJnlTemplate: Record "Gen. Journal Template";
        GenJnlBatch: Record "Gen. Journal Batch";
        CostAccSetup: Record "Cost Accounting Setup";
        TempErrorMessage: Record "Error Message" temporary;
        DimMgt: Codeunit DimensionManagement;
        CostAccMgt: Codeunit "Cost Account Mgt";
        ApplicationAreaMgmt: Codeunit System.Environment.Configuration."Application Area Mgmt.";
        ErrorMessageMgt: Codeunit "Error Message Management";
        SkipFiscalYearCheck: Boolean;
        GenJnlTemplateFound: Boolean;
        OverrideDimErr: Boolean;
        LogErrorMode: Boolean;
        IsBatchMode: Boolean;
        IgnoreJournalTemplNameMandatoryCheck: Boolean;
        IsDeferralPostingAllowed: Boolean;

#pragma warning disable AA0074
        Text000: Label 'can only be a closing date for G/L entries';
        Text001: Label 'is not within your range of allowed posting dates';
#pragma warning disable AA0470
        Text002: Label '%1 or %2 must be G/L Account or Bank Account.';
        Text003: Label 'must have the same sign as %1';
        Text004: Label 'You must not specify %1 when %2 is %3.';
        Text005: Label '%1 + %2 must be %3.';
        Text006: Label '%1 + %2 must be -%3.';
#pragma warning restore AA0470
        Text007: Label 'must be positive';
        Text008: Label 'must be negative';
#pragma warning disable AA0470
        Text009: Label 'must have a different sign than %1';
        Text010: Label '%1 %2 and %3 %4 is not allowed.';
        Text011: Label 'The combination of dimensions used in %1 %2, %3, %4 is blocked. %5';
        Text012: Label 'A dimension used in %1 %2, %3, %4 has caused an error. %5';
#pragma warning restore AA0470
#pragma warning restore AA0074
        DuplicateRecordErr: Label 'Document No. %1 already exists. It is not possible to calculate new deferrals for a Document No. that already exists.', Comment = '%1=Document No.';
        SpecifyGenPostingTypeErr: Label 'Posting to Account %1 must either be of type Purchase or Sale (see %2), because there are specified values in one of the following fields: %3, %4 , %5, or %6', comment = '%1 an G/L Account number;%2 = Gen. Posting Type; %3 = Gen. Bus. Posting Group; %4 = Gen. Prod. Posting Group; %5 = VAT Bus. Posting Group, %6 = VAT Prod. Posting Group';
        SalesDocAlreadyExistsErr: Label 'Sales %1 %2 already exists.', Comment = '%1 = Document Type; %2 = Document No.';
        PurchDocAlreadyExistsErr: Label 'Purchase %1 %2 already exists.', Comment = '%1 = Document Type; %2 = Document No.';
        EmployeeBalancingDocTypeErr: Label 'must be empty or set to Payment when Balancing Account Type field is set to Employee';
        EmployeeAccountDocTypeErr: Label 'must be empty or set to Payment when Account Type field is set to Employee';
        GLAccCurrencyDoesNotMatchErr: Label 'The currency code %1 on general journal line does not match with the currency code %2 of G/L account %3.', Comment = '%1 and %2 - currency code, %3 - G/L Account No.';
        GLAccSourceCurrencyDoesNotMatchErr: Label 'The currency code %1 on general journal line does not match with the any source currency code of G/L account %2.', Comment = '%1 - currency code, %2 - G/L Account No.';
        GLAccSourceCurrencyDoesNotAllowedErr: Label 'The currency code %1 on general journal line does not allowed for posting to G/L account %2.', Comment = '%1 - currency code, %2 - G/L Account No.';
        SpendRequestIsDepletedMsg: Label 'Spend request %1 was approved for %2 and current allocation is %3.', Comment = '%1 is a document no., %2 and %3 are amounts in local currency.';

    /// <summary>
    /// Performs comprehensive validation checks on a general journal line before posting.
    /// Validates account numbers, amounts, dimensions, dates, and posting setup requirements.
    /// </summary>
    /// <param name="GenJnlLine">General journal line record to validate</param>
    /// <remarks>
    /// Core validation procedure that ensures journal line integrity before posting.
    /// Performs validation of accounts, balancing accounts, amounts, currency, dates, dimensions, and business rules.
    /// Integration events enable custom validation logic throughout the checking process.
    /// </remarks>
    procedure RunCheck(var GenJnlLine: Record "Gen. Journal Line")
    var
        ICGLAcount: Record "IC G/L Account";
        ICBankAccount: Record "IC Bank Account";
        ErrorMessageHandler: Codeunit "Error Message Handler";
        ErrorContextElement: Codeunit "Error Context Element";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeRunCheck(GenJnlLine, OverrideDimErr, IsHandled);
        if IsHandled then
            exit;

        if LogErrorMode then begin
            ErrorMessageMgt.Activate(ErrorMessageHandler);
            ErrorMessageMgt.PushContext(ErrorContextElement, GenJnlLine.RecordId, 0, '');
        end;

        GLSetup.Get();
        if GenJnlLine.EmptyLine() then
            exit;

        if not GenJnlTemplateFound then begin
            if GenJnlTemplate.Get(GenJnlLine."Journal Template Name") then;
            GenJnlTemplateFound := true;
        end;

        CheckDates(GenJnlLine);
        GenJnlLine.ValidateSalesPersonPurchaserCode(GenJnlLine);

        TestDocumentNo(GenJnlLine);

        TestAccountAndBalAccountType(GenJnlLine);

        if GenJnlLine."Bal. Account No." = '' then
            GenJnlLine.TestField("Account No.", ErrorInfo.Create());

        CheckZeroAmount(GenJnlLine);

        if ((GenJnlLine.Amount < 0) xor (GenJnlLine."Amount (LCY)" < 0)) and (GenJnlLine.Amount <> 0) and (GenJnlLine."Amount (LCY)" <> 0) then
            GenJnlLine.FieldError("Amount (LCY)", ErrorInfo.Create(StrSubstNo(Text003, GenJnlLine.FieldCaption(Amount)), true));

        if (GenJnlLine."Account Type" = GenJnlLine."Account Type"::"G/L Account") and
           (GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::"G/L Account")
        then
            CheckAppliesToDocNo(GenJnlLine);

        if (GenJnlLine."Recurring Method" in
            [GenJnlLine."Recurring Method"::"B  Balance", GenJnlLine."Recurring Method"::"RB Reversing Balance"]) and
           (GenJnlLine."Currency Code" <> '')
        then
            Error(
                ErrorInfo.Create(
                    StrSubstNo(
                        Text004,
                        GenJnlLine.FieldCaption("Currency Code"), GenJnlLine.FieldCaption("Recurring Method"), GenJnlLine."Recurring Method"),
                    true,
                    GenJnlLine,
                    GenJnlLine.FieldNo("Recurring Method")));

        if GenJnlLine."Account No." <> '' then
            CheckAccountNo(GenJnlLine);

        if GenJnlLine."Bal. Account No." <> '' then
            CheckBalAccountNo(GenJnlLine);
        if GenJnlLine."IC Account No." <> '' then begin
            if GenJnlLine."IC Account Type" = GenJnlLine."IC Account Type"::"G/L Account" then
                if ICGLAcount.Get(GenJnlLine."IC Account No.") then
                    ICGLAcount.TestField(Blocked, false, ErrorInfo.Create());
            if GenJnlLine."IC Account Type" = GenJnlLine."IC Account Type"::"Bank Account" then
                if ICBankAccount.Get(GenJnlLine."IC Account No.") then
                    ICBankAccount.TestField(Blocked, false, ErrorInfo.Create());
        end;

        if ((GenJnlLine."Account Type" = GenJnlLine."Account Type"::"G/L Account") and
            (GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::"G/L Account")) or
           ((GenJnlLine."Document Type" <> GenJnlLine."Document Type"::Invoice) and
            (not
             ((GenJnlLine."Document Type" = GenJnlLine."Document Type"::"Credit Memo") and
              CalcPmtDiscOnCrMemos(GenJnlLine."Payment Terms Code"))))
        then begin
            GenJnlLine.TestField("Pmt. Discount Date", 0D, ErrorInfo.Create());
            GenJnlLine.TestField("Payment Discount %", 0, ErrorInfo.Create());
        end;

        TestAppliesToID(GenJnlLine);

        if (GenJnlLine."Account Type" <> GenJnlLine."Account Type"::"Bank Account") and
           (GenJnlLine."Bal. Account Type" <> GenJnlLine."Bal. Account Type"::"Bank Account")
        then
            GenJnlLine.TestField("Bank Payment Type", GenJnlLine."Bank Payment Type"::" ", ErrorInfo.Create());

        if (GenJnlLine."Account Type" = GenJnlLine."Account Type"::"Fixed Asset") or
           (GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::"Fixed Asset")
        then
            CODEUNIT.Run(CODEUNIT::"FA Jnl.-Check Line", GenJnlLine);

        if (GenJnlLine."Account Type" <> GenJnlLine."Account Type"::"Fixed Asset") and
           (GenJnlLine."Bal. Account Type" <> GenJnlLine."Bal. Account Type"::"Fixed Asset")
        then begin
            GenJnlLine.TestField("Depreciation Book Code", '', ErrorInfo.Create());
            GenJnlLine.TestField("FA Posting Type", 0, ErrorInfo.Create());
        end;

        if GenJnlLine."Deferral Code" <> '' then
            CheckPostedDeferralHeaderExist(GenJnlLine);

        if not OverrideDimErr then
            CheckDimensions(GenJnlLine);

        CheckCurrencyCode(GenJnlLine);

        if CostAccSetup.Get() then
            CostAccMgt.CheckValidCCAndCOInGLEntry(GenJnlLine."Dimension Set ID");

        TestSpendRequest(GenJnlLine);

        OnAfterCheckGenJnlLine(GenJnlLine, ErrorMessageMgt);

        if LogErrorMode then
            ErrorMessageMgt.GetErrors(TempErrorMessage);
    end;

    local procedure TestDocumentNo(var GenJournalLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeTestDocumentNo(GenJournalLine, IsHandled);
        if IsHandled then
            exit;

        GenJournalLine.TestField("Document No.", ErrorInfo.Create());
    end;

    local procedure TestAccountAndBalAccountType(var GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeTestAccountAndBalAccountType(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        if (GenJnlLine."Account Type" in
                 [GenJnlLine."Account Type"::Customer,
                  GenJnlLine."Account Type"::Vendor,
                  GenJnlLine."Account Type"::"Fixed Asset",
                  GenJnlLine."Account Type"::"IC Partner"]) and
                (GenJnlLine."Bal. Account Type" in
                 [GenJnlLine."Bal. Account Type"::Customer,
                  GenJnlLine."Bal. Account Type"::Vendor,
                  GenJnlLine."Bal. Account Type"::"Fixed Asset",
                  GenJnlLine."Bal. Account Type"::"IC Partner"])
             then
            Error(
                ErrorInfo.Create(
                    StrSubstNo(
                    Text002,
                    GenJnlLine.FieldCaption("Account Type"), GenJnlLine.FieldCaption("Bal. Account Type")),
                true,
                GenJnlLine,
                GenJnlLine.FieldNo("Account Type")));
    end;

    local procedure TestAppliesToID(var GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeTestAppliesToID(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        if GenJnlLine."Applies-to Doc. No." <> '' then
            GenJnlLine.TestField("Applies-to ID", '', ErrorInfo.Create());
    end;

    local procedure TestSpendRequest(var GenJnlLine: Record "Gen. Journal Line")
    var
        SpendRequest: Record "Spend Request";
        GLAccount: Record "G/L Account";
        NotificationLifecycleMgt: Codeunit "Notification Lifecycle Mgt.";
        OverspendNotification: Notification;
    begin
        if GenJnlLine."Spend Request No." = '' then begin
            if (GenJnlLine."Account Type" = GenJnlLine."Account Type"::"G/L Account") and (GenJnlLine."Account No." <> '') then
                if GLAccount.Get(GenJnlLine."Account No.") then
                    if GLAccount."Spend Request Required" = GLAccount."Spend Request Required"::Required then
                        GenJnlLine.TestField("Spend Request No.");
            if (GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::"G/L Account") and (GenJnlLine."Bal. Account No." <> '') then
                if GLAccount.Get(GenJnlLine."Bal. Account No.") then
                    if GLAccount."Spend Request Required" = GLAccount."Spend Request Required"::Required then
                        GenJnlLine.TestField("Spend Request No.");
        end else begin
            SpendRequest.SetAutoCalcFields("Total Spent Amount (LCY)");
            SpendRequest.Get(GenJnlLine."Spend Request No.");
            // This spend request may have been closed in a prior entry in this transaction
            if not ((SpendRequest.Status = SpendRequest.Status::Closed) and (SpendRequest."Closed By Document No." = GenJnlLine."Document No.")) then
                SpendRequest.TestField(Status, SpendRequest.Status::Approved);
            if Abs(GenJnlLine."Amount (LCY)") > SpendRequest."Total Expected Amount (LCY)" - SpendRequest."Total Spent Amount (LCY)" then begin
                OverspendNotification.Scope := OverspendNotification.Scope::LocalScope;
                OverspendNotification.Message := StrSubstNo(SpendRequestIsDepletedMsg, SpendRequest."No.", SpendRequest."Total Expected Amount (LCY)", SpendRequest."Total Spent Amount (LCY)");
                NotificationLifecycleMgt.SendNotification(OverspendNotification, GenJnlLine.RecordId);
            end;
        end;
    end;

    /// <summary>
    /// Retrieves error messages collected during journal line validation checking.
    /// Returns all validation errors found during the RunCheck procedure execution.
    /// </summary>
    /// <param name="NewTempErrorMessage">Temporary error message record to receive collected errors</param>
    procedure GetErrors(var NewTempErrorMessage: Record "Error Message" temporary)
    begin
        NewTempErrorMessage.Copy(TempErrorMessage, true);
    end;

    local procedure CalcPmtDiscOnCrMemos(PaymentTermsCode: Code[10]): Boolean
    var
        PaymentTerms: Record "Payment Terms";
    begin
        if PaymentTermsCode <> '' then begin
            PaymentTerms.Get(PaymentTermsCode);
            exit(PaymentTerms."Calc. Pmt. Disc. on Cr. Memos");
        end;
    end;

    /// <summary>
    /// Checks if a posting date is not allowed based on general ledger setup restrictions.
    /// Validates against posting date restrictions in general ledger setup.
    /// </summary>
    /// <param name="PostingDate">Date to validate for posting allowance</param>
    /// <returns>True if the posting date is not allowed, false if it is allowed</returns>
    procedure DateNotAllowed(PostingDate: Date): Boolean
    var
        SetupRecordID: RecordID;
    begin
        exit(IsDateNotAllowed(PostingDate, SetupRecordID));
    end;

    /// <summary>
    /// Checks if a posting date is not allowed for deferral transactions based on setup restrictions.
    /// Validates against deferral-specific posting date restrictions.
    /// </summary>
    /// <param name="PostingDate">Date to validate for deferral posting allowance</param>
    /// <returns>True if the deferral posting date is not allowed, false if it is allowed</returns>
    procedure DeferralPostingDateNotAllowed(PostingDate: Date): Boolean
    var
        SetupRecordID: RecordID;
    begin
        exit(IsDeferralPostingDateNotAllowed(PostingDate, SetupRecordID));
    end;

    /// <summary>
    /// Checks if a posting date is not allowed for the specified journal template based on setup restrictions.
    /// Validates posting date against template-specific and user-specific posting date limitations.
    /// </summary>
    /// <param name="PostingDate">Date to validate for posting allowance</param>
    /// <param name="TemplateName">Journal template code to check posting date restrictions for</param>
    /// <returns>True if the posting date is not allowed, false if it is allowed</returns>
    procedure DateNotAllowed(PostingDate: Date; TemplateName: Code[20]): Boolean
    var
        SetupRecordID: RecordID;
    begin
        exit(IsDateNotAllowed(PostingDate, SetupRecordID, TemplateName));
    end;

    /// <summary>
    /// Checks if a posting date is not allowed based on user setup and general ledger setup restrictions.
    /// Provides detailed setup record information for troubleshooting date restriction issues.
    /// </summary>
    /// <param name="PostingDate">Date to validate for posting allowance</param>
    /// <param name="SetupRecordID">Returns the record ID of the setup causing date restriction</param>
    /// <returns>True if the posting date is not allowed, false if it is allowed</returns>
    procedure IsDateNotAllowed(PostingDate: Date; var SetupRecordID: RecordID) DateIsNotAllowed: Boolean
    var
        UserSetupManagement: Codeunit "User Setup Management";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeIsDateNotAllowed(PostingDate, SetupRecordID, GenJnlBatch, DateIsNotAllowed, IsHandled);
        if IsHandled then
            exit;

        DateIsNotAllowed := not UserSetupManagement.IsPostingDateValidWithSetup(PostingDate, SetupRecordID);
        OnAfterDateNoAllowed(PostingDate, DateIsNotAllowed);
        exit(DateIsNotAllowed);
    end;

    /// <summary>
    /// Checks if a posting date is not allowed for deferral transactions with detailed setup information.
    /// Validates against deferral-specific posting date restrictions and provides setup record details.
    /// </summary>
    /// <param name="PostingDate">Date to validate for deferral posting allowance</param>
    /// <param name="SetupRecordID">Returns the record ID of the setup causing date restriction</param>
    /// <returns>True if the deferral posting date is not allowed, false if it is allowed</returns>
    procedure IsDeferralPostingDateNotAllowed(PostingDate: Date; var SetupRecordID: RecordID) DateIsNotAllowed: Boolean
    var
        UserSetupManagement: Codeunit "User Setup Management";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeIsDeferralPostingDateNotAllowed(PostingDate, SetupRecordID, GenJnlBatch, DateIsNotAllowed, IsHandled);
        if IsHandled then
            exit;

        DateIsNotAllowed := not UserSetupManagement.IsDeferralPostingDateValidWithSetup(PostingDate, SetupRecordID);
        OnAfterDeferralPostingDateNoAllowed(PostingDate, DateIsNotAllowed);
        exit(DateIsNotAllowed);
    end;

    /// <summary>
    /// Checks if a posting date is not allowed for the specified journal template with detailed setup information.
    /// Validates posting date against template-specific restrictions and provides setup record details for troubleshooting.
    /// </summary>
    /// <param name="PostingDate">Date to validate for posting allowance</param>
    /// <param name="SetupRecordID">Returns the record ID of the setup causing date restriction</param>
    /// <param name="TemplateName">Journal template code to check posting date restrictions for</param>
    /// <returns>True if the posting date is not allowed, false if it is allowed</returns>
    procedure IsDateNotAllowed(PostingDate: Date; var SetupRecordID: RecordID; TemplateName: Code[20]) DateIsNotAllowed: Boolean
    var
        UserSetupManagement: Codeunit "User Setup Management";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeIsDateNotAllowed(PostingDate, SetupRecordID, GenJnlBatch, DateIsNotAllowed, IsHandled);
        if IsHandled then
            exit;

        DateIsNotAllowed :=
          not UserSetupManagement.IsPostingDateValidWithGenJnlTemplateWithSetup(PostingDate, TemplateName, SetupRecordID);
        OnAfterDateNoAllowed(PostingDate, DateIsNotAllowed);
        exit(DateIsNotAllowed);
    end;

    /// <summary>
    /// Sets the general journal batch context for validation operations.
    /// Configures the batch context used during journal line validation checks.
    /// </summary>
    /// <param name="NewGenJnlBatch">General journal batch record to set as validation context</param>
    procedure SetGenJnlBatch(NewGenJnlBatch: Record "Gen. Journal Batch")
    begin
        GenJnlBatch := NewGenJnlBatch;
    end;

    /// <summary>
    /// Sets whether to skip fiscal year validation during date checking.
    /// Controls fiscal year boundary validation during journal line validation.
    /// </summary>
    /// <param name="NewValue">True to skip fiscal year validation, false to perform validation</param>
    procedure SetSkipFiscalYearCheck(NewValue: Boolean)
    begin
        SkipFiscalYearCheck := NewValue;
    end;

    /// <summary>
    /// Validates that journal line amount is not positive and raises error if it is.
    /// Used for transaction types that should only have negative amounts (e.g., payments).
    /// </summary>
    /// <param name="GenJnlLine">General journal line record to validate</param>
    procedure ErrorIfPositiveAmt(GenJnlLine: Record "Gen. Journal Line")
    var
        RaiseError: Boolean;
    begin
        RaiseError := GenJnlLine.Amount > 0;
        OnBeforeErrorIfPositiveAmt(GenJnlLine, RaiseError);
        if RaiseError then
            GenJnlLine.FieldError(Amount, ErrorInfo.Create(Text008, true));
    end;

    /// <summary>
    /// Validates that journal line amount is not negative and raises error if it is.
    /// Used for transaction types that should only have positive amounts (e.g., receipts).
    /// </summary>
    /// <param name="GenJnlLine">General journal line record to validate</param>
    procedure ErrorIfNegativeAmt(GenJnlLine: Record "Gen. Journal Line")
    var
        RaiseError: Boolean;
    begin
        RaiseError := GenJnlLine.Amount < 0;
        OnBeforeErrorIfNegativeAmt(GenJnlLine, RaiseError);
        if RaiseError then
            GenJnlLine.FieldError(Amount, ErrorInfo.Create(Text007, true));
    end;

    /// <summary>
    /// Sets flag to override dimension error handling during validation.
    /// Allows dimension validation errors to be bypassed during checking process.
    /// </summary>
    procedure SetOverDimErr()
    begin
        OverrideDimErr := true;
    end;

    /// <summary>
    /// Sets flag to ignore journal template name mandatory validation.
    /// Bypasses the requirement for journal template name during validation checks.
    /// </summary>
    procedure SetIgnoreJournalTemplNameMandatoryCheck()
    begin
        IgnoreJournalTemplNameMandatoryCheck := true;
    end;

    local procedure CheckDates(GenJnlLine: Record "Gen. Journal Line")
    var
        AccountingPeriodMgt: Codeunit "Accounting Period Mgt.";
        DateCheckDone: Boolean;
        IsHandled: Boolean;
    begin
        OnBeforeCheckDates(GenJnlLine);
        GenJnlLine.TestField("Posting Date", ErrorInfo.Create());
        if GenJnlLine."Posting Date" <> NormalDate(GenJnlLine."Posting Date") then begin
            if (GenJnlLine."Account Type" <> GenJnlLine."Account Type"::"G/L Account") or
               (GenJnlLine."Bal. Account Type" <> GenJnlLine."Bal. Account Type"::"G/L Account")
            then
                GenJnlLine.FieldError("Posting Date", ErrorInfo.Create(Text000, true));
            if not SkipFiscalYearCheck then begin
                IsHandled := false;
                OnBeforeCheckPostingDateInFiscalYear(GenJnlLine, IsHandled);
                if not IsHandled then
                    AccountingPeriodMgt.CheckPostingDateInFiscalYear(GenJnlLine."Posting Date");
            end;
        end;

        if not IgnoreJournalTemplNameMandatoryCheck then
            if GLSetup."Journal Templ. Name Mandatory" then
                GenJnlLine.TestField("Journal Template Name", ErrorInfo.Create());
        if IsDeferralPostingAllowed then begin
            if DeferralPostingDateNotAllowed(GenJnlLine."Posting Date") then
                GenJnlLine.FieldError("Posting Date", ErrorInfo.Create(Text001, true));
            DateCheckDone := true
        end;
        OnBeforeDateNotAllowed(GenJnlLine, DateCheckDone);
        if not DateCheckDone then
            if DateNotAllowed(GenJnlLine."Posting Date", GenJnlLine."Journal Template Name") then
                GenJnlLine.FieldError("Posting Date", ErrorInfo.Create(Text001, true));

        if GenJnlLine."Document Date" <> 0D then
            if (GenJnlLine."Document Date" <> NormalDate(GenJnlLine."Document Date")) and
               ((GenJnlLine."Account Type" <> GenJnlLine."Account Type"::"G/L Account") or
                (GenJnlLine."Bal. Account Type" <> GenJnlLine."Bal. Account Type"::"G/L Account"))
            then
                GenJnlLine.FieldError("Document Date", ErrorInfo.Create(Text000, true));

        if HasVAT(GenJnlLine) then
            CheckVATDate(GenJnlLine);
    end;

    local procedure CheckAccountNo(GenJnlLine: Record "Gen. Journal Line")
    var
        GenJournalTemplate: Record "Gen. Journal Template";
        ICPartner: Record "IC Partner";
        CheckDone: Boolean;
        IsHandled: Boolean;
    begin
        OnBeforeCheckAccountNo(GenJnlLine, CheckDone);
        if CheckDone then
            exit;

        case GenJnlLine."Account Type" of
            GenJnlLine."Account Type"::"G/L Account":
                begin
                    if (((GenJnlLine."Gen. Bus. Posting Group" <> '') or (GenJnlLine."Gen. Prod. Posting Group" <> '') or
                        (GenJnlLine."VAT Bus. Posting Group" <> '') or (GenJnlLine."VAT Prod. Posting Group" <> '')) and
                        (GenJnlLine."Gen. Posting Type" = GenJnlLine."Gen. Posting Type"::" "))
                    then
                        Error(
                            ErrorInfo.Create(
                                StrSubstNo(
                                    SpecifyGenPostingTypeErr, GenJnlLine."Account No.", GenJnlLine.FieldCaption("Gen. Posting Type"),
                                    GenJnlLine.FieldCaption("Gen. Bus. Posting Group"), GenJnlLine.FieldCaption("Gen. Prod. Posting Group"),
                                    GenJnlLine.FieldCaption("VAT Bus. Posting Group"), GenJnlLine.FieldCaption("VAT Prod. Posting Group")),
                                true,
                                GenJnlLine,
                                GenJnlLine.FieldNo("Gen. Posting Type")));

                    CheckGenProdPostingGroupWhenAdjustForPmtDisc(GenJnlLine);

                    if (GenJnlLine."Gen. Posting Type" <> GenJnlLine."Gen. Posting Type"::" ") and
                       (GenJnlLine."VAT Posting" = GenJnlLine."VAT Posting"::"Automatic VAT Entry")
                    then begin
                        if GenJnlLine."VAT Amount" + GenJnlLine."VAT Base Amount" <> GenJnlLine.Amount then
                            Error(
                                ErrorInfo.Create(
                                    StrSubstNo(
                                        Text005, GenJnlLine.FieldCaption("VAT Amount"), GenJnlLine.FieldCaption("VAT Base Amount"),
                                        GenJnlLine.FieldCaption(Amount)),
                                    true,
                                    GenJnlLine,
                                    GenJnlLine.FieldNo("VAT Amount")));
                        if GenJnlLine."Currency Code" <> '' then
                            if GenJnlLine."VAT Amount (LCY)" + GenJnlLine."VAT Base Amount (LCY)" <> GenJnlLine."Amount (LCY)" then
                                Error(
                                    ErrorInfo.Create(
                                        StrSubstNo(
                                            Text005, GenJnlLine.FieldCaption("VAT Amount (LCY)"),
                                            GenJnlLine.FieldCaption("VAT Base Amount (LCY)"), GenJnlLine.FieldCaption("Amount (LCY)")),
                                        true,
                                        GenJnlLine,
                                        GenJnlLine.FieldNo("VAT Amount (LCY)")));
                    end;
                end;
            GenJnlLine."Account Type"::Customer, GenJnlLine."Account Type"::Vendor, GenJnlLine."Account Type"::Employee:
                begin
                    GenJnlLine.TestField("Gen. Posting Type", 0, ErrorInfo.Create());
                    GenJnlLine.TestField("Gen. Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Gen. Prod. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("VAT Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("VAT Prod. Posting Group", '', ErrorInfo.Create());

                    CheckAccountType(GenJnlLine);

                    CheckDocType(GenJnlLine);

                    if not GenJnlLine."System-Created Entry" and
                       (((GenJnlLine.Amount < 0) xor (GenJnlLine."Sales/Purch. (LCY)" < 0)) and (GenJnlLine.Amount <> 0) and (GenJnlLine."Sales/Purch. (LCY)" <> 0))
                    then
                        GenJnlLine.FieldError("Sales/Purch. (LCY)", ErrorInfo.Create(StrSubstNo(Text003, GenJnlLine.FieldCaption(Amount)), true));
                    CheckJobNoIsEmpty(GenJnlLine);

                    IsHandled := false;
                    OnCheckAccountNoOnBeforeCheckICPartner(GenJnlLine, IsHandled);
                    if not IsHandled then
                        CheckICPartner(GenJnlLine."Account Type", GenJnlLine."Account No.", GenJnlLine."Document Type", GenJnlLine);
                end;
            GenJnlLine."Account Type"::"Bank Account":
                begin
                    GenJnlLine.TestField("Gen. Posting Type", 0, ErrorInfo.Create());
                    GenJnlLine.TestField("Gen. Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Gen. Prod. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("VAT Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("VAT Prod. Posting Group", '', ErrorInfo.Create());
                    CheckJobNoIsEmpty(GenJnlLine);
                    if (GenJnlLine.Amount < 0) and (GenJnlLine."Bank Payment Type" = GenJnlLine."Bank Payment Type"::"Computer Check") then
                        GenJnlLine.TestField("Check Printed", true, ErrorInfo.Create());
                    CheckElectronicPaymentFields(GenJnlLine);
                end;
            GenJnlLine."Account Type"::"IC Partner":
                begin
                    ICPartner.Get(GenJnlLine."Account No.");
                    ICPartner.CheckICPartner();
                    if GenJnlLine."Journal Template Name" <> '' then begin
                        GenJournalTemplate.Get(GenJnlLine."Journal Template Name");
                        if GenJnlTemplate.Type <> GenJnlTemplate.Type::Intercompany then
                            GenJnlLine.FieldError("Account Type", ErrorInfo.Create());
                    end;
                end;
        end;

        OnAfterCheckAccountNo(GenJnlLine);
    end;

    local procedure CheckBalAccountNo(GenJnlLine: Record "Gen. Journal Line")
    var
        ICPartner: Record "IC Partner";
        CheckDone: Boolean;
    begin
        OnBeforeCheckBalAccountNo(GenJnlLine, CheckDone);
        if CheckDone then
            exit;

        case GenJnlLine."Bal. Account Type" of
            GenJnlLine."Bal. Account Type"::"G/L Account":
                begin
                    if ((GenJnlLine."Bal. Gen. Bus. Posting Group" <> '') or (GenJnlLine."Bal. Gen. Prod. Posting Group" <> '') or
                        (GenJnlLine."Bal. VAT Bus. Posting Group" <> '') or (GenJnlLine."Bal. VAT Prod. Posting Group" <> '')) and
                       not ApplicationAreaMgmt.IsSalesTaxEnabled()
                    then
                        GenJnlLine.TestField("Bal. Gen. Posting Type", ErrorInfo.Create());

                    CheckBalGenProdPostingGroupWhenAdjustForPmtDisc(GenJnlLine);

                    if (GenJnlLine."Bal. Gen. Posting Type" <> GenJnlLine."Bal. Gen. Posting Type"::" ") and
                       (GenJnlLine."VAT Posting" = GenJnlLine."VAT Posting"::"Automatic VAT Entry")
                    then begin
                        if GenJnlLine."Bal. VAT Amount" + GenJnlLine."Bal. VAT Base Amount" <> -GenJnlLine.Amount then
                            Error(
                                ErrorInfo.Create(
                                    StrSubstNo(
                                        Text006, GenJnlLine.FieldCaption("Bal. VAT Amount"), GenJnlLine.FieldCaption("Bal. VAT Base Amount"),
                                        GenJnlLine.FieldCaption(Amount)),
                                    true,
                                    GenJnlLine,
                                    GenJnlLine.FieldNo("Bal. VAT Amount")));
                        if GenJnlLine."Currency Code" <> '' then
                            if GenJnlLine."Bal. VAT Amount (LCY)" + GenJnlLine."Bal. VAT Base Amount (LCY)" <> -GenJnlLine."Amount (LCY)" then
                                Error(
                                    ErrorInfo.Create(
                                        StrSubstNo(
                                            Text006, GenJnlLine.FieldCaption("Bal. VAT Amount (LCY)"),
                                            GenJnlLine.FieldCaption("Bal. VAT Base Amount (LCY)"), GenJnlLine.FieldCaption("Amount (LCY)")),
                                                            true,
                                    GenJnlLine,
                                    GenJnlLine.FieldNo("Bal. VAT Amount (LCY)")));
                    end;
                end;
            GenJnlLine."Bal. Account Type"::Customer, GenJnlLine."Bal. Account Type"::Vendor, GenJnlLine."Bal. Account Type"::Employee:
                begin
                    GenJnlLine.TestField("Bal. Gen. Posting Type", 0, ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. Gen. Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. Gen. Prod. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. VAT Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. VAT Prod. Posting Group", '', ErrorInfo.Create());

                    CheckBalAccountType(GenJnlLine);

                    CheckBalDocType(GenJnlLine);

                    if ((GenJnlLine.Amount > 0) xor (GenJnlLine."Sales/Purch. (LCY)" < 0)) and (GenJnlLine.Amount <> 0) and (GenJnlLine."Sales/Purch. (LCY)" <> 0) then
                        GenJnlLine.FieldError("Sales/Purch. (LCY)", ErrorInfo.Create(StrSubstNo(Text009, GenJnlLine.FieldCaption(Amount)), true));
                    CheckJobNoIsEmpty(GenJnlLine);

                    CheckICPartner(GenJnlLine."Bal. Account Type", GenJnlLine."Bal. Account No.", GenJnlLine."Document Type", GenJnlLine);
                end;
            GenJnlLine."Bal. Account Type"::"Bank Account":
                begin
                    GenJnlLine.TestField("Bal. Gen. Posting Type", 0, ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. Gen. Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. Gen. Prod. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. VAT Bus. Posting Group", '', ErrorInfo.Create());
                    GenJnlLine.TestField("Bal. VAT Prod. Posting Group", '', ErrorInfo.Create());
                    if (GenJnlLine.Amount > 0) and (GenJnlLine."Bank Payment Type" = GenJnlLine."Bank Payment Type"::"Computer Check") then
                        GenJnlLine.TestField("Check Printed", true, ErrorInfo.Create());
                    CheckElectronicPaymentFields(GenJnlLine);
                end;
            GenJnlLine."Bal. Account Type"::"IC Partner":
                begin
                    ICPartner.Get(GenJnlLine."Bal. Account No.");
                    ICPartner.CheckICPartner();
                    if GenJnlTemplate.Type <> GenJnlTemplate.Type::Intercompany then
                        GenJnlLine.FieldError("Bal. Account Type", ErrorInfo.Create());
                end;
        end;

        OnAfterCheckBalAccountNo(GenJnlLine);
    end;

    local procedure CheckElectronicPaymentFields(GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckElectronicPaymentFields(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        if (GenJnlLine."Bank Payment Type" = GenJnlLine."Bank Payment Type"::"Electronic Payment") or
           (GenJnlLine."Bank Payment Type" = GenJnlLine."Bank Payment Type"::"Electronic Payment-IAT")
        then begin
            GenJnlLine.TestField("Exported to Payment File", true, ErrorInfo.Create());
            if CheckTransmitted(GenJnlLine) then
                GenJnlLine.TestField("Check Transmitted", true, ErrorInfo.Create());
        end;
    end;

    local procedure CheckTransmitted(GenJnlLine: Record "Gen. Journal Line"): Boolean
    var
        BankAccount: Record "Bank Account";
    begin
        if GenJnlLine."Account Type" = GenJnlLine."Account Type"::"Bank Account" then
            if BankAccount.Get(GenJnlLine."Account No.") then
                exit(BankAccount."Check Transmitted");
        if GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::"Bank Account" then
            if BankAccount.Get(GenJnlLine."Bal. Account No.") then
                exit(BankAccount."Check Transmitted");
        exit(false);
    end;

    local procedure CheckJobNoIsEmpty(GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckJobNoIsEmpty(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        GenJnlLine.TestField("Job No.", '', ErrorInfo.Create());
    end;

    /// <summary>
    /// Validates that a sales document number is not already used in existing customer ledger entries.
    /// Prevents duplicate sales document numbers to maintain document integrity and audit trail.
    /// </summary>
    /// <param name="GenJournalLine">General journal line containing sales document information to validate</param>
    procedure CheckSalesDocNoIsNotUsed(var GenJournalLine: Record "Gen. Journal Line")
    var
        OldCustLedgEntry: Record "Cust. Ledger Entry";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckSalesDocNoIsNotUsed(GenJournalLine."Document Type".AsInteger(), GenJournalLine."Document No.", IsHandled, GenJournalLine, OldCustLedgEntry);
        if IsHandled then
            exit;

        OldCustLedgEntry.SetRange("Document No.", GenJournalLine."Document No.");
        OldCustLedgEntry.SetRange("Document Type", GenJournalLine."Document Type");
        OnCheckSalesDocNoIsNotUsedOnAfterSetFilters(GenJournalLine, OldCustLedgEntry);
        if not OldCustLedgEntry.IsEmpty() then
            Error(
                ErrorInfo.Create(
                    StrSubstNo(SalesDocAlreadyExistsErr, GenJournalLine."Document Type", GenJournalLine."Document No."),
                    true,
                    GenJournalLine));
    end;

    /// <summary>
    /// Validates that a purchase document number is not already used in existing vendor ledger entries.
    /// Prevents duplicate purchase document numbers to maintain document integrity and audit trail.
    /// </summary>
    /// <param name="GenJournalLine">General journal line containing purchase document information to validate</param>
    procedure CheckPurchDocNoIsNotUsed(var GenJournalLine: Record "Gen. Journal Line")
    var
        OldVendLedgEntry: Record "Vendor Ledger Entry";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckPurchDocNoIsNotUsed(GenJournalLine."Document Type".AsInteger(), GenJournalLine."Document No.", IsHandled, GenJournalLine);
        if IsHandled then
            exit;

        OldVendLedgEntry.SetRange("Document No.", GenJournalLine."Document No.");
        OldVendLedgEntry.SetRange("Document Type", GenJournalLine."Document Type");
        if not OldVendLedgEntry.IsEmpty() then
            Error(
                ErrorInfo.Create(
                    StrSubstNo(PurchDocAlreadyExistsErr, GenJournalLine."Document Type", GenJournalLine."Document No."),
                    true,
                    GenJournalLine));
    end;

    /// <summary>
    /// Validates document type compatibility with account types and enforces amount sign requirements.
    /// Ensures document type and amount combinations comply with business logic for various account types.
    /// </summary>
    /// <param name="GenJnlLine">General journal line to validate for document type and amount consistency</param>
    procedure CheckDocType(GenJnlLine: Record "Gen. Journal Line")
    var
        IsPayment: Boolean;
        IsHandled: Boolean;
        IsFinChargeMemoNeg: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckDocType(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        if (GenJnlLine."Document Type" <> GenJnlLine."Document Type"::" ") and (not GenJnlLine."Financial Void") then begin
            if (GenJnlLine."Account Type" = GenJnlLine."Account Type"::Employee) and not
               (GenJnlLine."Document Type" in [GenJnlLine."Document Type"::Payment, GenJnlLine."Document Type"::" "])
            then
                GenJnlLine.FieldError("Document Type", ErrorInfo.Create(EmployeeAccountDocTypeErr, true));

            IsFinChargeMemoNeg := (GenJnlLine."Document Type" = GenJnlLine."Document Type"::"Finance Charge Memo") and (GenJnlLine."Account Type" = GenJnlLine."Account Type"::Customer) and (GenJnlLine.Amount < 0);
            IsPayment := GenJnlLine."Document Type" in [GenJnlLine."Document Type"::Payment, GenJnlLine."Document Type"::"Credit Memo"];
            if IsPayment xor ((GenJnlLine."Account Type" = GenJnlLine."Account Type"::Customer) xor IsVendorPaymentToCrMemo(GenJnlLine)) xor IsFinChargeMemoNeg then
                ErrorIfNegativeAmt(GenJnlLine)
            else
                ErrorIfPositiveAmt(GenJnlLine);
        end;
    end;

    local procedure CheckBalDocType(GenJnlLine: Record "Gen. Journal Line")
    var
        IsPayment: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckBalDocType(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        if GenJnlLine."Document Type" <> GenJnlLine."Document Type"::" " then begin
            if (GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::Employee) and not
               (GenJnlLine."Document Type" in [GenJnlLine."Document Type"::Payment, GenJnlLine."Document Type"::" "])
            then
                GenJnlLine.FieldError("Document Type", ErrorInfo.Create(EmployeeBalancingDocTypeErr, true));

            IsPayment := GenJnlLine."Document Type" in [GenJnlLine."Document Type"::Payment, GenJnlLine."Document Type"::"Credit Memo"];
            if IsPayment = (GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::Customer) then
                ErrorIfNegativeAmt(GenJnlLine)
            else
                ErrorIfPositiveAmt(GenJnlLine);
        end;
    end;

    local procedure CheckICPartner(AccountType: Enum "Gen. Journal Account Type"; AccountNo: Code[20]; DocumentType: Enum "Gen. Journal Document Type"; GenJnlLine: Record "Gen. Journal Line")
    var
        Customer: Record Customer;
        Vendor: Record Vendor;
        ICPartner: Record "IC Partner";
        Employee: Record Employee;
        CheckDone: Boolean;
    begin
        OnBeforeCheckICPartner(AccountType, AccountNo, DocumentType.AsInteger(), CheckDone, GenJnlLine);
        if CheckDone then
            exit;

        case AccountType of
            AccountType::Customer:
                if Customer.Get(AccountNo) then begin
                    Customer.CheckBlockedCustOnJnls(Customer, DocumentType, true);
                    if (Customer."IC Partner Code" <> '') and (GenJnlTemplate.Type = GenJnlTemplate.Type::Intercompany) and
                       ICPartner.Get(Customer."IC Partner Code")
                    then
                        ICPartner.CheckICPartnerIndirect(Format(AccountType), AccountNo);
                end;
            AccountType::Vendor:
                if Vendor.Get(AccountNo) then begin
                    Vendor.CheckBlockedVendOnJnls(Vendor, DocumentType, true);
                    if (Vendor."IC Partner Code" <> '') and (GenJnlTemplate.Type = GenJnlTemplate.Type::Intercompany) and
                       ICPartner.Get(Vendor."IC Partner Code")
                    then
                        ICPartner.CheckICPartnerIndirect(Format(AccountType), AccountNo);
                end;
            AccountType::Employee:
                if Employee.Get(AccountNo) then
                    Employee.CheckBlockedEmployeeOnJnls(true)
        end;
    end;

    local procedure CheckDimensions(GenJnlLine: Record "Gen. Journal Line")
    var
        TableID: array[10] of Integer;
        No: array[10] of Code[20];
        CheckDone: Boolean;
    begin
        OnBeforeCheckDimensions(GenJnlLine, CheckDone);
        if CheckDone then
            exit;

        if not DimMgt.CheckDimIDComb(GenJnlLine."Dimension Set ID") then
            ThrowGenJnlLineError(GenJnlLine, Text011, DimMgt.GetDimCombErr());

        TableID[1] := DimMgt.TypeToTableID1(GenJnlLine."Account Type".AsInteger());
        No[1] := GenJnlLine."Account No.";
        TableID[2] := DimMgt.TypeToTableID1(GenJnlLine."Bal. Account Type".AsInteger());
        No[2] := GenJnlLine."Bal. Account No.";
        TableID[3] := Database::Job;
        No[3] := GenJnlLine."Job No.";
        TableID[4] := Database::"Salesperson/Purchaser";
        No[4] := GenJnlLine."Salespers./Purch. Code";
        TableID[5] := Database::Campaign;
        No[5] := GenJnlLine."Campaign No.";

        CheckDone := false;
        OnCheckDimensionsOnAfterAssignDimTableIDs(GenJnlLine, TableID, No, CheckDone);

        if not CheckDone then
            if not DimMgt.CheckDimValuePosting(TableID, No, GenJnlLine."Dimension Set ID") then
                ThrowGenJnlLineError(GenJnlLine, Text012, DimMgt.GetDimValuePostingErr());
    end;

    local procedure CheckZeroAmount(var GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckZeroAmount(GenJnlLine, IsBatchMode, IsHandled);
        if IsHandled then
            exit;

        if GenJnlLine.NeedCheckZeroAmount() and not (GenJnlLine.IsRecurring() and IsBatchMode) then
            GenJnlLine.TestField(Amount, ErrorInfo.Create());
    end;

    /// <summary>
    /// Determines if a journal line represents a vendor payment being applied to a credit memo.
    /// Used for payment logic and document type validation in payment journals.
    /// </summary>
    /// <param name="GenJournalLine">General journal line to analyze for vendor payment to credit memo scenario</param>
    /// <returns>True if the line is a vendor payment applying to a credit memo, false otherwise</returns>
    procedure IsVendorPaymentToCrMemo(GenJournalLine: Record "Gen. Journal Line") Result: Boolean
    var
        GenJournalTemplate: Record "Gen. Journal Template";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeIsVendorPaymentToCrMemo(GenJournalLine, Result, IsHandled);
        if IsHandled then
            exit;

        if (GenJournalLine."Account Type" = GenJournalLine."Account Type"::Vendor) and
            (GenJournalLine."Document Type" = GenJournalLine."Document Type"::Payment) and
            (GenJournalLine."Applies-to Doc. Type" = GenJournalLine."Applies-to Doc. Type"::"Credit Memo") and
            (GenJournalLine."Applies-to Doc. No." <> '')
        then begin
            GenJournalTemplate.Get(GenJournalLine."Journal Template Name");
            exit(GenJournalTemplate.Type = GenJournalTemplate.Type::Payments);
        end;
        exit(false);
    end;

    /// <summary>
    /// Throws a formatted error for journal line validation failures with contextual information.
    /// Provides structured error handling with journal line context and detailed error messages.
    /// </summary>
    /// <param name="GenJournalLine">General journal line that caused the validation error</param>
    /// <param name="ErrorTemplate">Template string for formatting the error message with placeholders</param>
    /// <param name="ErrorText">Specific error text describing the validation failure</param>
    procedure ThrowGenJnlLineError(GenJournalLine: Record "Gen. Journal Line"; ErrorTemplate: Text; ErrorText: Text)
    begin
        if LogErrorMode then
            exit;

        if GenJournalLine."Line No." <> 0 then
            Error(
                ErrorInfo.Create(
                    StrSubstNo(
                        ErrorTemplate,
                        GenJournalLine.TableCaption, GenJournalLine."Journal Template Name", GenJournalLine."Journal Batch Name", GenJournalLine."Line No.",
                        ErrorText),
                    true,
                    GenJournalLine));

        Error(
            ErrorInfo.Create(ErrorText, true, GenJournalLine));
    end;

    /// <summary>
    /// Sets batch processing mode to control validation behavior during bulk operations.
    /// Modifies validation logic to accommodate batch processing requirements.
    /// </summary>
    /// <param name="NewBatchMode">True to enable batch mode processing, false for single line processing</param>
    procedure SetBatchMode(NewBatchMode: Boolean)
    begin
        IsBatchMode := NewBatchMode;
    end;

    /// <summary>
    /// Validates general product posting group is specified when VAT setup requires payment discount adjustment.
    /// Ensures proper posting group configuration for payment discount VAT calculations.
    /// </summary>
    /// <param name="GenJnlLine">General journal line to validate for payment discount VAT setup requirements</param>
    procedure CheckGenProdPostingGroupWhenAdjustForPmtDisc(GenJnlLine: Record "Gen. Journal Line")
    var
        VATPostingSetup: Record "VAT Posting Setup";
    begin
        if GenJnlLine."System-Created Entry" or
            not (GenJnlLine."Gen. Posting Type" in [GenJnlLine."Gen. Posting Type"::Purchase, GenJnlLine."Gen. Posting Type"::Sale]) or
            not (GenJnlLine."Document Type" in [GenJnlLine."Document Type"::Invoice, GenJnlLine."Document Type"::"Credit Memo"])
        then
            exit;

        if VATPostingSetup.Get(GenJnlLine."VAT Bus. Posting Group", GenJnlLine."VAT Prod. Posting Group") and
           VATPostingSetup."Adjust for Payment Discount"
        then
            GenJnlLine.TestField("Gen. Prod. Posting Group", ErrorInfo.Create());
    end;

    /// <summary>
    /// Validates balancing general product posting group is specified when VAT setup requires payment discount adjustment.
    /// Ensures proper balancing posting group configuration for payment discount VAT calculations.
    /// </summary>
    /// <param name="GenJnlLine">General journal line to validate for balancing payment discount VAT setup requirements</param>
    procedure CheckBalGenProdPostingGroupWhenAdjustForPmtDisc(GenJnlLine: Record "Gen. Journal Line")
    var
        VATPostingSetup: Record "VAT Posting Setup";
    begin
        if GenJnlLine."System-Created Entry" or
            not (GenJnlLine."Bal. Gen. Posting Type" in [GenJnlLine."Bal. Gen. Posting Type"::Purchase, GenJnlLine."Bal. Gen. Posting Type"::Sale]) or
            not (GenJnlLine."Document Type" in [GenJnlLine."Document Type"::Invoice, GenJnlLine."Document Type"::"Credit Memo"])
        then
            exit;

        if VATPostingSetup.Get(GenJnlLine."Bal. VAT Bus. Posting Group", GenJnlLine."Bal. VAT Prod. Posting Group") and
           VATPostingSetup."Adjust for Payment Discount"
        then
            GenJnlLine.TestField("Bal. Gen. Prod. Posting Group", ErrorInfo.Create());
    end;

    local procedure CheckAccountType(GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckAccountType(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        if ((GenJnlLine."Account Type" = GenJnlLine."Account Type"::Customer) and
            (GenJnlLine."Bal. Gen. Posting Type" = GenJnlLine."Bal. Gen. Posting Type"::Purchase)) or
           ((GenJnlLine."Account Type" = GenJnlLine."Account Type"::Vendor) and
            (GenJnlLine."Bal. Gen. Posting Type" = GenJnlLine."Bal. Gen. Posting Type"::Sale))
        then
            Error(
                ErrorInfo.Create(
                    StrSubstNo(
                        Text010,
                        GenJnlLine.FieldCaption("Account Type"), GenJnlLine."Account Type",
                        GenJnlLine.FieldCaption("Bal. Gen. Posting Type"), GenJnlLine."Bal. Gen. Posting Type"),
                    true,
                    GenJnlLine));
    end;

    local procedure CheckBalAccountType(GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckBalAccountType(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        if ((GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::Customer) and
            (GenJnlLine."Gen. Posting Type" = GenJnlLine."Gen. Posting Type"::Purchase)) or
           ((GenJnlLine."Bal. Account Type" = GenJnlLine."Bal. Account Type"::Vendor) and
            (GenJnlLine."Gen. Posting Type" = GenJnlLine."Gen. Posting Type"::Sale))
        then
            Error(
                ErrorInfo.Create(
                    StrSubstNo(
                        Text010,
                        GenJnlLine.FieldCaption("Bal. Account Type"), GenJnlLine."Bal. Account Type",
                        GenJnlLine.FieldCaption("Gen. Posting Type"), GenJnlLine."Gen. Posting Type"),
                    true,
                    GenJnlLine));
    end;

    /// <summary>
    /// Sets error logging mode to control error handling behavior during validation.
    /// Enables or disables error collection mode for dimension management and validation processing.
    /// </summary>
    /// <param name="NewLogErrorMode">True to enable error logging mode, false to throw errors immediately</param>
    procedure SetLogErrorMode(NewLogErrorMode: Boolean)
    begin
        LogErrorMode := NewLogErrorMode;
        if LogErrorMode then
            DimMgt.SetCollectErrorsMode();
    end;

    local procedure CheckAppliesToDocNo(GenJnlLine: Record "Gen. Journal Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckAppliesToDocNo(GenJnlLine, IsHandled);
        if IsHandled then
            exit;

        GenJnlLine.TestField("Applies-to Doc. No.", '', ErrorInfo.Create());
        GenJnlLine.TestField("Applies-to ID", '', ErrorInfo.Create());
    end;

    local procedure CheckVATDate(var GenJournalLine: Record "Gen. Journal Line")
    var
        VATReportingDateMgt: Codeunit "VAT Reporting Date Mgt";
        IsHandled: Boolean;
        ThrowError: Boolean;
    begin
        IsHandled := false;
        // Posting of some document types do not catch errors with ErrorMessageMgt.
        // For these it is needed that we throw error with message directly to display to user
        ThrowError := GenJournalLine."Document Type" in [Enum::"Gen. Journal Document Type"::" ", Enum::"Gen. Journal Document Type"::"Finance Charge Memo", Enum::"Gen. Journal Document Type"::Reminder, Enum::"Gen. Journal Document Type"::"Credit Memo"];
        OnBeforeCheckVATDate(GenJournalLine, IsHandled);
        if not IsHandled then
            if not VATReportingDateMgt.IsValidDate(GenJournalLine, GenJournalLine.FieldNo("VAT Reporting Date"), ThrowError) then
                Error('');
    end;

    local procedure CheckCurrencyCode(GenJnlLine: Record "Gen. Journal Line")
    var
        ACYOnlyPosting: Boolean;
    begin
        ACYOnlyPosting :=
          (GLSetup."Additional Reporting Currency" <> '') and
          (GenJnlLine."Additional-Currency Posting" = GenJnlLine."Additional-Currency Posting"::"Additional-Currency Amount Only") and
          (GenJnlLine."Currency Code" = GLSetup."Additional Reporting Currency");

        if (GenJnlLine."Currency Code" <> '') and (GenJnlLine."Currency Code" <> GLSetup."LCY Code") then begin
            CheckAccountCurrencyCode(
                GenJnlLine, GenJnlLine."Account No.", GenJnlLine."Account Type", GenJnlLine."Currency Code", ACYOnlyPosting);
            CheckAccountCurrencyCode(
                GenJnlLine, GenJnlLine."Bal. Account No.", GenJnlLine."Bal. Account Type", GenJnlLine."Currency Code", ACYOnlyPosting);
        end;
    end;

    local procedure CheckAccountCurrencyCode(GenJnlLine: Record "Gen. Journal Line"; AccountNo: Code[20]; AccountType: Enum "Gen. Journal Account Type"; CurrencyCode: Code[10]; ACYOnly: Boolean)
    var
        GLAccount: Record "G/L Account";
        BankAccount: Record "Bank Account";
        BankAccountPostingGroup: Record "Bank Account Posting Group";
        Customer: Record Customer;
        CustomerPostingGroup: Record "Customer Posting Group";
        Vendor: Record Vendor;
        VendorPostingGroup: Record "Vendor Posting Group";
    begin
        if (AccountNo = '') or ACYOnly then
            exit;

        case AccountType of
            AccountType::"G/L Account":
                begin
                    GLAccount.Get(AccountNo);
                    CheckGLAccountSourceCurrency(GLAccount, CurrencyCode);
                end;
            AccountType::Customer:
                begin
                    Customer.Get(AccountNo);
                    CustomerPostingGroup.Get(Customer."Customer Posting Group");
                    if CustomerPostingGroup."Receivables Account" = '' then
                        CustomerPostingGroup.Get(GenJnlLine."Posting Group");
                    GLAccount.Get(CustomerPostingGroup."Receivables Account");
                    CheckGLAccountSourceCurrency(GLAccount, CurrencyCode);
                end;
            AccountType::Vendor:
                begin
                    Vendor.Get(AccountNo);
                    VendorPostingGroup.Get(Vendor."Vendor Posting Group");
                    if VendorPostingGroup."Payables Account" = '' then
                        VendorPostingGroup.Get(GenJnlLine."Posting Group");
                    GLAccount.Get(VendorPostingGroup."Payables Account");
                    CheckGLAccountSourceCurrency(GLAccount, CurrencyCode);
                end;
            AccountType::"Bank Account":
                begin
                    BankAccount.Get(AccountNo);
                    BankAccountPostingGroup.Get(BankAccount."Bank Acc. Posting Group");
                    if BankAccountPostingGroup."G/L Account No." = '' then
                        BankAccountPostingGroup.Get(GenJnlLine."Posting Group");
                    GLAccount.Get(BankAccountPostingGroup."G/L Account No.");
                    CheckGLAccountSourceCurrency(GLAccount, CurrencyCode);
                end;
        end;
    end;

    internal procedure CheckGLAccountSourceCurrency(var GLAccount: Record "G/L Account"; CurrencyCode: Code[10])
    var
        GLAccountSourceCurrency: Record "G/L Account Source Currency";
    begin
        GLSetup.Get();
        case GLAccount."Source Currency Posting" of
            GLAccount."Source Currency Posting"::"Same Currency":
                if (CurrencyCode <> GLAccount."Source Currency Code") and
                    (GLAccount."Source Currency Code" <> '') and (GLAccount."Source Currency Code" <> GLSetup."LCY Code")
                then
                    Error(GLAccCurrencyDoesNotMatchErr, CurrencyCode, GLAccount."Source Currency Code", GLAccount."No.");
            GLAccount."Source Currency Posting"::"Multiple Currencies":
                if CurrencyCode <> '' then begin
                    GLAccountSourceCurrency.SetRange("G/L Account No.", GLAccount."No.");
                    GLAccountSourceCurrency.SetRange("Currency Code", CurrencyCode);
                    if GLAccountSourceCurrency.IsEmpty() then
                        Error(GLAccSourceCurrencyDoesNotMatchErr, CurrencyCode, GLAccount."No.");
                end;
            GLAccount."Source Currency Posting"::"LCY Only":
                if CurrencyCode <> '' then
                    Error(GLAccSourceCurrencyDoesNotAllowedErr, CurrencyCode, GLAccount."No.");
        end;
    end;

    local procedure CheckPostedDeferralHeaderExist(GenJnlLine: Record "Gen. Journal Line")
    var
        DeferralHeader: Record "Deferral Header";
        PostedDeferralHeader: Record "Posted Deferral Header";
        AccountNo: Code[20];
        ErrorTxt: Text;
    begin
        if not CheckDeferralHeaderExist(GenJnlLine) then
            exit;

        AccountNo := GetDeferralAccountNo(GenJnlLine);

        if PostedDeferralHeader.Get(
            DeferralHeader."Deferral Doc. Type"::"G/L",
            GenJnlLine."Document No.",
            AccountNo,
            0,
            '',
            GenJnlLine."Line No.")
        then begin
            ErrorTxt := StrSubstNo(DuplicateRecordErr, GenJnlLine."Document No.");
            Error(ErrorInfo.Create(ErrorTxt, true, GenJnlLine, GenJnlLine.FieldNo("Deferral Code")));
        end;
    end;

    local procedure CheckDeferralHeaderExist(GenJnlLine: Record "Gen. Journal Line"): Boolean
    var
        DeferralHeader: Record "Deferral Header";
    begin
        if DeferralHeader.Get(
            DeferralHeader."Deferral Doc. Type"::"G/L",
            GenJnlLine."Journal Template Name",
            GenJnlLine."Journal Batch Name", 0, '',
            GenJnlLine."Line No.")
        then
            exit(true);
    end;

    local procedure GetDeferralAccountNo(GenJournalLine: Record "Gen. Journal Line"): Code[20]
    var
        CustPostingGr: Record "Customer Posting Group";
        VendPostingGr: Record "Vendor Posting Group";
        BankAcc: Record "Bank Account";
        BankAccPostingGr: Record "Bank Account Posting Group";
        GLAccountType: Enum "Gen. Journal Account Type";
        Account: Code[20];
        GLAccount: Code[20];
    begin
        if (GenJournalLine."Account No." = '') and (GenJournalLine."Bal. Account No." <> '') then begin
            GLAccount := GenJournalLine."Bal. Account No.";
            GLAccountType := GenJournalLine."Bal. Account Type";
        end else begin
            GLAccount := GenJournalLine."Account No.";
            GLAccountType := GenJournalLine."Account Type";
        end;

        case GLAccountType of
            GenJournalLine."Account Type"::Customer:
                begin
                    CustPostingGr.Get(GenJournalLine."Posting Group");
                    Account := CustPostingGr.GetReceivablesAccount();
                end;
            GenJournalLine."Account Type"::Vendor:
                begin
                    VendPostingGr.Get(GenJournalLine."Posting Group");
                    Account := VendPostingGr.GetPayablesAccount();
                end;
            GenJournalLine."Account Type"::"Bank Account":
                begin
                    BankAcc.Get(GLAccount);
                    BankAccPostingGr.Get(BankAcc."Bank Acc. Posting Group");
                    Account := BankAccPostingGr."G/L Account No.";
                end;
            else
                Account := GLAccount;
        end;

        exit(Account);
    end;

    internal procedure HasVAT(var GenJnlLine: Record "Gen. Journal Line"): Boolean
    begin
        exit((GenJnlLine."Gen. Posting Type" <> GenJnlLine."Gen. Posting Type"::" ") or
            (GenJnlLine."Bal. Gen. Posting Type" <> GenJnlLine."Bal. Gen. Posting Type"::" "));
    end;

    procedure CheckDeferralPostingAllowed(DeferralPostingAllowed: Boolean)
    begin
        IsDeferralPostingAllowed := DeferralPostingAllowed;
    end;

    /// <summary>
    /// Integration event raised after completing account number validation for journal lines.
    /// Enables custom validation logic and processing after standard account number checks are completed.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record that was validated.</param>
    [IntegrationEvent(true, false)]
    local procedure OnAfterCheckAccountNo(var GenJournalLine: Record "Gen. Journal Line")
    begin
    end;

    /// <summary>
    /// Integration event raised after completing balancing account number validation for journal lines.
    /// Enables custom validation logic and processing after standard balancing account number checks are completed.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record that was validated.</param>
    [IntegrationEvent(true, false)]
    local procedure OnAfterCheckBalAccountNo(var GenJournalLine: Record "Gen. Journal Line")
    begin
    end;

    /// <summary>
    /// Integration event raised after completing all journal line validation checks.
    /// Enables custom validation logic and error handling after standard journal line validation is completed.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record that was validated.</param>
    /// <param name="ErrorMessageMgt">Error message management codeunit for handling validation errors.</param>
    [IntegrationEvent(true, false)]
    local procedure OnAfterCheckGenJnlLine(var GenJournalLine: Record "Gen. Journal Line"; var ErrorMessageMgt: Codeunit "Error Message Management")
    begin
    end;

    /// <summary>
    /// Integration event raised after checking if a posting date is not allowed based on user permissions and setup.
    /// Enables custom date validation logic and override of standard date restrictions.
    /// </summary>
    /// <param name="PostingDate">Posting date being validated.</param>
    /// <param name="DateIsNotAllowed">Boolean indicating if the date should be considered invalid (can be modified).</param>
    [IntegrationEvent(true, false)]
    local procedure OnAfterDateNoAllowed(PostingDate: Date; var DateIsNotAllowed: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised after checking if a deferral posting date is not allowed based on deferral setup and permissions.
    /// Enables custom deferral date validation logic and override of standard deferral date restrictions.
    /// </summary>
    /// <param name="PostingDate">Deferral posting date being validated.</param>
    /// <param name="DateIsNotAllowed">Boolean indicating if the deferral date should be considered invalid (can be modified).</param>
    [IntegrationEvent(true, false)]
    local procedure OnAfterDeferralPostingDateNoAllowed(PostingDate: Date; var DateIsNotAllowed: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before performing date validation checks for journal lines.
    /// Enables custom logic to completely override standard date validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated.</param>
    /// <param name="DateCheckDone">Set to true to skip standard date validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeDateNotAllowed(GenJnlLine: Record "Gen. Journal Line"; var DateCheckDone: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before determining if a posting date is not allowed.
    /// Enables custom date validation logic to override standard date restriction checks.
    /// </summary>
    /// <param name="PostingDate">Posting date being validated.</param>
    /// <param name="SetupRecordID">Record ID of the setup record used for validation context.</param>
    /// <param name="GenJnlBatch">Journal batch record providing context for date validation.</param>
    /// <param name="DateIsNotAllowed">Boolean indicating if date should be considered invalid (can be modified).</param>
    /// <param name="IsHandled">Set to true to skip standard date validation logic.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeIsDateNotAllowed(PostingDate: Date; SetupRecordID: RecordId; GenJnlBatch: Record "Gen. Journal Batch"; var DateIsNotAllowed: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before determining if a deferral posting date is not allowed.
    /// Enables custom deferral date validation logic to override standard deferral date restriction checks.
    /// </summary>
    /// <param name="PostingDate">Deferral posting date being validated.</param>
    /// <param name="SetupRecordID">Record ID of the setup record used for validation context.</param>
    /// <param name="GenJnlBatch">Journal batch record providing context for deferral date validation.</param>
    /// <param name="DateIsNotAllowed">Boolean indicating if deferral date should be considered invalid (can be modified).</param>
    /// <param name="IsHandled">Set to true to skip standard deferral date validation logic.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeIsDeferralPostingDateNotAllowed(PostingDate: Date; SetupRecordID: RecordId; GenJnlBatch: Record "Gen. Journal Batch"; var DateIsNotAllowed: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before performing account number validation for journal lines.
    /// Enables custom logic to completely override standard account number validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated.</param>
    /// <param name="CheckDone">Set to true to skip standard account number validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckAccountNo(var GenJnlLine: Record "Gen. Journal Line"; var CheckDone: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before performing balancing account number validation for journal lines.
    /// Enables custom logic to completely override standard balancing account number validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated.</param>
    /// <param name="CheckDone">Set to true to skip standard balancing account number validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckBalAccountNo(var GenJnlLine: Record "Gen. Journal Line"; var CheckDone: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before performing dimension validation for journal lines.
    /// Enables custom logic to completely override standard dimension validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for dimensions.</param>
    /// <param name="CheckDone">Set to true to skip standard dimension validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckDimensions(var GenJnlLine: Record "Gen. Journal Line"; var CheckDone: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating document type and amount combinations for journal lines.
    /// Enables custom logic to completely override standard document type validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for document type compatibility.</param>
    /// <param name="IsHandled">Set to true to skip standard document type validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckDocType(GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating balancing document type and amount combinations for journal lines.
    /// Enables custom logic to completely override standard balancing document type validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for balancing document type compatibility.</param>
    /// <param name="IsHandled">Set to true to skip standard balancing document type validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckBalDocType(GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before performing intercompany partner validation for journal lines.
    /// Enables custom logic to completely override standard IC partner validation processing.
    /// </summary>
    /// <param name="AccountType">Type of account being validated for IC partner relationships.</param>
    /// <param name="AccountNo">Account number being validated for IC partner setup.</param>
    /// <param name="DocumentType">Document type being processed for IC validation.</param>
    /// <param name="CheckDone">Set to true to skip standard IC partner validation logic.</param>
    /// <param name="GenJnlLine">Journal line record providing context for IC partner validation.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckICPartner(AccountType: Enum "Gen. Journal Account Type"; AccountNo: Code[20]; DocumentType: Option; var CheckDone: Boolean; GenJnlLine: Record "Gen. Journal Line")
    begin
    end;

    /// <summary>
    /// Integration event raised before validating sales document number uniqueness in customer ledger entries.
    /// Enables custom logic to completely override standard sales document number duplication checking.
    /// </summary>
    /// <param name="DocType">Document type being validated for uniqueness.</param>
    /// <param name="DocNo">Document number being validated for uniqueness in customer ledger.</param>
    /// <param name="IsHandled">Set to true to skip standard sales document number validation logic.</param>
    /// <param name="GenJournalLine">Journal line record providing context for sales document validation.</param>
    /// <param name="OldCustLedgerEntry">Customer ledger entry record used for duplication checking.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckSalesDocNoIsNotUsed(DocType: Option; DocNo: Code[20]; var IsHandled: Boolean; GenJournalLine: Record "Gen. Journal Line"; var OldCustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Integration event raised before validating purchase document number uniqueness in vendor ledger entries.
    /// Enables custom logic to completely override standard purchase document number duplication checking.
    /// </summary>
    /// <param name="DocType">Document type being validated for uniqueness.</param>
    /// <param name="DocNo">Document number being validated for uniqueness in vendor ledger.</param>
    /// <param name="IsHandled">Set to true to skip standard purchase document number validation logic.</param>
    /// <param name="GenJournalLine">Journal line record providing context for purchase document validation.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckPurchDocNoIsNotUsed(DocType: Option; DocNo: Code[20]; var IsHandled: Boolean; GenJournalLine: Record "Gen. Journal Line")
    begin
    end;

    /// <summary>
    /// Integration event raised before validating that journal line amount is not negative.
    /// Enables custom logic to override negative amount error handling for specific scenarios.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for negative amount restrictions.</param>
    /// <param name="RaiseError">Set to true to trigger negative amount error, false to suppress error.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeErrorIfNegativeAmt(GenJnlLine: Record "Gen. Journal Line"; var RaiseError: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating that journal line amount is not positive.
    /// Enables custom logic to override positive amount error handling for specific scenarios.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for positive amount restrictions.</param>
    /// <param name="RaiseError">Set to true to trigger positive amount error, false to suppress error.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeErrorIfPositiveAmt(GenJnlLine: Record "Gen. Journal Line"; var RaiseError: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before performing the main journal line validation check.
    /// Enables custom logic to completely override standard journal line validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated.</param>
    /// <param name="OverrideDimErr">Boolean indicating if dimension errors should be overridden.</param>
    /// <param name="IsHandled">Set to true to skip standard journal line validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeRunCheck(var GenJournalLine: Record "Gen. Journal Line"; OverrideDimErr: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating document number field requirements for journal lines.
    /// Enables custom logic to completely override standard document number validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for document number requirements.</param>
    /// <param name="IsHandled">Set to true to skip standard document number field validation logic.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeTestDocumentNo(var GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating account and balancing account type compatibility for journal lines.
    /// Enables custom logic to completely override standard account type compatibility validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for account type compatibility.</param>
    /// <param name="IsHandled">Set to true to skip standard account type validation logic.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeTestAccountAndBalAccountType(var GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating applies-to ID field requirements for journal lines.
    /// Enables custom logic to completely override standard applies-to ID validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for applies-to ID requirements.</param>
    /// <param name="IsHandled">Set to true to skip standard applies-to ID validation logic.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeTestAppliesToID(var GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating posting date against fiscal year boundaries for journal lines.
    /// Enables custom logic to completely override standard fiscal year date validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for fiscal year posting date compliance.</param>
    /// <param name="IsHandled">Set to true to skip standard fiscal year validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckPostingDateInFiscalYear(GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised at the beginning of the CheckDates procedure before any date validation is performed.
    /// Enables custom validation logic on journal line fields as part of the initial validation phase.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckDates(var GenJnlLine: Record "Gen. Journal Line")
    begin
    end;

    /// <summary>
    /// Integration event raised after assigning dimension table IDs during dimension validation processing.
    /// Enables custom dimension table assignments and modification of dimension validation scope.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for dimension requirements.</param>
    /// <param name="TableID">Array of table IDs used for dimension validation context.</param>
    /// <param name="No">Array of record numbers corresponding to table IDs for dimension validation.</param>
    /// <param name="CheckDone">Set to true to skip remaining dimension validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnCheckDimensionsOnAfterAssignDimTableIDs(var GenJournalLine: Record "Gen. Journal Line"; var TableID: array[10] of Integer; var No: array[10] of Code[20]; var CheckDone: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating account type and posting type compatibility for journal lines.
    /// Enables custom logic to completely override standard account type validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for account type compatibility.</param>
    /// <param name="IsHandled">Set to true to skip standard account type validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckAccountType(GenJnlLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating balancing account type and posting type compatibility for journal lines.
    /// Enables custom logic to completely override standard balancing account type validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for balancing account type compatibility.</param>
    /// <param name="IsHandled">Set to true to skip standard balancing account type validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckBalAccountType(GenJnlLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating applies-to document number requirements for journal lines.
    /// Enables custom logic to completely override standard applies-to document validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for applies-to document requirements.</param>
    /// <param name="IsHandled">Set to true to skip standard applies-to document validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckAppliesToDocNo(GenJnlLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating that job number field is empty for journal lines.
    /// Enables custom logic to completely override standard job number validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for job number field requirements.</param>
    /// <param name="IsHandled">Set to true to skip standard job number validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckJobNoIsEmpty(GenJnlLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating that journal line amount is not zero.
    /// Enables custom logic to completely override standard zero amount validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for zero amount restrictions.</param>
    /// <param name="IsBatchMode">Boolean indicating if validation is running in batch mode.</param>
    /// <param name="IsHandled">Set to true to skip standard zero amount validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckZeroAmount(GenJnlLine: Record "Gen. Journal Line"; IsBatchMode: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating electronic payment fields for journal lines.
    /// Enables custom logic to completely override standard electronic payment validation processing.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being validated for electronic payment requirements.</param>
    /// <param name="IsHandled">Set to true to skip standard electronic payment validation logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeCheckElectronicPaymentFields(var GenJnlLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before determining if a vendor payment applies to a credit memo.
    /// Enables custom logic to completely override standard vendor payment to credit memo logic.
    /// </summary>
    /// <param name="GenJnlLine">Journal line record being analyzed for vendor payment to credit memo scenario.</param>
    /// <param name="Result">Boolean result indicating if line is vendor payment to credit memo (can be modified).</param>
    /// <param name="IsHandled">Set to true to skip standard vendor payment to credit memo logic.</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeIsVendorPaymentToCrMemo(GenJnlLine: Record "Gen. Journal Line"; var Result: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating VAT reporting date for journal lines.
    /// Enables custom logic to completely override standard VAT date validation processing.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for VAT date requirements.</param>
    /// <param name="IsHandled">Set to true to skip standard VAT date validation logic.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckVATDate(var GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised after setting filters for sales document number duplication validation.
    /// Enables custom filter modifications during sales document number uniqueness checking.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record providing context for sales document validation.</param>
    /// <param name="OldCustLedgerEntry">Customer ledger entry record being filtered for duplication checking.</param>
    [IntegrationEvent(false, false)]
    local procedure OnCheckSalesDocNoIsNotUsedOnAfterSetFilters(GenJournalLine: Record "Gen. Journal Line"; var OldCustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Integration event raised before checking intercompany partner setup during account number validation.
    /// Enables custom logic to override IC partner checking for specific account scenarios.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record being validated for intercompany partner requirements.</param>
    /// <param name="IsHandled">Set to true to skip standard IC partner checking logic.</param>
    [IntegrationEvent(false, false)]
    local procedure OnCheckAccountNoOnBeforeCheckICPartner(var GenJournalLine: Record "Gen. Journal Line"; var IsHandled: Boolean);
    begin
    end;
}