lib, cli: Use --flat as the default mode for all reports.

This commit is contained in:
Stephen Morgan 2020-07-07 15:04:39 +10:00 committed by Simon Michael
parent df84a417d7
commit 1425301a8c
17 changed files with 85 additions and 102 deletions

View File

@ -18,13 +18,15 @@ module Hledger.Reports.BalanceReport (
where
import Data.Time.Calendar
import Safe (headDef)
import Hledger.Data
import Hledger.Read (mamountp')
import Hledger.Query
import Hledger.Utils
import Hledger.Reports.MultiBalanceReport (multiBalanceReportWith)
import Hledger.Reports.ReportOptions
import Hledger.Reports.MultiBalanceReport (balanceReportFromMultiBalanceReport)
import Hledger.Reports.ReportTypes
-- | A simple balance report. It has:
@ -58,10 +60,21 @@ flatShowsExclusiveBalance = True
-- | Generate a simple balance report, containing the matched accounts and
-- their balances (change of balance) during the specified period.
-- This is like PeriodChangeReport with a single column (but more mature,
-- eg this can do hierarchical display).
-- If the normalbalance_ option is set, it adjusts the sorting and sign of
-- amounts (see ReportOpts and CompoundBalanceCommand).
balanceReport :: ReportOpts -> Query -> Journal -> BalanceReport
balanceReport = balanceReportFromMultiBalanceReport
balanceReport ropts q j = (rows, total)
where
report = multiBalanceReportWith ropts q j (journalPriceOracle (infer_value_ ropts) j)
rows = [( prrFullName row
, prrDisplayName row
, prrDepth row - 1 -- BalanceReport uses 0-based account depths
, headAmt row
) | row <- prRows report]
total = headAmt $ prTotals report
headAmt = headDef nullmixedamt . prrAmounts
-- tests
@ -106,6 +119,19 @@ tests_BalanceReport = tests "BalanceReport" [
,test "no args, sample journal" $
(defreportopts, samplejournal) `gives`
([
("assets:bank:checking","assets:bank:checking",0, mamountp' "$1.00")
,("assets:bank:saving","assets:bank:saving",0, mamountp' "$1.00")
,("assets:cash","assets:cash",0, mamountp' "$-2.00")
,("expenses:food","expenses:food",0, mamountp' "$1.00")
,("expenses:supplies","expenses:supplies",0, mamountp' "$1.00")
,("income:gifts","income:gifts",0, mamountp' "$-1.00")
,("income:salary","income:salary",0, mamountp' "$-1.00")
],
Mixed [usd 0])
,test "with --tree" $
(defreportopts{accountlistmode_=ALTree}, samplejournal) `gives`
([
("assets","assets",0, mamountp' "$0.00")
,("assets:bank","bank",1, mamountp' "$2.00")
@ -139,8 +165,7 @@ tests_BalanceReport = tests "BalanceReport" [
,test "with date:" $
(defreportopts{query_="date:'in 2009'"}, samplejournal2) `gives`
([],
Mixed [])
([], Mixed [num 0])
,test "with date2:" $
(defreportopts{query_="date2:'in 2009'"}, samplejournal2) `gives`
@ -161,12 +186,10 @@ tests_BalanceReport = tests "BalanceReport" [
,test "with not:desc:" $
(defreportopts{query_="not:desc:income"}, samplejournal) `gives`
([
("assets","assets",0, mamountp' "$-1.00")
,("assets:bank:saving","bank:saving",1, mamountp' "$1.00")
,("assets:cash","cash",1, mamountp' "$-2.00")
,("expenses","expenses",0, mamountp' "$2.00")
,("expenses:food","food",1, mamountp' "$1.00")
,("expenses:supplies","supplies",1, mamountp' "$1.00")
("assets:bank:saving","assets:bank:saving",0, mamountp' "$1.00")
,("assets:cash","assets:cash",0, mamountp' "$-2.00")
,("expenses:food","expenses:food",0, mamountp' "$1.00")
,("expenses:supplies","expenses:supplies",0, mamountp' "$1.00")
,("income:gifts","income:gifts",0, mamountp' "$-1.00")
],
Mixed [usd 0])
@ -182,7 +205,7 @@ tests_BalanceReport = tests "BalanceReport" [
,test "with period on an unpopulated period" $
(defreportopts{period_= PeriodBetween (fromGregorian 2008 1 2) (fromGregorian 2008 1 3)}, samplejournal) `gives`
([],Mixed [])
([], Mixed [num 0])

View File

@ -15,7 +15,6 @@ module Hledger.Reports.MultiBalanceReport (
multiBalanceReport,
multiBalanceReportWith,
balanceReportFromMultiBalanceReport,
CompoundBalanceReport,
compoundBalanceReport,
@ -45,7 +44,7 @@ import Data.Semigroup ((<>))
#endif
import Data.Semigroup (sconcat)
import Data.Time.Calendar (Day, addDays, fromGregorian)
import Safe (headDef, headMay, lastMay)
import Safe (headMay, lastMay)
import Text.Tabular as T
import Text.Tabular.AsciiWide (render)
@ -108,16 +107,15 @@ multiBalanceReportWith :: ReportOpts -> Query -> Journal -> PriceOracle -> Multi
multiBalanceReportWith ropts q j priceoracle = report
where
-- Queries, report/column dates.
ropts' = dbg "ropts'" $ setDefaultAccountListMode ALFlat ropts
reportspan = dbg "reportspan" $ calculateReportSpan ropts' q j
reportq = dbg "reportq" $ makeReportQuery ropts' reportspan q
reportspan = dbg "reportspan" $ calculateReportSpan ropts q j
reportq = dbg "reportq" $ makeReportQuery ropts reportspan q
-- Group postings into their columns.
colps = dbg'' "colps" $ getPostingsByColumn ropts' reportq j reportspan
colps = dbg'' "colps" $ getPostingsByColumn ropts reportq j reportspan
colspans = dbg "colspans" $ M.keys colps
-- Postprocess the report, negating balances and taking percentages if needed
report = dbg' "report" $ generateMultiBalanceReport ropts' reportq j priceoracle reportspan colspans colps
report = dbg' "report" $ generateMultiBalanceReport ropts reportq j priceoracle reportspan colspans colps
-- | Generate a compound balance report from a list of CBCSubreportSpec. This
-- shares postings between the subreports.
@ -135,12 +133,11 @@ compoundBalanceReportWith :: ReportOpts -> Query -> Journal -> PriceOracle
compoundBalanceReportWith ropts q j priceoracle subreportspecs = cbr
where
-- Queries, report/column dates.
ropts' = dbg "ropts'" $ setDefaultAccountListMode ALFlat ropts
reportspan = dbg "reportspan" $ calculateReportSpan ropts' q j
reportq = dbg "reportq" $ makeReportQuery ropts' reportspan q
reportspan = dbg "reportspan" $ calculateReportSpan ropts q j
reportq = dbg "reportq" $ makeReportQuery ropts reportspan q
-- Group postings into their columns.
colps = dbg'' "colps" $ getPostingsByColumn ropts'{empty_=True} reportq j reportspan
colps = dbg'' "colps" $ getPostingsByColumn ropts{empty_=True} reportq j reportspan
colspans = dbg "colspans" $ M.keys colps
-- Filter the column postings according to each subreport
@ -153,11 +150,11 @@ compoundBalanceReportWith ropts q j priceoracle subreportspecs = cbr
( cbcsubreporttitle
-- Postprocess the report, negating balances and taking percentages if needed
, prNormaliseSign cbcsubreportnormalsign $
generateMultiBalanceReport ropts'' reportq j priceoracle reportspan colspans colps'
generateMultiBalanceReport ropts' reportq j priceoracle reportspan colspans colps'
, cbcsubreportincreasestotal
)
where
ropts'' = ropts'{normalbalance_=Just cbcsubreportnormalsign}
ropts' = ropts{normalbalance_=Just cbcsubreportnormalsign}
-- Sum the subreport totals by column. Handle these cases:
-- - no subreports
@ -173,14 +170,6 @@ compoundBalanceReportWith ropts q j priceoracle subreportspecs = cbr
cbr = CompoundPeriodicReport "" colspans subreports overalltotals
-- | Calculate the span of the report to be generated.
setDefaultAccountListMode :: AccountListMode -> ReportOpts -> ReportOpts
setDefaultAccountListMode def ropts = ropts{accountlistmode_=mode}
where
mode = case accountlistmode_ ropts of
ALDefault -> def
a -> a
-- | Calculate starting balances, if needed for -H
--
-- Balances at report start date, from all earlier postings which otherwise match the query.
@ -200,8 +189,9 @@ startingBalances ropts q j reportspan = acctchanges
startbalq = dbg'' "startbalq" $ And [datelessq, precedingspanq]
datelessq = dbg "datelessq" $ filterQuery (not . queryIsDateOrDate2) q
ropts' | tree_ ropts = ropts{no_elide_=True, period_=precedingperiod}
| otherwise = ropts{accountlistmode_=ALFlat, period_=precedingperiod}
ropts' = case accountlistmode_ ropts of
ALTree -> ropts{no_elide_=True, period_=precedingperiod}
ALFlat -> ropts{period_=precedingperiod}
precedingperiod = dateSpanAsPeriod . spanIntersect precedingspan .
periodAsDateSpan $ period_ ropts
@ -296,9 +286,9 @@ acctChangesFromPostings :: ReportOpts -> Query -> [Posting] -> HashMap ClippedAc
acctChangesFromPostings ropts q ps = HM.fromList [(aname a, a) | a <- as]
where
as = filterAccounts . drop 1 $ accountsFromPostings ps
filterAccounts
| tree_ ropts = filter ((depthq `matchesAccount`) . aname) -- exclude deeper balances
| otherwise = clipAccountsAndAggregate (queryDepth depthq) . -- aggregate deeper balances at the depth limit.
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)
depthq = dbg "depthq" $ filterQuery queryIsDepth q
@ -416,7 +406,7 @@ buildReportRows ropts acctvalues =
, let rowtot = if balancetype_ ropts == PeriodChange then sum rowbals else 0
, let rowavg = averageMixedAmounts rowbals
]
where balance = if tree_ ropts then aibalance else aebalance
where balance = case accountlistmode_ ropts of ALTree -> aibalance; ALFlat -> aebalance
-- | Calculate accounts which are to be displayed in the report, as well as
-- their name and depth
@ -432,9 +422,9 @@ displayedAccounts ropts q valuedaccts
where
keep name amts = isInteresting name amts || name `HM.member` interestingParents
displayedName name
| flat_ ropts = DisplayName name droppedName 1
| otherwise = DisplayName name leaf . max 0 $ level - boringParents
displayedName name = case accountlistmode_ ropts of
ALTree -> DisplayName name leaf . max 0 $ level - boringParents
ALFlat -> DisplayName name droppedName 1
where
droppedName = accountNameDrop (drop_ ropts) name
leaf = accountNameFromComponents . reverse . map accountLeafName $
@ -451,13 +441,14 @@ displayedAccounts ropts q valuedaccts
&& (empty_ ropts || depth == 0 || not (isZeroRow balance amts)) -- Boring because has only zero entries
where
d = accountNameLevel name
balance = if tree_ ropts && d == depth then aibalance else aebalance
balance | ALTree <- accountlistmode_ ropts, d == depth = aibalance
| otherwise = aebalance
-- Accounts interesting because they are a fork for interesting subaccounts
interestingParents = dbg'' "interestingParents" $ HM.filterWithKey keepParent tallies
where
keepParent name subaccts
| flat_ ropts = False
| ALFlat <- accountlistmode_ ropts = False
| no_elide_ ropts = subaccts > 0 && accountNameLevel name > drop_ ropts
| otherwise = subaccts > 1 && accountNameLevel name > drop_ ropts
tallies = subaccountTallies . HM.keys $ HM.filterWithKey isInteresting valuedaccts
@ -515,7 +506,7 @@ calculateTotalsRow ropts displayaccts rows =
where isHighest = not . any (`HM.member` displayaccts) . init . expandAccountName
colamts = transpose . map prrAmounts $ filter isHighest rows
where isHighest row = not (tree_ ropts) || prrFullName row `HM.member` highestlevelaccts
where isHighest row = flat_ ropts || prrFullName row `HM.member` highestlevelaccts
-- TODO: If colamts is null, then this is empty. Do we want it to be a full
-- column of zeros?
@ -547,25 +538,6 @@ postprocessReport ropts displaynames =
(perdivide rowavg $ prrAverage totalrow)
-- | Generates a simple non-columnar BalanceReport, but using multiBalanceReport,
-- in order to support --historical. If the normalbalance_ option is set, it
-- adjusts the sorting and sign of amounts (see ReportOpts and
-- CompoundBalanceCommand).
balanceReportFromMultiBalanceReport :: ReportOpts -> Query -> Journal
-> ([(AccountName, AccountName, Int, MixedAmount)], MixedAmount)
balanceReportFromMultiBalanceReport ropts q j = (rows', total)
where
PeriodicReport _ rows (PeriodicReportRow _ totals _ _) =
multiBalanceReportWith ropts' q j (journalPriceOracle (infer_value_ ropts) j)
rows' = [( displayFull a
, displayName a
, if tree_ ropts' then displayDepth a - 1 else 0 -- BalanceReport uses 0-based account depths
, headDef nullmixedamt amts -- 0 columns is illegal, should not happen, return zeroes if it does
) | PeriodicReportRow a amts _ _ <- rows]
total = headDef nullmixedamt totals
ropts' = setDefaultAccountListMode ALTree ropts
-- | Transpose a Map of HashMaps to a HashMap of Maps.
--
-- Makes sure that all DateSpans are present in all rows.

View File

@ -76,9 +76,9 @@ data BalanceType = PeriodChange -- ^ The change of balance in each period.
instance Default BalanceType where def = PeriodChange
-- | Should accounts be displayed: in the command's default style, hierarchically, or as a flat list ?
data AccountListMode = ALDefault | ALTree | ALFlat deriving (Eq, Show, Data, Typeable)
data AccountListMode = ALFlat | ALTree deriving (Eq, Show, Data, Typeable)
instance Default AccountListMode where def = ALDefault
instance Default AccountListMode where def = ALFlat
-- | Standard options for customising report filtering and output.
-- Most of these correspond to standard hledger command-line options
@ -225,7 +225,7 @@ checkReportOpts ropts@ReportOpts{..} =
accountlistmodeopt :: RawOpts -> AccountListMode
accountlistmodeopt =
fromMaybe ALDefault . choiceopt parse where
fromMaybe ALFlat . choiceopt parse where
parse = \case
"tree" -> Just ALTree
"flat" -> Just ALFlat
@ -423,10 +423,11 @@ whichDateFromOpts ReportOpts{..} = if date2_ then SecondaryDate else PrimaryDate
-- | Legacy-compatible convenience aliases for accountlistmode_.
tree_ :: ReportOpts -> Bool
tree_ = (==ALTree) . accountlistmode_
tree_ ReportOpts{accountlistmode_ = ALTree} = True
tree_ ReportOpts{accountlistmode_ = ALFlat} = False
flat_ :: ReportOpts -> Bool
flat_ = (==ALFlat) . accountlistmode_
flat_ = not . tree_
-- depthFromOpts :: ReportOpts -> Int
-- depthFromOpts opts = min (fromMaybe 99999 $ depth_ opts) (queryDepth $ queryFromOpts nulldate opts)

View File

@ -94,14 +94,7 @@ asInit d reset ui@UIState{
]
-- run the report
(items,_total) = report ropts' q j
where
-- XXX in historical mode, --forecast throws off the starting balances
report | balancetype_ ropts == HistoricalBalance = balanceReportFromMultiBalanceReport
| otherwise = balanceReport
-- still using the old balanceReport for change reports as it
-- does not include every account from before the report period
(items,_total) = balanceReport ropts' q j
-- pre-render the list items
displayitem (fullacct, shortacct, indent, bal) =

View File

@ -86,7 +86,7 @@ close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do
False -> normaliseMixedAmount . mixedAmountStripPrices
-- the balances to close
(acctbals,_) = balanceReportFromMultiBalanceReport ropts_ q j
(acctbals,_) = balanceReport ropts_ q j
totalamt = sum $ map (\(_,_,_,b) -> normalise b) acctbals
-- since balance assertion amounts are required to be exact, the

View File

@ -101,14 +101,8 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportopts_=r
_ -> Nothing
balancetype = fromMaybe cbctype mBalanceTypeOverride
-- Set balance type in the report options.
-- Also, use tree mode (by default, at least?) if --cumulative/--historical
-- are used in single column mode, since in that situation we will be using
-- balanceReportFromMultiBalanceReport which does not support eliding boring parents,
-- and tree mode hides this.. or something.. XXX
-- This limitation on longer exists. Should this be changed?
ropts' = ropts{
balancetype_=balancetype,
accountlistmode_=if not (flat_ ropts) && interval_==NoInterval && balancetype `elem` [CumulativeChange, HistoricalBalance] then ALTree else accountlistmode_,
no_total_=if percent_ && length cbcqueries > 1 then True else no_total_
}

View File

@ -63,7 +63,7 @@ hledger -f- print --explicit --empty
# When preserving a zero amount's commodity, we should also preserve
# the amount style, such as where to place the symbol.
# https://github.com/simonmichael/hledger/issues/230
hledger -f- balance
hledger -f- balance --tree
<<<
D 1000,00€

View File

@ -22,7 +22,7 @@
# 1 4:5
#--------------------
# 0
$ hledger -f - bal
$ hledger -f - bal --tree
0 1:2
1 3
0 4

View File

@ -1,5 +1,5 @@
# 1.
hledger -f sample.journal balance
hledger -f sample.journal balance --tree
>>>
$-1 assets
$1 bank:saving
@ -16,7 +16,7 @@ hledger -f sample.journal balance
>>>=0
# 2.
hledger -f sample.journal balance o
hledger -f sample.journal balance --tree o
>>>
$1 expenses:food
$-2 income
@ -58,7 +58,7 @@ hledger -f - balance -b 2016 -e 2017
>>>= 0
# 4. Period reporting works for two years
hledger -f - balance -b 2015 -e 2017
hledger -f - balance --tree -b 2015 -e 2017
<<<
2015/10/10 Client A | Invoice #1
assets:receivables $10,000.00
@ -91,7 +91,7 @@ hledger -f - balance -b 2015 -e 2017
>>>= 0
# 5. Period reporting works for one month
hledger -f - balance -b 2015/11 -e 2015/12
hledger -f - balance --tree -b 2015/11 -e 2015/12
<<<
2015/10/10 Client A | Invoice #1
assets:receivables $10,000.00

View File

@ -1,4 +1,4 @@
hledger -f sample.journal balance --format="%30(account) %-.20(total)"
hledger -f sample.journal balance --tree --format="%30(account) %-.20(total)"
>>>
assets $-1
bank:saving $1

View File

@ -24,7 +24,7 @@ hledger -f - balance --no-total
>>>=0
# 3. But not with --no-elide
hledger -f - balance --no-total --no-elide
hledger -f - balance --no-total --tree --no-elide
<<<
1/1
(a:b) 1
@ -34,7 +34,7 @@ hledger -f - balance --no-total --no-elide
>>>=0
# 4. Nor when it has more than one subaccount
hledger -f - balance --no-total
hledger -f - balance --tree --no-total
<<<
1/1
(a:b) 1

View File

@ -1,6 +1,6 @@
#!/usr/bin/env shelltest
# 1. Single column percent
hledger -f sample.journal balance expenses -%
hledger -f sample.journal balance expenses -% --tree
>>>
100.0 % expenses
50.0 % food

View File

@ -223,7 +223,7 @@ Balance changes in 2018:
2018/1/1
(a:k) 1
$ hledger -f- bal -N
$ hledger -f- bal -N --tree
1 a:k
2 b
1 i
@ -268,7 +268,7 @@ account d
2018/1/1
(d) 1
$ hledger -f- bal -N
$ hledger -f- bal -N --tree
1 d
1 a:l
3 b
@ -358,7 +358,7 @@ $ hledger -f- bal -N --sort-amount --flat
(a:ab) 3
(b) 2
$ hledger -f- bal -N -S
$ hledger -f- bal -N -S --tree
4 a
3 ab
1 aa

View File

@ -240,7 +240,7 @@ Balance Sheet 2016-01-31
# 9. Check that accounts brought to zero by subaccount balances
# are not erased from balancesheet
hledger -f - balancesheet
hledger -f - balancesheet --tree
<<<
2018-10-01
income:whatever

View File

@ -47,7 +47,7 @@ hledger -f chinese.journal register --width 80
>>>=0
# 3.
hledger -f chinese.journal balance
hledger -f chinese.journal balance --tree
>>>
0 㐀:㐁
1 A 㐂

View File

@ -38,7 +38,7 @@ $ hledger register -f- --auto
>=
# 3. balance
$ hledger balance -f- --auto
$ hledger balance -f- --auto --tree
$115 assets
$95 bank
$20 cash

View File

@ -35,7 +35,7 @@ hledger -f- print
>>>=0
#
# 5. real and balanced virtual postings are balanced separately, and multiple blank virtuals are ok
hledger -f- balance
hledger -f- balance --tree
<<<
2010/1/1 x
a 1