Skip to content
Open
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 @@ -188,6 +188,8 @@ codeunit 12 "Gen. Jnl.-Post Line"
HadWHTEntryNo: Boolean;
NextNo: Integer;
UseVendExchRate: Boolean;
VendGSTAmountACYEnabled: Boolean;
VendGSTAmountACYRead: Boolean;
Text28000: Label 'No Matching Document';
CheckRem: Boolean;
IsReversal: Boolean;
Expand Down Expand Up @@ -8162,9 +8164,36 @@ codeunit 12 "Gen. Jnl.-Post Line"
InitGLEntry(
GenJnlLine, GLEntry, GLAccNo, TotalAmountLCY, TotalAmountAddCurr, true, true, TotalAmountAddCurr)
else
InitGLEntry(
GenJnlLine, GLEntry, GLAccNo, TotalAmountLCY, 0, false, true, TotalAmountAddCurr);
if VendorACYExchangeRateApplies() then
Comment thread
dcenic marked this conversation as resolved.
// [641827] GLCalcAddCurrency keeps the vendor ACY here, so derive Source Currency Amount from LCY.
InitGLEntry(
GenJnlLine, GLEntry, GLAccNo, TotalAmountLCY, TotalAmountAddCurr, true, true,
CalcAmountSrcCurr(GenJnlLine, TotalAmountLCY))
else
InitGLEntry(
GenJnlLine, GLEntry, GLAccNo, TotalAmountLCY, 0, false, true, TotalAmountAddCurr);
end;
end;

local procedure VendorACYExchangeRateApplies(): Boolean
Comment thread
dcenic marked this conversation as resolved.

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}$

VendorACYExchangeRateApplies() (GenJnlPostLine.Codeunit.al, new local procedure) is documented as mirroring GLCalcAddCurrency's vendor-ACY branch, but it omits one of that branch's guard conditions. GLCalcAddCurrency only takes the vendor-ACY path when (AddCurrencyCode <> '') and (GenJnlLine."Additional-Currency Posting" = GenJnlLine."Additional-Currency Posting"::None) AND PurchSetup."Enable Vendor GST Amount (ACY)" and UseVendExchRate; otherwise it falls through to UseVendExchRate := false; exit(OldAddCurrAmount). VendorACYExchangeRateApplies() only checks AddCurrencyCode <> '', UseVendExchRate, and the setup flag — it never checks GenJnlLine."Additional-Currency Posting". When a gen. journal line has "Additional-Currency Posting" <> None (e.g. set to "Amount Only"/"Additional-Currency Amount Only") together with AddCurrencyCode <> '', UseVendExchRate = true and the setup flag enabled, GLCalcAddCurrency would skip the vendor-ACY branch entirely (falling through to the OldAddCurrAmount exit), while the new HandleDtldAdjustment call site will still believe VendorACYExchangeRateApplies() = true and derive the balancing entry's Source Currency Amount via CalcAmountSrcCurr(GenJnlLine, TotalAmountLCY). This diverges from the actual value GLCalcAddCurrency produced for the entry's Additional-Currency Amount, unbalancing the posted document for that posting-rule combination. Add the same "Additional-Currency Posting" = ::None guard to VendorACYExchangeRateApplies() so it truly mirrors GLCalcAddCurrency's condition.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4

begin
// Mirrors GLCalcAddCurrency's vendor-ACY branch so the balancing entry's Source Currency Amount matches its ACY.
if (AddCurrencyCode = '') or (not UseVendExchRate) then
Comment thread
dcenic marked this conversation as resolved.
exit(false);
exit(GetVendGSTAmountACYEnabled());
end;

local procedure GetVendGSTAmountACYEnabled(): Boolean

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}$

GetVendGSTAmountACYEnabled() caches "Purchases & Payables Setup"."Enable Vendor GST Amount (ACY)" in the new instance-level fields VendGSTAmountACYEnabled/VendGSTAmountACYRead for the lifetime of the codeunit 12 instance, and nothing in the diff ever resets VendGSTAmountACYRead back to false. Codeunit 12 is reused across multiple gen. journal lines/documents within one posting run (this is exactly why UseVendExchRate is defensively reset to false in three separate places in this same codeunit, including one added by this PR). If the setup flag is toggled between documents processed by the same running instance (e.g. an admin change mid-batch, or reuse in a long-running batch/test session), the cached value silently continues driving VendorACYExchangeRateApplies() instead of reflecting the current setup, unlike GLCalcAddCurrency's own PurchSetup.Get() which re-reads on every call. Reset VendGSTAmountACYRead alongside UseVendExchRate at the existing reset points, or drop the cache and read the setup field fresh (as GLCalcAddCurrency already does) since this is a single boolean field.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4

var
PurchSetup: Record "Purchases & Payables Setup";
begin
if not VendGSTAmountACYRead then begin
PurchSetup.SetLoadFields("Enable Vendor GST Amount (ACY)");
PurchSetup.Get();
VendGSTAmountACYEnabled := PurchSetup."Enable Vendor GST Amount (ACY)";
VendGSTAmountACYRead := true;
end;
exit(VendGSTAmountACYEnabled);
end;

local procedure PostDtldAdjustment(GenJnlLine: Record "Gen. Journal Line"; var GLEntry: Record "G/L Entry"; AdjAmount: array[4] of Decimal; TotalAmountLCY: Decimal; TotalAmountAddCurr: Decimal; GLAcc: Code[20]; ArrayIndex: Integer): Boolean
Expand Down Expand Up @@ -8434,6 +8463,8 @@ codeunit 12 "Gen. Jnl.-Post Line"
GLEntry: Record "G/L Entry";
IsHandled: Boolean;
begin
// General balancing entries are never the vendor-ACY case; clear the transient flag so leftover state cannot select it.
UseVendExchRate := false;
HandleDtldAdjustment(GenJnlLine, GLEntry, AdjAmountBuf, Amount, AmountACY, GLAccNo);
GLEntry."Bal. Account Type" := GenJnlLine."Bal. Account Type";
GLEntry."Bal. Account No." := GenJnlLine."Bal. Account No.";
Expand Down
88 changes: 87 additions & 1 deletion src/Layers/APAC/Tests/Local/ERMMiscellaneousAPAC.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,93 @@ codeunit 141008 "ERM - Miscellaneous APAC"
PurchaseLine.Quantity * PurchaseLine."Direct Unit Cost" * PurchaseHeader."Vendor Exchange Rate (ACY)");
end;

[Test]
[Scope('OnPrem')]
procedure PurchaseInvoiceWithVendorACYPostsACYOnPayablesEntry()
Comment thread
dcenic marked this conversation as resolved.
var
Currency: Record Currency;
GLEntry: Record "G/L Entry";
PurchaseHeader: Record "Purchase Header";
PurchaseLine: Record "Purchase Line";
PurchasesPayablesSetup: Record "Purchases & Payables Setup";
CurrencyCode: Code[10];
PayablesAccountNo: Code[20];
PostedDocumentNo: Code[20];
ExpectedACYAmount: Decimal;
NonPayablesACY: Decimal;
OriginalVendorGSTAmountACY: Boolean;
begin
// [FEATURE] [Purchase] [ACY]
// [SCENARIO 641827] Vendor ACY is posted on the payables entry for a purchase invoice in LCY.
Initialize();
UpdateGeneralLedgerSetupGSTReport();
Comment thread
dcenic marked this conversation as resolved.

// [GIVEN] Vendor GST amounts in ACY are enabled and an Additional Reporting Currency is configured.
CurrencyCode := LibraryERM.CreateCurrencyWithRandomExchRates();
Currency.Get(CurrencyCode);
PurchasesPayablesSetup.Get();
Comment thread
dcenic marked this conversation as resolved.
OriginalVendorGSTAmountACY := PurchasesPayablesSetup."Enable Vendor GST Amount (ACY)";
PurchasesPayablesSetup."Enable Vendor GST Amount (ACY)" := true;
PurchasesPayablesSetup.Modify();
Comment on lines +841 to +844

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\ —\ Testing}$

This test changes "Purchases & Payables Setup"."Enable Vendor GST Amount (ACY)" without first calling LibrarySetupStorage.Save for that table. If any assertion above fails, the manual restore at the end never runs and later tests inherit the modified setup. Save the setup before the modification so Initialize()/LibrarySetupStorage.Restore() cleans it up even on failure.

Suggested change
PurchasesPayablesSetup.Get();
OriginalVendorGSTAmountACY := PurchasesPayablesSetup."Enable Vendor GST Amount (ACY)";
PurchasesPayablesSetup."Enable Vendor GST Amount (ACY)" := true;
PurchasesPayablesSetup.Modify();
PurchasesPayablesSetup.Get();
LibrarySetupStorage.Save(DATABASE::"Purchases & Payables Setup");
OriginalVendorGSTAmountACY := PurchasesPayablesSetup."Enable Vendor GST Amount (ACY)";
PurchasesPayablesSetup."Enable Vendor GST Amount (ACY)" := true;
PurchasesPayablesSetup.Modify();

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.32.4

LibraryERM.SetAddReportingCurrency(CurrencyCode);

// [GIVEN] A purchase invoice in LCY with a vendor exchange rate for ACY.
CreatePurchDocWithLine(
PurchaseHeader, PurchaseLine, PurchaseHeader."Document Type"::Invoice,
PurchaseLine.Type::"G/L Account", LibraryERM.CreateGLAccountWithPurchSetup(), WorkDate());
PurchaseHeader.TestField("Currency Code", '');
PurchaseHeader."Vendor Exchange Rate (ACY)" := LibraryRandom.RandInt(10);
PurchaseHeader.Modify();
ExpectedACYAmount :=
Comment thread
dcenic marked this conversation as resolved.
PurchaseLine.Quantity * PurchaseLine."Direct Unit Cost" * PurchaseHeader."Vendor Exchange Rate (ACY)";

// [WHEN] The purchase invoice is posted.
PostedDocumentNo := LibraryPurchase.PostPurchaseDocument(PurchaseHeader, false, true);

// [THEN] The purchase (expense) G/L entry carries the vendor-rate ACY: Quantity * Direct Unit Cost * Vendor Exchange Rate (ACY).
GLEntry.SetRange("Document No.", PostedDocumentNo);
GLEntry.SetRange("G/L Account No.", PurchaseLine."No.");
Assert.RecordCount(GLEntry, 1);
GLEntry.FindFirst();
GLEntry.TestField("Additional-Currency Amount", ExpectedACYAmount);

// [THEN] The payables (balancing) entry carries the offsetting ACY of all non-residual entries, in blank source currency.
PayablesAccountNo := GetPayablesAccountFromVendorPostingGroup(PurchaseHeader."Pay-to Vendor No.");
GLEntry.Reset();
GLEntry.SetRange("Document No.", PostedDocumentNo);
GLEntry.SetFilter(
"G/L Account No.", '<>%1&<>%2&<>%3',
PayablesAccountNo, Currency."Residual Gains Account", Currency."Residual Losses Account");
GLEntry.CalcSums("Additional-Currency Amount");
NonPayablesACY := GLEntry."Additional-Currency Amount";
Assert.IsTrue(NonPayablesACY <> 0, AmountMustBeEqualMsg);

GLEntry.SetRange("G/L Account No.", PayablesAccountNo);
Assert.RecordCount(GLEntry, 1);
GLEntry.FindFirst();
GLEntry.TestField("Source Currency Code", '');
GLEntry.TestField("Source Currency Amount", GLEntry.Amount);
GLEntry.TestField("Additional-Currency Amount", -NonPayablesACY);

// [THEN] LCY, source currency, and ACY are balanced.
GLEntry.Reset();
GLEntry.SetRange("Document No.", PostedDocumentNo);
GLEntry.CalcSums(Amount, "Source Currency Amount", "Additional-Currency Amount");
Assert.AreEqual(0, GLEntry.Amount, AmountMustBeEqualMsg);
Assert.AreEqual(0, GLEntry."Source Currency Amount", AmountMustBeEqualMsg);
Assert.AreEqual(0, GLEntry."Additional-Currency Amount", AmountMustBeEqualMsg);

// [THEN] No residual gains or losses entry is created.
GLEntry.SetFilter(
"G/L Account No.", '%1|%2', Currency."Residual Gains Account", Currency."Residual Losses Account");
Assert.RecordCount(GLEntry, 0);

// The flag is not registered in setup storage, so restore its original value to avoid leaking into later tests.
PurchasesPayablesSetup.Get();
PurchasesPayablesSetup."Enable Vendor GST Amount (ACY)" := OriginalVendorGSTAmountACY;
PurchasesPayablesSetup.Modify();
end;

[Test]
[Scope('OnPrem')]
procedure SalesOrderWithTwoLinesAndDeferralCreatesSingleGSTSalesEntry()
Expand Down Expand Up @@ -2944,4 +3031,3 @@ codeunit 141008 "ERM - Miscellaneous APAC"
SalesStatistics.OK().Invoke();
end;
}

Loading