Report 1408 Bank Acc. Recon. - Test, source in 29
Source29
src/Layers/W1/BaseApp/Bank/Reports/BankAccReconTest.Report.al916 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.Reports;
using Microsoft.Bank.BankAccount;
using Microsoft.Bank.Check;
using Microsoft.Bank.Ledger;
using Microsoft.Bank.Reconciliation;
using Microsoft.Finance.Dimension;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Ledger;
using Microsoft.Purchases.Payables;
using Microsoft.Sales.Receivables;
using System.Utilities;
/// <summary>
/// Generates test report for bank account reconciliation validation before posting.
/// Validates reconciliation balances, identifies errors, and provides posting verification.
/// </summary>
/// <remarks>
/// Data Sources: Bank Acc. Reconciliation, Bank Acc. Reconciliation Line, Bank Account Ledger Entry.
/// Features: Balance validation, error detection, outstanding transaction analysis, posting readiness check.
/// Usage: Pre-posting validation, reconciliation testing, error identification, audit preparation.
/// </remarks>
report 1408 "Bank Acc. Recon. - Test"
{
Caption = 'Bank Acc. Recon. - Test';
EnableHyperlinks = true;
DefaultRenderingLayout = RDLCLayout;
dataset
{
dataitem("Bank Acc. Reconciliation"; "Bank Acc. Reconciliation")
{
DataItemTableView = sorting("Bank Account No.", "Statement No.");
RequestFilterFields = "Bank Account No.", "Statement No.";
column(Bank_Acc__Reconciliation_Bank_Account_No_; "Bank Account No.")
{
}
column(Bank_Acc__Reconciliation_Statement_No_; "Statement No.")
{
}
dataitem(PageCounter; "Integer")
{
DataItemTableView = sorting(Number) where(Number = const(1));
column(FORMAT_TODAY_0_4_; Format(Today, 0, 4))
{
}
column(COMPANYNAME; COMPANYPROPERTY.DisplayName())
{
}
column(Bank_Acc__Reconciliation__TABLECAPTION___________BankAccReconFilter; "Bank Acc. Reconciliation".TableCaption + ': ' + BankAccReconFilter)
{
}
column(BankAccReconFilter; BankAccReconFilter)
{
}
column(Bank_Acc__Reconciliation___Bank_Account_No____________Bank_Acc__Reconciliation___Statement_No__; "Bank Acc. Reconciliation"."Bank Account No." + ' ' + "Bank Acc. Reconciliation"."Statement No.")
{
}
column(Bank_Acc__Reconciliation___Balance_Last_Statement_; "Bank Acc. Reconciliation"."Balance Last Statement")
{
}
column(Bank_Acc__Reconciliation___Statement_Date_; Format("Bank Acc. Reconciliation"."Statement Date"))
{
}
column(Bank_Acc__Reconciliation___Statement_Ending_Balance_; CalculatedStatementEndingBalance)
{
}
column(Bank_Acc__Reconciliation___TotalBalOnBankAccount; BankAcc."Balance at Date")
{
}
column(Bank_Acc__Reconciliation___TotalBalOnGLAccount; TotalBalOnGLAccount)
{
}
column(Bank_Acc__Reconciliation___TotalBalOnGLAccountLCY; TotalBalOnGLAccountLCY)
{
}
column(Bank_Acc__Reconciliation___TotalOutstdBankTransactions; TotalOutstdBankTransac)
{
}
column(Bank_Acc__Reconciliation___TotalOutstdPayments; TotalOutstdPayments)
{
}
column(ErrorLabel; ErrorLabel)
{
}
column(HeaderError1; HeaderError1)
{
}
column(HeaderError2; HeaderError2)
{
}
column(HeaderError3; HeaderError3)
{
}
column(DirectPostingURLLink; DirectPostingURLLinkLbl)
{
}
column(Bank_Account_Statement___TestCaption; Bank_Account_Statement___TestCaptionLbl)
{
}
column(CurrReport_PAGENOCaption; CurrReport_PAGENOCaptionLbl)
{
}
column(Bank_Acc__Reconciliation___Balance_Last_Statement_Caption; Bank_Acc__Reconciliation___Balance_Last_Statement_CaptionLbl)
{
}
column(Bank_Acc__Reconciliation___Statement_Date_Caption; Bank_Acc__Reconciliation___Statement_Date_CaptionLbl)
{
}
column(Bank_Acc__Reconciliation___Statement_Ending_Balance_Caption; StatementEndingBalanceLbl)
{
}
column(G_L_BalanceCaption; G_L_BalanceCaptionTxt)
{
}
column(G_L_Balance_LCYCaption; G_L_BalanceLCYCaptionTxt)
{
}
column(Bank_Acc_BalanceCaption; Bank_Acc_BalanceCaptionTxt)
{
}
column(Ending_G_L_BalanceCaption; Ending_G_L_BalanceCaptionLbl)
{
}
column(Subtotal_Caption; Subtotal_CaptionLbl)
{
}
column(Difference_Caption; Difference_CaptionLbl)
{
}
column(Ending_BalanceCaption; Ending_BalanceCaptionTxt)
{
}
column(Outstanding_BankTransactionsCaption; Outstanding_BankTransactionsCaptionLbl)
{
}
column(Total_Outstanding_BankTransactionsCaption; Total_Outstanding_BankTransactionsCaptionLbl)
{
}
column(Outstanding_PaymentsCaption; Outstanding_PaymentsCaptionLbl)
{
}
column(Total_Outstanding_PaymentsCaption; Total_Outstanding_PaymentsCaptionLbl)
{
}
column(Print_Outstanding_Transactions; PrintOutstandingTransactions)
{
}
column(Currency_CodeCaption; CurrencyCodeCaption)
{
}
column(Currency_Code; CurrencyCode)
{
}
column(Statement_BalanceCaption; Statement_BalanceCaptionLbl)
{
}
column(GL_Subtotal; (BankAcc."Balance at Date"))
{
}
column(Ending_GL_Balance; EndingGLBalance)
{
}
column(Statement_Subtotal; (CalculatedStatementEndingBalance + TotalOutstdBankTransac))
{
}
column(Adjusted_Statement_Ending_Balance; EndingStatementBalance)
{
}
column(Sum_Of_Differences; SumOfDifferences)
{
}
dataitem(HeaderErrorCounter; "Integer")
{
DataItemTableView = sorting(Number);
column(ErrorText_Number_; ErrorText[Number])
{
}
column(ErrorText_Number_Caption; ErrorText_Number_CaptionLbl)
{
}
trigger OnPostDataItem()
begin
ErrorCounter := 0;
end;
trigger OnPreDataItem()
begin
SetRange(Number, 1, ErrorCounter);
end;
}
dataitem("Bank Acc. Reconciliation Line"; "Bank Acc. Reconciliation Line")
{
DataItemLink = "Statement Type" = field("Statement Type"), "Bank Account No." = field("Bank Account No."), "Statement No." = field("Statement No.");
DataItemLinkReference = "Bank Acc. Reconciliation";
DataItemTableView = sorting("Bank Account No.", "Statement No.", "Statement Line No.");
column(Bank_Acc__Reconciliation_Line__Transaction_Date_; Format("Transaction Date"))
{
}
column(Bank_Acc__Reconciliation_Line__Document_No__; "Document No.")
{
}
column(Bank_Acc__Reconciliation_Line_Description; Description)
{
}
column(Bank_Acc__Reconciliation_Line__Value_Date_; Format("Value Date"))
{
}
column(Bank_Acc__Reconciliation_Line__Applied_Entries_; "Applied Entries")
{
}
column(Bank_Acc__Reconciliation_Line__Applied_Amount_; "Applied Amount")
{
}
column(Bank_Acc__Reconciliation_Line__Statement_Amount_; "Statement Amount")
{
}
column(Bank_Acc__Reconciliation_Line_Type; '')
{
}
column(Bank_Acc__Reconciliation_Line_Statement_No; "Statement No.")
{
}
column(Bank_Acc__Reconciliation_Line_Statement_Line_No; "Statement Line No.")
{
}
column(Bank_Acc__Reconciliation_Line_Difference; Difference)
{
}
column(Bank_Acc__Reconciliation_Line__Applied_Amount__Control25; "Applied Amount")
{
}
column(Bank_Acc__Reconciliation_Line_Difference_Control29; Difference)
{
}
column(Bank_Acc__Reconciliation_Line__Transaction_Date_Caption; Bank_Acc__Reconciliation_Line__Transaction_Date_CaptionLbl)
{
}
column(Bank_Acc__Reconciliation_Line__Document_No__Caption; FieldCaption("Document No."))
{
}
column(Bank_Acc__Reconciliation_Line_DescriptionCaption; FieldCaption(Description))
{
}
column(Bank_Acc__Reconciliation_Line__Value_Date_Caption; Bank_Acc__Reconciliation_Line__Value_Date_CaptionLbl)
{
}
column(Bank_Acc__Reconciliation_Line__Applied_Entries_Caption; FieldCaption("Applied Entries"))
{
}
column(Bank_Acc__Reconciliation_Line__Statement_Amount_Caption; FieldCaption("Statement Amount"))
{
}
column(Bank_Acc__Reconciliation_Line__Applied_Amount_Caption; FieldCaption("Applied Amount"))
{
}
column(Bank_Acc__Reconciliation_Line_TypeCaption; '')
{
}
column(Bank_Acc__Reconciliation_Line_DifferenceCaption; FieldCaption(Difference))
{
}
column(TotalsCaption; TotalsCaptionLbl)
{
}
dataitem(LineErrorCounter; "Integer")
{
DataItemTableView = sorting(Number);
column(ErrorText_Number__Control97; ErrorText[Number])
{
}
column(ErrorText_Number__Control97Caption; ErrorText_Number__Control97CaptionLbl)
{
}
trigger OnPostDataItem()
begin
ErrorCounter := 0;
end;
trigger OnPreDataItem()
begin
SetRange(Number, 1, ErrorCounter);
end;
}
trigger OnAfterGetRecord()
var
BankAccRecMatchBuffer: Record "Bank Acc. Rec. Match Buffer";
TableID: array[10] of Integer;
No: array[10] of Code[20];
IsManyToOne: Boolean;
begin
AppliedAmount := 0;
ErrorLabel := 1;
if "Bank Acc. Reconciliation"."Statement Type" =
"Bank Acc. Reconciliation"."Statement Type"::"Bank Reconciliation"
then begin
BankAccLedgEntry.SetFilterBankAccNoOpen("Bank Account No.");
BankAccLedgEntry.SetFilter("Statement Status", '=%1 | =%2',
BankAccLedgEntry."Statement Status"::"Bank Acc. Entry Applied",
BankAccLedgEntry."Statement Status"::"Check Entry Applied");
BankAccLedgEntry.SetRange("Statement No.", "Statement No.");
BankAccRecMatchBuffer.SetRange("Bank Account No.", "Bank Account No.");
BankAccRecMatchBuffer.SetRange("Statement No.", "Statement No.");
BankAccRecMatchBuffer.SetRange("Statement Line No.", "Statement Line No.");
if BankAccRecMatchBuffer.FindFirst() then begin
IsManyToOne := true;
BankAccLedgEntry.SetRange("Entry No.", BankAccRecMatchBuffer."Ledger Entry No.");
end else
BankAccLedgEntry.SetRange("Statement Line No.", "Statement Line No.");
OnBankAccReconciliationLineAfterGetRecordOnAfterBankAccLedgEntrySetFilters("Bank Acc. Reconciliation Line", BankAccLedgEntry);
if not BankAccLedgEntry.IsEmpty() then begin
BankAccLedgEntry.FindSet();
repeat
AppliedAmount := AppliedAmount + BankAccLedgEntry.Amount;
VerifyCheckLedgerEntry(BankAccLedgEntry);
until BankAccLedgEntry.Next() = 0;
end;
end else
AppliedAmount := GetPaymentReconciliationAppliedAmount("Bank Acc. Reconciliation Line");
OnBeforeCheckAppliedAmount("Bank Acc. Reconciliation Line", AppliedAmount);
if ("Applied Amount" <> AppliedAmount) and (not IsManyToOne) then
AddError(StrSubstNo(AmountWrongErr, FieldCaption("Applied Amount"), AppliedAmount));
if not DimensionManagement.CheckDimIDComb("Dimension Set ID") then
AddError(DimensionManagement.GetDimCombErr());
TableID[1] := DimensionManagement.TypeToTableID1("Account Type".AsInteger());
No[1] := "Account No.";
if not DimensionManagement.CheckDimValuePosting(TableID, No, "Dimension Set ID") then
AddError(DimensionManagement.GetDimValuePostingErr());
TotalAmount := TotalAmount + "Statement Amount";
if (not BankAccRecMatchBuffer."Is Processed") then
TotalAppliedAmount := TotalAppliedAmount + AppliedAmount;
MarkManyToOneMatchAsProcessed(BankAccRecMatchBuffer."Bank Account No.",
BankAccRecMatchBuffer."Statement No.",
BankAccRecMatchBuffer."Match ID");
end;
trigger OnPostDataItem()
var
BankAccRecMatchBuffer: Record "Bank Acc. Rec. Match Buffer";
begin
if TotalAmount <> TotalAppliedAmount then begin
AddError(ApplicationErr);
FooterError1 := ApplicationErr;
end;
if BankAccReconLine.Difference <> 0 then begin
AddError(
StrSubstNo(
TotalDifferenceErr, BankAccReconLine.Difference, 0));
FooterError2 := StrSubstNo(TotalDifferenceErr, BankAccReconLine.Difference, 0);
end;
BankAccRecMatchBuffer.SetRange("Bank Account No.", "Bank Account No.");
BankAccRecMatchBuffer.SetRange("Statement No.", "Statement No.");
if BankAccRecMatchBuffer.FindSet() then
repeat
BankAccRecMatchBuffer."Is Processed" := false;
BankAccRecMatchBuffer.Modify();
until BankAccRecMatchBuffer.Next() = 0;
end;
trigger OnPreDataItem()
begin
TotalAmount := 0;
TotalAppliedAmount := 0;
ErrorLabel := 1;
end;
}
dataitem(FooterErrorCounter; "Integer")
{
DataItemTableView = sorting(Number);
column(FooterError1; FooterError1)
{
}
column(FooterError2; FooterError2)
{
}
trigger OnPostDataItem()
begin
ErrorCounter := 0;
end;
trigger OnPreDataItem()
begin
SetRange(Number, 1, ErrorCounter);
end;
}
dataitem(OutstandingBankTransaction; "Outstanding Bank Transaction")
{
UseTemporary = true;
column(Outstd_Bank_Transac_Amount; Amount)
{
}
column(Outstd_Bank_Transac_EntryNo_; "Entry No.")
{
}
column(Outstd_Bank_Transac_Posting_Date; Format("Posting Date"))
{
}
column(Outstd_Bank_Transac_Doc_Type; "Document Type")
{
}
column(Outstd_Bank_Transac_Doc_No_; "Document No.")
{
}
column(Outstd_Bank_Transac_Bank_Acc_No_; "Bank Account No.")
{
}
column(Outstd_Bank_Transac_Description; Description)
{
}
column(Outstd_Bank_Transac_Type; Type)
{
}
column(Outstd_Bank_Transac_Applied; Applied)
{
}
}
dataitem(OutstandingPayment; "Outstanding Bank Transaction")
{
UseTemporary = true;
column(Outstd_Payment_Amount; Amount)
{
}
column(Outstd_Payment_Entry_No_; "Entry No.")
{
}
column(Outstd_Payment_Posting_Date; Format("Posting Date"))
{
}
column(Outstd_Payment_Doc_Type; "Document Type")
{
}
column(Outstd_Payment_Doc_No_; "Document No.")
{
}
column(Outstd_Payment_Bank_Acc_No_; "Bank Account No.")
{
}
column(Outstd_Payment_Description; Description)
{
}
column(Outstd_Payment_Type; Type)
{
}
column(Outstd_Payment_Applied; Applied)
{
}
}
}
trigger OnAfterGetRecord()
var
BankAccPostingGroup: Record "Bank Account Posting Group";
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
begin
SetupRecord();
StatementEndingBalanceLbl := Bank_Acc__Reconciliation___Statement_Ending_Balance_CaptionLbl;
if "Bank Acc. Reconciliation"."Statement Type" = "Bank Acc. Reconciliation"."Statement Type"::"Payment Application" then
StatementEndingBalanceLbl += CalculatedFieldCaptionLbl;
ErrorLabel := 1;
BankAccReconLine.FilterBankRecLines("Bank Acc. Reconciliation");
BankAccReconLine.CalcSums("Statement Amount", Difference);
if "Statement Type" = "Statement Type"::"Bank Reconciliation" then
if BankAccReconLine."Statement Amount" <> "Statement Ending Balance" - "Balance Last Statement" then begin
AddError(
StrSubstNo(
StatementBalanceErr, FieldCaption("Statement Ending Balance")));
HeaderError1 := StrSubstNo(StatementBalanceErr, FieldCaption("Statement Ending Balance"));
end;
if BankAcc.Get("Bank Account No.") then begin
CurrencyCode := BankAcc."Currency Code";
CurrencyCodeCaption := BankAcc.FieldCaption("Currency Code");
if BankAcc.Blocked then begin
AddError(
StrSubstNo(
TableValueWrongErr,
BankAcc.FieldCaption(Blocked), false, BankAcc.TableCaption(), "Bank Account No."));
HeaderError2 := StrSubstNo(TableValueWrongErr, BankAcc.FieldCaption(Blocked), false, BankAcc.TableCaption(), "Bank Account No.");
end;
end else begin
AddError(
StrSubstNo(
TableValueMissingErr,
BankAcc.TableCaption(), "Bank Account No."));
HeaderError2 := StrSubstNo(TableValueMissingErr, BankAcc.TableCaption(), "Bank Account No.");
end;
if "Statement Type" = "Statement Type"::"Payment Application" then
// Bank Reconciliations done from the Payment Reconciliation Journal page, have no Statement Ending Date defined (unless set elsewhere by code e.g. tests)
// We take it as the last date of the entries
if "Statement Date" = 0D then begin
BankAccReconciliationLine.Reset();
BankAccReconciliationLine.SetRange("Statement No.", "Statement No.");
BankAccReconciliationLine.SetCurrentKey("Transaction Date");
BankAccReconciliationLine.SetAscending("Transaction Date", false);
if BankAccReconciliationLine.FindFirst() then
"Statement Date" := BankAccReconciliationLine."Transaction Date";
end;
if "Statement Date" = 0D then
AddError(StrSubstNo(StatementDateErr, FieldCaption("Statement Date")));
if BankAccPostingGroup.Get(BankAcc."Bank Acc. Posting Group") then begin
G_L_BalanceLCYCaptionTxt := StrSubstNo(G_L_BalanceLCYCaptionLbl, BankAccPostingGroup."G/L Account No.");
G_L_BalanceCaptionTxt := StrSubstNo(G_L_BalanceCaptionLbl, BankAccPostingGroup."G/L Account No.", BankAcc."Currency Code");
Bank_Acc_BalanceCaptionTxt := Bank_Acc_BalanceCaptionLbl;
Ending_BalanceCaptionTxt := BankAccountBalanceLbl;
if "Statement Date" <> 0D then begin
BankAcc.SetFilter("Date Filter", '..%1', "Statement Date");
G_L_BalanceCaptionTxt := G_L_BalanceCaptionTxt + AtLbl + format("Statement Date");
G_L_BalanceLCYCaptionTxt := G_L_BalanceLCYCaptionTxt + AtLbl + format("Statement Date");
Bank_Acc_BalanceCaptionTxt := Bank_Acc_BalanceCaptionTxt + AtLbl + format("Statement Date");
end;
TotalBalOnGLAccountLCY := BankAccReconTest.GetGLAccountBalanceLCY(BankAcc, BankAccPostingGroup, "Statement Date");
TotalBalOnGLAccount := BankAccReconTest.GetGLAccountBalance(TotalBalOnGLAccountLCY, "Statement Date", BankAcc."Currency Code");
CheckForDirectEntries(BankAcc, BankAccPostingGroup, "Statement Date");
end;
BankAcc.CalcFields("Balance at Date", "Balance at Date (LCY)");
if PrintOutstandingTransactions then begin
OutstandingBankTransaction.DeleteAll();
OutstandingPayment.DeleteAll();
CreateOutstandingBankTransactions();
end;
TotalOutstdBankTransac := BankAccReconTest.TotalOutstandingBankTransactions("Bank Acc. Reconciliation");
TotalOutstdPayments := BankAccReconTest.TotalOutstandingPayments("Bank Acc. Reconciliation");
EndingGLBalance := BankAcc."Balance at Date";
// Notice that BankAccReconLine had the sum of fields computed with CalcSums
if "Bank Acc. Reconciliation"."Statement Type" = "Bank Acc. Reconciliation"."Statement Type"::"Bank Reconciliation" then
CalculatedStatementEndingBalance := "Bank Acc. Reconciliation"."Statement Ending Balance"
else
CalculatedStatementEndingBalance := "Bank Acc. Reconciliation"."Balance Last Statement" + BankAccReconLine."Statement Amount";
SumOfDifferences := BankAccReconLine.Difference;
EndingStatementBalance := CalculatedStatementEndingBalance + TotalOutstdBankTransac + TotalOutstdPayments;
end;
}
}
requestpage
{
SaveValues = true;
layout
{
area(content)
{
group(Control2)
{
ShowCaption = false;
field(PrintOutstdTransac; PrintOutstandingTransactions)
{
ApplicationArea = Basic, Suite;
Caption = 'Print Outstanding Transactions';
ToolTip = 'Specifies if the report includes reconciliation lines for outstanding transactions.';
Visible = ShouldShowOutstandingBankTransactions;
}
}
}
}
actions
{
}
}
rendering
{
layout(RDLCLayout)
{
Type = RDLC;
LayoutFile = './Bank/Reports/BankAccReconTest.rdlc';
Summary = 'Report layout made in the legacy RDLC format. Use an RDLC editor to modify the layout.';
}
}
labels
{
Warning = 'Warning!';
}
trigger OnPreReport()
begin
BankAccReconFilter := "Bank Acc. Reconciliation".GetFilters();
end;
trigger OnInitReport()
begin
OnBeforeInitReport(ShouldShowOutstandingBankTransactions);
end;
var
BankAcc: Record "Bank Account";
BankAccLedgEntry: Record "Bank Account Ledger Entry";
CheckLedgEntry: Record "Check Ledger Entry";
BankAccReconLine: Record "Bank Acc. Reconciliation Line";
DimensionManagement: Codeunit DimensionManagement;
BankAccReconTest: Codeunit "Bank Acc. Recon. Test";
AppliedAmount: Decimal;
TotalAmount: Decimal;
TotalAppliedAmount: Decimal;
TotalOutstdBankTransac: Decimal;
TotalOutstdPayments: Decimal;
EndingGLBalance: Decimal;
EndingStatementBalance: Decimal;
CalculatedStatementEndingBalance: Decimal;
TotalBalOnGLAccount: Decimal;
TotalBalOnGLAccountLCY: Decimal;
SumOfDifferences: Decimal;
BankAccReconFilter: Text;
G_L_BalanceLCYCaptionTxt: Text;
G_L_BalanceCaptionTxt: Text;
Bank_Acc_BalanceCaptionTxt: Text;
Ending_BalanceCaptionTxt: Text;
ErrorCounter: Integer;
ErrorText: array[99] of Text[250];
ErrorLabel: Integer;
HeaderError1: Text[250];
HeaderError2: Text[250];
HeaderError3: Text[250];
FooterError1: Text[250];
FooterError2: Text[250];
CurrencyCodeCaption: Text[250];
StatementEndingBalanceLbl: Text;
CurrencyCode: Code[20];
PrintOutstandingTransactions: Boolean;
ShouldShowOutstandingBankTransactions: Boolean;
StatementDateErr: Label '%1 must be specified.', Comment = '%1=Statement Date field caption';
StatementBalanceErr: Label '%1 is not equal to Total Balance.', Comment = '%1=Statement Ending Balance field caption';
TableValueWrongErr: Label '%1 must be %2 for %3 %4.', Comment = '%1=field caption;%2=field value;%3=table name caption;%4=field value';
TableValueEmptyErr: Label '%1 must not be empty for %2 %3.', Comment = '%1=field caption;%2=table name caption;%3=field value';
TableValueMissingErr: Label '%1 %2 does not exist.', Comment = '%1=table name caption;%2=table field name caption';
AmountWrongErr: Label '%1 must be %2.', Comment = '%1=field name caption;%2=field value';
ApplicationErr: Label 'Application is wrong.';
TotalDifferenceErr: Label 'The total difference is %1. It must be %2.', Comment = '%1=field value;%2=field value';
Bank_Account_Statement___TestCaptionLbl: Label 'Bank Account Statement';
CurrReport_PAGENOCaptionLbl: Label 'Page';
Bank_Acc__Reconciliation___Balance_Last_Statement_CaptionLbl: Label 'Balance Last Statement';
Bank_Acc__Reconciliation___Statement_Date_CaptionLbl: Label 'Statement Date';
Bank_Acc__Reconciliation___Statement_Ending_Balance_CaptionLbl: Label 'Statement Ending Balance';
ErrorText_Number_CaptionLbl: Label 'Warning!';
CalculatedFieldCaptionLbl: Label ' (Calculated)';
Bank_Acc__Reconciliation_Line__Transaction_Date_CaptionLbl: Label 'Transaction Date';
Bank_Acc__Reconciliation_Line__Value_Date_CaptionLbl: Label 'Value Date';
TotalsCaptionLbl: Label 'Totals';
ErrorText_Number__Control97CaptionLbl: Label 'Warning!';
Outstanding_BankTransactionsCaptionLbl: Label 'Outstanding Bank Transactions';
Outstanding_PaymentsCaptionLbl: Label 'Outstanding Checks';
Total_Outstanding_BankTransactionsCaptionLbl: Label 'Total Outstanding Bank Transactions';
Total_Outstanding_PaymentsCaptionLbl: Label 'Total Outstanding Payments';
G_L_BalanceCaptionLbl: Label 'G/L Account No. %1 Balance (%2) - Calculated', Comment = '%1= Account number; %2= Currency symbol';
G_L_BalanceLCYCaptionLbl: Label 'G/L Account No. %1 Balance', Comment = '%1= Account number';
Bank_Acc_BalanceCaptionLbl: Label 'Bank Account Balance';
Ending_G_L_BalanceCaptionLbl: Label 'Ending G/L Balance';
Subtotal_CaptionLbl: Label 'Subtotal';
Difference_CaptionLbl: Label 'Sum of Differences';
BankAccountBalanceLbl: Label 'Bank Account Balance';
AtLbl: Label ' at ', Comment = 'used to build the construct a string like balance at 31-12-2020';
Statement_BalanceCaptionLbl: Label 'Statement Balance';
PotentialUnstabilityDueToDirectPostingEntriesLbl: Label 'Bank reconciliation might not be possible because there are direct posting entries. For more information, see %1.', Comment = '%1= URL link';
DirectPostingURLLinkLbl: Label 'https://go.microsoft.com/fwlink/?linkid=2197950';
local procedure AddError(Text: Text[250])
begin
ErrorCounter := ErrorCounter + 1;
ErrorText[ErrorCounter] := Text;
end;
local procedure MarkManyToOneMatchAsProcessed(BankAccNo: Code[20]; StatementNo: Code[20]; MatchID: Integer)
var
BankAccRecMatchBuffer: Record "Bank Acc. Rec. Match Buffer";
begin
BankAccRecMatchBuffer.SetRange("Bank Account No.", BankAccNo);
BankAccRecMatchBuffer.SetRange("Statement No.", StatementNo);
BankAccRecMatchBuffer.SetRange("Match ID", MatchID);
if BankAccRecMatchBuffer.FindSet() then
repeat
BankAccRecMatchBuffer."Is Processed" := true;
BankAccRecMatchBuffer.Modify();
until BankAccRecMatchBuffer.Next() = 0;
end;
local procedure CreateOutstandingBankTransactions()
var
BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
LedgEntryRemainingAmount: Decimal;
RemainingAmt: Decimal;
begin
BankAccReconTest.SetOutstandingFilters("Bank Acc. Reconciliation", BankAccountLedgerEntry);
if BankAccountLedgerEntry.IsEmpty() then
exit;
BankAccountLedgerEntry.SetAutoCalcFields("Check Ledger Entries");
if BankAccountLedgerEntry.FindSet() then
repeat
if BankAccReconTest.CheckBankAccountLedgerEntryFilters(BankAccountLedgerEntry, "Bank Acc. Reconciliation"."Statement No.", "Bank Acc. Reconciliation"."Statement Date") then
if BankAccountLedgerEntry."Check Ledger Entries" <> 0 then begin
RemainingAmt := BankAccountLedgerEntry.Amount -
OutstandingPayment.GetAppliedAmount(BankAccountLedgerEntry."Entry No.");
if RemainingAmt <> 0 then
OutstandingPayment.CopyFromBankAccLedgerEntry(BankAccountLedgerEntry, OutstandingPayment.Type::"Check Ledger Entry",
"Bank Acc. Reconciliation"."Statement Type".AsInteger(), "Bank Acc. Reconciliation"."Statement No.", RemainingAmt)
end else begin
LedgEntryRemainingAmount := OutstandingBankTransaction.GetRemainingAmount(BankAccountLedgerEntry."Entry No.");
if LedgEntryRemainingAmount = 0 then
LedgEntryRemainingAmount := BankAccountLedgerEntry.Amount;
RemainingAmt := LedgEntryRemainingAmount - OutstandingBankTransaction.GetAppliedAmount(BankAccountLedgerEntry."Entry No.");
if RemainingAmt <> 0 then
OutstandingBankTransaction.CopyFromBankAccLedgerEntry(BankAccountLedgerEntry,
OutstandingBankTransaction.Type::"Bank Account Ledger Entry",
"Bank Acc. Reconciliation"."Statement Type".AsInteger(), "Bank Acc. Reconciliation"."Statement No.", RemainingAmt)
end;
until BankAccountLedgerEntry.Next() = 0;
end;
local procedure GetPaymentReconciliationAppliedAmount(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"): Decimal
var
AppliedPaymentEntry: Record "Applied Payment Entry";
BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
CustLedgerEntry: Record "Cust. Ledger Entry";
VendorLedgerEntry: Record "Vendor Ledger Entry";
AppliedAmountTemp: Decimal;
begin
AppliedPaymentEntry.SetRange("Statement Type", BankAccReconciliationLine."Statement Type");
AppliedPaymentEntry.SetRange("Bank Account No.", BankAccReconciliationLine."Bank Account No.");
AppliedPaymentEntry.SetRange("Statement No.", BankAccReconciliationLine."Statement No.");
AppliedPaymentEntry.SetRange("Statement Line No.", BankAccReconciliationLine."Statement Line No.");
if AppliedPaymentEntry.FindSet() then
repeat
if AppliedPaymentEntry."Applies-to Entry No." = 0 then
AppliedAmountTemp += AppliedPaymentEntry."Applied Amount"
else
case AppliedPaymentEntry."Account Type" of
AppliedPaymentEntry."Account Type"::Customer:
if CustLedgerEntry.Get(AppliedPaymentEntry."Applies-to Entry No.") then begin
if not CustLedgerEntry.Open then
AddError(
StrSubstNo(
TableValueWrongErr,
CustLedgerEntry.FieldCaption(Open), true,
CustLedgerEntry.TableCaption(), CustLedgerEntry."Entry No."));
CustLedgerEntry.CalcFields(Amount);
if Abs(AppliedPaymentEntry."Applied Amount") > Abs(CustLedgerEntry.Amount) then
AddError(StrSubstNo(AmountWrongErr, AppliedPaymentEntry.FieldCaption("Applied Amount"), CustLedgerEntry.Amount));
AppliedAmountTemp += AppliedPaymentEntry."Applied Amount";
end else
AddError(
StrSubstNo(
TableValueMissingErr,
CustLedgerEntry.TableCaption(),
AppliedPaymentEntry."Applies-to Entry No."));
AppliedPaymentEntry."Account Type"::Vendor:
if VendorLedgerEntry.Get(AppliedPaymentEntry."Applies-to Entry No.") then begin
if not VendorLedgerEntry.Open then
AddError(
StrSubstNo(
TableValueWrongErr,
VendorLedgerEntry.FieldCaption(Open), true,
VendorLedgerEntry.TableCaption(), VendorLedgerEntry."Entry No."));
VendorLedgerEntry.CalcFields(Amount);
if Abs(AppliedPaymentEntry."Applied Amount") > Abs(VendorLedgerEntry.Amount) then
AddError(StrSubstNo(AmountWrongErr, AppliedPaymentEntry.FieldCaption("Applied Amount"), VendorLedgerEntry.Amount));
AppliedAmountTemp += AppliedPaymentEntry."Applied Amount";
end else
AddError(
StrSubstNo(
TableValueMissingErr,
VendorLedgerEntry.TableCaption(),
AppliedPaymentEntry."Applies-to Entry No."));
AppliedPaymentEntry."Account Type"::"Bank Account":
if BankAccountLedgerEntry.Get(AppliedPaymentEntry."Applies-to Entry No.") then begin
if not BankAccountLedgerEntry.Open then
AddError(
StrSubstNo(
TableValueWrongErr,
BankAccountLedgerEntry.FieldCaption(Open), true,
BankAccountLedgerEntry.TableCaption(), BankAccountLedgerEntry."Entry No."));
AppliedAmountTemp += AppliedPaymentEntry."Applied Amount";
end else
AddError(
StrSubstNo(
TableValueMissingErr,
BankAccountLedgerEntry.TableCaption(),
AppliedPaymentEntry."Applies-to Entry No."));
end;
until AppliedPaymentEntry.Next() = 0;
exit(AppliedAmountTemp);
end;
local procedure VerifyCheckLedgerEntry(BankAccountLedgerEntry: Record "Bank Account Ledger Entry")
begin
CheckLedgEntry.Reset();
CheckLedgEntry.SetCurrentKey("Bank Account Ledger Entry No.");
CheckLedgEntry.SetRange("Bank Account Ledger Entry No.", BankAccountLedgerEntry."Entry No.");
CheckLedgEntry.SetRange(Open, true);
if not CheckLedgEntry.IsEmpty() then begin
CheckLedgEntry.FindSet();
repeat
if not CheckLedgEntry.Open then
AddError(
StrSubstNo(
TableValueWrongErr,
CheckLedgEntry.FieldCaption(Open), true,
CheckLedgEntry.TableCaption(), CheckLedgEntry."Entry No."));
if (CheckLedgEntry."Statement Status" <> CheckLedgEntry."Statement Status"::"Bank Acc. Entry Applied") and
(CheckLedgEntry."Statement Status" <> CheckLedgEntry."Statement Status"::"Check Entry Applied") then
AddError(
StrSubstNo(
TableValueWrongErr,
CheckLedgEntry.FieldCaption("Statement Status"),
CheckLedgEntry."Statement Status"::"Bank Acc. Entry Applied",
CheckLedgEntry.TableCaption(), CheckLedgEntry."Entry No."));
if CheckLedgEntry."Statement No." = '' then
AddError(
StrSubstNo(
TableValueEmptyErr,
CheckLedgEntry.FieldCaption("Statement No."),
CheckLedgEntry.TableCaption(), CheckLedgEntry."Entry No."));
if CheckLedgEntry."Statement Line No." = 0 then
AddError(
StrSubstNo(
TableValueEmptyErr,
CheckLedgEntry.FieldCaption("Statement Line No."),
CheckLedgEntry.TableCaption(), CheckLedgEntry."Entry No."));
until CheckLedgEntry.Next() = 0;
end;
end;
local procedure SetupRecord()
begin
"Bank Acc. Reconciliation".CalcFields(
"Total Balance on Bank Account",
"Bank Account Balance (LCY)",
"Total Positive Adjustments",
"Total Negative Adjustments",
"Total Outstd Bank Transactions",
"Total Outstd Payments",
"Total Applied Amount",
"Total Unposted Applied Amount");
end;
local procedure CheckForDirectEntries(BankAcc: Record "Bank Account"; BankAccPostingGroup: Record "Bank Account Posting Group"; StatementDate: Date)
var
GLAccount: Record "G/L Account";
GLEntries: Record "G/L Entry";
begin
if BankAccPostingGroup."G/L Account No." = '' then
exit;
if not GLAccount.Get(BankAccPostingGroup."G/L Account No.") then
exit;
GLEntries.SetRange("G/L Account No.", BankAccPostingGroup."G/L Account No.");
if (StatementDate <> 0D) then
GLEntries.SetFilter("Posting Date", '<= %1', StatementDate);
GLEntries.SetFilter("Source No.", ''''' |<>%1', BankAcc."No.");
if not GLEntries.IsEmpty() then
HeaderError3 := StrSubstNo(PotentialUnstabilityDueToDirectPostingEntriesLbl, DirectPostingURLLinkLbl);
end;
/// <summary>
/// Integration event raised after filtering bank account ledger entries for reconciliation line validation.
/// Enables custom filtering logic for bank reconciliation testing scenarios.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line being processed</param>
/// <param name="BankAccLedgEntry">Bank account ledger entry with applied filters</param>
/// <remarks>
/// Raised from bank reconciliation line processing after standard ledger entry filtering.
/// </remarks>
[IntegrationEvent(false, false)]
local procedure OnBankAccReconciliationLineAfterGetRecordOnAfterBankAccLedgEntrySetFilters(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var BankAccLedgEntry: Record "Bank Account Ledger Entry")
begin
end;
/// <summary>
/// Integration event raised before validating applied amounts in bank reconciliation testing.
/// Allows custom validation logic for applied amount verification.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line being validated</param>
/// <param name="AppliedAmount">Applied amount being checked for accuracy</param>
/// <remarks>
/// Raised from applied amount validation process before standard amount checking.
/// </remarks>
[IntegrationEvent(false, false)]
local procedure OnBeforeCheckAppliedAmount(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AppliedAmount: Decimal)
begin
end;
/// <summary>
/// Integration event raised before initializing the bank reconciliation test report.
/// Allows customization of outstanding bank transaction display behavior.
/// </summary>
/// <param name="ShouldShowOutstandingBankTransactions">Controls whether outstanding transactions are displayed in the report</param>
/// <remarks>
/// Raised from report initialization process before determining transaction display options.
/// </remarks>
[IntegrationEvent(false, false)]
local procedure OnBeforeInitReport(var ShouldShowOutstandingBankTransactions: Boolean)
begin
end;
}