Skip to content
Closed
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 @@ -42,6 +42,7 @@ codeunit 148322 "ERM Withholding Tax Tests II"
ValueMustBeSameMsg: Label 'Value must be same.';
WHTAccountCodeEmptyErr: Label '%1 must have a value in Withholding Tax Posting Setup: Withholding Tax Bus. Post. Group=%2, Withholding Tax Prod. Post. Group=%3. It cannot be zero or empty.',
Comment = '%1 = Field Caption, %2 = WHT Business Posting Group Code, %3 = WHT Product Posting Group Code';
WHTProdPostGroupNotCopiedErr: Label 'Withholding Tax Prod. Post. Group must be copied from the applied Invoice to the Payment line.';
IsInitialized: Boolean;

[Test]
Expand Down Expand Up @@ -595,6 +596,48 @@ codeunit 148322 "ERM Withholding Tax Tests II"
VerifyPostingNoSeriesOnePostedDoc('B0', 6, 2);
end;

[Test]
[Scope('OnPrem')]
[HandlerFunctions('ConfirmHandler')]
procedure WHTProdPostGroupTravelsFromJournalLine()
var
GenJournalLine: array[2] of Record "Gen. Journal Line";
VATPostingSetup: Record "VAT Posting Setup";
WHTBusPostingGroup: Record "Wthldg. Tax Bus. Post. Group";
WHTPostingSetup: Record "Withholding Tax Posting Setup";
WHTProdPostingGroup: Record "Wthldg. Tax Prod. Post. Group";
VendorLedgerEntry: Record "Vendor Ledger Entry";
DocumentNo: Code[20];
begin
// [SCENARIO 639524] Withholding Tax Prod. Post. Group travels from the applied invoice to the payment line and Withholding Tax Entries are generated at payment.
Initialize();

// [GIVEN] WHT Posting Setup realized on Payment and a posted Invoice for a WHT liable Vendor.
UpdateLocalFunctionalitiesOnGeneralLedgerSetup(true);

// [GIVEN] Create a WHT Posting Setup for a WHT liable Vendor and a posted Invoice for that Vendor.
LibraryERM.FindVATPostingSetup(VATPostingSetup, VATPostingSetup."VAT Calculation Type"::"Normal VAT");
LibraryWithholdingTax.CreateWHTBusinessPostingGroup(WHTBusPostingGroup);
LibraryWithholdingTax.CreateWHTProductPostingGroup(WHTProdPostingGroup);

// [GIVEN] Create a posted Invoice for a WHT liable Vendor with a WHT Prod. Post. Group.
CreateGeneralJournalLineWithBalAccountType(
GenJournalLine[1], GenJournalLine[1]."Document Type"::Invoice, CreateVendor(VATPostingSetup."VAT Bus. Posting Group", WHTBusPostingGroup.Code), '',
'', GenJournalLine[1]."Bal. Account Type"::"G/L Account", CreateGLAccountWithVATBusPostingGroup(VATPostingSetup, WHTProdPostingGroup.Code),
-LibraryRandom.RandDecInRange(100, 200, 2));

// [GIVEN] WHT Posting Setup realized on Payment and a posted Invoice for a WHT liable Vendor.
UpdateGenJournalLineWHTAbsorbBase(GenJournalLine[1]);
FindWHTPostingSetup(WHTPostingSetup, GenJournalLine[1]."Wthldg. Tax Bus. Post. Group", GenJournalLine[1]."Wthldg. Tax Prod. Post. Group", '');

LibraryERM.PostGeneralJnlLine(GenJournalLine[1]);
DocumentNo := FindVendorLedgerEntry(GenJournalLine[1]."Account No.");
LibraryERM.FindVendorLedgerEntry(VendorLedgerEntry, VendorLedgerEntry."Document Type"::Invoice, DocumentNo);

// [THEN] The WHT Prod. Post. Group is copied from the Invoice to the Payment line.
Assert.AreEqual(GetWHTProdPostGroupFromInvoiceEntry(VendorLedgerEntry), WHTPostingSetup."Wthldg. Tax Prod. Post. Group", WHTProdPostGroupNotCopiedErr);
end;

Comment thread
neeleshsinghal marked this conversation as resolved.
local procedure Initialize()
var
VATPostingSetup: Record "VAT Posting Setup";
Expand Down Expand Up @@ -979,6 +1022,20 @@ codeunit 148322 "ERM Withholding Tax Tests II"
exit(VendorLedgerEntry.Amount);
end;

local procedure GetWHTProdPostGroupFromInvoiceEntry(VendorLedgerEntry: Record "Vendor Ledger Entry"): Code[20]
var
WithholdingTaxEntry: Record "Withholding Tax Entry";
begin
WithholdingTaxEntry.SetLoadFields("Wthldg. Tax Prod. Post. Group");
WithholdingTaxEntry.SetRange("Document No.", VendorLedgerEntry."Document No.");
WithholdingTaxEntry.SetRange("Transaction Type", WithholdingTaxEntry."Transaction Type"::Purchase);
WithholdingTaxEntry.SetRange("Document Type", WithholdingTaxEntry."Document Type"::Invoice);
WithholdingTaxEntry.SetRange("Bill-to/Pay-to No.", VendorLedgerEntry."Vendor No.");
if not WithholdingTaxEntry.FindFirst() then
exit('');
exit(WithholdingTaxEntry."Wthldg. Tax Prod. Post. Group");
end;

local procedure PostPurchaseDocument(DocumentType: Enum "Purchase Document Type"; DocumentNo: Code[20]): Code[20]
var
PurchaseHeader: Record "Purchase Header";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,36 @@ codeunit 6786 "Withholding Tax Jnl Subscriber"
PostUnrealizedWHT(GenJournalLine, GenJnlPostLine);
end;


[EventSubscriber(ObjectType::Page, Page::"Create Payment", OnBeforeUpdateGnlJnlLineDimensionsFromVendorPayment, '', false, false)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Agent} \quad \color{gray}{\texttt{\small Iteration\ 4}}$

AssignWHTPostingGroupOnCreatePayment only subscribes to Page "Create Payment"'s OnBeforeUpdateGnlJnlLineDimensionsFromVendorPayment event, so the Wthldg.

Tax Prod. Post. Group is copied to the payment line only when a payment is created via the Vendor Ledger Entries "Create Payment" action. The very similar OnBeforeUpdateGnlJnlLineDimensionsFromVendorPaymentBuffer event, raised by the Suggest Vendor Payments report/batch job in every localization layer (W1, NA, IT, RU, CH, ES, APAC, FI), has no corresponding subscriber added in this PR. Payment lines generated by Suggest Vendor Payments will therefore still have a blank Wthldg. Tax Prod. Post. Group, leaving the exact bug this PR fixes unresolved for that entry point.

Recommendation:

  • add an equivalent subscriber (or shared helper) for OnBeforeUpdateGnlJnlLineDimensionsFromVendorPaymentBuffer so both payment-creation paths populate the field consistently.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why

local procedure AssignWHTPostingGroupOnCreatePayment(var GenJournalLine: Record "Gen. Journal Line"; TempVendorPaymentBuffer: Record "Vendor Payment Buffer" temporary)
var
VendorLedgerEntry: Record "Vendor Ledger Entry";
WithholdingTaxEntry: Record "Withholding Tax Entry";
begin
if CheckWithholdingTaxDisabled() then
exit;

if GenJournalLine."Applies-to ID" = '' then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Agent} \quad \color{gray}{\texttt{\small Iteration\ 4}}$

The guard if GenJournalLine."Applies-to ID" = '' then exit; can never be true for this event: the only publisher, CopyTempPaymentBufferToGenJournalLines in CreatePayment.Page.al, unconditionally sets GenJnlLine."Applies-to ID" := GenJnlLine."Document No." (always a non-blank generated value) before raising OnBeforeUpdateGnlJnlLineDimensionsFromVendorPayment.

The guard is dead code that misleadingly suggests the subscriber skips unapplied payments via this check, while the real filtering is done by the following TempVendorPaymentBuffer."Vendor Ledg. Entry No." = 0 guard.

Recommendation:

  • remove the ineffective check or replace it with a check that reflects the real intent (for example, TempVendorPaymentBuffer."Vendor Ledg. Entry Doc. No." = '') if the goal is to skip payments not applied to a specific document.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why

exit;

if TempVendorPaymentBuffer."Vendor Ledg. Entry No." = 0 then
exit;

VendorLedgerEntry.SetLoadFields("Document No.", "Vendor No.");
VendorLedgerEntry.Get(TempVendorPaymentBuffer."Vendor Ledg. Entry No.");

WithholdingTaxEntry.SetLoadFields("Wthldg. Tax Prod. Post. Group");
WithholdingTaxEntry.SetRange("Document No.", VendorLedgerEntry."Document No.");
WithholdingTaxEntry.SetRange("Transaction Type", WithholdingTaxEntry."Transaction Type"::Purchase);
WithholdingTaxEntry.SetRange("Document Type", WithholdingTaxEntry."Document Type"::Invoice);
WithholdingTaxEntry.SetRange("Bill-to/Pay-to No.", VendorLedgerEntry."Vendor No.");
Comment thread
neeleshsinghal marked this conversation as resolved.
if not WithholdingTaxEntry.FindFirst() then
exit;

GenJournalLine."Wthldg. Tax Prod. Post. Group" := WithholdingTaxEntry."Wthldg. Tax Prod. Post. Group";
end;

local procedure PostUnrealizedWHT(var GenJnlLine: Record "Gen. Journal Line"; var GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line")
var
GenJournalLineWHT: Record "Gen. Journal Line";
Expand Down
Loading