From 97906b796c817654cbbd84ce871dfe05056b5b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joshua=20Mart=C3=ADnez=20Pineda?= Date: Fri, 26 Jun 2026 11:48:53 +0200 Subject: [PATCH 1/4] fix for edge case when balance of a posting account at ending date is zero --- .../src/Financials/TrialBalance.Codeunit.al | 6 ++- .../src/TrialBalanceExcelReports.Codeunit.al | 45 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al b/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al index 268ad055ff..9a9c858853 100644 --- a/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al +++ b/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al @@ -263,7 +263,9 @@ codeunit 4410 "Trial Balance" TrialBalanceData.SetRange("G/L Account No.", EXRTrialBalanceQuery.AccountNumber); TrialBalanceData.SetRange("Dimension 1 Code", EXRTrialBalanceQuery.DimensionValue1Code); TrialBalanceData.SetRange("Dimension 2 Code", EXRTrialBalanceQuery.DimensionValue2Code); - if not TrialBalanceData.FindFirst() then begin // This shouldn't happen, but we consider it regardless + if not TrialBalanceData.FindFirst() then begin + // The combination nets to zero at the end date, so the first pass skipped it as All Zero, but it still has an opening balance here. + TrialBalanceData.Init(); TrialBalanceData."G/L Account No." := EXRTrialBalanceQuery.AccountNumber; TrialBalanceData."Dimension 1 Code" := EXRTrialBalanceQuery.DimensionValue1Code; TrialBalanceData."Dimension 2 Code" := EXRTrialBalanceQuery.DimensionValue2Code; @@ -333,6 +335,8 @@ codeunit 4410 "Trial Balance" TrialBalanceData.SetRange("Dimension 2 Code", EXRTrialBalanceBUQuery.DimensionValue2Code); TrialBalanceData.SetRange("Business Unit Code", EXRTrialBalanceBUQuery.BusinessUnitCode); if not TrialBalanceData.FindFirst() then begin + // The combination nets to zero at the end date, so the first pass skipped it as All Zero, but it still has an opening balance here. + TrialBalanceData.Init(); TrialBalanceData."G/L Account No." := EXRTrialBalanceBUQuery.AccountNumber; TrialBalanceData."Dimension 1 Code" := EXRTrialBalanceBUQuery.DimensionValue1Code; TrialBalanceData."Dimension 2 Code" := EXRTrialBalanceBUQuery.DimensionValue2Code; diff --git a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al index c789c3de90..593fcab626 100644 --- a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al +++ b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al @@ -673,6 +673,51 @@ codeunit 139544 "Trial Balance Excel Reports" Assert.AreEqual(NonZeroAccount, TrialBalanceData."G/L Account No.", 'The non-zero account should be the one returned'); end; + [Test] + procedure QueryPathDoesNotInheritStaleAmountsForZeroNetCombination() + var + GLAccount: Record "G/L Account"; + TempDimension1Values, TempDimension2Values : Record "Dimension Value" temporary; + TempTrialBalanceData: Record "EXR Trial Balance Buffer"; + TrialBalance: Codeunit "Trial Balance"; + ZeroNetAccountNo, StaleSourceAccountNo : Code[20]; + OpeningAmount, StaleBalance : Decimal; + PeriodStart: Date; + begin + // [SCENARIO] A combination that nets to zero at the end date but still has an opening balance is dropped by the first + // query pass (All Zero) and re-created by the second (opening-balance) pass. That re-created record must be + // Init()'d, otherwise it inherits the stale amounts left on the shared buffer record by the previous account. + Initialize(); + PeriodStart := DMY2Date(1, 1, Date2DMY(WorkDate(), 3)); + + // [GIVEN] A zero-net posting account (10000) read first in the second pass, and a stale-source posting account + // (20000) read last in the first pass so it leaves a non-zero balance on the shared buffer record. + ZeroNetAccountNo := CreateGLAccountWithNo('10000', Enum::"G/L Account Type"::Posting, ''); + StaleSourceAccountNo := CreateGLAccountWithNo('20000', Enum::"G/L Account Type"::Posting, ''); + + OpeningAmount := 1000; + StaleBalance := 777; + // [GIVEN] 10000 has an opening balance before the period that is fully reversed within the period -> end balance 0 + CreateGLEntryWithAmount(ZeroNetAccountNo, '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3) - 1), OpeningAmount); + CreateGLEntryWithAmount(ZeroNetAccountNo, '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3)), -OpeningAmount); + // [GIVEN] 20000 has only in-period activity (non-zero end balance, no opening balance), so it is absent from the second pass + CreateGLEntryWithAmount(StaleSourceAccountNo, '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3)), StaleBalance); + + // [WHEN] Running the query-based trial balance for the current year + GLAccount.SetRange("Date Filter", PeriodStart, DMY2Date(31, 12, Date2DMY(WorkDate(), 3))); + TrialBalance.ConfigureTrialBalance(false, false); + TrialBalance.InsertTrialBalanceReportData(GLAccount, TempDimension1Values, TempDimension2Values, TempTrialBalanceData); + + // [THEN] The zero-net account reports its own amounts (Starting Balance = opening, Net Change = -opening, Balance = 0), + // not the stale source account's balance carried over from the previous record. + TempTrialBalanceData.Reset(); + TempTrialBalanceData.SetRange("G/L Account No.", ZeroNetAccountNo); + Assert.IsTrue(TempTrialBalanceData.FindFirst(), 'Buffer record should exist for the zero-net account'); + Assert.AreEqual(OpeningAmount, TempTrialBalanceData."Starting Balance", 'Starting Balance should equal the opening balance'); + Assert.AreEqual(-OpeningAmount, TempTrialBalanceData."Net Change", 'Net Change should reverse the opening balance, not inherit a stale net change'); + Assert.AreEqual(0, TempTrialBalanceData.Balance, 'Balance should be zero, not the stale source account balance'); + end; + [Test] procedure QueryPathStartingBalanceIncludesClosingDateEntries() var From bdb3a9fa9cba1811a55ab22cede3567eb31c2404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joshua=20Mart=C3=ADnez=20Pineda?= Date: Fri, 26 Jun 2026 12:07:26 +0200 Subject: [PATCH 2/4] fix for edge case when balance of a posting account at ending date is zero --- .../src/Financials/TrialBalance.Codeunit.al | 53 ++++++++++------ .../src/TrialBalanceExcelReports.Codeunit.al | 63 ++++++++++--------- 2 files changed, 70 insertions(+), 46 deletions(-) diff --git a/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al b/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al index 9a9c858853..7011836c56 100644 --- a/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al +++ b/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al @@ -247,12 +247,9 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (ACY)" := EXRTrialBalanceQuery.ACYAmount; TrialBalanceData."Net Change (Debit) (ACY)" := EXRTrialBalanceQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := EXRTrialBalanceQuery.ACYCreditAmount; - TrialBalanceData.CheckAllZero(); - if not TrialBalanceData."All Zero" then begin - TrialBalanceData.Insert(true); - InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); - InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); - end; + // Insert every combination the query returns, including ones that net to zero at the end date, so their + // debit/credit splits survive for the second pass to adjust. All-zero rows are removed at the end. + TrialBalanceData.Insert(true); end; EXRTrialBalanceQuery.Close(); @@ -264,7 +261,7 @@ codeunit 4410 "Trial Balance" TrialBalanceData.SetRange("Dimension 1 Code", EXRTrialBalanceQuery.DimensionValue1Code); TrialBalanceData.SetRange("Dimension 2 Code", EXRTrialBalanceQuery.DimensionValue2Code); if not TrialBalanceData.FindFirst() then begin - // The combination nets to zero at the end date, so the first pass skipped it as All Zero, but it still has an opening balance here. + // This shouldn't happen now that the first pass inserts every combination with entries up to the ending date, but we Init() and consider it regardless. TrialBalanceData.Init(); TrialBalanceData."G/L Account No." := EXRTrialBalanceQuery.AccountNumber; TrialBalanceData."Dimension 1 Code" := EXRTrialBalanceQuery.DimensionValue1Code; @@ -286,9 +283,9 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (Debit) (ACY)" := TrialBalanceData."Net Change (Debit) (ACY)" - EXRTrialBalanceQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := TrialBalanceData."Net Change (Credit) (ACY)" - EXRTrialBalanceQuery.ACYCreditAmount; TrialBalanceData.Modify(); - InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); - InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); end; + + RemoveAllZeroRowsAndRegisterDimensions(TrialBalanceData, Dimension1Values, Dimension2Values); end; local procedure InsertTrialBalanceFromBUQuery(var Dimension1Values: Record "Dimension Value" temporary; var Dimension2Values: Record "Dimension Value" temporary; var TrialBalanceData: Record "EXR Trial Balance Buffer"; StartDate: Date; EndDate: Date; AccountNoFilter: Text) @@ -317,12 +314,9 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (ACY)" := EXRTrialBalanceBUQuery.ACYAmount; TrialBalanceData."Net Change (Debit) (ACY)" := EXRTrialBalanceBUQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := EXRTrialBalanceBUQuery.ACYCreditAmount; - TrialBalanceData.CheckAllZero(); - if not TrialBalanceData."All Zero" then begin - TrialBalanceData.Insert(true); - InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); - InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); - end; + // Insert every combination the query returns, including ones that net to zero at the end date, so their + // debit/credit splits survive for the second pass to adjust. All-zero rows are removed at the end. + TrialBalanceData.Insert(true); end; EXRTrialBalanceBUQuery.Close(); @@ -335,7 +329,7 @@ codeunit 4410 "Trial Balance" TrialBalanceData.SetRange("Dimension 2 Code", EXRTrialBalanceBUQuery.DimensionValue2Code); TrialBalanceData.SetRange("Business Unit Code", EXRTrialBalanceBUQuery.BusinessUnitCode); if not TrialBalanceData.FindFirst() then begin - // The combination nets to zero at the end date, so the first pass skipped it as All Zero, but it still has an opening balance here. + // This shouldn't happen now that the first pass inserts every combination with entries up to the ending date, but we Init() and consider it regardless. TrialBalanceData.Init(); TrialBalanceData."G/L Account No." := EXRTrialBalanceBUQuery.AccountNumber; TrialBalanceData."Dimension 1 Code" := EXRTrialBalanceBUQuery.DimensionValue1Code; @@ -356,9 +350,32 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (Debit) (ACY)" := TrialBalanceData."Net Change (Debit) (ACY)" - EXRTrialBalanceBUQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := TrialBalanceData."Net Change (Credit) (ACY)" - EXRTrialBalanceBUQuery.ACYCreditAmount; TrialBalanceData.Modify(); - InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); - InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); end; + + RemoveAllZeroRowsAndRegisterDimensions(TrialBalanceData, Dimension1Values, Dimension2Values); + end; + + local procedure RemoveAllZeroRowsAndRegisterDimensions(var TrialBalanceData: Record "EXR Trial Balance Buffer"; var Dimension1Values: Record "Dimension Value" temporary; var Dimension2Values: Record "Dimension Value" temporary) + begin + // The first query pass inserts every combination that has entries (so the debit/credit splits survive the + // second pass), including ones that net to zero. Now that the net change is final, drop the combinations with + // no activity and register the dimension values used by the rows we keep. + TrialBalanceData.Reset(); + if TrialBalanceData.FindSet() then + repeat + TrialBalanceData.CheckAllZero(); + TrialBalanceData.Modify(); + until TrialBalanceData.Next() = 0; + + TrialBalanceData.SetRange("All Zero", true); + TrialBalanceData.DeleteAll(); + TrialBalanceData.SetRange("All Zero"); + + if TrialBalanceData.FindSet() then + repeat + InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); + InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); + until TrialBalanceData.Next() = 0; end; local procedure BuildAccountToTotalsMap(AccountNoFilter: Text; var AccountToTotals: Dictionary of [Code[20], List of [Code[20]]]) diff --git a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al index 593fcab626..987195e62e 100644 --- a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al +++ b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al @@ -674,48 +674,55 @@ codeunit 139544 "Trial Balance Excel Reports" end; [Test] - procedure QueryPathDoesNotInheritStaleAmountsForZeroNetCombination() + procedure QueryPathReportsCorrectDebitCreditSplitsForZeroEndBalance() var - GLAccount: Record "G/L Account"; + GLAccount, KeptAccount, DroppedAccount : Record "G/L Account"; TempDimension1Values, TempDimension2Values : Record "Dimension Value" temporary; TempTrialBalanceData: Record "EXR Trial Balance Buffer"; TrialBalance: Codeunit "Trial Balance"; - ZeroNetAccountNo, StaleSourceAccountNo : Code[20]; - OpeningAmount, StaleBalance : Decimal; - PeriodStart: Date; + OpeningDebit, OffsettingAmount : Decimal; + PriorYear: Integer; begin - // [SCENARIO] A combination that nets to zero at the end date but still has an opening balance is dropped by the first - // query pass (All Zero) and re-created by the second (opening-balance) pass. That re-created record must be - // Init()'d, otherwise it inherits the stale amounts left on the shared buffer record by the previous account. + // [SCENARIO] Combinations that net to zero at the end date are inserted by the first pass (instead of being + // dropped as All Zero) so the second pass can produce correct debit/credit splits. Combinations with no net + // activity at all are then removed, while those with period activity are kept with correct gross columns. Initialize(); - PeriodStart := DMY2Date(1, 1, Date2DMY(WorkDate(), 3)); + PriorYear := Date2DMY(WorkDate(), 3) - 1; + OpeningDebit := 1000; + OffsettingAmount := 500; - // [GIVEN] A zero-net posting account (10000) read first in the second pass, and a stale-source posting account - // (20000) read last in the first pass so it leaves a non-zero balance on the shared buffer record. - ZeroNetAccountNo := CreateGLAccountWithNo('10000', Enum::"G/L Account Type"::Posting, ''); - StaleSourceAccountNo := CreateGLAccountWithNo('20000', Enum::"G/L Account Type"::Posting, ''); + // [GIVEN] An account whose opening debit is fully reversed by a credit within the period (net-zero end, period activity) + CreateGLAccount(KeptAccount); + CreateGLEntryWithAmount(KeptAccount."No.", '', '', '', DMY2Date(15, 6, PriorYear), OpeningDebit); + CreateGLEntryWithAmount(KeptAccount."No.", '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3)), -OpeningDebit); - OpeningAmount := 1000; - StaleBalance := 777; - // [GIVEN] 10000 has an opening balance before the period that is fully reversed within the period -> end balance 0 - CreateGLEntryWithAmount(ZeroNetAccountNo, '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3) - 1), OpeningAmount); - CreateGLEntryWithAmount(ZeroNetAccountNo, '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3)), -OpeningAmount); - // [GIVEN] 20000 has only in-period activity (non-zero end balance, no opening balance), so it is absent from the second pass - CreateGLEntryWithAmount(StaleSourceAccountNo, '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3)), StaleBalance); + // [GIVEN] An account with only offsetting opening turnover and no period activity (net-zero everything) + CreateGLAccount(DroppedAccount); + CreateGLEntryWithAmount(DroppedAccount."No.", '', '', '', DMY2Date(15, 6, PriorYear), OffsettingAmount); + CreateGLEntryWithAmount(DroppedAccount."No.", '', '', '', DMY2Date(15, 6, PriorYear), -OffsettingAmount); // [WHEN] Running the query-based trial balance for the current year - GLAccount.SetRange("Date Filter", PeriodStart, DMY2Date(31, 12, Date2DMY(WorkDate(), 3))); + GLAccount.SetRange("Date Filter", DMY2Date(1, 1, Date2DMY(WorkDate(), 3)), DMY2Date(31, 12, Date2DMY(WorkDate(), 3))); TrialBalance.ConfigureTrialBalance(false, false); TrialBalance.InsertTrialBalanceReportData(GLAccount, TempDimension1Values, TempDimension2Values, TempTrialBalanceData); - // [THEN] The zero-net account reports its own amounts (Starting Balance = opening, Net Change = -opening, Balance = 0), - // not the stale source account's balance carried over from the previous record. + // [THEN] The active combination is kept with correct net AND gross debit/credit columns + TempTrialBalanceData.Reset(); + TempTrialBalanceData.SetRange("G/L Account No.", KeptAccount."No."); + Assert.IsTrue(TempTrialBalanceData.FindFirst(), 'Buffer record should exist; the combination has period activity'); + Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Starting Balance", 'Starting Balance should equal the opening debit'); + Assert.AreEqual(-OpeningDebit, TempTrialBalanceData."Net Change", 'Net Change should reverse the opening'); + Assert.AreEqual(0, TempTrialBalanceData.Balance, 'Balance should net to zero at the end date'); + Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Starting Balance (Debit)", 'Opening debit split'); + Assert.AreEqual(0, TempTrialBalanceData."Net Change (Debit)", 'No period debit turnover'); + Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Net Change (Credit)", 'Period credit turnover equals the reversal'); + Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Balance (Debit)", 'Cumulative debit at the end date'); + Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Balance (Credit)", 'Cumulative credit at the end date'); + + // [THEN] The inactive combination is dropped entirely (previously it surfaced as a phantom stale row) TempTrialBalanceData.Reset(); - TempTrialBalanceData.SetRange("G/L Account No.", ZeroNetAccountNo); - Assert.IsTrue(TempTrialBalanceData.FindFirst(), 'Buffer record should exist for the zero-net account'); - Assert.AreEqual(OpeningAmount, TempTrialBalanceData."Starting Balance", 'Starting Balance should equal the opening balance'); - Assert.AreEqual(-OpeningAmount, TempTrialBalanceData."Net Change", 'Net Change should reverse the opening balance, not inherit a stale net change'); - Assert.AreEqual(0, TempTrialBalanceData.Balance, 'Balance should be zero, not the stale source account balance'); + TempTrialBalanceData.SetRange("G/L Account No.", DroppedAccount."No."); + Assert.IsTrue(TempTrialBalanceData.IsEmpty(), 'A combination with no net activity should be dropped'); end; [Test] From af5c0e49428ee74b03e6364ac682edc82c7ebf46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joshua=20Mart=C3=ADnez=20Pineda?= Date: Fri, 26 Jun 2026 13:02:36 +0200 Subject: [PATCH 3/4] fix for edge case when balance of a posting account at ending date is zero --- .../src/Financials/TrialBalance.Codeunit.al | 43 +++++----------- .../src/TrialBalanceExcelReports.Codeunit.al | 51 +++++++++---------- 2 files changed, 35 insertions(+), 59 deletions(-) diff --git a/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al b/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al index 7011836c56..32d6a211fb 100644 --- a/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al +++ b/src/Apps/W1/ExcelReports/App/src/Financials/TrialBalance.Codeunit.al @@ -247,9 +247,11 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (ACY)" := EXRTrialBalanceQuery.ACYAmount; TrialBalanceData."Net Change (Debit) (ACY)" := EXRTrialBalanceQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := EXRTrialBalanceQuery.ACYCreditAmount; - // Insert every combination the query returns, including ones that net to zero at the end date, so their - // debit/credit splits survive for the second pass to adjust. All-zero rows are removed at the end. + // Every combination the query returns has entries, so it represents real activity and is kept even when it + // nets to zero. The second pass adjusts any that also have an opening balance. TrialBalanceData.Insert(true); + InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); + InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); end; EXRTrialBalanceQuery.Close(); @@ -283,9 +285,9 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (Debit) (ACY)" := TrialBalanceData."Net Change (Debit) (ACY)" - EXRTrialBalanceQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := TrialBalanceData."Net Change (Credit) (ACY)" - EXRTrialBalanceQuery.ACYCreditAmount; TrialBalanceData.Modify(); + InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); + InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); end; - - RemoveAllZeroRowsAndRegisterDimensions(TrialBalanceData, Dimension1Values, Dimension2Values); end; local procedure InsertTrialBalanceFromBUQuery(var Dimension1Values: Record "Dimension Value" temporary; var Dimension2Values: Record "Dimension Value" temporary; var TrialBalanceData: Record "EXR Trial Balance Buffer"; StartDate: Date; EndDate: Date; AccountNoFilter: Text) @@ -314,9 +316,11 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (ACY)" := EXRTrialBalanceBUQuery.ACYAmount; TrialBalanceData."Net Change (Debit) (ACY)" := EXRTrialBalanceBUQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := EXRTrialBalanceBUQuery.ACYCreditAmount; - // Insert every combination the query returns, including ones that net to zero at the end date, so their - // debit/credit splits survive for the second pass to adjust. All-zero rows are removed at the end. + // Every combination the query returns has entries, so it represents real activity and is kept even when it + // nets to zero. The second pass adjusts any that also have an opening balance. TrialBalanceData.Insert(true); + InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); + InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); end; EXRTrialBalanceBUQuery.Close(); @@ -350,32 +354,9 @@ codeunit 4410 "Trial Balance" TrialBalanceData."Net Change (Debit) (ACY)" := TrialBalanceData."Net Change (Debit) (ACY)" - EXRTrialBalanceBUQuery.ACYDebitAmount; TrialBalanceData."Net Change (Credit) (ACY)" := TrialBalanceData."Net Change (Credit) (ACY)" - EXRTrialBalanceBUQuery.ACYCreditAmount; TrialBalanceData.Modify(); + InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); + InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); end; - - RemoveAllZeroRowsAndRegisterDimensions(TrialBalanceData, Dimension1Values, Dimension2Values); - end; - - local procedure RemoveAllZeroRowsAndRegisterDimensions(var TrialBalanceData: Record "EXR Trial Balance Buffer"; var Dimension1Values: Record "Dimension Value" temporary; var Dimension2Values: Record "Dimension Value" temporary) - begin - // The first query pass inserts every combination that has entries (so the debit/credit splits survive the - // second pass), including ones that net to zero. Now that the net change is final, drop the combinations with - // no activity and register the dimension values used by the rows we keep. - TrialBalanceData.Reset(); - if TrialBalanceData.FindSet() then - repeat - TrialBalanceData.CheckAllZero(); - TrialBalanceData.Modify(); - until TrialBalanceData.Next() = 0; - - TrialBalanceData.SetRange("All Zero", true); - TrialBalanceData.DeleteAll(); - TrialBalanceData.SetRange("All Zero"); - - if TrialBalanceData.FindSet() then - repeat - InsertUsedDimensionValue(1, TrialBalanceData."Dimension 1 Code", Dimension1Values); - InsertUsedDimensionValue(2, TrialBalanceData."Dimension 2 Code", Dimension2Values); - until TrialBalanceData.Next() = 0; end; local procedure BuildAccountToTotalsMap(AccountNoFilter: Text; var AccountToTotals: Dictionary of [Code[20], List of [Code[20]]]) diff --git a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al index 987195e62e..7689e60293 100644 --- a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al +++ b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al @@ -641,24 +641,27 @@ codeunit 139544 "Trial Balance Excel Reports" end; [Test] - procedure QueryPathSkipsAllZeroRecords() + procedure QueryPathIncludesAccountsThatNetToZero() var GLAccount: Record "G/L Account"; TempDimension1Values, TempDimension2Values : Record "Dimension Value" temporary; TrialBalanceData: Record "EXR Trial Balance Buffer"; TrialBalance: Codeunit "Trial Balance"; ZeroAccount, NonZeroAccount : Code[20]; + GrossAmount: Decimal; begin - // [SCENARIO] Accounts with entries that sum to zero are not included in the buffer. - // [GIVEN] One account with cancelling entries (net zero) and another with a non-zero balance + // [SCENARIO] Accounts that have entries are included even when they net to zero. The query only returns + // accounts with activity, so a zero net change still represents real (offsetting) turnover worth showing. + // [GIVEN] One account with cancelling entries (net zero, gross turnover) and another with a non-zero balance Initialize(); LibraryERM.CreateGLAccount(GLAccount); ZeroAccount := GLAccount."No."; LibraryERM.CreateGLAccount(GLAccount); NonZeroAccount := GLAccount."No."; - CreateGLEntryWithAmount(ZeroAccount, '', '', '', WorkDate(), 500); - CreateGLEntryWithAmount(ZeroAccount, '', '', '', WorkDate(), -500); + GrossAmount := 500; + CreateGLEntryWithAmount(ZeroAccount, '', '', '', WorkDate(), GrossAmount); + CreateGLEntryWithAmount(ZeroAccount, '', '', '', WorkDate(), -GrossAmount); CreateGLEntryWithAmount(NonZeroAccount, '', '', '', WorkDate(), 100); // [WHEN] Running the trial balance @@ -667,49 +670,46 @@ codeunit 139544 "Trial Balance Excel Reports" TrialBalance.ConfigureTrialBalance(false, false); TrialBalance.InsertTrialBalanceReportData(GLAccount, TempDimension1Values, TempDimension2Values, TrialBalanceData); - // [THEN] Only the non-zero account appears - Assert.AreEqual(1, TrialBalanceData.Count(), 'Only the non-zero account should be in the buffer'); - TrialBalanceData.FindFirst(); - Assert.AreEqual(NonZeroAccount, TrialBalanceData."G/L Account No.", 'The non-zero account should be the one returned'); + // [THEN] Both accounts are in the buffer + Assert.AreEqual(2, TrialBalanceData.Count(), 'Both accounts with entries should be in the buffer'); + // [THEN] The net-zero account is present with zero net change and balance, but its gross turnover is reported + TrialBalanceData.SetRange("G/L Account No.", ZeroAccount); + Assert.IsTrue(TrialBalanceData.FindFirst(), 'The net-zero account should be included'); + Assert.AreEqual(0, TrialBalanceData."Net Change", 'Net Change should be zero'); + Assert.AreEqual(0, TrialBalanceData.Balance, 'Balance should be zero'); + Assert.AreEqual(GrossAmount, TrialBalanceData."Net Change (Debit)", 'Gross debit turnover should be reported'); + Assert.AreEqual(GrossAmount, TrialBalanceData."Net Change (Credit)", 'Gross credit turnover should be reported'); end; [Test] procedure QueryPathReportsCorrectDebitCreditSplitsForZeroEndBalance() var - GLAccount, KeptAccount, DroppedAccount : Record "G/L Account"; + GLAccount, KeptAccount : Record "G/L Account"; TempDimension1Values, TempDimension2Values : Record "Dimension Value" temporary; TempTrialBalanceData: Record "EXR Trial Balance Buffer"; TrialBalance: Codeunit "Trial Balance"; - OpeningDebit, OffsettingAmount : Decimal; + OpeningDebit: Decimal; PriorYear: Integer; begin - // [SCENARIO] Combinations that net to zero at the end date are inserted by the first pass (instead of being - // dropped as All Zero) so the second pass can produce correct debit/credit splits. Combinations with no net - // activity at all are then removed, while those with period activity are kept with correct gross columns. + // [SCENARIO] A combination that nets to zero at the end date but has period activity keeps correct debit/credit + // splits: the first pass inserts it (carrying the end-date totals) so the second pass can subtract the opening. Initialize(); PriorYear := Date2DMY(WorkDate(), 3) - 1; OpeningDebit := 1000; - OffsettingAmount := 500; // [GIVEN] An account whose opening debit is fully reversed by a credit within the period (net-zero end, period activity) CreateGLAccount(KeptAccount); CreateGLEntryWithAmount(KeptAccount."No.", '', '', '', DMY2Date(15, 6, PriorYear), OpeningDebit); CreateGLEntryWithAmount(KeptAccount."No.", '', '', '', DMY2Date(15, 6, Date2DMY(WorkDate(), 3)), -OpeningDebit); - // [GIVEN] An account with only offsetting opening turnover and no period activity (net-zero everything) - CreateGLAccount(DroppedAccount); - CreateGLEntryWithAmount(DroppedAccount."No.", '', '', '', DMY2Date(15, 6, PriorYear), OffsettingAmount); - CreateGLEntryWithAmount(DroppedAccount."No.", '', '', '', DMY2Date(15, 6, PriorYear), -OffsettingAmount); - // [WHEN] Running the query-based trial balance for the current year GLAccount.SetRange("Date Filter", DMY2Date(1, 1, Date2DMY(WorkDate(), 3)), DMY2Date(31, 12, Date2DMY(WorkDate(), 3))); TrialBalance.ConfigureTrialBalance(false, false); TrialBalance.InsertTrialBalanceReportData(GLAccount, TempDimension1Values, TempDimension2Values, TempTrialBalanceData); - // [THEN] The active combination is kept with correct net AND gross debit/credit columns - TempTrialBalanceData.Reset(); + // [THEN] The combination is present with correct net AND gross debit/credit columns TempTrialBalanceData.SetRange("G/L Account No.", KeptAccount."No."); - Assert.IsTrue(TempTrialBalanceData.FindFirst(), 'Buffer record should exist; the combination has period activity'); + Assert.IsTrue(TempTrialBalanceData.FindFirst(), 'Buffer record should exist for the net-zero-at-end combination'); Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Starting Balance", 'Starting Balance should equal the opening debit'); Assert.AreEqual(-OpeningDebit, TempTrialBalanceData."Net Change", 'Net Change should reverse the opening'); Assert.AreEqual(0, TempTrialBalanceData.Balance, 'Balance should net to zero at the end date'); @@ -718,11 +718,6 @@ codeunit 139544 "Trial Balance Excel Reports" Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Net Change (Credit)", 'Period credit turnover equals the reversal'); Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Balance (Debit)", 'Cumulative debit at the end date'); Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Balance (Credit)", 'Cumulative credit at the end date'); - - // [THEN] The inactive combination is dropped entirely (previously it surfaced as a phantom stale row) - TempTrialBalanceData.Reset(); - TempTrialBalanceData.SetRange("G/L Account No.", DroppedAccount."No."); - Assert.IsTrue(TempTrialBalanceData.IsEmpty(), 'A combination with no net activity should be dropped'); end; [Test] From 0a63b1cafb92d4d6898f0ea184a72e31743c9921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joshua=20Mart=C3=ADnez=20Pineda?= Date: Fri, 26 Jun 2026 13:57:47 +0200 Subject: [PATCH 4/4] Fix AA0237: rename non-temporary TempTrialBalanceData to TrialBalanceData The buffer parameter of InsertTrialBalanceReportData is non-temporary, so the test's local record cannot be temporary; AA0237 forbids the Temp prefix on a non-temporary variable. Rename to TrialBalanceData to match the rest of the file. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/TrialBalanceExcelReports.Codeunit.al | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al index 7689e60293..5a4f6c2a5e 100644 --- a/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al +++ b/src/Apps/W1/ExcelReports/Test/src/TrialBalanceExcelReports.Codeunit.al @@ -686,7 +686,7 @@ codeunit 139544 "Trial Balance Excel Reports" var GLAccount, KeptAccount : Record "G/L Account"; TempDimension1Values, TempDimension2Values : Record "Dimension Value" temporary; - TempTrialBalanceData: Record "EXR Trial Balance Buffer"; + TrialBalanceData: Record "EXR Trial Balance Buffer"; TrialBalance: Codeunit "Trial Balance"; OpeningDebit: Decimal; PriorYear: Integer; @@ -705,19 +705,19 @@ codeunit 139544 "Trial Balance Excel Reports" // [WHEN] Running the query-based trial balance for the current year GLAccount.SetRange("Date Filter", DMY2Date(1, 1, Date2DMY(WorkDate(), 3)), DMY2Date(31, 12, Date2DMY(WorkDate(), 3))); TrialBalance.ConfigureTrialBalance(false, false); - TrialBalance.InsertTrialBalanceReportData(GLAccount, TempDimension1Values, TempDimension2Values, TempTrialBalanceData); + TrialBalance.InsertTrialBalanceReportData(GLAccount, TempDimension1Values, TempDimension2Values, TrialBalanceData); // [THEN] The combination is present with correct net AND gross debit/credit columns - TempTrialBalanceData.SetRange("G/L Account No.", KeptAccount."No."); - Assert.IsTrue(TempTrialBalanceData.FindFirst(), 'Buffer record should exist for the net-zero-at-end combination'); - Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Starting Balance", 'Starting Balance should equal the opening debit'); - Assert.AreEqual(-OpeningDebit, TempTrialBalanceData."Net Change", 'Net Change should reverse the opening'); - Assert.AreEqual(0, TempTrialBalanceData.Balance, 'Balance should net to zero at the end date'); - Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Starting Balance (Debit)", 'Opening debit split'); - Assert.AreEqual(0, TempTrialBalanceData."Net Change (Debit)", 'No period debit turnover'); - Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Net Change (Credit)", 'Period credit turnover equals the reversal'); - Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Balance (Debit)", 'Cumulative debit at the end date'); - Assert.AreEqual(OpeningDebit, TempTrialBalanceData."Balance (Credit)", 'Cumulative credit at the end date'); + TrialBalanceData.SetRange("G/L Account No.", KeptAccount."No."); + Assert.IsTrue(TrialBalanceData.FindFirst(), 'Buffer record should exist for the net-zero-at-end combination'); + Assert.AreEqual(OpeningDebit, TrialBalanceData."Starting Balance", 'Starting Balance should equal the opening debit'); + Assert.AreEqual(-OpeningDebit, TrialBalanceData."Net Change", 'Net Change should reverse the opening'); + Assert.AreEqual(0, TrialBalanceData.Balance, 'Balance should net to zero at the end date'); + Assert.AreEqual(OpeningDebit, TrialBalanceData."Starting Balance (Debit)", 'Opening debit split'); + Assert.AreEqual(0, TrialBalanceData."Net Change (Debit)", 'No period debit turnover'); + Assert.AreEqual(OpeningDebit, TrialBalanceData."Net Change (Credit)", 'Period credit turnover equals the reversal'); + Assert.AreEqual(OpeningDebit, TrialBalanceData."Balance (Debit)", 'Cumulative debit at the end date'); + Assert.AreEqual(OpeningDebit, TrialBalanceData."Balance (Credit)", 'Cumulative credit at the end date'); end; [Test]