diff --git a/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccPayableExcel.Report.al b/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccPayableExcel.Report.al index e4de74d342..ca15184655 100644 --- a/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccPayableExcel.Report.al +++ b/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccPayableExcel.Report.al @@ -76,8 +76,9 @@ report 4403 "EXR Aged Acc Payable Excel" { IncludeCaption = true; } - column(CurrencyCode; CurrencyCodeDisplayCode) + column(CurrencyCode; "Currency Code") { + IncludeCaption = true; } column(PostingDate; "Posting Date") { @@ -118,11 +119,7 @@ report 4403 "EXR Aged Acc Payable Excel" Clear(AgingData); AgingData.DeleteAll(); InsertAgingData(VendorAgingData); - - if AgingData."Currency Code" = '' then - CurrencyCodeDisplayCode := GeneralLedgerSetup.GetCurrencyCode('') - else - CurrencyCodeDisplayCode := AgingData."Currency Code"; + CurrencyCodeDisplayCode := AgingData."Currency Code"; end; } @@ -263,7 +260,6 @@ report 4403 "EXR Aged Acc Payable Excel" DueByCurrencies = 'Due by Currencies', MaxLength = 31, Comment = 'Excel worksheet name.'; OpenByFCY = 'Open by (FCY)'; DataRetrieved = 'Data retrieved:'; - CurrencyCodeDisplay = 'Currency Code'; AgedAsOf = 'Aged as of:'; AgedAccountsPayable = 'Aged Accounts Payable'; AgedAccountsPayablePrint = 'Aged Accounts Payable (Print)', MaxLength = 31, Comment = 'Excel worksheet name.'; @@ -314,6 +310,9 @@ report 4403 "EXR Aged Acc Payable Excel" if PeriodCount = 0 then PeriodCount := 5; + if not GeneralLedgerSetup.Get() then + Clear(GeneralLedgerSetup); + WorkingEndDate := EndingDate; WorkingStartDate := CalcDate(PeriodLength, WorkingEndDate); repeat @@ -357,7 +356,10 @@ report 4403 "EXR Aged Acc Payable Excel" AgingData."Document No." := VendorLedgerEntry."Document No."; AgingData."Dimension 1 Code" := VendorLedgerEntry."Global Dimension 1 Code"; AgingData."Dimension 2 Code" := VendorLedgerEntry."Global Dimension 2 Code"; - AgingData."Currency Code" := VendorLedgerEntry."Currency Code"; + if VendorLedgerEntry."Currency Code" = '' then + AgingData."Currency Code" := GeneralLedgerSetup.GetCurrencyCode('') + else + AgingData."Currency Code" := VendorLedgerEntry."Currency Code"; AgingData."Posting Date" := VendorLedgerEntry."Posting Date"; AgingData."Document Date" := VendorLedgerEntry."Document Date"; AgingData."Due Date" := VendorLedgerEntry."Due Date"; diff --git a/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccountsRecExcel.Report.al b/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccountsRecExcel.Report.al index 1aad3b6cd9..2c4b0ad3b3 100644 --- a/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccountsRecExcel.Report.al +++ b/src/Apps/W1/ExcelReports/App/src/Financials/EXRAgedAccountsRecExcel.Report.al @@ -76,8 +76,9 @@ report 4402 "EXR Aged Accounts Rec Excel" { IncludeCaption = true; } - column(CurrencyCode; CurrencyCodeDisplayCode) + column(CurrencyCode; "Currency Code") { + IncludeCaption = true; } column(PostingDate; "Posting Date") { @@ -118,11 +119,7 @@ report 4402 "EXR Aged Accounts Rec Excel" Clear(AgingData); AgingData.DeleteAll(); InsertAgingData(CustomerAgingData); - - if AgingData."Currency Code" = '' then - CurrencyCodeDisplayCode := GeneralLedgerSetup.GetCurrencyCode('') - else - CurrencyCodeDisplayCode := AgingData."Currency Code"; + CurrencyCodeDisplayCode := AgingData."Currency Code"; end; } @@ -263,7 +260,6 @@ report 4402 "EXR Aged Accounts Rec Excel" DueByCurrencies = 'Due by Currencies', MaxLength = 31, Comment = 'Excel worksheet name.'; OpenByFCY = 'Open by (FCY)'; DataRetrieved = 'Data retrieved:'; - CurrencyCodeDisplay = 'Currency Code'; AgedAsOf = 'Aged as of:'; AgedAccountsReceivable = 'Aged Accounts Receivable'; AgedAccountsReceivablePrint = 'Aged Accounts Rec. (Print)', MaxLength = 31, Comment = 'Excel worksheet name.'; @@ -314,6 +310,9 @@ report 4402 "EXR Aged Accounts Rec Excel" if PeriodCount = 0 then PeriodCount := 5; + if not GeneralLedgerSetup.Get() then + Clear(GeneralLedgerSetup); + WorkingEndDate := EndingDate; WorkingStartDate := CalcDate(PeriodLength, WorkingEndDate); repeat @@ -357,7 +356,10 @@ report 4402 "EXR Aged Accounts Rec Excel" AgingData."Document No." := CustLedgerEntry."Document No."; AgingData."Dimension 1 Code" := CustLedgerEntry."Global Dimension 1 Code"; AgingData."Dimension 2 Code" := CustLedgerEntry."Global Dimension 2 Code"; - AgingData."Currency Code" := CustLedgerEntry."Currency Code"; + if CustLedgerEntry."Currency Code" = '' then + AgingData."Currency Code" := GeneralLedgerSetup.GetCurrencyCode('') + else + AgingData."Currency Code" := CustLedgerEntry."Currency Code"; AgingData."Posting Date" := CustLedgerEntry."Posting Date"; AgingData."Document Date" := CustLedgerEntry."Document Date"; AgingData."Due Date" := CustLedgerEntry."Due Date"; diff --git a/src/Apps/W1/ExcelReports/Test/app.json b/src/Apps/W1/ExcelReports/Test/app.json index 1b190250c8..f31091b480 100644 --- a/src/Apps/W1/ExcelReports/Test/app.json +++ b/src/Apps/W1/ExcelReports/Test/app.json @@ -38,6 +38,10 @@ { "from": 139543, "to": 139547 + }, + { + "from": 139555, + "to": 139555 } ], "resourceExposurePolicy": { diff --git a/src/Apps/W1/ExcelReports/Test/src/AgedAccountsExcelReports.Codeunit.al b/src/Apps/W1/ExcelReports/Test/src/AgedAccountsExcelReports.Codeunit.al new file mode 100644 index 0000000000..ae10c90436 --- /dev/null +++ b/src/Apps/W1/ExcelReports/Test/src/AgedAccountsExcelReports.Codeunit.al @@ -0,0 +1,261 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.Finance.ExcelReports.Test; + +using Microsoft.Finance.ExcelReports; +using Microsoft.Finance.GeneralLedger.Journal; +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Purchases.Payables; +using Microsoft.Purchases.Vendor; +using Microsoft.Sales.Customer; +using Microsoft.Sales.Receivables; + +codeunit 139555 "Aged Accounts Excel Reports" +{ + Subtype = Test; + RequiredTestIsolation = Disabled; + TestPermissions = Disabled; + + var + LibraryRandom: Codeunit "Library - Random"; + LibraryReportDataset: Codeunit "Library - Report Dataset"; + Assert: Codeunit Assert; + + [Test] + [HandlerFunctions('EXRAgedAccountsRecExcelHandler')] + procedure AgedAccountsRecRendersCurrencyCodePerEntry() + var + Customer: Record Customer; + GeneralLedgerSetup: Record "General Ledger Setup"; + UsdEntry, LcyEntry : Record "Cust. Ledger Entry"; + Variant: Variant; + RequestPageXml: Text; + LcyCode: Code[10]; + ForeignCurrencyCode: Code[10]; + DocNo, CurrencyCode : Text; + i: Integer; + UsdRowSeen, LcyRowSeen : Boolean; + begin + // [SCENARIO 637444] Aged Accounts Receivable Excel renders each row's own Currency Code, not a single per-customer value + InitializeAgingData(); + + // [GIVEN] G/L Setup with a distinct LCY Code + LcyCode := 'LCY'; + ForeignCurrencyCode := 'USD'; + if not GeneralLedgerSetup.Get() then + GeneralLedgerSetup.Insert(); + GeneralLedgerSetup."LCY Code" := LcyCode; + GeneralLedgerSetup.Modify(); + + // [GIVEN] A customer with one foreign-currency entry and one LCY (empty Currency Code) entry + CreateMinimalCustomer(Customer); + CreateCustLedgerEntry(UsdEntry, Customer."No.", "Gen. Journal Document Type"::Invoice, ForeignCurrencyCode); + CreateCustLedgerEntry(LcyEntry, Customer."No.", "Gen. Journal Document Type"::Invoice, ''); + Commit(); + + // [WHEN] Running the Aged Accounts Receivable Excel report + RequestPageXml := Report.RunRequestPage(Report::"EXR Aged Accounts Rec Excel", RequestPageXml); + LibraryReportDataset.RunReportAndLoad(Report::"EXR Aged Accounts Rec Excel", Variant, RequestPageXml); + + // [THEN] The foreign-currency row shows the foreign code and the LCY row shows the LCY code + LibraryReportDataset.SetXmlNodeList('DataItem[@name="AgingData"]'); + Assert.AreEqual(2, LibraryReportDataset.RowCount(), 'Two aging entries should be exported'); + for i := 1 to 2 do begin + LibraryReportDataset.GetNextRow(); + LibraryReportDataset.FindCurrentRowValue('DocumentNo', Variant); + DocNo := Variant; + LibraryReportDataset.FindCurrentRowValue('CurrencyCode', Variant); + CurrencyCode := Variant; + if DocNo = UsdEntry."Document No." then begin + Assert.AreEqual(ForeignCurrencyCode, CurrencyCode, 'Foreign-currency row should show its own currency code'); + UsdRowSeen := true; + end else + if DocNo = LcyEntry."Document No." then begin + Assert.AreEqual(LcyCode, CurrencyCode, 'LCY (empty Currency Code) row should fall back to G/L Setup LCY Code'); + LcyRowSeen := true; + end; + end; + Assert.IsTrue(UsdRowSeen, 'Foreign-currency row should be present'); + Assert.IsTrue(LcyRowSeen, 'LCY row should be present'); + end; + + [Test] + [HandlerFunctions('EXRAgedAccPayableExcelHandler')] + procedure AgedAccountsPayableRendersCurrencyCodePerEntry() + var + Vendor: Record Vendor; + GeneralLedgerSetup: Record "General Ledger Setup"; + UsdEntry, LcyEntry : Record "Vendor Ledger Entry"; + Variant: Variant; + RequestPageXml: Text; + LcyCode: Code[10]; + ForeignCurrencyCode: Code[10]; + DocNo, CurrencyCode : Text; + i: Integer; + UsdRowSeen, LcyRowSeen : Boolean; + begin + // [SCENARIO 637444] Aged Accounts Payable Excel renders each row's own Currency Code, not a single per-vendor value + InitializeAgingData(); + + // [GIVEN] G/L Setup with a distinct LCY Code + LcyCode := 'LCY'; + ForeignCurrencyCode := 'USD'; + if not GeneralLedgerSetup.Get() then + GeneralLedgerSetup.Insert(); + GeneralLedgerSetup."LCY Code" := LcyCode; + GeneralLedgerSetup.Modify(); + + // [GIVEN] A vendor with one foreign-currency entry and one LCY (empty Currency Code) entry + CreateMinimalVendor(Vendor); + CreateVendorLedgerEntry(UsdEntry, Vendor."No.", "Gen. Journal Document Type"::Invoice, ForeignCurrencyCode); + CreateVendorLedgerEntry(LcyEntry, Vendor."No.", "Gen. Journal Document Type"::Invoice, ''); + Commit(); + + // [WHEN] Running the Aged Accounts Payable Excel report + RequestPageXml := Report.RunRequestPage(Report::"EXR Aged Acc Payable Excel", RequestPageXml); + LibraryReportDataset.RunReportAndLoad(Report::"EXR Aged Acc Payable Excel", Variant, RequestPageXml); + + // [THEN] The foreign-currency row shows the foreign code and the LCY row shows the LCY code + LibraryReportDataset.SetXmlNodeList('DataItem[@name="AgingData"]'); + Assert.AreEqual(2, LibraryReportDataset.RowCount(), 'Two aging entries should be exported'); + for i := 1 to 2 do begin + LibraryReportDataset.GetNextRow(); + LibraryReportDataset.FindCurrentRowValue('DocumentNo', Variant); + DocNo := Variant; + LibraryReportDataset.FindCurrentRowValue('CurrencyCode', Variant); + CurrencyCode := Variant; + if DocNo = UsdEntry."Document No." then begin + Assert.AreEqual(ForeignCurrencyCode, CurrencyCode, 'Foreign-currency row should show its own currency code'); + UsdRowSeen := true; + end else + if DocNo = LcyEntry."Document No." then begin + Assert.AreEqual(LcyCode, CurrencyCode, 'LCY (empty Currency Code) row should fall back to G/L Setup LCY Code'); + LcyRowSeen := true; + end; + end; + Assert.IsTrue(UsdRowSeen, 'Foreign-currency row should be present'); + Assert.IsTrue(LcyRowSeen, 'LCY row should be present'); + end; + + local procedure InitializeAgingData() + var + Vendor: Record Vendor; + Customer: Record Customer; + VendorLedgerEntry: Record "Vendor Ledger Entry"; + CustLedgerEntry: Record "Cust. Ledger Entry"; + DetailedVendorLedgEntry: Record "Detailed Vendor Ledg. Entry"; + DetailedCustLedgEntry: Record "Detailed Cust. Ledg. Entry"; + begin + DetailedVendorLedgEntry.DeleteAll(); + DetailedCustLedgEntry.DeleteAll(); + VendorLedgerEntry.DeleteAll(); + CustLedgerEntry.DeleteAll(); + Vendor.DeleteAll(); + Customer.DeleteAll(); + end; + + local procedure CreateMinimalVendor(var Vendor: Record Vendor) + begin + Vendor.Init(); + Vendor."No." := CopyStr(Format(CreateGuid()), 1, MaxStrLen(Vendor."No.")); + Vendor.Name := Vendor."No."; + Vendor.Insert(); + end; + + local procedure CreateMinimalCustomer(var Customer: Record Customer) + begin + Customer.Init(); + Customer."No." := CopyStr(Format(CreateGuid()), 1, MaxStrLen(Customer."No.")); + Customer.Name := Customer."No."; + Customer.Insert(); + end; + + local procedure CreateVendorLedgerEntry(var VendorLedgerEntry: Record "Vendor Ledger Entry"; VendorNo: Code[20]; DocumentType: Enum "Gen. Journal Document Type"; CurrencyCode: Code[10]) + var + DetailedVendorLedgEntry: Record "Detailed Vendor Ledg. Entry"; + EntryNo: Integer; + Amount: Decimal; + begin + if VendorLedgerEntry.FindLast() then; + EntryNo := VendorLedgerEntry."Entry No." + 1; + + VendorLedgerEntry.Init(); + VendorLedgerEntry."Entry No." := EntryNo; + VendorLedgerEntry."Vendor No." := VendorNo; + VendorLedgerEntry."Vendor Name" := VendorNo; + VendorLedgerEntry."Document Type" := DocumentType; + VendorLedgerEntry."Document No." := 'DOC' + Format(EntryNo); + VendorLedgerEntry."Posting Date" := WorkDate(); + VendorLedgerEntry."Document Date" := WorkDate(); + VendorLedgerEntry."Due Date" := WorkDate() + 30; + VendorLedgerEntry."Currency Code" := CurrencyCode; + VendorLedgerEntry.Open := true; + VendorLedgerEntry.Insert(); + + // Create detailed vendor ledger entry for remaining amount + Amount := -LibraryRandom.RandDec(1000, 2); + if DetailedVendorLedgEntry.FindLast() then; + DetailedVendorLedgEntry.Init(); + DetailedVendorLedgEntry."Entry No." := DetailedVendorLedgEntry."Entry No." + 1; + DetailedVendorLedgEntry."Vendor Ledger Entry No." := VendorLedgerEntry."Entry No."; + DetailedVendorLedgEntry."Vendor No." := VendorNo; + DetailedVendorLedgEntry."Posting Date" := WorkDate(); + DetailedVendorLedgEntry."Entry Type" := DetailedVendorLedgEntry."Entry Type"::"Initial Entry"; + DetailedVendorLedgEntry.Amount := Amount; + DetailedVendorLedgEntry."Amount (LCY)" := Amount; + DetailedVendorLedgEntry.Insert(); + end; + + local procedure CreateCustLedgerEntry(var CustLedgerEntry: Record "Cust. Ledger Entry"; CustomerNo: Code[20]; DocumentType: Enum "Gen. Journal Document Type"; CurrencyCode: Code[10]) + var + DetailedCustLedgEntry: Record "Detailed Cust. Ledg. Entry"; + EntryNo: Integer; + Amount: Decimal; + begin + if CustLedgerEntry.FindLast() then; + EntryNo := CustLedgerEntry."Entry No." + 1; + + CustLedgerEntry.Init(); + CustLedgerEntry."Entry No." := EntryNo; + CustLedgerEntry."Customer No." := CustomerNo; + CustLedgerEntry."Customer Name" := CustomerNo; + CustLedgerEntry."Document Type" := DocumentType; + CustLedgerEntry."Document No." := 'DOC' + Format(EntryNo); + CustLedgerEntry."Posting Date" := WorkDate(); + CustLedgerEntry."Document Date" := WorkDate(); + CustLedgerEntry."Due Date" := WorkDate() + 30; + CustLedgerEntry."Currency Code" := CurrencyCode; + CustLedgerEntry.Open := true; + CustLedgerEntry.Insert(); + + // Create detailed customer ledger entry for remaining amount + Amount := LibraryRandom.RandDec(1000, 2); + if DetailedCustLedgEntry.FindLast() then; + DetailedCustLedgEntry.Init(); + DetailedCustLedgEntry."Entry No." := DetailedCustLedgEntry."Entry No." + 1; + DetailedCustLedgEntry."Cust. Ledger Entry No." := CustLedgerEntry."Entry No."; + DetailedCustLedgEntry."Customer No." := CustomerNo; + DetailedCustLedgEntry."Posting Date" := WorkDate(); + DetailedCustLedgEntry."Entry Type" := DetailedCustLedgEntry."Entry Type"::"Initial Entry"; + DetailedCustLedgEntry.Amount := Amount; + DetailedCustLedgEntry."Amount (LCY)" := Amount; + DetailedCustLedgEntry.Insert(); + end; + + [RequestPageHandler] + procedure EXRAgedAccPayableExcelHandler(var EXRAgedAccPayableExcel: TestRequestPage "EXR Aged Acc Payable Excel") + begin + EXRAgedAccPayableExcel.AgedAsOfOption.SetValue(WorkDate()); + EXRAgedAccPayableExcel.OK().Invoke(); + end; + + [RequestPageHandler] + procedure EXRAgedAccountsRecExcelHandler(var EXRAgedAccountsRecExcel: TestRequestPage "EXR Aged Accounts Rec Excel") + begin + EXRAgedAccountsRecExcel.AgedAsOfOption.SetValue(WorkDate()); + EXRAgedAccountsRecExcel.OK().Invoke(); + end; +}