Table 981 Payment Registration Buffer, source in 29

Source29

src/Layers/W1/BaseApp/Bank/Payment/PaymentRegistrationBuffer.Table.al480 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.Payment;

using Microsoft.Bank.BankAccount;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Foundation.Navigate;
using Microsoft.Sales.Customer;
using Microsoft.Sales.Receivables;

/// <summary>
/// Table 981 "Payment Registration Buffer" serves as a temporary buffer for payment registration operations.
/// Stores customer ledger entry information for the payment registration workspace, enabling quick
/// payment entry and batch processing. Supports both individual and lump payment scenarios.
/// </summary>
/// <remarks>
/// Used exclusively as a temporary table (ReplicateData = false) for payment registration UI.
/// Integrates with customer ledger entries and supports payment tolerance and discount calculations.
/// Provides extensibility through integration events for custom payment processing logic.
/// </remarks>
table 981 "Payment Registration Buffer"
{
    Caption = 'Payment Registration Buffer';
    ReplicateData = false;
    DataClassification = CustomerContent;

    fields
    {
        /// <summary>
        /// Customer ledger entry number that this buffer record represents.
        /// </summary>
        field(1; "Ledger Entry No."; Integer)
        {
            Caption = 'Ledger Entry No.';
        }
        /// <summary>
        /// Customer number or source account number for the payment.
        /// </summary>
        field(2; "Source No."; Code[20])
        {
            Caption = 'Source No.';
            ToolTip = 'Specifies the number of the source document that the entry originates from.';
        }
        /// <summary>
        /// Document type of the customer ledger entry.
        /// </summary>
        field(3; "Document Type"; Enum "Gen. Journal Document Type")
        {
            Caption = 'Document Type';
            ToolTip = 'Specifies the type of document that the payment relates to.';
        }
        /// <summary>
        /// Document number of the customer ledger entry.
        /// </summary>
        field(4; "Document No."; Code[20])
        {
            Caption = 'Document No.';
            ToolTip = 'Specifies the number of the document that the payment relates to.';
        }
        /// <summary>
        /// Description of the customer ledger entry.
        /// </summary>
        field(5; Description; Text[100])
        {
            Caption = 'Description';
            ToolTip = 'Specifies the invoice transaction that the payment relates to.';
        }
        /// <summary>
        /// Due date of the customer ledger entry.
        /// </summary>
        field(6; "Due Date"; Date)
        {
            Caption = 'Due Date';
            ToolTip = 'Specifies the payment due date on the related document.';
        }
        /// <summary>
        /// Customer name associated with the payment.
        /// </summary>
        field(7; Name; Text[100])
        {
            Caption = 'Name';
            ToolTip = 'Specifies the name of the customer or vendor that the payment relates to.';
        }
        /// <summary>
        /// Remaining amount to be paid on the customer ledger entry.
        /// </summary>
        field(8; "Remaining Amount"; Decimal)
        {
            AutoFormatType = 0;
            Caption = 'Remaining Amount';
            ToolTip = 'Specifies the amount that remains to be paid on the document.';
        }
        /// <summary>
        /// Indicates whether a payment has been made for this entry.
        /// </summary>
        field(9; "Payment Made"; Boolean)
        {
            Caption = 'Payment Made';
            ToolTip = 'Specifies if you have received or made payment for the document.';

            trigger OnValidate()
            begin
                if not "Payment Made" then begin
                    "Amount Received" := 0;
                    "Date Received" := 0D;
                    "Remaining Amount" := "Original Remaining Amount";
                    "External Document No." := '';
                    exit;
                end;

                AutoFillDate();
                if "Amount Received" = 0 then
                    SuggestAmountReceivedBasedOnDate();
                UpdateRemainingAmount();
            end;
        }
        /// <summary>
        /// Date when the payment was received.
        /// </summary>
        field(10; "Date Received"; Date)
        {
            Caption = 'Date Received';
            ToolTip = 'Specifies the date when the payment was made.';

            trigger OnValidate()
            begin
                if "Date Received" <> 0D then
                    Validate("Payment Made", true);
            end;
        }
        /// <summary>
        /// Amount received for this payment entry.
        /// </summary>
        field(11; "Amount Received"; Decimal)
        {
            AutoFormatType = 0;
            Caption = 'Amount Received';
            ToolTip = 'Specifies the amount that is paid in the bank account.';

            trigger OnValidate()
            var
                MaximumRemainingAmount: Decimal;
            begin
                if "Limit Amount Received" then begin
                    MaximumRemainingAmount := GetMaximumPaymentAmountBasedOnDate();
                    if "Amount Received" > MaximumRemainingAmount then
                        "Amount Received" := MaximumRemainingAmount;
                end;

                AutoFillDate();
                "Payment Made" := true;
                UpdateRemainingAmount();
            end;
        }
        /// <summary>
        /// Original remaining amount before any payment processing.
        /// </summary>
        field(12; "Original Remaining Amount"; Decimal)
        {
            AutoFormatType = 0;
            Caption = 'Original Remaining Amount';
        }
        /// <summary>
        /// Remaining amount after applying payment discount.
        /// </summary>
        field(13; "Rem. Amt. after Discount"; Decimal)
        {
            AutoFormatType = 0;
            Caption = 'Rem. Amt. after Discount';
            ToolTip = 'Specifies the remaining amount after the payment discount is deducted.';
        }
        /// <summary>
        /// Payment discount date for this customer ledger entry.
        /// </summary>
        field(14; "Pmt. Discount Date"; Date)
        {
            Caption = 'Pmt. Discount Date';
            ToolTip = 'Specifies the date on which the amount in the entry must be paid for a payment discount to be granted.';

            trigger OnValidate()
            begin
                if "Pmt. Discount Date" <> 0D then
                    Validate("Payment Made", true);
            end;
        }
        /// <summary>
        /// Indicates whether the amount received should be limited to maximum allowable amount.
        /// </summary>
        field(15; "Limit Amount Received"; Boolean)
        {
            Caption = 'Limit Amount Received';
        }
        /// <summary>
        /// Payment method code used for this payment registration.
        /// </summary>
        field(16; "Payment Method Code"; Code[10])
        {
            Caption = 'Payment Method Code';
        }
        /// <summary>
        /// Type of balancing account for the payment (G/L Account or Bank Account).
        /// </summary>
        field(17; "Bal. Account Type"; enum "Payment Balance Account Type")
        {
            Caption = 'Bal. Account Type';
        }
        /// <summary>
        /// Number of the balancing account for the payment.
        /// </summary>
        field(18; "Bal. Account No."; Code[20])
        {
            Caption = 'Bal. Account No.';
            TableRelation = if ("Bal. Account Type" = const("G/L Account")) "G/L Account"
            else
            if ("Bal. Account Type" = const("Bank Account")) "Bank Account";
        }
        /// <summary>
        /// External document number for the payment registration.
        /// </summary>
        field(19; "External Document No."; Code[35])
        {
            Caption = 'External Document No.';
            ToolTip = 'Specifies a document number that refers to the customer''s or vendor''s numbering system.';
        }
    }

    keys
    {
        key(Key1; "Ledger Entry No.")
        {
            Clustered = true;
        }
        key(Key2; "Due Date")
        {
        }
    }

    fieldgroups
    {
    }

    var
        DueDateMsg: Label 'The payment is overdue. You can calculate interest for late payments from customers by choosing the Finance Charge Memo button.';
        PmtDiscMsg: Label 'Payment Discount Date is earlier than Date Received. Payment will be registered as partial payment.';

    procedure PopulateTable()
    var
        CustLedgerEntry: Record "Cust. Ledger Entry";
        [SecurityFiltering(SecurityFilter::Filtered)]
        Customer: Record Customer;
        PaymentRegistrationSetup: Record "Payment Registration Setup";
    begin
        PaymentRegistrationSetup.Get(UserId);
        PaymentRegistrationSetup.TestField("Bal. Account No.");

        Reset();
        DeleteAll();

        CustLedgerEntry.SetFilter("Document Type", '<>%1', CustLedgerEntry."Document Type"::Payment);
        CustLedgerEntry.SetRange(Open, true);
        OnPopulateTableOnAfterCustLedgerEntrySetFilters(CustLedgerEntry, Rec);
        if CustLedgerEntry.FindSet() then
            repeat
                if Customer.Get(CustLedgerEntry."Customer No.") then begin
                    CustLedgerEntry.CalcFields("Remaining Amount");

                    Init();
                    "Ledger Entry No." := CustLedgerEntry."Entry No.";
                    "Source No." := CustLedgerEntry."Customer No.";
                    Name := Customer.Name;
                    "Document No." := CustLedgerEntry."Document No.";
                    "Document Type" := CustLedgerEntry."Document Type";
                    Description := CustLedgerEntry.Description;
                    "Due Date" := CustLedgerEntry."Due Date";
                    "Remaining Amount" := CustLedgerEntry."Remaining Amount";
                    "Original Remaining Amount" := CustLedgerEntry."Remaining Amount";
                    "Pmt. Discount Date" := CustLedgerEntry."Pmt. Discount Date";
                    "Rem. Amt. after Discount" := "Remaining Amount" - CustLedgerEntry."Remaining Pmt. Disc. Possible";
                    if CustLedgerEntry."Payment Method Code" <> '' then
                        "Payment Method Code" := CustLedgerEntry."Payment Method Code";
                    "Bal. Account Type" := Enum::"Payment Balance Account Type".FromInteger(PaymentRegistrationSetup."Bal. Account Type");
                    "Bal. Account No." := PaymentRegistrationSetup."Bal. Account No.";
                    "External Document No." := CustLedgerEntry."External Document No.";
                    OnPopulateTableOnBeforeInsert(Rec, CustLedgerEntry);
                    Insert();
                end;
            until CustLedgerEntry.Next() = 0;

        if FindSet() then;
    end;

    procedure Navigate()
    var
        CustLedgerEntry: Record "Cust. Ledger Entry";
        Navigate: Page Navigate;
    begin
        CustLedgerEntry.Get("Ledger Entry No.");
        Navigate.SetDoc(CustLedgerEntry."Posting Date", CustLedgerEntry."Document No.");
        Navigate.Run();
    end;

    procedure Reload()
    var
        TempDataSavePmtRegnBuf: Record "Payment Registration Buffer" temporary;
        TempRecSavePmtRegnBuf: Record "Payment Registration Buffer" temporary;
    begin
        TempRecSavePmtRegnBuf.Copy(Rec, true);

        SaveUserValues(TempDataSavePmtRegnBuf);

        PopulateTable();

        RestoreUserValues(TempDataSavePmtRegnBuf);

        Copy(TempRecSavePmtRegnBuf);
        if Get("Ledger Entry No.") then;
    end;

    local procedure SaveUserValues(var TempSavePmtRegnBuf: Record "Payment Registration Buffer" temporary)
    var
        TempWorkPmtRegnBuf: Record "Payment Registration Buffer" temporary;
    begin
        TempWorkPmtRegnBuf.Copy(Rec, true);
        TempWorkPmtRegnBuf.Reset();
        TempWorkPmtRegnBuf.SetRange("Payment Made", true);
        if TempWorkPmtRegnBuf.FindSet() then
            repeat
                TempSavePmtRegnBuf := TempWorkPmtRegnBuf;
                TempSavePmtRegnBuf.Insert();
            until TempWorkPmtRegnBuf.Next() = 0;
    end;

    local procedure RestoreUserValues(var TempSavePmtRegnBuf: Record "Payment Registration Buffer" temporary)
    begin
        if TempSavePmtRegnBuf.FindSet() then
            repeat
                if Get(TempSavePmtRegnBuf."Ledger Entry No.") then begin
                    "Payment Made" := TempSavePmtRegnBuf."Payment Made";
                    "Date Received" := TempSavePmtRegnBuf."Date Received";
                    "Pmt. Discount Date" := TempSavePmtRegnBuf."Pmt. Discount Date";
                    SuggestAmountReceivedBasedOnDate();
                    "Remaining Amount" := TempSavePmtRegnBuf."Remaining Amount";
                    "Amount Received" := TempSavePmtRegnBuf."Amount Received";
                    "External Document No." := TempSavePmtRegnBuf."External Document No.";
                    OnRestoreUserValuesOnBeforeModify(Rec, TempSavePmtRegnBuf);
                    Modify();
                end;
            until TempSavePmtRegnBuf.Next() = 0;
    end;

    procedure GetPmtDiscStyle(): Text
    begin
        if ("Pmt. Discount Date" < "Date Received") and ("Remaining Amount" <> 0) and ("Date Received" < "Due Date") then
            exit('Unfavorable');
        exit('');
    end;

    procedure GetDueDateStyle() ReturnValue: Text
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetDueDateStyle(Rec, ReturnValue, IsHandled);
        if IsHandled then
            exit(ReturnValue);

        if "Due Date" < "Date Received" then
            exit('Unfavorable');
        exit('');
    end;

    procedure GetWarning(): Text
    begin
        if "Date Received" <= "Pmt. Discount Date" then
            exit('');

        if "Date Received" > "Due Date" then
            exit(DueDateMsg);

        if "Remaining Amount" <> 0 then
            exit(PmtDiscMsg);

        exit('');
    end;

    local procedure AutoFillDate()
    var
        PaymentRegistrationSetup: Record "Payment Registration Setup";
    begin
        if "Date Received" = 0D then begin
            PaymentRegistrationSetup.Get(UserId);
            if PaymentRegistrationSetup."Auto Fill Date Received" then
                "Date Received" := WorkDate();
        end;
    end;

    local procedure SuggestAmountReceivedBasedOnDate()
    begin
        "Amount Received" := GetMaximumPaymentAmountBasedOnDate();
        if "Date Received" = 0D then
            exit;
        "Remaining Amount" := 0;
    end;

    local procedure GetMaximumPaymentAmountBasedOnDate(): Decimal
    begin
        if "Date Received" = 0D then
            exit(0);

        if "Date Received" <= "Pmt. Discount Date" then
            exit("Rem. Amt. after Discount");

        exit("Original Remaining Amount");
    end;

    local procedure UpdateRemainingAmount()
    begin
        if "Date Received" = 0D then
            exit;
        if Abs("Amount Received") >= Abs("Original Remaining Amount") then
            "Remaining Amount" := 0
        else
            if "Date Received" <= "Pmt. Discount Date" then begin
                if "Amount Received" >= "Rem. Amt. after Discount" then
                    "Remaining Amount" := 0
                else
                    "Remaining Amount" := "Original Remaining Amount" - "Amount Received";
            end else
                "Remaining Amount" := "Original Remaining Amount" - "Amount Received";
    end;

    /// <summary>
    /// Integration event raised before inserting a payment registration buffer record during table population.
    /// Enables custom modifications to buffer data before record insertion.
    /// </summary>
    /// <param name="PaymentRegistrationBuffer">Payment registration buffer record being inserted</param>
    /// <param name="CustLedgerEntry">Source customer ledger entry for the buffer record</param>
    [IntegrationEvent(false, false)]
    local procedure OnPopulateTableOnBeforeInsert(var PaymentRegistrationBuffer: Record "Payment Registration Buffer"; CustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Integration event raised after applying filters to customer ledger entries during table population.
    /// Enables custom filter modifications for payment registration processing.
    /// </summary>
    /// <param name="CustLedgerEntry">Customer ledger entry with applied filters</param>
    /// <param name="PaymentRegistrationBuffer">Payment registration buffer context</param>
    [IntegrationEvent(false, false)]
    local procedure OnPopulateTableOnAfterCustLedgerEntrySetFilters(var CustLedgerEntry: Record "Cust. Ledger Entry"; var PaymentRegistrationBuffer: Record "Payment Registration Buffer")
    begin
    end;

    /// <summary>
    /// Integration event raised before modifying a payment registration buffer record during user value restoration.
    /// Enables custom logic before restoring user-entered payment values.
    /// </summary>
    /// <param name="PaymentRegistrationBuffer">Payment registration buffer record being modified</param>
    /// <param name="TempSavePmtRegnBuf">Temporary buffer containing saved user values</param>
    [IntegrationEvent(false, false)]
    local procedure OnRestoreUserValuesOnBeforeModify(var PaymentRegistrationBuffer: Record "Payment Registration Buffer"; var TempSavePmtRegnBuf: Record "Payment Registration Buffer" temporary)
    begin
    end;

    /// <summary>
    /// Integration event raised before determining the due date style for payment registration display.
    /// Enables custom styling logic for due date visualization.
    /// </summary>
    /// <param name="PaymentRegistrationBuffer">Payment registration buffer record</param>
    /// <param name="ReturnValue">Style value to be returned</param>
    /// <param name="IsHandled">Set to true to skip standard style determination</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetDueDateStyle(var PaymentRegistrationBuffer: Record "Payment Registration Buffer"; var ReturnValue: Text; var IsHandled: Boolean)
    begin
    end;
}