Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bal: with --declared, include all declared accounts #1765

Merged
merged 2 commits into from
Nov 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 29 additions & 23 deletions hledger-lib/Hledger/Reports/MultiBalanceReport.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-|
Expand All @@ -26,7 +27,6 @@ module Hledger.Reports.MultiBalanceReport (
getPostingsByColumn,
getPostings,
startingPostings,
startingBalancesFromPostings,
generateMultiBalanceReport,
balanceReportTableAsText,

Expand Down Expand Up @@ -122,8 +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" . startingBalancesFromPostings rspec j priceoracle
$ startingPostings rspec j priceoracle reportspan
startbals = dbg5 "startbals" $
startingBalances rspec j priceoracle $ startingPostings rspec j priceoracle reportspan

-- Generate and postprocess the report, negating balances and taking percentages if needed
report = dbg4 "multiBalanceReportWith" $
Expand Down Expand Up @@ -166,7 +166,7 @@ compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr
-- Filter the column postings according to each subreport
colps' = map (second $ filter (matchesPosting q)) colps
-- We need to filter historical postings directly, rather than their accumulated balances. (#1698)
startbals' = startingBalancesFromPostings rspec j priceoracle $ filter (matchesPosting q) startps
startbals' = startingBalances rspec j priceoracle $ filter (matchesPosting q) startps
ropts = cbcsubreportoptions $ _rsReportOpts rspec
q = cbcsubreportquery j

Expand All @@ -183,10 +183,12 @@ compoundBalanceReportWith rspec' j priceoracle subreportspecs = cbr

cbr = CompoundPeriodicReport "" (map fst colps) subreports overalltotals

-- | Calculate starting balances from postings, if needed for -H.
startingBalancesFromPostings :: ReportSpec -> Journal -> PriceOracle -> [Posting]
-- XXX seems refactorable
-- | Calculate accounts' balances on the report start date, from these postings
-- which should be all postings before that data, and possibly also from account declarations.
startingBalances :: ReportSpec -> Journal -> PriceOracle -> [Posting]
-> HashMap AccountName Account
startingBalancesFromPostings rspec j priceoracle ps =
startingBalances rspec j priceoracle ps =
M.findWithDefault nullacct emptydatespan
<$> calculateReportMatrix rspec j priceoracle mempty [(emptydatespan, ps)]

Expand Down Expand Up @@ -261,24 +263,27 @@ getPostings rspec@ReportSpec{_rsQuery=query, _rsReportOpts=ropts} j priceoracle
-- handles the hledger-ui+future txns case above).
depthless = dbg3 "depthless" $ filterQuery (not . queryIsDepth) query

-- | Given a set of postings, eg for a single report column, gather
-- the accounts that have postings and calculate the change amount for
-- each. Accounts and amounts will be depth-clipped appropriately if
-- a depth limit is in effect.
acctChangesFromPostings :: ReportSpec -> [Posting] -> HashMap ClippedAccountName Account
acctChangesFromPostings ReportSpec{_rsQuery=query,_rsReportOpts=ropts} ps =
-- | From set of postings, eg for a single report column, calculate the balance change in each account.
-- Accounts and amounts will be depth-clipped appropriately if a depth limit is in effect.
--
-- When --declared is used, accounts which have been declared with an account directive are included,
-- with a 0 balance change. These are really only needed when calculating starting balances, but
-- it's harmless to have them in the column changes as well.
acctChanges :: ReportSpec -> Journal -> [Posting] -> HashMap ClippedAccountName Account
acctChanges ReportSpec{_rsQuery=query,_rsReportOpts=ropts} j ps =
HM.fromList [(aname a, a) | a <- as]
where
as = filterAccounts . drop 1 $ accountsFromPostings ps
as = filterAccounts $
drop 1 (accountsFromPostings ps)
++ if declared_ ropts then [nullacct{aname} | aname <- journalAccountNamesDeclared j] else []
filterAccounts = case accountlistmode_ ropts of
ALTree -> filter ((depthq `matchesAccount`) . aname) -- exclude deeper balances
ALFlat -> clipAccountsAndAggregate (queryDepth depthq) . -- aggregate deeper balances at the depth limit.
filter ((0<) . anumpostings)
ALTree -> filter ((depthq `matchesAccount`) . aname) -- exclude deeper balances
ALFlat -> clipAccountsAndAggregate (queryDepth depthq) -- aggregate deeper balances at the depth limit
-- . filter ((0<) . anumpostings) -- exclude unposted accounts
depthq = dbg3 "depthq" $ filterQuery queryIsDepth query

-- | Gather the account balance changes into a regular matrix, then
-- accumulate and value amounts, as specified by the report options.
--
-- Makes sure all report columns have an entry.
calculateReportMatrix :: ReportSpec -> Journal -> PriceOracle
-> HashMap ClippedAccountName Account
Expand Down Expand Up @@ -308,11 +313,12 @@ calculateReportMatrix rspec@ReportSpec{_rsReportOpts=ropts} j priceoracle startb
startingBalance = HM.lookupDefault nullacct name startbals
valuedStart = avalue (DateSpan Nothing historicalDate) startingBalance

-- Transpose to get each account's balance changes across all columns, then
-- pad with zeros
allchanges = ((<>zeros) <$> acctchanges) <> (zeros <$ startbals)
acctchanges = dbg5 "acctchanges" $ transposeMap colacctchanges
colacctchanges = dbg5 "colacctchanges" $ map (second $ acctChangesFromPostings rspec) colps
-- In each column, get each account's balance changes
colacctchanges = dbg5 "colacctchanges" $ map (second $ acctChanges rspec j) colps :: [(DateSpan, HashMap ClippedAccountName Account)]
-- Transpose it to get each account's balance changes across all columns
acctchanges = dbg5 "acctchanges" $ transposeMap colacctchanges :: HashMap AccountName (Map DateSpan Account)
-- Fill out the matrix with zeros in empty cells
allchanges = ((<>zeros) <$> acctchanges) <> (zeros <$ startbals)

avalue = acctApplyBoth . mixedAmountApplyValuationAfterSumFromOptsWith ropts j priceoracle
acctApplyBoth f a = a{aibalance = f $ aibalance a, aebalance = f $ aebalance a}
Expand Down
7 changes: 5 additions & 2 deletions hledger-lib/Hledger/Reports/ReportOptions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,13 @@ data ReportOpts = ReportOpts {
-- (Not a regexp, nor a full hledger query, for now.)
,accountlistmode_ :: AccountListMode
,drop_ :: Int
,declared_ :: Bool -- ^ Include accounts declared but not yet posted to ?
,row_total_ :: Bool
,no_total_ :: Bool
,show_costs_ :: Bool -- ^ Whether to show costs for reports which normally don't show them
,show_costs_ :: Bool -- ^ Show costs for reports which normally don't show them ?
,sort_amount_ :: Bool
,percent_ :: Bool
,invert_ :: Bool -- ^ if true, flip all amount signs in reports
,invert_ :: Bool -- ^ Flip all amount signs in reports ?
,normalbalance_ :: Maybe NormalSign
-- ^ This can be set when running balance reports on a set of accounts
-- with the same normal balance type (eg all assets, or all incomes).
Expand Down Expand Up @@ -197,6 +198,7 @@ defreportopts = ReportOpts
, budgetpat_ = Nothing
, accountlistmode_ = ALFlat
, drop_ = 0
, declared_ = False
, row_total_ = False
, no_total_ = False
, show_costs_ = False
Expand Down Expand Up @@ -250,6 +252,7 @@ rawOptsToReportOpts d rawopts =
,budgetpat_ = maybebudgetpatternopt rawopts
,accountlistmode_ = accountlistmodeopt rawopts
,drop_ = posintopt "drop" rawopts
,declared_ = boolopt "declared" rawopts
,row_total_ = boolopt "row-total" rawopts
,no_total_ = boolopt "no-total" rawopts
,show_costs_ = boolopt "show-costs" rawopts
Expand Down
1 change: 1 addition & 0 deletions hledger/Hledger/Cli/Commands/Balance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ balancemode = hledgerCommandMode
]
++ flattreeflags True ++
[flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "omit N leading account name parts (in flat mode)"
,flagNone ["declared"] (setboolopt "declared") "include accounts which have been declared but not yet used"
,flagNone ["average","A"] (setboolopt "average") "show a row average column (in multicolumn reports)"
,flagNone ["related","r"] (setboolopt "related") "show postings' siblings instead"
,flagNone ["row-total","T"] (setboolopt "row-total") "show a row total column (in multicolumn reports)"
Expand Down
6 changes: 6 additions & 0 deletions hledger/Hledger/Cli/Commands/Balance.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ Here are some ways to handle that:
[csv-mode]: https://elpa.gnu.org/packages/csv-mode.html
[visidata]: https://www.visidata.org

### Showing declared accounts

With `--declared`, accounts which have been declared with an [account directive](#declaring-accounts),
even if they have no transactions yet, will be included in the balance report with a zero balance,
and will be visible with `-E/--empty`.

### Commodity layout

With `--layout`, you can control how amounts with more than one commodity are displayed:
Expand Down
7 changes: 7 additions & 0 deletions hledger/test/balance/balance.test
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,10 @@ hledger -f - balance -N --output-format=csv --tree
"Assets:Cash","$-1"
>>>= 0

# 9. --declared includes all declared accounts, with a zero balance if they have no postings.
hledger -f - balance -N --declared -E
<<<
account a
>>>
0 a
>>>= 0