Table 274 Bank Acc. Reconciliation Line

App
Base Application
Namespace
Microsoft.Bank.Reconciliation
Versions
17-28

Fields, 34Keys, 3Procedures, 54Events, 26Obsolete, 6

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Bank/Reconciliation/BankAccReconciliationLine.Table.al1694 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.CRM.Team;
using Microsoft.Finance.Dimension;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Finance.GeneralLedger.Ledger;
using Microsoft.FixedAssets.FixedAsset;
using Microsoft.Foundation.AuditCodes;
using Microsoft.HumanResources.Employee;
using Microsoft.Intercompany.Partner;
using Microsoft.Purchases.Payables;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
using Microsoft.Sales.Receivables;
using System.IO;
using System.Utilities;

/// <summary>
/// Detailed bank statement lines for bank account reconciliation and payment application processes.
/// This table contains individual transaction records imported from bank statements or payment files,
/// providing the line-item detail for reconciliation workflows. Each line represents a single bank
/// transaction with amount, description, and matching information for application to ledger entries.
/// Supports both automated and manual matching scenarios with comprehensive tracking of applied amounts.
/// </summary>
/// <remarks>
/// Key features include statement import handling, automatic matching integration, manual application support,
/// text-to-account mapping, difference management, and posting preparation. The table works closely with
/// Applied Payment Entry records to track detailed payment applications and supports complex scenarios
/// including partial payments, split transactions, and multi-currency reconciliation.
/// Integration points include bank statement import, matching algorithms, payment application rules,
/// and general journal posting processes for finalizing reconciled transactions.
/// </remarks>
table 274 "Bank Acc. Reconciliation Line"
{
    Caption = 'Bank Acc. Reconciliation Line';
    Permissions = TableData "Data Exch. Field" = rimd;
    DataClassification = CustomerContent;

    fields
    {
        /// <summary>
        /// Bank account number that this reconciliation line relates to.
        /// Links the line to the specific bank account being reconciled and determines currency settings.
        /// </summary>
        field(1; "Bank Account No."; Code[20])
        {
            Caption = 'Bank Account No.';
            TableRelation = "Bank Account";
        }
        /// <summary>
        /// Statement number identifying the bank reconciliation or payment application batch.
        /// Groups related statement lines under a single reconciliation session.
        /// </summary>
        field(2; "Statement No."; Code[20])
        {
            Caption = 'Statement No.';
            TableRelation = "Bank Acc. Reconciliation"."Statement No." where("Bank Account No." = field("Bank Account No."));
        }
        /// <summary>
        /// Sequential line number within the bank statement.
        /// Provides unique identification and ordering of transactions within a statement.
        /// </summary>
        field(3; "Statement Line No."; Integer)
        {
            Caption = 'Statement Line No.';
        }
        /// <summary>
        /// Document number associated with the bank transaction.
        /// Contains reference numbers from bank statements such as check numbers or transaction IDs.
        /// </summary>
        field(4; "Document No."; Code[20])
        {
            Caption = 'Document No.';
            ToolTip = 'Specifies a number of your choice that will appear on the reconciliation line.';
        }
        /// <summary>
        /// Date when the bank transaction occurred.
        /// Used for matching and validation during reconciliation processes.
        /// </summary>
        field(5; "Transaction Date"; Date)
        {
            Caption = 'Transaction Date';
            ToolTip = 'Specifies the posting date of the bank account or check ledger entry on the reconciliation line when the Suggest Lines function is used.';
        }
        /// <summary>
        /// Descriptive text for the bank transaction.
        /// Contains transaction details imported from bank statements, used for matching and reporting.
        /// </summary>
        field(6; Description; Text[100])
        {
            Caption = 'Description';
            ToolTip = 'Specifies a description for the transaction on the reconciliation line.';
        }
        /// <summary>
        /// Amount of the bank statement transaction.
        /// Represents the actual bank transaction amount to be reconciled against ledger entries.
        /// </summary>
        field(7; "Statement Amount"; Decimal)
        {
            AutoFormatExpression = GetCurrencyCode();
            AutoFormatType = 1;
            Caption = 'Statement Amount';
            ToolTip = 'Specifies the amount that was paid into the bank account and then imported as a bank statement line represented by the journal line.';

            trigger OnValidate()
            begin
                Difference := "Statement Amount" - "Applied Amount";
            end;
        }
        /// <summary>
        /// Calculated difference between statement amount and applied amount.
        /// Shows remaining unreconciled amount that requires attention or posting to difference accounts.
        /// </summary>
        field(8; Difference; Decimal)
        {
            AutoFormatExpression = GetCurrencyCode();
            AutoFormatType = 1;
            BlankZero = true;
            Caption = 'Difference';
            ToolTip = 'Specifies the difference between the amount in the Statement Amount field and the amount in the Applied Amount field.';

            trigger OnValidate()
            begin
                "Statement Amount" := "Applied Amount" + Difference;
            end;
        }
        /// <summary>
        /// Total amount applied to ledger entries for this statement line.
        /// Calculated sum of all payment applications for this bank transaction.
        /// </summary>
        field(9; "Applied Amount"; Decimal)
        {
            AutoFormatExpression = GetCurrencyCode();
            AutoFormatType = 1;
            BlankZero = true;
            Caption = 'Applied Amount';
            ToolTip = 'Specifies the amount of the transaction on the reconciliation line that has been applied to a bank account or check ledger entry.';
            Editable = false;

            trigger OnValidate()
            begin
                Difference := "Statement Amount" - "Applied Amount";
            end;
        }
        /// <summary>
        /// Number of ledger entries applied to this statement line.
        /// Provides quick reference to application complexity and enables drill-down to application details.
        /// </summary>
        field(11; "Applied Entries"; Integer)
        {
            Caption = 'Applied Entries';
            ToolTip = 'Specifies whether the transaction on the bank''s statement has been applied to one or more bank account or check ledger entries.';
            Editable = false;

            trigger OnLookup()
            begin
                Rec.DisplayApplication();
            end;
        }
        /// <summary>
        /// Value date of the bank transaction as reported by the bank.
        /// May differ from transaction date and affects interest calculations and reporting.
        /// </summary>
        field(12; "Value Date"; Date)
        {
            Caption = 'Value Date';
            ToolTip = 'Specifies the value date of the transaction on the bank reconciliation line.';
        }
        /// <summary>
        /// Indicates whether the statement line is ready for automatic payment application.
        /// Used to control which lines participate in automated matching processes.
        /// </summary>
        field(13; "Ready for Application"; Boolean)
        {
            Caption = 'Ready for Application';
        }
        /// <summary>
        /// Check number associated with the bank transaction.
        /// Contains check reference for check-based payments imported from bank statements.
        /// </summary>
        field(14; "Check No."; Code[20])
        {
            Caption = 'Check No.';
            ToolTip = 'Specifies the check number for the transaction on the reconciliation line.';
        }
        /// <summary>
        /// Name of the related party (customer, vendor) for the transaction.
        /// Used for automatic matching and payment application to appropriate accounts.
        /// </summary>
        field(15; "Related-Party Name"; Text[250])
        {
            Caption = 'Related-Party Name';
            ToolTip = 'Specifies the name of the customer or vendor who made the payment that is represented by the journal line.';
        }
        /// <summary>
        /// Additional transaction information from the bank statement.
        /// Contains supplementary details that may assist in transaction identification and matching.
        /// </summary>
        field(16; "Additional Transaction Info"; Text[100])
        {
            Caption = 'Additional Transaction Info';
            ToolTip = 'Specifies additional information on the bank statement line for the payment.';
        }
        /// <summary>
        /// Data exchange entry number linking to the imported bank statement file.
        /// References the specific import session for audit trail and troubleshooting.
        /// </summary>
        field(17; "Data Exch. Entry No."; Integer)
        {
            Caption = 'Data Exch. Entry No.';
            Editable = false;
            TableRelation = "Data Exch.";
        }
        /// <summary>
        /// Line number within the data exchange entry.
        /// Identifies the specific line in the imported file that created this statement line.
        /// </summary>
        field(18; "Data Exch. Line No."; Integer)
        {
            Caption = 'Data Exch. Line No.';
            Editable = false;
        }
        /// <summary>
        /// Specifies the type of the bank reconciliation statement for the line.
        /// </summary>
        field(20; "Statement Type"; Enum "Bank Acc. Rec. Stmt. Type")
        {
            Caption = 'Statement Type';
        }
        /// <summary>
        /// Specifies the account type for the bank reconciliation line.
        /// </summary>
        field(21; "Account Type"; Enum "Gen. Journal Account Type")
        {
            Caption = 'Account Type';
            ToolTip = 'Specifies the type of account that the payment application will be posted to when you post the payment reconciliation journal.';

            trigger OnValidate()
            begin
                TestField("Applied Amount", 0);
                if "Account Type" = "Account Type"::"IC Partner" then
                    if not ConfirmManagement.GetResponse(ICPartnerAccountTypeQst, false) then begin
                        "Account Type" := xRec."Account Type";
                        exit;
                    end;
                if "Account Type" <> xRec."Account Type" then
                    Validate("Account No.", '');
            end;
        }
        /// <summary>
        /// Specifies the account number for the bank reconciliation line.
        /// </summary>
        field(22; "Account No."; Code[20])
        {
            Caption = 'Account No.';
            ToolTip = 'Specifies the account number that the payment application will be posted to when you post the payment reconciliation journal.';
            TableRelation = if ("Account Type" = const("G/L Account")) "G/L Account" where("Account Type" = const(Posting),
                                                                                          Blocked = const(false))
            else
            if ("Account Type" = const(Customer)) Customer
            else
            if ("Account Type" = const(Vendor)) Vendor
            else
            if ("Account Type" = const("Bank Account")) "Bank Account"
            else
            if ("Account Type" = const("Fixed Asset")) "Fixed Asset"
            else
            if ("Account Type" = const("IC Partner")) "IC Partner"
            else
            if ("Account Type" = const(Employee)) Employee;

            trigger OnValidate()
            begin
                TestField("Applied Amount", 0);
                CreateDimFromDefaultDim();
            end;
        }
        /// <summary>
        /// Specifies the transaction text from the bank statement.
        /// </summary>
        field(23; "Transaction Text"; Text[140])
        {
            Caption = 'Transaction Text';
            ToolTip = 'Specifies the text that the customer or vendor entered on that payment transaction that is represented by the journal line.';

            trigger OnValidate()
            begin
                if ("Statement Type" = "Statement Type"::"Payment Application") or (Description = '') then
                    Description := CopyStr("Transaction Text", 1, MaxStrLen(Description));
            end;
        }
        /// <summary>
        /// Specifies the bank account number of the related party from the bank statement.
        /// </summary>
        field(24; "Related-Party Bank Acc. No."; Text[100])
        {
            Caption = 'Related-Party Bank Acc. No.';
            ToolTip = 'Specifies the bank account number of the customer or vendor who made the payment.';
        }
        /// <summary>
        /// Specifies the address of the related party from the bank statement.
        /// </summary>
        field(25; "Related-Party Address"; Text[100])
        {
            Caption = 'Related-Party Address';
            ToolTip = 'Specifies the address of the customer or vendor who made the payment that is represented by the journal line.';
        }
        /// <summary>
        /// Specifies the city of the related party from the bank statement.
        /// </summary>
        field(26; "Related-Party City"; Text[50])
        {
            Caption = 'Related-Party City';
            ToolTip = 'Specifies the city name of the customer or vendor.';
        }
        /// <summary>
        /// Specifies the payment reference number from the bank statement.
        /// </summary>
        field(27; "Payment Reference No."; Code[50])
        {
            Caption = 'Payment Reference';
        }
        /// <summary>
        /// Specifies the first shortcut dimension code for the bank reconciliation line.
        /// </summary>
        field(31; "Shortcut Dimension 1 Code"; Code[20])
        {
            CaptionClass = '1,2,1';
            Caption = 'Shortcut Dimension 1 Code';
            ToolTip = 'Specifies the code for Shortcut Dimension 1, which is one of two global dimension codes that you set up on the General Ledger Setup page.';
            TableRelation = "Dimension Value".Code where("Global Dimension No." = const(1),
                                                          Blocked = const(false));

            trigger OnValidate()
            begin
                Rec.ValidateShortcutDimCode(1, "Shortcut Dimension 1 Code");
            end;
        }
        /// <summary>
        /// Specifies the second shortcut dimension code for the bank reconciliation line.
        /// </summary>
        field(32; "Shortcut Dimension 2 Code"; Code[20])
        {
            CaptionClass = '1,2,2';
            Caption = 'Shortcut Dimension 2 Code';
            ToolTip = 'Specifies the code for Shortcut Dimension 2, which is one of two global dimension codes that you set up om the General Ledger Setup page.';
            TableRelation = "Dimension Value".Code where("Global Dimension No." = const(2),
                                                          Blocked = const(false));

            trigger OnValidate()
            begin
                Rec.ValidateShortcutDimCode(2, "Shortcut Dimension 2 Code");
            end;
        }
        /// <summary>
        /// Specifies the confidence level of the automatic matching for the bank reconciliation line.
        /// </summary>
        field(50; "Match Confidence"; Enum "Bank Rec. Match Confidence")
        {
            CalcFormula = max("Applied Payment Entry"."Match Confidence" where("Statement Type" = field("Statement Type"),
                                                                                "Bank Account No." = field("Bank Account No."),
                                                                                "Statement No." = field("Statement No."),
                                                                                "Statement Line No." = field("Statement Line No.")));
            Caption = 'Match Confidence';
            ToolTip = 'Specifies the quality of the automatic payment application on the journal line.';
            Editable = false;
            FieldClass = FlowField;
            InitValue = "None";
        }
        /// <summary>
        /// Specifies the quality score of the automatic matching for the bank reconciliation line.
        /// </summary>
        field(51; "Match Quality"; Integer)
        {
            CalcFormula = max("Applied Payment Entry".Quality where("Bank Account No." = field("Bank Account No."),
                                                                     "Statement No." = field("Statement No."),
                                                                     "Statement Line No." = field("Statement Line No."),
                                                                     "Statement Type" = field("Statement Type")));
            Caption = 'Match Quality';
            Editable = false;
            FieldClass = FlowField;
        }
        /// <summary>
        /// Specifies the sorting order for the bank reconciliation line.
        /// </summary>
        field(60; "Sorting Order"; Integer)
        {
            Caption = 'Sorting Order';
        }
        /// <summary>
        /// Specifies the parent line number for grouped bank reconciliation lines.
        /// </summary>
        field(61; "Parent Line No."; Integer)
        {
            Caption = 'Parent Line No.';
            Editable = false;
        }
        /// <summary>
        /// Specifies the unique transaction identifier from the bank statement.
        /// </summary>
        field(70; "Transaction ID"; Text[50])
        {
            Caption = 'Transaction ID';
            ToolTip = 'Specifies the ID of the imported bank transaction.';
        }
        /// <summary>
        /// Specifies the ID of the dimension set for the bank reconciliation line.
        /// </summary>
        field(480; "Dimension Set ID"; Integer)
        {
            Caption = 'Dimension Set ID';
            Editable = false;
            TableRelation = "Dimension Set Entry";

            trigger OnLookup()
            begin
                Rec.ShowDimensions();
            end;

            trigger OnValidate()
            begin
                DimMgt.UpdateGlobalDimFromDimSetID("Dimension Set ID", "Shortcut Dimension 1 Code", "Shortcut Dimension 2 Code");
            end;
        }
    }

    keys
    {
        key(Key1; "Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.")
        {
            Clustered = true;
        }
        key(Key2; "Account Type", "Statement Amount")
        {
        }
    }

    fieldgroups
    {
    }

    trigger OnDelete()
    begin
        RemoveApplication();
        ClearDataExchEntries();
        RemoveAppliedPaymentEntries();
        DeletePaymentMatchingDetails();
        UpdateParentLineStatementAmount();
        if Find() then;
    end;

    trigger OnInsert()
    begin
        BankAccRecon.Get("Statement Type", "Bank Account No.", "Statement No.");
        "Applied Entries" := 0;
        Validate("Applied Amount", 0);
    end;

    trigger OnModify()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeOnModify(Rec, IsHandled);
        if IsHandled then
            exit;

        if xRec."Statement Amount" <> "Statement Amount" then
            RemoveApplication();
    end;

    trigger OnRename()
    begin
        Error(YouCannotRenameErr, TableCaption);
    end;

    var
        BankAccSetStmtNo: Codeunit "Bank Acc. Entry Set Recon.-No.";
        DimMgt: Codeunit DimensionManagement;
        ConfirmManagement: Codeunit "Confirm Management";

        YouCannotRenameErr: Label 'You cannot rename a %1.', Comment = '%1 - Table name';
        AmountWithinToleranceRangeTok: Label '>=%1&<=%2', Locked = true;
        AmountOustideToleranceRangeTok: Label '<%1|>%2', Locked = true;
        TransactionAmountMustNotBeZeroErr: Label 'The Transaction Amount field must have a value that is not 0.';
        CreditTheAccountQst: Label 'The remaining amount to apply is %2.\\Do you want to create a new payment application line that will debit or credit %1 with the remaining amount when you post the payment?', Comment = '%1 is the account name, %2 is the amount that is not applied (there is filed on the page named Remaining Amount To Apply)';
        ExcessiveAmountErr: Label 'The remaining amount to apply is %1.', Comment = '%1 is the amount that is not applied (there is filed on the page named Remaining Amount To Apply)';
        ImportPostedTransactionsQst: Label 'The bank statement contains payments that are already applied, but the related bank account ledger entries are not closed.\\Do you want to include these payments in the import?';
        ICPartnerAccountTypeQst: Label 'The resulting entry will be of type IC Transaction, but no Intercompany Outbox transaction will be created. \\Do you want to use the IC Partner account type anyway?';
        AppliedEntriesFilterLbl: Label '|%1', Locked = true;
        MatchedAutomaticallyFilterLbl: Label '=%1|%2|%3|%4', Locked = true;
        NotAppliedTxt: Label 'Not applied';
        MatchedAutomaticallyTxt: Label 'Matched Automatically';
        MatchedFromTextMappingRulesTxt: Label 'Matched - Text-To-Account Mapping';
        AppliedManuallyStatusTxt: Label 'Applied Manually';
        ReviewedStatusTxt: Label 'Application Reviewed';
        PaymentRecJournalFeatureNameTelemetryTxt: Label 'Payment Reconciliation', Locked = true;

    protected var
        BankAccLedgEntry: Record "Bank Account Ledger Entry";
        CheckLedgEntry: Record "Check Ledger Entry";
        BankAccRecon: Record "Bank Acc. Reconciliation";

    internal procedure GetPaymentRecJournalTelemetryFeatureName(): Text
    begin
        exit(PaymentRecJournalFeatureNameTelemetryTxt);
    end;

    /// <summary>
    /// Opens the payment application interface to display and manage applications for this reconciliation line.
    /// For bank reconciliation, shows bank account ledger entries; for payment application, shows detailed payment applications.
    /// Provides interactive interface for reviewing, modifying, or removing payment applications.
    /// </summary>
    procedure DisplayApplication()
    var
        PaymentApplication: Page "Payment Application";
    begin
        case "Statement Type" of
            "Statement Type"::"Bank Reconciliation":
                begin
                    BankAccLedgEntry.Reset();
                    BankAccLedgEntry.SetCurrentKey("Bank Account No.", Open);
                    BankAccLedgEntry.SetRange("Bank Account No.", "Bank Account No.");
                    BankAccLedgEntry.SetRange(Open, true);
                    BankAccLedgEntry.SetRange(
                        "Statement Status", BankAccLedgEntry."Statement Status"::"Bank Acc. Entry Applied");
                    BankAccLedgEntry.SetRange("Statement No.", "Statement No.");
                    BankAccLedgEntry.SetRange("Statement Line No.", "Statement Line No.");
                    OnDisplayApplicationOnAfterBankAccLedgEntrySetFilters(Rec, BankAccLedgEntry);
                    PAGE.Run(0, BankAccLedgEntry);
                end;
            "Statement Type"::"Payment Application":
                begin
                    if "Statement Amount" = 0 then
                        Error(TransactionAmountMustNotBeZeroErr);
                    PaymentApplication.SetBankAccReconcLine(Rec);
                    OnDisplayApplicationOnAfterSetBankAccReconcLine(PaymentApplication);
                    PaymentApplication.RunModal();
                end;
        end;
    end;

    procedure TransferFromPostedPaymentReconLine(PostedPaymentReconLine: Record "Posted Payment Recon. Line")
    begin
        Rec."Bank Account No." := PostedPaymentReconLine."Bank Account No.";
        Rec."Statement No." := PostedPaymentReconLine."Statement No.";
        Rec."Statement Line No." := PostedPaymentReconLine."Statement Line No.";
        Rec."Document No." := PostedPaymentReconLine."Document No.";
        Rec."Transaction Date" := PostedPaymentReconLine."Transaction Date";
        Rec.Description := PostedPaymentReconLine.Description;
        Rec."Statement Amount" := PostedPaymentReconLine."Statement Amount";
        Rec.Difference := PostedPaymentReconLine.Difference;
        Rec."Applied Amount" := PostedPaymentReconLine."Applied Amount";
        Rec."Applied Entries" := PostedPaymentReconLine."Applied Entries";
        Rec."Value Date" := PostedPaymentReconLine."Value Date";
        Rec."Check No." := PostedPaymentReconLine."Check No.";
        Rec."Related-Party Name" := PostedPaymentReconLine."Related-Party Name";
        Rec."Additional Transaction Info" := PostedPaymentReconLine."Additional Transaction Info";
        Rec."Data Exch. Entry No." := PostedPaymentReconLine."Data Exch. Entry No.";
        Rec."Data Exch. Line No." := PostedPaymentReconLine."Data Exch. Line No.";
        Rec."Statement Type" := Rec."Statement Type"::"Payment Application";
        Rec."Account Type" := PostedPaymentReconLine."Account Type";
        Rec."Account No." := PostedPaymentReconLine."Account No.";
        Rec."Transaction Text" := PostedPaymentReconLine.Description;
        Rec."Transaction ID" := CopyStr(PostedPaymentReconLine."Transaction ID", 1, MaxStrLen(Rec."Transaction ID"));
        OnAfterTransferFromPostedPaymentReconLine(Rec, PostedPaymentReconLine);
    end;

    procedure GetPaymentFile(var DataExchField: Record "Data Exch. Field"): Boolean
    begin
        if not DataExchField.ReadPermission() then
            exit(false);

        DataExchField.SetRange("Data Exch. No.", "Data Exch. Entry No.");
        DataExchField.SetRange("Line No.", "Data Exch. Line No.");
        exit(DataExchField.FindFirst());
    end;

    /// <summary>
    /// Retrieves the currency code associated with the bank account for this reconciliation line.
    /// Optimizes performance by caching the bank account record when possible.
    /// </summary>
    /// <returns>Currency code of the associated bank account; empty string if bank account not found or uses local currency.</returns>
    procedure GetCurrencyCode(): Code[10]
    var
        BankAccount: Record "Bank Account";
    begin
        if "Bank Account No." = BankAccount."No." then
            exit(BankAccount."Currency Code");

        if BankAccount.Get("Bank Account No.") then
            exit(BankAccount."Currency Code");

        exit('');
    end;

    procedure GetStyle() Result: Text
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetStyle(Rec, Result, IsHandled);
        if IsHandled then
            exit(Result);

        if "Applied Entries" <> 0 then
            exit('Favorable');

        exit('');
    end;

    /// <summary>
    /// Clears data exchange entries and related import data for this reconciliation line.
    /// Removes data exchange field records and updates related reconciliation lines that share the same data exchange entry.
    /// Used during cleanup operations when removing or modifying imported statement data.
    /// </summary>
    procedure ClearDataExchEntries()
    var
        DataExchField: Record "Data Exch. Field";
        BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
    begin
        DataExchField.DeleteRelatedRecords("Data Exch. Entry No.", "Data Exch. Line No.");

        BankAccReconciliationLine.SetRange("Statement Type", "Statement Type");
        BankAccReconciliationLine.SetRange("Bank Account No.", "Bank Account No.");
        BankAccReconciliationLine.SetRange("Statement No.", "Statement No.");
        BankAccReconciliationLine.SetRange("Data Exch. Entry No.", "Data Exch. Entry No.");
        BankAccReconciliationLine.SetFilter("Statement Line No.", '<>%1', "Statement Line No.");
        if BankAccReconciliationLine.IsEmpty() then
            DataExchField.DeleteRelatedRecords("Data Exch. Entry No.", 0);
    end;

    procedure ShowDimensions()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeShowDimensions(Rec, IsHandled);
        if IsHandled then
            exit;
        "Dimension Set ID" :=
          DimMgt.EditDimensionSet("Dimension Set ID", StrSubstNo('%1 %2 %3', TableCaption(), "Statement No.", "Statement Line No."));
        DimMgt.UpdateGlobalDimFromDimSetID("Dimension Set ID", "Shortcut Dimension 1 Code", "Shortcut Dimension 2 Code");
        OnAfterShowDimensions(Rec);
    end;

    procedure CreateDim(DefaultDimSource: List of [Dictionary of [Integer, Code[20]]])
    var
        SourceCodeSetup: Record "Source Code Setup";
        BankAccReconciliation: Record "Bank Acc. Reconciliation";
    begin
        SourceCodeSetup.Get();

        "Shortcut Dimension 1 Code" := '';
        "Shortcut Dimension 2 Code" := '';
        BankAccReconciliation.Get(Rec."Statement Type", Rec."Bank Account No.", Rec."Statement No.");
        "Dimension Set ID" :=
          DimMgt.GetRecDefaultDimID(
            Rec, CurrFieldNo, DefaultDimSource, SourceCodeSetup."Payment Reconciliation Journal",
            "Shortcut Dimension 1 Code", "Shortcut Dimension 2 Code", BankAccReconciliation."Dimension Set ID", DATABASE::"Bank Account");

        DimMgt.UpdateGlobalDimFromDimSetID("Dimension Set ID", "Shortcut Dimension 1 Code", "Shortcut Dimension 2 Code");

        OnAfterCreateDim(Rec, DefaultDimSource);
    end;

    procedure SetUpNewLine()
    begin
        "Transaction Date" := WorkDate();
        "Match Confidence" := "Match Confidence"::None;
        "Document No." := '';

        OnAfterSetUpNewLine(Rec, xRec);
    end;

    procedure ValidateShortcutDimCode(FieldNumber: Integer; var ShortcutDimCode: Code[20])
    begin
        OnBeforeValidateShortcutDimCode(Rec, xRec, FieldNumber, ShortcutDimCode);

        DimMgt.ValidateShortcutDimValues(FieldNumber, ShortcutDimCode, "Dimension Set ID");

        OnAfterValidateShortcutDimCode(Rec, xRec, FieldNumber, ShortcutDimCode);
    end;

    procedure ShowShortcutDimCode(var ShortcutDimCode: array[8] of Code[20])
    begin
        DimMgt.GetShortcutDimensions(Rec."Dimension Set ID", ShortcutDimCode);
    end;

    procedure AcceptAppliedPaymentEntriesSelectedLines()
    begin
        if FindSet() then
            repeat
                AcceptApplication();
            until Next() = 0;
    end;

    procedure RejectAppliedPaymentEntriesSelectedLines()
    begin
        if FindSet() then
            repeat
                RejectAppliedPayment();
            until Next() = 0;
    end;

    procedure RejectAppliedPayment()
    begin
        RemoveAppliedPaymentEntries();
        DeletePaymentMatchingDetails();
    end;

    procedure AcceptApplication()
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
    begin
        // For customer payments, the applied amount is positive, so positive difference means excessive amount.
        // For vendor payments, the applied amount is negative, so negative difference means excessive amount.
        // If "Applied Amount" and Difference have the same sign, then this is an overpayment situation.
        // Two non-zero numbers have the same sign if and only if their product is a positive number.
        if Difference * "Applied Amount" > 0 then begin
            if "Account Type" = "Account Type"::"Bank Account" then
                Error(ExcessiveAmountErr, Difference);
            SetAppliedPaymentEntryFromRec(AppliedPaymentEntry);
            if not AppliedPaymentEntry.Find() then begin
                if not Confirm(StrSubstNo(CreditTheAccountQst, GetAppliedToName(), Difference)) then
                    exit;
                TransferRemainingAmountToAccount();
            end;
        end;

        AppliedPaymentEntry.FilterAppliedPmtEntry(Rec);
        AppliedPaymentEntry.ModifyAll("Match Confidence", "Match Confidence"::Accepted);
    end;

    procedure FilterManyToOneMatches(var BankAccRecMatchBuffer: Record "Bank Acc. Rec. Match Buffer")
    begin
        BankAccRecMatchBuffer.SetRange("Statement No.", Rec."Statement No.");
        BankAccRecMatchBuffer.SetRange("Bank Account No.", Rec."Bank Account No.");
        BankAccRecMatchBuffer.SetRange("Statement Line No.", Rec."Statement Line No.");
    end;

    protected procedure RemoveApplication()
    var
        BankAccRecMatchBuffer: Record "Bank Acc. Rec. Match Buffer";
        BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
        ManyToOneBLEFound: Boolean;
    begin
        if "Statement Type" <> "Statement Type"::"Bank Reconciliation" then
            exit;
        FilterManyToOneMatches(BankAccRecMatchBuffer);
        if BankAccRecMatchBuffer.FindFirst() then begin
            BankAccLedgEntry.Reset();
            BankAccLedgEntry.SetRange("Entry No.", BankAccRecMatchBuffer."Ledger Entry No.");
            BankAccLedgEntry.SetRange(Open, true);
            BankAccLedgEntry.SetRange(
                "Statement Status", BankAccLedgEntry."Statement Status"::"Bank Acc. Entry Applied");
            if BankAccLedgEntry.FindFirst() then begin
                ManyToOneBLEFound := true;
                BankAccSetStmtNo.RemoveReconNo(BankAccLedgEntry, Rec, false);
                BankAccRecMatchBuffer.Delete();
            end;
        end;

        BankAccRecMatchBuffer.Reset();
        BankAccRecMatchBuffer.SetRange("Ledger Entry No.", BankAccLedgEntry."Entry No.");
        BankAccRecMatchBuffer.SetRange("Statement No.", Rec."Statement No.");
        BankAccRecMatchBuffer.SetRange("Bank Account No.", Rec."Bank Account No.");
        if (BankAccRecMatchBuffer.FindSet()) and (ManyToOneBLEFound) then begin
            repeat
                BankAccReconciliationLine.SetRange("Statement Line No.", BankAccRecMatchBuffer."Statement Line No.");
                BankAccReconciliationLine.SetRange("Statement No.", BankAccRecMatchBuffer."Statement No.");
                BankAccReconciliationLine.SetRange("Bank Account No.", BankAccRecMatchBuffer."Bank Account No.");
                if BankAccReconciliationLine.FindFirst() then begin
                    BankAccReconciliationLine."Applied Entries" := 0;
                    BankAccReconciliationLine.Validate("Applied Amount", 0);
                    BankAccReconciliationLine.Modify();
                end;
            until BankAccRecMatchBuffer.Next() = 0;

            BankAccRecMatchBuffer.DeleteAll();
        end;

        BankAccLedgEntry.Reset();
        BankAccLedgEntry.SetCurrentKey("Bank Account No.", Open);
        BankAccLedgEntry.SetRange("Bank Account No.", "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.", "Statement No.");
        BankAccLedgEntry.SetRange("Statement Line No.", "Statement Line No.");
        OnRemoveApplicationOnAfterBankAccLedgEntrySetFilters(Rec, BankAccLedgEntry);
        BankAccLedgEntry.LockTable();
        CheckLedgEntry.LockTable();
        if BankAccLedgEntry.Find('-') then
            repeat
                BankAccSetStmtNo.RemoveReconNo(BankAccLedgEntry, Rec, true);
            until BankAccLedgEntry.Next() = 0;
        "Applied Entries" := 0;
        Validate("Applied Amount", 0);
        Modify();
    end;

    procedure SetManualApplication()
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
    begin
        AppliedPaymentEntry.FilterAppliedPmtEntry(Rec);
        AppliedPaymentEntry.ModifyAll("Match Confidence", "Match Confidence"::Manual)
    end;

    local procedure RemoveAppliedPaymentEntries()
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
    begin
        Validate("Applied Amount", 0);
        Validate("Applied Entries", 0);
        Validate("Account No.", '');
        Modify(true);

        AppliedPaymentEntry.FilterAppliedPmtEntry(Rec);
        AppliedPaymentEntry.DeleteAll(true);

        OnAfterRemoveAppliedPaymentEntries(Rec);
    end;

    local procedure DeletePaymentMatchingDetails()
    var
        PaymentMatchingDetails: Record "Payment Matching Details";
    begin
        PaymentMatchingDetails.SetRange("Statement Type", "Statement Type");
        PaymentMatchingDetails.SetRange("Bank Account No.", "Bank Account No.");
        PaymentMatchingDetails.SetRange("Statement No.", "Statement No.");
        PaymentMatchingDetails.SetRange("Statement Line No.", "Statement Line No.");
        PaymentMatchingDetails.DeleteAll(true);
    end;

    procedure GetAppliedEntryAccountName(AppliedToEntryNo: Integer): Text
    var
        AccountType: Option;
        AccountNo: Code[20];
    begin
        AccountType := GetAppliedEntryAccountType(AppliedToEntryNo);
        AccountNo := GetAppliedEntryAccountNo(AppliedToEntryNo);
        exit(GetAccountName(AccountType, AccountNo));
    end;

    procedure GetAppliedToName(): Text
    var
        AccountType: Option;
        AccountNo: Code[20];
    begin
        AccountType := GetAppliedToAccountType();
        AccountNo := GetAppliedToAccountNo();
        exit(GetAccountName(AccountType, AccountNo));
    end;

    procedure GetAppliedEntryAccountType(AppliedToEntryNo: Integer): Integer
    var
        BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
    begin
        if "Account Type" = "Account Type"::"Bank Account" then
            if BankAccountLedgerEntry.Get(AppliedToEntryNo) then
                exit(BankAccountLedgerEntry."Bal. Account Type".AsInteger());
        exit("Account Type".AsInteger());
    end;

    procedure GetAppliedToAccountType(): Integer
    var
        BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
    begin
        if "Account Type" = "Account Type"::"Bank Account" then
            if BankAccountLedgerEntry.Get(GetFirstAppliedToEntryNo()) then
                exit(BankAccountLedgerEntry."Bal. Account Type".AsInteger());
        exit("Account Type".AsInteger());
    end;

    procedure GetAppliedEntryAccountNo(AppliedToEntryNo: Integer) AccountNo: Code[20]
    var
        CustLedgerEntry: Record "Cust. Ledger Entry";
        VendorLedgerEntry: Record "Vendor Ledger Entry";
        BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
    begin
        case "Account Type" of
            "Account Type"::Customer:
                if CustLedgerEntry.Get(AppliedToEntryNo) then
                    exit(CustLedgerEntry."Customer No.");
            "Account Type"::Vendor:
                if VendorLedgerEntry.Get(AppliedToEntryNo) then
                    exit(VendorLedgerEntry."Vendor No.");
            "Account Type"::"Bank Account":
                if BankAccountLedgerEntry.Get(AppliedToEntryNo) then
                    exit(BankAccountLedgerEntry."Bal. Account No.");
        end;
        AccountNo := "Account No.";

        OnAfterGetAppliedEntryAccountNo(Rec, AppliedToEntryNo, AccountNo);
    end;

    procedure GetAppliedToAccountNo(): Code[20]
    var
        BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
    begin
        if "Account Type" = "Account Type"::"Bank Account" then
            if BankAccountLedgerEntry.Get(GetFirstAppliedToEntryNo()) then
                exit(BankAccountLedgerEntry."Bal. Account No.");
        exit("Account No.")
    end;

    local procedure GetAccountName(AccountType: Option; AccountNo: Code[20]) Name: Text
    var
        Customer: Record Customer;
        Vendor: Record Vendor;
        Employee: Record Employee;
        GLAccount: Record "G/L Account";
        BankAccount: Record "Bank Account";
    begin
        case AccountType of
            "Account Type"::Customer.AsInteger():
                if Customer.Get(AccountNo) then
                    Name := Customer.Name;
            "Account Type"::Vendor.AsInteger():
                if Vendor.Get(AccountNo) then
                    Name := Vendor.Name;
            "Account Type"::Employee.AsInteger():
                if Employee.Get(AccountNo) then
                    Name := Employee.FullName();
            "Account Type"::"G/L Account".AsInteger():
                if GLAccount.Get(AccountNo) then
                    Name := GLAccount.Name;
            "Account Type"::"Bank Account".AsInteger():
                if BankAccount.Get(AccountNo) then
                    Name := BankAccount.Name;
        end;

        OnAfterGetAccountName(AccountType, AccountNo, Name);
    end;

    local procedure SetAppliedPaymentEntryFromRec(var AppliedPaymentEntry: Record "Applied Payment Entry")
    begin
        AppliedPaymentEntry.TransferFromBankAccReconLine(Rec);
        AppliedPaymentEntry."Account Type" := Enum::"Gen. Journal Account Type".FromInteger(GetAppliedToAccountType());
        AppliedPaymentEntry."Account No." := GetAppliedToAccountNo();
    end;

    procedure AppliedEntryAccountDrillDown(AppliedEntryNo: Integer)
    var
        AccountType: Option;
        AccountNo: Code[20];
    begin
        AccountType := GetAppliedEntryAccountType(AppliedEntryNo);
        AccountNo := GetAppliedEntryAccountNo(AppliedEntryNo);
        OpenAccountPage(AccountType, AccountNo);
    end;

    procedure AppliedToDrillDown()
    var
        AccountType: Option;
        AccountNo: Code[20];
    begin
        AccountType := GetAppliedToAccountType();
        AccountNo := GetAppliedToAccountNo();
        OpenAccountPage(AccountType, AccountNo);
    end;

    procedure OpenAccountPage(AccountType: Option; AccountNo: Code[20])
    var
        Customer: Record Customer;
        Vendor: Record Vendor;
        GLAccount: Record "G/L Account";
        BankAccount: Record "Bank Account";
    begin
        case AccountType of
            "Account Type"::Customer.AsInteger():
                begin
                    Customer.Get(AccountNo);
                    PAGE.Run(PAGE::"Customer Card", Customer);
                end;
            "Account Type"::Vendor.AsInteger():
                begin
                    Vendor.Get(AccountNo);
                    PAGE.Run(PAGE::"Vendor Card", Vendor);
                end;
            "Account Type"::"G/L Account".AsInteger():
                begin
                    GLAccount.Get(AccountNo);
                    PAGE.Run(PAGE::"G/L Account Card", GLAccount);
                end;
            "Account Type"::"Bank Account".AsInteger():
                begin
                    BankAccount.Get(AccountNo);
                    PAGE.Run(PAGE::"Bank Account Card", BankAccount);
                end;
        end;

        OnAfterOpenAccountPage(AccountType, AccountNo);
    end;

    procedure DrillDownOnNoOfLedgerEntriesWithinAmountTolerance()
    begin
        DrillDownOnNoOfLedgerEntriesBasedOnAmount(AmountWithinToleranceRangeTok);
    end;

    procedure DrillDownOnNoOfLedgerEntriesOutsideOfAmountTolerance()
    begin
        DrillDownOnNoOfLedgerEntriesBasedOnAmount(AmountOustideToleranceRangeTok);
    end;

    local procedure DrillDownOnNoOfLedgerEntriesBasedOnAmount(AmountFilter: Text)
    var
        CustLedgerEntry: Record "Cust. Ledger Entry";
        VendorLedgerEntry: Record "Vendor Ledger Entry";
        BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
        MinAmount: Decimal;
        MaxAmount: Decimal;
    begin
        GetAmountRangeForTolerance(MinAmount, MaxAmount);

        case "Account Type" of
            "Account Type"::Customer:
                begin
                    GetCustomerLedgerEntriesInAmountRange(CustLedgerEntry, "Account No.", AmountFilter, MinAmount, MaxAmount);
                    PAGE.Run(PAGE::"Customer Ledger Entries", CustLedgerEntry);
                end;
            "Account Type"::Vendor:
                begin
                    GetVendorLedgerEntriesInAmountRange(VendorLedgerEntry, "Account No.", AmountFilter, MinAmount, MaxAmount);
                    PAGE.Run(PAGE::"Vendor Ledger Entries", VendorLedgerEntry);
                end;
            "Account Type"::"Bank Account":
                begin
                    GetBankAccountLedgerEntriesInAmountRange(BankAccountLedgerEntry, AmountFilter, MinAmount, MaxAmount);
                    PAGE.Run(PAGE::"Bank Account Ledger Entries", BankAccountLedgerEntry);
                end;
        end;

        OnAfterDrillDownOnNoOfLedgerEntriesBasedOnAmount(Rec, AmountFilter);
    end;

    local procedure GetCustomerLedgerEntriesInAmountRange(var CustLedgerEntry: Record "Cust. Ledger Entry"; AccountNo: Code[20]; AmountFilter: Text; MinAmount: Decimal; MaxAmount: Decimal): Integer
    var
        BankAccount: Record "Bank Account";
        Result: Integer;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetCustomerLedgerEntriesInAmountRange(Rec, CustLedgerEntry, AccountNo, AmountFilter, MinAmount, MaxAmount, Result, IsHandled);
        if IsHandled then
            exit(Result);
        CustLedgerEntry.SetAutoCalcFields("Remaining Amount", "Remaining Amt. (LCY)");
        BankAccount.Get("Bank Account No.");
        GetApplicableCustomerLedgerEntries(CustLedgerEntry, BankAccount."Currency Code", AccountNo);

        if BankAccount.IsInLocalCurrency() then
            CustLedgerEntry.SetFilter("Remaining Amt. (LCY)", AmountFilter, MinAmount, MaxAmount)
        else
            CustLedgerEntry.SetFilter("Remaining Amount", AmountFilter, MinAmount, MaxAmount);

        exit(CustLedgerEntry.Count);
    end;

    local procedure GetVendorLedgerEntriesInAmountRange(var VendorLedgerEntry: Record "Vendor Ledger Entry"; AccountNo: Code[20]; AmountFilter: Text; MinAmount: Decimal; MaxAmount: Decimal): Integer
    var
        BankAccount: Record "Bank Account";
        Result: Integer;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetVendorLedgerEntriesInAmountRange(Rec, VendorLedgerEntry, AccountNo, AmountFilter, MinAmount, MaxAmount, Result, IsHandled);
        if IsHandled then
            exit(Result);
        VendorLedgerEntry.SetAutoCalcFields("Remaining Amount", "Remaining Amt. (LCY)");

        BankAccount.Get("Bank Account No.");
        GetApplicableVendorLedgerEntries(VendorLedgerEntry, BankAccount."Currency Code", AccountNo);

        if BankAccount.IsInLocalCurrency() then
            VendorLedgerEntry.SetFilter("Remaining Amt. (LCY)", AmountFilter, MinAmount, MaxAmount)
        else
            VendorLedgerEntry.SetFilter("Remaining Amount", AmountFilter, MinAmount, MaxAmount);

        exit(VendorLedgerEntry.Count);
    end;

    local procedure GetBankAccountLedgerEntriesInAmountRange(var BankAccountLedgerEntry: Record "Bank Account Ledger Entry"; AmountFilter: Text; MinAmount: Decimal; MaxAmount: Decimal): Integer
    var
        BankAccount: Record "Bank Account";
    begin
        BankAccount.Get("Bank Account No.");
        GetApplicableBankAccountLedgerEntries(BankAccountLedgerEntry, BankAccount."Currency Code", "Bank Account No.");

        BankAccountLedgerEntry.SetFilter("Remaining Amount", AmountFilter, MinAmount, MaxAmount);

        exit(BankAccountLedgerEntry.Count);
    end;

    local procedure GetApplicableCustomerLedgerEntries(var CustLedgerEntry: Record "Cust. Ledger Entry"; CurrencyCode: Code[10]; AccountNo: Code[20])
    begin
        CustLedgerEntry.SetRange(Open, true);
        CustLedgerEntry.SetRange("Applies-to ID", '');
        CustLedgerEntry.SetFilter("Document Type", '<>%1&<>%2',
          CustLedgerEntry."Document Type"::Payment,
          CustLedgerEntry."Document Type"::Refund);

        if CurrencyCode <> '' then
            CustLedgerEntry.SetRange("Currency Code", CurrencyCode);

        if AccountNo <> '' then
            CustLedgerEntry.SetFilter("Customer No.", AccountNo);
    end;

    local procedure GetApplicableVendorLedgerEntries(var VendorLedgerEntry: Record "Vendor Ledger Entry"; CurrencyCode: Code[10]; AccountNo: Code[20])
    begin
        VendorLedgerEntry.SetRange(Open, true);
        VendorLedgerEntry.SetRange("Applies-to ID", '');
        VendorLedgerEntry.SetFilter("Document Type", '<>%1&<>%2',
          VendorLedgerEntry."Document Type"::Payment,
          VendorLedgerEntry."Document Type"::Refund);

        if CurrencyCode <> '' then
            VendorLedgerEntry.SetRange("Currency Code", CurrencyCode);

        if AccountNo <> '' then
            VendorLedgerEntry.SetFilter("Vendor No.", AccountNo);
    end;

    local procedure GetApplicableBankAccountLedgerEntries(var BankAccountLedgerEntry: Record "Bank Account Ledger Entry"; CurrencyCode: Code[10]; AccountNo: Code[20])
    begin
        BankAccountLedgerEntry.SetRange(Open, true);

        if CurrencyCode <> '' then
            BankAccountLedgerEntry.SetRange("Currency Code", CurrencyCode);

        if AccountNo <> '' then
            BankAccountLedgerEntry.SetRange("Bank Account No.", AccountNo);
    end;

    procedure FilterBankRecLinesByDate(BankAccReconciliation: Record "Bank Acc. Reconciliation"; Overwrite: Boolean)
    begin
        FilterBankRecLines(BankAccReconciliation, Overwrite);

        // Records sorted by transaction date to optimize matching
        Rec.SetCurrentKey("Transaction Date");
        Rec.SetAscending("Transaction Date", true);
    end;

    procedure FilterBankRecLines(BankAccReconciliation: Record "Bank Acc. Reconciliation"; Overwrite: Boolean)
    begin
        Rec.Reset();
        Rec.SetRange("Statement Type", BankAccReconciliation."Statement Type");
        Rec.SetRange("Bank Account No.", BankAccReconciliation."Bank Account No.");
        Rec.SetRange("Statement No.", BankAccReconciliation."Statement No.");
        if not Overwrite then
            Rec.SetRange("Applied Entries", 0);
        OnAfterFilterBankRecLines(Rec, BankAccReconciliation);
    end;

    procedure FilterBankRecLines(BankAccReconciliation: Record "Bank Acc. Reconciliation")
    begin
        FilterBankRecLines(BankAccReconciliation, true);
    end;

    procedure LinesExist(BankAccRecon: Record "Bank Acc. Reconciliation"): Boolean
    begin
        FilterBankRecLines(BankAccRecon);
        exit(FindSet());
    end;

    procedure GetAppliedToDocumentNo(SeparatorTxt: Text): Text
    var
        ApplyType: Option "Document No.","Entry No.";
    begin
        exit(GetAppliedNo(ApplyType::"Document No.", SeparatorTxt));
    end;

    procedure GetAppliedToDocumentNo(): Text
    begin
        exit(GetAppliedToDocumentNo(', '));
    end;

    procedure GetAppliedToEntryNo(): Text
    var
        ApplyType: Option "Document No.","Entry No.";
    begin
        exit(GetAppliedNo(ApplyType::"Entry No.", ', '));
    end;

    procedure GetAppliedToEntryFilter(): Text
    var
        ApplyType: Option "Document No.","Entry No.";
    begin
        exit(GetAppliedNo(ApplyType::"Entry No.", '|'));
    end;

    local procedure GetFirstAppliedToEntryNo(): Integer
    var
        AppliedEntryNumbers: Text;
        AppliedToEntryNo: Integer;
    begin
        AppliedEntryNumbers := GetAppliedToEntryNo();
        if AppliedEntryNumbers = '' then
            exit(0);
        Evaluate(AppliedToEntryNo, SelectStr(1, AppliedEntryNumbers));
        exit(AppliedToEntryNo);
    end;

    local procedure GetAppliedNo(ApplyType: Option "Document No.","Entry No."; SeparatorText: Text): Text
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
        AppliedNumbers: Text;
    begin
        AppliedPaymentEntry.SetRange("Statement Type", "Statement Type");
        AppliedPaymentEntry.SetRange("Bank Account No.", "Bank Account No.");
        AppliedPaymentEntry.SetRange("Statement No.", "Statement No.");
        AppliedPaymentEntry.SetRange("Statement Line No.", "Statement Line No.");

        AppliedNumbers := '';
        if AppliedPaymentEntry.FindSet() then
            repeat
                if ApplyType = ApplyType::"Document No." then begin
                    if AppliedPaymentEntry."Document No." <> '' then
                        if AppliedNumbers = '' then
                            AppliedNumbers := AppliedPaymentEntry."Document No."
                        else
                            AppliedNumbers := AppliedNumbers + SeparatorText + AppliedPaymentEntry."Document No.";
                end else
                    if AppliedPaymentEntry."Applies-to Entry No." <> 0 then
                        if AppliedNumbers = '' then
                            AppliedNumbers := Format(AppliedPaymentEntry."Applies-to Entry No.")
                        else
                            AppliedNumbers := AppliedNumbers + SeparatorText + Format(AppliedPaymentEntry."Applies-to Entry No.");
            until AppliedPaymentEntry.Next() = 0;

        exit(AppliedNumbers);
    end;

    procedure ShowAppliedToEntries()
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
        CustLedgEntry: Record "Cust. Ledger Entry";
        VendLedgEntry: Record "Vendor Ledger Entry";
        AppliedBankAccLedgEntry: Record "Bank Account Ledger Entry";
        GLEntry: Record "G/L Entry";
        AppliedEntriesNumbersFilter: Text;
    begin
        AppliedPaymentEntry.SetRange("Statement Type", "Statement Type");
        AppliedPaymentEntry.SetRange("Bank Account No.", "Bank Account No.");
        AppliedPaymentEntry.SetRange("Statement No.", "Statement No.");
        AppliedPaymentEntry.SetRange("Statement Line No.", "Statement Line No.");

        if not AppliedPaymentEntry.FindSet() then
            exit;

        AppliedEntriesNumbersFilter := Format(AppliedPaymentEntry."Applies-to Entry No.");

        if (AppliedPaymentEntry.Next() <> 0) then
            repeat
                AppliedEntriesNumbersFilter += StrSubstNo(AppliedEntriesFilterLbl, AppliedPaymentEntry."Applies-to Entry No.");
            until AppliedPaymentEntry.Next() = 0;

        case "Account Type" of
            "Account Type"::"G/L Account":
                begin
                    GLEntry.SetFilter("Entry No.", AppliedEntriesNumbersFilter);
                    PAGE.Run(0, GLEntry);
                end;
            "Account Type"::Customer:
                begin
                    CustLedgEntry.SetFilter("Entry No.", AppliedEntriesNumbersFilter);
                    PAGE.Run(0, CustLedgEntry);
                end;
            "Account Type"::Vendor:
                begin
                    VendLedgEntry.SetFilter("Entry No.", AppliedEntriesNumbersFilter);
                    PAGE.Run(0, VendLedgEntry);
                end;
            "Account Type"::"Bank Account":
                begin
                    AppliedBankAccLedgEntry.SetFilter("Entry No.", AppliedEntriesNumbersFilter);
                    PAGE.Run(0, AppliedBankAccLedgEntry);
                end;
        end;
    end;

    procedure TransferRemainingAmountToAccount()
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
    begin
        TestField("Account No.");

        SetAppliedPaymentEntryFromRec(AppliedPaymentEntry);
        AppliedPaymentEntry.Validate("Applied Amount", Difference);
        AppliedPaymentEntry.Validate("Match Confidence", AppliedPaymentEntry."Match Confidence"::Manual);
        AppliedPaymentEntry.Insert(true);
    end;

    procedure GetStatusText(): Text
    begin
        if ("Applied Entries" = 0) and ("Match Confidence" = "Match Confidence"::None) then
            exit(NotAppliedTxt);

        case "Match Confidence" of
            "Match Confidence"::Manual:
                exit(AppliedManuallyStatusTxt);
            "Match Confidence"::Accepted:
                exit(ReviewedStatusTxt);
            "Match Confidence"::"High - Text-to-Account Mapping":
                exit(MatchedFromTextMappingRulesTxt);
            "Match Confidence"::None:
                if ("Applied Entries" > 0) then
                    exit(MatchedAutomaticallyTxt);
            "Match Confidence"::Low, "Match Confidence"::Medium, "Match Confidence"::High:
                exit(MatchedAutomaticallyTxt);
        end;

        Session.LogMessage('0000BN5', StrSubstNo('Unexpected - could not find the status text - Match Confidence %1, Match Quality %2, Applied Entries %3', "Match Confidence", "Match Quality", "Applied Entries"), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', '');
        exit('');
    end;

    procedure GetAmountRangeForTolerance(var MinAmount: Decimal; var MaxAmount: Decimal)
    var
        BankAccount: Record "Bank Account";
        TempAmount: Decimal;
    begin
        BankAccount.Get("Bank Account No.");
        case BankAccount."Match Tolerance Type" of
            BankAccount."Match Tolerance Type"::Amount:
                begin
                    MinAmount := "Statement Amount" - BankAccount."Match Tolerance Value";
                    MaxAmount := "Statement Amount" + BankAccount."Match Tolerance Value";

                    if ("Statement Amount" >= 0) and (MinAmount < 0) then
                        MinAmount := 0
                    else
                        if ("Statement Amount" < 0) and (MaxAmount > 0) then
                            MaxAmount := 0;
                end;
            BankAccount."Match Tolerance Type"::Percentage:
                begin
                    MinAmount := "Statement Amount" * (1 - BankAccount."Match Tolerance Value" / 100);
                    MaxAmount := "Statement Amount" * (1 + BankAccount."Match Tolerance Value" / 100);

                    if "Statement Amount" < 0 then begin
                        TempAmount := MinAmount;
                        MinAmount := MaxAmount;
                        MaxAmount := TempAmount;
                    end;
                end;
        end;

        MinAmount := Round(MinAmount);
        MaxAmount := Round(MaxAmount);
    end;

    procedure GetAppliedPmtData(var AppliedPmtEntry: Record "Applied Payment Entry"; var RemainingAmountAfterPosting: Decimal; var DifferenceStatementAmtToApplEntryAmount: Decimal; PmtAppliedToTxt: Text)
    var
        CurrRemAmtAfterPosting: Decimal;
    begin
        AppliedPmtEntry.Init();
        RemainingAmountAfterPosting := 0;
        DifferenceStatementAmtToApplEntryAmount := 0;

        AppliedPmtEntry.FilterAppliedPmtEntry(Rec);
        AppliedPmtEntry.SetFilter("Applies-to Entry No.", '<>0');
        if AppliedPmtEntry.FindSet() then begin
            DifferenceStatementAmtToApplEntryAmount := "Statement Amount";
            repeat
                CurrRemAmtAfterPosting :=
                  AppliedPmtEntry.GetRemAmt() -
                  AppliedPmtEntry.GetAmtAppliedToOtherStmtLines();

                RemainingAmountAfterPosting += CurrRemAmtAfterPosting - AppliedPmtEntry."Applied Amount";
                DifferenceStatementAmtToApplEntryAmount -= CurrRemAmtAfterPosting - AppliedPmtEntry."Applied Pmt. Discount";
                OnGetAppliedPmtDataOnAfterLoopIteration(Rec, AppliedPmtEntry);
            until AppliedPmtEntry.Next() = 0;
        end;

        if "Applied Entries" > 1 then
            AppliedPmtEntry.Description := StrSubstNo(PmtAppliedToTxt, "Applied Entries");
    end;

    [Scope('OnPrem')]
    procedure GetAppliedPmtData(var AppliedPmtEntry: Record "Applied Payment Entry"; PmtAppliedToTxt: Text)
    begin
        AppliedPmtEntry.Init();

        AppliedPmtEntry.FilterAppliedPmtEntry(Rec);
        AppliedPmtEntry.SetFilter("Applies-to Entry No.", '<>0');
        if "Applied Entries" > 1 then
            AppliedPmtEntry.Description := StrSubstNo(PmtAppliedToTxt, "Applied Entries");
    end;

    local procedure UpdateParentLineStatementAmount()
    var
        BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
    begin
        if BankAccReconciliationLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Parent Line No.") then begin
            BankAccReconciliationLine.Validate("Statement Amount", "Statement Amount" + BankAccReconciliationLine."Statement Amount");
            BankAccReconciliationLine.Modify(true)
        end
    end;

    procedure IsTransactionPostedAndReconciled(): Boolean
    var
        PostedPaymentReconLine: Record "Posted Payment Recon. Line";
        BankAccountStatementLine: Record "Bank Account Statement Line";
        ReturnValue: Boolean;
        IsHandled: Boolean;
    begin
        OnBeforeIsTransactionPostedAndReconciled(Rec, ReturnValue, IsHandled);
        if IsHandled then
            exit(ReturnValue);

        if "Transaction ID" <> '' then begin
            PostedPaymentReconLine.SetRange("Bank Account No.", "Bank Account No.");
            PostedPaymentReconLine.SetRange("Transaction ID", "Transaction ID");
            PostedPaymentReconLine.SetRange(Reconciled, true);
            if not PostedPaymentReconLine.IsEmpty() then
                exit(true);
            BankAccountStatementLine.SetRange("Bank Account No.", "Bank Account No.");
            BankAccountStatementLine.SetRange("Transaction ID", "Transaction ID");
            exit(not BankAccountStatementLine.IsEmpty);
        end;
        exit(false);
    end;

    local procedure IsTransactionPostedAndNotReconciled(): Boolean
    var
        PostedPaymentReconLine: Record "Posted Payment Recon. Line";
    begin
        if "Transaction ID" <> '' then begin
            PostedPaymentReconLine.SetRange("Bank Account No.", "Bank Account No.");
            PostedPaymentReconLine.SetRange("Transaction ID", "Transaction ID");
            PostedPaymentReconLine.SetRange(Reconciled, false);
            exit(not PostedPaymentReconLine.IsEmpty());
        end;
        exit(false);
    end;

    local procedure IsTransactionAlreadyImported(): Boolean
    var
        BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
    begin
        if "Transaction ID" <> '' then begin
            BankAccReconciliationLine.SetRange("Statement Type", "Statement Type");
            BankAccReconciliationLine.SetRange("Bank Account No.", "Bank Account No.");
            BankAccReconciliationLine.SetRange("Transaction ID", "Transaction ID");
            exit(not BankAccReconciliationLine.IsEmpty());
        end;
        exit(false);
    end;

    local procedure AllowImportDuplicatedTransactions(): Boolean
    var
        BankAccReconciliation: Record "Bank Acc. Reconciliation";
    begin
        BankAccReconciliation.SetLoadFields("Allow Duplicated Transactions");
        BankAccReconciliation.Get(Rec."Statement Type", Rec."Bank Account No.", Rec."Statement No.");
        exit(BankAccReconciliation."Allow Duplicated Transactions");
    end;

    local procedure AllowImportOfPostedNotReconciledTransactions(): Boolean
    var
        BankAccReconciliation: Record "Bank Acc. Reconciliation";
    begin
        BankAccReconciliation.Get(Rec."Statement Type", Rec."Bank Account No.", Rec."Statement No.");
        if BankAccReconciliation."Import Posted Transactions" = BankAccReconciliation."Import Posted Transactions"::" " then begin
            BankAccReconciliation."Import Posted Transactions" := BankAccReconciliation."Import Posted Transactions"::No;
            if GuiAllowed then
                if Confirm(ImportPostedTransactionsQst) then
                    BankAccReconciliation."Import Posted Transactions" := BankAccReconciliation."Import Posted Transactions"::Yes;
            BankAccReconciliation.Modify();
        end;

        exit(BankAccReconciliation."Import Posted Transactions" = BankAccReconciliation."Import Posted Transactions"::Yes);
    end;

    procedure CanImport(): Boolean
    begin
        if IsTransactionPostedAndReconciled() then
            exit(false);

        if IsTransactionAlreadyImported() then
            if not AllowImportDuplicatedTransactions() then
                exit(false);

        if IsTransactionPostedAndNotReconciled() then
            exit(AllowImportOfPostedNotReconciledTransactions());

        exit(true);
    end;

    procedure BankStatementLinesListIsEmpty(StatementNo: Code[20]; StatementType: Option; BankAccountNo: Code[20]): Boolean
    var
        BankAccReconciliationLine: record "Bank Acc. Reconciliation Line";
    begin
        BankAccReconciliationLine.SetRange("Bank Account No.", BankAccountNo);
        BankAccReconciliationLine.SetRange("Statement No.", StatementNo);
        BankAccReconciliationLine.SetRange("Statement Type", StatementType);

        exit(BankAccReconciliationLine.IsEmpty);
    end;

    local procedure GetSalepersonPurchaserCode(): Code[20]
    var
        Customer: Record Customer;
        Vendor: Record Vendor;
    begin
        case "Account Type" of
            "Account Type"::Customer:
                if Customer.Get("Account No.") then
                    exit(Customer."Salesperson Code");
            "Account Type"::Vendor:
                if Vendor.Get("Account No.") then
                    exit(Vendor."Purchaser Code");
        end;
    end;

    internal procedure GetAppliesToIDForBankStatement(): Code[50]
    begin
        exit(CopyStr(Rec."Bank Account No." + '-' + Format(Rec."Statement No."), 1, 50));
    end;

    procedure GetAppliesToID(): Code[50]
    var
        CustLedgerEntry: Record "Cust. Ledger Entry";
    begin
        exit(
            CopyStr(
                GetAppliesToIDForBankStatement() + '-' + Format(Rec."Statement Line No."),
                1,
                MaxStrLen(CustLedgerEntry."Applies-to ID")));
    end;

    procedure GetDescription(): Text[100]
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
    begin
        if Description <> '' then
            exit(Description);

        AppliedPaymentEntry.FilterAppliedPmtEntry(Rec);
        AppliedPaymentEntry.SetFilter("Applies-to Entry No.", '<>%1', 0);
        if AppliedPaymentEntry.FindSet() then
            if AppliedPaymentEntry.Next() = 0 then
                exit(AppliedPaymentEntry.Description);

        exit('');
    end;

    procedure GetMatchedAutomaticallyFilter(): Text
    begin
        exit(StrSubstNo(MatchedAutomaticallyFilterLbl, "Match Confidence"::None, "Match Confidence"::Low, "Match Confidence"::Medium, "Match Confidence"::High));
    end;

    procedure CreateDimFromDefaultDim()
    var
        DefaultDimSource: List of [Dictionary of [Integer, Code[20]]];
    begin
        InitDefaultDimensionSources(DefaultDimSource);
        CreateDim(DefaultDimSource);
    end;

    local procedure InitDefaultDimensionSources(var DefaultDimSource: List of [Dictionary of [Integer, Code[20]]])
    begin
        DimMgt.AddDimSource(DefaultDimSource, DimMgt.TypeToTableID1(Rec."Account Type".AsInteger()), Rec."Account No.");
        DimMgt.AddDimSource(DefaultDimSource, Database::"Salesperson/Purchaser", GetSalepersonPurchaserCode());

        OnAfterInitDefaultDimensionSources(Rec, DefaultDimSource);
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterInitDefaultDimensionSources(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var DefaultDimSource: List of [Dictionary of [Integer, Code[20]]])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterSetUpNewLine(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; xBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line");
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterShowDimensions(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCreateDim(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; DefaultDimSource: List of [Dictionary of [Integer, Code[20]]])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterFilterBankRecLines(var Rec: Record "Bank Acc. Reconciliation Line"; BankAccRecon: Record "Bank Acc. Reconciliation")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterGetAccountName(AccountType: Option; AccountNo: Code[20]; var Name: Text)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterGetAppliedEntryAccountNo(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AppliedToEntryNo: Integer; var AccountNo: Code[20])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterOpenAccountPage(AccountType: Option; AccountNo: Code[20])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterRemoveAppliedPaymentEntries(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterDrillDownOnNoOfLedgerEntriesBasedOnAmount(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AmountFilter: Text)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterValidateShortcutDimCode(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var xBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; FieldNumber: Integer; var ShortcutDimCode: Code[20])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeOnModify(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeValidateShortcutDimCode(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var xBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; FieldNumber: Integer; var ShortcutDimCode: Code[20])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnDisplayApplicationOnAfterBankAccLedgEntrySetFilters(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var BankAccLedgEntry: Record "Bank Account Ledger Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeIsTransactionPostedAndReconciled(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var ReturnValue: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnDisplayApplicationOnAfterSetBankAccReconcLine(var PaymentApplication: Page "Payment Application");
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnRemoveApplicationOnAfterBankAccLedgEntrySetFilters(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var BankAccLedgEntry: Record "Bank Account Ledger Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetStyle(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var Result: text; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetCustomerLedgerEntriesInAmountRange(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var CustLedgerEntry: Record "Cust. Ledger Entry"; AccountNo: Code[20]; AmountFilter: Text; MinAmount: Decimal; MaxAmount: Decimal; var Result: Integer; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetVendorLedgerEntriesInAmountRange(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var VendorLedgerEntry: Record "Vendor Ledger Entry"; AccountNo: Code[20]; AmountFilter: Text; MinAmount: Decimal; MaxAmount: Decimal; var Result: Integer; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeShowDimensions(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnGetAppliedPmtDataOnAfterLoopIteration(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AppliedPmtEntry: Record "Applied Payment Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterTransferFromPostedPaymentReconLine(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; PostedPaymentReconLine: Record "Posted Payment Recon. Line")
    begin
    end;
}