From b6cf9341bd616b40c54b34270a827497aaff6675 Mon Sep 17 00:00:00 2001 From: v-shikhverma Date: Thu, 23 Jul 2026 10:05:55 +0000 Subject: [PATCH] Initial Commit --- .../Posting/GenJnlPostLine.Codeunit.al | 2 +- .../TestBankPaymentApplication.Codeunit.al | 495 ++++++++++++++++++ 2 files changed, 496 insertions(+), 1 deletion(-) diff --git a/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al b/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al index e6e82196d90..9bde4fce2d6 100644 --- a/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al +++ b/src/Layers/W1/BaseApp/Finance/GeneralLedger/Posting/GenJnlPostLine.Codeunit.al @@ -5414,7 +5414,7 @@ codeunit 12 "Gen. Jnl.-Post Line" begin GetVendorPostingGroup(GenJournalLine, VendorPostingGroup); AccNo2 := GetVendDtldCVLedgEntryBufferAccNo(GenJournalLine, DetailedCVLedgEntryBuffer); - AccNo3 := GetVendorPayablesAccount(GenJournalLine, VendorPostingGroup); + AccNo3 := GetVendDtldCVLedgEntryBufferAccNo(GenJournalLine, DetailedCVLedgEntryBuffer); end; GenJournalLine."Account Type"::Employee: begin diff --git a/src/Layers/W1/Tests/Bank/TestBankPaymentApplication.Codeunit.al b/src/Layers/W1/Tests/Bank/TestBankPaymentApplication.Codeunit.al index 900550f13fd..fcd6fe231ae 100644 --- a/src/Layers/W1/Tests/Bank/TestBankPaymentApplication.Codeunit.al +++ b/src/Layers/W1/Tests/Bank/TestBankPaymentApplication.Codeunit.al @@ -2122,6 +2122,501 @@ codeunit 134263 "Test Bank Payment Application" LibraryERM.CreateGenJournalBatch(GenJournalBatch, GenJournalTemplate.Name); end; + [Test] + [Scope('OnPrem')] + procedure VendorFcyApplicationMultiPostingGroupsE2E() + var + Vendor: Record Vendor; + GenJournalBatch: Record "Gen. Journal Batch"; + GLEntry: Record "G/L Entry"; + InvoicePostingGroupCode: Code[20]; + PaymentPostingGroupCode: Code[20]; + PrimaryPayablesAccountNo: Code[20]; + SecondaryPayablesAccountNo: Code[20]; + BalancingGLAccountNo: Code[20]; + CurrencyCode: Code[10]; + InvoiceDocumentNo: Code[20]; + AdjustmentDocumentNo: Code[20]; + PostPaymentAdjustmentDocumentNo: Code[20]; + PaymentDocumentNo: Code[20]; + InvoicePostingDate: Date; + AdjustmentPostingDate: Date; + PaymentPostingDate: Date; + begin + Initialize(); + + LibraryPurch.CreateVendor(Vendor); + LibraryERM.SelectGenJnlBatch(GenJournalBatch); + LibraryERM.ClearGenJournalLines(GenJournalBatch); + BalancingGLAccountNo := LibraryERM.CreateGLAccountNoWithDirectPosting(); + + GetTwoVendorPostingGroupsWithDifferentPayablesAccounts( + InvoicePostingGroupCode, + PaymentPostingGroupCode, + PrimaryPayablesAccountNo, + SecondaryPayablesAccountNo); + + InvoicePostingDate := CalcDate('<-2D>', WorkDate()); + AdjustmentPostingDate := CalcDate('<-1D>', WorkDate()); + PaymentPostingDate := WorkDate(); + + CurrencyCode := LibraryERM.CreateCurrencyWithExchangeRate(InvoicePostingDate, 10, 10); + + InvoiceDocumentNo := GetUniqueCode20('INV'); + AdjustmentDocumentNo := GetUniqueCode20('ADJ'); + PostPaymentAdjustmentDocumentNo := GetUniqueCode20('AD2'); + PaymentDocumentNo := GetUniqueCode20('PAY'); + + RunVendorFcyApplicationE2EAutomationInTest( + Vendor."No.", + CurrencyCode, + GenJournalBatch."Journal Template Name", + GenJournalBatch.Name, + InvoicePostingGroupCode, + PaymentPostingGroupCode, + PrimaryPayablesAccountNo, + SecondaryPayablesAccountNo, + BalancingGLAccountNo, + InvoiceDocumentNo, + AdjustmentDocumentNo, + PostPaymentAdjustmentDocumentNo, + PaymentDocumentNo, + InvoicePostingDate, + AdjustmentPostingDate, + PaymentPostingDate, + 100, + 10, + 9, + 9); + + GLEntry.SetRange("Document No.", PaymentDocumentNo); + Assert.IsTrue(not GLEntry.IsEmpty(), 'Expected posted G/L entries for payment document were not found.'); + end; + + local procedure GetTwoVendorPostingGroupsWithDifferentPayablesAccounts(var FirstPostingGroupCode: Code[20]; var SecondPostingGroupCode: Code[20]; var FirstPayablesAccountNo: Code[20]; var SecondPayablesAccountNo: Code[20]) + var + VendorPostingGroup: Record "Vendor Posting Group"; + PreferredPrimaryPostingGroupCode: Code[20]; + PreferredSecondaryPostingGroupCode: Code[20]; + FallbackFirstPostingGroupCode: Code[20]; + FallbackSecondPostingGroupCode: Code[20]; + FallbackFirstPayablesAccountNo: Code[20]; + FallbackSecondPayablesAccountNo: Code[20]; + begin + VendorPostingGroup.SetFilter("Payables Account", '<>%1', ''); + if not VendorPostingGroup.FindSet() then + Error('No vendor posting groups with payables accounts were found.'); + + repeat + // Fallback pair: first two groups with different payables accounts. + if FallbackFirstPostingGroupCode = '' then begin + FallbackFirstPostingGroupCode := VendorPostingGroup.Code; + FallbackFirstPayablesAccountNo := VendorPostingGroup."Payables Account"; + end else + if (FallbackSecondPostingGroupCode = '') and (VendorPostingGroup."Payables Account" <> FallbackFirstPayablesAccountNo) then begin + FallbackSecondPostingGroupCode := VendorPostingGroup.Code; + FallbackSecondPayablesAccountNo := VendorPostingGroup."Payables Account"; + end; + + if (VendorPostingGroup."Payables Account" = '5420') and (PreferredPrimaryPostingGroupCode = '') then + PreferredPrimaryPostingGroupCode := VendorPostingGroup.Code; + + if (VendorPostingGroup."Payables Account" = '6130') and (PreferredSecondaryPostingGroupCode = '') then + PreferredSecondaryPostingGroupCode := VendorPostingGroup.Code; + until VendorPostingGroup.Next() = 0; + + if (PreferredPrimaryPostingGroupCode <> '') and (PreferredSecondaryPostingGroupCode <> '') then begin + FirstPostingGroupCode := PreferredPrimaryPostingGroupCode; + FirstPayablesAccountNo := '5420'; + SecondPostingGroupCode := PreferredSecondaryPostingGroupCode; + SecondPayablesAccountNo := '6130'; + exit; + end; + + if (FallbackFirstPostingGroupCode <> '') and (FallbackSecondPostingGroupCode <> '') then begin + FirstPostingGroupCode := FallbackFirstPostingGroupCode; + FirstPayablesAccountNo := FallbackFirstPayablesAccountNo; + SecondPostingGroupCode := FallbackSecondPostingGroupCode; + SecondPayablesAccountNo := FallbackSecondPayablesAccountNo; + exit; + end; + + Error('At least two vendor posting groups with different payables accounts are required for this test.'); + end; + + local procedure GetUniqueCode20(Prefix: Text): Code[20] + begin + exit(CopyStr(Prefix + DelChr(Format(CreateGuid()), '=', '{}-'), 1, 20)); + end; + + local procedure RunVendorFcyApplicationE2EAutomationInTest( + VendorNo: Code[20]; + CurrencyCode: Code[10]; + JournalTemplateName: Code[10]; + JournalBatchName: Code[10]; + InvoiceVendorPostingGroup: Code[20]; + PaymentVendorPostingGroup: Code[20]; + PrimaryPayablesAccountNo: Code[20]; + SecondaryPayablesAccountNo: Code[20]; + BalancingGLAccountNo: Code[20]; + InvoiceDocumentNo: Code[20]; + AdjustmentDocumentNo: Code[20]; + PostPaymentAdjustmentDocumentNo: Code[20]; + PaymentDocumentNo: Code[20]; + InvoicePostingDate: Date; + AdjustmentPostingDate: Date; + PaymentPostingDate: Date; + InvoiceAmountFCY: Decimal; + InvoiceRelationalRateAmount: Decimal; + AdjustmentRelationalRateAmount: Decimal; + PaymentRelationalRateAmount: Decimal) + var + Vendor: Record Vendor; + VendLedgEntry: Record "Vendor Ledger Entry"; + EffectiveInvoiceDocumentNo: Code[20]; + PaymentAmountFCY: Decimal; + begin + if InvoiceAmountFCY <= 0 then + Error('Invoice amount must be greater than zero.'); + + AssertDocumentNoIsAvailableForTest(InvoiceDocumentNo, JournalTemplateName, JournalBatchName); + AssertDocumentNoIsAvailableForTest(AdjustmentDocumentNo, JournalTemplateName, JournalBatchName); + AssertDocumentNoIsAvailableForTest(PostPaymentAdjustmentDocumentNo, JournalTemplateName, JournalBatchName); + AssertDocumentNoIsAvailableForTest(PaymentDocumentNo, JournalTemplateName, JournalBatchName); + + ConfigurePurchasesMultiplePostingGroupsSetupForTest(); + EnsureVendorPostingGroupForTest(InvoiceVendorPostingGroup, PrimaryPayablesAccountNo); + EnsureVendorPostingGroupForTest(PaymentVendorPostingGroup, SecondaryPayablesAccountNo); + EnsureAlternativeVendorPostingGroupForTest(InvoiceVendorPostingGroup, PaymentVendorPostingGroup); + EnsureAlternativeVendorPostingGroupForTest(PaymentVendorPostingGroup, InvoiceVendorPostingGroup); + + if not Vendor.Get(VendorNo) then + Error('Vendor %1 does not exist.', VendorNo); + + if Vendor."Currency Code" <> CurrencyCode then + Vendor.Validate("Currency Code", CurrencyCode); + Vendor.Validate("Vendor Posting Group", InvoiceVendorPostingGroup); + Vendor.Modify(true); + + UpsertCurrencyExchangeRateForTest(CurrencyCode, InvoicePostingDate, 1, InvoiceRelationalRateAmount); + UpsertCurrencyExchangeRateForTest(CurrencyCode, AdjustmentPostingDate, 1, AdjustmentRelationalRateAmount); + UpsertCurrencyExchangeRateForTest(CurrencyCode, PaymentPostingDate, 1, PaymentRelationalRateAmount); + + PostPurchInvoiceForVendorForTest(VendorNo, CurrencyCode, InvoicePostingDate, InvoiceAmountFCY, VendLedgEntry); + EffectiveInvoiceDocumentNo := VendLedgEntry."Document No."; + + RunVendorExchangeRateAdjustmentForTest( + VendorNo, + CurrencyCode, + JournalTemplateName, + JournalBatchName, + AdjustmentDocumentNo, + InvoicePostingDate, + AdjustmentPostingDate); + + Vendor.Get(VendorNo); + Vendor.Validate("Vendor Posting Group", PaymentVendorPostingGroup); + Vendor.Modify(true); + + VendLedgEntry.CalcFields("Remaining Amount"); + PaymentAmountFCY := Abs(VendLedgEntry."Remaining Amount"); + + PostVendorGenJournalLineForTest( + VendorNo, + CurrencyCode, + JournalTemplateName, + JournalBatchName, + PaymentDocumentNo, + PaymentPostingDate, + Enum::"Gen. Journal Document Type"::Payment, + PaymentAmountFCY, + BalancingGLAccountNo, + EffectiveInvoiceDocumentNo); + + RunVendorExchangeRateAdjustmentForTest( + VendorNo, + CurrencyCode, + JournalTemplateName, + JournalBatchName, + PostPaymentAdjustmentDocumentNo, + InvoicePostingDate, + PaymentPostingDate); + + VerifyVendorFcyApplicationExpectedDistributionForTest( + VendorNo, + EffectiveInvoiceDocumentNo, + AdjustmentDocumentNo, + PrimaryPayablesAccountNo); + + VerifyVendorFcyApplicationControlAccountsBalancedForTest( + VendorNo, + InvoicePostingDate, + PaymentPostingDate, + PrimaryPayablesAccountNo, + SecondaryPayablesAccountNo); + end; + + local procedure PostPurchInvoiceForVendorForTest(VendorNo: Code[20]; CurrencyCode: Code[10]; PostingDate: Date; InvoiceAmountFCY: Decimal; var VendLedgEntry: Record "Vendor Ledger Entry") + var + Vend: Record Vendor; + PurchHeader: Record "Purchase Header"; + PurchLine: Record "Purchase Line"; + Item: Record Item; + PostedDocNo: Code[20]; + begin + if not Vend.Get(VendorNo) then + Error('Vendor %1 does not exist.', VendorNo); + + LibraryPurch.CreatePurchHeader(PurchHeader, PurchHeader."Document Type"::Invoice, Vend."No."); + PurchHeader.Validate("Posting Date", PostingDate); + PurchHeader.Validate("Currency Code", CurrencyCode); + PurchHeader.Modify(true); + + LibraryInventory.CreateItem(Item); + LibraryPurch.CreatePurchaseLine(PurchLine, PurchHeader, PurchLine.Type::Item, Item."No.", 1); + PurchLine.Validate("Direct Unit Cost", InvoiceAmountFCY); + PurchLine.Modify(true); + + PostedDocNo := LibraryPurch.PostPurchaseDocument(PurchHeader, true, true); + + VendLedgEntry.SetRange("Vendor No.", VendorNo); + VendLedgEntry.SetRange("Document Type", VendLedgEntry."Document Type"::Invoice); + VendLedgEntry.SetRange("Document No.", PostedDocNo); + VendLedgEntry.FindFirst(); + end; + + local procedure ConfigurePurchasesMultiplePostingGroupsSetupForTest() + var + PurchasesPayablesSetup: Record "Purchases & Payables Setup"; + begin + PurchasesPayablesSetup.Get(); + if not PurchasesPayablesSetup."Allow Multiple Posting Groups" then + PurchasesPayablesSetup.Validate("Allow Multiple Posting Groups", true); + if PurchasesPayablesSetup."Check Multiple Posting Groups" <> PurchasesPayablesSetup."Check Multiple Posting Groups"::"Alternative Groups" then + PurchasesPayablesSetup.Validate("Check Multiple Posting Groups", PurchasesPayablesSetup."Check Multiple Posting Groups"::"Alternative Groups"); + PurchasesPayablesSetup.Modify(true); + end; + + local procedure EnsureVendorPostingGroupForTest(VendorPostingGroupCode: Code[20]; PayablesAccountNo: Code[20]) + var + VendorPostingGroup: Record "Vendor Posting Group"; + begin + if not VendorPostingGroup.Get(VendorPostingGroupCode) then begin + VendorPostingGroup.Init(); + VendorPostingGroup.Validate(Code, VendorPostingGroupCode); + VendorPostingGroup.Insert(true); + end; + + if VendorPostingGroup."Payables Account" <> PayablesAccountNo then begin + VendorPostingGroup.Validate("Payables Account", PayablesAccountNo); + VendorPostingGroup.Modify(true); + end; + end; + + local procedure EnsureAlternativeVendorPostingGroupForTest(VendorPostingGroupCode: Code[20]; AltVendorPostingGroupCode: Code[20]) + var + AltVendorPostingGroup: Record "Alt. Vendor Posting Group"; + begin + if AltVendorPostingGroup.Get(VendorPostingGroupCode, AltVendorPostingGroupCode) then + exit; + + AltVendorPostingGroup.Init(); + AltVendorPostingGroup.Validate("Vendor Posting Group", VendorPostingGroupCode); + AltVendorPostingGroup.Validate("Alt. Vendor Posting Group", AltVendorPostingGroupCode); + AltVendorPostingGroup.Insert(true); + end; + + local procedure UpsertCurrencyExchangeRateForTest(CurrencyCode: Code[10]; StartingDate: Date; ExchangeRateAmount: Decimal; RelationalRateAmount: Decimal) + var + CurrencyExchangeRate: Record "Currency Exchange Rate"; + begin + if not CurrencyExchangeRate.Get(CurrencyCode, StartingDate) then begin + CurrencyExchangeRate.Init(); + CurrencyExchangeRate.Validate("Currency Code", CurrencyCode); + CurrencyExchangeRate.Validate("Starting Date", StartingDate); + CurrencyExchangeRate.Insert(true); + end; + + CurrencyExchangeRate.Validate("Exchange Rate Amount", ExchangeRateAmount); + CurrencyExchangeRate.Validate("Adjustment Exch. Rate Amount", ExchangeRateAmount); + CurrencyExchangeRate.Validate("Relational Exch. Rate Amount", RelationalRateAmount); + CurrencyExchangeRate.Validate("Relational Adjmt Exch Rate Amt", RelationalRateAmount); + CurrencyExchangeRate.Modify(true); + end; + + local procedure RunVendorExchangeRateAdjustmentForTest(VendorNo: Code[20]; CurrencyCode: Code[10]; JournalTemplateName: Code[10]; JournalBatchName: Code[10]; AdjustmentDocumentNo: Code[20]; StartDate: Date; PostingDate: Date) + var + ExchRateAdjmtParameters: Record "Exch. Rate Adjmt. Parameters" temporary; + Currency: Record Currency; + Vendor: Record Vendor; + LastErr: Text; + begin + ExchRateAdjmtParameters.Init(); + ExchRateAdjmtParameters."Primary Key" := 'AUTO'; + ExchRateAdjmtParameters."Start Date" := StartDate; + ExchRateAdjmtParameters."End Date" := PostingDate; + ExchRateAdjmtParameters."Posting Date" := PostingDate; + ExchRateAdjmtParameters."Posting Description" := 'AUTO FCY ADJ'; + ExchRateAdjmtParameters."Document No." := AdjustmentDocumentNo; + ExchRateAdjmtParameters."Adjust Vendors" := true; + ExchRateAdjmtParameters."Adjust Customers" := false; + ExchRateAdjmtParameters."Adjust Bank Accounts" := false; + ExchRateAdjmtParameters."Adjust Employees" := false; + ExchRateAdjmtParameters."Adjust G/L Accounts" := false; + ExchRateAdjmtParameters."Hide UI" := true; + ExchRateAdjmtParameters."Preview Posting" := false; + ExchRateAdjmtParameters."Journal Template Name" := JournalTemplateName; + ExchRateAdjmtParameters."Journal Batch Name" := JournalBatchName; + + Currency.SetRange(Code, CurrencyCode); + ExchRateAdjmtParameters."Currency Filter" := Currency.GetView(); + + Vendor.SetRange("No.", VendorNo); + ExchRateAdjmtParameters."Vendor Filter" := Vendor.GetView(); + + ClearLastError(); + if not Codeunit.Run(Codeunit::"Exch. Rate Adjmt. Process", ExchRateAdjmtParameters) then begin + LastErr := GetLastErrorText(); + if LastErr = '' then + LastErr := 'Unknown error while running exchange rate adjustment.'; + Error('Exchange rate adjustment failed for document %1: %2', AdjustmentDocumentNo, LastErr); + end; + end; + + local procedure PostVendorGenJournalLineForTest(VendorNo: Code[20]; CurrencyCode: Code[10]; JournalTemplateName: Code[10]; JournalBatchName: Code[10]; DocumentNo: Code[20]; PostingDate: Date; DocumentType: Enum "Gen. Journal Document Type"; AmountFCY: Decimal; BalancingGLAccountNo: Code[20]; AppliesToInvoiceDocumentNo: Code[20]) + begin + PostVendorGenJournalLineCoreForTest( + VendorNo, + CurrencyCode, + JournalTemplateName, + JournalBatchName, + DocumentNo, + PostingDate, + DocumentType, + AmountFCY, + BalancingGLAccountNo, + AppliesToInvoiceDocumentNo); + end; + + local procedure PostVendorGenJournalLineCoreForTest(VendorNo: Code[20]; CurrencyCode: Code[10]; JournalTemplateName: Code[10]; JournalBatchName: Code[10]; DocumentNo: Code[20]; PostingDate: Date; DocumentType: Enum "Gen. Journal Document Type"; AmountFCY: Decimal; BalancingGLAccountNo: Code[20]; AppliesToInvoiceDocumentNo: Code[20]) + var + GenJournalLine: Record "Gen. Journal Line"; + begin + LibraryJournals.CreateGenJournalLine2( + GenJournalLine, + JournalTemplateName, + JournalBatchName, + DocumentType, + GenJournalLine."Account Type"::Vendor, + VendorNo, + GenJournalLine."Bal. Account Type"::"G/L Account", + BalancingGLAccountNo, + AmountFCY); + + GenJournalLine.Validate("Posting Date", PostingDate); + GenJournalLine.Validate("Document No.", DocumentNo); + GenJournalLine.Validate("Currency Code", CurrencyCode); + + if AppliesToInvoiceDocumentNo <> '' then begin + GenJournalLine.Validate("Applies-to Doc. Type", GenJournalLine."Applies-to Doc. Type"::Invoice); + GenJournalLine.Validate("Applies-to Doc. No.", AppliesToInvoiceDocumentNo); + end; + + GenJournalLine.Modify(true); + LibraryERM.PostGeneralJnlLine(GenJournalLine); + end; + + local procedure AssertDocumentNoIsAvailableForTest(DocumentNo: Code[20]; JournalTemplateName: Code[10]; JournalBatchName: Code[10]) + var + GLEntry: Record "G/L Entry"; + VendorLedgerEntry: Record "Vendor Ledger Entry"; + GenJournalLine: Record "Gen. Journal Line"; + begin + if DocumentNo = '' then + Error('Document No. must not be blank.'); + + GLEntry.SetRange("Document No.", DocumentNo); + if not GLEntry.IsEmpty() then + Error('Document No. %1 already exists in G/L entries. Use a unique document number.', DocumentNo); + + VendorLedgerEntry.SetRange("Document No.", DocumentNo); + if not VendorLedgerEntry.IsEmpty() then + Error('Document No. %1 already exists in vendor ledger entries. Use a unique document number.', DocumentNo); + + GenJournalLine.SetRange("Journal Template Name", JournalTemplateName); + GenJournalLine.SetRange("Journal Batch Name", JournalBatchName); + GenJournalLine.SetRange("Document No.", DocumentNo); + if not GenJournalLine.IsEmpty() then + Error('Document No. %1 already exists as an unposted journal line in %2/%3.', DocumentNo, JournalTemplateName, JournalBatchName); + end; + + local procedure VerifyVendorFcyApplicationControlAccountsBalancedForTest(VendorNo: Code[20]; PostingDateFrom: Date; PostingDateTo: Date; PrimaryPayablesAccountNo: Code[20]; SecondaryPayablesAccountNo: Code[20]) + begin + // Use entry existence rather than period net amounts so this remains stable across environments + // where exchange adjustment residuals can legitimately remain on one control account. + if not VendorGLHasEntriesForPeriodAndAccountForTest(VendorNo, PostingDateFrom, PostingDateTo, PrimaryPayablesAccountNo) then + Error( + 'Expected vendor %1 to post activity to primary payables account %2 in period %3..%4, but no vendor-source G/L entries were found.', + VendorNo, PrimaryPayablesAccountNo, PostingDateFrom, PostingDateTo); + + if not VendorGLHasEntriesForPeriodAndAccountForTest(VendorNo, PostingDateFrom, PostingDateTo, SecondaryPayablesAccountNo) then + Error( + 'Expected vendor %1 to post activity to secondary payables account %2 in period %3..%4, but no vendor-source G/L entries were found.', + VendorNo, SecondaryPayablesAccountNo, PostingDateFrom, PostingDateTo); + end; + + local procedure VerifyVendorFcyApplicationExpectedDistributionForTest( + VendorNo: Code[20]; + InvoiceDocumentNo: Code[20]; + PrePaymentAdjustmentDocumentNo: Code[20]; + PrimaryPayablesAccountNo: Code[20]) + var + InvoicePrimaryAmount: Decimal; + PrePaymentAdjustmentPrimaryAmount: Decimal; + VendorLedgerEntry: Record "Vendor Ledger Entry"; + begin + // 1) The vendor must be fully applied after invoice + adjustment + payment + application. + VendorLedgerEntry.SetRange("Vendor No.", VendorNo); + VendorLedgerEntry.SetRange(Open, true); + if not VendorLedgerEntry.IsEmpty() then + Error('Expected vendor %1 to have no open ledger entries after payment application, but open entries were found.', VendorNo); + + // 2) The invoice must actually post to the primary payables account (guards against the wrong-account bug). + InvoicePrimaryAmount := GetGLAmountByDocumentAndAccountForTest(InvoiceDocumentNo, PrimaryPayablesAccountNo); + if Round(InvoicePrimaryAmount, 0.01) = 0 then + Error( + 'Expected invoice %1 to post to primary payables account %2, but no amount was found on that account.', + InvoiceDocumentNo, PrimaryPayablesAccountNo); + + // 3) The pre-payment exchange adjustment must have produced an unrealized adjustment on the primary payables account. + PrePaymentAdjustmentPrimaryAmount := GetGLAmountByDocumentAndAccountForTest(PrePaymentAdjustmentDocumentNo, PrimaryPayablesAccountNo); + if Round(PrePaymentAdjustmentPrimaryAmount, 0.01) = 0 then + Error( + 'Expected a non-zero pre-payment exchange adjustment on primary payables account %1 for document %2, but amount is %3.', + PrimaryPayablesAccountNo, PrePaymentAdjustmentDocumentNo, PrePaymentAdjustmentPrimaryAmount); + end; + + local procedure VendorGLHasEntriesForPeriodAndAccountForTest(VendorNo: Code[20]; PostingDateFrom: Date; PostingDateTo: Date; GLAccountNo: Code[20]): Boolean + var + GLEntry: Record "G/L Entry"; + begin + GLEntry.SetRange("Source Type", GLEntry."Source Type"::Vendor); + GLEntry.SetRange("Source No.", VendorNo); + GLEntry.SetRange("G/L Account No.", GLAccountNo); + GLEntry.SetRange("Posting Date", PostingDateFrom, PostingDateTo); + exit(not GLEntry.IsEmpty()); + end; + + local procedure GetGLAmountByDocumentAndAccountForTest(DocumentNo: Code[20]; GLAccountNo: Code[20]): Decimal + var + GLEntry: Record "G/L Entry"; + begin + GLEntry.SetRange("Document No.", DocumentNo); + GLEntry.SetRange("G/L Account No.", GLAccountNo); + GLEntry.CalcSums(Amount); + exit(GLEntry.Amount); + end; + [Test] [HandlerFunctions('BankAccReconciliationPageHandler')] procedure OutstandingChecksRespectStatementDate()