feat: bal: select budget( transaction)s by name (#1612)

This makes it possible to keep multiple named budgets in one journal,
and select the one you want with --budget's argument.

More precisely, you can select the subset of periodic transactions
rules which contain a certain fixed, case-insensitive substring.

Only one such --budget argument is supported, the last one on the
command line takes precedence.
This commit is contained in:
Simon Michael 2021-07-14 12:47:11 -10:00
parent b0aa70b27a
commit d2b799a717
5 changed files with 66 additions and 7 deletions

View File

@ -98,14 +98,21 @@ budgetReport rspec bopts reportspan j = dbg4 "sortedbudgetreport" budgetreport
-- their purpose and effect is to define balance change goals, per account and period,
-- for BudgetReport.
journalAddBudgetGoalTransactions :: BalancingOpts -> ReportOpts -> DateSpan -> Journal -> Journal
journalAddBudgetGoalTransactions bopts _ropts reportspan j =
journalAddBudgetGoalTransactions bopts ropts reportspan j =
either error' id $ journalBalanceTransactions bopts j{ jtxns = budgetts } -- PARTIAL:
where
budgetspan = dbg3 "budget span" $ reportspan
pat = fromMaybe "" $ dbg3 "budget pattern" $ T.toLower <$> budgetpat_ ropts
-- select periodic transactions matching a pattern
-- (the argument of the (final) --budget option).
-- XXX two limitations/wishes, requiring more extensive type changes:
-- - give an error if pat is non-null and matches no periodic txns
-- - allow a regexp or a full hledger query, not just a substring
budgetts =
dbg5 "budget goal txns" $
[makeBudgetTxn t
| pt <- jperiodictxns j
, pat `T.isInfixOf` T.toLower (ptdescription pt)
, t <- runPeriodicTransaction pt budgetspan
]
makeBudgetTxn t = txnTieKnot $ t { tdescription = T.pack "Budget transaction" }

View File

@ -117,8 +117,11 @@ data ReportOpts = ReportOpts {
-- for account transactions reports (aregister)
,txn_dates_ :: Bool
-- for balance reports (bal, bs, cf, is)
,balancecalc_ :: BalanceCalculation
,balanceaccum_ :: BalanceAccumulation
,balancecalc_ :: BalanceCalculation -- ^ What to calculate in balance report cells
,balanceaccum_ :: BalanceAccumulation -- ^ How to accumulate balance report values over time
,budgetpat_ :: Maybe T.Text -- ^ A case-insensitive description substring
-- to select periodic transactions for budget reports.
-- (Not a regexp, nor a full hledger query, for now.)
,accountlistmode_ :: AccountListMode
,drop_ :: Int
,row_total_ :: Bool
@ -167,6 +170,7 @@ defreportopts = ReportOpts
, txn_dates_ = False
, balancecalc_ = def
, balanceaccum_ = def
, budgetpat_ = Nothing
, accountlistmode_ = ALFlat
, drop_ = 0
, row_total_ = False
@ -212,8 +216,9 @@ rawOptsToReportOpts rawopts = do
,average_ = boolopt "average" rawopts
,related_ = boolopt "related" rawopts
,txn_dates_ = boolopt "txn-dates" rawopts
,balancecalc_ = balancecalcopt rawopts
,balancecalc_ = balancecalcopt rawopts
,balanceaccum_ = balanceaccumopt rawopts
,budgetpat_ = maybebudgetpatternopt rawopts
,accountlistmode_ = accountlistmodeopt rawopts
,drop_ = posintopt "drop" rawopts
,row_total_ = boolopt "row-total" rawopts
@ -289,6 +294,10 @@ accountlistmodeopt =
"flat" -> Just ALFlat
_ -> Nothing
-- Get the argument of the --budget option if any, or the empty string.
maybebudgetpatternopt :: RawOpts -> Maybe T.Text
maybebudgetpatternopt = fmap T.pack . maybestringopt "budget"
balancecalcopt :: RawOpts -> BalanceCalculation
balancecalcopt =
fromMaybe CalcChange . choiceopt parse where

View File

@ -277,8 +277,14 @@ balancemode = hledgerCommandMode
(
[flagNone ["sum"] (setboolopt "sum")
"show sum of posting amounts (default)"
,flagNone ["budget"] (setboolopt "budget")
"show sum of posting amounts compared to budget goals defined by periodic transactions\n "
-- XXX --budget[=DESCPAT], --forecast[=PERIODEXP], could be more consistent
,flagOpt "" ["budget"] (\s opts -> Right $ setopt "budget" s opts) "DESCPAT"
(unlines
[ "show sum of posting amounts together with budget goals defined by periodic"
, "transactions. With a DESCPAT argument (must be separated by = not space),"
, "use only periodic transactions with matching description"
, "(case insensitive substring match)."
])
,flagNone ["valuechange"] (setboolopt "valuechange")
"show change of value of period-end historical balances"
,flagNone ["change"] (setboolopt "change")

View File

@ -606,7 +606,7 @@ Budget performance in 2020-01-01..2020-01-15:
|| $400 [80% of $500]
```
#### Nested budgets
#### Budgets and subaccounts
You can add budgets to any account in your account hierarchy. If you have budgets on both parent account and some of its children, then budget(s)
of the child account(s) would be added to the budget of their parent, much like account balances behave.
@ -685,6 +685,27 @@ Budget performance in 2019/01:
|| 0 [ 0]
```
#### Selecting budget goals
The budget report evaluates periodic transaction rules to generate special "goal transactions",
which generate the goal amounts for each account in each report subperiod.
When troubleshooting, you can use the print command to show these as forecasted transactions:
```shell
$ hledger print --forecast=BUDGETREPORTPERIOD tag:generated
```
By default, the budget report uses all available periodic transaction rules to generate goals.
This includes rules with a different report interval from your report.
Eg if you have daily, weekly and monthly periodic rules,
all of these will contribute to the goals in a monthly budget report.
You can select a subset of periodic rules by providing an argument to the `--budget` flag.
`--budget=DESCPAT` will match all periodic rules whose description contains DESCPAT,
a case-insensitive substring (not a regular expression or query).
This means you can give your periodic rules descriptions
(remember that [two spaces are needed](#two-spaces-between-period-expression-and-description)),
and then select from multiple budgets defined in your journal.
### Customising single-period balance reports
For single-period balance reports displayed in the terminal (only),

View File

@ -595,3 +595,19 @@ Budget performance in 2020Q1:
expenses:food || $400 [80% of $500] 0 [0% of $500] $600 [120% of $500]
---------------++-----------------------------------------------------------
|| 0 [ 0% of $500] 0 [0% of $500] 0 [ 0% of $500]
# 30. Select from multiple named budgets.
<
~ weekly weekly budget
(aaa) 1
~ monthly monthly budget
(bbb) 10
$ hledger -f- bal --budget=weekly -p 2021-01
> /aaa/
>=
$ hledger -f- bal --budget=monthly -p 2021-01
> !/aaa/
>=