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;
}