Table 1294 Applied Payment Entry

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

Fields, 18Keys, 1Procedures, 17Events, 18

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Bank/Reconciliation/AppliedPaymentEntry.Table.al1448 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.Ledger;
using Microsoft.Finance.Currency;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Finance.GeneralLedger.Ledger;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Finance.ReceivablesPayables;
using Microsoft.FixedAssets.FixedAsset;
using Microsoft.HumanResources.Employee;
using Microsoft.HumanResources.Payables;
using Microsoft.Intercompany.Partner;
using Microsoft.Purchases.Payables;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
using Microsoft.Sales.Receivables;

/// <summary>
/// Records payment applications linking bank statement lines to customer, vendor, and general ledger entries.
/// This table serves as the detailed application registry for payment reconciliation processes, tracking
/// which specific ledger entries have been matched to bank statement transactions and the amounts applied.
/// Supports complex application scenarios including partial payments, payment discounts, and multi-currency applications.
/// </summary>
/// <remarks>
/// Key features include automatic matching confidence scoring, payment discount calculations, tolerance handling,
/// and comprehensive validation of application logic. Integrates with customer, vendor, employee, and G/L ledger
/// entries to provide complete payment application functionality across all account types.
/// </remarks>
table 1294 "Applied Payment Entry"
{
    Caption = 'Applied Payment Entry';
    LookupPageID = "Payment Application";
    DataClassification = CustomerContent;

    fields
    {
        /// <summary>
        /// Bank account number for the payment reconciliation statement.
        /// Links the payment application to the specific bank account being reconciled.
        /// </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 payment applications under a single processing 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."),
                                                                              "Statement Type" = field("Statement Type"));
        }
        /// <summary>
        /// Line number within the bank statement being applied.
        /// References the specific bank transaction line from the imported statement.
        /// </summary>
        field(3; "Statement Line No."; Integer)
        {
            Caption = 'Statement Line No.';
        }
        /// <summary>
        /// Type of reconciliation statement being processed.
        /// Determines whether this is a bank reconciliation or payment application scenario.
        /// </summary>
        field(20; "Statement Type"; Enum "Bank Acc. Rec. Stmt. Type")
        {
            Caption = 'Statement Type';
        }
        /// <summary>
        /// Type of account that the payment is being applied to.
        /// Determines the ledger entry type for payment application (Customer, Vendor, G/L Account, etc.).
        /// </summary>
        field(21; "Account Type"; Enum "Gen. Journal Account Type")
        {
            Caption = 'Account Type';

            trigger OnValidate()
            begin
                Validate("Account No.", '');
            end;
        }
        /// <summary>
        /// Account number for the payment application.
        /// Specifies the customer, vendor, G/L account, or other account receiving the payment.
        /// </summary>
        field(22; "Account No."; Code[20])
        {
            Caption = 'Account No.';
            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";

            trigger OnValidate()
            begin
                if "Account No." = '' then
                    CheckApplnIsSameAcc();

                GetAccInfo();
                Validate("Applies-to Entry No.", 0);
            end;
        }
        /// <summary>
        /// Entry number of the specific ledger entry being applied.
        /// Links the payment to a particular customer invoice, vendor bill, or other transaction.
        /// </summary>
        field(23; "Applies-to Entry No."; Integer)
        {
            Caption = 'Applies-to Entry No.';
            TableRelation = if ("Account Type" = const("G/L Account")) "G/L Entry"
            else
            if ("Account Type" = const(Customer)) "Cust. Ledger Entry" where(Open = const(true))
            else
            if ("Account Type" = const(Vendor)) "Vendor Ledger Entry" where(Open = const(true))
            else
            if ("Account Type" = const("Bank Account")) "Bank Account Ledger Entry" where(Open = const(true));

            trigger OnLookup()
            var
                CustLedgEntry: Record "Cust. Ledger Entry";
                VendLedgEntry: Record "Vendor Ledger Entry";
                BankAccLedgEntry: Record "Bank Account Ledger Entry";
                GLEntry: Record "G/L Entry";
                IsHandled: Boolean;
            begin
                IsHandled := false;
                OnBeforeAppliesToEntryNoLookup(Rec, IsHandled);
                if IsHandled then
                    exit;

                case "Account Type" of
                    "Account Type"::"G/L Account":
                        begin
                            GLEntry.SetRange("G/L Account No.", "Account No.");
                            if PAGE.RunModal(0, GLEntry) = ACTION::LookupOK then
                                Validate("Applies-to Entry No.", GLEntry."Entry No.");
                        end;
                    "Account Type"::Customer:
                        begin
                            CustLedgEntry.SetRange(Open, true);
                            CustLedgEntry.SetRange("Customer No.", "Account No.");
                            if PAGE.RunModal(0, CustLedgEntry) = ACTION::LookupOK then
                                Validate("Applies-to Entry No.", CustLedgEntry."Entry No.");
                        end;
                    "Account Type"::Vendor:
                        begin
                            VendLedgEntry.SetRange(Open, true);
                            VendLedgEntry.SetRange("Vendor No.", "Account No.");
                            if PAGE.RunModal(0, VendLedgEntry) = ACTION::LookupOK then
                                Validate("Applies-to Entry No.", VendLedgEntry."Entry No.");
                        end;
                    "Account Type"::"Bank Account":
                        begin
                            BankAccLedgEntry.SetRange(Open, true);
                            BankAccLedgEntry.SetRange("Bank Account No.", "Account No.");
                            if PAGE.RunModal(0, BankAccLedgEntry) = ACTION::LookupOK then
                                Validate("Applies-to Entry No.", BankAccLedgEntry."Entry No.");
                        end;
                end;

                OnAfterLookupAppliesToEntryNo(Rec);
            end;

            trigger OnValidate()
            begin
                if "Applies-to Entry No." = 0 then begin
                    Validate("Applied Amount", 0);
                    exit;
                end;

                CheckCurrencyCombination();
                GetLedgEntryInfo();
                UpdatePaymentDiscount(SuggestDiscToApply(false));
                Validate("Applied Amount", SuggestAmtToApply());
            end;
        }
        /// <summary>
        /// Amount being applied from the bank statement to the ledger entry.
        /// Represents the portion of the bank transaction applied to this specific entry.
        /// </summary>
        field(24; "Applied Amount"; Decimal)
        {
            AutoFormatType = 1;
            AutoFormatExpression = Rec."Currency Code";
            Caption = 'Applied Amount';

            trigger OnValidate()
            begin
                if "Applies-to Entry No." <> 0 then
                    TestField("Applied Amount");
                CheckEntryAmt();
                UpdatePaymentDiscount(SuggestDiscToApply(true));
                if "Applied Pmt. Discount" <> 0 then
                    "Applied Amount" := SuggestAmtToApply();

                UpdateParentBankAccReconLine(false);
            end;
        }
        /// <summary>
        /// Payment discount amount applied with this payment.
        /// Calculated based on payment terms and payment timing for early payment discounts.
        /// </summary>
        field(29; "Applied Pmt. Discount"; Decimal)
        {
            AutoFormatExpression = Rec."Currency Code";
            AutoFormatType = 1;
            Caption = 'Applied Pmt. Discount';
        }
        /// <summary>
        /// Quality score or matching confidence indicator for the payment application.
        /// Used internally to track automatic matching quality and validation results.
        /// </summary>
        field(30; Quality; Integer)
        {
            Caption = 'Quality';
        }
        /// <summary>
        /// Posting date of the applied ledger entry.
        /// Inherited from the ledger entry being applied to for reference and validation.
        /// </summary>
        field(31; "Posting Date"; Date)
        {
            Caption = 'Posting Date';
        }
        /// <summary>
        /// Document type of the applied ledger entry.
        /// Inherited from the ledger entry being applied (Invoice, Credit Memo, Payment, etc.).
        /// </summary>
        field(32; "Document Type"; Enum "Gen. Journal Document Type")
        {
            Caption = 'Document Type';
        }
        /// <summary>
        /// Document number of the applied ledger entry.
        /// Inherited from the ledger entry being applied for reference and audit purposes.
        /// </summary>
        field(33; "Document No."; Code[20])
        {
            Caption = 'Document No.';
        }
        /// <summary>
        /// Description of the applied ledger entry.
        /// Inherited from the ledger entry being applied for display and identification purposes.
        /// </summary>
        field(34; Description; Text[100])
        {
            Caption = 'Description';
        }
        /// <summary>
        /// Currency code of the applied ledger entry.
        /// Used for multi-currency payment applications and exchange rate calculations.
        /// </summary>
        field(35; "Currency Code"; Code[10])
        {
            Caption = 'Currency Code';
            TableRelation = Currency;
        }
        /// <summary>
        /// Due date of the applied ledger entry.
        /// Inherited from the ledger entry being applied for payment timing analysis.
        /// </summary>
        field(36; "Due Date"; Date)
        {
            Caption = 'Due Date';
        }
        /// <summary>
        /// External document number of the applied ledger entry.
        /// Inherited from the ledger entry being applied for external reference and audit.
        /// </summary>
        field(37; "External Document No."; Code[35])
        {
            Caption = 'External Document No.';
        }
        /// <summary>
        /// Confidence level of the automatic matching algorithm for this payment application.
        /// Indicates how certain the system is that this application is correct.
        /// </summary>
        field(50; "Match Confidence"; Enum "Bank Rec. Match Confidence")
        {
            Caption = 'Match Confidence';
            Editable = false;
            InitValue = "None";
        }
    }

    keys
    {
        key(Key1; "Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.", "Account Type", "Account No.", "Applies-to Entry No.")
        {
            Clustered = true;
        }
    }

    fieldgroups
    {
    }

    trigger OnDelete()
    begin
        "Applied Amount" := 0;
        "Applied Pmt. Discount" := 0;
        UpdateParentBankAccReconLine(true);
        ClearCustVendEntryApplicationData();
    end;

    trigger OnInsert()
    begin
        if "Applies-to Entry No." <> 0 then
            TestField("Applied Amount");

        CheckApplnIsSameAcc();
    end;

    trigger OnModify()
    begin
        TestField("Applied Amount");
        CheckApplnIsSameAcc();
    end;

    var
        CurrencyExchRate: Record "Currency Exchange Rate";
#pragma warning disable AA0470
        CurrencyMismatchErr: Label 'Currency codes on bank account %1 and ledger entry %2 do not match.';
        AmtCannotExceedErr: Label 'The Amount to Apply cannot exceed %1. This is because the Remaining Amount on the entry is %2 and the amount assigned to other statement lines is %3.';
#pragma warning restore AA0470
        CannotApplyStmtLineErr: Label 'You cannot apply to %1 %2 because the statement line already contains an application to %3 %4.', Comment = '%1 = Account Type, %2 = Account No., %3 = Account Type, %4 = Account No.';

    local procedure CheckApplnIsSameAcc()
    var
        ExistingAppliedPmtEntry: Record "Applied Payment Entry";
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
    begin
        if "Account No." = '' then
            exit;
        BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");
        ExistingAppliedPmtEntry.FilterAppliedPmtEntry(BankAccReconLine);
        if ExistingAppliedPmtEntry.FindFirst() then
            CheckCurrentMatchesExistingAppln(ExistingAppliedPmtEntry);
        if ExistingAppliedPmtEntry.FindLast() then
            CheckCurrentMatchesExistingAppln(ExistingAppliedPmtEntry);
    end;

    local procedure CheckCurrentMatchesExistingAppln(ExistingAppliedPmtEntry: Record "Applied Payment Entry")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckCurrentMatchesExistingAppln(ExistingAppliedPmtEntry, IsHandled);
        if IsHandled then
            exit;

        if ("Account Type" = ExistingAppliedPmtEntry."Account Type") and
           ("Account No." = ExistingAppliedPmtEntry."Account No.")
        then
            exit;

        Error(
          CannotApplyStmtLineErr,
          "Account Type", "Account No.",
          ExistingAppliedPmtEntry."Account Type",
          ExistingAppliedPmtEntry."Account No.");
    end;

    local procedure CheckEntryAmt()
    var
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
        AmtAvailableToApply: Decimal;
    begin
        if "Applied Amount" = 0 then
            exit;

        BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");
        // Amount should not exceed Remaining Amount
        AmtAvailableToApply := GetRemAmt() - GetAmtAppliedToOtherStmtLines();
        if "Applies-to Entry No." <> 0 then
            if "Applied Amount" > 0 then begin
                if not ("Applied Amount" in [0 .. AmtAvailableToApply]) then
                    Error(AmtCannotExceedErr, AmtAvailableToApply, GetRemAmt(), GetAmtAppliedToOtherStmtLines());
            end else
                if not ("Applied Amount" in [AmtAvailableToApply .. 0]) then
                    Error(AmtCannotExceedErr, AmtAvailableToApply, GetRemAmt(), GetAmtAppliedToOtherStmtLines());
    end;

    local procedure UpdateParentBankAccReconLine(IsDelete: Boolean)
    var
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
        NewAppliedAmt: Decimal;
    begin
        BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");

        NewAppliedAmt := GetTotalAppliedAmountInclPmtDisc(IsDelete);

        BankAccReconLine."Applied Entries" := GetNoOfAppliedEntries(IsDelete);

        if IsDelete then begin
            if NewAppliedAmt = 0 then begin
                BankAccReconLine.Validate("Applied Amount", 0);
                BankAccReconLine.Validate("Account No.", '')
            end
        end else
            if BankAccReconLine."Applied Amount" = 0 then begin
                BankAccReconLine.Validate("Account Type", "Account Type");
                BankAccReconLine.Validate("Account No.", "Account No.");
            end else
                CheckApplnIsSameAcc();

        BankAccReconLine.Validate("Applied Amount", NewAppliedAmt);
        OnUpdateParentBankAccReconLineOnBeforeBankAccReconLineModify(Rec, BankAccReconLine, IsDelete);
        BankAccReconLine.Modify();
    end;

    local procedure CheckCurrencyCombination()
    var
        CustLedgEntry: Record "Cust. Ledger Entry";
        VendorLedgEntry: Record "Vendor Ledger Entry";
        BankAccLedgEntry: Record "Bank Account Ledger Entry";
    begin
        if IsBankLCY() then
            exit;

        if "Applies-to Entry No." = 0 then
            exit;

        case "Account Type" of
            "Account Type"::Customer:
                begin
                    CustLedgEntry.Get("Applies-to Entry No.");
                    if not CurrencyMatches("Bank Account No.", CustLedgEntry."Currency Code", GetLCYCode()) then
                        Error(CurrencyMismatchErr, "Bank Account No.", "Applies-to Entry No.");
                end;
            "Account Type"::Vendor:
                begin
                    VendorLedgEntry.Get("Applies-to Entry No.");
                    if not CurrencyMatches("Bank Account No.", VendorLedgEntry."Currency Code", GetLCYCode()) then
                        Error(CurrencyMismatchErr, "Bank Account No.", "Applies-to Entry No.");
                end;
            "Account Type"::"Bank Account":
                begin
                    BankAccLedgEntry.Get("Applies-to Entry No.");
                    if not CurrencyMatches("Bank Account No.", BankAccLedgEntry."Currency Code", GetLCYCode()) then
                        Error(CurrencyMismatchErr, "Bank Account No.", "Applies-to Entry No.");
                end;
        end;

        OnAfterCheckCurrencyCombination(Rec);
    end;

    local procedure CurrencyMatches(BankAccNo: Code[20]; LedgEntryCurrCode: Code[10]; LCYCode: Code[10]): Boolean
    var
        BankAcc: Record "Bank Account";
        BankAccCurrCode: Code[10];
    begin
        BankAcc.Get(BankAccNo);
        BankAccCurrCode := BankAcc."Currency Code";
        if BankAccCurrCode = '' then
            BankAccCurrCode := LCYCode;
        if LedgEntryCurrCode = '' then
            LedgEntryCurrCode := LCYCode;
        exit(LedgEntryCurrCode = BankAccCurrCode);
    end;

    local procedure IsBankLCY(): Boolean
    var
        BankAcc: Record "Bank Account";
    begin
        BankAcc.Get("Bank Account No.");
        exit(BankAcc.IsInLocalCurrency());
    end;

    local procedure GetLCYCode(): Code[10]
    var
        GLSetup: Record "General Ledger Setup";
    begin
        GLSetup.Get();
        exit(GLSetup.GetCurrencyCode(''));
    end;

    /// <summary>
    /// Calculates the suggested amount to apply based on remaining amounts and payment application logic.
    /// Takes into account amounts already applied to other statement lines, payment discounts, and document type specific rules.
    /// For credit memos, applies special logic based on account type and amount direction.
    /// </summary>
    /// <returns>Decimal value representing the suggested amount to apply for this payment entry.</returns>
    procedure SuggestAmtToApply(): Decimal
    var
        RemAmtToApply: Decimal;
        LineRemAmtToApply: Decimal;
        IsHandled: Boolean;
        Result: Decimal;
    begin
        IsHandled := false;
        OnBeforeSuggestAmtToApply(Rec, IsHandled, Result);
        if IsHandled then
            exit(Result);

        RemAmtToApply := GetRemAmt() - GetAmtAppliedToOtherStmtLines();
        LineRemAmtToApply := GetStmtLineRemAmtToApply() + "Applied Pmt. Discount";

        if "Account Type" = "Account Type"::Customer then
            if (LineRemAmtToApply >= 0) and ("Document Type" = "Document Type"::"Credit Memo") then
                exit(RemAmtToApply);
        if "Account Type" = "Account Type"::Vendor then
            if (LineRemAmtToApply <= 0) and ("Document Type" = "Document Type"::"Credit Memo") then
                exit(RemAmtToApply);

        exit(
          AbsMin(
            RemAmtToApply,
            LineRemAmtToApply));
    end;

    /// <summary>
    /// Calculates the suggested payment discount amount to apply based on discount terms and timing.
    /// Evaluates payment discount eligibility using due dates, tolerance periods, and applied amounts.
    /// </summary>
    /// <param name="UseAppliedAmt">If true, uses the applied amount for discount calculation; otherwise uses suggested amount.</param>
    /// <returns>Decimal value representing the suggested payment discount amount to apply.</returns>
    procedure SuggestDiscToApply(UseAppliedAmt: Boolean): Decimal
    var
        PmtDiscDueDate: Date;
        PmtDiscToleranceDate: Date;
        RemPmtDiscPossible: Decimal;
    begin
        if InclPmtDisc(UseAppliedAmt) then begin
            GetDiscInfo(PmtDiscDueDate, PmtDiscToleranceDate, RemPmtDiscPossible);
            exit(RemPmtDiscPossible + GetAcceptedPmtTolerance());
        end;
        exit(GetAcceptedPmtTolerance());
    end;

    /// <summary>
    /// Retrieves payment discount information including due dates and remaining discount amounts.
    /// Populates payment discount details from customer or vendor ledger entries based on account type.
    /// </summary>
    /// <param name="PmtDiscDueDate">Payment discount due date when discount expires.</param>
    /// <param name="PmtDiscToleranceDate">Payment discount tolerance date for extended discount period.</param>
    /// <param name="RemPmtDiscPossible">Remaining payment discount amount available for application.</param>
    procedure GetDiscInfo(var PmtDiscDueDate: Date; var PmtDiscToleranceDate: Date; var RemPmtDiscPossible: Decimal)
    begin
        PmtDiscDueDate := 0D;
        RemPmtDiscPossible := 0;

        if "Account No." = '' then
            exit;
        if "Applies-to Entry No." = 0 then
            exit;

        case "Account Type" of
            "Account Type"::Customer:
                GetCustLedgEntryDiscInfo(PmtDiscDueDate, PmtDiscToleranceDate, RemPmtDiscPossible);
            "Account Type"::Vendor:
                GetVendLedgEntryDiscInfo(PmtDiscDueDate, PmtDiscToleranceDate, RemPmtDiscPossible);
        end;

        OnAfterGetDiscInfo(Rec, PmtDiscDueDate, PmtDiscToleranceDate, RemPmtDiscPossible);
    end;

    local procedure GetCustLedgEntryDiscInfo(var PmtDiscDueDate: Date; var PmtDiscToleranceDate: Date; var RemPmtDiscPossible: Decimal)
    var
        CustLedgEntry: Record "Cust. Ledger Entry";
    begin
        CustLedgEntry.Get("Applies-to Entry No.");
        PmtDiscDueDate := CustLedgEntry."Pmt. Discount Date";
        PmtDiscToleranceDate := CustLedgEntry."Pmt. Disc. Tolerance Date";
        if IsBankLCY() and (CustLedgEntry."Currency Code" <> '') then
            RemPmtDiscPossible :=
              Round(CustLedgEntry."Remaining Pmt. Disc. Possible" / CustLedgEntry."Adjusted Currency Factor")
        else
            RemPmtDiscPossible := CustLedgEntry."Remaining Pmt. Disc. Possible";
    end;

    local procedure GetVendLedgEntryDiscInfo(var PmtDiscDueDate: Date; var PmtDiscToleranceDate: Date; var RemPmtDiscPossible: Decimal)
    var
        VendLedgEntry: Record "Vendor Ledger Entry";
    begin
        VendLedgEntry.Get("Applies-to Entry No.");
        PmtDiscDueDate := VendLedgEntry."Pmt. Discount Date";
        PmtDiscToleranceDate := VendLedgEntry."Pmt. Disc. Tolerance Date";
        VendLedgEntry.CalcFields("Amount (LCY)", Amount);
        if IsBankLCY() and (VendLedgEntry."Currency Code" <> '') then
            RemPmtDiscPossible :=
              Round(VendLedgEntry."Remaining Pmt. Disc. Possible" / VendLedgEntry."Adjusted Currency Factor")
        else
            RemPmtDiscPossible := VendLedgEntry."Remaining Pmt. Disc. Possible";
    end;

    /// <summary>
    /// Calculates the remaining amount available for application from the target ledger entry.
    /// Returns different amounts based on account type (Customer, Vendor, Employee, Bank Account) and entry status.
    /// For unassigned entries, returns the statement line remaining amount.
    /// </summary>
    /// <returns>Decimal value representing the remaining amount available for application.</returns>
    procedure GetRemAmt() Result: Decimal
    begin
        if "Account No." = '' then
            exit(0);
        if "Applies-to Entry No." = 0 then
            exit(GetStmtLineRemAmtToApply());

        case "Account Type" of
            "Account Type"::Customer:
                exit(GetCustLedgEntryRemAmt());
            "Account Type"::Vendor:
                exit(GetVendLedgEntryRemAmt());
            "Account Type"::Employee:
                exit(GetEmployeeLedgEntryRemAmt());
            "Account Type"::"Bank Account":
                exit(GetBankAccLedgEntryRemAmt());
        end;

        OnAfterGetRemAmt(Rec, Result);
    end;

    local procedure GetAcceptedPmtTolerance() Result: Decimal
    begin
        if ("Account No." = '') or ("Applies-to Entry No." = 0) then
            exit(0);
        case "Account Type" of
            "Account Type"::Customer:
                exit(GetCustLedgEntryPmtTolAmt());
            "Account Type"::Vendor:
                exit(GetVendLedgEntryPmtTolAmt());
        end;

        OnAfterGetAcceptedPmtTolerance(Rec, Result);
    end;

    local procedure GetCustLedgEntryRemAmt() Result: Decimal
    var
        CustLedgEntry: Record "Cust. Ledger Entry";
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnGetCustLedgEntryRemAmtOnBeforeCalcFields(Rec, IsHandled, Result);
        if IsHandled then
            exit(Result);

        CustLedgEntry.Get("Applies-to Entry No.");
        if IsBankLCY() and (CustLedgEntry."Currency Code" <> '') then begin
            BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");
            CustLedgEntry.CalcFields("Remaining Amount");
            exit(
                CurrencyExchRate.ExchangeAmount(
                    CustLedgEntry."Remaining Amount", CustLedgEntry."Currency Code", '', BankAccReconLine."Transaction Date"));
        end;
        CustLedgEntry.CalcFields("Remaining Amount");
        exit(CustLedgEntry."Remaining Amount");
    end;

    local procedure GetVendLedgEntryRemAmt() Result: Decimal
    var
        VendLedgEntry: Record "Vendor Ledger Entry";
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnGetVendLedgEntryRemAmtOnBeforeCalcFields(Rec, IsHandled, Result);
        if IsHandled then
            exit(Result);

        VendLedgEntry.Get("Applies-to Entry No.");
        if IsBankLCY() and (VendLedgEntry."Currency Code" <> '') then begin
            BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");
            VendLedgEntry.CalcFields("Remaining Amount");
            exit(
                CurrencyExchRate.ExchangeAmount(
                    VendLedgEntry."Remaining Amount", VendLedgEntry."Currency Code", '', BankAccReconLine."Transaction Date"));
        end;
        VendLedgEntry.CalcFields("Remaining Amount");
        exit(VendLedgEntry."Remaining Amount");
    end;

    local procedure GetEmployeeLedgEntryRemAmt(): Decimal
    var
        EmployeeLedgEntry: Record "Employee Ledger Entry";
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
    begin
        EmployeeLedgEntry.Get("Applies-to Entry No.");
        if IsBankLCY() and (EmployeeLedgEntry."Currency Code" <> '') then begin
            BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");
            EmployeeLedgEntry.CalcFields("Remaining Amount");
            exit(
                CurrencyExchRate.ExchangeAmount(
                    EmployeeLedgEntry."Remaining Amount", EmployeeLedgEntry."Currency Code", '', BankAccReconLine."Transaction Date"));
        end;
        EmployeeLedgEntry.CalcFields("Remaining Amount");
        exit(EmployeeLedgEntry."Remaining Amount");
    end;

    local procedure GetBankAccLedgEntryRemAmt(): Decimal
    var
        BankAccLedgEntry: Record "Bank Account Ledger Entry";
    begin
        BankAccLedgEntry.Get("Applies-to Entry No.");
        if IsBankLCY() then
            exit(
              Round(
                BankAccLedgEntry."Remaining Amount" *
                BankAccLedgEntry."Amount (LCY)" / BankAccLedgEntry.Amount));
        exit(BankAccLedgEntry."Remaining Amount");
    end;

    local procedure GetCustLedgEntryPmtTolAmt(): Decimal
    var
        BankAccountReconciliationLine: Record "Bank Acc. Reconciliation Line";
        CustLedgEntry: Record "Cust. Ledger Entry";
    begin
        BankAccountReconciliationLine.Get(Rec."Statement Type", Rec."Bank Account No.", Rec."Statement No.", Rec."Statement Line No.");
        CustLedgEntry.SetLoadFields("Applies-to ID", "Accepted Payment Tolerance");
        CustLedgEntry.SetRange("Applies-to ID", BankAccountReconciliationLine.GetAppliesToID());
        if CustLedgEntry.IsEmpty() then
            exit(0);
        CustLedgEntry.CalcSums("Accepted Payment Tolerance");
        exit(CustLedgEntry."Accepted Payment Tolerance");
    end;

    local procedure GetVendLedgEntryPmtTolAmt(): Decimal
    var
        BankAccountReconciliationLine: Record "Bank Acc. Reconciliation Line";
        VendorLedgerEntry: Record "Vendor Ledger Entry";
    begin
        BankAccountReconciliationLine.Get(Rec."Statement Type", Rec."Bank Account No.", Rec."Statement No.", Rec."Statement Line No.");
        VendorLedgerEntry.SetLoadFields("Applies-to ID", "Accepted Payment Tolerance");
        VendorLedgerEntry.SetRange("Applies-to ID", BankAccountReconciliationLine.GetAppliesToID());
        if VendorLedgerEntry.IsEmpty() then
            exit(0);
        VendorLedgerEntry.CalcSums("Accepted Payment Tolerance");
        exit(VendorLedgerEntry."Accepted Payment Tolerance");
    end;

    /// <summary>
    /// Calculates the remaining amount available on the statement line for payment application.
    /// Considers the difference amount on the bank reconciliation line and any previously applied amounts.
    /// Returns zero if the line difference is already fully allocated.
    /// </summary>
    /// <returns>Decimal value representing the remaining amount available for application on the statement line.</returns>
    procedure GetStmtLineRemAmtToApply(): Decimal
    var
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
    begin
        BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");

        if BankAccReconLine.Difference = 0 then
            exit(0);

        exit(BankAccReconLine.Difference + GetOldAppliedAmtInclDisc());
    end;

    local procedure GetOldAppliedAmtInclDisc(): Decimal
    var
        OldAppliedPmtEntry: Record "Applied Payment Entry";
    begin
        OldAppliedPmtEntry := Rec;
        if not OldAppliedPmtEntry.Find() then
            exit(0);
        exit(OldAppliedPmtEntry."Applied Amount" - OldAppliedPmtEntry."Applied Pmt. Discount");
    end;

    local procedure IsAcceptedPmtDiscTolerance() Result: Boolean
    begin
        if ("Account No." = '') or ("Applies-to Entry No." = 0) then
            exit(false);
        case "Account Type" of
            "Account Type"::Customer:
                exit(IsCustLedgEntryPmtDiscTol());
            "Account Type"::Vendor:
                exit(IsVendLedgEntryPmtDiscTol());
        end;

        OnAfterIsAcceptedPmtDiscTolerance(rec, Result);
    end;

    local procedure IsCustLedgEntryPmtDiscTol(): Boolean
    var
        CustLedgEntry: Record "Cust. Ledger Entry";
    begin
        CustLedgEntry.Get("Applies-to Entry No.");
        exit(CustLedgEntry."Accepted Pmt. Disc. Tolerance");
    end;

    local procedure IsVendLedgEntryPmtDiscTol(): Boolean
    var
        VendLedgEntry: Record "Vendor Ledger Entry";
    begin
        VendLedgEntry.Get("Applies-to Entry No.");
        exit(VendLedgEntry."Accepted Pmt. Disc. Tolerance");
    end;

    local procedure AbsMin(Amt1: Decimal; Amt2: Decimal): Decimal
    begin
        if Abs(Amt1) < Abs(Amt2) then
            exit(Amt1);
        exit(Amt2)
    end;

    local procedure GetAccInfo()
    begin
        if "Account No." = '' then
            exit;

        case "Account Type" of
            "Account Type"::Customer:
                GetCustInfo();
            "Account Type"::Vendor:
                GetVendInfo();
            "Account Type"::Employee:
                GetEmployeeInfo();
            "Account Type"::"Bank Account":
                GetBankAccInfo();
            "Account Type"::"G/L Account":
                GetGLAccInfo();
        end;
    end;

    local procedure GetCustInfo()
    var
        Cust: Record Customer;
    begin
        Cust.Get("Account No.");
        Description := Cust.Name;
    end;

    local procedure GetVendInfo()
    var
        Vend: Record Vendor;
    begin
        Vend.Get("Account No.");
        Description := Vend.Name;
    end;

    local procedure GetEmployeeInfo()
    var
        Employee: Record Employee;
    begin
        Employee.Get("Account No.");
        Description := Employee.FullName();
    end;

    local procedure GetBankAccInfo()
    var
        BankAcc: Record "Bank Account";
    begin
        BankAcc.Get("Account No.");
        Description := BankAcc.Name;
        "Currency Code" := BankAcc."Currency Code";
    end;

    local procedure GetGLAccInfo()
    var
        GLAcc: Record "G/L Account";
    begin
        GLAcc.Get("Account No.");
        Description := GLAcc.Name;
    end;

    /// <summary>
    /// Retrieves and populates ledger entry information based on the applied entry number and account type.
    /// Loads relevant details from customer, vendor, employee, or bank account ledger entries
    /// into the current applied payment entry record fields.
    /// </summary>
    procedure GetLedgEntryInfo()
    begin
        if "Applies-to Entry No." = 0 then
            exit;

        case "Account Type" of
            "Account Type"::Customer:
                GetCustLedgEntryInfo();
            "Account Type"::Vendor:
                GetVendLedgEntryInfo();
            "Account Type"::Employee:
                GetEmployeeLedgEntryInfo();
            "Account Type"::"Bank Account":
                GetBankAccLedgEntryInfo();
        end;

        OnAfterGetLedgEntryInfo(Rec);
    end;

    internal procedure RunOnAfterGetLedgEntryInfo()
    begin
        OnAfterGetLedgEntryInfo(Rec);
    end;

    local procedure GetCustLedgEntryInfo()
    var
        CustLedgEntry: Record "Cust. Ledger Entry";
    begin
        CustLedgEntry.Get("Applies-to Entry No.");
        Description := CustLedgEntry.Description;
        "Posting Date" := CustLedgEntry."Posting Date";
        "Due Date" := CustLedgEntry."Due Date";
        "Document Type" := CustLedgEntry."Document Type";
        "Document No." := CustLedgEntry."Document No.";
        "External Document No." := CustLedgEntry."External Document No.";
        "Currency Code" := CustLedgEntry."Currency Code";
    end;

    local procedure GetVendLedgEntryInfo()
    var
        VendLedgEntry: Record "Vendor Ledger Entry";
    begin
        VendLedgEntry.Get("Applies-to Entry No.");
        Description := VendLedgEntry.Description;
        "Posting Date" := VendLedgEntry."Posting Date";
        "Due Date" := VendLedgEntry."Due Date";
        "Document Type" := VendLedgEntry."Document Type";
        "Document No." := VendLedgEntry."Document No.";
        "External Document No." := VendLedgEntry."External Document No.";
        "Currency Code" := VendLedgEntry."Currency Code";
    end;

    local procedure GetEmployeeLedgEntryInfo()
    var
        EmployeeLedgEntry: Record "Employee Ledger Entry";
    begin
        EmployeeLedgEntry.Get("Applies-to Entry No.");
        Description := EmployeeLedgEntry.Description;
        "Posting Date" := EmployeeLedgEntry."Posting Date";
        "Document Type" := EmployeeLedgEntry."Document Type";
        "Document No." := EmployeeLedgEntry."Document No.";
        "Currency Code" := EmployeeLedgEntry."Currency Code";
    end;

    local procedure GetBankAccLedgEntryInfo()
    var
        BankAccLedgEntry: Record "Bank Account Ledger Entry";
    begin
        BankAccLedgEntry.Get("Applies-to Entry No.");
        Description := BankAccLedgEntry.Description;
        "Posting Date" := BankAccLedgEntry."Posting Date";
        "Due Date" := 0D;
        "Document Type" := BankAccLedgEntry."Document Type";
        "Document No." := BankAccLedgEntry."Document No.";
        "External Document No." := BankAccLedgEntry."External Document No.";
        "Currency Code" := BankAccLedgEntry."Currency Code";
    end;

    procedure GetAmtAppliedToOtherStmtLines(): Decimal
    var
        AppliedPmtEntry: Record "Applied Payment Entry";
    begin
        if "Applies-to Entry No." = 0 then
            exit(0);

        AppliedPmtEntry := Rec;
        AppliedPmtEntry.FilterEntryAppliedToOtherStmtLines();
        AppliedPmtEntry.CalcSums("Applied Amount");
        exit(AppliedPmtEntry."Applied Amount");
    end;

    procedure FilterEntryAppliedToOtherStmtLines()
    begin
        Reset();
        SetRange("Statement Type", "Statement Type");
        SetRange("Bank Account No.", "Bank Account No.");
        SetRange("Statement No.", "Statement No.");
        SetFilter("Statement Line No.", '<>%1', "Statement Line No.");
        SetRange("Account Type", "Account Type");
        SetRange("Account No.", "Account No.");
        SetRange("Applies-to Entry No.", "Applies-to Entry No.");
        OnAfterFilterEntryAppliedToOtherStmtLines(Rec);
    end;

    /// <summary>
    /// Sets filters on the applied payment entry table to match a specific bank reconciliation line.
    /// Filters by statement type, bank account number, statement number, and statement line number
    /// to retrieve all payment applications for the specified reconciliation line.
    /// </summary>
    /// <param name="BankAccReconLine">Bank reconciliation line record to filter applied payment entries for.</param>
    procedure FilterAppliedPmtEntry(BankAccReconLine: Record "Bank Acc. Reconciliation Line")
    begin
        Reset();
        SetRange("Statement Type", BankAccReconLine."Statement Type");
        SetRange("Bank Account No.", BankAccReconLine."Bank Account No.");
        SetRange("Statement No.", BankAccReconLine."Statement No.");
        SetRange("Statement Line No.", BankAccReconLine."Statement Line No.");
    end;

    /// <summary>
    /// Checks if applied payment entry lines exist for a specific bank reconciliation line.
    /// Uses FilterAppliedPmtEntry to set appropriate filters and then checks for record existence.
    /// </summary>
    /// <param name="BankAccReconLine">Bank reconciliation line record to check for applied payment entries.</param>
    /// <returns>True if applied payment entries exist for the specified reconciliation line; false otherwise.</returns>
    procedure AppliedPmtEntryLinesExist(BankAccReconLine: Record "Bank Acc. Reconciliation Line"): Boolean
    begin
        FilterAppliedPmtEntry(BankAccReconLine);
        exit(FindSet());
    end;

    /// <summary>
    /// Transfers key identification fields from a bank reconciliation line to this applied payment entry.
    /// Copies statement type, bank account number, statement number, and statement line number
    /// to establish the relationship between the payment entry and its source reconciliation line.
    /// </summary>
    /// <param name="BankAccReconLine">Bank reconciliation line record to transfer identification fields from.</param>
    procedure TransferFromBankAccReconLine(BankAccReconLine: Record "Bank Acc. Reconciliation Line")
    begin
        "Statement Type" := BankAccReconLine."Statement Type";
        "Bank Account No." := BankAccReconLine."Bank Account No.";
        "Statement No." := BankAccReconLine."Statement No.";
        "Statement Line No." := BankAccReconLine."Statement Line No.";
    end;

    /// <summary>
    /// Creates an applied payment entry from bank statement matching buffer results and text mapper amounts.
    /// Initializes a new payment application based on automatic matching results, including account details,
    /// applied amounts, and quality scoring. Handles both regular matching and text-to-account mapping scenarios.
    /// </summary>
    /// <param name="BankAccReconLine">Bank reconciliation line that the payment is being applied to.</param>
    /// <param name="BankStmtMatchingBuffer">Matching buffer containing the identified payment candidate details.</param>
    /// <param name="TextMapperAmount">Amount determined by text-to-account mapping rules; zero if not applicable.</param>
    /// <param name="EntryNo">Entry number of the target ledger entry for application.</param>
    procedure ApplyFromBankStmtMatchingBuf(BankAccReconLine: Record "Bank Acc. Reconciliation Line"; BankStmtMatchingBuffer: Record "Bank Statement Matching Buffer"; TextMapperAmount: Decimal; EntryNo: Integer)
    var
        BankPmtApplRule: Record "Bank Pmt. Appl. Rule";
    begin
        Init();
        TransferFromBankAccReconLine(BankAccReconLine);
        Validate("Account Type", BankStmtMatchingBuffer."Account Type");
        Validate("Account No.", BankStmtMatchingBuffer."Account No.");
        if (EntryNo < 0) and (not BankStmtMatchingBuffer."One to Many Match") then begin // text mapper
            Validate("Applies-to Entry No.", 0);
            Validate("Applied Amount", TextMapperAmount);
        end else
            Validate("Applies-to Entry No.", EntryNo);
        Validate(Quality, BankStmtMatchingBuffer.Quality);
        Validate("Match Confidence", BankPmtApplRule.GetMatchConfidence(BankStmtMatchingBuffer.Quality));
        OnApplyFromBankStmtMatchingBufOnBeforeInsert(BankAccReconLine, BankStmtMatchingBuffer, TextMapperAmount, EntryNo, Rec);
        Insert(true);
    end;

    local procedure InclPmtDisc(UseAppliedAmt: Boolean): Boolean
    var
        BankAccReconLine: Record "Bank Acc. Reconciliation Line";
        PaymentToleranceManagement: Codeunit "Payment Tolerance Management";
        UsePmtDisc: Boolean;
        AmtApplied: Decimal;
        PmtDiscDueDate: Date;
        PmtDiscToleranceDate: Date;
        RemPmtDiscPossible: Decimal;
    begin
        GetDiscInfo(PmtDiscDueDate, PmtDiscToleranceDate, RemPmtDiscPossible);
        if not ("Document Type" in ["Document Type"::"Credit Memo", "Document Type"::Invoice]) then
            exit(false);
        BankAccReconLine.Get("Statement Type", "Bank Account No.", "Statement No.", "Statement Line No.");
        if (BankAccReconLine."Account Type" = BankAccReconLine."Account Type"::"G/L Account") or (BankAccReconLine."Account No." = '') then begin
            BankAccReconLine."Account Type" := "Account Type";
            BankAccReconLine."Account No." := "Account No.";
        end;
        UsePmtDisc := (BankAccReconLine."Transaction Date" <= PmtDiscDueDate) and (RemPmtDiscPossible <> 0);
        if UseAppliedAmt then
            PaymentToleranceManagement.PmtTolPmtReconJnl(BankAccReconLine);
        if (not UsePmtDisc) and (not IsAcceptedPmtDiscTolerance()) then
            exit(false);

        if UseAppliedAmt then
            AmtApplied := "Applied Amount" + GetAmtAppliedToOtherStmtLines()
        else
            AmtApplied := BankAccReconLine.Difference + GetOldAppliedAmtInclDisc() + GetAmtAppliedToOtherStmtLines();

        exit(Abs(AmtApplied) >= Abs(GetRemAmt() - RemPmtDiscPossible - GetAcceptedPmtTolerance()));
    end;

    procedure GetTotalAppliedAmountInclPmtDisc(IsDelete: Boolean): Decimal
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
        TotalAmountIncludingPmtDisc: Decimal;
    begin
        AppliedPaymentEntry.SetRange("Statement Type", "Statement Type");
        AppliedPaymentEntry.SetRange("Statement No.", "Statement No.");
        AppliedPaymentEntry.SetRange("Statement Line No.", "Statement Line No.");
        AppliedPaymentEntry.SetRange("Bank Account No.", "Bank Account No.");
        AppliedPaymentEntry.SetRange("Account Type", "Account Type");
        AppliedPaymentEntry.SetRange("Account No.", "Account No.");
        AppliedPaymentEntry.SetFilter("Applies-to Entry No.", '<>%1', "Applies-to Entry No.");
        OnAfterAppliedPaymentEntryFilterOnGetTotalAppliedAmountInclPmtDisc(AppliedPaymentEntry, Rec);

        if IsDelete then
            TotalAmountIncludingPmtDisc := 0
        else
            TotalAmountIncludingPmtDisc := "Applied Amount" - "Applied Pmt. Discount";

        if AppliedPaymentEntry.FindSet() then
            repeat
                TotalAmountIncludingPmtDisc += AppliedPaymentEntry."Applied Amount";
                TotalAmountIncludingPmtDisc -= AppliedPaymentEntry."Applied Pmt. Discount";
            until AppliedPaymentEntry.Next() = 0;

        exit(TotalAmountIncludingPmtDisc);
    end;

    local procedure GetNoOfAppliedEntries(IsDelete: Boolean): Decimal
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
    begin
        AppliedPaymentEntry.SetRange("Statement Type", "Statement Type");
        AppliedPaymentEntry.SetRange("Statement No.", "Statement No.");
        AppliedPaymentEntry.SetRange("Statement Line No.", "Statement Line No.");
        AppliedPaymentEntry.SetRange("Bank Account No.", "Bank Account No.");
        AppliedPaymentEntry.SetRange("Account Type", "Account Type");
        AppliedPaymentEntry.SetRange("Account No.", "Account No.");
        AppliedPaymentEntry.SetFilter("Applies-to Entry No.", '<>%1', "Applies-to Entry No.");
        OnAfterAppliedPaymentEntryFiltersGetNoOfAppliedEntries(AppliedPaymentEntry, Rec);

        if IsDelete then
            exit(AppliedPaymentEntry.Count);

        exit(AppliedPaymentEntry.Count + 1);
    end;

    procedure UpdatePaymentDiscount(PaymentDiscountAmount: Decimal)
    var
        AppliedPaymentEntry: Record "Applied Payment Entry";
    begin
        // Payment discount must go to last entry only because of posting
        AppliedPaymentEntry.SetRange("Statement Type", "Statement Type");
        AppliedPaymentEntry.SetRange("Bank Account No.", "Bank Account No.");
        AppliedPaymentEntry.SetRange("Statement No.", "Statement No.");
        AppliedPaymentEntry.SetRange("Account Type", "Account Type");
        AppliedPaymentEntry.SetRange("Applies-to Entry No.", "Applies-to Entry No.");
        AppliedPaymentEntry.SetFilter("Applied Pmt. Discount", '<>0');

        if AppliedPaymentEntry.FindFirst() then
            AppliedPaymentEntry.RemovePaymentDiscount();

        if PaymentDiscountAmount = 0 then
            exit;

        AppliedPaymentEntry.SetRange("Applied Pmt. Discount");

        if AppliedPaymentEntry.FindLast() then
            if "Statement Line No." < AppliedPaymentEntry."Statement Line No." then begin
                AppliedPaymentEntry.SetPaymentDiscount(PaymentDiscountAmount, true);
                exit;
            end;

        SetPaymentDiscount(PaymentDiscountAmount, false);
    end;

    procedure SetPaymentDiscount(PaymentDiscountAmount: Decimal; DifferentLineThanCurrent: Boolean)
    begin
        Validate("Applied Pmt. Discount", PaymentDiscountAmount);

        if DifferentLineThanCurrent then begin
            "Applied Amount" += "Applied Pmt. Discount";
            Modify(true);
        end;
    end;

    procedure RemovePaymentDiscount()
    begin
        "Applied Amount" := "Applied Amount" - "Applied Pmt. Discount";
        "Applied Pmt. Discount" := 0;
        Modify(true);
    end;

    local procedure ClearCustVendEntryApplicationData()
    begin
        if "Applies-to Entry No." = 0 then
            exit;

        case "Account Type" of
            "Account Type"::Customer:
                ClearCustApplicationData("Applies-to Entry No.");
            "Account Type"::Vendor:
                ClearVendApplicationData("Applies-to Entry No.");
            "Account Type"::Employee:
                ClearEmployeeApplicationData("Applies-to Entry No.");
        end;
    end;

    local procedure ClearCustApplicationData(EntryNo: Integer)
    var
        CustLedgEntry: Record "Cust. Ledger Entry";
    begin
        CustLedgEntry.Get(EntryNo);
        CustLedgEntry."Accepted Pmt. Disc. Tolerance" := false;
        CustLedgEntry."Accepted Payment Tolerance" := 0;
        CustLedgEntry."Amount to Apply" := 0;
        CustLedgEntry."Applies-to ID" := '';
        CODEUNIT.Run(CODEUNIT::"Cust. Entry-Edit", CustLedgEntry);
    end;

    local procedure ClearVendApplicationData(EntryNo: Integer)
    var
        VendLedgEntry: Record "Vendor Ledger Entry";
    begin
        VendLedgEntry.Get(EntryNo);
        VendLedgEntry."Accepted Pmt. Disc. Tolerance" := false;
        VendLedgEntry."Accepted Payment Tolerance" := 0;
        VendLedgEntry."Amount to Apply" := 0;
        VendLedgEntry."Applies-to ID" := '';
        CODEUNIT.Run(CODEUNIT::"Vend. Entry-Edit", VendLedgEntry);
    end;

    local procedure ClearEmployeeApplicationData(EntryNo: Integer)
    var
        EmployeeLedgEntry: Record "Employee Ledger Entry";
    begin
        EmployeeLedgEntry.Get(EntryNo);
        EmployeeLedgEntry."Amount to Apply" := 0;
        EmployeeLedgEntry."Applies-to ID" := '';
        CODEUNIT.Run(CODEUNIT::"Empl. Entry-Edit", EmployeeLedgEntry);
    end;

    procedure CalcAmountToApply(PostingDate: Date) AmountToApply: Decimal
    var
        BankAccount: Record "Bank Account";
        CurrExchRate: Record "Currency Exchange Rate";
        CustLedgerEntry: Record "Cust. Ledger Entry";
        VendorLedgerEntry: Record "Vendor Ledger Entry";
        EmployeeLedgerEntry: Record "Employee Ledger Entry";
        RemainingAmount: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcAmountToApply(Rec, PostingDate, AmountToApply, IsHandled);
        if IsHandled then
            exit(AmountToApply);

        BankAccount.Get("Bank Account No.");
        if BankAccount.IsInLocalCurrency() then begin
            AmountToApply :=
              CurrExchRate.ExchangeAmount("Applied Amount", '', "Currency Code", PostingDate);
            case "Account Type" of
                "Account Type"::Customer:
                    begin
                        CustLedgerEntry.Get("Applies-to Entry No.");
                        CustLedgerEntry.CalcFields("Remaining Amount");
                        RemainingAmount := CustLedgerEntry."Remaining Amount";
                    end;
                "Account Type"::Vendor:
                    begin
                        VendorLedgerEntry.Get("Applies-to Entry No.");
                        VendorLedgerEntry.CalcFields("Remaining Amount");
                        RemainingAmount := VendorLedgerEntry."Remaining Amount";
                    end;
                "Account Type"::Employee:
                    begin
                        EmployeeLedgerEntry.Get("Applies-to Entry No.");
                        EmployeeLedgerEntry.CalcFields("Remaining Amount");
                        RemainingAmount := EmployeeLedgerEntry."Remaining Amount";
                    end;
            end;
            if Abs(AmountToApply) > Abs(RemainingAmount) then
                AmountToApply := RemainingAmount;
        end else
            exit("Applied Amount");
        exit(AmountToApply);
    end;

    /// <summary>
    /// Event raised after checking the currency combination for applied payment entries.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record being validated.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterCheckCurrencyCombination(var AppliedPaymentEntry: Record "Applied Payment Entry")
    begin
    end;

    /// <summary>
    /// Event raised after filtering applied payment entries when getting the total applied amount including payment discount.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record being filtered.</param>
    /// <param name="AppliedPaymentEntryRec">The reference applied payment entry record.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterAppliedPaymentEntryFilterOnGetTotalAppliedAmountInclPmtDisc(var AppliedPaymentEntry: Record "Applied Payment Entry"; AppliedPaymentEntryRec: Record "Applied Payment Entry")
    begin
    end;

    /// <summary>
    /// Event raised after filtering entries that are applied to other statement lines.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record being filtered.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterFilterEntryAppliedToOtherStmtLines(var AppliedPaymentEntry: Record "Applied Payment Entry")
    begin
    end;

    /// <summary>
    /// Event raised after getting discount information for an applied payment entry.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="PmtDiscDueDate">The payment discount due date.</param>
    /// <param name="PmtDiscToleranceDate">The payment discount tolerance date.</param>
    /// <param name="RemPmtDiscPossible">The remaining payment discount possible amount.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterGetDiscInfo(var AppliedPaymentEntry: Record "Applied Payment Entry"; var PmtDiscDueDate: Date; var PmtDiscToleranceDate: Date; var RemPmtDiscPossible: Decimal)
    begin
    end;

    /// <summary>
    /// Event raised before checking if the current match exists for an existing application.
    /// </summary>
    /// <param name="ExistingAppliedPmtEntry">The existing applied payment entry record.</param>
    /// <param name="IsHandled">Indicates whether the event has been handled.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckCurrentMatchesExistingAppln(ExistingAppliedPmtEntry: Record "Applied Payment Entry"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Event raised after applying filters on applied payment entries to get the number of applied entries.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record being filtered.</param>
    /// <param name="AppliedPaymentEntryRec">The reference applied payment entry record.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterAppliedPaymentEntryFiltersGetNoOfAppliedEntries(var AppliedPaymentEntry: Record "Applied Payment Entry"; AppliedPaymentEntryRec: Record "Applied Payment Entry")
    begin
    end;

    /// <summary>
    /// Event raised after getting the accepted payment tolerance for an applied payment entry.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="Result">The calculated payment tolerance result.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterGetAcceptedPmtTolerance(AppliedPaymentEntry: Record "Applied Payment Entry"; var Result: Decimal)
    begin
    end;

    /// <summary>
    /// Event raised after checking if the payment discount tolerance is accepted for an applied payment entry.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="Result">Indicates whether the payment discount tolerance is accepted.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterIsAcceptedPmtDiscTolerance(AppliedPaymentEntry: Record "Applied Payment Entry"; var Result: Boolean)
    begin
    end;

    /// <summary>
    /// Event raised after looking up the applies-to entry number for an applied payment entry.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record being updated.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterLookupAppliesToEntryNo(var AppliedPaymentEntry: Record "Applied Payment Entry")
    begin
    end;

    /// <summary>
    /// Event raised after getting ledger entry information for an applied payment entry.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record being updated.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterGetLedgEntryInfo(var AppliedPaymentEntry: Record "Applied Payment Entry")
    begin
    end;

    /// <summary>
    /// Event raised after getting the remaining amount for an applied payment entry.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="Result">The calculated remaining amount.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterGetRemAmt(AppliedPaymentEntry: Record "Applied Payment Entry"; var Result: Decimal)
    begin
    end;

    /// <summary>
    /// Event raised before calculating the amount to apply for an applied payment entry.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="PostingDate">The posting date for the calculation.</param>
    /// <param name="AmountToApply">The calculated amount to apply.</param>
    /// <param name="IsHandled">Indicates whether the event has been handled.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcAmountToApply(var AppliedPaymentEntry: Record "Applied Payment Entry"; PostingDate: Date; var AmountToApply: Decimal; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Event raised when updating the parent bank account reconciliation line before modifying the bank account reconciliation line record.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="BankAccReconciliationLine">The bank account reconciliation line record being modified.</param>
    /// <param name="IsDelete">Indicates whether this is a delete operation.</param>
    [IntegrationEvent(false, false)]
    local procedure OnUpdateParentBankAccReconLineOnBeforeBankAccReconLineModify(var AppliedPaymentEntry: Record "Applied Payment Entry"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; IsDelete: Boolean)
    begin
    end;

    /// <summary>
    /// Event raised when applying from bank statement matching buffer before inserting the applied payment entry.
    /// </summary>
    /// <param name="BankAccReconLine">The bank account reconciliation line record.</param>
    /// <param name="BankStmtMatchingBuffer">The bank statement matching buffer record.</param>
    /// <param name="TextMapperAmount">The text mapper amount.</param>
    /// <param name="EntryNo">The entry number.</param>
    /// <param name="AppliedPaymentEntry">The applied payment entry record being inserted.</param>
    [IntegrationEvent(false, false)]
    local procedure OnApplyFromBankStmtMatchingBufOnBeforeInsert(BankAccReconLine: Record "Bank Acc. Reconciliation Line"; BankStmtMatchingBuffer: Record "Bank Statement Matching Buffer"; TextMapperAmount: Decimal; EntryNo: Integer; var AppliedPaymentEntry: Record "Applied Payment Entry")
    begin
    end;

    /// <summary>
    /// Event raised when getting customer ledger entry remaining amount before calculating fields.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="IsHandled">Indicates whether the event has been handled.</param>
    /// <param name="Result">The remaining amount result if handled.</param>
    [IntegrationEvent(false, false)]
    local procedure OnGetCustLedgEntryRemAmtOnBeforeCalcFields(AppliedPaymentEntry: Record "Applied Payment Entry"; var IsHandled: Boolean; var Result: Decimal)
    begin
    end;

    /// <summary>
    /// Event raised when getting vendor ledger entry remaining amount before calculating fields.
    /// </summary>
    /// <param name="AppliedPaymentEntry">The applied payment entry record.</param>
    /// <param name="IsHandled">Indicates whether the event has been handled.</param>
    /// <param name="Result">The remaining amount result if handled.</param>
    [IntegrationEvent(false, false)]
    local procedure OnGetVendLedgEntryRemAmtOnBeforeCalcFields(AppliedPaymentEntry: Record "Applied Payment Entry"; var IsHandled: Boolean; var Result: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeSuggestAmtToApply(var AppliedPaymentEntry: Record "Applied Payment Entry"; var IsHandled: Boolean; var Result: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeAppliesToEntryNoLookup(var AppliedPaymentEntry: Record "Applied Payment Entry"; var IsHandled: boolean)
    begin
    end;
}