budget: tweak hiding/grouping of unbudgeted accounts
It now works slightly differently. Eg: - <unbudgeted>'s subaccounts are hidden by default - --show-unbudgeted shows all unbudgeted accounts, including subaccounts of budgeted parents - --show-unbudgeted doesn't affect the grouping under <unbudgeted> IMHO it's a nice simplification and increase in consistency, while still meeting the original intent.
This commit is contained in:
parent
8b650d8e4f
commit
568a442307
@ -57,24 +57,24 @@ accountNameLevel :: AccountName -> Int
|
|||||||
accountNameLevel "" = 0
|
accountNameLevel "" = 0
|
||||||
accountNameLevel a = T.length (T.filter (==acctsepchar) a) + 1
|
accountNameLevel a = T.length (T.filter (==acctsepchar) a) + 1
|
||||||
|
|
||||||
|
-- | A top-level account prefixed to some accounts in budget reports.
|
||||||
|
-- Defined here so it can be ignored by accountNameDrop.
|
||||||
|
unbudgetedAccountName :: T.Text
|
||||||
|
unbudgetedAccountName = "<unbudgeted>"
|
||||||
|
|
||||||
-- | Remove some number of account name components from the front of the account name.
|
-- | Remove some number of account name components from the front of the account name.
|
||||||
-- If the special "<unbudgeted>" top-level account is present, it is preserved and
|
-- If the special "<unbudgeted>" top-level account is present, it is preserved and
|
||||||
-- dropping affects the rest of the account name.
|
-- dropping affects the rest of the account name.
|
||||||
accountNameDrop :: Int -> AccountName -> AccountName
|
accountNameDrop :: Int -> AccountName -> AccountName
|
||||||
accountNameDrop n a
|
accountNameDrop n a
|
||||||
| a == unbudgetedAccount = a
|
| a == unbudgetedAccountName = a
|
||||||
| unbudgetedAccountAndSep `T.isPrefixOf` a =
|
| unbudgetedAccountAndSep `T.isPrefixOf` a =
|
||||||
case accountNameDrop n $ T.drop (T.length unbudgetedAccountAndSep) a of
|
case accountNameDrop n $ T.drop (T.length unbudgetedAccountAndSep) a of
|
||||||
"" -> unbudgetedAccount
|
"" -> unbudgetedAccountName
|
||||||
a' -> unbudgetedAccountAndSep <> a'
|
a' -> unbudgetedAccountAndSep <> a'
|
||||||
| otherwise = accountNameFromComponents $ drop n $ accountNameComponents a
|
| otherwise = accountNameFromComponents $ drop n $ accountNameComponents a
|
||||||
where
|
where
|
||||||
unbudgetedAccountAndSep = unbudgetedAccount <> acctsep
|
unbudgetedAccountAndSep = unbudgetedAccountName <> acctsep
|
||||||
|
|
||||||
-- | A top-level account prefixed to some accounts in budget reports.
|
|
||||||
-- Defined here so it can be ignored by accountNameDrop.
|
|
||||||
unbudgetedAccount :: T.Text
|
|
||||||
unbudgetedAccount = "<unbudgeted>"
|
|
||||||
|
|
||||||
-- | Sorted unique account names implied by these account names,
|
-- | Sorted unique account names implied by these account names,
|
||||||
-- ie these plus all their parent accounts up to the root.
|
-- ie these plus all their parent accounts up to the root.
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import Data.Monoid ((<>))
|
|||||||
#endif
|
#endif
|
||||||
import Data.Ord
|
import Data.Ord
|
||||||
import Data.Time.Calendar
|
import Data.Time.Calendar
|
||||||
--import Safe
|
import Safe
|
||||||
import Test.HUnit
|
import Test.HUnit
|
||||||
--import Data.List
|
--import Data.List
|
||||||
--import Data.Maybe
|
--import Data.Maybe
|
||||||
@ -65,24 +65,19 @@ type BudgetReport = PeriodicReport (Maybe Change, Maybe BudgetGoal)
|
|||||||
budgetReport :: ReportOpts -> Bool -> Bool -> DateSpan -> Day -> Journal -> BudgetReport
|
budgetReport :: ReportOpts -> Bool -> Bool -> DateSpan -> Day -> Journal -> BudgetReport
|
||||||
budgetReport ropts assrt showunbudgeted reportspan d j =
|
budgetReport ropts assrt showunbudgeted reportspan d j =
|
||||||
let
|
let
|
||||||
budgetj = budgetJournal assrt ropts reportspan j
|
q = queryFromOpts d ropts
|
||||||
budgetedacctsinperiod =
|
budgetj = budgetJournal assrt ropts reportspan j
|
||||||
|
budgetedaccts =
|
||||||
dbg2 "budgetedacctsinperiod" $
|
dbg2 "budgetedacctsinperiod" $
|
||||||
accountNamesFromPostings $
|
accountNamesFromPostings $
|
||||||
concatMap tpostings $
|
concatMap tpostings $
|
||||||
concatMap (flip runPeriodicTransaction reportspan) $
|
concatMap (flip runPeriodicTransaction reportspan) $
|
||||||
jperiodictxns j
|
jperiodictxns j
|
||||||
actualj =
|
actualj = budgetRollUp budgetedaccts showunbudgeted j
|
||||||
budgetRollUp budgetedacctsinperiod showunbudgeted
|
|
||||||
-- (if showunbudgeted then id else budgetRollUp budgetedacctsinperiod True budgetj)
|
|
||||||
j
|
|
||||||
q = queryFromOpts d ropts
|
|
||||||
budgetgoalreport = dbg1 "budgetgoalreport" $ multiBalanceReport ropts q budgetj
|
budgetgoalreport = dbg1 "budgetgoalreport" $ multiBalanceReport ropts q budgetj
|
||||||
actualreport = dbg1 "actualreport" $ multiBalanceReport ropts q actualj
|
actualreport = dbg1 "actualreport" $ multiBalanceReport ropts q actualj
|
||||||
in
|
in
|
||||||
dbg1 "budgetreport" $
|
dbg1 "budgetreport" $ combineBudgetAndActual budgetgoalreport actualreport
|
||||||
-- (if showunbudgeted then id else hideUnbudgetedAccounts budgetedacctsinperiod) $
|
|
||||||
combineBudgetAndActual budgetgoalreport actualreport
|
|
||||||
|
|
||||||
-- | Use all periodic transactions in the journal to generate
|
-- | Use all periodic transactions in the journal to generate
|
||||||
-- budget transactions in the specified report period.
|
-- budget transactions in the specified report period.
|
||||||
@ -101,19 +96,15 @@ budgetJournal assrt _ropts reportspan j =
|
|||||||
]
|
]
|
||||||
makeBudgetTxn t = txnTieKnot $ t { tdescription = T.pack "Budget transaction" }
|
makeBudgetTxn t = txnTieKnot $ t { tdescription = T.pack "Budget transaction" }
|
||||||
|
|
||||||
-- variations on hiding unbudgeted accounts:
|
-- | Adjust a journal's account names for budget reporting, in two ways:
|
||||||
|
|
||||||
-- | Adjust a journal for budget reporting, hiding all or most unbudgeted subaccounts.
|
|
||||||
-- Specifically,
|
|
||||||
--
|
--
|
||||||
-- - account names with no budget goal are rewritten to their closest parent with a budget goal
|
-- 1. accounts with no budget goal anywhere in their ancestry are moved
|
||||||
-- (thereby hiding unbudgeted subaccounts of budgeted accounts, regardless of depth limit).
|
-- under the "unbudgeted" top level account.
|
||||||
--
|
--
|
||||||
-- - accounts with no budgeted parent are rewritten to "<unbudgeted>:topaccountname"
|
-- 2. subaccounts with no budget goal are merged with their closest parent account
|
||||||
-- (hiding subaccounts of unbudgeted accounts, regardless of depth limit),
|
-- with a budget goal, so that only budgeted accounts are shown.
|
||||||
-- unless --show-unbudgeted is provided.
|
-- This can be disabled by --show-unbudgeted.
|
||||||
--
|
--
|
||||||
-- This is slightly inconsistent/confusing but probably useful.
|
|
||||||
budgetRollUp :: [AccountName] -> Bool -> Journal -> Journal
|
budgetRollUp :: [AccountName] -> Bool -> Journal -> Journal
|
||||||
budgetRollUp budgetedaccts showunbudgeted j = j { jtxns = remapTxn <$> jtxns j }
|
budgetRollUp budgetedaccts showunbudgeted j = j { jtxns = remapTxn <$> jtxns j }
|
||||||
where
|
where
|
||||||
@ -122,47 +113,15 @@ budgetRollUp budgetedaccts showunbudgeted j = j { jtxns = remapTxn <$> jtxns j }
|
|||||||
mapPostings f t = txnTieKnot $ t { tpostings = f $ tpostings t }
|
mapPostings f t = txnTieKnot $ t { tpostings = f $ tpostings t }
|
||||||
remapPosting p = p { paccount = remapAccount $ paccount p, porigin = Just . fromMaybe p $ porigin p }
|
remapPosting p = p { paccount = remapAccount $ paccount p, porigin = Just . fromMaybe p $ porigin p }
|
||||||
where
|
where
|
||||||
remapAccount origAcctName = remapAccount' origAcctName
|
remapAccount a
|
||||||
|
| hasbudget = a
|
||||||
|
| hasbudgetedparent = if showunbudgeted then a else budgetedparent
|
||||||
|
| otherwise = if showunbudgeted then u <> acctsep <> a else u
|
||||||
where
|
where
|
||||||
remapAccount' a
|
hasbudget = a `elem` budgetedaccts
|
||||||
| a `elem` budgetedaccts = a
|
hasbudgetedparent = not $ T.null budgetedparent
|
||||||
| not (T.null parent) = remapAccount' parent
|
budgetedparent = headDef "" $ filter (`elem` budgetedaccts) $ parentAccountNames a
|
||||||
| showunbudgeted = origAcctName
|
u = unbudgetedAccountName
|
||||||
| otherwise = unbudgetedAccount <> acctsep <> a
|
|
||||||
where
|
|
||||||
parent = parentAccountName a
|
|
||||||
|
|
||||||
--type PeriodicReportRow a =
|
|
||||||
-- ( AccountName -- A full account name.
|
|
||||||
-- , AccountName -- Shortened form of the account name to display in tree mode. Usually the leaf name, possibly with parent accounts prefixed.
|
|
||||||
-- , Int -- Indent level for displaying this account name in tree mode. 0, 1, 2...
|
|
||||||
-- , [a] -- The data value for each subperiod.
|
|
||||||
-- , a -- The total of this row's values.
|
|
||||||
-- , a -- The average of this row's values.
|
|
||||||
-- )
|
|
||||||
-- XXX doesn't work right with depth limit, show-unbudgeted, tree mode
|
|
||||||
-- | Adjust a budget report, altering the account name for any rows which have no
|
|
||||||
-- budget goals in any period, so that they are grouped under a special "unbudgeted"
|
|
||||||
-- prefix, and moving all "unbudgeted" rows to the end.
|
|
||||||
hideOrRenameUnbudgetedAccounts :: [AccountName] -> BudgetReport -> BudgetReport
|
|
||||||
hideOrRenameUnbudgetedAccounts budgetedaccts (PeriodicReport (spans, rows, totalrow)) =
|
|
||||||
PeriodicReport (spans, rs ++ unbudgetedrs, totalrow)
|
|
||||||
where
|
|
||||||
(rs, unbudgetedrs) = partition (any (isJust . snd) . fourth6) $ map renameacct rows
|
|
||||||
renameacct r@(a, a', indent, vals, tot, avg) =
|
|
||||||
-- if any (isJust . snd) vals
|
|
||||||
if a `elem` budgetedaccts
|
|
||||||
then r
|
|
||||||
else (rename a, mayberename a', indent, vals, tot, avg)
|
|
||||||
where
|
|
||||||
rename = ("<unbudgeted>:"<>)
|
|
||||||
mayberename = id -- XXX
|
|
||||||
|
|
||||||
-- | Adjust a budget report, removing any rows which do not correspond to
|
|
||||||
-- one of the provided budgeted accounts.
|
|
||||||
hideUnbudgetedAccounts :: [AccountName] -> BudgetReport -> BudgetReport
|
|
||||||
hideUnbudgetedAccounts budgetedaccts (PeriodicReport (spans, rows, totalrow)) =
|
|
||||||
PeriodicReport (spans, filter ((`elem` budgetedaccts) . first6) rows, totalrow)
|
|
||||||
|
|
||||||
-- | Combine a per-account-and-subperiod report of budget goals, and one
|
-- | Combine a per-account-and-subperiod report of budget goals, and one
|
||||||
-- of actual change amounts, into a budget performance report.
|
-- of actual change amounts, into a budget performance report.
|
||||||
|
|||||||
@ -34,28 +34,28 @@
|
|||||||
$ hledger bal -D -b 2016-12-01 -e 2016-12-04 -f - --budget
|
$ hledger bal -D -b 2016-12-01 -e 2016-12-04 -f - --budget
|
||||||
Budget performance in 2016/12/01-2016/12/03:
|
Budget performance in 2016/12/01-2016/12/03:
|
||||||
|
|
||||||
|| 2016/12/01 2016/12/02 2016/12/03
|
|| 2016/12/01 2016/12/02 2016/12/03
|
||||||
=======================++==============================================================================
|
==================++==============================================================================
|
||||||
<unbudgeted>:expenses || 0 0 $40
|
<unbudgeted> || 0 0 $40
|
||||||
assets:cash || $-10 [ 40% of $-25] $-14 [ 56% of $-25] $-51 [ 204% of $-25]
|
assets:cash || $-10 [ 40% of $-25] $-14 [ 56% of $-25] $-51 [ 204% of $-25]
|
||||||
expenses:food || $10 [ 100% of $10] $9 [ 90% of $10] $11 [ 110% of $10]
|
expenses:food || $10 [ 100% of $10] $9 [ 90% of $10] $11 [ 110% of $10]
|
||||||
expenses:leisure || 0 [ 0% of $15] $5 [ 33% of $15] 0 [ 0% of $15]
|
expenses:leisure || 0 [ 0% of $15] $5 [ 33% of $15] 0 [ 0% of $15]
|
||||||
-----------------------++------------------------------------------------------------------------------
|
------------------++------------------------------------------------------------------------------
|
||||||
|| 0 [ 0] 0 [ 0] 0 [ 0]
|
|| 0 [ 0] 0 [ 0] 0 [ 0]
|
||||||
|
|
||||||
# 2. --show-unbudgeted
|
# 2. --show-unbudgeted
|
||||||
$ hledger bal -D -b 2016-12-01 -e 2016-12-04 -f - --budget --show-unbudgeted
|
$ hledger bal -D -b 2016-12-01 -e 2016-12-04 -f - --budget --show-unbudgeted
|
||||||
Budget performance in 2016/12/01-2016/12/03:
|
Budget performance in 2016/12/01-2016/12/03:
|
||||||
|
|
||||||
|| 2016/12/01 2016/12/02 2016/12/03
|
|| 2016/12/01 2016/12/02 2016/12/03
|
||||||
==================++==============================================================================
|
==============================++==============================================================================
|
||||||
assets:cash || $-10 [ 40% of $-25] $-14 [ 56% of $-25] $-51 [ 204% of $-25]
|
<unbudgeted>:expenses:cab || 0 0 $15
|
||||||
expenses:cab || 0 0 $15
|
<unbudgeted>:expenses:movies || 0 0 $25
|
||||||
expenses:food || $10 [ 100% of $10] $9 [ 90% of $10] $11 [ 110% of $10]
|
assets:cash || $-10 [ 40% of $-25] $-14 [ 56% of $-25] $-51 [ 204% of $-25]
|
||||||
expenses:leisure || 0 [ 0% of $15] $5 [ 33% of $15] 0 [ 0% of $15]
|
expenses:food || $10 [ 100% of $10] $9 [ 90% of $10] $11 [ 110% of $10]
|
||||||
expenses:movies || 0 0 $25
|
expenses:leisure || 0 [ 0% of $15] $5 [ 33% of $15] 0 [ 0% of $15]
|
||||||
------------------++------------------------------------------------------------------------------
|
------------------------------++------------------------------------------------------------------------------
|
||||||
|| 0 [ 0] 0 [ 0] 0 [ 0]
|
|| 0 [ 0] 0 [ 0] 0 [ 0]
|
||||||
|
|
||||||
# 3. Test that budget works with mix of commodities
|
# 3. Test that budget works with mix of commodities
|
||||||
<
|
<
|
||||||
@ -95,14 +95,14 @@ Budget performance in 2016/12/01-2016/12/03:
|
|||||||
$ hledger bal -D -b 2016-12-01 -e 2016-12-04 -f - --budget
|
$ hledger bal -D -b 2016-12-01 -e 2016-12-04 -f - --budget
|
||||||
Budget performance in 2016/12/01-2016/12/03:
|
Budget performance in 2016/12/01-2016/12/03:
|
||||||
|
|
||||||
|| 2016/12/01 2016/12/02 2016/12/03
|
|| 2016/12/01 2016/12/02 2016/12/03
|
||||||
=======================++=====================================================================================
|
==================++=====================================================================================
|
||||||
<unbudgeted>:expenses || 0 0 $40
|
<unbudgeted> || 0 0 $40
|
||||||
assets:cash || $-15 [ 60% of $-25] $-26 [ 104% of $-25] $-51 [ 204% of $-25]
|
assets:cash || $-15 [ 60% of $-25] $-26 [ 104% of $-25] $-51 [ 204% of $-25]
|
||||||
expenses:food || £10 [ 150% of $10] 20 CAD [ 210% of $10] $11 [ 110% of $10]
|
expenses:food || £10 [ 150% of $10] 20 CAD [ 210% of $10] $11 [ 110% of $10]
|
||||||
expenses:leisure || 0 [ 0% of $15] $5 [ 33% of $15] 0 [ 0% of $15]
|
expenses:leisure || 0 [ 0% of $15] $5 [ 33% of $15] 0 [ 0% of $15]
|
||||||
-----------------------++-------------------------------------------------------------------------------------
|
------------------++-------------------------------------------------------------------------------------
|
||||||
|| $-15, £10 [ 0] $-21, 20 CAD [ 0] 0 [ 0]
|
|| $-15, £10 [ 0] $-21, 20 CAD [ 0] 0 [ 0]
|
||||||
|
|
||||||
<
|
<
|
||||||
~ daily
|
~ daily
|
||||||
@ -182,12 +182,12 @@ Budget performance in 2018/01/01w01:
|
|||||||
$ hledger -f- bal --budget -D
|
$ hledger -f- bal --budget -D
|
||||||
Budget performance in 2018/01/01-2018/01/04:
|
Budget performance in 2018/01/01-2018/01/04:
|
||||||
|
|
||||||
|| 2018/01/01 2018/01/02 2018/01/03 2018/01/04
|
|| 2018/01/01 2018/01/02 2018/01/03 2018/01/04
|
||||||
================++========================================================================================================
|
==============++========================================================================================================
|
||||||
<unbudgeted>:b || 1 1 1 1
|
<unbudgeted> || 1 1 1 1
|
||||||
a || 1 1 [ 100% of 1] 1 [ 100% of 1] 1
|
a || 1 1 [ 100% of 1] 1 [ 100% of 1] 1
|
||||||
----------------++--------------------------------------------------------------------------------------------------------
|
--------------++--------------------------------------------------------------------------------------------------------
|
||||||
|| 2 2 [ 200% of 1] 2 [ 200% of 1] 2
|
|| 2 2 [ 200% of 1] 2 [ 200% of 1] 2
|
||||||
|
|
||||||
# 8. Multiple bounded budgets.
|
# 8. Multiple bounded budgets.
|
||||||
<
|
<
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user