From 62351dd329a60cf9962fb3da4a12c898f2a8a9e0 Mon Sep 17 00:00:00 2001 From: Stephen Morgan Date: Thu, 16 Sep 2021 22:18:27 +1000 Subject: [PATCH] fix: bal: Make sure boring parent accounts in compound balance reports include historical postings when account declarations have undeclared parents. (#1698) --- .../Hledger/Reports/MultiBalanceReport.hs | 36 +++++++++++-------- hledger/test/balancesheet.test | 35 ++++++++++++++++++ 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/hledger-lib/Hledger/Reports/MultiBalanceReport.hs b/hledger-lib/Hledger/Reports/MultiBalanceReport.hs index f2ba236df..7754840a9 100644 --- a/hledger-lib/Hledger/Reports/MultiBalanceReport.hs +++ b/hledger-lib/Hledger/Reports/MultiBalanceReport.hs @@ -25,7 +25,8 @@ module Hledger.Reports.MultiBalanceReport ( makeReportQuery, getPostingsByColumn, getPostings, - startingBalances, + startingPostings, + startingBalancesFromPostings, generateMultiBalanceReport, balanceReportTableAsText, @@ -121,7 +122,8 @@ multiBalanceReportWith rspec' j priceoracle = report -- The matched accounts with a starting balance. All of these should appear -- in the report, even if they have no postings during the report period. - startbals = dbg5 "startbals" $ startingBalances rspec j priceoracle reportspan + startbals = dbg5 "startbals" . startingBalancesFromPostings rspec j priceoracle + $ startingPostings rspec j priceoracle reportspan -- Generate and postprocess the report, negating balances and taking percentages if needed report = dbg4 "multiBalanceReportWith" $ @@ -147,9 +149,9 @@ compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr -- Group postings into their columns. colps = dbg5 "colps" $ getPostingsByColumn rspec j priceoracle reportspan - -- The matched accounts with a starting balance. All of these should appear + -- The matched postings with a starting balance. All of these should appear -- in the report, even if they have no postings during the report period. - startbals = dbg5 "startbals" $ startingBalances rspec j priceoracle reportspan + startps = dbg5 "startps" $ startingPostings rspec j priceoracle reportspan subreports = map generateSubreport subreportspecs where @@ -163,7 +165,8 @@ compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr where -- Filter the column postings according to each subreport colps' = filter (matchesPosting q) <$> colps - startbals' = HM.filterWithKey (\k _ -> matchesAccount q k) startbals + -- We need to filter historical postings directly, rather than their accumulated balances. (#1698) + startbals' = startingBalancesFromPostings rspec j priceoracle $ filter (matchesPosting q) startps ropts = cbcsubreportoptions $ _rsReportOpts rspec q = cbcsubreportquery j @@ -180,28 +183,31 @@ compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr cbr = CompoundPeriodicReport "" (M.keys colps) subreports overalltotals +-- | Calculate starting balances from postings, if needed for -H. +startingBalancesFromPostings :: ReportSpec -> Journal -> PriceOracle -> [Posting] + -> HashMap AccountName Account +startingBalancesFromPostings rspec j priceoracle = + fmap (M.findWithDefault nullacct emptydatespan) + . calculateReportMatrix rspec j priceoracle mempty + . M.singleton emptydatespan --- | Calculate starting balances, if needed for -H +-- | Postings needed to calculate starting balances. -- -- Balances at report start date, from all earlier postings which otherwise match the query. -- These balances are unvalued. -- TODO: Do we want to check whether to bother calculating these? isHistorical -- and startDate is not nothing, otherwise mempty? This currently gives a -- failure with some totals which are supposed to be 0 being blank. -startingBalances :: ReportSpec -> Journal -> PriceOracle -> DateSpan -> HashMap AccountName Account -startingBalances rspec@ReportSpec{_rsQuery=query,_rsReportOpts=ropts} j priceoracle reportspan = - fmap (M.findWithDefault nullacct precedingspan) acctmap +startingPostings :: ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting] +startingPostings rspec@ReportSpec{_rsQuery=query,_rsReportOpts=ropts} j priceoracle reportspan = + map fst $ getPostings rspec' j priceoracle where - acctmap = calculateReportMatrix rspec' j priceoracle mempty - . M.singleton precedingspan . map fst $ getPostings rspec' j priceoracle - rspec' = rspec{_rsQuery=startbalq,_rsReportOpts=ropts'} -- If we're re-valuing every period, we need to have the unvalued start -- balance, so we can do it ourselves later. ropts' = case value_ ropts of - Just (AtEnd _) -> ropts''{value_=Nothing} - _ -> ropts'' - where ropts'' = ropts{period_=precedingperiod, no_elide_=accountlistmode_ ropts == ALTree} + Just (AtEnd _) -> ropts{period_=precedingperiod, value_=Nothing} + _ -> ropts{period_=precedingperiod} -- q projected back before the report start date. -- When there's no report start date, in case there are future txns (the hledger-ui case above), diff --git a/hledger/test/balancesheet.test b/hledger/test/balancesheet.test index a900aa7c8..4b8f79c89 100644 --- a/hledger/test/balancesheet.test +++ b/hledger/test/balancesheet.test @@ -327,3 +327,38 @@ $ hledger -f - balancesheet --tree --output-format=csv --drop 1 "Liabilities","" "total" "Net:","$1" + +# 14. When declaring account types, we want boring parents to include their +# interesting children, including their historical postings. (#1698) +< +account assets:foobar:reserve ; type: Cash +account assets:foobar:current ; type: Cash +account assets:receivable:anon ; type: Asset + +2018-12-06 + assets:foobar:current 1 A + expenses:example + +2019-01-04 + assets:foobar:current 1 A + expenses:example + +$ hledger -f - balancesheet --tree --no-elide --begin 2019-01-01 +Balance Sheet 2019-01-04 + + || 2019-01-04 +=============++============ + Assets || +-------------++------------ + assets || 2 A + foobar || 2 A + current || 2 A +-------------++------------ + || 2 A +=============++============ + Liabilities || +-------------++------------ +-------------++------------ + || +=============++============ + Net: || 2 A