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:
parent
d9b0184720
commit
8cd9e81c34
@ -82,10 +82,12 @@ budgetReport rspec bopts reportspan j = dbg4 "sortedbudgetreport" budgetreport
|
|||||||
jperiodictxns j
|
jperiodictxns j
|
||||||
actualj = journalWithBudgetAccountNames budgetedaccts showunbudgeted j
|
actualj = journalWithBudgetAccountNames budgetedaccts showunbudgeted j
|
||||||
budgetj = journalAddBudgetGoalTransactions bopts ropts reportspan j
|
budgetj = journalAddBudgetGoalTransactions bopts ropts reportspan j
|
||||||
actualreport@(PeriodicReport actualspans _ _) =
|
priceoracle = journalPriceOracle (infer_prices_ ropts) j
|
||||||
dbg5 "actualreport" $ multiBalanceReport rspec{_rsReportOpts=ropts{empty_=True}} actualj
|
|
||||||
budgetgoalreport@(PeriodicReport _ budgetgoalitems budgetgoaltotals) =
|
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'
|
budgetgoalreport'
|
||||||
-- If no interval is specified:
|
-- If no interval is specified:
|
||||||
-- budgetgoalreport's span might be shorter actualreport's due to periodic txns;
|
-- budgetgoalreport's span might be shorter actualreport's due to periodic txns;
|
||||||
|
|||||||
@ -47,6 +47,8 @@ import qualified Data.Map as M
|
|||||||
import Data.Maybe (fromMaybe, isJust, mapMaybe)
|
import Data.Maybe (fromMaybe, isJust, mapMaybe)
|
||||||
import Data.Ord (Down(..))
|
import Data.Ord (Down(..))
|
||||||
import Data.Semigroup (sconcat)
|
import Data.Semigroup (sconcat)
|
||||||
|
import Data.Set (Set)
|
||||||
|
import qualified Data.Set as Set
|
||||||
import Data.Time.Calendar (fromGregorian)
|
import Data.Time.Calendar (fromGregorian)
|
||||||
import Safe (lastDef, minimumMay)
|
import Safe (lastDef, minimumMay)
|
||||||
|
|
||||||
@ -102,16 +104,16 @@ type ClippedAccountName = AccountName
|
|||||||
-- by the balance command (in multiperiod mode) and (via compoundBalanceReport)
|
-- by the balance command (in multiperiod mode) and (via compoundBalanceReport)
|
||||||
-- by the bs/cf/is commands.
|
-- by the bs/cf/is commands.
|
||||||
multiBalanceReport :: ReportSpec -> Journal -> MultiBalanceReport
|
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
|
where infer = infer_prices_ $ _rsReportOpts rspec
|
||||||
|
|
||||||
-- | A helper for multiBalanceReport. This one takes an extra argument,
|
-- | A helper for multiBalanceReport. This one takes some extra arguments,
|
||||||
-- a PriceOracle to be used for looking up market prices. Commands which
|
-- a 'PriceOracle' to be used for looking up market prices, and a set of
|
||||||
-- run multiple reports (bs etc.) can generate the price oracle just
|
-- 'AccountName's which should not be elided. Commands which run multiple
|
||||||
-- once for efficiency, passing it to each report by calling this
|
-- reports (bs etc.) can generate the price oracle just once for efficiency,
|
||||||
-- function directly.
|
-- passing it to each report by calling this function directly.
|
||||||
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport
|
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> Set AccountName -> MultiBalanceReport
|
||||||
multiBalanceReportWith rspec' j priceoracle = report
|
multiBalanceReportWith rspec' j priceoracle unelidableaccts = report
|
||||||
where
|
where
|
||||||
-- Queries, report/column dates.
|
-- Queries, report/column dates.
|
||||||
reportspan = dbg3 "reportspan" $ reportSpan j rspec'
|
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
|
-- Generate and postprocess the report, negating balances and taking percentages if needed
|
||||||
report = dbg4 "multiBalanceReportWith" $
|
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
|
-- | Generate a compound balance report from a list of CBCSubreportSpec. This
|
||||||
-- shares postings between the subreports.
|
-- shares postings between the subreports.
|
||||||
@ -159,7 +161,7 @@ compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr
|
|||||||
( cbcsubreporttitle
|
( cbcsubreporttitle
|
||||||
-- Postprocess the report, negating balances and taking percentages if needed
|
-- Postprocess the report, negating balances and taking percentages if needed
|
||||||
, cbcsubreporttransform $
|
, cbcsubreporttransform $
|
||||||
generateMultiBalanceReport rspecsub j priceoracle colps' startbals'
|
generateMultiBalanceReport rspecsub j priceoracle mempty colps' startbals'
|
||||||
, cbcsubreportincreasestotal
|
, cbcsubreportincreasestotal
|
||||||
)
|
)
|
||||||
where
|
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
|
-- | 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
|
-- given by AccountName and columns by DateSpan, then generate a MultiBalanceReport
|
||||||
-- from the columns.
|
-- from the columns.
|
||||||
generateMultiBalanceReport :: ReportSpec -> Journal -> PriceOracle
|
generateMultiBalanceReport :: ReportSpec -> Journal -> PriceOracle -> Set AccountName
|
||||||
-> [(DateSpan, [Posting])] -> HashMap AccountName Account
|
-> [(DateSpan, [Posting])] -> HashMap AccountName Account
|
||||||
-> MultiBalanceReport
|
-> MultiBalanceReport
|
||||||
generateMultiBalanceReport rspec@ReportSpec{_rsReportOpts=ropts} j priceoracle colps startbals =
|
generateMultiBalanceReport rspec@ReportSpec{_rsReportOpts=ropts} j priceoracle unelidableaccts colps startbals =
|
||||||
report
|
report
|
||||||
where
|
where
|
||||||
-- Process changes into normal, cumulative, or historical amounts, plus value them
|
-- Process changes into normal, cumulative, or historical amounts, plus value them
|
||||||
matrix = calculateReportMatrix rspec j priceoracle startbals colps
|
matrix = calculateReportMatrix rspec j priceoracle startbals colps
|
||||||
|
|
||||||
-- All account names that will be displayed, possibly depth-clipped.
|
-- 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.
|
-- All the rows of the report.
|
||||||
rows = dbg5 "rows" . (if invert_ ropts then map (fmap maNegate) else id) -- Negate amounts if applicable
|
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
|
-- | Calculate accounts which are to be displayed in the report, as well as
|
||||||
-- their name and depth
|
-- their name and depth
|
||||||
displayedAccounts :: ReportSpec -> HashMap AccountName (Map DateSpan Account)
|
displayedAccounts :: ReportSpec
|
||||||
|
-> Set AccountName
|
||||||
|
-> HashMap AccountName (Map DateSpan Account)
|
||||||
-> HashMap AccountName DisplayName
|
-> HashMap AccountName DisplayName
|
||||||
displayedAccounts ReportSpec{_rsQuery=query,_rsReportOpts=ropts} valuedaccts
|
displayedAccounts ReportSpec{_rsQuery=query,_rsReportOpts=ropts} unelidableaccts valuedaccts
|
||||||
| depth == 0 = HM.singleton "..." $ DisplayName "..." "..." 1
|
| depth == 0 = HM.singleton "..." $ DisplayName "..." "..." 1
|
||||||
| otherwise = HM.mapWithKey (\a _ -> displayedName a) displayedAccts
|
| otherwise = HM.mapWithKey (\a _ -> displayedName a) displayedAccts
|
||||||
where
|
where
|
||||||
@ -421,7 +425,8 @@ displayedAccounts ReportSpec{_rsQuery=query,_rsReportOpts=ropts} valuedaccts
|
|||||||
-- Accounts interesting for their own sake
|
-- Accounts interesting for their own sake
|
||||||
isInteresting name amts =
|
isInteresting name amts =
|
||||||
d <= depth -- Throw out anything too deep
|
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
|
|| not (isZeroRow balance amts) -- Keep everything with a non-zero balance in the row
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|||||||
@ -409,7 +409,31 @@ Budget performance in 2019-01-01..2019-01-03:
|
|||||||
-------------------++---------------------------
|
-------------------++---------------------------
|
||||||
|| 0 [ 0]
|
|| 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
|
~ monthly from 2019/01
|
||||||
expenses:personal $1,000.00
|
expenses:personal $1,000.00
|
||||||
@ -439,7 +463,7 @@ Budget performance in 2019-01-01..2019-01-03:
|
|||||||
-------------------------------++----------------------------
|
-------------------------------++----------------------------
|
||||||
|| 0 [ 0]
|
|| 0 [ 0]
|
||||||
|
|
||||||
# 21.
|
# 23.
|
||||||
$ hledger -f- bal --budget -E
|
$ hledger -f- bal --budget -E
|
||||||
Budget performance in 2019-01-01..2019-01-03:
|
Budget performance in 2019-01-01..2019-01-03:
|
||||||
|
|
||||||
@ -452,7 +476,7 @@ Budget performance in 2019-01-01..2019-01-03:
|
|||||||
----------------------------------------++----------------------------
|
----------------------------------------++----------------------------
|
||||||
|| 0 [ 0]
|
|| 0 [ 0]
|
||||||
|
|
||||||
# 22.
|
# 24.
|
||||||
$ hledger -f- bal --budget --tree
|
$ hledger -f- bal --budget --tree
|
||||||
Budget performance in 2019-01-01..2019-01-03:
|
Budget performance in 2019-01-01..2019-01-03:
|
||||||
|
|
||||||
@ -464,7 +488,7 @@ Budget performance in 2019-01-01..2019-01-03:
|
|||||||
-------------------++----------------------------
|
-------------------++----------------------------
|
||||||
|| 0 [ 0]
|
|| 0 [ 0]
|
||||||
|
|
||||||
# 23.
|
# 25.
|
||||||
$ hledger -f- bal --budget --tree -E
|
$ hledger -f- bal --budget --tree -E
|
||||||
Budget performance in 2019-01-01..2019-01-03:
|
Budget performance in 2019-01-01..2019-01-03:
|
||||||
|
|
||||||
@ -477,7 +501,7 @@ Budget performance in 2019-01-01..2019-01-03:
|
|||||||
-------------------++----------------------------
|
-------------------++----------------------------
|
||||||
|| 0 [ 0]
|
|| 0 [ 0]
|
||||||
|
|
||||||
## 24. Zero budget == no budget
|
# 26. Zero budget == no budget
|
||||||
<
|
<
|
||||||
~ monthly from 2019-01
|
~ monthly from 2019-01
|
||||||
expenses:bills $100 ; bills has a $100 budget of its own, separate from subaccounts
|
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]
|
|| 0 [ 0]
|
||||||
|
|
||||||
# 25. -E shows d and e
|
# 27. -E shows d and e
|
||||||
$ hledger bal -f- --budget -E
|
$ hledger bal -f- --budget -E
|
||||||
Budget performance in 2019-01-01..2019-01-02:
|
Budget performance in 2019-01-01..2019-01-02:
|
||||||
|
|
||||||
@ -532,7 +556,7 @@ Budget performance in 2019-01-01..2019-01-02:
|
|||||||
------------------++------------------------
|
------------------++------------------------
|
||||||
|| 0 [ 0]
|
|| 0 [ 0]
|
||||||
|
|
||||||
# 26. The totals row shows correct totals.
|
# 28. The totals row shows correct totals.
|
||||||
# -T/--total and -A/--average adds those columns.
|
# -T/--total and -A/--average adds those columns.
|
||||||
$ hledger bal -f- --budget -TA not:income
|
$ hledger bal -f- --budget -TA not:income
|
||||||
Budget performance in 2019-01-01..2019-01-02:
|
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]
|
|| $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
|
$ hledger bal -f- --budget -TA not:income -O csv
|
||||||
"Account","2019-01-01..2019-01-02","budget","Total","budget","Average","budget"
|
"Account","2019-01-01..2019-01-02","budget","Total","budget","Average","budget"
|
||||||
"expenses:bills","$80","$370","$80","$370","$80","$370"
|
"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"
|
"expenses:bills:f","$10","0","$10","0","$10","0"
|
||||||
"Total:","$80","$370","$80","$370","$80","$370"
|
"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
|
# 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,
|
# transaction date (1/15 and 3/15) as start and end date by default,
|
||||||
# and since "monthly" generates transactions on the 1st,
|
# 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]
|
|| 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
|
$ hledger -f- bal --budget -M date:2020q1
|
||||||
Budget performance in 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]
|
|| 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
|
~ weekly weekly budget
|
||||||
(aaa) 1
|
(aaa) 1
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user