Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5631,7 +5631,9 @@ codeunit 12 "Gen. Jnl.-Post Line"
if DetailedCVLedgEntryBuffer."Initial Document Type" = DetailedCVLedgEntryBuffer."Initial Document Type"::Bill then
AccNo := VendPostingGr."Payables Account"
else
if (GenJournalLine."Document Type" = GenJournalLine."Document Type"::Payment) and (GenJournalLine."Applies-to Doc. No." <> '') and (DetailedCVLedgEntryBuffer."Document Type" = DetailedCVLedgEntryBuffer."Document Type"::Payment) then
if (GenJournalLine."Document Type" = GenJournalLine."Document Type"::Payment) and (GenJournalLine."Applies-to Doc. No." <> '') and (DetailedCVLedgEntryBuffer."Document Type" = DetailedCVLedgEntryBuffer."Document Type"::Payment) and
(not IsApplnEntryForAppliedVendorDoc(GenJournalLine, DetailedCVLedgEntryBuffer))
then
AccNo := VendPostingGr."Payables Account"
else
AccNo := GetVendDtldCVLedgEntryBufferAccNo(GenJournalLine, DetailedCVLedgEntryBuffer)
Expand Down Expand Up @@ -5661,7 +5663,9 @@ codeunit 12 "Gen. Jnl.-Post Line"
if DetailedCVLedgEntryBuffer."Initial Document Type" = DetailedCVLedgEntryBuffer."Initial Document Type"::Bill then
AccNo := VendPostingGr."Payables Account"
else
if (GenJournalLine."Document Type" = GenJournalLine."Document Type"::Payment) and (GenJournalLine."Applies-to Doc. No." <> '') and (DetailedCVLedgEntryBuffer."Document Type" = DetailedCVLedgEntryBuffer."Document Type"::Payment) then
if (GenJournalLine."Document Type" = GenJournalLine."Document Type"::Payment) and (GenJournalLine."Applies-to Doc. No." <> '') and (DetailedCVLedgEntryBuffer."Document Type" = DetailedCVLedgEntryBuffer."Document Type"::Payment) and
(not IsApplnEntryForAppliedVendorDoc(GenJournalLine, DetailedCVLedgEntryBuffer))
then
AccNo := VendPostingGr."Payables Account"
else
AccNo := GetVendDtldCVLedgEntryBufferAccNo(GenJournalLine, DetailedCVLedgEntryBuffer)
Expand Down Expand Up @@ -5839,6 +5843,19 @@ codeunit 12 "Gen. Jnl.-Post Line"
exit(GetVendorPayablesAccount(GenJournalLine, VendorPostingGroup));
end;

local procedure IsApplnEntryForAppliedVendorDoc(GenJournalLine: Record "Gen. Journal Line"; DetailedCVLedgEntryBuffer: Record "Detailed CV Ledg. Entry Buffer"): Boolean
var
VendorLedgerEntry: Record "Vendor Ledger Entry";
begin
// An application creates one detailed entry for the payment line's own ledger entry and one for the
// applied document (e.g. the invoice). When applying via "Applies-to Doc. No." the payment's own ledger
// entry might not be inserted yet, so it must use the line's posting group payables account. The applied
// document already exists and, when it uses a different posting group, must post to its own posting
// group's payables account so that the resulting G/L entries balance.
if not VendorLedgerEntry.Get(DetailedCVLedgEntryBuffer."CV Ledger Entry No.") then
exit(false);
exit(VendorLedgerEntry."Document Type" <> GenJournalLine."Document Type");
end;
local procedure GetEmplDtldCVLedgEntryBufferAccNo(var GenJournalLine: Record "Gen. Journal Line"; var DetailedCVLedgEntryBuffer: Record "Detailed CV Ledg. Entry Buffer"): Code[20]
var
EmployeeLedgerEntry: Record "Employee Ledger Entry";
Expand Down
80 changes: 80 additions & 0 deletions src/Layers/ES/Tests/Local/ERMApplyUnapply.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ codeunit 147310 "ERM Apply Unapply"
UnAppliedErr: Label 'Entries are still applied.';
GLEntryMustBeFound: Label 'GL Entry must be found.';
ExpectedApplicationGLEntriesForPayablesAccountsErr: Label 'Expected two Application GL entries for Payables Accounts, found %1.';
PayablesAccountMustBalanceErr: Label 'Payables Account %1 must net to zero after the invoice is fully applied by the payment.';
InvoiceMustBeFullyAppliedErr: Label 'Invoice must be fully applied.';
InvoiceLedgerEntryMustBeClosedErr: Label 'Invoice ledger entry must be closed.';

[Test]
[Scope('OnPrem')]
Expand Down Expand Up @@ -2216,6 +2219,83 @@ codeunit 147310 "ERM Apply Unapply"
Assert.AreEqual(2, GLEntryCount, StrSubstNo(ExpectedApplicationGLEntriesForPayablesAccountsErr, GLEntryCount));
end;

[Test]
[Scope('OnPrem')]
procedure ApplyPmtToPostedPurchInvWithDifferentPostingGroupsViaAppliesToDocNo()
var
GenJournalLine: Record "Gen. Journal Line";
GLEntry: Record "G/L Entry";
Item: Record Item;
PaymentMethod: Record "Payment Method";
PurchaseHeader: Record "Purchase Header";
PurchaseLine: Record "Purchase Line";
PurchasesPayablesSetup: Record "Purchases & Payables Setup";
Vendor: Record Vendor;
VendorLedgerEntry: Record "Vendor Ledger Entry";
VendorPostingGroup: array[2] of Record "Vendor Posting Group";
PostedInvoiceNo: Code[20];
Amount: Decimal;
begin
// [SCENARIO 638329][ES] Applying a Payment to a posted Purchase Invoice with different Posting Groups using "Applies-to Doc. No." on the line keeps each Payables Account balanced.
Initialize();

// [GIVEN] Allow Multiple Posting Groups is true in Purchases & Payables Setup.
PurchasesPayablesSetup.Get();
PurchasesPayablesSetup."Allow Multiple Posting Groups" := true;
PurchasesPayablesSetup.Modify();

// [GIVEN] Two Vendor Posting Groups "VPG1" and "VPG2" with different Payables Accounts, "VPG2" being an alternate of "VPG1".
LibraryPurchase.CreateVendorPostingGroup(VendorPostingGroup[1]);
LibraryPurchase.CreateVendorPostingGroup(VendorPostingGroup[2]);
LibraryPurchase.CreateAltVendorPostingGroup(VendorPostingGroup[1].Code, VendorPostingGroup[2].Code);

// [GIVEN] Vendor "V" with Allow Multiple Posting Groups and Vendor Posting Group = "VPG1".
LibraryPurchase.CreateVendor(Vendor);
Vendor.Validate("Allow Multiple Posting Groups", true);
Vendor.Validate("Vendor Posting Group", VendorPostingGroup[1].Code);
Vendor.Modify(true);

// [GIVEN] A non-Cartera Payment Method so the posted invoice creates a regular Invoice ledger entry (not a Bill).
PaymentMethod.Get(Vendor."Payment Method Code");
PaymentMethod.Validate("Invoices to Cartera", false);
PaymentMethod.Modify(true);

// [GIVEN] Posted Purchase Invoice "PI" for Vendor "V" with Posting Group "VPG1", Amount = "X".
LibraryInventory.CreateItem(Item);
LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Invoice, Vendor."No.");
PurchaseHeader.Validate("Prices Including VAT", true);
PurchaseHeader.Modify(true);
LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, Item."No.", 1);
PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandDecInRange(100, 500, 2));
PurchaseLine.Modify(true);
Amount := PurchaseLine."Amount Including VAT";
PostedInvoiceNo := LibraryPurchase.PostPurchaseDocument(PurchaseHeader, false, true);

// [WHEN] A Payment with Posting Group "VPG2" is posted applying to "PI" via "Applies-to Doc. No." on the journal line.
CreateGenJnlLine(GenJournalLine, GenJournalLine."Document Type"::Payment, GenJournalLine."Account Type"::Vendor, Vendor."No.", Amount);
GenJournalLine.Validate("Posting Group", VendorPostingGroup[2].Code);
GenJournalLine.Validate("Applies-to Doc. Type", GenJournalLine."Applies-to Doc. Type"::Invoice);
GenJournalLine.Validate("Applies-to Doc. No.", PostedInvoiceNo);
GenJournalLine.Modify(true);
LibraryERM.PostGeneralJnlLine(GenJournalLine);

// [THEN] The Invoice ledger entry is fully applied (closed).
LibraryERM.FindVendorLedgerEntry(VendorLedgerEntry, VendorLedgerEntry."Document Type"::Invoice, PostedInvoiceNo);
VendorLedgerEntry.CalcFields("Remaining Amount");
Assert.AreEqual(0, VendorLedgerEntry."Remaining Amount", InvoiceMustBeFullyAppliedErr);
Assert.IsFalse(VendorLedgerEntry.Open, InvoiceLedgerEntryMustBeClosedErr);

// [THEN] The invoice Payables Account "VPG1" nets to zero (closing entry posts to the invoice's own account, not the payment's).
GLEntry.SetRange("G/L Account No.", VendorPostingGroup[1]."Payables Account");
GLEntry.CalcSums(Amount);
Assert.AreEqual(0, GLEntry.Amount, StrSubstNo(PayablesAccountMustBalanceErr, VendorPostingGroup[1]."Payables Account"));

// [THEN] The payment Payables Account "VPG2" nets to zero.
GLEntry.SetRange("G/L Account No.", VendorPostingGroup[2]."Payables Account");
GLEntry.CalcSums(Amount);
Assert.AreEqual(0, GLEntry.Amount, StrSubstNo(PayablesAccountMustBalanceErr, VendorPostingGroup[2]."Payables Account"));
end;

local procedure SumGLEntryAmountsForPayablesAccounts(var PayablesAccountCodes: List of [Code[20]]): Decimal
var
GLEntry: Record "G/L Entry";
Expand Down
Loading