fix: budget: Make sure boring parents of unbudgeted accounts are not

elided if they have a budget. (#1800)

This only affects calls with --budget and -E, but not with --no-elide.
This commit is contained in:
Stephen Morgan 2022-01-06 19:18:04 +01:00 committed by Simon Michael
parent d9b0184720
commit 8cd9e81c34
3 changed files with 61 additions and 30 deletions

View File

@ -82,10 +82,12 @@ budgetReport rspec bopts reportspan j = dbg4 "sortedbudgetreport" budgetreport
jperiodictxns j
actualj = journalWithBudgetAccountNames budgetedaccts showunbudgeted j
budgetj = journalAddBudgetGoalTransactions bopts ropts reportspan j
actualreport@(PeriodicReport actualspans _ _) =
dbg5 "actualreport" $ multiBalanceReport rspec{_rsReportOpts=ropts{empty_=True}} actualj
priceoracle = journalPriceOracle (infer_prices_ ropts) j
budgetgoalreport@(PeriodicReport _ budgetgoalitems budgetgoaltotals) =
dbg5 "budgetgoalreport" $ multiBalanceReport rspec{_rsReportOpts=ropts{empty_=True}} budgetj
dbg5 "budgetgoalreport" $ multiBalanceReportWith rspec{_rsReportOpts=ropts{empty_=True}} budgetj priceoracle mempty
budgetedacctsseen = S.fromList $ map prrFullName budgetgoalitems
actualreport@(PeriodicReport actualspans _ _) =
dbg5 "actualreport" $ multiBalanceReportWith rspec{_rsReportOpts=ropts{empty_=True}} actualj priceoracle budgetedacctsseen
budgetgoalreport'
-- If no interval is specified:
-- budgetgoalreport's span might be shorter actualreport's due to periodic txns;

View File

@ -47,6 +47,8 @@ import qualified Data.Map as M
import Data.Maybe (fromMaybe, isJust, mapMaybe)
import Data.Ord (Down(..))
import Data.Semigroup (sconcat)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Time.Calendar (fromGregorian)
import Safe (lastDef, minimumMay)
@ -102,16 +104,16 @@ type ClippedAccountName = AccountName
-- by the balance command (in multiperiod mode) and (via compoundBalanceReport)
-- by the bs/cf/is commands.
multiBalanceReport :: ReportSpec -> Journal -> MultiBalanceReport
multiBalanceReport rspec j = multiBalanceReportWith rspec j (journalPriceOracle infer j)
multiBalanceReport rspec j = multiBalanceReportWith rspec j (journalPriceOracle infer j) mempty
where infer = infer_prices_ $ _rsReportOpts rspec
-- | A helper for multiBalanceReport. This one takes an extra argument,
-- a PriceOracle to be used for looking up market prices. Commands which
-- run multiple reports (bs etc.) can generate the price oracle just
-- once for efficiency, passing it to each report by calling this
-- function directly.
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith rspec' j priceoracle = report
-- | A helper for multiBalanceReport. This one takes some extra arguments,
-- a 'PriceOracle' to be used for looking up market prices, and a set of
-- 'AccountName's which should not be elided. Commands which run multiple
-- reports (bs etc.) can generate the price oracle just once for efficiency,
-- passing it to each report by calling this function directly.
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> Set AccountName -> MultiBalanceReport
multiBalanceReportWith rspec' j priceoracle unelidableaccts = report
where
-- Queries, report/column dates.
reportspan = dbg3 "reportspan" $ reportSpan j rspec'
@ -127,7 +129,7 @@ multiBalanceReportWith rspec' j priceoracle = report
-- Generate and postprocess the report, negating balances and taking percentages if needed
report = dbg4 "multiBalanceReportWith" $
generateMultiBalanceReport rspec j priceoracle colps startbals
generateMultiBalanceReport rspec j priceoracle unelidableaccts colps startbals
-- | Generate a compound balance report from a list of CBCSubreportSpec. This
-- shares postings between the subreports.
@ -159,7 +161,7 @@ compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr
( cbcsubreporttitle
-- Postprocess the report, negating balances and taking percentages if needed
, cbcsubreporttransform $
generateMultiBalanceReport rspecsub j priceoracle colps' startbals'
generateMultiBalanceReport rspecsub j priceoracle mempty colps' startbals'
, cbcsubreportincreasestotal
)
where
@ -343,17 +345,17 @@ calculateReportMatrix rspec@ReportSpec{_rsReportOpts=ropts} j priceoracle startb
-- | Lay out a set of postings grouped by date span into a regular matrix with rows
-- given by AccountName and columns by DateSpan, then generate a MultiBalanceReport
-- from the columns.
generateMultiBalanceReport :: ReportSpec -> Journal -> PriceOracle
generateMultiBalanceReport :: ReportSpec -> Journal -> PriceOracle -> Set AccountName
-> [(DateSpan, [Posting])] -> HashMap AccountName Account
-> MultiBalanceReport
generateMultiBalanceReport rspec@ReportSpec{_rsReportOpts=ropts} j priceoracle colps startbals =
generateMultiBalanceReport rspec@ReportSpec{_rsReportOpts=ropts} j priceoracle unelidableaccts colps startbals =
report
where
-- Process changes into normal, cumulative, or historical amounts, plus value them
matrix = calculateReportMatrix rspec j priceoracle startbals colps
-- All account names that will be displayed, possibly depth-clipped.
displaynames = dbg5 "displaynames" $ displayedAccounts rspec matrix
displaynames = dbg5 "displaynames" $ displayedAccounts rspec unelidableaccts matrix
-- All the rows of the report.
rows = dbg5 "rows" . (if invert_ ropts then map (fmap maNegate) else id) -- Negate amounts if applicable
@ -394,9 +396,11 @@ buildReportRows ropts displaynames =
-- | Calculate accounts which are to be displayed in the report, as well as
-- their name and depth
displayedAccounts :: ReportSpec -> HashMap AccountName (Map DateSpan Account)
displayedAccounts :: ReportSpec
-> Set AccountName
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName DisplayName
displayedAccounts ReportSpec{_rsQuery=query,_rsReportOpts=ropts} valuedaccts
displayedAccounts ReportSpec{_rsQuery=query,_rsReportOpts=ropts} unelidableaccts valuedaccts
| depth == 0 = HM.singleton "..." $ DisplayName "..." "..." 1
| otherwise = HM.mapWithKey (\a _ -> displayedName a) displayedAccts
where
@ -421,7 +425,8 @@ displayedAccounts ReportSpec{_rsQuery=query,_rsReportOpts=ropts} valuedaccts
-- Accounts interesting for their own sake
isInteresting name amts =
d <= depth -- Throw out anything too deep
&& ( (empty_ ropts && keepWhenEmpty amts) -- Keep empty accounts when called with --empty
&& ( name `Set.member` unelidableaccts -- Unelidable accounts should be kept unless too deep
||(empty_ ropts && keepWhenEmpty amts) -- Keep empty accounts when called with --empty
|| not (isZeroRow balance amts) -- Keep everything with a non-zero balance in the row
)
where

View File

@ -409,7 +409,31 @@ Budget performance in 2019-01-01..2019-01-03:
-------------------++---------------------------
|| 0 [ 0]
# 20. Subaccounts + nested budgets
# 20. Also should work when there are no postings directly in budgeted parents (#1800)
$ hledger -f- bal -e 2019-01-02 --budget -E
Budget performance in 2019-01-01:
|| 2019-01-01
===============================++===========================
expenses:personal || $10.00 [1% of $1,000.00]
expenses:personal:electronics || $10.00
liabilities || $-10.00 [1% of $-1000.00]
-------------------------------++---------------------------
|| 0 [ 0]
# 21. Also should work when there are no postings directly in budgeted parents with --tree (#1800)
$ hledger -f- bal -e 2019-01-02 --budget --tree -E
Budget performance in 2019-01-01:
|| 2019-01-01
===================++===========================
expenses:personal || $10.00 [1% of $1,000.00]
electronics || $10.00
liabilities || $-10.00 [1% of $-1000.00]
-------------------++---------------------------
|| 0 [ 0]
# 22. Subaccounts + nested budgets
<
~ monthly from 2019/01
expenses:personal $1,000.00
@ -439,7 +463,7 @@ Budget performance in 2019-01-01..2019-01-03:
-------------------------------++----------------------------
|| 0 [ 0]
# 21.
# 23.
$ hledger -f- bal --budget -E
Budget performance in 2019-01-01..2019-01-03:
@ -452,7 +476,7 @@ Budget performance in 2019-01-01..2019-01-03:
----------------------------------------++----------------------------
|| 0 [ 0]
# 22.
# 24.
$ hledger -f- bal --budget --tree
Budget performance in 2019-01-01..2019-01-03:
@ -464,7 +488,7 @@ Budget performance in 2019-01-01..2019-01-03:
-------------------++----------------------------
|| 0 [ 0]
# 23.
# 25.
$ hledger -f- bal --budget --tree -E
Budget performance in 2019-01-01..2019-01-03:
@ -477,7 +501,7 @@ Budget performance in 2019-01-01..2019-01-03:
-------------------++----------------------------
|| 0 [ 0]
## 24. Zero budget == no budget
# 26. Zero budget == no budget
<
~ monthly from 2019-01
expenses:bills $100 ; bills has a $100 budget of its own, separate from subaccounts
@ -515,7 +539,7 @@ Budget performance in 2019-01-01..2019-01-02:
------------------++------------------------
|| 0 [ 0]
# 25. -E shows d and e
# 27. -E shows d and e
$ hledger bal -f- --budget -E
Budget performance in 2019-01-01..2019-01-02:
@ -532,7 +556,7 @@ Budget performance in 2019-01-01..2019-01-02:
------------------++------------------------
|| 0 [ 0]
# 26. The totals row shows correct totals.
# 28. The totals row shows correct totals.
# -T/--total and -A/--average adds those columns.
$ hledger bal -f- --budget -TA not:income
Budget performance in 2019-01-01..2019-01-02:
@ -547,7 +571,7 @@ Budget performance in 2019-01-01..2019-01-02:
------------------++--------------------------------------------------------------
|| $80 [22% of $370] $80 [22% of $370] $80 [22% of $370]
# 27. CSV output works.
# 29. CSV output works.
$ hledger bal -f- --budget -TA not:income -O csv
"Account","2019-01-01..2019-01-02","budget","Total","budget","Average","budget"
"expenses:bills","$80","$370","$80","$370","$80","$370"
@ -557,7 +581,7 @@ $ hledger bal -f- --budget -TA not:income -O csv
"expenses:bills:f","$10","0","$10","0","$10","0"
"Total:","$80","$370","$80","$370","$80","$370"
# 28. You would expect this to show a budget goal in jan, feb, mar.
# 30. You would expect this to show a budget goal in jan, feb, mar.
# But by the usual report date logic, which picks the oldest and newest
# transaction date (1/15 and 3/15) as start and end date by default,
# and since "monthly" generates transactions on the 1st,
@ -585,7 +609,7 @@ Budget performance in 2020Q1:
---------------++-----------------------------------------------------------
|| 0 [ 0% of $500] 0 [0% of $500] 0 [ 0% of $500]
# 29. Specifying the report period works around it.
# 31. Specifying the report period works around it.
$ hledger -f- bal --budget -M date:2020q1
Budget performance in 2020Q1:
@ -596,7 +620,7 @@ Budget performance in 2020Q1:
---------------++-----------------------------------------------------------
|| 0 [ 0% of $500] 0 [0% of $500] 0 [ 0% of $500]
# 30. Select from multiple named budgets.
# 32. Select from multiple named budgets.
<
~ weekly weekly budget
(aaa) 1