Codeunit 370 Bank Acc. Reconciliation Post, source in 29
Source29
src/Layers/W1/BaseApp/Bank/Reconciliation/BankAccReconciliationPost.Codeunit.al1276 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.Bank.Reconciliation;
using Microsoft.Bank.BankAccount;
using Microsoft.Bank.Check;
using Microsoft.Bank.Ledger;
using Microsoft.Bank.Statement;
using Microsoft.Finance.Analysis;
using Microsoft.Finance.Currency;
using Microsoft.Finance.Dimension;
using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Finance.GeneralLedger.Posting;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Foundation.AuditCodes;
using Microsoft.HumanResources.Payables;
using Microsoft.Purchases.Payables;
using Microsoft.Sales.Receivables;
using System.Reflection;
using System.Telemetry;
using System.Utilities;
/// <summary>
/// Handles the posting process for bank account reconciliation and payment application documents.
/// This codeunit processes completed reconciliation statements, creating permanent ledger entries,
/// applying payments to customer and vendor accounts, and finalizing bank reconciliation transactions.
/// Supports both bank reconciliation and payment application workflows with comprehensive validation,
/// posting preview, and audit trail generation.
/// </summary>
/// <remarks>
/// Key features include posting validation, applied payment entries creation, bank ledger entry updates,
/// check ledger entry processing, dimension posting, and integration with general journal posting engine.
/// Provides posting preview capability for validation before final commitment and comprehensive error handling
/// for application discrepancies, tolerance violations, and incomplete reconciliations.
/// Integration events enable customization of posting logic, validation rules, and posting procedures.
/// </remarks>
codeunit 370 "Bank Acc. Reconciliation Post"
{
Permissions = TableData "Bank Account Ledger Entry" = rm,
TableData "Check Ledger Entry" = rm,
TableData "Bank Account" = rm,
TableData "Bank Account Statement" = ri,
TableData "Bank Account Statement Line" = ri,
TableData "Posted Payment Recon. Hdr" = ri;
TableNo = "Bank Acc. Reconciliation";
trigger OnRun()
begin
if GuiAllowed and not PreviewMode then begin
Window.Open('#1#################################\\' + PostingLinesTxt);
Window.Update(1, StrSubstNo('%1 %2', Rec."Bank Account No.", Rec."Statement No."));
end;
InitPost(Rec);
Post(Rec);
FinalizePost(Rec);
if PreviewMode then
exit;
if GuiAllowed then
Window.Close();
Commit();
end;
var
BankAcc: Record "Bank Account";
BankAccLedgEntry: Record "Bank Account Ledger Entry";
CheckLedgEntry: Record "Check Ledger Entry";
GenJnlLine: Record "Gen. Journal Line";
GLSetup: Record "General Ledger Setup";
SourceCodeSetup: Record "Source Code Setup";
GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line";
Window: Dialog;
SourceCode: Code[10];
PostedStamentNo: Code[20];
TotalAmount: Decimal;
TotalAppliedAmount: Decimal;
Lines: Integer;
Difference: Decimal;
PreviewMode: Boolean;
PostPaymentsOnly: Boolean;
#pragma warning disable AA0470
PostingLinesTxt: Label 'Posting lines #2######';
StatementEndingBalanceErr: Label '%1 must be equal to Total Balance.';
#pragma warning restore AA0470
#pragma warning disable AA0074
#pragma warning disable AA0470
Text003: Label 'The application is not correct. The total amount applied is %1; it should be %2.';
Text004: Label 'The total difference is %1. It must be %2.';
#pragma warning restore AA0470
#pragma warning restore AA0074
ExcessiveAmtErr: Label 'You must apply the excessive amount of %1 %2 manually.', Comment = '%1 a decimal number, %2 currency code';
NotFullyAppliedErr: Label 'One or more payments are not fully applied.\\The sum of applied amounts is %1. It must be %2.', Comment = '%1 - total applied amount, %2 - total transaction amount';
LineNoTAppliedErr: Label 'The line with transaction date %1 and transaction text ''%2'' is not applied. You must apply all lines.', Comment = '%1 - transaction date, %2 - arbitrary text';
TransactionAlreadyReconciledErr: Label 'The line with transaction date %1 and transaction text ''%2'' is already reconciled.\\You must remove it from the payment reconciliation journal before posting.', Comment = '%1 - transaction date, %2 - arbitrary text';
EventNameTelemetryTxt: Label 'Post bank reconciliation', Locked = true;
EventNameTelemetryPmtTxt: Label 'Post payment application', Locked = true;
PaymentRecCategoryLbl: Label 'AL Payment Reconciliation', Locked = true;
BankAccountRecCategoryLbl: Label 'AL Bank Account Rec', Locked = true;
MissingBankAccReconLineQst: Label 'There''s nothing to post because the reconciliation doesn''t contain any entries.\\Do you want to continue?';
EmptyDescriptionTxt: Label 'Autogenerated blank entry';
PartialApplicationBankLedgerEntryQst: Label 'Line %1 relates to a bank ledger entry that isn''t fully reconciled. Carefully review the entry before proceeding. Partial applications can make your financial records inaccurate, so they aren''t recommended.\\Do you want to continue?', Comment = '%1 - Statement Line No.';
/// <summary>
/// Runs the posting process in preview mode to validate transactions before final posting.
/// Enables users to review the impact of posting without committing changes to the database,
/// providing validation of applied amounts, account assignments, and posting calculations.
/// </summary>
/// <param name="BankAccReconciliation">Bank reconciliation document to preview for posting.</param>
/// <returns>True if preview completed successfully; false if validation errors occurred.</returns>
[Scope('OnPrem')]
[CommitBehavior(CommitBehavior::Ignore)]
procedure RunPreview(var BankAccReconciliation: Record "Bank Acc. Reconciliation"): Boolean
begin
PreviewMode := true;
InitPost(BankAccReconciliation);
Post(BankAccReconciliation);
FinalizePost(BankAccReconciliation);
exit(true);
end;
local procedure InitPost(var BankAccRecon: Record "Bank Acc. Reconciliation")
begin
OnBeforeInitPost(BankAccRecon);
GLSetup.Get();
case BankAccRecon."Statement Type" of
BankAccRecon."Statement Type"::"Bank Reconciliation":
begin
BankAccRecon.TestField("Statement Date");
CheckLinesMatchEndingBalance(BankAccRecon, Difference);
end;
BankAccRecon."Statement Type"::"Payment Application":
begin
SourceCodeSetup.Get();
SourceCode := SourceCodeSetup."Payment Reconciliation Journal";
PostPaymentsOnly := BankAccRecon."Post Payments Only";
if PreviewMode then
exit;
if not PostPaymentsOnly then
if GuiAllowed then begin
if PAGE.RunModal(Page::"Post Pmts and Rec. Bank Acc.", BankAccRecon) <> ACTION::LookupOK then
Error('');
BankAccRecon.Get(BankAccRecon."Statement Type", BankAccRecon."Bank Account No.", BankAccRecon."Statement No.");
CheckLinesMatchEndingBalance(BankAccRecon, Difference);
end;
end;
end;
end;
local procedure StoreFieldsPrePosting(BankAccRecon: Record "Bank Acc. Reconciliation"; var PrePostingOutstdPayments: Decimal; var PrePostingOutstdBankTransactions: Decimal; var PrePostingGLBalance: Decimal; var PrePostingTotalPositiveDifference: Decimal; var PrePostingTotalNegativeDifference: Decimal)
var
PreBankAcc: Record "Bank Account";
BankAccPostingGroup: Record "Bank Account Posting Group";
BankAccReconTest: Codeunit "Bank Acc. Recon. Test";
begin
BankAccRecon.CalcFields(
"Total Applied Amount",
"Total Outstd Bank Transactions",
"Total Outstd Payments",
"Total Unposted Applied Amount"
);
PrePostingOutstdPayments := BankAccReconTest.TotalOutstandingPayments(BankAccRecon);
PrePostingOutstdBankTransactions := BankAccReconTest.TotalOutstandingBankTransactions(BankAccRecon);
PrePostingTotalPositiveDifference := BankAccReconTest.TotalPositiveDifference(BankAccRecon);
PrePostingTotalNegativeDifference := BankAccReconTest.TotalNegativeDifference(BankAccRecon);
PreBankAcc.SetFilter("Date Filter", '..%1', BankAccRecon."Statement Date");
PreBankAcc.SetAutoCalcFields("Balance at Date");
if PreBankAcc.Get(BankAccRecon."Bank Account No.") then
if BankAccPostingGroup.Get(PreBankAcc."Bank Acc. Posting Group") then
PrePostingGLBalance := BankAccReconTest.GetGLAccountBalanceLCY(PreBankAcc, BankAccPostingGroup, BankAccRecon."Statement Date");
end;
local procedure Post(BankAccRecon: Record "Bank Acc. Reconciliation")
var
BankAccReconLine: Record "Bank Acc. Reconciliation Line";
BankAccRecMatchBuffer: Record "Bank Acc. Rec. Match Buffer";
FeatureTelemetry: Codeunit "Feature Telemetry";
AppliedAmount: Decimal;
PrePostingOutstdPayments: Decimal;
PrePostingOutstdBankTransactions: Decimal;
PrePostingGLBalance: Decimal;
PrePostingTotalPositiveDifference: Decimal;
PrePostingTotalNegativeDifference: Decimal;
TotalTransAmtNotAppliedErr: Text;
begin
OnBeforePost(BankAccRecon, BankAccReconLine);
case BankAccRecon."Statement Type" of
BankAccRecon."Statement Type"::"Bank Reconciliation":
FeatureTelemetry.LogUptake('0000JLO', BankAccRecon.GetBankReconciliationTelemetryFeatureName(), Enum::"Feature Uptake Status"::Used);
BankAccRecon."Statement Type"::"Payment Application":
FeatureTelemetry.LogUptake('0000KMI', BankAccReconLine.GetPaymentRecJournalTelemetryFeatureName(), Enum::"Feature Uptake Status"::Used);
end;
StoreFieldsPrePosting(BankAccRecon, PrePostingOutstdPayments, PrePostingOutstdBankTransactions, PrePostingGLBalance, PrePostingTotalPositiveDifference, PrePostingTotalNegativeDifference);
// Run through lines
BankAccReconLine.FilterBankRecLines(BankAccRecon);
OnPostAfterFilterBankAccRecLines(BankAccReconLine, BankAccRecon);
TotalAmount := 0;
TotalAppliedAmount := 0;
Lines := 0;
if BankAccReconLine.IsEmpty() then
if Confirm(MissingBankAccReconLineQst) then
InsertEmptyBankAccReconLine(BankAccRecon)
else
Error('');
BankAccLedgEntry.LockTable();
CheckLedgEntry.LockTable();
PostedStamentNo := GetPostedStamentNo(BankAccRecon);
if BankAccReconLine.FindSet() then
repeat
Lines := Lines + 1;
if GuiAllowed then
if not PreviewMode then
Window.Update(2, Lines);
AppliedAmount := 0;
BankAccReconLine.FilterManyToOneMatches(BankAccRecMatchBuffer);
if (BankAccRecon."Statement Type" = BankAccRecon."Statement Type"::"Bank Reconciliation") and BankAccRecMatchBuffer.FindFirst() then begin
if (not BankAccRecMatchBuffer."Is Processed") then
CloseBankAccLEManyToOne(BankAccRecMatchBuffer, AppliedAmount, BankAccRecon."Statement Date");
end else begin
// Adjust entries
// Test amount and settled amount
case BankAccRecon."Statement Type" of
BankAccRecon."Statement Type"::"Bank Reconciliation":
CloseBankAccLedgEntry(BankAccReconLine, AppliedAmount, BankAccRecon."Statement Date");
BankAccRecon."Statement Type"::"Payment Application":
PostPaymentApplications(BankAccReconLine, AppliedAmount);
end;
OnBeforeAppliedAmountCheck(BankAccReconLine, AppliedAmount);
BankAccReconLine.TestField("Applied Amount", AppliedAmount);
end;
TotalAmount += BankAccReconLine."Statement Amount";
TotalAppliedAmount += AppliedAmount;
until BankAccReconLine.Next() = 0;
// Test amount
if BankAccRecon."Statement Type" = BankAccRecon."Statement Type"::"Payment Application" then
TotalTransAmtNotAppliedErr := NotFullyAppliedErr
else
TotalTransAmtNotAppliedErr := Text003;
if TotalAmount <> TotalAppliedAmount then
Error(
TotalTransAmtNotAppliedErr,
TotalAppliedAmount, TotalAmount);
if Difference <> 0 then
Error(Text004, Difference, 0);
if PreviewMode then
exit;
// Get bank
if not PostPaymentsOnly then
UpdateBank(BankAccRecon, TotalAmount);
case BankAccRecon."Statement Type" of
BankAccRecon."Statement Type"::"Bank Reconciliation":
begin
TransferToBankStmt(BankAccRecon, PrePostingOutstdPayments, PrePostingOutstdBankTransactions, PrePostingGLBalance, PrePostingTotalPositiveDifference, PrePostingTotalNegativeDifference);
FeatureTelemetry.LogUsage('0000JLP', BankAccRecon.GetBankReconciliationTelemetryFeatureName(), EventNameTelemetryTxt);
Session.LogMessage('0000JLQ', Format(Lines), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', BankAccountRecCategoryLbl);
end;
BankAccRecon."Statement Type"::"Payment Application":
begin
HandlePaymentApplicationTransfer(BankAccRecon, PrePostingOutstdPayments, PrePostingOutstdBankTransactions, PrePostingGLBalance, PrePostingTotalPositiveDifference, PrePostingTotalNegativeDifference);
FeatureTelemetry.LogUsage('0000KMJ', BankAccReconLine.GetPaymentRecJournalTelemetryFeatureName(), EventNameTelemetryPmtTxt);
Session.LogMessage('0000KMK', Format(Lines), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PaymentRecCategoryLbl);
end;
end;
end;
local procedure HandlePaymentApplicationTransfer(BankAccRecon: Record "Bank Acc. Reconciliation"; PrePostingOutstdPayments: Decimal; PrePostingOutstdBankTransactions: Decimal; PrePostingGLBalance: Decimal; PrePostingTotalPositiveDifference: Decimal; PrePostingTotalNegativeDifference: Decimal)
var
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeHandlePaymentApplicationTransfer(BankAccRecon, IsHandled);
if IsHandled then
exit;
TransferToPostPmtAppln(BankAccRecon);
if not BankAccRecon."Post Payments Only" then
TransferToBankStmt(BankAccRecon, PrePostingOutstdPayments, PrePostingOutstdBankTransactions, PrePostingGLBalance, PrePostingTotalPositiveDifference, PrePostingTotalNegativeDifference);
end;
local procedure FinalizePost(BankAccRecon: Record "Bank Acc. Reconciliation")
var
UpdateAnalysisView: Codeunit "Update Analysis View";
CreationDateTime: DateTime;
MatchedWithAI: Boolean;
LineCount: Integer;
TelemetryCategories: Dictionary of [Text, Text];
DurationUntilPosting: BigInteger;
begin
if PreviewMode then
exit;
OnBeforeFinalizePost(BankAccRecon);
CreationDateTime := BankAccRecon.SystemCreatedAt;
MatchedWithAI := AIMatchProposalsExist(BankAccRecon);
DeleteRelatedRecords(BankAccRecon, LineCount);
UpdateAnalysisView.UpdateAll(0, true);
TelemetryCategories.Add('Category', BankAccountRecCategoryLbl);
TelemetryCategories.Add('MatchedWithAI', Format(MatchedWithAI, 0, 9));
TelemetryCategories.Add('NumberOfLines', Format(LineCount));
if TryCalculateDurationToPost(DurationUntilPosting, CreationDateTime) then
Session.LogMessage('0000LHY', Format(DurationUntilPosting), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, TelemetryCategories);
OnAfterFinalizePost(BankAccRecon);
end;
local procedure DeleteRelatedRecords(var BankAccReconciliation: Record "Bank Acc. Reconciliation"; var LineCount: Integer)
var
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
AppliedPaymentEntry: Record "Applied Payment Entry";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeDeleteRelatedRecords(BankAccReconciliation, IsHandled);
if IsHandled then
exit;
if BankAccReconciliationLine.LinesExist(BankAccReconciliation) then
repeat
AppliedPaymentEntry.FilterAppliedPmtEntry(BankAccReconciliationLine);
AppliedPaymentEntry.DeleteAll();
BankAccReconciliationLine.Delete();
BankAccReconciliationLine.ClearDataExchEntries();
LineCount += 1;
until BankAccReconciliationLine.Next() = 0;
BankAccReconciliation.Find();
BankAccReconciliation.Delete();
end;
[TryFunction]
local procedure TryCalculateDurationToPost(var DurationUntilPosting: BigInteger; CreationDateTime: DateTime)
begin
DurationUntilPosting := CurrentDateTime() - CreationDateTime;
end;
local procedure AIMatchProposalsExist(var BankAccReconciliation: Record "Bank Acc. Reconciliation"): Boolean
var
PaymentMatchingDetails: Record "Payment Matching Details";
begin
PaymentMatchingDetails.SetRange("Statement Type", BankAccReconciliation."Statement Type");
PaymentMatchingDetails.SetRange("Bank Account No.", BankAccReconciliation."Bank Account No.");
PaymentMatchingDetails.SetRange("Statement No.", BankAccReconciliation."Statement No.");
PaymentMatchingDetails.SetFilter(Message, '*Copilot*');
exit(not PaymentMatchingDetails.IsEmpty());
end;
local procedure CheckLinesMatchEndingBalance(BankAccRecon: Record "Bank Acc. Reconciliation"; var Difference: Decimal)
var
BankAccReconLine: Record "Bank Acc. Reconciliation Line";
begin
BankAccReconLine.LinesExist(BankAccRecon);
BankAccReconLine.CalcSums(BankAccReconLine."Statement Amount", BankAccReconLine.Difference);
OnCheckLinesMatchEndingBalanceOnAfterCalcSums(BankAccReconLine);
if BankAccReconLine."Statement Amount" <>
BankAccRecon."Statement Ending Balance" - BankAccRecon."Balance Last Statement"
then
Error(StatementEndingBalanceErr, BankAccRecon.FieldCaption("Statement Ending Balance"));
Difference := BankAccReconLine.Difference;
end;
local procedure CloseBankAccLEManyToOne(BankAccRecMatchBuffer: Record "Bank Acc. Rec. Match Buffer"; var AppliedAmount: Decimal; StatementDate: Date);
var
BankAccRecMatchBufferCopy: Record "Bank Acc. Rec. Match Buffer";
BankAccRecLine: Record "Bank Acc. Reconciliation Line";
LedgerEntryNo: Integer;
TotalRecLinesAmount: Decimal;
begin
BankAccRecMatchBufferCopy.SetRange("Bank Account No.", BankAccRecMatchBuffer."Bank Account No.");
BankAccRecMatchBufferCopy.SetRange("Statement No.", BankAccRecMatchBuffer."Statement No.");
BankAccRecMatchBufferCopy.SetRange("Match ID", BankAccRecMatchBuffer."Match ID");
if (not BankAccRecMatchBufferCopy.IsEmpty()) then
if BankAccRecMatchBufferCopy.FindSet() then
repeat
LedgerEntryNo := BankAccRecMatchBufferCopy."Ledger Entry No.";
BankAccRecMatchBufferCopy."Is Processed" := true;
BankAccRecMatchBufferCopy.Modify();
BankAccRecLine.SetRange("Bank Account No.", BankAccRecMatchBufferCopy."Bank Account No.");
BankAccRecLine.SetRange("Statement No.", BankAccRecMatchBufferCopy."Statement No.");
BankAccRecLine.SetRange("Statement Line No.", BankAccRecMatchBufferCopy."Statement Line No.");
if BankAccRecLine.FindFirst() then
TotalRecLinesAmount += BankAccRecLine."Statement Amount";
until BankAccRecMatchBufferCopy.Next() = 0;
BankAccLedgEntry.Get(LedgerEntryNo);
BankAccLedgEntry.TestField("Remaining Amount", TotalRecLinesAmount);
AppliedAmount += BankAccLedgEntry."Remaining Amount";
BankAccLedgEntry."Remaining Amount" := 0;
BankAccLedgEntry.Open := false;
BankAccLedgEntry."Closed at Date" := StatementDate;
BankAccLedgEntry."Statement Status" := BankAccLedgEntry."Statement Status"::Closed;
OnCloseBankAccLedgEntryOnBeforeBankAccLedgEntryModify(BankAccLedgEntry, BankAccRecLine);
BankAccLedgEntry.Modify();
CheckLedgEntry.Reset();
CheckLedgEntry.SetCurrentKey("Bank Account Ledger Entry No.");
CheckLedgEntry.SetRange(
"Bank Account Ledger Entry No.", BankAccLedgEntry."Entry No.");
CheckLedgEntry.SetRange(Open, true);
if CheckLedgEntry.Find('-') then
repeat
CheckLedgEntry.TestField(Open, true);
CheckLedgEntry.TestField(
"Statement Status",
CheckLedgEntry."Statement Status"::"Bank Acc. Entry Applied");
CheckLedgEntry.Open := false;
CheckLedgEntry."Statement Status" := CheckLedgEntry."Statement Status"::Closed;
CheckLedgEntry.Modify();
until CheckLedgEntry.Next() = 0;
end;
local procedure CloseBankAccLedgEntry(BankAccReconLine: Record "Bank Acc. Reconciliation Line"; var AppliedAmount: Decimal; StatementClosingDate: Date)
var
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeCloseBankAccLedgEntry(BankAccReconLine, AppliedAmount, IsHandled);
if IsHandled then
exit;
BankAccLedgEntry.Reset();
BankAccLedgEntry.SetCurrentKey("Bank Account No.", Open);
BankAccLedgEntry.SetRange("Bank Account No.", BankAccReconLine."Bank Account No.");
BankAccLedgEntry.SetRange(Open, true);
BankAccLedgEntry.SetFilter("Statement Status", '%1|%2', BankAccLedgEntry."Statement Status"::"Bank Acc. Entry Applied", BankAccLedgEntry."Statement Status"::"Check Entry Applied");
BankAccLedgEntry.SetRange("Statement No.", BankAccReconLine."Statement No.");
BankAccLedgEntry.SetRange("Statement Line No.", BankAccReconLine."Statement Line No.");
OnCloseBankAccLedgEntryOnAfterBankAccLedgEntrySetFilters(BankAccLedgEntry, BankAccReconLine);
if BankAccLedgEntry.FindSet(true) then
repeat
AppliedAmount += BankAccLedgEntry."Remaining Amount";
BankAccLedgEntry."Remaining Amount" := 0;
BankAccLedgEntry.Open := false;
BankAccLedgEntry."Closed at Date" := StatementClosingDate;
BankAccLedgEntry."Statement Status" := BankAccLedgEntry."Statement Status"::Closed;
BankAccLedgEntry."Statement No." := PostedStamentNo;
OnCloseBankAccLedgEntryOnBeforeBankAccLedgEntryModify(BankAccLedgEntry, BankAccReconLine);
BankAccLedgEntry.Modify();
CheckLedgEntry.Reset();
CheckLedgEntry.SetCurrentKey("Bank Account Ledger Entry No.");
CheckLedgEntry.SetRange(
"Bank Account Ledger Entry No.", BankAccLedgEntry."Entry No.");
CheckLedgEntry.SetRange(Open, true);
if CheckLedgEntry.Find('-') then
repeat
CheckLedgEntry.TestField(Open, true);
CheckLedgEntry.TestField("Statement No.", BankAccReconLine."Statement No.");
CheckLedgEntry.TestField("Statement Line No.", BankAccReconLine."Statement Line No.");
CheckLedgEntry.Open := false;
CheckLedgEntry."Statement Status" := CheckLedgEntry."Statement Status"::Closed;
OnCloseBankAccLedgEntryOnBeforeCheckLedgEntryModify(CheckLedgEntry, BankAccReconLine);
CheckLedgEntry.Modify();
until CheckLedgEntry.Next() = 0;
until BankAccLedgEntry.Next() = 0;
end;
local procedure PostPaymentApplications(BankAccReconLine: Record "Bank Acc. Reconciliation Line"; var AppliedAmount: Decimal)
var
BankAccReconciliation: Record "Bank Acc. Reconciliation";
Currency: Record Currency;
CurrExchRate: Record "Currency Exchange Rate";
AppliedPmtEntry: Record "Applied Payment Entry";
BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
DimensionManagement: Codeunit DimensionManagement;
ConfirmManagement: Codeunit "Confirm Management";
PaymentLineAmount: Decimal;
RemainingAmount: Decimal;
IsApplied: Boolean;
IsHandled: Boolean;
ContinueWithPartialApplicationOfBLE: Boolean;
begin
IsHandled := false;
OnBeforePostPaymentApplications(BankAccReconLine, AppliedAmount, IsHandled);
if IsHandled then
exit;
if BankAccReconLine.IsTransactionPostedAndReconciled() then
Error(TransactionAlreadyReconciledErr, BankAccReconLine."Transaction Date", BankAccReconLine."Transaction Text");
OnPostPaymentApplicationsOnAfterTransactionPostedAndReconciledCheck(BankAccReconLine, AppliedAmount, SourceCode);
if BankAccReconLine."Account No." = '' then
Error(LineNoTAppliedErr, BankAccReconLine."Transaction Date", BankAccReconLine."Transaction Text");
BankAcc.Get(BankAccReconLine."Bank Account No.");
GenJnlLine.Init();
GenJnlLine.SetSuppressCommit(true);
GenJnlLine."Document Type" := GenJnlLine."Document Type"::Payment;
if IsRefund(BankAccReconLine) then
if BankAccReconLine."Account Type" = BankAccReconLine."Account Type"::Employee then
GenJnlLine."Document Type" := GenJnlLine."Document Type"::" "
else
GenJnlLine."Document Type" := GenJnlLine."Document Type"::Refund;
GenJnlLine."Posting Date" := BankAccReconLine."Transaction Date";
GenJnlLine."VAT Reporting Date" := BankAccReconLine."Transaction Date";
GenJnlLine."Account Type" := Enum::"Gen. Journal Account Type".FromInteger(BankAccReconLine.GetAppliedToAccountType());
BankAccReconciliation.Get(
BankAccReconLine."Statement Type", BankAccReconLine."Bank Account No.", BankAccReconLine."Statement No.");
GenJnlLine."Copy VAT Setup to Jnl. Lines" := BankAccReconciliation."Copy VAT Setup to Jnl. Line";
GenJnlLine.Validate("Account No.", BankAccReconLine.GetAppliedToAccountNo());
GenJnlLine."Dimension Set ID" := BankAccReconLine."Dimension Set ID";
DimensionManagement.UpdateGlobalDimFromDimSetID(
BankAccReconLine."Dimension Set ID", GenJnlLine."Shortcut Dimension 1 Code", GenJnlLine."Shortcut Dimension 2 Code");
GenJnlLine.Description := BankAccReconLine.GetDescription();
GenJnlLine."Document No." := PostedStamentNo;
GenJnlLine."Bal. Account Type" := GenJnlLine."Bal. Account Type"::"Bank Account";
GenJnlLine."Bal. Account No." := BankAcc."No.";
GenJnlLine."Source Code" := SourceCode;
GenJnlLine."Allow Zero-Amount Posting" := true;
GenJnlLine."Applies-to ID" := BankAccReconLine.GetAppliesToID();
if GLSetup."Journal Templ. Name Mandatory" then begin
GLSetup.TestField("Bank Acc. Recon. Template Name");
GLSetup.TestField("Bank Acc. Recon. Batch Name");
GenJnlLine."Journal Template Name" := GLSetup."Bank Acc. Recon. Template Name";
GenJnlLine."Journal Batch Name" := GLSetup."Bank Acc. Recon. Batch Name";
end;
IsApplied := false;
IsHandled := false;
OnPostPaymentApplicationsOnAfterInitGenJnlLine(GenJnlLine, BankAccReconLine, IsApplied, AppliedAmount, PaymentLineAmount, IsHandled);
if IsHandled then
exit;
if AppliedPmtEntry.AppliedPmtEntryLinesExist(BankAccReconLine) then
repeat
AppliedAmount += AppliedPmtEntry."Applied Amount" - AppliedPmtEntry."Applied Pmt. Discount";
PaymentLineAmount += AppliedPmtEntry."Applied Amount" - AppliedPmtEntry."Applied Pmt. Discount";
AppliedPmtEntry.TestField("Account Type", BankAccReconLine."Account Type");
AppliedPmtEntry.TestField("Account No.", BankAccReconLine."Account No.");
if AppliedPmtEntry."Applies-to Entry No." <> 0 then begin
case AppliedPmtEntry."Account Type" of
AppliedPmtEntry."Account Type"::Customer:
ApplyCustLedgEntry(
AppliedPmtEntry, GenJnlLine."Applies-to ID", GenJnlLine."Posting Date", 0D, 0D, AppliedPmtEntry."Applied Pmt. Discount");
AppliedPmtEntry."Account Type"::Vendor:
ApplyVendLedgEntry(
AppliedPmtEntry, GenJnlLine."Applies-to ID", GenJnlLine."Posting Date", 0D, 0D, AppliedPmtEntry."Applied Pmt. Discount");
AppliedPmtEntry."Account Type"::Employee:
ApplyEmployeeLedgEntry(
AppliedPmtEntry, GenJnlLine."Applies-to ID", GenJnlLine."Posting Date", 0D, 0D, AppliedPmtEntry."Applied Pmt. Discount");
AppliedPmtEntry."Account Type"::"Bank Account":
begin
BankAccountLedgerEntry.Get(AppliedPmtEntry."Applies-to Entry No.");
RemainingAmount := BankAccountLedgerEntry."Remaining Amount";
case true of
RemainingAmount = AppliedPmtEntry."Applied Amount":
begin
if not PostPaymentsOnly then
CloseBankAccountLedgerEntry(AppliedPmtEntry."Applies-to Entry No.", AppliedPmtEntry."Applied Amount", BankAccReconciliation."Statement Date", BankAccReconLine."Statement Line No.");
PaymentLineAmount -= AppliedPmtEntry."Applied Amount";
end;
Abs(RemainingAmount) > Abs(AppliedPmtEntry."Applied Amount"):
begin
ContinueWithPartialApplicationOfBLE := ConfirmManagement.GetResponseOrDefault(StrSubstNo(PartialApplicationBankLedgerEntryQst, AppliedPmtEntry."Statement Line No."), false);
Session.LogMessage('0000KSG', 'Partial application of BLE', Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::All, 'Continue', Format(ContinueWithPartialApplicationOfBLE));
if not ContinueWithPartialApplicationOfBLE then
Error('');
if not PostPaymentsOnly then begin
BankAccountLedgerEntry."Remaining Amount" -= AppliedPmtEntry."Applied Amount";
BankAccountLedgerEntry.Modify();
end;
PaymentLineAmount -= AppliedPmtEntry."Applied Amount";
end;
Abs(RemainingAmount) < Abs(AppliedPmtEntry."Applied Amount"):
begin
if not PostPaymentsOnly then
CloseBankAccountLedgerEntry(AppliedPmtEntry."Applies-to Entry No.", RemainingAmount, BankAccReconciliation."Statement Date", BankAccReconLine."Statement Line No.");
PaymentLineAmount -= RemainingAmount;
end;
end;
end;
else
OnPostPaymentApplicationsOnAccountTypeCaseElse(AppliedPmtEntry, GenJnlLine);
end;
IsApplied := true;
end;
until AppliedPmtEntry.Next() = 0;
if PaymentLineAmount <> 0 then begin
if not IsApplied then
GenJnlLine."Applies-to ID" := '';
if (GenJnlLine."Account Type" <> GenJnlLine."Account Type"::"Bank Account") or
(GenJnlLine."Currency Code" = BankAcc."Currency Code")
then begin
GenJnlLine.Validate("Currency Code", BankAcc."Currency Code");
Currency.Initialize(GenJnlLine."Currency Code");
GenJnlLine.Amount := Round(-PaymentLineAmount, Currency."Amount Rounding Precision");
if GenJnlLine."Currency Code" <> '' then
GenJnlLine."Amount (LCY)" := Round(
CurrExchRate.ExchangeAmtFCYToLCY(
GenJnlLine."Posting Date", GenJnlLine."Currency Code",
GenJnlLine.Amount, GenJnlLine."Currency Factor"));
GenJnlLine.Validate("VAT %");
GenJnlLine.Validate("Bal. VAT %")
end else
Error(ExcessiveAmtErr, PaymentLineAmount, GLSetup.GetCurrencyCode(BankAcc."Currency Code"));
OnPostPaymentApplicationsOnBeforeValidateApplyRequirements(BankAccReconLine, GenJnlLine, AppliedAmount);
GenJnlLine.ValidateApplyRequirements(GenJnlLine);
GenJnlPostLine.RunWithCheck(GenJnlLine);
OnPostPaymentApplicationsOnAfterPostGenJnlLine(GenJnlLine, GenJnlPostLine);
if not PostPaymentsOnly then begin
BankAccountLedgerEntry.SetRange(Open, true);
BankAccountLedgerEntry.SetRange("Bank Account No.", BankAcc."No.");
BankAccountLedgerEntry.SetRange("Document Type", GenJnlLine."Document Type");
BankAccountLedgerEntry.SetRange("Document No.", PostedStamentNo);
BankAccountLedgerEntry.SetRange("Posting Date", GenJnlLine."Posting Date");
OnPostPaymentApplicationsOnAfterBankAccountLedgerEntrySetFilters(BankAccountLedgerEntry, GenJnlLine);
if BankAccountLedgerEntry.FindLast() then
CloseBankAccountLedgerEntry(BankAccountLedgerEntry."Entry No.", BankAccountLedgerEntry.Amount, BankAccReconciliation."Statement Date", BankAccReconLine."Statement Line No.");
end;
end;
end;
local procedure UpdateBank(BankAccRecon: Record "Bank Acc. Reconciliation"; Amt: Decimal)
var
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeUpdateBank(BankAccRecon, BankAcc, IsHandled, Amt, PostedStamentNo);
if IsHandled then
exit;
BankAcc.LockTable();
BankAcc.Get(BankAccRecon."Bank Account No.");
BankAcc.TestField(Blocked, false);
BankAcc."Last Statement No." := PostedStamentNo;
BankAcc."Balance Last Statement" := BankAccRecon."Balance Last Statement" + Amt;
BankAcc.Modify();
end;
local procedure TransferToBankStmt(BankAccRecon: Record "Bank Acc. Reconciliation"; PrePostingOutstdPayments: Decimal; PrePostingOutstdBankTransactions: Decimal; PrePostingGLBalance: Decimal; PrePostingTotalPositiveDifference: Decimal; PrePostingTotalNegativeDifference: Decimal)
var
BankAccStmt: Record "Bank Account Statement";
BankAccStmtLine: Record "Bank Account Statement Line";
BankAccReconLine: Record "Bank Acc. Reconciliation Line";
BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
AppliedPaymentEntry: Record "Applied Payment Entry";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeTransferToBankStmt(BankAccRecon, PrePostingOutstdPayments, PrePostingOutstdBankTransactions, PrePostingGLBalance, PrePostingTotalPositiveDifference, PrePostingTotalNegativeDifference, IsHandled);
if IsHandled then
exit;
BankAccStmt.Init();
BankAccStmt.TransferFields(BankAccRecon);
BankAccStmt."Statement No." := PostedStamentNo;
if BankAccReconLine.LinesExist(BankAccRecon) then
repeat
BankAccStmtLine.TransferFields(BankAccReconLine);
BankAccStmtLine."Statement No." := BankAccStmt."Statement No.";
OnTransferToBankStmtOnBeforeBankAccStmtLineInsert(BankAccStmtLine, BankAccReconLine);
if AppliedPaymentEntry.AppliedPmtEntryLinesExist(BankAccReconLine) then
if (AppliedPaymentEntry."Applies-to Entry No." <> 0) and (AppliedPaymentEntry."Account Type" = AppliedPaymentEntry."Account Type"::"Bank Account") then begin
BankAccountLedgerEntry.Get(AppliedPaymentEntry."Applies-to Entry No.");
BankAccStmtLine.Difference := BankAccountLedgerEntry."Remaining Amount";
end;
BankAccStmtLine.Insert();
BankAccReconLine.ClearDataExchEntries();
until BankAccReconLine.Next() = 0;
BankAccStmtLine.SetRange("Bank Account No.", BankAccStmt."Bank Account No.");
BankAccStmtLine.SetRange("Statement No.", BankAccStmt."Statement No.");
BankAccStmtLine.CalcSums("Statement Amount");
BankAccStmt."G/L Balance at Posting Date" := PrePostingGLBalance;
BankAccStmt."Outstd. Payments at Posting" := PrePostingOutstdPayments;
BankAccStmt."Outstd. Transact. at Posting" := PrePostingOutstdBankTransactions;
BankAccStmt."Total Pos. Diff. at Posting" := PrePostingTotalPositiveDifference;
BankAccStmt."Total Neg. Diff. at Posting" := PrePostingTotalNegativeDifference;
OnBeforeBankAccStmtInsert(BankAccStmt, BankAccRecon);
BankAccStmt.Insert();
end;
local procedure GetPostedStamentNo(BankAccRecon: Record "Bank Acc. Reconciliation") StatementNo: Code[20]
var
BankAccStmt: Record "Bank Account Statement";
begin
StatementNo := BankAccRecon."Statement No.";
BankAccStmt.SetRange("Bank Account No.", BankAccRecon."Bank Account No.");
BankAccStmt.SetRange("Statement No.", BankAccRecon."Statement No.");
if not BankAccStmt.IsEmpty() then
StatementNo := GetNextStatementNoAndUpdateBankAccount(BankAccRecon."Bank Account No.");
end;
local procedure TransferToPostPmtAppln(BankAccRecon: Record "Bank Acc. Reconciliation")
var
PostedPmtReconHdr: Record "Posted Payment Recon. Hdr";
PostedPmtReconLine: Record "Posted Payment Recon. Line";
BankAccReconLine: Record "Bank Acc. Reconciliation Line";
TypeHelper: Codeunit "Type Helper";
FieldLength: Integer;
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeTransferToPostPmtAppln(BankAccRecon, IsHandled);
if IsHandled then
exit;
if BankAccReconLine.LinesExist(BankAccRecon) then
repeat
PostedPmtReconLine.TransferFields(BankAccReconLine);
FieldLength := TypeHelper.GetFieldLength(DATABASE::"Posted Payment Recon. Line",
PostedPmtReconLine.FieldNo("Applied Document No."));
PostedPmtReconLine."Applied Document No." := CopyStr(BankAccReconLine.GetAppliedToDocumentNo(), 1, FieldLength);
FieldLength := TypeHelper.GetFieldLength(DATABASE::"Posted Payment Recon. Line",
PostedPmtReconLine.FieldNo("Applied Entry No."));
PostedPmtReconLine."Applied Entry No." := CopyStr(BankAccReconLine.GetAppliedToEntryNo(), 1, FieldLength);
PostedPmtReconLine.Reconciled := not PostPaymentsOnly;
OnTransferToPostPmtApplnOnBeforePostedPmtReconLineInsert(PostedPmtReconLine, BankAccReconLine);
PostedPmtReconLine.Insert();
BankAccReconLine.ClearDataExchEntries();
until BankAccReconLine.Next() = 0;
PostedPmtReconHdr.TransferFields(BankAccRecon);
OnBeforePostedPmtReconInsert(PostedPmtReconHdr, BankAccRecon);
PostedPmtReconHdr.Insert();
end;
/// <summary>
/// Applies a payment entry to a customer ledger entry during bank reconciliation posting.
/// Updates the customer ledger entry with payment application details including discount dates,
/// tolerance calculations, and application amounts. Handles currency exchange rate calculations
/// for foreign currency transactions and integrates with the payment discount calculation engine.
/// </summary>
/// <param name="AppliedPmtEntry">Applied payment entry containing application details and amounts.</param>
/// <param name="AppliesToID">Application identifier for grouping related applications; empty for immediate application.</param>
/// <param name="PostingDate">Date for payment application posting and currency rate calculations.</param>
/// <param name="PmtDiscDueDate">Payment discount due date for discount eligibility calculation.</param>
/// <param name="PmtDiscToleranceDate">Payment discount tolerance date for extended discount availability.</param>
/// <param name="RemPmtDiscPossible">Remaining payment discount amount available for the application.</param>
procedure ApplyCustLedgEntry(AppliedPmtEntry: Record "Applied Payment Entry"; AppliesToID: Code[50]; PostingDate: Date; PmtDiscDueDate: Date; PmtDiscToleranceDate: Date; RemPmtDiscPossible: Decimal)
var
CustLedgEntry: Record "Cust. Ledger Entry";
CurrExchRate: Record "Currency Exchange Rate";
IsHandled: Boolean;
begin
CustLedgEntry.Get(AppliedPmtEntry."Applies-to Entry No.");
CustLedgEntry.TestField(Open);
BankAcc.Get(AppliedPmtEntry."Bank Account No.");
IsHandled := false;
OnBeforeApplyCustLedgEntry(CustLedgEntry, AppliedPmtEntry, BankAcc, AppliesToID, PostingDate, PmtDiscDueDate, PmtDiscToleranceDate, RemPmtDiscPossible, IsHandled);
if IsHandled then
exit;
if AppliesToID = '' then begin
CustLedgEntry."Pmt. Discount Date" := PmtDiscDueDate;
CustLedgEntry."Pmt. Disc. Tolerance Date" := PmtDiscToleranceDate;
CustLedgEntry."Remaining Pmt. Disc. Possible" := RemPmtDiscPossible;
if BankAcc.IsInLocalCurrency() then
CustLedgEntry."Remaining Pmt. Disc. Possible" :=
CurrExchRate.ExchangeAmount(CustLedgEntry."Remaining Pmt. Disc. Possible", '', CustLedgEntry."Currency Code", PostingDate);
end else begin
CustLedgEntry."Applies-to ID" := AppliesToID;
CustLedgEntry."Amount to Apply" := AppliedPmtEntry.CalcAmountToApply(PostingDate);
end;
if PreviewMode then
CustEntryEditNoCommit(CustLedgEntry)
else
CODEUNIT.Run(CODEUNIT::"Cust. Entry-Edit", CustLedgEntry);
end;
[CommitBehavior(CommitBehavior::Ignore)]
local procedure CustEntryEditNoCommit(var CustLedgerEntry: Record "Cust. Ledger Entry")
begin
CODEUNIT.Run(CODEUNIT::"Cust. Entry-Edit", CustLedgerEntry);
end;
/// <summary>
/// Applies a payment entry to a vendor ledger entry during bank reconciliation posting.
/// Updates the vendor ledger entry with payment application details including discount dates,
/// tolerance calculations, and application amounts. Handles currency exchange rate calculations
/// for foreign currency transactions and manages vendor-specific payment discount processing.
/// </summary>
/// <param name="AppliedPmtEntry">Applied payment entry containing application details and amounts.</param>
/// <param name="AppliesToID">Application identifier for grouping related applications; empty for immediate application.</param>
/// <param name="PostingDate">Date for payment application posting and currency rate calculations.</param>
/// <param name="PmtDiscDueDate">Payment discount due date for discount eligibility calculation.</param>
/// <param name="PmtDiscToleranceDate">Payment discount tolerance date for extended discount availability.</param>
/// <param name="RemPmtDiscPossible">Remaining payment discount amount available for the application.</param>
procedure ApplyVendLedgEntry(AppliedPmtEntry: Record "Applied Payment Entry"; AppliesToID: Code[50]; PostingDate: Date; PmtDiscDueDate: Date; PmtDiscToleranceDate: Date; RemPmtDiscPossible: Decimal)
var
VendLedgEntry: Record "Vendor Ledger Entry";
CurrExchRate: Record "Currency Exchange Rate";
ApplyVendLedgEntryHandled: Boolean;
begin
VendLedgEntry.Get(AppliedPmtEntry."Applies-to Entry No.");
VendLedgEntry.TestField(Open);
BankAcc.Get(AppliedPmtEntry."Bank Account No.");
ApplyVendLedgEntryHandled := false;
OnBeforeApplyVendLedgEntry(VendLedgEntry, AppliedPmtEntry, BankAcc, AppliesToID, PostingDate, PmtDiscDueDate, PmtDiscToleranceDate, RemPmtDiscPossible, ApplyVendLedgEntryHandled);
if not ApplyVendLedgEntryHandled then
if AppliesToID = '' then begin
VendLedgEntry."Pmt. Discount Date" := PmtDiscDueDate;
VendLedgEntry."Pmt. Disc. Tolerance Date" := PmtDiscToleranceDate;
VendLedgEntry."Remaining Pmt. Disc. Possible" := RemPmtDiscPossible;
if BankAcc.IsInLocalCurrency() then
VendLedgEntry."Remaining Pmt. Disc. Possible" :=
CurrExchRate.ExchangeAmount(VendLedgEntry."Remaining Pmt. Disc. Possible", '', VendLedgEntry."Currency Code", PostingDate);
end else begin
VendLedgEntry."Applies-to ID" := AppliesToID;
VendLedgEntry."Amount to Apply" := AppliedPmtEntry.CalcAmountToApply(PostingDate);
end;
if PreviewMode then
VendEntryEditNoCommit(VendLedgEntry)
else
CODEUNIT.Run(CODEUNIT::"Vend. Entry-Edit", VendLedgEntry);
end;
[CommitBehavior(CommitBehavior::Ignore)]
local procedure VendEntryEditNoCommit(var VendorLedgerEntry: Record "Vendor Ledger Entry")
begin
CODEUNIT.Run(CODEUNIT::"Vend. Entry-Edit", VendorLedgerEntry);
end;
procedure ApplyEmployeeLedgEntry(AppliedPmtEntry: Record "Applied Payment Entry"; AppliesToID: Code[50]; PostingDate: Date; PmtDiscDueDate: Date; PmtDiscToleranceDate: Date; RemPmtDiscPossible: Decimal)
var
EmployeeLedgerEntry: Record "Employee Ledger Entry";
begin
EmployeeLedgerEntry.Get(AppliedPmtEntry."Applies-to Entry No.");
EmployeeLedgerEntry.TestField(Open);
BankAcc.Get(AppliedPmtEntry."Bank Account No.");
if AppliesToID <> '' then begin
EmployeeLedgerEntry."Applies-to ID" := AppliesToID;
EmployeeLedgerEntry."Amount to Apply" := AppliedPmtEntry.CalcAmountToApply(PostingDate);
end;
if PreviewMode then
EmplEntryEditNoCommit(EmployeeLedgerEntry)
else
CODEUNIT.Run(CODEUNIT::"Empl. Entry-Edit", EmployeeLedgerEntry);
end;
[CommitBehavior(CommitBehavior::Ignore)]
local procedure EmplEntryEditNoCommit(var EmployeeLedgerEntry: Record "Employee Ledger Entry")
begin
CODEUNIT.Run(CODEUNIT::"Empl. Entry-Edit", EmployeeLedgerEntry);
end;
local procedure CloseBankAccountLedgerEntry(EntryNo: Integer; AppliedAmount: Decimal; StatementDate: Date; StatementLineNo: Integer)
var
BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
CheckLedgerEntry: Record "Check Ledger Entry";
begin
BankAccountLedgerEntry.Get(EntryNo);
BankAccountLedgerEntry.TestField(Open);
BankAccountLedgerEntry.TestField("Remaining Amount", AppliedAmount);
BankAccountLedgerEntry."Remaining Amount" := 0;
BankAccountLedgerEntry.Open := false;
BankAccountLedgerEntry."Statement Status" := BankAccountLedgerEntry."Statement Status"::Closed;
BankAccountLedgerEntry."Closed at Date" := StatementDate;
BankAccountLedgerEntry."Statement No." := PostedStamentNo;
BankAccountLedgerEntry."Statement Line No." := StatementLineNo;
BankAccountLedgerEntry.Modify();
CheckLedgerEntry.Reset();
CheckLedgerEntry.SetCurrentKey("Bank Account Ledger Entry No.");
CheckLedgerEntry.SetRange(
"Bank Account Ledger Entry No.", BankAccountLedgerEntry."Entry No.");
CheckLedgerEntry.SetRange(Open, true);
if CheckLedgerEntry.FindSet() then
repeat
CheckLedgerEntry.Open := false;
CheckLedgerEntry."Statement Status" := CheckLedgerEntry."Statement Status"::Closed;
CheckLedgEntry."Statement No." := PostedStamentNo;
CheckLedgEntry."Statement Line No." := StatementLineNo;
CheckLedgerEntry.Modify();
until CheckLedgerEntry.Next() = 0;
end;
local procedure GetNextStatementNoAndUpdateBankAccount(BankAccountNo: Code[20]): Code[20]
var
BankAccount: Record "Bank Account";
begin
BankAccount.SetLoadFields("Last Statement No.");
BankAccount.Get(BankAccountNo);
if BankAccount."Last Statement No." <> '' then
BankAccount."Last Statement No." := IncStr(BankAccount."Last Statement No.")
else
BankAccount."Last Statement No." := '1';
BankAccount.Modify();
exit(BankAccount."Last Statement No.");
end;
local procedure IsRefund(BankAccReconLine: Record "Bank Acc. Reconciliation Line"): Boolean
begin
if (BankAccReconLine."Account Type" = BankAccReconLine."Account Type"::Customer) and (BankAccReconLine."Statement Amount" < 0) or
(BankAccReconLine."Account Type" in [BankAccReconLine."Account Type"::Vendor, BankAccReconLine."Account Type"::Employee]) and (BankAccReconLine."Statement Amount" > 0)
then
exit(true);
exit(false);
end;
procedure Preview(var BankAccReconciliationSource: Record "Bank Acc. Reconciliation")
var
BankAccReconPostPreview: Codeunit "Bank. Acc. Recon. Post Preview";
BankAccountReconciliationPost: Codeunit "Bank Acc. Reconciliation Post";
begin
BankAccReconPostPreview.Preview(BankAccountReconciliationPost, BankAccReconciliationSource);
end;
local procedure InsertEmptyBankAccReconLine(BankAccReconciliation: Record "Bank Acc. Reconciliation")
var
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
begin
BankAccReconciliationLine.Init();
BankAccReconciliationLine."Bank Account No." := BankAccReconciliation."Bank Account No.";
BankAccReconciliationLine."Statement No." := BankAccReconciliation."Statement No.";
BankAccReconciliationLine."Statement Line No." := 10000;
BankAccReconciliationLine."Transaction Date" := BankAccReconciliation."Statement Date";
BankAccReconciliationLine.Description := EmptyDescriptionTxt;
BankAccReconciliationLine.Validate("Statement Amount", 0);
BankAccReconciliationLine.Insert();
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Bank. Acc. Recon. Post Preview", 'OnRunPreview', '', false, false)]
local procedure OnRunPreview(var Result: Boolean; var Subscriber: Codeunit "Bank Acc. Reconciliation Post"; var BankAccReconciliationSource: Record "Bank Acc. Reconciliation")
var
BankAccReconciliation: Record "Bank Acc. Reconciliation";
BankAccountReconciliationPost: Codeunit "Bank Acc. Reconciliation Post";
begin
BankAccountReconciliationPost := Subscriber;
BankAccReconciliation.Copy(BankAccReconciliationSource);
Result := not BankAccountReconciliationPost.RunPreview(BankAccReconciliation);
end;
/// <summary>
/// Event raised before checking the applied amount for a bank account reconciliation line.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="AppliedAmount">The applied amount being checked.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeAppliedAmountCheck(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var AppliedAmount: Decimal)
begin
end;
/// <summary>
/// Event raised before inserting a bank account statement record during posting.
/// </summary>
/// <param name="BankAccStatement">The bank account statement record being inserted.</param>
/// <param name="BankAccReconciliation">The bank account reconciliation record being posted.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeBankAccStmtInsert(var BankAccStatement: Record "Bank Account Statement"; BankAccReconciliation: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised before closing a bank account ledger entry during reconciliation posting.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="AppliedAmount">The applied amount.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeCloseBankAccLedgEntry(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var AppliedAmount: Decimal; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before finalizing the posting process for a bank account reconciliation.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record being finalized.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeFinalizePost(var BankAccReconciliation: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised before deleting related records during bank account reconciliation posting.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeDeleteRelatedRecords(BankAccReconciliation: Record "Bank Acc. Reconciliation"; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before posting payment applications for a bank account reconciliation line.
/// </summary>
/// <param name="BankAccReconLine">The bank account reconciliation line record.</param>
/// <param name="AppliedAmount">The applied amount.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforePostPaymentApplications(BankAccReconLine: Record "Bank Acc. Reconciliation Line"; var AppliedAmount: Decimal; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised after finalizing the posting process for a bank account reconciliation.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record that was finalized.</param>
[IntegrationEvent(false, false)]
local procedure OnAfterFinalizePost(var BankAccReconciliation: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised before initializing the posting process for a bank account reconciliation.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record being initialized.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeInitPost(var BankAccReconciliation: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised before posting a bank account reconciliation.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record being posted.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforePost(var BankAccReconciliation: Record "Bank Acc. Reconciliation"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before inserting a posted payment reconciliation header record.
/// </summary>
/// <param name="PostedPaymentReconHdr">The posted payment reconciliation header record being inserted.</param>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforePostedPmtReconInsert(var PostedPaymentReconHdr: Record "Posted Payment Recon. Hdr"; BankAccReconciliation: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised before transferring to posted payment application during reconciliation posting.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeTransferToPostPmtAppln(var BankAccReconciliation: Record "Bank Acc. Reconciliation"; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before transferring to bank statement during reconciliation posting.
/// </summary>
/// <param name="BankAccRecon">The bank account reconciliation record.</param>
/// <param name="PrePostingOutstdPayments">The pre-posting outstanding payments amount.</param>
/// <param name="PrePostingOutstdBankTransactions">The pre-posting outstanding bank transactions amount.</param>
/// <param name="PrePostingGLBalance">The pre-posting general ledger balance.</param>
/// <param name="PrePostingTotalPositiveDifference">The pre-posting total positive difference.</param>
/// <param name="PrePostingTotalNegativeDifference">The pre-posting total negative difference.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeTransferToBankStmt(var BankAccRecon: Record "Bank Acc. Reconciliation"; PrePostingOutstdPayments: Decimal; PrePostingOutstdBankTransactions: Decimal; PrePostingGLBalance: Decimal; PrePostingTotalPositiveDifference: Decimal; PrePostingTotalNegativeDifference: Decimal; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised after calculating sums when checking if lines match ending balance.
/// </summary>
/// <param name="BankAccReconLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnCheckLinesMatchEndingBalanceOnAfterCalcSums(var BankAccReconLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before modifying bank account ledger entry when closing bank account ledger entry.
/// </summary>
/// <param name="BankAccountLedgerEntry">The bank account ledger entry record being modified.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnCloseBankAccLedgEntryOnBeforeBankAccLedgEntryModify(var BankAccountLedgerEntry: Record "Bank Account Ledger Entry"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before modifying check ledger entry when closing bank account ledger entry.
/// </summary>
/// <param name="CheckLedgerEntry">The check ledger entry record being modified.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnCloseBankAccLedgEntryOnBeforeCheckLedgEntryModify(var CheckLedgerEntry: Record "Check Ledger Entry"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised after setting filters on bank account ledger entry when closing bank account ledger entry.
/// </summary>
/// <param name="BankAccountLedgerEntry">The bank account ledger entry record with filters applied.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnCloseBankAccLedgEntryOnAfterBankAccLedgEntrySetFilters(var BankAccountLedgerEntry: Record "Bank Account Ledger Entry"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before handling payment application transfer during reconciliation posting.
/// </summary>
/// <param name="BankAccRecon">The bank account reconciliation record.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeHandlePaymentApplicationTransfer(var BankAccRecon: Record "Bank Acc. Reconciliation"; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised after initializing the general journal line when posting payment applications.
/// </summary>
/// <param name="GenJournalLine">The general journal line record being initialized.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="IsApplied">Indicates whether the payment has been applied.</param>
/// <param name="AppliedAmount">The total applied amount.</param>
/// <param name="PaymentLineAmount">The payment line amount.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnPostPaymentApplicationsOnAfterInitGenJnlLine(var GenJournalLine: Record "Gen. Journal Line"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var IsApplied: Boolean; var AppliedAmount: Decimal; var PaymentLineAmount: Decimal; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised after posting a general journal line during payment application posting.
/// </summary>
/// <param name="GenJournalLine">The general journal line record that was posted.</param>
/// <param name="GenJnlPostLine">The general journal posting line codeunit instance.</param>
[IntegrationEvent(false, false)]
local procedure OnPostPaymentApplicationsOnAfterPostGenJnlLine(var GenJournalLine: Record "Gen. Journal Line"; var GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line")
begin
end;
/// <summary>
/// Event raised after setting filters on bank account ledger entry during payment application posting.
/// </summary>
/// <param name="BankAccountLedgerEntry">The bank account ledger entry record with filters applied.</param>
/// <param name="GenJournalLine">The general journal line record.</param>
[IntegrationEvent(false, false)]
local procedure OnPostPaymentApplicationsOnAfterBankAccountLedgerEntrySetFilters(var BankAccountLedgerEntry: Record "Bank Account Ledger Entry"; GenJournalLine: Record "Gen. Journal Line")
begin
end;
/// <summary>
/// Event raised after checking that transaction is posted and reconciled during payment application posting.
/// </summary>
/// <param name="BankAccReconLine">The bank account reconciliation line record.</param>
/// <param name="AppliedAmount">The applied amount.</param>
/// <param name="SourceCode">The source code for the posting.</param>
[IntegrationEvent(false, false)]
local procedure OnPostPaymentApplicationsOnAfterTransactionPostedAndReconciledCheck(BankAccReconLine: Record "Bank Acc. Reconciliation Line"; var AppliedAmount: Decimal; SourceCode: Code[10])
begin
end;
/// <summary>
/// Event raised before validating apply requirements during payment application posting.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="GenJournalLine">The general journal line record.</param>
/// <param name="AppliedAmount">The applied amount.</param>
[IntegrationEvent(false, false)]
local procedure OnPostPaymentApplicationsOnBeforeValidateApplyRequirements(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var GenJournalLine: Record "Gen. Journal Line"; AppliedAmount: Decimal)
begin
end;
/// <summary>
/// Event raised for account type case handling not covered by standard customer, vendor, or employee types.
/// </summary>
/// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
/// <param name="GenJournalLine">The general journal line record.</param>
[IntegrationEvent(false, false)]
local procedure OnPostPaymentApplicationsOnAccountTypeCaseElse(var AppliedPaymentEntry: Record "Applied Payment Entry"; var GenJournalLine: Record "Gen. Journal Line")
begin
end;
/// <summary>
/// Event raised after filtering bank account reconciliation lines during posting.
/// </summary>
/// <param name="BankAccReconLines">The bank account reconciliation lines record with filters applied.</param>
/// <param name="BankAccRecon">The bank account reconciliation record.</param>
[IntegrationEvent(false, false)]
local procedure OnPostAfterFilterBankAccRecLines(var BankAccReconLines: Record "Bank Acc. Reconciliation Line"; BankAccRecon: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised before inserting bank account statement line when transferring to bank statement.
/// </summary>
/// <param name="BankAccStmtLine">The bank account statement line record being inserted.</param>
/// <param name="BankAccReconLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnTransferToBankStmtOnBeforeBankAccStmtLineInsert(var BankAccStmtLine: Record "Bank Account Statement Line"; BankAccReconLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before updating the bank account during reconciliation posting.
/// </summary>
/// <param name="BankAccRecon">The bank account reconciliation record.</param>
/// <param name="BankAccount">The bank account record being updated.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
/// <param name="Amt">The amount for the update.</param>
/// <param name="PostedStamentNo">The posted statement number.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeUpdateBank(var BankAccRecon: Record "Bank Acc. Reconciliation"; var BankAccount: Record "Bank Account"; var IsHandled: Boolean; Amt: Decimal; PostedStamentNo: Code[20])
begin
end;
/// <summary>
/// Event raised before inserting posted payment reconciliation line when transferring to posted payment application.
/// </summary>
/// <param name="PostedPmtReconLine">The posted payment reconciliation line record being inserted.</param>
/// <param name="BankAccReconLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnTransferToPostPmtApplnOnBeforePostedPmtReconLineInsert(var PostedPmtReconLine: Record "Posted Payment Recon. Line"; BankAccReconLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before applying a customer ledger entry during bank reconciliation posting.
/// </summary>
/// <param name="CustLedgerEntry">The customer ledger entry record being applied.</param>
/// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
/// <param name="BankAccount">The bank account record.</param>
/// <param name="AppliesToID">The applies-to identifier for the application.</param>
/// <param name="PostingDate">The posting date for the application.</param>
/// <param name="PmtDiscDueDate">The payment discount due date.</param>
/// <param name="PmtDiscToleranceDate">The payment discount tolerance date.</param>
/// <param name="RemPmtDiscPossible">The remaining payment discount possible amount.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeApplyCustLedgEntry(var CustLedgerEntry: Record "Cust. Ledger Entry"; AppliedPaymentEntry: Record "Applied Payment Entry"; var BankAccount: Record "Bank Account"; AppliesToID: Code[50]; PostingDate: Date; PmtDiscDueDate: Date; PmtDiscToleranceDate: Date; RemPmtDiscPossible: Decimal; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before applying a vendor ledger entry during bank reconciliation posting.
/// </summary>
/// <param name="VendLedgerEntry">The vendor ledger entry record being applied.</param>
/// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
/// <param name="BankAccount">The bank account record.</param>
/// <param name="AppliesToID">The applies-to identifier for the application.</param>
/// <param name="PostingDate">The posting date for the application.</param>
/// <param name="PmtDiscDueDate">The payment discount due date.</param>
/// <param name="PmtDiscToleranceDate">The payment discount tolerance date.</param>
/// <param name="RemPmtDiscPossible">The remaining payment discount possible amount.</param>
/// <param name="ApplyVendLedgEntryHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeApplyVendLedgEntry(var VendLedgerEntry: Record "Vendor Ledger Entry"; AppliedPaymentEntry: Record "Applied Payment Entry"; var BankAccount: Record "Bank Account"; AppliesToID: Code[50]; PostingDate: Date; PmtDiscDueDate: Date; PmtDiscToleranceDate: Date; RemPmtDiscPossible: Decimal; var ApplyVendLedgEntryHandled: Boolean)
begin
end;
}