Codeunit 392 Reminder-Make

App
Base Application
Namespace
Microsoft.Sales.Reminder
Versions
17-28

Procedures, 7Events, 59Obsolete, 1

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Sales/Reminder/ReminderMake.Codeunit.al1251 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.Sales.Reminder;

using Microsoft.Finance.Currency;
using Microsoft.Sales.Customer;
using Microsoft.Sales.FinanceCharge;
using Microsoft.Sales.Receivables;
using System.Globalization;
using System.Telemetry;
using System.Utilities;

/// <summary>
/// Creates reminder documents for customers by analyzing overdue ledger entries and applying reminder terms.
/// </summary>
codeunit 392 "Reminder-Make"
{

    trigger OnRun()
    begin
    end;

    var
        TempCurrencyGlobal: Record Currency temporary;
        GlobalCustomer: Record Customer;
        GlobalCustLedgEntry: Record "Cust. Ledger Entry";
        GlobalCustLedgEntry2: Record "Cust. Ledger Entry";
        GlobalReminderTerms: Record "Reminder Terms";
        GlobalReminderHeaderReq: Record "Reminder Header";
        GlobalReminderHeader: Record "Reminder Header";
        GlobalReminderEntry: Record "Reminder/Fin. Charge Entry";
        TempCustLedgerEntryOnHold: Record "Cust. Ledger Entry" temporary;
        GlobalCustLedgEntryLineFeeFilters: Record "Cust. Ledger Entry";
        FeatureTelemetry: Codeunit "Feature Telemetry";
        AmountsNotDueLineInserted: Boolean;
        OverdueEntriesOnly: Boolean;
        HeaderExists: Boolean;
        IncludeEntriesOnHold: Boolean;
        OpenEntriesNotDueLbl: Label 'Open Entries Not Due';
        OpenEntriesOnHoldLbl: Label 'Open Entries On Hold';
        CustLedgEntryLastIssuedReminderLevelFilter: Text;

    /// <summary>
    /// Executes the reminder creation process for the configured customer and ledger entry filters.
    /// </summary>
    /// <returns>True if at least one reminder was created successfully; otherwise, false.</returns>
    procedure "Code"() RetVal: Boolean
    var
        ReminderLine: Record "Reminder Line";
        IsHandled: Boolean;
    begin
        CustLedgEntryLastIssuedReminderLevelFilter := GlobalCustLedgEntry.GetFilter("Last Issued Reminder Level");
        if GlobalReminderHeader."No." <> '' then begin
            HeaderExists := true;
            GlobalReminderHeader.TestField("Customer No.");
            GlobalCustomer.Get(GlobalReminderHeader."Customer No.");
            IsHandled := false;
            OnCodeOnAfterGlobalReminderGetGlobalCustomer(GlobalReminderHeader, GlobalCustomer, IsHandled, RetVal);
            if IsHandled then
                exit(RetVal);
            GlobalReminderHeader.TestField("Document Date");
            GlobalReminderHeader.TestField("Reminder Terms Code");
            GlobalReminderHeaderReq := GlobalReminderHeader;
            ReminderLine.SetRange("Reminder No.", GlobalReminderHeader."No.");
            ReminderLine.DeleteAll();
        end;

        IsHandled := false;
        OnCodeOnBeforeGetReminderTerms(GlobalCustomer, GlobalCustLedgEntry, CustLedgEntryLastIssuedReminderLevelFilter, GlobalReminderHeader, IsHandled, RetVal);
        if IsHandled then
            exit(RetVal);
        GetReminderTerms();
        if HeaderExists then
            RetVal := MakeReminder(GlobalReminderHeader."Currency Code")
        else begin
            TempCurrencyGlobal.DeleteAll();
            GlobalCustLedgEntry2.CopyFilters(GlobalCustLedgEntry);
            GlobalCustLedgEntry.SetCurrentKey("Customer No.", Open, Positive);
            GlobalCustLedgEntry.SetRange("Customer No.", GlobalCustomer."No.");
            GlobalCustLedgEntry.SetRange(Open, true);
            GlobalCustLedgEntry.SetRange(Positive, true);
            OnBeforeCustLedgerEntryFind(GlobalCustLedgEntry, GlobalReminderHeaderReq, GlobalCustomer);
            if GlobalCustLedgEntry.FindSet() then
                repeat
                    if (GlobalCustLedgEntry."On Hold" = '') or IncludeEntriesOnHold then begin
                        TempCurrencyGlobal.Code := GlobalCustLedgEntry."Currency Code";
                        if TempCurrencyGlobal.Insert() then;
                    end;
                until GlobalCustLedgEntry.Next() = 0;
            GlobalCustLedgEntry.CopyFilters(GlobalCustLedgEntry2);
            RetVal := true;
            OnCodeOnBeforeCurrencyLoop(GlobalCustLedgEntry, GlobalReminderHeaderReq, GlobalReminderTerms, OverdueEntriesOnly,
                IncludeEntriesOnHold, HeaderExists, CustLedgEntryLastIssuedReminderLevelFilter, TempCurrencyGlobal,
                GlobalCustomer, GlobalCustLedgEntryLineFeeFilters);
            if TempCurrencyGlobal.FindSet() then
                repeat
                    if not MakeReminder(TempCurrencyGlobal.Code) then
                        RetVal := false;
                until TempCurrencyGlobal.Next() = 0;
        end;

        OnAfterCode(RetVal);
    end;

    local procedure GetReminderTerms()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetReminderTerms(GlobalReminderHeader, GlobalReminderTerms, IsHandled);
        if IsHandled then
            exit;

        if GlobalCustomer."Reminder Terms Code" = '' then
            GlobalReminderHeader.TestField("Reminder Terms Code");

        if GlobalReminderHeader."Reminder Terms Code" = '' then begin
            GlobalCustomer.TestField("Reminder Terms Code");
            GlobalReminderTerms.Get(GlobalCustomer."Reminder Terms Code");
        end else
            GlobalReminderTerms.Get(GlobalReminderHeader."Reminder Terms Code")
    end;

    /// <summary>
    /// Initializes the reminder creation parameters for batch processing of customer reminders.
    /// </summary>
    /// <param name="Cust2">Specifies the customer to create reminders for.</param>
    /// <param name="CustLedgEntry2">Specifies the customer ledger entries filter to include.</param>
    /// <param name="ReminderHeaderReq2">Specifies the reminder header settings to use as a template.</param>
    /// <param name="OverdueEntriesOnly2">Specifies whether to include only overdue entries.</param>
    /// <param name="IncludeEntriesOnHold2">Specifies whether to include entries that are on hold.</param>
    /// <param name="CustLedgEntryLinefeeOn">Specifies the customer ledger entries filter for line fee calculation.</param>
    procedure Set(Cust2: Record Customer; var CustLedgEntry2: Record "Cust. Ledger Entry"; ReminderHeaderReq2: Record "Reminder Header"; OverdueEntriesOnly2: Boolean; IncludeEntriesOnHold2: Boolean; var CustLedgEntryLinefeeOn: Record "Cust. Ledger Entry")
    begin
        GlobalCustomer := Cust2;
        GlobalCustLedgEntry.Copy(CustLedgEntry2);
        GlobalReminderHeaderReq := ReminderHeaderReq2;
        OverdueEntriesOnly := OverdueEntriesOnly2;
        IncludeEntriesOnHold := IncludeEntriesOnHold2;
        GlobalCustLedgEntryLineFeeFilters.CopyFilters(CustLedgEntryLinefeeOn);

        OnAfterSet(GlobalCustomer, GlobalCustLedgEntry, GlobalReminderHeaderReq, OverdueEntriesOnly, IncludeEntriesOnHold, CustLedgEntryLinefeeOn);
    end;

    /// <summary>
    /// Suggests reminder lines for an existing reminder header based on overdue customer ledger entries.
    /// </summary>
    /// <param name="ReminderHeader2">Specifies the reminder header to add suggested lines to.</param>
    /// <param name="CustLedgEntry2">Specifies the customer ledger entries filter to include.</param>
    /// <param name="OverdueEntriesOnly2">Specifies whether to include only overdue entries.</param>
    /// <param name="IncludeEntriesOnHold2">Specifies whether to include entries that are on hold.</param>
    /// <param name="CustLedgEntryLinefeeOn">Specifies the customer ledger entries filter for line fee calculation.</param>
    procedure SuggestLines(ReminderHeader2: Record "Reminder Header"; var CustLedgEntry2: Record "Cust. Ledger Entry"; OverdueEntriesOnly2: Boolean; IncludeEntriesOnHold2: Boolean; var CustLedgEntryLinefeeOn: Record "Cust. Ledger Entry")
    begin
        GlobalReminderHeader := ReminderHeader2;
        GlobalCustLedgEntry.Copy(CustLedgEntry2);
        OverdueEntriesOnly := OverdueEntriesOnly2;
        IncludeEntriesOnHold := IncludeEntriesOnHold2;
        GlobalCustLedgEntryLineFeeFilters.CopyFilters(CustLedgEntryLinefeeOn);
        OnAfterSuggestLines(GlobalReminderHeader, CustLedgEntry2, OverdueEntriesOnly2, IncludeEntriesOnHold2, CustLedgEntryLinefeeOn);
    end;

    local procedure MakeReminder(CurrencyCode: Code[10]) RetVal: Boolean
    var
        ReminderLevel: Record "Reminder Level";
        ReminderLine: Record "Reminder Line";
        MakeDoc: Boolean;
        StartLineInserted: Boolean;
        NextLineNo: Integer;
        LineLevel: Integer;
        MaxLineLevel: Integer;
        MaxReminderLevel: Integer;
        CustAmount: Decimal;
        ReminderDueDate: Date;
        OpenEntriesNotDueTranslated: Text[100];
        OpenEntriesOnHoldTranslated: Text[100];
        IsHandled: Boolean;
        IsGracePeriodExpired: Boolean;
        ShouldMakeDoc: Boolean;
    begin
        IsHandled := false;
        OnBeforeMakeReminder(GlobalReminderHeader, CurrencyCode, RetVal, IsHandled, GlobalReminderHeaderReq, OverdueEntriesOnly, IncludeEntriesOnHold, HeaderExists, GlobalCustomer);
        if IsHandled then
            exit;

        FilterCustLedgEntryReminderLevel(GlobalCustLedgEntry, ReminderLevel, CurrencyCode);
        if not ReminderLevel.FindLast() then
            exit(false);
        TempCustLedgerEntryOnHold.DeleteAll();

        if not FindAndMarkReminderCandidates(GlobalCustLedgEntry, ReminderLevel, CustAmount, MakeDoc, MaxReminderLevel, MaxLineLevel) then
            exit(false);

        ReminderLevel.SetRange("Reminder Terms Code", GlobalReminderTerms.Code);
        ReminderLevel.SetRange("No.", 1, MaxLineLevel);
        if not ReminderLevel.FindLast() then
            ReminderLevel.Init();
        ShouldMakeDoc := MakeDoc and (CustAmount > 0) and (CustAmountLCY(CurrencyCode, CustAmount) >= GlobalReminderTerms."Minimum Amount (LCY)");
        OnMakeReminderOnAfterCalcShouldMakeDoc(GlobalReminderHeaderReq, GlobalReminderHeader, GlobalCustomer, ShouldMakeDoc, MakeDoc, GlobalCustLedgEntry);
        if ShouldMakeDoc then begin
            if CheckCustomerIsBlocked(GlobalCustomer) then
                exit(false);
            ReminderLine.LockTable();
            GlobalReminderHeader.LockTable();
            if not HeaderExists then begin
                GlobalReminderHeader.SetCurrentKey("Customer No.", "Currency Code");
                GlobalReminderHeader.SetRange("Customer No.", GlobalCustomer."No.");
                GlobalReminderHeader.SetRange("Currency Code", CurrencyCode);
                OnBeforeReminderHeaderFind(GlobalReminderHeader, GlobalReminderHeaderReq, GlobalReminderTerms, GlobalCustomer, GlobalCustLedgEntry);
                if GlobalReminderHeader.FindFirst() then
                    exit(false);
                GlobalReminderHeader.Init();
                GlobalReminderHeader."No." := '';
                GlobalReminderHeader."Posting Date" := GlobalReminderHeaderReq."Posting Date";
                OnBeforeReminderHeaderInsert(GlobalReminderHeader, GlobalReminderHeaderReq, GlobalReminderTerms, GlobalCustomer);
                GlobalReminderHeader.Insert(true);
                GlobalReminderHeader.Validate("Customer No.", GlobalCustomer."No.");
                GlobalReminderHeader.Validate("Currency Code", CurrencyCode);
                GlobalReminderHeader."Document Date" := GlobalReminderHeaderReq."Document Date";
                GlobalReminderHeader."Use Header Level" := GlobalReminderHeaderReq."Use Header Level";
            end;
            GlobalReminderHeader."Reminder Level" := ReminderLevel."No.";
            OnBeforeReminderHeaderModify(GlobalReminderHeader, GlobalReminderHeaderReq, HeaderExists, GlobalReminderTerms, GlobalCustomer, ReminderLevel, GlobalCustLedgEntry);
            GlobalReminderHeader.Modify();
            NextLineNo := 0;
            ReminderLevel.MarkedOnly(true);
            GlobalCustLedgEntry.MarkedOnly(true);
            ReminderLevel.FindLast();

            repeat
                StartLineInserted := false;
                FilterCustLedgEntries(ReminderLevel);
                OnMakeReminderOnAfterFilterCustLedgEntries(ReminderLine);
                AmountsNotDueLineInserted := false;
                if GlobalCustLedgEntry.FindSet() then
                    repeat
                        SetReminderLine(LineLevel, ReminderDueDate);
                        IsGracePeriodExpired := IsGracePeriodExpiredForOverdueEntry(ReminderDueDate, GlobalReminderHeaderReq."Document Date", ReminderLevel."Grace Period");
                        OnMakeReminderOnAfterCalcIsGracePeriodExpired(ReminderDueDate, GlobalReminderHeaderReq, IsGracePeriodExpired);
                        if IsGracePeriodExpired then begin
                            if (NextLineNo > 0) and not StartLineInserted then
                                InsertReminderLine(
                                  GlobalReminderHeader."No.", ReminderLine."Line Type"::"Reminder Line", '', NextLineNo);
                            InitReminderLine(
                              ReminderLine, GlobalReminderHeader."No.", ReminderLine."Line Type"::"Reminder Line", '', NextLineNo);
                            ReminderLine.Type := ReminderLine.Type::"Customer Ledger Entry";
                            ReminderLine.Validate("Entry No.", GlobalCustLedgEntry."Entry No.");
                            SetReminderLevel(GlobalReminderHeader, ReminderLine, ReminderLevel."No.");
                            OnBeforeReminderLineInsert(ReminderLine, GlobalReminderHeader, ReminderLevel, GlobalCustLedgEntry, GlobalReminderHeaderReq);
                            ReminderLine.Insert();
                            StartLineInserted := true;

                            AddLineFeeForCustLedgEntry(GlobalCustLedgEntry, ReminderLevel, NextLineNo);
                        end;
                    until GlobalCustLedgEntry.Next() = 0;

                OnMakeReminderOnAfterReminderLevelLoop(ReminderLevel, NextLineNo, StartLineInserted, GlobalReminderHeaderReq, GlobalReminderHeader, GlobalCustomer);
            until ReminderLevel.Next(-1) = 0;

            OnAfterReminderLinesInsertLoop(GlobalReminderHeader, CurrencyCode, NextLineNo, MaxReminderLevel, OverdueEntriesOnly);

            GlobalReminderHeader."Reminder Level" := MaxReminderLevel;
            GlobalReminderHeader.Validate("Reminder Level");
            OnMakeReminderOnBeforeReminderHeaderInsertLines(GlobalReminderHeader);
            GlobalReminderHeader.InsertLines();
            ReminderLine.SetRange("Reminder No.", GlobalReminderHeader."No.");
            ReminderLine.FindLast();
            NextLineNo := ReminderLine."Line No.";
            GetOpenEntriesNotDueOnHoldTranslated(GlobalCustomer."Language Code", OpenEntriesNotDueTranslated, OpenEntriesOnHoldTranslated);
            GlobalCustLedgEntry.SetRange("Last Issued Reminder Level");
            OnMakeReminderOnBeforeCustLedgEntryFindSet(GlobalCustLedgEntry, GlobalCustomer, GlobalReminderHeader, MaxReminderLevel, OverdueEntriesOnly);
            if GlobalCustLedgEntry.FindSet() then
                repeat
                    AddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilter(ReminderLine, ReminderLevel, LineLevel, ReminderDueDate, NextLineNo, OpenEntriesNotDueTranslated, StartLineInserted)
                until GlobalCustLedgEntry.Next() = 0;
            OnMakeReminderOnAfterAddRemiderLinesFromCustLedgEntriesWithNoReminderLevelFilter(GlobalCustLedgEntry, GlobalCustomer, GlobalReminderHeader, MaxReminderLevel, OverdueEntriesOnly);

            if IncludeEntriesOnHold then
                if TempCustLedgerEntryOnHold.FindSet() then begin
                    InsertReminderLine(
                      GlobalReminderHeader."No.", ReminderLine."Line Type"::"On Hold", '', NextLineNo);
                    InsertReminderLine(
                      GlobalReminderHeader."No.", ReminderLine."Line Type"::"On Hold", OpenEntriesOnHoldTranslated, NextLineNo);
                    repeat
                        InitReminderLine(
                          ReminderLine, GlobalReminderHeader."No.", ReminderLine."Line Type"::"On Hold", '', NextLineNo);
                        ReminderLine.Type := ReminderLine.Type::"Customer Ledger Entry";
                        ReminderLine.Validate("Entry No.", TempCustLedgerEntryOnHold."Entry No.");
                        ReminderLine."No. of Reminders" := 0;
                        OnMakeReminderOnBeforeOnHoldReminderLineInsert(ReminderLine, GlobalReminderHeader, ReminderLevel, GlobalCustLedgEntry, TempCustLedgerEntryOnHold);
                        ReminderLine.Insert();
                    until TempCustLedgerEntryOnHold.Next() = 0;
                end;
            OnMakeReminderOnBeforeReminderHeaderModify(GlobalReminderHeader, ReminderLine, NextLineNo, MaxReminderLevel);
            GlobalReminderHeader.Modify();
        end;

        RemoveLinesOfNegativeReminder(GlobalReminderHeader);

        ReminderLevel.Reset();
        GlobalCustLedgEntry.Reset();

        OnAfterMakeReminder(GlobalReminderHeader, ReminderLine);

        exit(true);
    end;

    local procedure AddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilter(var ReminderLine: Record "Reminder Line"; var ReminderLevel: Record "Reminder Level"; LineLevel: Integer; ReminderDueDate: Date; var NextLineNo: Integer; OpenEntriesNotDueTranslated: Text[100]; var StartLineInserted: Boolean)
    var
        IsHandled: Boolean;
        IsGracePeriodExpired: Boolean;
    begin
        IsHandled := false;
        OnBeforeAddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilter(GlobalCustLedgEntry, GlobalReminderHeaderReq, GlobalReminderHeader, IsHandled, ReminderLine, NextLineNo, StartLineInserted);
        if IsHandled then
            exit;

        if (not OverdueEntriesOnly) or
           (GlobalCustLedgEntry."Document Type" in [GlobalCustLedgEntry."Document Type"::Payment, GlobalCustLedgEntry."Document Type"::Refund])
        then begin
            SetReminderLine(LineLevel, ReminderDueDate);
            IsGracePeriodExpired :=
                IsGracePeriodExpiredForOverdueEntry(ReminderDueDate, GlobalReminderHeaderReq."Document Date", ReminderLevel."Grace Period");
            if (not IsGracePeriodExpired) and (LineLevel = 1) then begin
                OnAddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilterOnBeforeCheckAmountsNotDueLineInserted(
                    GlobalReminderHeader, ReminderLine, AmountsNotDueLineInserted);
                if not AmountsNotDueLineInserted then begin
                    InsertReminderLine(GlobalReminderHeader."No.", ReminderLine."Line Type"::"Not Due", '', NextLineNo);
                    InsertReminderLine(
                      GlobalReminderHeader."No.", ReminderLine."Line Type"::"Not Due", OpenEntriesNotDueTranslated, NextLineNo);
                    AmountsNotDueLineInserted := true;
                end;
                InitReminderLine(
                  ReminderLine, GlobalReminderHeader."No.", ReminderLine."Line Type"::"Not Due", '', NextLineNo);
                ReminderLine.Type := ReminderLine.Type::"Customer Ledger Entry";
                ReminderLine.Validate("Entry No.", GlobalCustLedgEntry."Entry No.");
                ReminderLine."No. of Reminders" := 0;
                OnAddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilterOnBeforeReminderLineInsert(ReminderLine, GlobalReminderHeader, ReminderLevel, GlobalCustLedgEntry);
                ReminderLine.Insert();
                AmountsNotDueLineInserted := true;
                RemoveNotDueLinesInSectionReminderLine(ReminderLine);
            end;
        end;
    end;

    local procedure FindAndMarkReminderCandidates(var CustLedgEntry: Record "Cust. Ledger Entry"; var ReminderLevel: Record "Reminder Level"; var CustAmount: Decimal; var MakeDoc: Boolean; var MaxReminderLevel: Integer; var MaxLineLevel: Integer) Result: Boolean
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeFindAndMarkReminderCandidates(
            ReminderLevel, GlobalReminderHeaderReq, GlobalReminderTerms, GlobalReminderEntry,
            CustLedgEntry, TempCustLedgerEntryOnHold, CustLedgEntryLastIssuedReminderLevelFilter, CustAmount,
            MakeDoc, MaxReminderLevel, MaxLineLevel, OverdueEntriesOnly, IncludeEntriesOnHold, IsHandled, Result);
        if IsHandled then
            exit(Result);

        repeat
            FilterCustLedgEntries(ReminderLevel);
            if CustLedgEntry.FindSet() then
                repeat
                    IsHandled := false;
                    OnFindAndMarkReminderCandidatesOnBeforeCustLedgEntryLoop(CustLedgEntry, GlobalReminderHeaderReq, IsHandled);
                    if not IsHandled then
                        if CustLedgEntry."On Hold" = '' then
                            MarkReminderCandidate(CustLedgEntry, ReminderLevel, CustAmount, MakeDoc, MaxReminderLevel, MaxLineLevel, false)
                        else // The customer ledger entry is on hold
                            if IncludeEntriesOnHold then begin
                                TempCustLedgerEntryOnHold := CustLedgEntry;
                                if TempCustLedgerEntryOnHold.Insert() then;
                                MarkReminderCandidate(CustLedgEntry, ReminderLevel, CustAmount, MakeDoc, MaxReminderLevel, MaxLineLevel, true);
                            end;
                until CustLedgEntry.Next() = 0;
        until ReminderLevel.Next(-1) = 0;

        IsHandled := false;
        OnAfterFindAndMarkReminderCandidates(GlobalCustLedgEntry, ReminderLevel, CustAmount, MakeDoc, MaxReminderLevel, MaxLineLevel, IsHandled, Result);
        if IsHandled then
            exit(Result);

        exit(true);
    end;

    local procedure MarkReminderCandidate(var CustLedgEntry: Record "Cust. Ledger Entry"; var ReminderLevel: Record "Reminder Level"; var CustAmount: Decimal; var MakeDoc: Boolean; var MaxReminderLevel: Integer; var MaxLineLevel: Integer; EntryOnHold: Boolean)
    var
        ReminderDueDate: Date;
        LineLevel: Integer;
        MarkEntry: Boolean;
        IsHandled: Boolean;
        IsGracePeriodExpired: Boolean;
    begin
        IsHandled := false;
        OnBeforeMarkReminderCandidate(CustLedgEntry, IsHandled);
        if IsHandled then
            exit;

        SetReminderLine(LineLevel, ReminderDueDate);
        IsGracePeriodExpired := IsGracePeriodExpiredForOverdueEntry(ReminderDueDate, GlobalReminderHeaderReq."Document Date", ReminderLevel."Grace Period");
        IsHandled := false;
        OnMarkReminderCandidateOnAfterCalcIsGracePeriodExpired(ReminderLevel, ReminderDueDate, GlobalReminderHeaderReq, GlobalReminderTerms, CustLedgEntry, GlobalReminderHeader, LineLevel, IsGracePeriodExpired, IsHandled);
        if IsHandled then
            exit;

        MarkEntry := false;
        if IsGracePeriodExpired and
           ((LineLevel <= GlobalReminderTerms."Max. No. of Reminders") or (GlobalReminderTerms."Max. No. of Reminders" = 0))
        then begin
            MarkEntry := true;
            CustLedgEntry.CalcFields("Remaining Amount");
            CustAmount += CustLedgEntry."Remaining Amount";
            if CustLedgEntry.Positive and IsGracePeriodExpired then
                MakeDoc := true;
        end else
            if not IsGracePeriodExpired and
               (not OverdueEntriesOnly or
                (CustLedgEntry."Document Type" in [CustLedgEntry."Document Type"::Payment, CustLedgEntry."Document Type"::Refund]))
            then
                MarkEntry := true;

        if MarkEntry then begin
            if not EntryOnHold then
                CustLedgEntry.Mark(true);
            ReminderLevel.Mark(true);
            if (ReminderLevel."No." > MaxReminderLevel) and
               (CustLedgEntry."Document Type" <> CustLedgEntry."Document Type"::"Credit Memo") and
               IsGracePeriodExpired
            then
                MaxReminderLevel := ReminderLevel."No.";
            if MaxLineLevel < LineLevel then
                MaxLineLevel := LineLevel;
        end;
    end;

    local procedure InsertReminderLine(ReminderNo: Code[50]; LineType: Enum "Reminder Line Type"; Description: Text[100]; var NextLineNo: Integer)
    var
        ReminderLine: Record "Reminder Line";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeInsertReminderLine(ReminderNo, LineType, Description, NextLineNo, IsHandled);
        if IsHandled then
            exit;

        InitReminderLine(ReminderLine, ReminderNo, LineType, Description, NextLineNo);
        ReminderLine.Insert();
    end;

    local procedure InitReminderLine(var ReminderLine: Record "Reminder Line"; ReminderNo: Code[50]; LineType: Enum "Reminder Line Type"; Description: Text[100]; var NextLineNo: Integer)
    begin
        NextLineNo := GetLastLineNo(GlobalReminderHeader."No.") + 10000;

        ReminderLine.Init();
        ReminderLine."Reminder No." := CopyStr(ReminderNo, 1, MaxStrLen(ReminderLine."Reminder No."));
        ReminderLine."Line No." := NextLineNo;
        ReminderLine."Line Type" := LineType;
        ReminderLine.Description := Description;

        OnAfterInitReminderLine(GlobalReminderHeader, ReminderLine, LineType, Description);
    end;

    local procedure CustAmountLCY(CurrencyCode: Code[10]; Amount: Decimal): Decimal
    var
        CurrExchRate: Record "Currency Exchange Rate";
    begin
        if CurrencyCode <> '' then
            exit(
              CurrExchRate.ExchangeAmtFCYToLCY(
                GlobalReminderHeaderReq."Posting Date", CurrencyCode, Amount,
                CurrExchRate.ExchangeRate(GlobalReminderHeaderReq."Posting Date", CurrencyCode)));
        exit(Amount);
    end;

    /// <summary>
    /// Applies filters to customer ledger entries based on the specified reminder level.
    /// </summary>
    /// <param name="ReminderLevel2">Specifies the reminder level to filter customer ledger entries by.</param>
    procedure FilterCustLedgEntries(var ReminderLevel2: Record "Reminder Level")
    var
        ReminderLevel3: Record "Reminder Level";
        LastLevel: Boolean;
    begin
        if SkipCurrentReminderLevel(ReminderLevel2."No.") then begin
            GlobalCustLedgEntry.SetRange("Last Issued Reminder Level", -1);
            exit;
        end;
        ReminderLevel3 := ReminderLevel2;
        ReminderLevel3.CopyFilters(ReminderLevel2);
        if ReminderLevel3.Next() = 0 then
            LastLevel := true
        else
            LastLevel := false;
        if GlobalReminderTerms."Max. No. of Reminders" > 0 then
            if ReminderLevel2."No." <= GlobalReminderTerms."Max. No. of Reminders" then
                if LastLevel then
                    GlobalCustLedgEntry.SetRange("Last Issued Reminder Level", ReminderLevel2."No." - 1, GlobalReminderTerms."Max. No. of Reminders" - 1)
                else
                    GlobalCustLedgEntry.SetRange("Last Issued Reminder Level", ReminderLevel2."No." - 1)
            else
                GlobalCustLedgEntry.SetRange("Last Issued Reminder Level", -1)
        else
            if LastLevel then
                GlobalCustLedgEntry.SetFilter("Last Issued Reminder Level", '%1..', ReminderLevel2."No." - 1)
            else
                GlobalCustLedgEntry.SetRange("Last Issued Reminder Level", ReminderLevel2."No." - 1);
    end;

    local procedure SkipCurrentReminderLevel(ReminderLevelNo: Integer): Boolean
    var
        "Integer": Record "Integer";
    begin
        if CustLedgEntryLastIssuedReminderLevelFilter = '' then
            exit(false);

        Integer.SetFilter(Number, CustLedgEntryLastIssuedReminderLevelFilter);
        Integer.FindSet();
        if Integer.Number > ReminderLevelNo - 1 then
            exit(true);
        repeat
            if Integer.Number = ReminderLevelNo - 1 then
                exit(false)
        until Integer.Next() = 0;
        exit(true);
    end;

    local procedure FilterCustLedgEntryReminderLevel(var CustLedgEntry: Record "Cust. Ledger Entry"; var ReminderLevel: Record "Reminder Level"; CurrencyCode: Code[10])
    begin
        CustLedgEntry.SetCurrentKey("Customer No.", Open, Positive, "Due Date", "Currency Code");
        CustLedgEntry.SetRange(Open, true);
        CustLedgEntry.SetRange("Customer No.", GlobalCustomer."No.");
        CustLedgEntry.SetRange("Currency Code", CurrencyCode);
        ReminderLevel.SetRange("Reminder Terms Code", GlobalReminderTerms.Code);

        OnAfterFilterCustLedgEntryReminderLevel(CustLedgEntry, ReminderLevel, GlobalReminderTerms, GlobalCustomer, GlobalReminderHeaderReq, GlobalReminderHeader);
    end;

    /// <summary>
    /// Calculates the reminder level and due date for a customer ledger entry based on previous reminder history.
    /// </summary>
    /// <param name="LineLevel2">Returns the calculated reminder level for the entry.</param>
    /// <param name="ReminderDueDate2">Returns the due date for the reminder entry.</param>
    procedure SetReminderLine(var LineLevel2: Integer; var ReminderDueDate2: Date)
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeSetReminderLine(LineLevel2, ReminderDueDate2, IsHandled, GlobalCustLedgEntry, GlobalReminderEntry);
        if not IsHandled then begin
            if GlobalCustLedgEntry."Last Issued Reminder Level" > 0 then begin
                GlobalReminderEntry.SetCurrentKey("Customer Entry No.", Type);
                GlobalReminderEntry.SetRange("Customer Entry No.", GlobalCustLedgEntry."Entry No.");
                GlobalReminderEntry.SetRange(Type, GlobalReminderEntry.Type::Reminder);
                GlobalReminderEntry.SetRange("Reminder Level", GlobalCustLedgEntry."Last Issued Reminder Level");
                GlobalReminderEntry.SetRange(Canceled, false);
                OnSetReminderLineOnAfterSetFilters(GlobalReminderEntry);
                if GlobalReminderEntry.FindLast() then begin
                    ReminderDueDate2 := GlobalReminderEntry."Due Date";
                    LineLevel2 := GlobalReminderEntry."Reminder Level" + 1;
                    OnSetReminderLineOnAfterFindNextLineLevel(GlobalReminderEntry, LineLevel2, ReminderDueDate2);
                    exit;
                end
            end;
            ReminderDueDate2 := GlobalCustLedgEntry."Due Date";
            LineLevel2 := 1;
        end;

        OnAfterSetReminderLine(GlobalCustLedgEntry, LineLevel2, ReminderDueDate2);
    end;

    /// <summary>
    /// Adds a line fee for a customer ledger entry if applicable based on reminder level settings.
    /// </summary>
    /// <param name="CustLedgEntry">Specifies the customer ledger entry to add a line fee for.</param>
    /// <param name="ReminderLevel">Specifies the reminder level containing the line fee settings.</param>
    /// <param name="NextLineNo">Specifies the next line number to use for the reminder line.</param>
    procedure AddLineFeeForCustLedgEntry(var CustLedgEntry: Record "Cust. Ledger Entry"; var ReminderLevel: Record "Reminder Level"; NextLineNo: Integer)
    var
        TempCustLedgEntry: Record "Cust. Ledger Entry" temporary;
        ReminderLine: Record "Reminder Line";
        IssuedReminderLine: Record "Issued Reminder Line";
        CustPostingGr: Record "Customer Posting Group";
        LineFeeAmount: Decimal;
    begin
        TempCustLedgEntry := CustLedgEntry;
        TempCustLedgEntry.Insert();
        TempCustLedgEntry.Reset();
        TempCustLedgEntry.CopyFilters(GlobalCustLedgEntryLineFeeFilters);
        if TempCustLedgEntry.IsEmpty() then
            exit;

        CustLedgEntry.CalcFields("Remaining Amount");
        OnAddLineFeeForCustLedgEntryOnAfterCalcRemainingAmount(CustLedgEntry);
        LineFeeAmount := ReminderLevel.GetAdditionalFee(CustLedgEntry."Remaining Amount",
            GlobalReminderHeader."Currency Code", true, GlobalReminderHeader."Posting Date");
        if LineFeeAmount = 0 then
            exit;

        IssuedReminderLine.SetRange(Type, IssuedReminderLine.Type::"Line Fee");
        IssuedReminderLine.SetRange("Applies-To Document Type", CustLedgEntry."Document Type");
        IssuedReminderLine.SetRange("Applies-To Document No.", CustLedgEntry."Document No.");
        IssuedReminderLine.SetRange("No. of Reminders", ReminderLevel."No.");
        IssuedReminderLine.SetRange(Canceled, false);
        if not IssuedReminderLine.IsEmpty() then
            exit;

        CustPostingGr.Get(GlobalReminderHeader."Customer Posting Group");

        NextLineNo := NextLineNo + 100;
        ReminderLine.Init();
        ReminderLine.Validate("Reminder No.", GlobalReminderHeader."No.");
        ReminderLine.Validate("Line No.", NextLineNo);
        ReminderLine.Validate(Type, ReminderLine.Type::"Line Fee");
        ReminderLine.Validate("No.", CustPostingGr.GetAddFeePerLineAccount());
        ReminderLine.Validate("No. of Reminders", ReminderLevel."No.");
        ReminderLine.Validate("Applies-to Document Type", CustLedgEntry."Document Type");
        ReminderLine.Validate("Applies-to Document No.", CustLedgEntry."Document No.");
        ReminderLine.Validate("Due Date", CalcDate(ReminderLevel."Due Date Calculation", GlobalReminderHeader."Document Date"));
        OnAddLineFeeForCustLedgEntryOnReminderLineInsert(ReminderLine);
        ReminderLine.Insert(true);
    end;

    internal procedure EmitCreateReminderTelemetry()
    begin
        FeatureTelemetry.LogUptake('0000LB0', 'Reminder', Enum::"Feature Uptake Status"::"Set up");
        FeatureTelemetry.LogUptake('0000LB1', 'Reminder', Enum::"Feature Uptake Status"::Used);
        FeatureTelemetry.LogUsage('0000LB2', 'Reminder', 'Make Reminder called.');
    end;

    local procedure GetLastLineNo(ReminderNo: Code[50]): Integer
    var
        ReminderLineExtra: Record "Reminder Line";
    begin
        ReminderLineExtra.SetRange("Reminder No.", ReminderNo);
        if ReminderLineExtra.FindLast() then;
        exit(ReminderLineExtra."Line No.");
    end;

    local procedure SetReminderLevel(ReminderHeader: Record "Reminder Header"; var ReminderLine: Record "Reminder Line"; LineLevel: Integer)
    begin
        if ReminderHeader."Use Header Level" then
            ReminderLine."No. of Reminders" := ReminderHeader."Reminder Level"
        else
            ReminderLine."No. of Reminders" := LineLevel;
    end;

    local procedure RemoveLinesOfNegativeReminder(var ReminderHeader: Record "Reminder Header")
    var
        ReminderTotal: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeRemoveLinesOfNegativeReminder(ReminderHeader, GlobalReminderHeaderReq, GlobalCustomer, IsHandled);
        if IsHandled then
            exit;

        ReminderHeader.CalcFields(
          "Remaining Amount", "Interest Amount", "Additional Fee", "VAT Amount");

        ReminderTotal := ReminderHeader."Remaining Amount" + ReminderHeader."Interest Amount" +
          ReminderHeader."Additional Fee" + ReminderHeader."VAT Amount";

        if ReminderTotal < 0 then
            ReminderHeader.Delete(true);
    end;

    local procedure GetOpenEntriesNotDueOnHoldTranslated(CustomerLanguageCode: Code[10]; var OpenEntriesNotDueTranslated: Text[100]; var OpenEntriesOnHoldTranslated: Text[100])
    var
        Language: Codeunit Language;
        CurrentLanguageCode: Integer;
    begin
        if CustomerLanguageCode <> '' then begin
            CurrentLanguageCode := GlobalLanguage;
            GlobalLanguage(Language.GetLanguageIdOrDefault(CustomerLanguageCode));
            OpenEntriesNotDueTranslated := OpenEntriesNotDueLbl;
            OpenEntriesOnHoldTranslated := OpenEntriesOnHoldLbl;
            GlobalLanguage(CurrentLanguageCode);
        end else begin
            OpenEntriesNotDueTranslated := OpenEntriesNotDueLbl;
            OpenEntriesOnHoldTranslated := OpenEntriesOnHoldLbl;
        end;
    end;

    local procedure RemoveNotDueLinesInSectionReminderLine(ReminderLine: Record "Reminder Line")
    var
        ReminderLineToDelete: Record "Reminder Line";
    begin
        ReminderLineToDelete.SetRange("Reminder No.", ReminderLine."Reminder No.");
        ReminderLineToDelete.SetRange(Type, ReminderLine.Type);
        ReminderLineToDelete.SetRange("Entry No.", ReminderLine."Entry No.");
        ReminderLineToDelete.SetRange("Document Type", ReminderLine."Document Type");
        ReminderLineToDelete.SetRange("Document No.", ReminderLine."Document No.");
        ReminderLineToDelete.SetFilter("Line Type", '<>%1', ReminderLine."Line Type");
        if ReminderLineToDelete.FindFirst() then
            ReminderLineToDelete.Delete(true);
    end;

    local procedure CheckCustomerIsBlocked(Customer: Record Customer) Result: Boolean
    begin
        Result := Customer.Blocked = Customer.Blocked::All;
        OnAfterCheckCustomerIsBlocked(Customer, Result);
    end;

    local procedure IsGracePeriodExpiredForOverdueEntry(DueDate: Date; ReminderDocumentDate: Date; GracePeriod: DateFormula): Boolean
    begin
        exit(CalcDate(GracePeriod, DueDate) < ReminderDocumentDate);
    end;

    /// <summary>
    /// Raised after the reminder creation process completes.
    /// </summary>
    /// <param name="RetVal">Indicates whether at least one reminder was created successfully.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterCode(var RetVal: Boolean)
    begin
    end;

    /// <summary>
    /// Raised after filters are applied to customer ledger entries based on reminder level.
    /// </summary>
    /// <param name="CustLedgerEntry">The customer ledger entry record being filtered.</param>
    /// <param name="ReminderLevel">The reminder level used for filtering.</param>
    /// <param name="ReminderTerms">The reminder terms configuration.</param>
    /// <param name="Customer">The customer record for the reminder.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderHeader">The reminder header being created.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterFilterCustLedgEntryReminderLevel(var CustLedgerEntry: Record "Cust. Ledger Entry"; var ReminderLevel: Record "Reminder Level"; ReminderTerms: Record "Reminder Terms"; Customer: Record Customer; ReminderHeaderReq: Record "Reminder Header"; ReminderHeader: Record "Reminder Header")
    begin
    end;

    /// <summary>
    /// Raised after a reminder line is initialized with default values.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header for the line.</param>
    /// <param name="ReminderLine">The initialized reminder line.</param>
    /// <param name="LineType">The type of the reminder line.</param>
    /// <param name="Description">The description for the reminder line.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterInitReminderLine(var ReminderHeader: Record "Reminder Header"; var ReminderLine: Record "Reminder Line"; LineType: Enum "Reminder Line Type"; Description: Text)
    begin
    end;

    /// <summary>
    /// Raised after a reminder document is created with all its lines.
    /// </summary>
    /// <param name="ReminderHeader">The completed reminder header.</param>
    /// <param name="ReminderLine">The last reminder line processed.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterMakeReminder(var ReminderHeader: Record "Reminder Header"; var ReminderLine: Record "Reminder Line")
    begin
    end;

    /// <summary>
    /// Raised after the reminder creation parameters are set.
    /// </summary>
    /// <param name="Cust">The customer for reminder creation.</param>
    /// <param name="CustLedgEntry">The customer ledger entries filter.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="OverdueEntriesOnly">Indicates whether only overdue entries are included.</param>
    /// <param name="IncludeEntriesOnHold">Indicates whether entries on hold are included.</param>
    /// <param name="CustLedgEntryLinefeeOn">The customer ledger entries filter for line fees.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterSet(var Cust: Record Customer; var CustLedgEntry: Record "Cust. Ledger Entry"; var ReminderHeaderReq: Record "Reminder Header"; var OverdueEntriesOnly: Boolean; var IncludeEntriesOnHold: Boolean; var CustLedgEntryLinefeeOn: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised after the reminder level and due date are calculated for a customer ledger entry.
    /// </summary>
    /// <param name="CustLedgEntry">The customer ledger entry being processed.</param>
    /// <param name="LineLevel2">The calculated reminder level for the entry.</param>
    /// <param name="ReminderDueDate2">The calculated due date for the reminder.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterSetReminderLine(CustLedgEntry: Record "Cust. Ledger Entry"; var LineLevel2: Integer; var ReminderDueDate2: Date)
    begin
    end;

    /// <summary>
    /// Raised after the suggest lines parameters are set for an existing reminder.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header to add lines to.</param>
    /// <param name="CustLedgEntry2">The customer ledger entries filter.</param>
    /// <param name="OverdueEntriesOnly2">Indicates whether only overdue entries are included.</param>
    /// <param name="IncludeEntriesOnHold2">Indicates whether entries on hold are included.</param>
    /// <param name="CustLedgEntryLinefeeOn">The customer ledger entries filter for line fees.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterSuggestLines(ReminderHeader: Record "Reminder Header"; var CustLedgEntry2: Record "Cust. Ledger Entry"; OverdueEntriesOnly2: Boolean; IncludeEntriesOnHold2: Boolean; var CustLedgEntryLinefeeOn: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised after the remaining amount is calculated for a customer ledger entry when adding line fees.
    /// </summary>
    /// <param name="CustLedgerEntry">The customer ledger entry with calculated remaining amount.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAddLineFeeForCustLedgEntryOnAfterCalcRemainingAmount(var CustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised before inserting a line fee reminder line.
    /// </summary>
    /// <param name="ReminderLine">The line fee reminder line to be inserted.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAddLineFeeForCustLedgEntryOnReminderLineInsert(var ReminderLine: Record "Reminder Line")
    begin
    end;

    /// <summary>
    /// Raised before checking if the amounts not due line has been inserted.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header being processed.</param>
    /// <param name="ReminderLine">The current reminder line.</param>
    /// <param name="AmountsNotDueLineInserted">Indicates whether the amounts not due line has been inserted.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilterOnBeforeCheckAmountsNotDueLineInserted(ReminderHeader: Record "Reminder Header"; ReminderLine: Record "Reminder Line"; var AmountsNotDueLineInserted: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before adding reminder lines from customer ledger entries with no reminder level filter.
    /// </summary>
    /// <param name="CustLedgEntry">The customer ledger entry to add.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderHeader">The reminder header being created.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    /// <param name="ReminderLine">The reminder line being created.</param>
    /// <param name="NextLineNo">The next line number to use.</param>
    /// <param name="StartLineInserted">Indicates whether a start line was inserted.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeAddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilter(var CustLedgEntry: Record "Cust. Ledger Entry"; ReminderHeaderReq: Record "Reminder Header"; ReminderHeader: Record "Reminder Header"; var IsHandled: Boolean; var ReminderLine: Record "Reminder Line"; var NextLineNo: Integer; var StartLineInserted: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before finding customer ledger entries for reminder creation.
    /// </summary>
    /// <param name="CustLedgerEntry">The customer ledger entry record to search.</param>
    /// <param name="ReminderHeader">The reminder header request parameters.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCustLedgerEntryFind(var CustLedgerEntry: Record "Cust. Ledger Entry"; ReminderHeader: Record "Reminder Header"; Customer: Record Customer)
    begin
    end;

    /// <summary>
    /// Raised before retrieving the reminder terms for a customer.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header being processed.</param>
    /// <param name="ReminderTerms">The reminder terms to retrieve.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetReminderTerms(var ReminderHeader: Record "Reminder Header"; var ReminderTerms: Record "Reminder Terms"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before finding and marking customer ledger entries as reminder candidates.
    /// </summary>
    /// <param name="ReminderLevel">The reminder level configuration.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderTerms">The reminder terms configuration.</param>
    /// <param name="ReminderEntry">The reminder entry record for history lookup.</param>
    /// <param name="CustLedgEntry">The customer ledger entries to process.</param>
    /// <param name="TempCustLedgEntryOnHold">Temporary table for entries on hold.</param>
    /// <param name="CustLedgEntryLastIssuedReminderLevelFilter">Filter for last issued reminder level.</param>
    /// <param name="CustAmount">The cumulative customer amount.</param>
    /// <param name="MakeDoc">Indicates whether to create a reminder document.</param>
    /// <param name="MaxReminderLevel">The maximum reminder level found.</param>
    /// <param name="MaxLineLevel">The maximum line level found.</param>
    /// <param name="OverdueEntriesOnly">Indicates whether only overdue entries are included.</param>
    /// <param name="IncludeEntriesOnHold">Indicates whether entries on hold are included.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    /// <param name="Result">Returns the result when handling is skipped.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeFindAndMarkReminderCandidates(var ReminderLevel: Record "Reminder Level"; ReminderHeaderReq: Record "Reminder Header"; ReminderTerms: Record "Reminder Terms"; var ReminderEntry: Record "Reminder/Fin. Charge Entry"; var CustLedgEntry: Record "Cust. Ledger Entry"; var TempCustLedgEntryOnHold: Record "Cust. Ledger Entry" temporary; var CustLedgEntryLastIssuedReminderLevelFilter: Text; var CustAmount: Decimal; var MakeDoc: Boolean; var MaxReminderLevel: Integer; var MaxLineLevel: Integer; OverdueEntriesOnly: Boolean; IncludeEntriesOnHold: Boolean; var IsHandled: Boolean; var Result: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before inserting a reminder line.
    /// </summary>
    /// <param name="ReminderNo">The reminder document number.</param>
    /// <param name="LineType">The type of the reminder line.</param>
    /// <param name="Description">The description for the line.</param>
    /// <param name="NextLineNo">The next line number to use.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeInsertReminderLine(ReminderNo: Code[20]; LineType: Enum "Reminder Line Type"; Description: Text[100]; var NextLineNo: Integer; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before creating a reminder for a specific currency.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header to create.</param>
    /// <param name="CurrencyCode">The currency code for the reminder.</param>
    /// <param name="RetVal">Returns the result when handling is skipped.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="OverdueEntriesOnly">Indicates whether only overdue entries are included.</param>
    /// <param name="IncludeEntriesOnHold">Indicates whether entries on hold are included.</param>
    /// <param name="HeaderExists">Indicates whether a reminder header already exists.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeMakeReminder(var ReminderHeader: Record "Reminder Header"; CurrencyCode: Code[10]; var RetVal: Boolean; var IsHandled: Boolean; ReminderHeaderReq: Record "Reminder Header"; OverdueEntriesOnly: Boolean; IncludeEntriesOnHold: Boolean; HeaderExists: Boolean; Customer: Record Customer)
    begin
    end;

    /// <summary>
    /// Raised before marking a customer ledger entry as a reminder candidate.
    /// </summary>
    /// <param name="CustLedgEntry">The customer ledger entry to evaluate.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeMarkReminderCandidate(var CustLedgEntry: Record "Cust. Ledger Entry"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before removing lines from a reminder with negative total amount.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header to evaluate.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeRemoveLinesOfNegativeReminder(var ReminderHeader: Record "Reminder Header"; ReminderHeaderReq: Record "Reminder Header"; Customer: Record Customer; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before finding an existing reminder header for the customer and currency.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header record to search.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderTerms">The reminder terms configuration.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    /// <param name="CustLedgerEntry">The customer ledger entry being processed.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeReminderHeaderFind(var ReminderHeader: Record "Reminder Header"; ReminderHeaderReq: Record "Reminder Header"; ReminderTerms: Record "Reminder Terms"; Customer: Record Customer; CustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised before inserting a new reminder header.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header to insert.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderTerms">The reminder terms configuration.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeReminderHeaderInsert(var ReminderHeader: Record "Reminder Header"; ReminderHeaderReq: Record "Reminder Header"; ReminderTerms: Record "Reminder Terms"; Customer: Record Customer)
    begin
    end;

    /// <summary>
    /// Raised before modifying the reminder header with reminder level and other settings.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header to modify.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="HeaderExists">Indicates whether the header already existed.</param>
    /// <param name="ReminderTerms">The reminder terms configuration.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    /// <param name="ReminderLevel">The reminder level being applied.</param>
    /// <param name="CustLedgerEntry">The customer ledger entries being processed.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeReminderHeaderModify(var ReminderHeader: Record "Reminder Header"; var ReminderHeaderReq: Record "Reminder Header"; HeaderExists: Boolean; ReminderTerms: Record "Reminder Terms"; Customer: Record Customer; ReminderLevel: Record "Reminder Level"; var CustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised before inserting a reminder line for a customer ledger entry.
    /// </summary>
    /// <param name="ReminderLine">The reminder line to insert.</param>
    /// <param name="ReminderHeader">The reminder header for the line.</param>
    /// <param name="ReminderLevel">The reminder level for the line.</param>
    /// <param name="CustLedgerEntry">The customer ledger entry for the line.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeReminderLineInsert(var ReminderLine: Record "Reminder Line"; ReminderHeader: Record "Reminder Header"; ReminderLevel: Record "Reminder Level"; CustLedgerEntry: Record "Cust. Ledger Entry"; ReminderHeaderReq: Record "Reminder Header")
    begin
    end;

    /// <summary>
    /// Raised before processing reminders for each currency.
    /// </summary>
    /// <param name="CustLedgEntry">The customer ledger entries to process.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderTerms">The reminder terms configuration.</param>
    /// <param name="OverdueEntriesOnly">Indicates whether only overdue entries are included.</param>
    /// <param name="IncludeEntriesOnHold">Indicates whether entries on hold are included.</param>
    /// <param name="HeaderExists">Indicates whether a reminder header already exists.</param>
    /// <param name="CustLedgEntryLastIssuedReminderLevelFilter">Filter for last issued reminder level.</param>
    /// <param name="TempCurrency">Temporary table of currencies to process.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    /// <param name="CustLedgEntryLineFeeFilters">The customer ledger entries filter for line fees.</param>
    [IntegrationEvent(false, false)]
    local procedure OnCodeOnBeforeCurrencyLoop(CustLedgEntry: Record "Cust. Ledger Entry"; ReminderHeaderReq: Record "Reminder Header"; ReminderTerms: Record "Reminder Terms"; OverdueEntriesOnly: Boolean; IncludeEntriesOnHold: Boolean; HeaderExists: Boolean; CustLedgEntryLastIssuedReminderLevelFilter: Text; var TempCurrency: Record Currency temporary; Customer: Record Customer; var CustLedgEntryLineFeeFilters: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised before retrieving reminder terms for processing.
    /// </summary>
    /// <param name="Customer">The customer to get reminder terms for.</param>
    /// <param name="CustLedgerEntry">The customer ledger entries being processed.</param>
    /// <param name="CustLedgEntryLastIssuedReminderLevelFilter">Filter for last issued reminder level.</param>
    /// <param name="ReminderHeader">The reminder header being processed.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    /// <param name="ReturnValue">Returns the result when handling is skipped.</param>
    [IntegrationEvent(false, false)]
    local procedure OnCodeOnBeforeGetReminderTerms(var Customer: Record Customer; var CustLedgerEntry: Record "Cust. Ledger Entry"; var CustLedgEntryLastIssuedReminderLevelFilter: Text; var ReminderHeader: Record "Reminder Header"; var IsHandled: Boolean; var ReturnValue: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before processing each customer ledger entry in the reminder candidate loop.
    /// </summary>
    /// <param name="CustLedgerEntry">The customer ledger entry being processed.</param>
    /// <param name="ReminderHeader">The reminder header request parameters.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    [IntegrationEvent(false, false)]
    local procedure OnFindAndMarkReminderCandidatesOnBeforeCustLedgEntryLoop(var CustLedgerEntry: Record "Cust. Ledger Entry"; var ReminderHeader: Record "Reminder Header"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Raised after finding and marking reminder candidates from customer ledger entries.
    /// </summary>
    /// <param name="CustLedgEntry">The customer ledger entries processed.</param>
    /// <param name="ReminderLevel">The reminder level configuration.</param>
    /// <param name="CustAmount">The cumulative customer amount.</param>
    /// <param name="MakeDoc">Indicates whether to create a reminder document.</param>
    /// <param name="MaxReminderLevel">The maximum reminder level found.</param>
    /// <param name="MaxLineLevel">The maximum line level found.</param>
    /// <param name="IsHandled">Set to true to skip default result.</param>
    /// <param name="Result">Returns the result when handling is skipped.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterFindAndMarkReminderCandidates(CustLedgEntry: Record "Cust. Ledger Entry"; ReminderLevel: Record "Reminder Level"; var CustAmount: Decimal; MakeDoc: Boolean; MaxReminderLevel: Integer; MaxLineLevel: Integer; var IsHandled: Boolean; var Result: Boolean)
    begin
    end;

    /// <summary>
    /// Raised after calculating whether a reminder document should be created.
    /// </summary>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderHeader">The reminder header being created.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    /// <param name="ShouldMakeDoc">Indicates whether a reminder document should be created.</param>
    /// <param name="MakeDoc">The original make document flag.</param>
    /// <param name="CustLedgerEntry">The customer ledger entries being processed.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnAfterCalcShouldMakeDoc(ReminderHeaderReq: Record "Reminder Header"; ReminderHeader: Record "Reminder Header"; Customer: Record Customer; var ShouldMakeDoc: Boolean; MakeDoc: Boolean; var CustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised after adding reminder lines from customer ledger entries with no reminder level filter.
    /// </summary>
    /// <param name="CustLedgerEntry">The customer ledger entries processed.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    /// <param name="ReminderHeader">The reminder header being created.</param>
    /// <param name="MaxReminderLevel">The maximum reminder level.</param>
    /// <param name="OverdueEntriesOnly">Indicates whether only overdue entries are included.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnAfterAddRemiderLinesFromCustLedgEntriesWithNoReminderLevelFilter(var CustLedgerEntry: Record "Cust. Ledger Entry"; Customer: Record Customer; ReminderHeader: Record "Reminder Header"; MaxReminderLevel: Integer; var OverdueEntriesOnly: Boolean)
    begin
    end;

    /// <summary>
    /// Raised after processing each reminder level in the reminder creation loop.
    /// </summary>
    /// <param name="ReminderLevel">The reminder level processed.</param>
    /// <param name="NextLineNo">The next line number available.</param>
    /// <param name="StartLineInserted">Indicates whether a start line was inserted.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderHeader">The reminder header being created.</param>
    /// <param name="Customer">The customer for the reminder.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnAfterReminderLevelLoop(var ReminderLevel: Record "Reminder Level"; var NextLineNo: Integer; StartLineInserted: Boolean; ReminderHeaderReq: Record "Reminder Header"; ReminderHeader: Record "Reminder Header"; Customer: Record Customer)
    begin
    end;

    /// <summary>
    /// Raised after calculating whether the grace period has expired for an overdue entry.
    /// </summary>
    /// <param name="ReminderDueDate">The due date of the reminder entry.</param>
    /// <param name="ReminderHeader">The reminder header request parameters.</param>
    /// <param name="IsGracePeriodExpired">Indicates whether the grace period has expired.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnAfterCalcIsGracePeriodExpired(var ReminderDueDate: Date; var ReminderHeader: Record "Reminder Header"; var IsGracePeriodExpired: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before finding customer ledger entries for processing in the reminder creation.
    /// </summary>
    /// <param name="CustLedgEntry">The customer ledger entry record to search.</param>
    /// <param name="Cust">The customer for the reminder.</param>
    /// <param name="ReminderHeader">The reminder header being created.</param>
    /// <param name="MaxReminderLevel">The maximum reminder level.</param>
    /// <param name="OverDueEntriesOnly">Indicates whether only overdue entries are included.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnBeforeCustLedgEntryFindSet(var CustLedgEntry: Record "Cust. Ledger Entry"; Cust: Record Customer; ReminderHeader: Record "Reminder Header"; MaxReminderLevel: Integer; var OverDueEntriesOnly: Boolean)
    begin
    end;

    /// <summary>
    /// Raised before inserting a reminder line for an on-hold customer ledger entry.
    /// </summary>
    /// <param name="ReminderLine">The reminder line to insert.</param>
    /// <param name="ReminderHeader">The reminder header for the line.</param>
    /// <param name="ReminderLevel">The reminder level configuration.</param>
    /// <param name="CustLedgerEntry">The customer ledger entries being processed.</param>
    /// <param name="CustLedgEntryOnHoldTEMP">Temporary table of entries on hold.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnBeforeOnHoldReminderLineInsert(var ReminderLine: Record "Reminder Line"; ReminderHeader: Record "Reminder Header"; ReminderLevel: Record "Reminder Level"; var CustLedgerEntry: Record "Cust. Ledger Entry"; var CustLedgEntryOnHoldTEMP: Record "Cust. Ledger Entry" temporary)
    begin
    end;

    /// <summary>
    /// Raised before modifying the reminder header after all lines are processed.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header to modify.</param>
    /// <param name="ReminderLine">The last reminder line processed.</param>
    /// <param name="NextLineNo">The next line number available.</param>
    /// <param name="MaxReminderLevel">The maximum reminder level.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnBeforeReminderHeaderModify(var ReminderHeader: Record "Reminder Header"; var ReminderLine: Record "Reminder Line"; var NextLineNo: Integer; MaxReminderLevel: Integer)
    begin
    end;

    /// <summary>
    /// Raised after calculating whether the grace period has expired when marking reminder candidates.
    /// </summary>
    /// <param name="ReminderLevel">The reminder level configuration.</param>
    /// <param name="ReminderDueDate">The due date of the reminder entry.</param>
    /// <param name="ReminderHeaderReq">The reminder header request parameters.</param>
    /// <param name="ReminderTerms">The reminder terms configuration.</param>
    /// <param name="CustLedgerEntry">The customer ledger entry being evaluated.</param>
    /// <param name="ReminderHeader">The reminder header being created.</param>
    /// <param name="LineLevel">The calculated line level.</param>
    /// <param name="IsGracePeriodExpired">Indicates whether the grace period has expired.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMarkReminderCandidateOnAfterCalcIsGracePeriodExpired(var ReminderLevel: Record "Reminder Level"; var ReminderDueDate: Date; var ReminderHeaderReq: Record "Reminder Header"; var ReminderTerms: Record "Reminder Terms"; var CustLedgerEntry: Record "Cust. Ledger Entry"; var ReminderHeader: Record "Reminder Header"; var LineLevel: Integer; var IsGracePeriodExpired: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Raised after setting filters on the reminder entry record when calculating reminder level.
    /// </summary>
    /// <param name="ReminderFinChargeEntry">The reminder entry record with filters applied.</param>
    [IntegrationEvent(false, false)]
    local procedure OnSetReminderLineOnAfterSetFilters(var ReminderFinChargeEntry: Record "Reminder/Fin. Charge Entry")
    begin
    end;

    /// <summary>
    /// Raised after finding the next line level from the reminder entry history.
    /// </summary>
    /// <param name="ReminderEntry">The reminder entry record found.</param>
    /// <param name="LineLevel2">The calculated next line level.</param>
    /// <param name="ReminderDueDate2">The due date from the reminder entry.</param>
    [IntegrationEvent(false, false)]
    local procedure OnSetReminderLineOnAfterFindNextLineLevel(ReminderEntry: Record "Reminder/Fin. Charge Entry"; var LineLevel2: Integer; var ReminderDueDate2: Date)
    begin
    end;

    /// <summary>
    /// Raised before inserting a reminder line for entries not due with no reminder level filter.
    /// </summary>
    /// <param name="ReminderLine">The reminder line to insert.</param>
    /// <param name="ReminderHeader">The reminder header for the line.</param>
    /// <param name="ReminderLevel">The reminder level configuration.</param>
    /// <param name="CustLedgerEntry">The customer ledger entry for the line.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAddRemiderLinesFromCustLedgEntryWithNoReminderLevelFilterOnBeforeReminderLineInsert(var ReminderLine: Record "Reminder Line"; ReminderHeader: Record "Reminder Header"; ReminderLevel: Record "Reminder Level"; var CustLedgerEntry: Record "Cust. Ledger Entry")
    begin
    end;

    /// <summary>
    /// Raised after checking if a customer is blocked.
    /// </summary>
    /// <param name="Customer">The customer record that was checked.</param>
    /// <param name="Result">Returns whether the customer is blocked.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterCheckCustomerIsBlocked(Customer: Record Customer; var Result: Boolean)
    begin
    end;

    /// <summary>
    /// Raised after filtering customer ledger entries in the make reminder process.
    /// </summary>
    /// <param name="ReminderLine">The reminder line being processed.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnAfterFilterCustLedgEntries(var ReminderLine: Record "Reminder Line")
    begin
    end;

    /// <summary>
    /// Raised before the reminder header inserts its standard lines.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header about to insert lines.</param>
    [IntegrationEvent(false, false)]
    local procedure OnMakeReminderOnBeforeReminderHeaderInsertLines(var ReminderHeader: Record "Reminder Header")
    begin
    end;

    /// <summary>
    /// Raised before setting the reminder line level and due date.
    /// </summary>
    /// <param name="LineLevel2">The line level to be set.</param>
    /// <param name="ReminderDueDate2">The reminder due date to be set.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    /// <param name="CustLedgerEntry">The customer ledger entry being processed.</param>
    /// <param name="ReminderFinChargeEntry">The reminder entry record for history lookup.</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeSetReminderLine(var LineLevel2: Integer; var ReminderDueDate2: Date; var IsHandled: Boolean; var CustLedgerEntry: Record "Cust. Ledger Entry"; var ReminderFinChargeEntry: Record "Reminder/Fin. Charge Entry")
    begin
    end;

    /// <summary>
    /// Raised after inserting all reminder lines in the loop.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header with inserted lines.</param>
    /// <param name="CurrencyCode">The currency code for the reminder.</param>
    /// <param name="NextLineNo">The next available line number.</param>
    /// <param name="MaxReminderLevel">The maximum reminder level applied.</param>
    /// <param name="OverdueEntriesOnly">Indicates whether only overdue entries were included.</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterReminderLinesInsertLoop(var ReminderHeader: Record "Reminder Header"; CurrencyCode: Code[10]; var NextLineNo: Integer; var MaxReminderLevel: Integer; OverdueEntriesOnly: Boolean);
    begin
    end;

    /// <summary>
    /// Raised after retrieving the customer for an existing reminder header.
    /// </summary>
    /// <param name="ReminderHeader">The reminder header being processed.</param>
    /// <param name="Customer">The customer retrieved for the reminder.</param>
    /// <param name="IsHandled">Set to true to skip default processing.</param>
    /// <param name="ReturnValue">Returns the result when handling is skipped.</param>
    [IntegrationEvent(false, false)]
    local procedure OnCodeOnAfterGlobalReminderGetGlobalCustomer(var ReminderHeader: Record "Reminder Header"; var Customer: Record Customer; var IsHandled: Boolean; var ReturnValue: Boolean)
    begin
    end;
}