Codeunit 1255 Match Bank Payments, source in 29
Source29
src/Layers/W1/BaseApp/Bank/Reconciliation/MatchBankPayments.Codeunit.al3187 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.DirectDebit;
using Microsoft.Bank.Ledger;
using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.HumanResources.Employee;
using Microsoft.HumanResources.Payables;
using Microsoft.Purchases.Payables;
using Microsoft.Purchases.Setup;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
using Microsoft.Sales.Receivables;
using Microsoft.Sales.Setup;
using System.Environment.Configuration;
using System.Telemetry;
/// <summary>
/// Core engine for automatic matching of bank statement lines to customer, vendor, and general ledger entries.
/// This codeunit implements sophisticated matching algorithms that analyze transaction amounts, dates, references,
/// and party names to automatically apply payments and reconcile bank transactions. Supports configurable matching
/// rules, confidence scoring, and multiple application scenarios including one-to-one, one-to-many, and many-to-one matching.
/// Integrates with payment discount calculations, tolerance handling, and text-to-account mapping.
/// </summary>
/// <remarks>
/// Key features include automatic payment application with configurable matching rules, text analysis for party identification,
/// amount and date tolerance matching, payment discount support, multi-currency handling, and comprehensive logging.
/// The codeunit uses scoring algorithms to rank match quality and applies configurable thresholds to determine
/// automatic vs. manual application. Integration events enable customization of matching logic and application rules.
/// Supports both payment reconciliation and bank reconciliation workflows with optimized performance for large volumes.
/// </remarks>
#pragma warning disable AA0198
codeunit 1255 "Match Bank Payments"
{
Permissions = TableData "Cust. Ledger Entry" = rm,
TableData "Vendor Ledger Entry" = rm;
TableNo = "Bank Acc. Reconciliation Line";
trigger OnRun()
var
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
begin
BankAccReconciliationLine.Copy(Rec);
Code(BankAccReconciliationLine);
Rec := BankAccReconciliationLine;
OnAfterCode(BankAccReconciliationLine);
end;
/// <summary>
/// Runs the automatic payment matching process without overwriting manual or accepted matches.
/// This procedure preserves user decisions while applying automatic matching to unprocessed lines,
/// ensuring that manual interventions and accepted applications are not disturbed by automation.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line record set to process for automatic matching.</param>
procedure MatchNoOverwriteOfManualOrAccepted(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
BankAccReconciliationLineBackup: Record "Bank Acc. Reconciliation Line";
begin
BankAccReconciliationLineBackup.Copy(BankAccReconciliationLine);
Code(BankAccReconciliationLineBackup, false);
BankAccReconciliationLine := BankAccReconciliationLineBackup;
OnAfterCode(BankAccReconciliationLine);
end;
var
BankAccount: Record "Bank Account";
BankPmtApplSettings: Record "Bank Pmt. Appl. Settings";
TempBankPmtApplRule: Record "Bank Pmt. Appl. Rule" temporary;
TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary;
#pragma warning disable AA0470
TextMapperRulesOverridenTxt: Label '%1 text mapper rules could be applied. They were overridden because a record with the %2 match confidence was found.';
MultipleEntriesWithSilarConfidenceFoundTxt: Label 'There are %1 ledger entries that this statement line could be applied to with the same confidence.';
MultipleStatementLinesWithSameConfidenceFoundTxt: Label 'There are %1 alternative statement lines that could be applied to the same ledger entry with the same confidence.';
#pragma warning restore AA0470
CannotFindRuleLbl: Label 'Unexpected - could not find the payment application rule, score %1', Locked = true, Comment = '%1 is the score';
AutomatchEventNameTelemetryTxt: Label 'Automatch', Locked = true;
TempOneToManyTempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary;
TempBankStmtMultipleMatchLine: Record "Bank Stmt Multiple Match Line" temporary;
TempCustomerLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary;
TempVendorLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary;
TempBankAccLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary;
TempEmployeeLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary;
TempDirectDebitCollectionBuffer: Record "Direct Debit Collection Buffer" temporary;
BankPmtApplSettingsInitialized: Boolean;
ApplyEntries: Boolean;
CandidateFilterReferenceDate: Date;
#pragma warning disable AA0470
CannotApplyDocumentNoOneToManyApplicationTxt: Label 'Document No. %1 was not applied because the transaction amount was insufficient.';
#pragma warning restore AA0470
UsePaymentDiscounts: Boolean;
MinimumMatchScore: Integer;
RelatedPartyMatchedInfoText: Text;
DocumentMatchedInfoText: Text;
LogInfoText: Boolean;
MatchSummaryMsg: Label '%1 payment lines out of %2 are applied.\\', Comment = '%1 number of lines that are applied, %2 total number of lines';
#pragma warning disable AA0470
MatchingStmtLinesMsg: Label 'The matching of statement lines to open ledger entries is in progress.\\Please wait while the operation is being completed.\\#1####### @2@@@@@@@@@@@@@';
ProcessedStmtLinesMsg: Label 'Processed %1 out of %2 lines.';
#pragma warning restore AA0470
CreatingAppliedEntriesMsg: Label 'The application of statement lines to open ledger entries is in progress. Please wait while the operation is being completed.';
ProgressBarMsg: Label 'Please wait while the operation is being completed.';
MustChooseAccountErr: Label 'You must choose an account to transfer the difference to.';
LineSplitTxt: Label 'The value in the Transaction Amount field has been reduced by %1. A new line with %1 in the Transaction Amount field has been created.', Comment = '%1 - Difference';
BankAccountRecCategoryLbl: Label 'AL Payment Reconciliation', Locked = true;
BankAccountRecTotalAndMatchedLinesLbl: Label 'Total Bank Statement Lines: %1, of those applied: %2, and text-matched: %3', Locked = true;
MatchedRelatedPartyOnBankAccountMsg: Label 'The related party was matched based on the bank account in the statement file.';
MatchedRelatedPartyOnBankStatementInfoMsg: Label 'The related party was matched based on the %1 in the statement file.', Comment = '%1 is the name of the fields used, e.g. Address, Name...';
TextPartialMatchMsg: Label '%1 was matched with %2% accuracy.', Comment = '%1 is the name of the field, %2 is percent';
RelatedPartyNameIsUniqueMsg: Label 'The name of the related party is unique in your records.';
ExternalDocumentNumberMatchedInTransactionTextMsg: Label 'We found a match for the external document number in the transaction text.';
DocumentNumberMatchedInTransactionTextMsg: Label 'We found a match for the document number in the transaction text.';
DocumentNumberMatchedByPaymentReferenceMsg: Label 'We matched the document number by using the payment reference.';
NewLinePlaceholderLbl: Label '%1%2', Locked = true;
RelatedPartyMatchInfoPlaceholderLbl: Label '%1, %2, %3', Locked = true;
PaymentRecPreformanceCategoryLbl: Label 'PaymentRecPerformance', Locked = true;
MatchedLineTelemetryTxt: Label 'Line with SystemId: %1 Matched, Total Time: %2. Total time Customer Matching %3, Total time Vendor Matching: %4, Total time Employee Matching: %5,Total time Bank Ledger Entries: %6, Total Time TextMappings: %7', Locked = true;
AppliedEntriesToBankStatementLineTxt: Label 'Line with SystemId: %1 - Applied entries - Account Type: %2, One-To-Many match: %3, Match Quality: %4.', Locked = true;
SpecificTaskSummaryTxt: Label 'Specific Task Summary: TotalTimeDirectCollection: %1, TotalTimeRelatedPartyMatching: %2, TotalTimeDocumentNoMatching: %3, TotalTimeAmountMatching: %4, TotalTimeStringNearness: %5, TotalTimeDocumentNoMatchingForBankLedgerEntry: %6, TotalTimeSearchingDocumentNoInLedgerEntries: %7, HitCountClosingDocumentMatches: %8 out of %9', Locked = true;
MissingRelatedPartyTelemetryMsg: Label 'The ledger entries contain the entries that do not exist in master data. This is indication of corrupted ledger entries. Master data type %1.', Locked = true, Comment = '%1 Master Data Type';
PmtRecNoSeriesNotificationNameLbl: Label 'Payment reconciliation journals need a number series.';
PmtRecNoSeriesNotificationDescriptionLbl: Label 'Notifies the user that payment reconciliation journals are not assigned to a number series.';
EntriesMatchedElsewhereMsg: Label 'There are open %1 ledger entries applied in other journals. The entries were not considered in the automatic application. To consider these entries, unapply them first from the other journals.', Comment = '%1 - either "customer", "vendor" or "employee".';
DontShowAgainTxt: Label 'Don''t show again.';
TotalTimeMatchingCustomerLedgerEntriesPerLine: Duration;
TotalTimeMatchingVendorLedgerEntriesPerLine: Duration;
TotalTimeMatchingEmployeeLedgerEntriesPerLine: Duration;
TotalTimeMatchingBankLedgerEntriesPerLine: Duration;
TotalTimeTimeTextMappingsPerLine: Duration;
TotalTimeDirectCollection: Duration;
TotalTimeRelatedPartyMatching: Duration;
TotalTimeDocumentNoMatching: Duration;
TotalTimeAmountMatching: Duration;
TotalTimeStringNearness: Duration;
TotalTimeDocumentNoMatchingForBankLedgerEntry: Duration;
TotalTimeSearchingDocumentNoInLedgerEntries: Duration;
BankLedgerEntriesClosingDocumentNumbers: Dictionary of [Integer, List of [Code[35]]];
MissingDataTypeTelemetrySent: List of [Integer];
TotalNoClosingDocumentMatches: Integer;
HitCountClosingDocumentMatches: Integer;
/// <summary>
/// Determines whether a bank statement line is mapped using text-to-account mapping rules.
/// Checks if the line was automatically matched using predefined text patterns and retrieves
/// the applicable mapping rules if found. Used to identify and process text-based automatic applications.
/// </summary>
/// <param name="BankAccReconciliaitonLine">Bank reconciliation line to check for text-to-account mapping.</param>
/// <param name="TempTextToAccMapping">Temporary table to receive applicable text-to-account mapping rules.</param>
/// <returns>True if the line uses text-to-account mapping; false otherwise.</returns>
procedure IsTextToAccountMappig(
BankAccReconciliaitonLine: Record "Bank Acc. Reconciliation Line";
var TempTextToAccMapping: Record "Text-to-Account Mapping" temporary
): Boolean
var
IsMapToTextAccount: Boolean;
begin
IsMapToTextAccount := BankAccReconciliaitonLine."Match Confidence" = BankAccReconciliaitonLine."Match Confidence"::"High - Text-to-Account Mapping";
if IsMapToTextAccount then
FindApplicableTextMappings(BankAccReconciliaitonLine, TempTextToAccMapping);
exit(IsMapToTextAccount);
end;
/// <summary>
/// Determines whether a bank statement line was matched automatically based on payment application rules.
/// Evaluates match confidence and applied entries count against configured automatic application thresholds,
/// retrieving the specific rule that enabled automatic matching for audit and processing purposes.
/// </summary>
/// <param name="BankAccReconciliaitonLine">Bank reconciliation line to check for automatic matching status.</param>
/// <param name="BankPmtApplRule">Returns the payment application rule that enabled automatic matching.</param>
/// <returns>True if the line was matched automatically; false if manual intervention was required.</returns>
procedure IsMatchedAutomatically(
BankAccReconciliaitonLine: Record "Bank Acc. Reconciliation Line";
var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"
): Boolean
var
IsAutoMatch: Boolean;
MatchingRuleFound: Boolean;
begin
IsAutoMatch := BankPmtApplRule.IsMatchedAutomatically(BankAccReconciliaitonLine."Match Confidence".AsInteger(), BankAccReconciliaitonLine."Applied Entries");
if IsAutoMatch or (BankAccReconciliaitonLine."Match Confidence" = BankAccReconciliaitonLine."Match Confidence"::Accepted) then begin
BankPmtApplRule.SetRange(Score, BankAccReconciliaitonLine."Match Quality");
MatchingRuleFound := BankPmtApplRule.FindFirst();
if not MatchingRuleFound and IsAutoMatch then
Session.LogMessage('0000BN6', StrSubstNo(CannotFindRuleLbl, BankAccReconciliaitonLine."Match Quality"), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', BankAccountRecCategoryLbl);
IsAutoMatch := MatchingRuleFound;
BankPmtApplRule.SetRange(Score);
end;
exit(IsAutoMatch);
end;
/// <summary>
/// Retrieves comprehensive matching details for payment application analysis and reporting.
/// Analyzes the bank statement line and its matching entries to provide detailed information about
/// how the automatic payment application was performed, including related party matching, amount
/// tolerance analysis, document number matching, and direct debit correlation. Used by UI components
/// to display matching confidence details and help users understand application decisions.
/// </summary>
/// <param name="BankAccReconciliaitonLine">Bank reconciliation line to analyze for matching details.</param>
/// <param name="BankPmtApplRule">Payment application rule used for the matching process.</param>
/// <param name="MatchedAutomatically">Indicates whether the line was matched automatically or manually.</param>
/// <param name="RelatedPartyMatchedText">Returns text describing how related party matching was performed.</param>
/// <param name="AmountMatchText">Returns text describing amount matching results and tolerance application.</param>
/// <param name="DocumentMatchedText">Returns text describing document number matching results.</param>
/// <param name="DirectDebitMatchedText">Returns text describing direct debit matching correlation.</param>
/// <param name="DirectDebitMatched">Returns whether direct debit correlation was found.</param>
/// <param name="NoOfLedgerEntriesWithinAmountTolerance">Returns count of ledger entries within amount tolerance.</param>
/// <param name="NoOfLedgerEntriesOutsideAmountTolerance">Returns count of ledger entries outside amount tolerance.</param>
/// <param name="RelatedEntryAdditionalMatchInfo">Returns additional information about related entry matching.</param>
/// <param name="DocumentNoAdditionalMatchInfo">Returns additional information about document number matching.</param>
procedure GetMatchPaymentDetailsInfo(
BankAccReconciliaitonLine: Record "Bank Acc. Reconciliation Line";
BankPmtApplRule: Record "Bank Pmt. Appl. Rule";
MatchedAutomatically: Boolean;
var RelatedPartyMatchedText: Text;
var AmountMatchText: Text;
var DocumentMatchedText: Text;
var DirectDebitMatchedText: Text;
var DirectDebitMatched: Boolean;
var NoOfLedgerEntriesWithinAmountTolerance: Integer;
var NoOfLedgerEntriesOutsideAmountTolerance: Integer;
var RelatedEntryAdditionalMatchInfo: Text;
var DocumentNoAdditionalMatchInfo: Text
)
var
ActualBankPmtApplRule: Record "Bank Pmt. Appl. Rule";
TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary;
MinAmount: Decimal;
MaxAmount: Decimal;
RemainingAmount: Decimal;
TempLedgerEntryFound: Boolean;
AppliesToEntriesFilter: Text;
begin
BankAccReconciliaitonLine.GetAmountRangeForTolerance(MinAmount, MaxAmount);
case BankAccReconciliaitonLine."Account Type" of
BankAccReconciliaitonLine."Account Type"::Customer:
begin
if TempCustomerLedgerEntryMatchingBuffer.IsEmpty() then
InitializeCustomerLedgerEntriesMatchingBuffer(BankAccReconciliaitonLine, TempCustomerLedgerEntryMatchingBuffer);
NoOfLedgerEntriesWithinAmountTolerance := TempCustomerLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(MinAmount, MaxAmount, BankAccReconciliaitonLine."Transaction Date", UsePaymentDiscounts);
NoOfLedgerEntriesOutsideAmountTolerance := TempCustomerLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesOutsideRange(MinAmount, MaxAmount, BankAccReconciliaitonLine."Transaction Date", UsePaymentDiscounts);
TempLedgerEntryMatchingBuffer.Copy(TempCustomerLedgerEntryMatchingBuffer, true);
end;
BankAccReconciliaitonLine."Account Type"::Vendor:
begin
if TempVendorLedgerEntryMatchingBuffer.IsEmpty() then
InitializeVendorLedgerEntriesMatchingBuffer(BankAccReconciliaitonLine, TempVendorLedgerEntryMatchingBuffer);
NoOfLedgerEntriesWithinAmountTolerance := TempVendorLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(MinAmount, MaxAmount, BankAccReconciliaitonLine."Transaction Date", UsePaymentDiscounts);
NoOfLedgerEntriesOutsideAmountTolerance := TempVendorLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesOutsideRange(MinAmount, MaxAmount, BankAccReconciliaitonLine."Transaction Date", UsePaymentDiscounts);
TempLedgerEntryMatchingBuffer.Copy(TempVendorLedgerEntryMatchingBuffer, true);
end;
BankAccReconciliaitonLine."Account Type"::"Bank Account":
begin
if TempBankAccLedgerEntryMatchingBuffer.IsEmpty() then
InitializeBankAccLedgerEntriesMatchingBuffer(BankAccReconciliaitonLine, TempBankAccLedgerEntryMatchingBuffer);
NoOfLedgerEntriesWithinAmountTolerance := TempBankAccLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(MinAmount, MaxAmount, BankAccReconciliaitonLine."Transaction Date", UsePaymentDiscounts);
NoOfLedgerEntriesOutsideAmountTolerance := TempBankAccLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesOutsideRange(MinAmount, MaxAmount, BankAccReconciliaitonLine."Transaction Date", UsePaymentDiscounts);
TempLedgerEntryMatchingBuffer.Copy(TempBankAccLedgerEntryMatchingBuffer, true);
end;
end;
if not MatchedAutomatically then
exit;
TempLedgerEntryFound := false;
AppliesToEntriesFilter := BankAccReconciliaitonLine.GetAppliedToEntryFilter();
if AppliesToEntriesFilter <> '' then begin
TempLedgerEntryMatchingBuffer.SetFilter("Entry No.", StrSubstNo('=%1', AppliesToEntriesFilter));
TempLedgerEntryFound := TempLedgerEntryMatchingBuffer.FindFirst();
end;
LogInfoText := true;
if TempLedgerEntryFound then
MatchEntries(TempLedgerEntryMatchingBuffer, BankAccReconciliaitonLine, BankAccReconciliaitonLine."Account Type", ActualBankPmtApplRule, RemainingAmount)
else begin
Session.LogMessage('0000BN7', StrSubstNo(
'Could not find the Temp Ledger Entry - Filter %1, Account Type %2',
BankAccReconciliaitonLine.GetAppliedToEntryFilter(),
BankAccReconciliaitonLine."Account Type"), Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', BankAccountRecCategoryLbl);
ActualBankPmtApplRule := BankPmtApplRule;
end;
AmountMatchText := Format(ActualBankPmtApplRule."Amount Incl. Tolerance Matched");
RelatedPartyMatchedText := Format(ActualBankPmtApplRule."Related Party Matched");
RelatedEntryAdditionalMatchInfo := RelatedPartyMatchedInfoText;
DocumentNoAdditionalMatchInfo := DocumentMatchedInfoText;
DocumentMatchedText := Format(ActualBankPmtApplRule."Doc. No./Ext. Doc. No. Matched");
DirectDebitMatchedText := Format(ActualBankPmtApplRule."Direct Debit Collect. Matched");
DirectDebitMatched := ActualBankPmtApplRule."Direct Debit Collect. Matched" = ActualBankPmtApplRule."Direct Debit Collect. Matched"::Yes;
end;
/// <summary>
/// Executes the complete payment matching process for bank reconciliation lines.
/// Maps ledger entries to statement lines using configured matching rules and optionally applies
/// the matched entries to create payment applications. Supports selective overwriting based on
/// the Overwrite parameter to preserve manual matching decisions.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line record set to process for payment matching.</param>
/// <param name="Overwrite">True to overwrite existing matches including manual and accepted; false to preserve them.</param>
procedure "Code"(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Overwrite: Boolean)
begin
if BankAccReconciliationLine.IsEmpty() then
exit;
MapLedgerEntriesToStatementLines(BankAccReconciliationLine, Overwrite, ApplyEntries);
if ApplyEntries then begin
ApplyLedgerEntriesToStatementLines(BankAccReconciliationLine, Overwrite);
NotifyIfEntriesMatchedElsewhere(BankAccReconciliationLine);
end;
end;
/// <summary>
/// Executes the complete payment matching process with default overwrite behavior.
/// This overload calls the main Code procedure with Overwrite set to true, allowing
/// the matching process to override existing manual and accepted matches.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line record set to process for payment matching.</param>
procedure "Code"(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
Code(BankAccReconciliationLine, true);
end;
local procedure NotifyIfEntriesMatchedElsewhere(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
CustLedgerEntry: Record "Cust. Ledger Entry";
VendorLedgerEntry: Record "Vendor Ledger Entry";
EmployeeLedgerEntry: Record "Employee Ledger Entry";
Prefix: Code[50];
MaxEntriesToCheck: Integer;
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeNotifyIfEntriesMatchedElsewhere(BankAccReconciliationLine, IsHandled);
if IsHandled then
exit;
if not GuiAllowed() then
exit;
MaxEntriesToCheck := 600;
Prefix := BankAccReconciliationLine.GetAppliesToIDForBankStatement();
CustLedgerEntry.SetRange(Open, true);
CustLedgerEntry.SetFilter("Applies-to ID", '<>%1', '');
if CustLedgerEntry.Count() < MaxEntriesToCheck then
if CustLedgerEntry.FindSet() then
repeat
if CopyStr(CustLedgerEntry."Applies-to ID", 1, StrLen(Prefix)) <> Prefix then begin
LaunchEntriesMatchedElsewhereNotification('customer');
exit;
end;
until CustLedgerEntry.Next() = 0;
VendorLedgerEntry.SetRange(Open, true);
VendorLedgerEntry.SetFilter("Applies-to ID", '<>%1', '');
if VendorLedgerEntry.Count() < MaxEntriesToCheck then
if VendorLedgerEntry.FindSet() then
repeat
if CopyStr(VendorLedgerEntry."Applies-to ID", 1, StrLen(Prefix)) <> Prefix then begin
LaunchEntriesMatchedElsewhereNotification('vendor');
exit;
end;
until VendorLedgerEntry.Next() = 0;
EmployeeLedgerEntry.SetRange(Open, true);
EmployeeLedgerEntry.SetFilter("Applies-to ID", '<>%1', '');
if EmployeeLedgerEntry.Count() < MaxEntriesToCheck then
if EmployeeLedgerEntry.FindSet() then
repeat
if CopyStr(EmployeeLedgerEntry."Applies-to ID", 1, StrLen(Prefix)) <> Prefix then begin
LaunchEntriesMatchedElsewhereNotification('employee');
exit;
end;
until EmployeeLedgerEntry.Next() = 0;
end;
local procedure LaunchEntriesMatchedElsewhereNotification(Entity: Text)
var
MyNotifications: Record "My Notifications";
Notification: Notification;
begin
if not MyNotifications.IsEnabled(EntriesMatchedElsewhereNotificationId()) then
exit;
Notification.Id := EntriesMatchedElsewhereNotificationId();
Notification.Message := StrSubstNo(EntriesMatchedElsewhereMsg, Entity);
Notification.Scope := NotificationScope::LocalScope;
Notification.AddAction(DontShowAgainTxt, Codeunit::"Match Bank Payments", 'HideEntriesMatchedElsewhereNotification');
Notification.Send();
end;
internal procedure HideEntriesMatchedElsewhereNotification(Notification: Notification)
var
MyNotifications: Record "My Notifications";
begin
if not MyNotifications.Disable(EntriesMatchedElsewhereNotificationId()) then
MyNotifications.InsertDefault(EntriesMatchedElsewhereNotificationId(), 'name', 'desc', false);
end;
local procedure EntriesMatchedElsewhereNotificationId(): Guid
begin
exit('625601f1-689d-456f-9966-6756420215e0');
end;
internal procedure DisableNotification(var MyNotification: Notification)
var
MyNotifications: Record "My Notifications";
begin
if MyNotification.Id <> GetNumberSeriesNotificationId() then
exit;
MyNotifications.InsertDefault(MyNotification.Id, PmtRecNoSeriesNotificationNameLbl, PmtRecNoSeriesNotificationDescriptionLbl, false);
MyNotifications.Disable(MyNotification.Id)
end;
internal procedure OpenBankAccountCard(var MyNotification: Notification)
var
LocalBankAccount: Record "Bank Account";
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
begin
if MyNotification.Id <> GetNumberSeriesNotificationId() then
exit;
if not LocalBankAccount.Get(MyNotification.GetData(BankAccReconciliationLine.FieldName("Bank Account No."))) then
exit;
Page.Run(Page::"Bank Account Card", LocalBankAccount, LocalBankAccount."Pmt. Rec. No. Series");
end;
/// <summary>
/// Gets the unique identifier for payment reconciliation number series notifications.
/// Returns a fixed GUID used to identify notifications related to missing number series
/// configuration for payment reconciliation journals.
/// </summary>
/// <returns>GUID identifier for number series notifications.</returns>
internal procedure GetNumberSeriesNotificationId(): Guid
begin
exit('76d60ad7-20a5-4b64-b160-f059e1c7ab2d');
end;
/// <summary>
/// Opens the Payment Application Review page for bank reconciliation lines requiring manual review.
/// Extracts reconciliation data from the notification and filters lines based on the review score
/// criteria to display only lines that need user attention for manual application decisions.
/// </summary>
/// <param name="ReviewNotification">Notification containing bank account, statement details, and review filter criteria.</param>
procedure OpenLinesForReviewPage(ReviewNotification: Notification)
var
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
begin
Evaluate(BankAccReconciliationLine."Statement Type", ReviewNotification.GetData(BankAccReconciliationLine.FieldName("Statement Type")));
BankAccReconciliationLine.SetRange("Statement Type", BankAccReconciliationLine."Statement Type");
BankAccReconciliationLine.SetRange("Statement No.", ReviewNotification.GetData(BankAccReconciliationLine.FieldName("Statement No.")));
BankAccReconciliationLine.SetRange("Bank Account No.", ReviewNotification.GetData(BankAccReconciliationLine.FieldName("Bank Account No.")));
if not BankAccReconciliationLine.FindFirst() then
exit;
GetLinesForReview(BankAccReconciliationLine, ReviewNotification.GetData('ReviewScoreFilter'));
Page.Run(Page::"Payment Application Review", BankAccReconciliationLine);
end;
/// <summary>
/// Opens the Payment Application Review page for bank reconciliation lines with amount differences.
/// Extracts reconciliation data from the notification and filters lines to show only those with
/// unresolved differences that require user review and manual resolution.
/// </summary>
/// <param name="ReviewNotification">Notification containing bank account and statement identification details.</param>
procedure OpenLinesWithDifferencePage(ReviewNotification: Notification)
var
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
begin
Evaluate(BankAccReconciliationLine."Statement Type", ReviewNotification.GetData(BankAccReconciliationLine.FieldName("Statement Type")));
BankAccReconciliationLine.SetRange("Statement Type", BankAccReconciliationLine."Statement Type");
BankAccReconciliationLine.SetRange("Statement No.", ReviewNotification.GetData(BankAccReconciliationLine.FieldName("Statement No.")));
BankAccReconciliationLine.SetRange("Bank Account No.", ReviewNotification.GetData(BankAccReconciliationLine.FieldName("Bank Account No.")));
if not BankAccReconciliationLine.FindFirst() then
exit;
GetLinesWithDifference(BankAccReconciliationLine);
Page.Run(Page::"Payment Application Review", BankAccReconciliationLine);
end;
/// <summary>
/// Filters bank reconciliation lines to include only those requiring manual review.
/// Applies match quality and confidence filters to identify lines that were automatically
/// matched but need user validation based on the specified review score criteria.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line record set to filter for review requirements.</param>
/// <param name="ReviewScoreFilter">Filter criteria for match quality scores that require review.</param>
procedure GetLinesForReview(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; ReviewScoreFilter: Text)
begin
if BankAccReconciliationLine.IsEmpty() then
exit;
BankAccReconciliationLine.SetFilter("Match Quality", ReviewScoreFilter);
BankAccReconciliationLine.SetFilter("Match Confidence", BankAccReconciliationLine.GetMatchedAutomaticallyFilter());
end;
/// <summary>
/// Filters bank reconciliation lines to include only those with unresolved amount differences.
/// Sets a filter to display lines where the difference field is not zero, indicating that
/// the applied amount does not fully match the statement amount and requires attention.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line record set to filter for difference handling.</param>
procedure GetLinesWithDifference(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
BankAccReconciliationLine.SetFilter(Difference, '<>0');
end;
local procedure ApplyLedgerEntriesToStatementLines(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Overwrite: Boolean)
var
BankAccReconciliation: Record "Bank Acc. Reconciliation";
FeatureTelemetry: Codeunit "Feature Telemetry";
Window: Dialog;
begin
FeatureTelemetry.LogUptake('0000KMM', BankAccReconciliation.GetPaymentRecJournalTelemetryFeatureName(), Enum::"Feature Uptake Status"::Used);
Window.Open(CreatingAppliedEntriesMsg);
BankAccReconciliation.Get(
BankAccReconciliationLine."Statement Type", BankAccReconciliationLine."Bank Account No.",
BankAccReconciliationLine."Statement No.");
DeleteAppliedPaymentEntries(BankAccReconciliation, Overwrite);
DeletePaymentMatchDetails(BankAccReconciliation);
CreateAppliedEntries(BankAccReconciliation);
if not Overwrite then
BankAccReconciliationLine.SetFilter("Match Confidence", '<>%1&<>%2',
BankAccReconciliationLine."Match Confidence"::Accepted,
BankAccReconciliationLine."Match Confidence"::Manual);
UpdatePaymentMatchDetails(BankAccReconciliationLine);
OnApplyLedgerEntriesToStatementLinesOnAfterUpdatePaymentMatchDetails(BankAccReconciliation, BankAccReconciliationLine, Overwrite);
Window.Close();
ShowMatchSummary(BankAccReconciliation);
FeatureTelemetry.LogUsage('0000KMN', BankAccReconciliation.GetPaymentRecJournalTelemetryFeatureName(), AutomatchEventNameTelemetryTxt);
end;
local procedure MapLedgerEntriesToStatementLines(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Overwrite: Boolean; ApplyEntries: Boolean)
var
BankAccReconciliationLine2: Record "Bank Acc. Reconciliation Line";
Window: Dialog;
TotalNoOfLines: Integer;
ProcessedLines: Integer;
LineStartTime: DateTime;
StartTime: DateTime;
TimeCustomerLedgerEntries: Duration;
TimeVendorLedgerEntries: Duration;
TimeEmployeeLedgerEntries: Duration;
TimeBankLedgerEntries: Duration;
TimeTextMappings: Duration;
DisableBankLedgerEntriesMatch: Boolean;
DisableCustomerLedgerEntriesMatch: Boolean;
DisableVendorLedgerEntriesMatch: Boolean;
DisableEmployeeLedgerEntriesMatch: Boolean;
SkipOtherEntries: Boolean;
begin
CandidateFilterReferenceDate := 0D;
TempBankStatementMatchingBuffer.Reset();
TempBankStatementMatchingBuffer.DeleteAll();
TempCustomerLedgerEntryMatchingBuffer.DeleteAll();
TempVendorLedgerEntryMatchingBuffer.DeleteAll();
TempBankAccLedgerEntryMatchingBuffer.DeleteAll();
TempDirectDebitCollectionBuffer.DeleteAll();
TempBankPmtApplRule.LoadRules();
MinimumMatchScore := GetLowestMatchScore();
InitializeBankPmtApplSettings();
DisableCustomerLedgerEntriesMatch := not BankPmtApplSettings."Cust. Ledger Entries Matching";
DisableVendorLedgerEntriesMatch := not BankPmtApplSettings."Vendor Ledger Entries Matching";
DisableBankLedgerEntriesMatch := not BankPmtApplSettings."Bank Ledger Entries Matching";
DisableEmployeeLedgerEntriesMatch := not BankPmtApplSettings."Empl. Ledger Entries Matching";
BankAccReconciliationLine.SetFilter("Statement Amount", '<>0');
if not Overwrite then
BankAccReconciliationLine.SetFilter("Match Confidence", '<>%1&<>%2',
BankAccReconciliationLine."Match Confidence"::Accepted,
BankAccReconciliationLine."Match Confidence"::Manual);
if BankAccReconciliationLine.FindSet() then begin
BankAccReconciliationLine2.CopyFilters(BankAccReconciliationLine);
BankAccReconciliationLine2.SetCurrentKey("Transaction Date");
BankAccReconciliationLine2.SetAscending("Transaction Date", true);
if BankAccReconciliationLine2.FindFirst() then
CandidateFilterReferenceDate := BankAccReconciliationLine2."Transaction Date";
OnDisableCustomerLedgerEntriesMatch(DisableCustomerLedgerEntriesMatch, BankAccReconciliationLine);
OnDisableVendorLedgerEntriesMatch(DisableVendorLedgerEntriesMatch, BankAccReconciliationLine);
OnDisableEmployeeLedgerEntriesMatch(DisableEmployeeLedgerEntriesMatch, BankAccReconciliationLine);
OnDisableBankLedgerEntriesMatch(DisableBankLedgerEntriesMatch, BankAccReconciliationLine);
if not DisableCustomerLedgerEntriesMatch then
InitializeCustomerLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempCustomerLedgerEntryMatchingBuffer, ApplyEntries);
if not DisableVendorLedgerEntriesMatch then
InitializeVendorLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempVendorLedgerEntryMatchingBuffer, ApplyEntries);
if not DisableBankLedgerEntriesMatch then
InitializeBankAccLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempBankAccLedgerEntryMatchingBuffer, ApplyEntries);
if not DisableEmployeeLedgerEntriesMatch then
InitializeEmployeeLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempEmployeeLedgerEntryMatchingBuffer, ApplyEntries);
InitializeDirectDebitCollectionEntriesMatchingBuffer(TempDirectDebitCollectionBuffer);
if not Overwrite then
RemoveAppliedEntriesFromBufferTables();
TotalNoOfLines := BankAccReconciliationLine.Count();
ProcessedLines := 0;
if ApplyEntries then
Window.Open(MatchingStmtLinesMsg)
else
Window.Open(ProgressBarMsg);
repeat
StartTime := CurrentDateTime();
LineStartTime := StartTime;
if not DisableCustomerLedgerEntriesMatch then
FindMatchingEntries(
BankAccReconciliationLine, TempCustomerLedgerEntryMatchingBuffer, TempBankStatementMatchingBuffer."Account Type"::Customer, SkipOtherEntries);
TimeCustomerLedgerEntries := CurrentDateTime() - StartTime;
TotalTimeMatchingCustomerLedgerEntriesPerLine += TimeCustomerLedgerEntries;
StartTime := CurrentDateTime();
if (not DisableVendorLedgerEntriesMatch) and (not SkipOtherEntries) then
FindMatchingEntries(
BankAccReconciliationLine, TempVendorLedgerEntryMatchingBuffer, TempBankStatementMatchingBuffer."Account Type"::Vendor, SkipOtherEntries);
TimeVendorLedgerEntries := CurrentDateTime() - StartTime;
TotalTimeMatchingVendorLedgerEntriesPerLine += TimeVendorLedgerEntries;
StartTime := CurrentDateTime();
if (not DisableEmployeeLedgerEntriesMatch) and (not SkipOtherEntries) then
FindMatchingEntries(
BankAccReconciliationLine, TempEmployeeLedgerEntryMatchingBuffer, TempBankStatementMatchingBuffer."Account Type"::Employee, SkipOtherEntries);
TimeEmployeeLedgerEntries := CurrentDateTime() - StartTime;
TotalTimeMatchingEmployeeLedgerEntriesPerLine += TimeEmployeeLedgerEntries;
StartTime := CurrentDateTime();
if (not DisableBankLedgerEntriesMatch) and (not SkipOtherEntries) then
FindMatchingEntries(
BankAccReconciliationLine,
TempBankAccLedgerEntryMatchingBuffer, TempBankStatementMatchingBuffer."Account Type"::"Bank Account", SkipOtherEntries);
TimeBankLedgerEntries := CurrentDateTime() - StartTime;
TotalTimeMatchingBankLedgerEntriesPerLine += TimeBankLedgerEntries;
StartTime := CurrentDateTime();
FindTextMappings(BankAccReconciliationLine);
TimeTextMappings := CurrentDateTime() - StartTime;
TotalTimeTimeTextMappingsPerLine += TimeTextMappings;
OnMapLedgerEntriesToStatementLinesOnAfterCalcTotalTimeTimeTextMappingsPerLine(
BankAccReconciliationLine, TempBankStatementMatchingBuffer,
TotalTimeMatchingCustomerLedgerEntriesPerLine, TotalTimeMatchingVendorLedgerEntriesPerLine,
TotalTimeMatchingEmployeeLedgerEntriesPerLine, TotalTimeMatchingBankLedgerEntriesPerLine, RelatedPartyMatchedInfoText,
LogInfoText, TotalTimeStringNearness, UsePaymentDiscounts, TempOneToManyTempBankStatementMatchingBuffer,
TempCustomerLedgerEntryMatchingBuffer, TempVendorLedgerEntryMatchingBuffer,
TempEmployeeLedgerEntryMatchingBuffer, TempBankAccLedgerEntryMatchingBuffer);
ProcessedLines += 1;
Session.LogMessage('0000DK9', StrSubstNo(MatchedLineTelemetryTxt, BankAccReconciliationLine.SystemId, CurrentDateTime() - LineStartTime, TimeCustomerLedgerEntries, TimeVendorLedgerEntries, TimeEmployeeLedgerEntries, TimeBankLedgerEntries, TimeTextMappings), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PaymentRecPreformanceCategoryLbl);
if ApplyEntries then begin
Window.Update(1, StrSubstNo(ProcessedStmtLinesMsg, ProcessedLines, TotalNoOfLines));
Window.Update(2, Round(ProcessedLines / TotalNoOfLines * 10000, 1));
end;
until BankAccReconciliationLine.Next() = 0;
UpdateOneToManyMatches(BankAccReconciliationLine);
Session.LogMessage('0000DKD', StrSubstNo(SpecificTaskSummaryTxt, TotalTimeDirectCollection, TotalTimeRelatedPartyMatching, TotalTimeDocumentNoMatching, TotalTimeAmountMatching, TotalTimeStringNearness, TotalTimeDocumentNoMatchingForBankLedgerEntry, TotalTimeSearchingDocumentNoInLedgerEntries, HitCountClosingDocumentMatches, TotalNoClosingDocumentMatches), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PaymentRecPreformanceCategoryLbl);
Window.Close();
end;
CandidateFilterReferenceDate := 0D;
end;
local procedure RemoveAppliedEntriesFromBufferTables()
begin
RemoveAppliedEntriesFromTempLEMatchingBuffer(TempCustomerLedgerEntryMatchingBuffer);
RemoveAppliedEntriesFromTempLEMatchingBuffer(TempVendorLedgerEntryMatchingBuffer);
RemoveAppliedEntriesFromTempLEMatchingBuffer(TempBankAccLedgerEntryMatchingBuffer);
RemoveAppliedEntriesFromTempLEMatchingBuffer(TempEmployeeLedgerEntryMatchingBuffer);
RemoveAppliedEntriesFromDirectDebMatchingBuffer(TempDirectDebitCollectionBuffer);
end;
local procedure RemoveAppliedEntriesFromTempLEMatchingBuffer(var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
var
AppliedPaymentEntry: Record "Applied Payment Entry";
EntryNo: Integer;
begin
AppliedPaymentEntry.SetFilter("Match Confidence", '%1|%2', AppliedPaymentEntry."Match Confidence"::Accepted, AppliedPaymentEntry."Match Confidence"::Manual);
if AppliedPaymentEntry.FindSet() then
repeat
EntryNo := AppliedPaymentEntry."Applies-to Entry No.";
TempLedgerEntryMatchingBuffer.SetRange("Entry No.", EntryNo);
TempLedgerEntryMatchingBuffer.DeleteAll();
until AppliedPaymentEntry.Next() = 0
end;
local procedure RemoveAppliedEntriesFromDirectDebMatchingBuffer(var TempDirectDebitCollectionEntryBuffer: Record "Direct Debit Collection Buffer" temporary)
var
AppliedPaymentEntry: Record "Applied Payment Entry";
EntryNo: Integer;
begin
AppliedPaymentEntry.SetFilter("Match Confidence", '%1|%2', AppliedPaymentEntry."Match Confidence"::Accepted, AppliedPaymentEntry."Match Confidence"::Manual);
if AppliedPaymentEntry.FindSet() then
repeat
EntryNo := AppliedPaymentEntry."Applies-to Entry No.";
TempDirectDebitCollectionEntryBuffer.SetRange("Entry No.", EntryNo);
TempDirectDebitCollectionEntryBuffer.DeleteAll();
until AppliedPaymentEntry.Next() = 0
end;
/// <summary>
/// Re-executes text-to-account mapping for bank reconciliation lines that haven't been manually accepted.
/// This procedure reapplies text mapping rules to identify better automatic matches, particularly useful
/// after updating mapping rules or when initial matching was incomplete. Preserves accepted and high-confidence matches.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line record set to reprocess with text mapper.</param>
procedure RerunTextMapper(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
AppliedPaymentEntry: Record "Applied Payment Entry";
BankAccReconciliation: Record "Bank Acc. Reconciliation";
begin
if BankAccReconciliationLine.IsEmpty() then
exit;
BankAccReconciliationLine.SetRange("Statement Type", BankAccReconciliationLine."Statement Type"::"Payment Application");
BankAccReconciliationLine.SetRange("Bank Account No.", BankAccReconciliationLine."Bank Account No.");
BankAccReconciliationLine.SetRange("Statement No.", BankAccReconciliationLine."Statement No.");
BankAccReconciliationLine.SetFilter("Match Confidence", '<>%1 & <>%2',
BankAccReconciliationLine."Match Confidence"::Accepted, BankAccReconciliationLine."Match Confidence"::High);
if BankAccReconciliationLine.FindSet() then begin
repeat
SetFilterToBankAccReconciliation(AppliedPaymentEntry, BankAccReconciliationLine);
if FindTextMappings(BankAccReconciliationLine) then
BankAccReconciliationLine.RejectAppliedPayment();
until BankAccReconciliationLine.Next() = 0;
BankAccReconciliation.Get(
BankAccReconciliationLine."Statement Type", BankAccReconciliationLine."Bank Account No.",
BankAccReconciliationLine."Statement No.");
CreateAppliedEntries(BankAccReconciliation);
// Update match details for lines matched by text mapper
BankAccReconciliationLine.SetRange(
"Match Confidence", BankAccReconciliationLine."Match Confidence"::"High - Text-to-Account Mapping");
UpdatePaymentMatchDetails(BankAccReconciliationLine);
end;
end;
/// <summary>
/// Transfers unresolved amount differences to a specified general ledger account.
/// Creates a new bank reconciliation line for the difference amount when the statement amount
/// exceeds the applied amount, allowing users to account for fees, charges, or other adjustments.
/// Splits the original line and applies the difference portion to the designated account.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line with amount difference to transfer.</param>
/// <param name="TempGenJournalLine">Temporary journal line containing account and description details for the transfer.</param>
procedure TransferDiffToAccount(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempGenJournalLine: Record "Gen. Journal Line" temporary)
var
BankAccReconciliation: Record "Bank Acc. Reconciliation";
TempBankPmtApplRule: Record "Bank Pmt. Appl. Rule" temporary;
Score: Integer;
Difference: Decimal;
TransactionDate: Date;
LineSplitMsg: Text;
ParentLineNo: Integer;
TransactionID: Text[50];
DifferenceTransferred: Boolean;
SuppressMessage: Boolean;
begin
if BankAccReconciliationLine.IsEmpty() or (BankAccReconciliationLine.Difference = 0) then
exit;
TempGenJournalLine.Amount := BankAccReconciliationLine.Difference;
TempGenJournalLine.Description := BankAccReconciliationLine.Description;
if not TempGenJournalLine.Insert() then
TempGenJournalLine.Modify();
RunTransferDifferencetoAccount(BankAccReconciliationLine, TempGenJournalLine, DifferenceTransferred);
if DifferenceTransferred then begin
if TempGenJournalLine."Account No." = '' then
Error(MustChooseAccountErr);
if BankAccReconciliationLine."Statement Amount" <> BankAccReconciliationLine.Difference then begin
ParentLineNo := GetParentLineNo(BankAccReconciliationLine);
Difference := BankAccReconciliationLine.Difference;
LineSplitMsg := StrSubstNo(LineSplitTxt, Difference);
TransactionDate := BankAccReconciliationLine."Transaction Date";
TransactionID := BankAccReconciliationLine."Transaction ID";
RevertAcceptedPmtToleranceFromAppliedEntries(BankAccReconciliationLine, Abs(Difference));
BankAccReconciliationLine."Statement Amount" := BankAccReconciliationLine."Applied Amount";
BankAccReconciliationLine.Difference := 0;
BankAccReconciliationLine.Modify();
BankAccReconciliationLine.Init();
BankAccReconciliationLine."Statement Line No." := GetAvailableSplitLineNo(BankAccReconciliationLine, ParentLineNo);
BankAccReconciliationLine."Parent Line No." := ParentLineNo;
BankAccReconciliationLine.Description := TempGenJournalLine.Description;
BankAccReconciliationLine."Transaction Text" := TempGenJournalLine.Description;
BankAccReconciliationLine."Transaction Date" := TransactionDate;
BankAccReconciliationLine."Statement Amount" := Difference;
BankAccReconciliationLine."Transaction ID" := TransactionID;
OnTransferDiffToAccountOnBeforeInsertBankAccReconciliationLine(BankAccReconciliationLine, TempGenJournalLine);
BankAccReconciliationLine.Insert();
end;
BankAccReconciliation.Get(
BankAccReconciliationLine."Statement Type", BankAccReconciliationLine."Bank Account No.",
BankAccReconciliationLine."Statement No.");
Score := TempBankPmtApplRule.GetTextMapperScore();
TempBankStatementMatchingBuffer.AddMatchCandidate(
BankAccReconciliationLine."Statement Line No.", -1,
Score, TempGenJournalLine."Account Type", TempGenJournalLine."Account No.");
CreateAppliedEntries(BankAccReconciliation);
BankAccReconciliationLine.SetManualApplication();
BankAccReconciliationLine.SetRange("Statement Type", BankAccReconciliationLine."Statement Type"::"Payment Application");
BankAccReconciliationLine.SetRange("Bank Account No.", BankAccReconciliationLine."Bank Account No.");
BankAccReconciliationLine.SetRange("Statement No.", BankAccReconciliationLine."Statement No.");
BankAccReconciliationLine.SetRange("Statement Line No.", BankAccReconciliationLine."Statement Line No.");
UpdatePaymentMatchDetails(BankAccReconciliationLine);
if LineSplitMsg <> '' then begin
OnBeforeShowLineSplitMessage(BankAccReconciliationLine, SuppressMessage);
if not SuppressMessage then
Message(LineSplitMsg);
end;
end;
end;
local procedure RunTransferDifferencetoAccount(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempGenJnlLine: Record "Gen. Journal Line" temporary; var DifferenceTransferred: Boolean)
begin
DifferenceTransferred := false;
OnTransferDifference(BankAccReconciliationLine, TempGenJnlLine, DifferenceTransferred);
if DifferenceTransferred then
exit;
if not GuiAllowed() then
exit;
DifferenceTransferred := Page.RunModal(Page::"Transfer Difference to Account", TempGenJnlLine) = Action::LookupOK;
end;
/// <summary>
/// Performs matching analysis for a single bank reconciliation line against a specific customer ledger entry.
/// Evaluates match criteria and calculates tolerance statistics to determine match quality and rule applicability.
/// Returns the number of potential matching entries within and outside the configured amount tolerance.
/// </summary>
/// <param name="BankPmtApplRule">Returns the payment application rule used for matching evaluation.</param>
/// <param name="BankAccReconciliationLine">Bank reconciliation line to match against customer entries.</param>
/// <param name="AppliesToEntryNo">Specific customer ledger entry number to evaluate for matching.</param>
/// <param name="NoOfLedgerEntriesWithinTolerance">Returns count of customer entries within amount tolerance.</param>
/// <param name="NoOfLedgerEntriesOutsideTolerance">Returns count of customer entries outside amount tolerance.</param>
procedure MatchSingleLineCustomer(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AppliesToEntryNo: Integer; var NoOfLedgerEntriesWithinTolerance: Integer; var NoOfLedgerEntriesOutsideTolerance: Integer)
var
MinAmount: Decimal;
MaxAmount: Decimal;
AccountNo: Code[20];
begin
InitializeBankPmtApplSettings();
ApplyEntries := false;
InitializeCustomerLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempCustomerLedgerEntryMatchingBuffer);
InitializeDirectDebitCollectionEntriesMatchingBuffer(TempDirectDebitCollectionBuffer);
if TempCustomerLedgerEntryMatchingBuffer.Get(AppliesToEntryNo, TempCustomerLedgerEntryMatchingBuffer."Account Type"::Customer) then;
FindMatchingEntry(
TempCustomerLedgerEntryMatchingBuffer, BankAccReconciliationLine, TempBankStatementMatchingBuffer."Account Type"::Customer,
BankPmtApplRule);
AccountNo := TempCustomerLedgerEntryMatchingBuffer."Account No.";
BankAccReconciliationLine.GetAmountRangeForTolerance(MinAmount, MaxAmount);
TempCustomerLedgerEntryMatchingBuffer.Reset();
TempCustomerLedgerEntryMatchingBuffer.SetRange("Account No.", AccountNo);
NoOfLedgerEntriesWithinTolerance :=
TempCustomerLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
NoOfLedgerEntriesOutsideTolerance :=
TempCustomerLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesOutsideRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
end;
/// <summary>
/// Performs matching analysis for a single bank reconciliation line against a specific vendor ledger entry.
/// Evaluates match criteria and calculates tolerance statistics to determine match quality and rule applicability.
/// Returns the number of potential matching entries within and outside the configured amount tolerance.
/// </summary>
/// <param name="BankPmtApplRule">Returns the payment application rule used for matching evaluation.</param>
/// <param name="BankAccReconciliationLine">Bank reconciliation line to match against vendor entries.</param>
/// <param name="AppliesToEntryNo">Specific vendor ledger entry number to evaluate for matching.</param>
/// <param name="NoOfLedgerEntriesWithinTolerance">Returns count of vendor entries within amount tolerance.</param>
/// <param name="NoOfLedgerEntriesOutsideTolerance">Returns count of vendor entries outside amount tolerance.</param>
procedure MatchSingleLineVendor(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AppliesToEntryNo: Integer; var NoOfLedgerEntriesWithinTolerance: Integer; var NoOfLedgerEntriesOutsideTolerance: Integer)
var
MinAmount: Decimal;
MaxAmount: Decimal;
AccountNo: Code[20];
begin
InitializeBankPmtApplSettings();
ApplyEntries := false;
InitializeVendorLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempVendorLedgerEntryMatchingBuffer);
if not TempVendorLedgerEntryMatchingBuffer.Get(AppliesToEntryNo, TempVendorLedgerEntryMatchingBuffer."Account Type"::Vendor) then;
FindMatchingEntry(
TempVendorLedgerEntryMatchingBuffer, BankAccReconciliationLine, TempBankStatementMatchingBuffer."Account Type"::Vendor,
BankPmtApplRule);
AccountNo := TempVendorLedgerEntryMatchingBuffer."Account No.";
BankAccReconciliationLine.GetAmountRangeForTolerance(MinAmount, MaxAmount);
TempVendorLedgerEntryMatchingBuffer.Reset();
TempVendorLedgerEntryMatchingBuffer.SetRange("Account No.", AccountNo);
NoOfLedgerEntriesWithinTolerance :=
TempVendorLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
NoOfLedgerEntriesOutsideTolerance :=
TempVendorLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesOutsideRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
end;
/// <summary>
/// Performs matching analysis for a single bank reconciliation line against a specific employee ledger entry.
/// Evaluates match criteria and calculates tolerance statistics to determine match quality and rule applicability.
/// Returns the number of potential matching entries within and outside the configured amount tolerance.
/// </summary>
/// <param name="BankPmtApplRule">Returns the payment application rule used for matching evaluation.</param>
/// <param name="BankAccReconciliationLine">Bank reconciliation line to match against employee entries.</param>
/// <param name="AppliesToEntryNo">Specific employee ledger entry number to evaluate for matching.</param>
/// <param name="NoOfLedgerEntriesWithinTolerance">Returns count of employee entries within amount tolerance.</param>
/// <param name="NoOfLedgerEntriesOutsideTolerance">Returns count of employee entries outside amount tolerance.</param>
procedure MatchSingleLineEmployee(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AppliesToEntryNo: Integer; var NoOfLedgerEntriesWithinTolerance: Integer; var NoOfLedgerEntriesOutsideTolerance: Integer)
var
MinAmount: Decimal;
MaxAmount: Decimal;
AccountNo: Code[20];
begin
InitializeBankPmtApplSettings();
ApplyEntries := false;
InitializeEmployeeLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempEmployeeLedgerEntryMatchingBuffer);
if not TempEmployeeLedgerEntryMatchingBuffer.Get(AppliesToEntryNo, TempEmployeeLedgerEntryMatchingBuffer."Account Type"::Employee) then;
FindMatchingEntry(
TempEmployeeLedgerEntryMatchingBuffer, BankAccReconciliationLine, TempBankStatementMatchingBuffer."Account Type"::Employee,
BankPmtApplRule);
AccountNo := TempEmployeeLedgerEntryMatchingBuffer."Account No.";
BankAccReconciliationLine.GetAmountRangeForTolerance(MinAmount, MaxAmount);
TempEmployeeLedgerEntryMatchingBuffer.Reset();
TempEmployeeLedgerEntryMatchingBuffer.SetRange("Account No.", AccountNo);
NoOfLedgerEntriesWithinTolerance :=
TempEmployeeLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
NoOfLedgerEntriesOutsideTolerance :=
TempEmployeeLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesOutsideRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
end;
/// <summary>
/// Performs matching analysis for a single bank reconciliation line against a specific bank account ledger entry.
/// Evaluates match criteria and calculates tolerance statistics to determine match quality and rule applicability.
/// Returns the number of potential matching entries within and outside the configured amount tolerance.
/// </summary>
/// <param name="BankPmtApplRule">Returns the payment application rule used for matching evaluation.</param>
/// <param name="BankAccReconciliationLine">Bank reconciliation line to match against bank account entries.</param>
/// <param name="AppliesToEntryNo">Specific bank account ledger entry number to evaluate for matching.</param>
/// <param name="NoOfLedgerEntriesWithinTolerance">Returns count of bank account entries within amount tolerance.</param>
/// <param name="NoOfLedgerEntriesOutsideTolerance">Returns count of bank account entries outside amount tolerance.</param>
procedure MatchSingleLineBankAccountLedgerEntry(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AppliesToEntryNo: Integer; var NoOfLedgerEntriesWithinTolerance: Integer; var NoOfLedgerEntriesOutsideTolerance: Integer)
var
MinAmount: Decimal;
MaxAmount: Decimal;
AccountNo: Code[20];
begin
InitializeBankPmtApplSettings();
ApplyEntries := false;
InitializeBankAccLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempBankAccLedgerEntryMatchingBuffer);
if not TempBankAccLedgerEntryMatchingBuffer.Get(AppliesToEntryNo, TempBankAccLedgerEntryMatchingBuffer."Account Type"::"Bank Account") then;
FindMatchingEntry(
TempBankAccLedgerEntryMatchingBuffer, BankAccReconciliationLine, TempBankStatementMatchingBuffer."Account Type"::"Bank Account",
BankPmtApplRule);
AccountNo := TempBankAccLedgerEntryMatchingBuffer."Account No.";
BankAccReconciliationLine.GetAmountRangeForTolerance(MinAmount, MaxAmount);
TempBankAccLedgerEntryMatchingBuffer.Reset();
TempBankAccLedgerEntryMatchingBuffer.SetRange("Account No.", AccountNo);
NoOfLedgerEntriesWithinTolerance :=
TempBankAccLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", false);
NoOfLedgerEntriesOutsideTolerance :=
TempBankAccLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesOutsideRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", false);
end;
local procedure FindMatchingEntries(var TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; AccountType: Enum "Gen. Journal Account Type"; var SkipOtherEntries: Boolean)
var
BankPmtApplRule: Record "Bank Pmt. Appl. Rule";
begin
InitializeBankPmtApplSettings();
TempLedgerEntryMatchingBuffer.Reset();
OnFindMatchingEntriesOnBeforeFindFirst(TempBankAccReconciliationLine, TempLedgerEntryMatchingBuffer, AccountType, SkipOtherEntries, ApplyEntries);
if TempLedgerEntryMatchingBuffer.FindFirst() then
repeat
FindMatchingEntry(TempLedgerEntryMatchingBuffer, TempBankAccReconciliationLine, AccountType, BankPmtApplRule);
if BankPmtApplSettings."Enable Apply Immediatelly" then
if BankPmtApplRule."Apply Immediatelly" then begin
SkipOtherEntries := true;
exit;
end;
until TempLedgerEntryMatchingBuffer.Next() = 0;
end;
local procedure FindMatchingEntry(TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type"; var BankPmtApplRule: Record "Bank Pmt. Appl. Rule")
var
Score: Integer;
RemainingAmount: Decimal;
begin
if CanEntriesMatch(
BankAccReconciliationLine, TempLedgerEntryMatchingBuffer."Remaining Amount", TempLedgerEntryMatchingBuffer."Posting Date")
then begin
MatchEntries(TempLedgerEntryMatchingBuffer, BankAccReconciliationLine, AccountType, BankPmtApplRule, RemainingAmount);
Score := TempBankPmtApplRule.GetBestMatchScore(BankPmtApplRule);
if Score >= MinimumMatchScore then
TempBankStatementMatchingBuffer.AddMatchCandidate(
BankAccReconciliationLine."Statement Line No.", TempLedgerEntryMatchingBuffer."Entry No.",
Score, AccountType,
TempLedgerEntryMatchingBuffer."Account No.");
if BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" = BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes then begin
TempBankStatementMatchingBuffer.InsertOrUpdateOneToManyRule(
TempLedgerEntryMatchingBuffer,
BankAccReconciliationLine."Statement Line No.",
BankPmtApplRule."Related Party Matched", AccountType,
RemainingAmount);
TempBankStmtMultipleMatchLine.InsertLine(
TempLedgerEntryMatchingBuffer,
BankAccReconciliationLine."Statement Line No.", AccountType);
end;
end;
end;
local procedure MatchEntries(TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type"; var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; var RemainingAmount: Decimal)
var
IsHandled: Boolean;
StartTime: DateTime;
begin
InitializeBankPmtApplSettings();
if AccountType = TempBankStatementMatchingBuffer."Account Type"::Customer then
DirectDebitCollectionMatching(BankPmtApplRule, BankAccReconciliationLine, TempLedgerEntryMatchingBuffer);
StartTime := CurrentDateTime();
RelatedPartyMatching(BankPmtApplRule, TempLedgerEntryMatchingBuffer, BankAccReconciliationLine, AccountType);
TotalTimeRelatedPartyMatching += CurrentDateTime() - StartTime;
IsHandled := false;
OnFindMatchingEntryOnBeforeDocumentMatching(BankPmtApplRule, BankAccReconciliationLine, TempLedgerEntryMatchingBuffer, IsHandled, TempBankStatementMatchingBuffer, AccountType, TotalTimeDocumentNoMatching, TotalTimeDocumentNoMatchingForBankLedgerEntry, DocumentMatchedInfoText, LogInfoText);
if not IsHandled then
if AccountType <> TempBankStatementMatchingBuffer."Account Type"::"Bank Account" then begin
StartTime := CurrentDateTime();
DocumentMatching(
BankPmtApplRule, BankAccReconciliationLine,
TempLedgerEntryMatchingBuffer."Document No.",
TempLedgerEntryMatchingBuffer."External Document No.",
TempLedgerEntryMatchingBuffer."Payment Reference");
TotalTimeDocumentNoMatching += CurrentDateTime() - StartTime;
end else begin
StartTime := CurrentDateTime();
DocumentMatchingForBankLedgerEntry(BankPmtApplRule, BankAccReconciliationLine, TempLedgerEntryMatchingBuffer);
TotalTimeDocumentNoMatchingForBankLedgerEntry += CurrentDateTime() - StartTime;
end;
IsHandled := false;
OnMatchEntriesOnAfterCalcTotalTimeDocumentNoMatching(
BankPmtApplRule, BankAccReconciliationLine, TempLedgerEntryMatchingBuffer,
AccountType, TempBankStatementMatchingBuffer, TotalTimeRelatedPartyMatching,
TotalTimeAmountMatching, RemainingAmount, RelatedPartyMatchedInfoText,
LogInfoText, TotalTimeStringNearness, UsePaymentDiscounts, TempOneToManyTempBankStatementMatchingBuffer,
TempCustomerLedgerEntryMatchingBuffer, TempVendorLedgerEntryMatchingBuffer,
TempEmployeeLedgerEntryMatchingBuffer, TempBankAccLedgerEntryMatchingBuffer, IsHandled, DocumentMatchedInfoText);
if not IsHandled then begin
StartTime := CurrentDateTime();
RemainingAmount := CalcRemainingAmount(TempLedgerEntryMatchingBuffer, BankAccReconciliationLine);
AmountInclToleranceMatching(
BankPmtApplRule, BankAccReconciliationLine, AccountType, RemainingAmount);
TotalTimeAmountMatching += CurrentDateTime() - StartTime;
end;
end;
local procedure CalcRemainingAmount(var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line") RemainingAmount: Decimal
var
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeCalcRemainingAmount(TempLedgerEntryMatchingBuffer, BankAccReconciliationLine, UsePaymentDiscounts, RemainingAmount, IsHandled);
if IsHandled then
exit(RemainingAmount);
RemainingAmount := TempLedgerEntryMatchingBuffer.GetApplicableRemainingAmount(BankAccReconciliationLine, UsePaymentDiscounts);
end;
/// <summary>
/// Initializes the customer ledger entries matching buffer with default apply entries setting.
/// This overload calls the main initialization procedure with ApplyEntries set to false,
/// populating the buffer for matching analysis without immediate application.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering customer entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with customer ledger entry data.</param>
procedure InitializeCustomerLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
begin
InitializeCustomerLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempLedgerEntryMatchingBuffer, false);
end;
/// <summary>
/// Initializes the customer ledger entries matching buffer with open customer transactions.
/// Populates a temporary buffer with customer ledger entries filtered by currency, document types,
/// and application status. Configures the buffer for payment matching against bank reconciliation lines.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering customer entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with customer ledger entry data.</param>
/// <param name="ApplyEntries">True to filter out entries already applied in other journals; false to include all entries.</param>
procedure InitializeCustomerLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; ApplyEntries: Boolean)
var
CustLedgerEntry: Record "Cust. Ledger Entry";
GeneralLedgerSetup: Record "General Ledger Setup";
SalesReceivablesSetup: Record "Sales & Receivables Setup";
CandidateFilterStartDate: Date;
begin
BankAccount.Get(BankAccReconciliationLine."Bank Account No.");
SalesReceivablesSetup.Get();
CustLedgerEntry.SetRange(Open, true);
CustLedgerEntry.SetFilter("Document Type", '%1|%2|%3|%4|%5',
CustLedgerEntry."Document Type"::" ",
CustLedgerEntry."Document Type"::Invoice,
CustLedgerEntry."Document Type"::"Credit Memo",
CustLedgerEntry."Document Type"::"Finance Charge Memo",
CustLedgerEntry."Document Type"::Reminder);
if ApplyEntries then
CustLedgerEntry.SetRange("Applies-to ID", '');
CandidateFilterStartDate := GetCandidateFilterStartDate(BankAccReconciliationLine);
if CandidateFilterStartDate <> 0D then
CustLedgerEntry.SetFilter("Posting Date", '>=%1', CandidateFilterStartDate);
OnInitCustomerLedgerEntriesMatchingBufferSetFilter(CustLedgerEntry, BankAccReconciliationLine);
if BankAccount.IsInLocalCurrency() then begin
CustLedgerEntry.SetAutoCalcFields("Remaining Amt. (LCY)");
if SalesReceivablesSetup."Appln. between Currencies" = SalesReceivablesSetup."Appln. between Currencies"::None then begin
GeneralLedgerSetup.Get();
CustLedgerEntry.SetFilter("Currency Code", '=%1|=%2', '', GeneralLedgerSetup.GetCurrencyCode(''));
end;
end else begin
CustLedgerEntry.SetAutoCalcFields("Remaining Amount");
CustLedgerEntry.SetRange("Currency Code", BankAccount."Currency Code");
end;
OnInitCustomerLedgerEntriesMatchingBufferOnBeforeCustLedgerEntryFindSet(CustLedgerEntry);
if CustLedgerEntry.FindSet() then
repeat
TempLedgerEntryMatchingBuffer.InsertFromCustomerLedgerEntry(
CustLedgerEntry, BankAccount.IsInLocalCurrency(), UsePaymentDiscounts);
until CustLedgerEntry.Next() = 0;
end;
/// <summary>
/// Initializes the vendor ledger entries matching buffer with default apply entries setting.
/// This overload calls the main initialization procedure with ApplyEntries set to false,
/// populating the buffer for matching analysis without immediate application.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering vendor entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with vendor ledger entry data.</param>
procedure InitializeVendorLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
begin
InitializeVendorLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempLedgerEntryMatchingBuffer, false);
end;
/// <summary>
/// Initializes the vendor ledger entries matching buffer with open vendor transactions.
/// Populates a temporary buffer with vendor ledger entries filtered by currency, document types,
/// and application status. Configures the buffer for payment matching against bank reconciliation lines.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering vendor entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with vendor ledger entry data.</param>
/// <param name="ApplyEntries">True to filter out entries already applied in other journals; false to include all entries.</param>
procedure InitializeVendorLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; ApplyEntries: Boolean)
var
VendorLedgerEntry: Record "Vendor Ledger Entry";
GeneralLedgerSetup: Record "General Ledger Setup";
PurchasesPayablesSetup: Record "Purchases & Payables Setup";
CandidateFilterStartDate: Date;
begin
BankAccount.Get(BankAccReconciliationLine."Bank Account No.");
PurchasesPayablesSetup.Get();
VendorLedgerEntry.SetRange(Open, true);
VendorLedgerEntry.SetFilter("Document Type", '%1|%2|%3|%4|%5',
VendorLedgerEntry."Document Type"::" ",
VendorLedgerEntry."Document Type"::Invoice,
VendorLedgerEntry."Document Type"::"Credit Memo",
VendorLedgerEntry."Document Type"::"Finance Charge Memo",
VendorLedgerEntry."Document Type"::Reminder);
if ApplyEntries then
VendorLedgerEntry.SetRange("Applies-to ID", '');
CandidateFilterStartDate := GetCandidateFilterStartDate(BankAccReconciliationLine);
if CandidateFilterStartDate <> 0D then
VendorLedgerEntry.SetFilter("Posting Date", '>=%1', CandidateFilterStartDate);
OnInitVendorLedgerEntriesMatchingBufferSetFilter(VendorLedgerEntry, BankAccReconciliationLine);
if BankAccount.IsInLocalCurrency() then begin
VendorLedgerEntry.SetAutoCalcFields("Remaining Amt. (LCY)");
if PurchasesPayablesSetup."Appln. between Currencies" = PurchasesPayablesSetup."Appln. between Currencies"::None then begin
GeneralLedgerSetup.Get();
VendorLedgerEntry.SetFilter("Currency Code", '=%1|=%2', '', GeneralLedgerSetup.GetCurrencyCode(''));
end;
end else begin
VendorLedgerEntry.SetAutoCalcFields("Remaining Amount");
VendorLedgerEntry.SetRange("Currency Code", BankAccount."Currency Code");
end;
OnInitVendorLedgerEntriesMatchingBufferOnAfterVendorLedgerEntryFindSet(VendorLedgerEntry);
if VendorLedgerEntry.FindSet() then
repeat
TempLedgerEntryMatchingBuffer.InsertFromVendorLedgerEntry(
VendorLedgerEntry, BankAccount.IsInLocalCurrency(), UsePaymentDiscounts);
until VendorLedgerEntry.Next() = 0;
end;
/// <summary>
/// Initializes the employee ledger entries matching buffer with default apply entries setting.
/// This overload calls the main initialization procedure with ApplyEntries set to false,
/// populating the buffer for matching analysis without immediate application.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering employee entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with employee ledger entry data.</param>
procedure InitializeEmployeeLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
begin
InitializeEmployeeLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempLedgerEntryMatchingBuffer, false);
end;
/// <summary>
/// Initializes the employee ledger entries matching buffer with open employee transactions.
/// Populates a temporary buffer with employee ledger entries filtered by currency, document types,
/// and application status. Configures the buffer for payment matching against bank reconciliation lines.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering employee entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with employee ledger entry data.</param>
/// <param name="ApplyEntries">True to filter out entries already applied in other journals; false to include all entries.</param>
procedure InitializeEmployeeLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; ApplyEntries: Boolean)
var
EmployeeLedgerEntry: Record "Employee Ledger Entry";
CandidateFilterStartDate: Date;
begin
BankAccount.Get(BankAccReconciliationLine."Bank Account No.");
EmployeeLedgerEntry.SetRange(Open, true);
EmployeeLedgerEntry.SetRange("Document Type", EmployeeLedgerEntry."Document Type"::" ");
if ApplyEntries then
EmployeeLedgerEntry.SetRange("Applies-to ID", '');
CandidateFilterStartDate := GetCandidateFilterStartDate(BankAccReconciliationLine);
if CandidateFilterStartDate <> 0D then
EmployeeLedgerEntry.SetFilter("Posting Date", '>=%1', CandidateFilterStartDate);
OnInitEmployeeLedgerEntriesMatchingBufferSetFilter(EmployeeLedgerEntry, BankAccReconciliationLine);
if not BankAccount.IsInLocalCurrency() then begin
EmployeeLedgerEntry.SetAutoCalcFields("Remaining Amount");
EmployeeLedgerEntry.SetRange("Currency Code", BankAccount."Currency Code");
end else
EmployeeLedgerEntry.SetAutoCalcFields("Remaining Amt. (LCY)");
OnInitEmployeeLedgerEntriesMatchingBufferOnAfterEmployeeLedgerEntryFindSet(EmployeeLedgerEntry);
if EmployeeLedgerEntry.FindSet() then
repeat
TempLedgerEntryMatchingBuffer.InsertFromEmployeeLedgerEntry(EmployeeLedgerEntry, BankAccount.IsInLocalCurrency());
until EmployeeLedgerEntry.Next() = 0;
end;
/// <summary>
/// Initializes the bank account ledger entries matching buffer with default skip reversed setting.
/// This overload calls the main initialization procedure with SkipReversed set to false,
/// populating the buffer for matching analysis including all bank entries.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering bank account entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with bank account ledger entry data.</param>
procedure InitializeBankAccLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
begin
InitializeBankAccLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempLedgerEntryMatchingBuffer, false);
end;
/// <summary>
/// Initializes the bank account ledger entries matching buffer with open bank account transactions.
/// Populates a temporary buffer with bank account ledger entries filtered by currency and reversal status.
/// Configures the buffer for payment matching against bank reconciliation lines for internal transfers.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line context for filtering bank account entries.</param>
/// <param name="TempLedgerEntryMatchingBuffer">Temporary buffer to populate with bank account ledger entry data.</param>
/// <param name="SkipReversed">True to exclude reversed entries from matching consideration; false to include all entries.</param>
procedure InitializeBankAccLedgerEntriesMatchingBuffer(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; SkipReversed: Boolean)
var
BankAccLedgerEntry: Record "Bank Account Ledger Entry";
GeneralLedgerSetup: Record "General Ledger Setup";
PurchasesPayablesSetup: Record "Purchases & Payables Setup";
CandidateFilterStartDate: Date;
begin
BankAccount.Get(BankAccReconciliationLine."Bank Account No.");
PurchasesPayablesSetup.Get();
BankAccLedgerEntry.SetRange(Open, true);
BankAccLedgerEntry.SetRange("Bank Account No.", BankAccReconciliationLine."Bank Account No.");
if SkipReversed then
BankAccLedgerEntry.SetRange(Reversed, false);
CandidateFilterStartDate := GetCandidateFilterStartDate(BankAccReconciliationLine);
if CandidateFilterStartDate <> 0D then
BankAccLedgerEntry.SetFilter("Posting Date", '>=%1', CandidateFilterStartDate);
OnInitBankAccLedgerEntriesMatchingBufferSetFilter(BankAccLedgerEntry, BankAccReconciliationLine);
if BankAccount.IsInLocalCurrency() then
if PurchasesPayablesSetup."Appln. between Currencies" = PurchasesPayablesSetup."Appln. between Currencies"::None then begin
GeneralLedgerSetup.Get();
BankAccLedgerEntry.SetFilter("Currency Code", '=%1|=%2', '', GeneralLedgerSetup.GetCurrencyCode(''));
end else
BankAccLedgerEntry.SetRange("Currency Code", BankAccount."Currency Code");
if BankAccLedgerEntry.FindSet() then
repeat
TempLedgerEntryMatchingBuffer.InsertFromBankAccLedgerEntry(BankAccLedgerEntry);
until BankAccLedgerEntry.Next() = 0;
end;
procedure InitializeDirectDebitCollectionEntriesMatchingBuffer(var TempDirectDebitCollectionEntryBuffer: Record "Direct Debit Collection Buffer" temporary)
var
DirectDebitCollectionEntry: Record "Direct Debit Collection Entry";
begin
if DirectDebitCollectionEntry.FindSet() then
repeat
TempDirectDebitCollectionEntryBuffer.TransferFields(DirectDebitCollectionEntry);
TempDirectDebitCollectionEntryBuffer.Insert();
until DirectDebitCollectionEntry.Next() = 0;
end;
/// <summary>
/// Finds and returns text-to-account mapping rules applicable to a bank reconciliation line.
/// Analyzes the transaction text against configured mapping patterns and populates a temporary
/// table with matching rules that can be used for automatic account assignment.
/// </summary>
/// <param name="BankAccReconciliationLine">Bank reconciliation line to analyze for text mapping matches.</param>
/// <param name="TempTextToAccMapping">Temporary table to populate with applicable mapping rules.</param>
/// <returns>True if applicable text mapping rules were found; false otherwise.</returns>
procedure FindApplicableTextMappings(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempTextToAccMapping: Record "Text-to-Account Mapping" temporary): Boolean
begin
exit(FindTextMappings(BankAccReconciliationLine, TempTextToAccMapping, true));
end;
local procedure FindTextMappings(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"): Boolean
var
TempTextToAccMapping: Record "Text-to-Account Mapping" temporary;
begin
exit(FindTextMappings(BankAccReconciliationLine, TempTextToAccMapping, false));
end;
local procedure SubstringMatchPercentage(ToMatchString: Text; OtherString: Text; var LongestCommonSubstringLength: Integer): Integer;
var
RecordMatchMgt: Codeunit "Record Match Mgt.";
CommonSubstring: Text;
MinLength: Integer;
begin
CommonSubstring := RecordMatchMgt.GetLongestCommonSubstring(ToMatchString, OtherString);
LongestCommonSubstringLength := StrLen(CommonSubstring);
MinLength := (StrLen(ToMatchString) + StrLen(OtherString) - Abs(StrLen(ToMatchString) - StrLen(OtherString))) / 2;
if (MinLength = 0) or (LongestCommonSubstringLength < StrLen(ToMatchString)) then
exit(0);
exit(GetNormalizingFactor() * LongestCommonSubstringLength div MinLength);
end;
local procedure FindTextMappings(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempTextToAccMapping: Record "Text-to-Account Mapping" temporary; TrackApplicableRules: Boolean): Boolean
var
TextToAccMapping: Record "Text-to-Account Mapping";
BankAccLedgerEntry: Record "Bank Account Ledger Entry";
RecordMatchMgt: Codeunit "Record Match Mgt.";
Nearness, LongestCommonSubstringLength : Integer;
Score: Integer;
AccountType: Enum "Gen. Journal Account Type";
AccountNo: Code[20];
EntryNo: Integer;
TextMapperMatched: Boolean;
begin
TextMapperMatched := false;
if TextToAccMapping.FindSet() then
repeat
Nearness := 0;
OnFindTextMappingsOnBeforeCalculateStringNearness(BankAccReconciliationLine, TextToAccMapping, Nearness);
if Nearness = 0 then
Nearness := SubstringMatchPercentage(RecordMatchMgt.Trim(TextToAccMapping."Mapping Text"), BankAccReconciliationLine."Transaction Text", LongestCommonSubstringLength);
case TextToAccMapping."Bal. Source Type" of
TextToAccMapping."Bal. Source Type"::"G/L Account":
if BankAccReconciliationLine."Statement Amount" >= 0 then
AccountNo := TextToAccMapping."Debit Acc. No."
else
AccountNo := TextToAccMapping."Credit Acc. No.";
else // Customer or Vendor
AccountNo := TextToAccMapping."Bal. Source No.";
end;
if Nearness >= GetExactMatchTreshold() then begin
Score := TempBankPmtApplRule.GetTextMapperScore(LongestCommonSubstringLength, LowerCase(BankAccReconciliationLine."Transaction Text").Contains(LowerCase(RecordMatchMgt.Trim(TextToAccMapping."Mapping Text"))));
// Customers could post the expense via Journal. In this case there is a risk that we will create double entries.
// We will seach for existing bank ledger entries with similar text in the similar range and mapp to that
if FindBankAccLedgerEntry(BankAccLedgerEntry, BankAccReconciliationLine, TextToAccMapping, AccountNo) then begin
EntryNo := BankAccLedgerEntry."Entry No.";
AccountType := TempBankStatementMatchingBuffer."Account Type"::"Bank Account";
AccountNo := BankAccLedgerEntry."Bank Account No.";
end else begin
EntryNo := -TextToAccMapping."Line No."; // mark negative to identify text-mapper
AccountType := Enum::"Gen. Journal Account Type".FromInteger(TextToAccMapping."Bal. Source Type");
end;
TextMapperMatched := true;
TempBankStatementMatchingBuffer.AddMatchCandidate(
BankAccReconciliationLine."Statement Line No.", EntryNo,
Score, AccountType, AccountNo);
OnFindTextMappingsOnAfterTextMapperMatched(BankAccReconciliationLine, TextToAccMapping, TempBankStatementMatchingBuffer);
if (TrackApplicableRules) then begin
TempTextToAccMapping.TransferFields(TextToAccMapping, true);
TempTextToAccMapping.Insert();
end;
end;
until TextToAccMapping.Next() = 0;
exit(TextMapperMatched)
end;
local procedure FindBankAccLedgerEntry(var BankAccLedgerEntry: Record "Bank Account Ledger Entry"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TextToAccountMapping: Record "Text-to-Account Mapping"; BalAccountNo: Code[20]): Boolean
var
RecordMatchMgt: Codeunit "Record Match Mgt.";
Nearness: Integer;
Found: Boolean;
Handled: Boolean;
begin
OnFindBankAccLedgerEntryForTextToAccountMapping(Handled, Found, BankAccLedgerEntry, BankAccReconciliationLine, TextToAccountMapping, BalAccountNo);
if Handled then
exit(Found);
BankAccLedgerEntry.SetRange("Bank Account No.", BankAccReconciliationLine."Bank Account No.");
BankAccLedgerEntry.SetRange(Open, true);
BankAccLedgerEntry.SetRange("Bal. Account Type", TextToAccountMapping."Bal. Source Type");
BankAccLedgerEntry.SetRange("Bal. Account No.", BalAccountNo);
BankAccLedgerEntry.SetRange("Remaining Amount", BankAccReconciliationLine."Statement Amount");
if BankAccReconciliationLine."Transaction Date" = 0D then
exit(false);
BankAccLedgerEntry.SetFilter("Posting Date", '>=%1&<=%2', CalcDate('<-2D>', BankAccReconciliationLine."Transaction Date"), CalcDate('<+2D>', BankAccReconciliationLine."Transaction Date"));
if not BankAccLedgerEntry.FindSet() then
exit(false);
repeat
Nearness := RecordMatchMgt.CalculateStringNearness(RecordMatchMgt.Trim(TextToAccountMapping."Mapping Text"), BankAccLedgerEntry.Description, StrLen(BankAccLedgerEntry.Description), GetNormalizingFactor());
if Nearness >= GetExactMatchTreshold() then
exit(true);
until BankAccLedgerEntry.Next() = 0;
exit(false);
end;
local procedure CreateAppliedEntries(BankAccReconciliation: Record "Bank Acc. Reconciliation")
var
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
begin
TempBankStatementMatchingBuffer.Reset();
TempBankStatementMatchingBuffer.SetCurrentKey(Quality, "No. of Entries");
TempBankStatementMatchingBuffer.Ascending(false);
TempBankStmtMultipleMatchLine.SetCurrentKey("Due Date");
OnCreateAppliedEntriesOnBeforeTempBankStatementMatchingBufferFindset(BankAccReconciliation, TempBankStatementMatchingBuffer, TempBankStmtMultipleMatchLine);
if TempBankStatementMatchingBuffer.FindSet() then
repeat
BankAccReconciliationLine.Get(
BankAccReconciliation."Statement Type", BankAccReconciliation."Bank Account No.", BankAccReconciliation."Statement No.",
TempBankStatementMatchingBuffer."Line No.");
if not StatementLineAlreadyApplied(TempBankStatementMatchingBuffer, BankAccReconciliationLine) then begin
PrepareLedgerEntryForApplication(BankAccReconciliationLine);
ApplyRecords(BankAccReconciliationLine, TempBankStatementMatchingBuffer);
end;
until TempBankStatementMatchingBuffer.Next() = 0;
end;
local procedure ApplyRecords(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary): Boolean
var
AppliedPaymentEntry: Record "Applied Payment Entry";
begin
if TempBankStatementMatchingBuffer.Quality = 0 then
exit(false);
if TempBankStatementMatchingBuffer."One to Many Match" then
ApplyOneToMany(BankAccReconciliationLine, TempBankStatementMatchingBuffer)
else begin
if EntryAlreadyApplied(TempBankStatementMatchingBuffer, BankAccReconciliationLine, TempBankStatementMatchingBuffer."Entry No.")
then
if not CanApplyManyToOne(TempBankStatementMatchingBuffer, BankAccReconciliationLine) then
exit(false);
AppliedPaymentEntry.ApplyFromBankStmtMatchingBuf(BankAccReconciliationLine, TempBankStatementMatchingBuffer,
BankAccReconciliationLine."Statement Amount", TempBankStatementMatchingBuffer."Entry No.");
end;
Session.LogMessage('0000DKA', StrSubstNo(AppliedEntriesToBankStatementLineTxt, BankAccReconciliationLine.SystemId, TempBankStatementMatchingBuffer."Account Type", TempBankStatementMatchingBuffer."One to Many Match", TempBankStatementMatchingBuffer.Quality), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', PaymentRecPreformanceCategoryLbl);
OnApplyRecordsOnAfterSendTraceTag(BankAccReconciliationLine, TempBankStatementMatchingBuffer);
exit(true);
end;
local procedure ApplyOneToMany(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary)
var
PaymentMatchingDetails: Record "Payment Matching Details";
AppliedPaymentEntry: Record "Applied Payment Entry";
begin
TempBankStmtMultipleMatchLine.SetRange("Line No.", TempBankStatementMatchingBuffer."Line No.");
TempBankStmtMultipleMatchLine.SetRange("Account Type", TempBankStatementMatchingBuffer."Account Type");
TempBankStmtMultipleMatchLine.SetRange("Account No.", TempBankStatementMatchingBuffer."Account No.");
TempBankStmtMultipleMatchLine.FindSet();
repeat
AppliedPaymentEntry.TransferFromBankAccReconLine(BankAccReconciliationLine);
if AppliedPaymentEntry.GetStmtLineRemAmtToApply() = 0 then
PaymentMatchingDetails.CreatePaymentMatchingDetail(BankAccReconciliationLine,
StrSubstNo(CannotApplyDocumentNoOneToManyApplicationTxt, TempBankStmtMultipleMatchLine."Document No."))
else begin
Clear(AppliedPaymentEntry);
if not EntryAlreadyApplied(
TempBankStatementMatchingBuffer, BankAccReconciliationLine, TempBankStmtMultipleMatchLine."Entry No.")
then
AppliedPaymentEntry.ApplyFromBankStmtMatchingBuf(BankAccReconciliationLine, TempBankStatementMatchingBuffer,
BankAccReconciliationLine."Statement Amount", TempBankStmtMultipleMatchLine."Entry No.")
end;
until TempBankStmtMultipleMatchLine.Next() = 0;
end;
local procedure CanEntriesMatch(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Amount: Decimal; EntryPostingDate: Date) Result: Boolean
var
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeCanEntriesMatch(BankAccReconciliationLine, Amount, EntryPostingDate, ApplyEntries, Result, IsHandled);
if IsHandled then
exit(Result);
if not ApplyEntries then
exit(true);
if BankAccReconciliationLine."Statement Amount" * Amount < 0 then
exit(false);
if ApplyEntries then
if BankAccReconciliationLine."Transaction Date" < EntryPostingDate then
exit(false);
exit(true);
end;
local procedure CanApplyManyToOne(TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary): Boolean
var
AppliedPaymentEntry: Record "Applied Payment Entry";
HasPositiveApplications: Boolean;
HasNegativeApplications: Boolean;
begin
// Many to one application is possbile if previous applied are for same Account
SetFilterToRelatedApplications(AppliedPaymentEntry, TempBankStatementMatchingBuffer,
TempBankAccReconciliationLine);
if AppliedPaymentEntry.IsEmpty() then
exit(false);
// Not possible if positive and negative applications already exists
AppliedPaymentEntry.SetFilter("Applied Amount", '>0');
HasPositiveApplications := not AppliedPaymentEntry.IsEmpty();
AppliedPaymentEntry.SetFilter("Applied Amount", '<0');
HasNegativeApplications := not AppliedPaymentEntry.IsEmpty();
if HasPositiveApplications and HasNegativeApplications then
exit(false);
// Remaining amount should not be 0
exit(GetRemainingAmount(TempBankStatementMatchingBuffer, TempBankAccReconciliationLine) <> 0);
end;
local procedure RelatedPartyMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type")
begin
case AccountType of
TempBankStatementMatchingBuffer."Account Type"::Customer:
CustomerMatching(BankPmtApplRule, TempLedgerEntryMatchingBuffer."Account No.", BankAccReconciliationLine, AccountType);
TempBankStatementMatchingBuffer."Account Type"::Vendor:
VendorMatching(BankPmtApplRule, TempLedgerEntryMatchingBuffer."Account No.", BankAccReconciliationLine, AccountType);
TempBankStatementMatchingBuffer."Account Type"::Employee:
EmployeeMatching(BankPmtApplRule, TempLedgerEntryMatchingBuffer."Account No.", BankAccReconciliationLine, AccountType);
TempBankStatementMatchingBuffer."Account Type"::"Bank Account":
RelatedPartyMatchingForBankAccLedgEntry(BankPmtApplRule, TempLedgerEntryMatchingBuffer, BankAccReconciliationLine, AccountType);
end;
end;
local procedure CustomerMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; AccountNo: Code[20]; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type")
var
Customer: Record Customer;
begin
if IsCustomerBankAccountMatching(
BankAccReconciliationLine."Related-Party Bank Acc. No.", AccountNo, BankAccReconciliationLine."Bank Account No.")
then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Fully;
AppendText(RelatedPartyMatchedInfoText, MatchedRelatedPartyOnBankAccountMsg);
exit;
end;
if not Customer.Get(AccountNo) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::No;
SendMissingRelatedPartyTelemetry(TempBankStatementMatchingBuffer."Account Type"::Customer);
exit;
end;
RelatedPartyInfoMatching(
BankPmtApplRule, BankAccReconciliationLine, Customer.Name, Customer.Address, Customer.City, AccountType);
end;
local procedure VendorMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; AccountNo: Code[20]; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type")
var
Vendor: Record Vendor;
begin
if not Vendor.Get(AccountNo) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::No;
SendMissingRelatedPartyTelemetry(TempBankStatementMatchingBuffer."Account Type"::Vendor);
exit;
end;
if IsVendorBankAccountMatching(BankAccReconciliationLine."Related-Party Bank Acc. No.", Vendor."No.", BankAccReconciliationLine."Bank Account No.") then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Fully;
AppendText(RelatedPartyMatchedInfoText, MatchedRelatedPartyOnBankAccountMsg);
exit;
end;
RelatedPartyInfoMatching(
BankPmtApplRule, BankAccReconciliationLine, Vendor.Name, Vendor.Address, Vendor.City, AccountType);
end;
local procedure EmployeeMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; AccountNo: Code[20]; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type")
var
Employee: Record Employee;
begin
if not Employee.Get(AccountNo) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::No;
SendMissingRelatedPartyTelemetry(TempBankStatementMatchingBuffer."Account Type"::Employee);
exit;
end;
if IsEmployeeBankAccountMatching(BankAccReconciliationLine."Related-Party Bank Acc. No.", Employee) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Fully;
AppendText(RelatedPartyMatchedInfoText, MatchedRelatedPartyOnBankAccountMsg);
exit;
end;
RelatedPartyInfoMatching(
BankPmtApplRule, BankAccReconciliationLine, Employee.FullName(), Employee.Address, Employee.City, AccountType);
end;
local procedure RelatedPartyMatchingForBankAccLedgEntry(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type")
begin
case TempLedgerEntryMatchingBuffer."Bal. Account Type" of
TempLedgerEntryMatchingBuffer."Bal. Account Type"::Customer:
CustomerMatching(BankPmtApplRule, TempLedgerEntryMatchingBuffer."Bal. Account No.", BankAccReconciliationLine, AccountType);
TempLedgerEntryMatchingBuffer."Bal. Account Type"::Vendor:
VendorMatching(BankPmtApplRule, TempLedgerEntryMatchingBuffer."Bal. Account No.", BankAccReconciliationLine, AccountType);
TempLedgerEntryMatchingBuffer."Bal. Account Type"::"Bank Account",
TempLedgerEntryMatchingBuffer."Bal. Account Type"::"G/L Account":
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::No;
end;
end;
local procedure RelatedPartyInfoMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Name: Text[100]; Address: Text[100]; City: Text[30]; AccountType: Enum "Gen. Journal Account Type")
var
Handled: Boolean;
begin
OnRelatedPartyInfoMatching(BankPmtApplRule, BankAccReconciliationLine, Name, Address, City, AccountType, RelatedPartyMatchedInfoText, Handled);
if Handled then
exit;
InitializeBankPmtApplSettings();
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::No;
// If Strutured text present don't look at unstructured text
if BankAccReconciliationLine."Related-Party Name" <> '' then begin
// Use string nearness as names can be reversed, wrongly capitalized, etc
IsNameMatching(BankAccReconciliationLine."Related-Party Name", Name, BankPmtApplRule, BankAccReconciliationLine);
if BankPmtApplRule."Related Party Matched" = BankPmtApplRule."Related Party Matched"::Partially then begin
// City and address should fully match
if (BankAccReconciliationLine."Related-Party City" = City) and
(BankAccReconciliationLine."Related-Party Address" = Address) and (City <> '') and (Address <> '')
then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Fully;
AppendText(RelatedPartyMatchedInfoText, StrSubstNo(MatchedRelatedPartyOnBankStatementInfoMsg, StrSubstNo(RelatedPartyMatchInfoPlaceholderLbl, BankAccReconciliationLine.FieldName(BankAccReconciliationLine."Related-Party Name"), BankAccReconciliationLine.FieldName(BankAccReconciliationLine."Related-Party City"), BankAccReconciliationLine.FieldName(BankAccReconciliationLine."Related-Party Address"))));
exit;
end;
if IsNameUnique(Name, AccountType) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Fully;
AppendText(RelatedPartyMatchedInfoText, RelatedPartyNameIsUniqueMsg);
exit;
end;
end;
exit;
end;
// Unstructured text is using string nearness since user may shorten the name or mistype
IsNameMatching(BankAccReconciliationLine."Transaction Text", Name, BankPmtApplRule, BankAccReconciliationLine);
if BankPmtApplRule."Related Party Matched" = BankPmtApplRule."Related Party Matched"::Partially then
if IsNameUnique(Name, AccountType) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Fully;
AppendText(RelatedPartyMatchedInfoText, RelatedPartyNameIsUniqueMsg);
exit;
end;
end;
local procedure SendMissingRelatedPartyTelemetry(GenJournalAccountType: Enum "Gen. Journal Account Type")
begin
if MissingDataTypeTelemetrySent.Contains(GenJournalAccountType.AsInteger()) then
exit;
Session.LogMessage('0000FT3', StrSubstNo(MissingRelatedPartyTelemetryMsg, GenJournalAccountType), Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', BankAccountRecCategoryLbl);
MissingDataTypeTelemetrySent.Add(GenJournalAccountType.AsInteger());
end;
local procedure DocumentMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; DocNo: Code[20]; ExtDocNo: Code[35]; PaymentReference: Code[50])
var
SearchText: Text;
begin
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::No;
if (PaymentReference <> '') and (BankAccReconciliationLine."Payment Reference No." = PaymentReference) then begin
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes;
AppendText(DocumentMatchedInfoText, DocumentNumberMatchedByPaymentReferenceMsg);
exit;
end;
SearchText := UpperCase(BankAccReconciliationLine."Transaction Text" + ' ' +
BankAccReconciliationLine."Additional Transaction Info");
if DocNoMatching(SearchText, DocNo) then begin
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes;
AppendText(DocumentMatchedInfoText, DocumentNumberMatchedInTransactionTextMsg);
exit;
end;
if DocNoMatching(SearchText, ExtDocNo) then begin
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes;
AppendText(DocumentMatchedInfoText, ExternalDocumentNumberMatchedInTransactionTextMsg);
end;
end;
procedure DocumentMatchingForBankLedgerEntry(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
var
SearchText: Text;
ClosingEntriesDocumentNumbers: List of [Code[35]];
DisableMatch: Boolean;
begin
InitializeBankPmtApplSettings();
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::No;
SearchText := UpperCase(BankAccReconciliationLine."Transaction Text" + ' ' +
BankAccReconciliationLine."Additional Transaction Info");
if DocNoMatching(SearchText, TempLedgerEntryMatchingBuffer."Document No.") then begin
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes;
exit;
end;
if DocNoMatching(SearchText, TempLedgerEntryMatchingBuffer."External Document No.") then begin
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes;
exit;
end;
OnDocumentMatchingForBankLedgerEntryOnBeforeMatch(SearchText, TempLedgerEntryMatchingBuffer, BankPmtApplRule);
if BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" = BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes then
exit;
if not BankPmtApplSettings."Bank Ledg Closing Doc No Match" then
exit;
OnDisableMatchBankLedgerEntriesFromClosingLedgerEntries(DisableMatch);
if DisableMatch then
exit;
if not BankLedgerEntriesClosingDocumentNumbers.Get(TempLedgerEntryMatchingBuffer."Entry No.", ClosingEntriesDocumentNumbers) then
PopulateClosingNumberDictionary(ClosingEntriesDocumentNumbers, TempLedgerEntryMatchingBuffer);
if ClosingEntriesDocumentNumbers.Count() = 0 then
exit;
if ClosingDocumentMatching(SearchText, ClosingEntriesDocumentNumbers) then
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::Yes;
end;
local procedure PopulateClosingNumberDictionary(var ClosingEntriesDocumentNumbers: List of [Code[35]]; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
var
CustLedgerEntry: Record "Cust. Ledger Entry";
CustLedgerEntry2: Record "Cust. Ledger Entry";
VendLedgerEntry: Record "Vendor Ledger Entry";
VendLedgerEntry2: Record "Vendor Ledger Entry";
StartTime: DateTime;
begin
StartTime := CurrentDateTime();
BankLedgerEntriesClosingDocumentNumbers.Add(TempLedgerEntryMatchingBuffer."Entry No.", ClosingEntriesDocumentNumbers);
CustLedgerEntry.SetCurrentKey("Document No.");
CustLedgerEntry.SetRange("Document No.", TempLedgerEntryMatchingBuffer."Document No.");
CustLedgerEntry.SetRange("Document Type", TempLedgerEntryMatchingBuffer."Document Type");
CustLedgerEntry.SetRange("Posting Date", TempLedgerEntryMatchingBuffer."Posting Date");
if CustLedgerEntry.FindSet() then
repeat
CustLedgerEntry2.SetRange(Open, false);
CustLedgerEntry2.SetRange("Closed by Entry No.", CustLedgerEntry."Entry No.");
if CustLedgerEntry2.FindFirst() then
ClosingEntriesDocumentNumbers.Add(CustLedgerEntry2."Document No.");
until CustLedgerEntry.Next() = 0;
VendLedgerEntry.SetCurrentKey("Document No.");
VendLedgerEntry.SetRange("Document No.", TempLedgerEntryMatchingBuffer."Document No.");
VendLedgerEntry.SetRange("Document Type", TempLedgerEntryMatchingBuffer."Document Type");
VendLedgerEntry.SetRange("Posting Date", TempLedgerEntryMatchingBuffer."Posting Date");
if VendLedgerEntry.FindSet() then
repeat
VendLedgerEntry2.SetRange(Open, false);
VendLedgerEntry2.SetRange("Closed by Entry No.", VendLedgerEntry."Entry No.");
if VendLedgerEntry2.FindFirst() then
ClosingEntriesDocumentNumbers.Add(VendLedgerEntry2."Document No.");
until VendLedgerEntry.Next() = 0;
TotalTimeSearchingDocumentNoInLedgerEntries += CurrentDateTime() - StartTime;
end;
local procedure ClosingDocumentMatching(SearchText: Text; var ClosingDocumentNumbers: List of [Code[35]]): Boolean
var
I: Integer;
begin
TotalNoClosingDocumentMatches += 1;
for I := 1 to ClosingDocumentNumbers.Count() do
if (DocNoMatching(SearchText, ClosingDocumentNumbers.Get(I))) then begin
HitCountClosingDocumentMatches += 1;
exit(true);
end;
exit(false);
end;
procedure DocNoMatching(SearchText: Text; DocNo: Code[35]): Boolean
var
Position: Integer;
begin
if StrLen(DocNo) < GetMatchLengthTreshold() then
exit(false);
Position := StrPos(SearchText, DocNo);
case Position of
0:
exit(false);
1:
begin
if StrLen(SearchText) = StrLen(DocNo) then
exit(true);
exit(not IsAlphanumeric(SearchText[Position + StrLen(DocNo)]));
end;
else begin
if StrLen(SearchText) < Position + StrLen(DocNo) then
exit(not IsAlphanumeric(SearchText[Position - 1]));
exit((not IsAlphanumeric(SearchText[Position - 1])) and
(not IsAlphanumeric(SearchText[Position + StrLen(DocNo)])));
end;
end;
exit(true);
end;
local procedure AmountInclToleranceMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; AccountType: Enum "Gen. Journal Account Type"; RemainingAmount: Decimal)
var
NoOfEntries: Integer;
MinAmount: Decimal;
MaxAmount: Decimal;
begin
BankAccReconciliationLine.GetAmountRangeForTolerance(MinAmount, MaxAmount);
BankPmtApplRule."Amount Incl. Tolerance Matched" := BankPmtApplRule."Amount Incl. Tolerance Matched"::"No Matches";
if (RemainingAmount < MinAmount) or
(RemainingAmount > MaxAmount)
then
exit;
NoOfEntries := 0;
BankPmtApplRule."Amount Incl. Tolerance Matched" := BankPmtApplRule."Amount Incl. Tolerance Matched"::"Multiple Matches";
// Check for Multiple Hits for One To Many matches
if BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" =
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::"Yes - Multiple"
then begin
TempOneToManyTempBankStatementMatchingBuffer.SetFilter("Total Remaining Amount", '>=%1&<=%2', MinAmount, MaxAmount);
NoOfEntries += TempOneToManyTempBankStatementMatchingBuffer.Count();
if NoOfEntries > 1 then
exit;
end;
// Check is a single match for One to One Matches
case AccountType of
TempBankStatementMatchingBuffer."Account Type"::Customer:
NoOfEntries +=
TempCustomerLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
TempBankStatementMatchingBuffer."Account Type"::Vendor:
NoOfEntries +=
TempVendorLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
TempBankStatementMatchingBuffer."Account Type"::Employee:
NoOfEntries +=
TempEmployeeLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", UsePaymentDiscounts);
TempBankStatementMatchingBuffer."Account Type"::"Bank Account":
NoOfEntries +=
TempBankAccLedgerEntryMatchingBuffer.GetNoOfLedgerEntriesWithinRange(
MinAmount, MaxAmount, BankAccReconciliationLine."Transaction Date", false);
end;
if NoOfEntries = 1 then
BankPmtApplRule."Amount Incl. Tolerance Matched" := BankPmtApplRule."Amount Incl. Tolerance Matched"::"One Match"
end;
local procedure DirectDebitCollectionMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
var
DirectDebitCollection: Record "Direct Debit Collection";
StartTime: DateTime;
begin
StartTime := CurrentDateTime;
BankPmtApplRule."Direct Debit Collect. Matched" := BankPmtApplRule."Direct Debit Collect. Matched"::"Not Considered";
if (BankAccReconciliationLine."Transaction ID" = '') or
(StrLen(BankAccReconciliationLine."Transaction ID") > MaxStrLen(TempDirectDebitCollectionBuffer."Transaction ID"))
then
exit;
TempDirectDebitCollectionBuffer.Reset();
TempDirectDebitCollectionBuffer.SetRange("Transaction ID", BankAccReconciliationLine."Transaction ID");
TempDirectDebitCollectionBuffer.SetFilter(Status, '%1|%2',
TempDirectDebitCollectionBuffer.Status::"File Created", TempDirectDebitCollectionBuffer.Status::Posted);
if TempDirectDebitCollectionBuffer.IsEmpty() then
exit;
BankPmtApplRule."Direct Debit Collect. Matched" := BankPmtApplRule."Direct Debit Collect. Matched"::No;
TempDirectDebitCollectionBuffer.SetRange("Applies-to Entry No.", TempLedgerEntryMatchingBuffer."Entry No.");
if TempDirectDebitCollectionBuffer.FindFirst() then
if DirectDebitCollection.Get(TempDirectDebitCollectionBuffer."Direct Debit Collection No.") then
if (DirectDebitCollection.Status in
[DirectDebitCollection.Status::"File Created",
DirectDebitCollection.Status::Posted,
DirectDebitCollection.Status::Closed])
then
BankPmtApplRule."Direct Debit Collect. Matched" := BankPmtApplRule."Direct Debit Collect. Matched"::Yes;
TotalTimeDirectCollection += CurrentDateTime() - StartTime;
end;
local procedure IsNameMatching(Description: Text; CustVendValue: Text; var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"): Integer
var
STRNameNearness: Integer;
begin
InitializeBankPmtApplSettings();
case BankPmtApplSettings."RelatedParty Name Matching" of
BankPmtApplSettings."RelatedParty Name Matching"::Disabled:
begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::No;
exit;
end;
BankPmtApplSettings."RelatedParty Name Matching"::"String Nearness":
begin
STRNameNearness := GetStringNearness(Description, CustVendValue);
if STRNameNearness >= GetExactMatchTreshold() then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Partially;
if STRNameNearness < 100 then
AppendText(RelatedPartyMatchedInfoText, StrSubstNo(TextPartialMatchMsg, BankAccReconciliationLine.FieldName("Related-Party Name"), STRNameNearness));
end;
exit;
end;
BankPmtApplSettings."RelatedParty Name Matching"::"Exact Match with Permutations":
begin
IsNameMatchingWithPermutations(Description, CustVendValue, BankPmtApplRule);
exit;
end;
end;
end;
local procedure IsNameMatchingWithPermutations(Description: Text; CustVendValue: Text; var BankPmtApplRule: Record "Bank Pmt. Appl. Rule")
var
SwappedFirstAndLastName: Text;
FirstName: text;
PositionOfFirstSpace: Integer;
begin
if StrLen(Description) < StrLen(CustVendValue) then
exit;
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::No;
if Description.Contains(CustVendValue) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Partially;
exit;
end;
// To simplify the algorith for matching we will just put the first name to the end
// This works optimal for First Name / Last Name and few other cultures
PositionOfFirstSpace := CustVendValue.IndexOf(' ');
if PositionOfFirstSpace <= 0 then
exit;
FirstName := CustVendValue.Substring(1, PositionOfFirstSpace - 1);
SwappedFirstAndLastName += CustVendValue.Substring(PositionOfFirstSpace + 1, StrLen(CustVendValue) - PositionOfFirstSpace) + ' ' + FirstName;
if Description.Contains(SwappedFirstAndLastName) then begin
BankPmtApplRule."Related Party Matched" := BankPmtApplRule."Related Party Matched"::Partially;
exit;
end;
end;
local procedure GetStringNearness(Description: Text; CustVendValue: Text): Integer
var
RecordMatchMgt: Codeunit "Record Match Mgt.";
StartTime: DateTime;
Nearness: Integer;
Disable: Boolean;
begin
if StrLen(Description) < (StrLen(CustVendValue) * GetCloseMatchTreshold() / GetNormalizingFactor()) then
exit(0);
Description := RecordMatchMgt.Trim(Description);
OnDisableStringNearnessMatch(Disable);
if Disable then
exit(0);
StartTime := CurrentDateTime();
Nearness := RecordMatchMgt.CalculateStringNearness(CustVendValue, Description, GetMatchLengthTreshold(), GetNormalizingFactor());
TotalTimeStringNearness += CurrentDateTime() - StartTime;
exit(Nearness);
end;
local procedure IsCustomerBankAccountMatching(ValueFromBankStatement: Text; CustomerNo: Code[20]; BankAccountNo: Code[20]) Result: Boolean
var
CustomerBankAccount: Record "Customer Bank Account";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeIsCustomerBankAccountMatching(ValueFromBankStatement, CustomerNo, BankAccountNo, Result, IsHandled);
if IsHandled then
exit(Result);
ValueFromBankStatement := BankAccountNoWithoutSpecialChars(ValueFromBankStatement);
if ValueFromBankStatement = '' then
exit(false);
CustomerBankAccount.SetRange("Customer No.", CustomerNo);
if CustomerBankAccount.FindSet() then
repeat
if BankAccountNoWithoutSpecialChars(CustomerBankAccount.GetBankAccountNo()) = ValueFromBankStatement then
exit(true);
until CustomerBankAccount.Next() = 0;
exit(false);
end;
local procedure IsVendorBankAccountMatching(ValueFromBankStatement: Text; VendorNo: Code[20]; BankAccountNo: Code[20]) Result: Boolean
var
VendorBankAccount: Record "Vendor Bank Account";
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeIsVendorBankAccountMatching(ValueFromBankStatement, VendorNo, BankAccountNo, Result, IsHandled);
if IsHandled then
exit(Result);
ValueFromBankStatement := BankAccountNoWithoutSpecialChars(ValueFromBankStatement);
if ValueFromBankStatement = '' then
exit(false);
VendorBankAccount.SetRange("Vendor No.", VendorNo);
if VendorBankAccount.FindSet() then
repeat
if BankAccountNoWithoutSpecialChars(VendorBankAccount.GetBankAccountNo()) = ValueFromBankStatement then
exit(true);
until VendorBankAccount.Next() = 0;
exit(false);
end;
local procedure IsEmployeeBankAccountMatching(ValueFromBankStatement: Text; Employee: Record Employee): Boolean
begin
ValueFromBankStatement := BankAccountNoWithoutSpecialChars(ValueFromBankStatement);
if ValueFromBankStatement = '' then
exit(false);
if Employee."Bank Account No." <> '' then
if BankAccountNoWithoutSpecialChars(Employee."Bank Account No.") = ValueFromBankStatement then
exit(true);
exit(false);
end;
local procedure BankAccountNoWithoutSpecialChars(Input: Text): Text
begin
exit(UpperCase(DelChr(Input, '=', DelChr(UpperCase(Input), '=', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'))));
end;
local procedure StatementLineAlreadyApplied(TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary): Boolean
var
AppliedPaymentEntry: Record "Applied Payment Entry";
begin
SetFilterToBankAccReconciliation(AppliedPaymentEntry, TempBankAccReconciliationLine);
AppliedPaymentEntry.SetRange("Statement Line No.", TempBankStatementMatchingBuffer."Line No.");
exit(not AppliedPaymentEntry.IsEmpty);
end;
local procedure EntryAlreadyApplied(TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary; EntryNo: Integer): Boolean
var
AppliedPaymentEntry: Record "Applied Payment Entry";
begin
SetFilterToBankAccReconciliation(AppliedPaymentEntry, TempBankAccReconciliationLine);
AppliedPaymentEntry.SetRange("Account Type", TempBankStatementMatchingBuffer."Account Type");
AppliedPaymentEntry.SetRange("Applies-to Entry No.", EntryNo);
exit(not AppliedPaymentEntry.IsEmpty);
end;
local procedure SetFilterToBankAccReconciliation(var AppliedPaymentEntry: Record "Applied Payment Entry"; TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary)
begin
AppliedPaymentEntry.FilterAppliedPmtEntry(TempBankAccReconciliationLine);
AppliedPaymentEntry.SetRange("Statement Line No.");
end;
local procedure SetFilterToRelatedApplications(var AppliedPaymentEntry: Record "Applied Payment Entry"; TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary)
begin
SetFilterToBankAccReconciliation(AppliedPaymentEntry, TempBankAccReconciliationLine);
AppliedPaymentEntry.SetRange("Account Type", TempBankStatementMatchingBuffer."Account Type");
AppliedPaymentEntry.SetRange("Account No.", TempBankStatementMatchingBuffer."Account No.");
AppliedPaymentEntry.SetRange("Applies-to Entry No.", TempBankStatementMatchingBuffer."Entry No.");
end;
local procedure GetRemainingAmount(TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary): Decimal
var
TempAppliedPaymentEntry: Record "Applied Payment Entry" temporary;
begin
TempAppliedPaymentEntry.TransferFromBankAccReconLine(TempBankAccReconciliationLine);
TempAppliedPaymentEntry."Account Type" := TempBankStatementMatchingBuffer."Account Type";
TempAppliedPaymentEntry."Account No." := TempBankStatementMatchingBuffer."Account No.";
TempAppliedPaymentEntry."Applies-to Entry No." := TempBankStatementMatchingBuffer."Entry No.";
exit(TempAppliedPaymentEntry.GetRemAmt() - TempAppliedPaymentEntry.GetAmtAppliedToOtherStmtLines());
end;
local procedure ShowMatchSummary(BankAccReconciliation: Record "Bank Acc. Reconciliation")
var
BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line";
MatchedCount: Integer;
TotalCount: Integer;
TextMatchCount: Integer;
FinalText: Text;
IsHandled: Boolean;
begin
BankAccReconciliationLine.SetRange("Statement Type", BankAccReconciliation."Statement Type"::"Payment Application");
BankAccReconciliationLine.SetRange("Bank Account No.", BankAccReconciliation."Bank Account No.");
BankAccReconciliationLine.SetRange("Statement No.", BankAccReconciliation."Statement No.");
TotalCount := BankAccReconciliationLine.Count();
BankAccReconciliationLine.SetFilter("Applied Entries", '>0');
MatchedCount := BankAccReconciliationLine.Count();
FinalText := StrSubstNo(MatchSummaryMsg, MatchedCount, TotalCount);
IsHandled := false;
OnShowMatchSummaryOnAfterSetFinalText(BankAccReconciliation, FinalText, IsHandled);
if not IsHandled then
Message(FinalText);
BankAccReconciliationLine.SetRange("Match Confidence", BankAccReconciliationLine."Match Confidence"::"High - Text-to-Account Mapping");
TextMatchCount := BankAccReconciliationLine.Count();
Session.LogMessage('0000AIA', StrSubstNo(BankAccountRecTotalAndMatchedLinesLbl, TotalCount, MatchedCount, TextMatchCount), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', BankAccountRecCategoryLbl);
end;
local procedure UpdateOneToManyMatches(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
RemoveInvalidOneToManyMatches();
GetOneToManyMatches();
ScoreOneToManyMatches(BankAccReconciliationLine);
end;
local procedure ScoreOneToManyMatches(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
BankPmtApplRule: Record "Bank Pmt. Appl. Rule";
Score: Integer;
begin
if TempBankStatementMatchingBuffer.FindSet() then
repeat
BankPmtApplRule."Doc. No./Ext. Doc. No. Matched" := BankPmtApplRule."Doc. No./Ext. Doc. No. Matched"::"Yes - Multiple";
BankPmtApplRule."Related Party Matched" := TempBankStatementMatchingBuffer."Related Party Matched";
BankAccReconciliationLine.Get(
BankAccReconciliationLine."Statement Type", BankAccReconciliationLine."Bank Account No.",
BankAccReconciliationLine."Statement No.", TempBankStatementMatchingBuffer."Line No.");
AmountInclToleranceMatching(
BankPmtApplRule, BankAccReconciliationLine, TempBankStatementMatchingBuffer."Account Type",
TempBankStatementMatchingBuffer."Total Remaining Amount");
Score := TempBankPmtApplRule.GetBestMatchScore(BankPmtApplRule);
TempBankStatementMatchingBuffer.Quality := Score;
TempBankStatementMatchingBuffer.Modify();
until TempBankStatementMatchingBuffer.Next() = 0;
TempBankStatementMatchingBuffer.Reset();
end;
local procedure RemoveInvalidOneToManyMatches()
begin
TempBankStatementMatchingBuffer.Reset();
TempBankStatementMatchingBuffer.SetRange("One to Many Match", true);
TempBankStatementMatchingBuffer.SetFilter("No. of Entries", '=1');
TempBankStatementMatchingBuffer.DeleteAll(true);
TempBankStatementMatchingBuffer.Reset();
end;
local procedure GetOneToManyMatches()
begin
TempBankStatementMatchingBuffer.Reset();
TempBankStatementMatchingBuffer.SetRange("One to Many Match", true);
TempBankStatementMatchingBuffer.SetFilter("No. of Entries", '>1');
if TempBankStatementMatchingBuffer.FindSet() then
repeat
TempOneToManyTempBankStatementMatchingBuffer := TempBankStatementMatchingBuffer;
TempOneToManyTempBankStatementMatchingBuffer.Insert(true);
until TempBankStatementMatchingBuffer.Next() = 0;
end;
procedure GetExactMatchTreshold(): Decimal
begin
exit(0.95 * GetNormalizingFactor());
end;
procedure GetCloseMatchTreshold(): Decimal
begin
exit(0.65 * GetNormalizingFactor());
end;
local procedure GetMatchLengthTreshold(): Decimal
begin
exit(4);
end;
procedure GetNormalizingFactor(): Decimal
begin
exit(100);
end;
local procedure GetLowestMatchScore(): Integer
var
Score: Integer;
begin
if not ApplyEntries then
exit(0);
TempBankPmtApplRule.SetFilter("Match Confidence", '<>%1', TempBankPmtApplRule."Match Confidence"::None);
TempBankPmtApplRule.SetCurrentKey(Score);
TempBankPmtApplRule.Ascending(false);
Score := 0;
if TempBankPmtApplRule.FindLast() then
Score := TempBankPmtApplRule.Score;
TempBankPmtApplRule.Reset();
exit(Score);
end;
local procedure IsNameUnique(Name: Text[100]; AccountType: Enum "Gen. Journal Account Type"): Boolean
var
Customer: Record Customer;
Vendor: Record Vendor;
begin
case AccountType of
TempBankStatementMatchingBuffer."Account Type"::Customer:
begin
Customer.SetFilter(Name, '%1', '@*' + Name + '*');
exit(Customer.Count = 1);
end;
TempBankStatementMatchingBuffer."Account Type"::Vendor:
begin
Vendor.SetFilter(Name, '%1', '@*' + Name + '*');
exit(Vendor.Count = 1);
end;
end;
end;
local procedure IsAlphanumeric(Character: Char): Boolean
begin
exit((Character in ['0' .. '9']) or (Character in ['A' .. 'Z']) or (Character in ['a' .. 'z']));
end;
procedure GetBankStatementMatchingBuffer(var TempBankStatementMatchingBuffer2: Record "Bank Statement Matching Buffer" temporary)
begin
TempBankStatementMatchingBuffer2.Copy(TempBankStatementMatchingBuffer, true);
TempBankStatementMatchingBuffer2.Reset();
end;
procedure GetLedgerEntriesAsMatchingBuffer(var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
BankPmtApplSettings: Record "Bank Pmt. Appl. Settings";
begin
BankPmtApplSettings.GetOrInsert();
if not BankPmtApplSettings."Cust Ledg Hidden In Apply Man" then
GetCustomerLedgerEntriesAsMatchingBuffer(TempBankStatementMatchingBuffer, BankAccReconciliationLine);
if not BankPmtApplSettings."Vend Ledg Hidden In Apply Man" then
GetVendorLedgerEntriesAsMatchingBuffer(TempBankStatementMatchingBuffer, BankAccReconciliationLine);
if not BankPmtApplSettings."Empl Ledg Hidden In Apply Man" then
GetEmployeeLedgerEntriesAsMatchingBuffer(TempBankStatementMatchingBuffer, BankAccReconciliationLine);
if not BankPmtApplSettings."Bank Ledg Hidden In Apply Man" then
GetBankLedgerEntriesAsMatchingBuffer(TempBankStatementMatchingBuffer, BankAccReconciliationLine);
end;
procedure GetCustomerLedgerEntriesAsMatchingBuffer(var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
GenJournalAccountType: Enum "Gen. Journal Account Type";
begin
InitializeCustomerLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempCustomerLedgerEntryMatchingBuffer);
TempCustomerLedgerEntryMatchingBuffer.Reset();
if TempCustomerLedgerEntryMatchingBuffer.FindSet() then
repeat
TempBankStatementMatchingBuffer.AddMatchCandidate(
BankAccReconciliationLine."Statement Line No.", TempCustomerLedgerEntryMatchingBuffer."Entry No.",
0, GenJournalAccountType::Customer, TempCustomerLedgerEntryMatchingBuffer."Account No.");
until TempCustomerLedgerEntryMatchingBuffer.Next() = 0;
end;
procedure GetVendorLedgerEntriesAsMatchingBuffer(var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
GenJournalAccountType: Enum "Gen. Journal Account Type";
begin
InitializeVendorLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempVendorLedgerEntryMatchingBuffer);
TempVendorLedgerEntryMatchingBuffer.Reset();
if TempVendorLedgerEntryMatchingBuffer.FindSet() then
repeat
TempBankStatementMatchingBuffer.AddMatchCandidate(
BankAccReconciliationLine."Statement Line No.", TempVendorLedgerEntryMatchingBuffer."Entry No.",
0, GenJournalAccountType::Vendor, TempVendorLedgerEntryMatchingBuffer."Account No.");
until TempVendorLedgerEntryMatchingBuffer.Next() = 0;
end;
procedure GetEmployeeLedgerEntriesAsMatchingBuffer(var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
GenJournalAccountType: Enum "Gen. Journal Account Type";
begin
InitializeEmployeeLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempEmployeeLedgerEntryMatchingBuffer);
TempEmployeeLedgerEntryMatchingBuffer.Reset();
if TempEmployeeLedgerEntryMatchingBuffer.FindSet() then
repeat
TempBankStatementMatchingBuffer.AddMatchCandidate(
BankAccReconciliationLine."Statement Line No.", TempEmployeeLedgerEntryMatchingBuffer."Entry No.",
0, GenJournalAccountType::Employee, TempEmployeeLedgerEntryMatchingBuffer."Account No.");
until TempEmployeeLedgerEntryMatchingBuffer.Next() = 0;
end;
procedure GetBankLedgerEntriesAsMatchingBuffer(var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
GenJournalAccountType: Enum "Gen. Journal Account Type";
begin
InitializeBankAccLedgerEntriesMatchingBuffer(BankAccReconciliationLine, TempBankAccLedgerEntryMatchingBuffer);
TempBankAccLedgerEntryMatchingBuffer.Reset();
if TempBankAccLedgerEntryMatchingBuffer.FindSet() then
repeat
TempBankStatementMatchingBuffer.AddMatchCandidate(
BankAccReconciliationLine."Statement Line No.", TempBankAccLedgerEntryMatchingBuffer."Entry No.",
0, GenJournalAccountType::"Bank Account", TempBankAccLedgerEntryMatchingBuffer."Account No.");
until TempBankAccLedgerEntryMatchingBuffer.Next() = 0;
end;
local procedure UpdatePaymentMatchDetails(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
BankAccReconciliationLine2: Record "Bank Acc. Reconciliation Line";
begin
BankAccReconciliationLine2.CopyFilters(BankAccReconciliationLine);
OnUpdatePaymentMatchDetailsOnAfterBankAccReconciliationLine2SetFilters(BankAccReconciliationLine2, BankAccReconciliationLine);
if BankAccReconciliationLine2.FindSet() then
repeat
BankAccReconciliationLine2.CalcFields("Match Confidence", "Match Quality");
AddWarningsForTextMapperOverriden(BankAccReconciliationLine2);
AddWarningsForStatementCanBeAppliedToMultipleEntries(BankAccReconciliationLine2);
AddWarningsForMultipleStatementLinesCouldBeAppliedToEntry(BankAccReconciliationLine2);
until BankAccReconciliationLine2.Next() = 0;
end;
local procedure DeletePaymentMatchDetails(BankAccReconciliation: Record "Bank Acc. Reconciliation")
var
PaymentMatchingDetails: Record "Payment Matching Details";
begin
PaymentMatchingDetails.SetRange("Statement Type", BankAccReconciliation."Statement Type");
PaymentMatchingDetails.SetRange("Bank Account No.", BankAccReconciliation."Bank Account No.");
PaymentMatchingDetails.SetRange("Statement No.", BankAccReconciliation."Statement No.");
OnDeletePaymentMatchDetailsOnAfterPaymentMatchingDetailsSetFilters(PaymentMatchingDetails, BankAccReconciliation);
PaymentMatchingDetails.DeleteAll(true);
end;
local procedure DeleteAppliedPaymentEntries(BankAccReconciliation: Record "Bank Acc. Reconciliation"; Overwrite: Boolean)
var
AppliedPaymentEntry: Record "Applied Payment Entry";
begin
AppliedPaymentEntry.SetRange("Statement Type", BankAccReconciliation."Statement Type");
AppliedPaymentEntry.SetRange("Bank Account No.", BankAccReconciliation."Bank Account No.");
AppliedPaymentEntry.SetRange("Statement No.", BankAccReconciliation."Statement No.");
if not Overwrite then
AppliedPaymentEntry.SetFilter("Match Confidence", '<>%1&<>%2', AppliedPaymentEntry."Match Confidence"::Accepted, AppliedPaymentEntry."Match Confidence"::Manual);
OnDeleteAppliedPaymentEntriesOnAfterAppliedPaymentEntrySetFilters(AppliedPaymentEntry, BankAccReconciliation);
AppliedPaymentEntry.DeleteAll(true);
end;
local procedure AddWarningsForTextMapperOverriden(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
PaymentMatchingDetails: Record "Payment Matching Details";
BankPmtApplRule: Record "Bank Pmt. Appl. Rule";
begin
if BankAccReconciliationLine."Match Quality" <= BankPmtApplRule.GetTextMapperScore() then
exit;
TempBankStatementMatchingBuffer.Reset();
TempBankStatementMatchingBuffer.SetRange("Line No.", BankAccReconciliationLine."Statement Line No.");
TempBankStatementMatchingBuffer.SetRange(Quality, BankPmtApplRule.GetTextMapperScore());
if TempBankStatementMatchingBuffer.Count > 0 then
PaymentMatchingDetails.CreatePaymentMatchingDetail(BankAccReconciliationLine,
StrSubstNo(TextMapperRulesOverridenTxt, TempBankStatementMatchingBuffer.Count,
BankAccReconciliationLine."Match Confidence"));
end;
local procedure AddWarningsForStatementCanBeAppliedToMultipleEntries(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
PaymentMatchingDetails: Record "Payment Matching Details";
BankPmtApplRule: Record "Bank Pmt. Appl. Rule";
MinRangeValue: Integer;
MaxRangeValue: Integer;
begin
if BankAccReconciliationLine."Match Confidence" = BankAccReconciliationLine."Match Confidence"::None then
exit;
if BankAccReconciliationLine."Match Quality" = BankPmtApplRule.GetTextMapperScore() then
exit;
TempBankStatementMatchingBuffer.Reset();
TempBankStatementMatchingBuffer.SetRange("Line No.", BankAccReconciliationLine."Statement Line No.");
MinRangeValue := BankPmtApplRule.GetLowestScoreInRange(BankAccReconciliationLine."Match Quality");
MaxRangeValue := BankPmtApplRule.GetHighestScoreInRange(BankAccReconciliationLine."Match Quality");
TempBankStatementMatchingBuffer.SetRange(Quality, MinRangeValue, MaxRangeValue);
if TempBankStatementMatchingBuffer.Count > 1 then
PaymentMatchingDetails.CreatePaymentMatchingDetail(BankAccReconciliationLine,
StrSubstNo(MultipleEntriesWithSilarConfidenceFoundTxt, TempBankStatementMatchingBuffer.Count));
end;
local procedure AddWarningsForMultipleStatementLinesCouldBeAppliedToEntry(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
var
PaymentMatchingDetails: Record "Payment Matching Details";
BankPmtApplRule: Record "Bank Pmt. Appl. Rule";
EntryNo: Integer;
MinRangeValue: Integer;
MaxRangeValue: Integer;
begin
if BankAccReconciliationLine."Match Confidence" = BankAccReconciliationLine."Match Confidence"::None then
exit;
if BankAccReconciliationLine."Match Quality" = BankPmtApplRule.GetTextMapperScore() then
exit;
TempBankStatementMatchingBuffer.Reset();
// Get Entry No.
TempBankStatementMatchingBuffer.SetRange("Line No.", BankAccReconciliationLine."Statement Line No.");
TempBankStatementMatchingBuffer.SetRange(Quality, BankAccReconciliationLine."Match Quality");
TempBankStatementMatchingBuffer.FindFirst();
EntryNo := TempBankStatementMatchingBuffer."Entry No.";
MinRangeValue := BankPmtApplRule.GetLowestScoreInRange(BankAccReconciliationLine."Match Quality");
MaxRangeValue := BankPmtApplRule.GetHighestScoreInRange(BankAccReconciliationLine."Match Quality");
TempBankStatementMatchingBuffer.Reset();
TempBankStatementMatchingBuffer.SetRange("Entry No.", EntryNo);
TempBankStatementMatchingBuffer.SetRange(Quality, MinRangeValue, MaxRangeValue);
TempBankStatementMatchingBuffer.SetFilter("Line No.", '<>%1', BankAccReconciliationLine."Statement Line No.");
if TempBankStatementMatchingBuffer.Count > 1 then
PaymentMatchingDetails.CreatePaymentMatchingDetail(BankAccReconciliationLine,
StrSubstNo(MultipleStatementLinesWithSameConfidenceFoundTxt, TempBankStatementMatchingBuffer.Count));
end;
local procedure AppendText(var MainText: Text; TextToAppend: Text)
begin
if not LogInfoText then
exit;
if (MainText <> '') then
MainText += GetNewLineText() + GetNewLineText();
MainText += TextToAppend;
end;
procedure SetApplyEntries(NewApplyEntries: Boolean)
begin
ApplyEntries := NewApplyEntries;
end;
local procedure GetAvailableSplitLineNo(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; ParentLineNo: Integer): Integer
var
SplitLineNo: Integer;
begin
SplitLineNo := BankAccReconciliationLine."Statement Line No." + 1;
BankAccReconciliationLine.SetRange("Parent Line No.", ParentLineNo);
if BankAccReconciliationLine.FindLast() then
SplitLineNo := BankAccReconciliationLine."Statement Line No." + 1;
exit(SplitLineNo)
end;
local procedure GetParentLineNo(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"): Integer
begin
if BankAccReconciliationLine."Parent Line No." <> 0 then
exit(BankAccReconciliationLine."Parent Line No.");
exit(BankAccReconciliationLine."Statement Line No.");
end;
local procedure PrepareLedgerEntryForApplication(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
if TempBankStatementMatchingBuffer."Entry No." <= 0 then // text mapping has "Entry No." = -10000
exit;
SetApplicationDataInCVLedgEntry(
TempBankStatementMatchingBuffer."Account Type", TempBankStatementMatchingBuffer."Entry No.",
BankAccReconciliationLine.GetAppliesToID());
end;
procedure SetApplicationDataInCVLedgEntry(AccountType: Enum "Gen. Journal Account Type"; EntryNo: Integer;
AppliesToID: Code[50])
var
BankAccReconLine: Record "Bank Acc. Reconciliation Line";
begin
if EntryNo = 0 then
exit;
case AccountType of
BankAccReconLine."Account Type"::Customer:
SetCustAppicationData(EntryNo, AppliesToID);
BankAccReconLine."Account Type"::Vendor:
SetVendAppicationData(EntryNo, AppliesToID);
BankAccReconLine."Account Type"::Employee:
SetEmployeeAppicationData(EntryNo, AppliesToID);
end;
end;
local procedure SetCustAppicationData(EntryNo: Integer; AppliesToID: Code[50])
var
CustLedgEntry: Record "Cust. Ledger Entry";
begin
CustLedgEntry.Get(EntryNo);
CustLedgEntry.CalcFields("Remaining Amount");
CustLedgEntry."Applies-to ID" := AppliesToID;
CustLedgEntry."Amount to Apply" := CustLedgEntry."Remaining Amount";
OnSetCustAppicationDataOnBeforeCustEntryEdit(CustLedgEntry);
CODEUNIT.Run(CODEUNIT::"Cust. Entry-Edit", CustLedgEntry);
end;
local procedure SetVendAppicationData(EntryNo: Integer; AppliesToID: Code[50])
var
VendLedgEntry: Record "Vendor Ledger Entry";
begin
VendLedgEntry.Get(EntryNo);
VendLedgEntry.CalcFields("Remaining Amount");
VendLedgEntry."Applies-to ID" := AppliesToID;
VendLedgEntry."Amount to Apply" := VendLedgEntry."Remaining Amount";
OnSetVendAppicationDataOnBeforeVendEntryEdit(VendLedgEntry);
CODEUNIT.Run(CODEUNIT::"Vend. Entry-Edit", VendLedgEntry);
end;
local procedure SetEmployeeAppicationData(EntryNo: Integer; AppliesToID: Code[50])
var
EmployeeLedgEntry: Record "Employee Ledger Entry";
begin
EmployeeLedgEntry.Get(EntryNo);
EmployeeLedgEntry.CalcFields("Remaining Amount");
EmployeeLedgEntry."Applies-to ID" := AppliesToID;
EmployeeLedgEntry."Amount to Apply" := EmployeeLedgEntry."Remaining Amount";
CODEUNIT.Run(CODEUNIT::"Empl. Entry-Edit", EmployeeLedgEntry);
end;
local procedure GetNewLineText(): Text
var
NewLine: array[2] of Char;
begin
NewLine[1] := 10;
NewLine[2] := 13;
exit(StrSubstNo(NewLinePlaceHolderLbl, NewLine[1], NewLine[2]));
end;
local procedure RevertAcceptedPmtToleranceFromAppliedEntries(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Difference: Decimal)
var
AppliedPmtEntry: Record "Applied Payment Entry";
CustLedgerEntry: Record "Cust. Ledger Entry";
VendorLedgerEntry: Record "Vendor Ledger Entry";
begin
if Difference = 0 then
exit;
AppliedPmtEntry.FilterAppliedPmtEntry(BankAccReconciliationLine);
AppliedPmtEntry.SetFilter("Applies-to Entry No.", '<>%1', 0);
if not AppliedPmtEntry.FindSet() then
exit;
repeat
case AppliedPmtEntry."Account Type" of
AppliedPmtEntry."Account Type"::Customer:
begin
CustLedgerEntry.Get(AppliedPmtEntry."Applies-to Entry No.");
if CustLedgerEntry."Accepted Payment Tolerance" <> 0 then begin
if -CustLedgerEntry."Accepted Payment Tolerance" > Difference then begin
CustLedgerEntry."Accepted Payment Tolerance" += Difference;
Difference := 0;
end else begin
Difference += CustLedgerEntry."Accepted Payment Tolerance";
CustLedgerEntry."Accepted Payment Tolerance" := 0;
end;
CustLedgerEntry.Modify();
end;
end;
AppliedPmtEntry."Account Type"::Vendor:
begin
VendorLedgerEntry.Get(AppliedPmtEntry."Applies-to Entry No.");
if VendorLedgerEntry."Accepted Payment Tolerance" <> 0 then begin
if VendorLedgerEntry."Accepted Payment Tolerance" > Difference then begin
VendorLedgerEntry."Accepted Payment Tolerance" -= Difference;
Difference := 0;
end else begin
Difference -= VendorLedgerEntry."Accepted Payment Tolerance";
VendorLedgerEntry."Accepted Payment Tolerance" := 0;
end;
VendorLedgerEntry.Modify();
end;
end;
end;
until (AppliedPmtEntry.Next() = 0) or (Difference = 0);
end;
local procedure InitializeBankPmtApplSettings()
begin
if BankPmtApplSettingsInitialized then
exit;
BankPmtApplSettings.GetOrInsert();
BankPmtApplSettingsInitialized := true;
end;
local procedure GetCandidateFilterStartDate(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"): Date
var
ReferenceDate: Date;
begin
InitializeBankPmtApplSettings();
// Buffers are built once per journal, so the batch's earliest transaction date is used to keep every line's candidates.
if CandidateFilterReferenceDate <> 0D then
ReferenceDate := CandidateFilterReferenceDate
else
ReferenceDate := BankAccReconciliationLine."Transaction Date";
exit(BankPmtApplSettings.GetCandidateLookbackStartDate(ReferenceDate));
end;
/// <summary>
/// Event raised after executing the main bank payment matching code.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnAfterCode(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before calculating the remaining amount for payment matching.
/// </summary>
/// <param name="TempLedgerEntryMatchingBuffer">The temporary ledger entry matching buffer record.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="UsePaymentDiscounts">Indicates whether to use payment discounts in calculation.</param>
/// <param name="RemainingAmount">The calculated remaining amount.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeCalcRemainingAmount(var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; UsePaymentDiscounts: Boolean; var RemainingAmount: Decimal; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before checking if customer bank account is matching.
/// </summary>
/// <param name="ValueFromBankStatement">The value from the bank statement.</param>
/// <param name="CustomerNo">The customer number.</param>
/// <param name="BankAccountNo">The bank account number.</param>
/// <param name="Result">The matching result.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeIsCustomerBankAccountMatching(ValueFromBankStatement: Text; CustomerNo: Code[20]; BankAccountNo: Code[20]; var Result: Boolean; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before notifying if entries are matched elsewhere.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(true, false)]
local procedure OnBeforeNotifyIfEntriesMatchedElsewhere(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before checking if vendor bank account is matching.
/// </summary>
/// <param name="ValueFromBankStatement">The value from the bank statement.</param>
/// <param name="VendorNo">The vendor number.</param>
/// <param name="BankAccountNo">The bank account number.</param>
/// <param name="Result">The matching result.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeIsVendorBankAccountMatching(ValueFromBankStatement: Text; VendorNo: Code[20]; BankAccountNo: Code[20]; var Result: Boolean; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised before matching bank ledger entry documents.
/// </summary>
/// <param name="SearchText">The text to search for in document matching.</param>
/// <param name="TempLedgerEntryMatchingBuffer">The temporary ledger entry matching buffer record.</param>
/// <param name="BankPmtApplRule">The bank payment application rule record.</param>
[IntegrationEvent(false, false)]
local procedure OnDocumentMatchingForBankLedgerEntryOnBeforeMatch(SearchText: Text; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var BankPmtApplRule: Record "Bank Pmt. Appl. Rule")
begin
end;
/// <summary>
/// Event raised before document matching when finding matching entries.
/// </summary>
/// <param name="BankPmtApplRule">The bank payment application rule record.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TempLedgerEntryMatchingBuffer">The temporary ledger entry matching buffer record.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
/// <param name="TempBankStatementMatchingBuffer">The temporary bank statement matching buffer record.</param>
/// <param name="AccountType">The account type for matching.</param>
/// <param name="TotalTimeDocumentNoMatching">The total time for document number matching.</param>
/// <param name="TotalTimeDocumentNoMatchingForBankLedgerEntry">The total time for bank ledger entry document matching.</param>
/// <param name="DocumentMatchedInfoText">The document matching information text.</param>
/// <param name="LogInfoText">Indicates whether to log information text.</param>
[IntegrationEvent(false, false)]
local procedure OnFindMatchingEntryOnBeforeDocumentMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var IsHandled: Boolean; TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer"; AccountType: Enum "Gen. Journal Account Type"; var TotalTimeDocumentNoMatching: Duration; var TotalTimeDocumentNoMatchingForBankLedgerEntry: Duration; var DocumentMatchedInfoText: Text; LogInfoText: Boolean)
begin
end;
/// <summary>
/// Event raised before finding the first matching entries in the matching process.
/// </summary>
/// <param name="TempBankAccReconciliationLine">The temporary bank account reconciliation line record.</param>
/// <param name="TempLedgerEntryMatchingBuffer">The temporary ledger entry matching buffer record.</param>
/// <param name="AccountType">The account type for matching.</param>
/// <param name="SkipOtherEntries">Indicates whether to skip other entries.</param>
/// <param name="ApplyEntries">Indicates whether to apply entries automatically.</param>
[IntegrationEvent(false, false)]
local procedure OnFindMatchingEntriesOnBeforeFindFirst(var TempBankAccReconciliationLine: Record "Bank Acc. Reconciliation Line" temporary; var TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; AccountType: Enum "Gen. Journal Account Type"; var SkipOtherEntries: Boolean; var ApplyEntries: Boolean)
begin
end;
/// <summary>
/// Event raised before calculating string nearness when finding text mappings.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TextToAccountMapping">The text-to-account mapping record.</param>
/// <param name="Nearness">The calculated nearness value.</param>
[IntegrationEvent(false, false)]
local procedure OnFindTextMappingsOnBeforeCalculateStringNearness(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TextToAccountMapping: Record "Text-to-Account Mapping"; var Nearness: Integer);
begin
end;
/// <summary>
/// Event raised when setting filters on bank account ledger entries for matching buffer initialization.
/// </summary>
/// <param name="BankAccountLedgerEntry">The bank account ledger entry record with filters being set.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnInitBankAccLedgerEntriesMatchingBufferSetFilter(var BankAccountLedgerEntry: Record "Bank Account Ledger Entry"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised when setting filters on customer ledger entries for matching buffer initialization.
/// </summary>
/// <param name="CustLedgerEntry">The customer ledger entry record with filters being set.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnInitCustomerLedgerEntriesMatchingBufferSetFilter(var CustLedgerEntry: Record "Cust. Ledger Entry"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before finding customer ledger entries when initializing the matching buffer.
/// </summary>
/// <param name="CustLedgerEntry">The customer ledger entry record before finding entries.</param>
[IntegrationEvent(false, false)]
local procedure OnInitCustomerLedgerEntriesMatchingBufferOnBeforeCustLedgerEntryFindSet(var CustLedgerEntry: Record "Cust. Ledger Entry")
begin
end;
/// <summary>
/// Event raised when setting filters on employee ledger entries for matching buffer initialization.
/// </summary>
/// <param name="EmployeeLedgerEntry">The employee ledger entry record with filters being set.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnInitEmployeeLedgerEntriesMatchingBufferSetFilter(var EmployeeLedgerEntry: Record "Employee Ledger Entry"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised when setting filters on vendor ledger entries for matching buffer initialization.
/// </summary>
/// <param name="VendorLedgerEntry">The vendor ledger entry record with filters being set.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnInitVendorLedgerEntriesMatchingBufferSetFilter(var VendorLedgerEntry: Record "Vendor Ledger Entry"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised after finding employee ledger entries when initializing the matching buffer.
/// </summary>
/// <param name="EmployeeLedgerEntry">The employee ledger entry record after finding entries.</param>
[IntegrationEvent(false, false)]
local procedure OnInitEmployeeLedgerEntriesMatchingBufferOnAfterEmployeeLedgerEntryFindSet(var EmployeeLedgerEntry: Record "Employee Ledger Entry")
begin
end;
/// <summary>
/// Event raised after finding vendor ledger entries when initializing the matching buffer.
/// </summary>
/// <param name="VendorLedgerEntry">The vendor ledger entry record after finding entries.</param>
[IntegrationEvent(false, false)]
local procedure OnInitVendorLedgerEntriesMatchingBufferOnAfterVendorLedgerEntryFindSet(var VendorLedgerEntry: Record "Vendor Ledger Entry")
begin
end;
/// <summary>
/// Event raised after setting the final text when showing match summary.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
/// <param name="FinalText">The final text to be displayed.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnShowMatchSummaryOnAfterSetFinalText(var BankAccReconciliation: Record "Bank Acc. Reconciliation"; FinalText: Text; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised after setting filters on payment matching details when deleting payment match details.
/// </summary>
/// <param name="PaymentMatchingDetails">The payment matching details record with filters set.</param>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
[IntegrationEvent(false, false)]
local procedure OnDeletePaymentMatchDetailsOnAfterPaymentMatchingDetailsSetFilters(var PaymentMatchingDetails: Record "Payment Matching Details"; BankAccReconciliation: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised after setting filters on applied payment entries when deleting applied payment entries.
/// </summary>
/// <param name="AppliedPaymentEntry">The applied payment entry record with filters set.</param>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
[IntegrationEvent(false, false)]
local procedure OnDeleteAppliedPaymentEntriesOnAfterAppliedPaymentEntrySetFilters(var AppliedPaymentEntry: Record "Applied Payment Entry"; BankAccReconciliation: Record "Bank Acc. Reconciliation")
begin
end;
/// <summary>
/// Event raised to control whether bank ledger entries from closing ledger entries should be disabled from matching.
/// </summary>
/// <param name="Disable">Indicates whether to disable bank ledger entries matching from closing ledger entries.</param>
[IntegrationEvent(false, false)]
local procedure OnDisableMatchBankLedgerEntriesFromClosingLedgerEntries(var Disable: boolean)
begin
end;
/// <summary>
/// Event raised to control whether string nearness matching should be disabled.
/// </summary>
/// <param name="Disable">Indicates whether to disable string nearness matching functionality.</param>
[IntegrationEvent(false, false)]
local procedure OnDisableStringNearnessMatch(var Disable: boolean)
begin
end;
/// <summary>
/// Event raised to control whether customer ledger entries matching should be disabled.
/// </summary>
/// <param name="Disable">Indicates whether to disable customer ledger entries matching functionality.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnDisableCustomerLedgerEntriesMatch(var Disable: boolean; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised to control whether vendor ledger entries matching should be disabled.
/// </summary>
/// <param name="Disable">Indicates whether to disable vendor ledger entries matching functionality.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnDisableVendorLedgerEntriesMatch(var Disable: boolean; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised to control whether employee ledger entries matching should be disabled.
/// </summary>
/// <param name="Disable">Indicates whether to disable employee ledger entries matching functionality.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnDisableEmployeeLedgerEntriesMatch(var Disable: boolean; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised to control whether bank ledger entries matching should be disabled.
/// </summary>
/// <param name="Disable">Indicates whether to disable bank ledger entries matching functionality.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnDisableBankLedgerEntriesMatch(var Disable: boolean; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised after calculating total time for document number matching when matching entries.
/// </summary>
/// <param name="BankPmtApplRule">The bank payment application rule record.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TempLedgerEntryMatchingBuffer">The temporary ledger entry matching buffer record.</param>
/// <param name="AccountType">The account type for matching.</param>
/// <param name="TempBankStatementMatchingBuffer">The temporary bank statement matching buffer record.</param>
/// <param name="TotalTimeRelatedPartyMatching">The total time for related party matching.</param>
/// <param name="TotalTimeAmountMatching">The total time for amount matching.</param>
/// <param name="RemainingAmount">The remaining amount after matching.</param>
/// <param name="RelatedPartyMatchedInfoText">The related party matching information text.</param>
/// <param name="LogInfoText">Indicates whether to log information text.</param>
/// <param name="TotalTimeStringNearness">The total time for string nearness matching.</param>
/// <param name="UsePaymentDiscounts">Indicates whether to use payment discounts.</param>
/// <param name="OneToManyTempBankStatementMatchingBuffer">The one-to-many temporary bank statement matching buffer record.</param>
/// <param name="TempCustomerLedgerEntryMatchingBuffer">The temporary customer ledger entry matching buffer record.</param>
/// <param name="TempVendorLedgerEntryMatchingBuffer">The temporary vendor ledger entry matching buffer record.</param>
/// <param name="TempEmployeeLedgerEntryMatchingBuffer">The temporary employee ledger entry matching buffer record.</param>
/// <param name="TempBankAccLedgerEntryMatchingBuffer">The temporary bank account ledger entry matching buffer record.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
/// <param name="DocumentMatchedInfoText">The document matching information text.</param>
[IntegrationEvent(false, false)]
local procedure OnMatchEntriesOnAfterCalcTotalTimeDocumentNoMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TempLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; AccountType: Enum "Gen. Journal Account Type"; TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; var TotalTimeRelatedPartyMatching: Duration; var TotalTimeAmountMatching: Duration; var RemainingAmount: Decimal; var RelatedPartyMatchedInfoText: Text; LogInfoText: Boolean; var TotalTimeStringNearness: Duration; UsePaymentDiscounts: Boolean; OneToManyTempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; var TempCustomerLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var TempVendorLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var TempEmployeeLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var TempBankAccLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var IsHandled: Boolean; var DocumentMatchedInfoText: Text)
begin
end;
/// <summary>
/// Event raised during related party information matching process.
/// </summary>
/// <param name="BankPmtApplRule">The bank payment application rule record.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="Name">The name value for matching.</param>
/// <param name="Address">The address value for matching.</param>
/// <param name="City">The city value for matching.</param>
/// <param name="AccountType">The account type for matching.</param>
/// <param name="RelatedPartyMatchedInfoText">The related party matching information text.</param>
/// <param name="Handled">Indicates whether the related party matching has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnRelatedPartyInfoMatching(var BankPmtApplRule: Record "Bank Pmt. Appl. Rule"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Name: Text[100]; Address: Text[100]; City: Text[30]; AccountType: Enum "Gen. Journal Account Type"; var RelatedPartyMatchedInfoText: Text; var Handled: Boolean)
begin
end;
/// <summary>
/// Event raised when finding bank account ledger entry for text-to-account mapping.
/// </summary>
/// <param name="Handled">Indicates whether the finding process has been handled.</param>
/// <param name="Found">Indicates whether a matching bank account ledger entry was found.</param>
/// <param name="BankAccLedgerEntry">The bank account ledger entry record found for mapping.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TextToAccountMapping">The text-to-account mapping record.</param>
/// <param name="BalAccountNo">The balance account number.</param>
[IntegrationEvent(false, false)]
local procedure OnFindBankAccLedgerEntryForTextToAccountMapping(var Handled: Boolean; var Found: Boolean; var BankAccLedgerEntry: Record "Bank Account Ledger Entry"; BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TextToAccountMapping: Record "Text-to-Account Mapping"; BalAccountNo: Code[20])
begin
end;
/// <summary>
/// Event raised after setting filters on the second bank account reconciliation line record when updating payment match details.
/// </summary>
/// <param name="BankAccReconciliationLine2">The second bank account reconciliation line record with filters set.</param>
/// <param name="BankAccReconciliationLine">The primary bank account reconciliation line record.</param>
[IntegrationEvent(false, false)]
local procedure OnUpdatePaymentMatchDetailsOnAfterBankAccReconciliationLine2SetFilters(var BankAccReconciliationLine2: Record "Bank Acc. Reconciliation Line"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line")
begin
end;
/// <summary>
/// Event raised before finding the temporary bank statement matching buffer when creating applied entries.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
/// <param name="TempBankStatementMatchingBuffer">The temporary bank statement matching buffer record.</param>
/// <param name="TempBankStmtMultipleMatchLine">The temporary bank statement multiple match line record.</param>
[IntegrationEvent(false, false)]
local procedure OnCreateAppliedEntriesOnBeforeTempBankStatementMatchingBufferFindset(BankAccReconciliation: Record "Bank Acc. Reconciliation"; var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; var TempBankStmtMultipleMatchLine: Record "Bank Stmt Multiple Match Line" temporary)
begin
end;
/// <summary>
/// Event raised before checking if entries can match based on specified criteria.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="Amount">The amount to match.</param>
/// <param name="EntryPostingDate">The entry posting date.</param>
/// <param name="ApplyEntries">Indicates whether to apply entries automatically.</param>
/// <param name="Result">The result of the matching check.</param>
/// <param name="IsHandled">Indicates whether the event has been handled.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeCanEntriesMatch(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Amount: Decimal; EntryPostingDate: Date; ApplyEntries: Boolean; var Result: Boolean; var IsHandled: Boolean)
begin
end;
/// <summary>
/// Event raised after updating payment match details when applying ledger entries to statement lines.
/// </summary>
/// <param name="BankAccReconciliation">The bank account reconciliation record.</param>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="Overwrite">Indicates whether to overwrite existing match details.</param>
[IntegrationEvent(false, false)]
local procedure OnApplyLedgerEntriesToStatementLinesOnAfterUpdatePaymentMatchDetails(BankAccReconciliation: Record "Bank Acc. Reconciliation"; var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; Overwrite: Boolean)
begin
end;
/// <summary>
/// Event raised after calculating total time for text mappings per line when mapping ledger entries to statement lines.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TempBankStatementMatchingBuffer">The temporary bank statement matching buffer record.</param>
/// <param name="TotalTimeMatchingCustomerLedgerEntriesPerLine">The total time for matching customer ledger entries per line.</param>
/// <param name="TotalTimeMatchingVendorLedgerEntriesPerLine">The total time for matching vendor ledger entries per line.</param>
/// <param name="TotalTimeMatchingEmployeeLedgerEntriesPerLine">The total time for matching employee ledger entries per line.</param>
/// <param name="TotalTimeMatchingBankLedgerEntriesPerLine">The total time for matching bank ledger entries per line.</param>
/// <param name="RelatedPartyMatchedInfoText">The related party matching information text.</param>
/// <param name="LogInfoText">Indicates whether to log information text.</param>
/// <param name="TotalTimeStringNearness">The total time for string nearness matching.</param>
/// <param name="UsePaymentDiscounts">Indicates whether to use payment discounts.</param>
/// <param name="OneToManyTempBankStatementMatchingBuffer">The one-to-many temporary bank statement matching buffer record.</param>
/// <param name="TempCustomerLedgerEntryMatchingBuffer">The temporary customer ledger entry matching buffer record.</param>
/// <param name="TempVendorLedgerEntryMatchingBuffer">The temporary vendor ledger entry matching buffer record.</param>
/// <param name="TempEmployeeLedgerEntryMatchingBuffer">The temporary employee ledger entry matching buffer record.</param>
/// <param name="TempBankAccLedgerEntryMatchingBuffer">The temporary bank account ledger entry matching buffer record.</param>
[IntegrationEvent(false, false)]
local procedure OnMapLedgerEntriesToStatementLinesOnAfterCalcTotalTimeTimeTextMappingsPerLine(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; var TotalTimeMatchingCustomerLedgerEntriesPerLine: Duration; var TotalTimeMatchingVendorLedgerEntriesPerLine: Duration; var TotalTimeMatchingEmployeeLedgerEntriesPerLine: Duration; var TotalTimeMatchingBankLedgerEntriesPerLine: Duration; var RelatedPartyMatchedInfoText: Text; LogInfoText: Boolean; var TotalTimeStringNearness: Duration; UsePaymentDiscounts: Boolean; OneToManyTempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary; var TempCustomerLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var TempVendorLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var TempEmployeeLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary; var TempBankAccLedgerEntryMatchingBuffer: Record "Ledger Entry Matching Buffer" temporary)
begin
end;
/// <summary>
/// Event raised before inserting a bank account reconciliation line when transferring differences to an account.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record to be inserted.</param>
/// <param name="TempGenJournalLine">The temporary general journal line record.</param>
[IntegrationEvent(false, false)]
local procedure OnTransferDiffToAccountOnBeforeInsertBankAccReconciliationLine(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; TempGenJournalLine: Record "Gen. Journal Line" temporary)
begin
end;
/// <summary>
/// Event raised after sending trace tag when applying records.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TempBankStatementMatchingBuffer">The temporary bank statement matching buffer record.</param>
[IntegrationEvent(false, false)]
local procedure OnApplyRecordsOnAfterSendTraceTag(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary);
begin
end;
/// <summary>
/// Event raised after text mapper has matched when finding text mappings.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TextToAccountMapping">The text-to-account mapping record that was matched.</param>
/// <param name="TempBankStatementMatchingBuffer">The temporary bank statement matching buffer record.</param>
[IntegrationEvent(false, false)]
local procedure OnFindTextMappingsOnAfterTextMapperMatched(var BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TextToAccountMapping: Record "Text-to-Account Mapping"; var TempBankStatementMatchingBuffer: Record "Bank Statement Matching Buffer" temporary);
begin
end;
/// <summary>
/// Event raised before editing a customer ledger entry when setting customer application data.
/// </summary>
/// <param name="CustLedgerEntry">The customer ledger entry record to be edited.</param>
[IntegrationEvent(false, false)]
local procedure OnSetCustAppicationDataOnBeforeCustEntryEdit(var CustLedgerEntry: Record "Cust. Ledger Entry")
begin
end;
/// <summary>
/// Event raised when transferring difference amounts to accounts during bank reconciliation.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="TempGenJnlLine">The temporary general journal line record for transferring differences.</param>
/// <param name="DifferenceTransferred">Indicates whether the difference has been transferred.</param>
[IntegrationEvent(false, false)]
local procedure OnTransferDifference(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var TempGenJnlLine: Record "Gen. Journal Line" temporary; var DifferenceTransferred: Boolean)
begin
end;
/// <summary>
/// Event raised before editing a vendor ledger entry when setting vendor application data.
/// </summary>
/// <param name="VendorLedgerEntry">The vendor ledger entry record to be edited.</param>
[IntegrationEvent(false, false)]
local procedure OnSetVendAppicationDataOnBeforeVendEntryEdit(var VendorLedgerEntry: Record "Vendor Ledger Entry")
begin
end;
/// <summary>
/// Event raised before showing the line split message during bank reconciliation.
/// </summary>
/// <param name="BankAccReconciliationLine">The bank account reconciliation line record.</param>
/// <param name="SuppressMessage">Indicates whether to suppress the line split message.</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeShowLineSplitMessage(BankAccReconciliationLine: Record "Bank Acc. Reconciliation Line"; var SuppressMessage: Boolean)
begin
end;
}
#pragma warning restore AA0198