mirror of
https://github.com/simonmichael/hledger.git
synced 2024-12-26 20:02:27 +03:00
lib: multiBalanceReport: Break calculateAccountChanges and acctChangesFromPostings separate functions.
This commit is contained in:
parent
a72c4f285b
commit
0dcfddd201
@ -22,8 +22,11 @@ module Hledger.Reports.MultiBalanceReport (
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Foldable (toList)
|
||||
import Data.List
|
||||
import Data.List.Extra (nubSort)
|
||||
import Data.HashMap.Strict (HashMap)
|
||||
import qualified Data.HashMap.Strict as HM
|
||||
import Data.Map (Map)
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe
|
||||
@ -125,12 +128,9 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
||||
startbals = dbg' "startbals" $ startingBalances ropts q j' reportspan
|
||||
-- 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.
|
||||
startaccts = dbg'' "startaccts" $ map fst startbals
|
||||
startaccts = dbg'' "startaccts" $ HM.keys startbals
|
||||
-- Helpers to look up an account's starting balance.
|
||||
startingBalanceFor a = fromMaybe nullmixedamt $ lookup a startbals
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- 3. Gather postings for each column.
|
||||
startingBalanceFor a = HM.lookupDefault nullmixedamt a startbals
|
||||
|
||||
-- Postings matching the query within the report period.
|
||||
ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings ropts reportq j'
|
||||
@ -142,21 +142,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
||||
-- Group postings into their columns.
|
||||
colps = dbg'' "colps" $ calculateColumns colspans ps
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- 4. Calculate account balance changes in each column.
|
||||
|
||||
-- In each column, gather the accounts that have postings and their change amount.
|
||||
acctChangesFromPostings :: [Posting] -> [(ClippedAccountName, MixedAmount)]
|
||||
acctChangesFromPostings ps = [(aname a, (if tree_ ropts then aibalance else aebalance) a) | a <- as]
|
||||
where
|
||||
as = depthLimit $
|
||||
(if tree_ ropts then id else filter ((>0).anumpostings)) $
|
||||
drop 1 $ accountsFromPostings ps
|
||||
depthLimit
|
||||
| tree_ ropts = filter ((depthq `matchesAccount`).aname) -- exclude deeper balances
|
||||
| otherwise = clipAccountsAndAggregate depth -- aggregate deeper balances at the depth limit
|
||||
colacctchanges :: [[(ClippedAccountName, MixedAmount)]] =
|
||||
dbg'' "colacctchanges" $ map (acctChangesFromPostings . snd) $ M.toList colps
|
||||
-- Each account's balance changes across all columns.
|
||||
acctchanges = dbg'' "acctchanges" $ calculateAccountChanges ropts q startbals colps
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- 5. Gather the account balance changes into a regular matrix including the accounts
|
||||
@ -173,16 +160,6 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
||||
where
|
||||
allpostedaccts :: [AccountName] =
|
||||
dbg'' "allpostedaccts" . sort . accountNamesFromPostings $ map fst ps
|
||||
-- Each column's balance changes for each account, adding zeroes where needed.
|
||||
colallacctchanges :: [[(ClippedAccountName, MixedAmount)]] =
|
||||
dbg'' "colallacctchanges"
|
||||
[ sortOn fst $ unionBy (\(a,_) (a',_) -> a == a') postedacctchanges zeroes
|
||||
| postedacctchanges <- colacctchanges ]
|
||||
where zeroes = [(a, nullmixedamt) | a <- displayaccts]
|
||||
-- Transpose to get each account's balance changes across all columns.
|
||||
acctchanges :: [(ClippedAccountName, [MixedAmount])] =
|
||||
dbg'' "acctchanges"
|
||||
[(a, map snd abs) | abs@((a,_):_) <- transpose colallacctchanges] -- never null, or used when null...
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- 6. Build the report rows.
|
||||
@ -191,7 +168,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
||||
rows :: [MultiBalanceReportRow] =
|
||||
dbg'' "rows" $
|
||||
[ PeriodicReportRow a (accountNameLevel a) valuedrowbals rowtot rowavg
|
||||
| (a,changes) <- dbg'' "acctchanges" acctchanges
|
||||
| (a,changesMap) <- HM.toList acctchanges
|
||||
, let changes = toList changesMap
|
||||
-- The row amounts to be displayed: per-period changes,
|
||||
-- zero-based cumulative totals, or
|
||||
-- starting-balance-based historical balances.
|
||||
@ -315,8 +293,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
||||
--
|
||||
-- Balances at report start date, from all earlier postings which otherwise match the query.
|
||||
-- These balances are unvalued except maybe converted to cost.
|
||||
startingBalances :: ReportOpts -> Query -> Journal -> DateSpan -> [(AccountName, MixedAmount)]
|
||||
startingBalances ropts q j reportspan = map (\(a,_,_,b) -> (a,b)) startbalanceitems
|
||||
startingBalances :: ReportOpts -> Query -> Journal -> DateSpan -> HashMap AccountName MixedAmount
|
||||
startingBalances ropts q j reportspan = HM.fromList $ map (\(a,_,_,b) -> (a,b)) startbalanceitems
|
||||
where
|
||||
(startbalanceitems,_) = dbg'' "starting balance report" $
|
||||
balanceReport ropts''{value_=Nothing, percent_=False} startbalq j
|
||||
@ -386,6 +364,37 @@ calculateColumns colspans = foldr addPosting emptyMap
|
||||
addPosting (p, d) = maybe id (M.adjust (p:)) $ latestSpanContaining colspans d
|
||||
emptyMap = M.fromList . zip colspans $ repeat []
|
||||
|
||||
-- | Calculate account balance changes in each column.
|
||||
--
|
||||
-- In each column, gather the accounts that have postings and their change amount.
|
||||
acctChangesFromPostings :: ReportOpts -> Query -> [Posting] -> HashMap ClippedAccountName MixedAmount
|
||||
acctChangesFromPostings ropts q ps =
|
||||
HM.fromList [(aname a, (if tree_ ropts then aibalance else aebalance) a) | a <- as]
|
||||
where
|
||||
as = depthLimit $
|
||||
(if tree_ ropts then id else filter ((>0).anumpostings)) $
|
||||
drop 1 $ accountsFromPostings ps
|
||||
depthLimit
|
||||
| tree_ ropts = filter ((depthq `matchesAccount`) . aname) -- exclude deeper balances
|
||||
| otherwise = clipAccountsAndAggregate $ queryDepth depthq -- aggregate deeper balances at the depth limit
|
||||
depthq = dbg1 "depthq" $ filterQuery queryIsDepth q
|
||||
|
||||
-- | Gather the account balance changes into a regular matrix including the accounts
|
||||
-- from all columns
|
||||
calculateAccountChanges :: ReportOpts -> Query
|
||||
-> HashMap ClippedAccountName MixedAmount
|
||||
-> Map DateSpan [Posting]
|
||||
-> HashMap ClippedAccountName (Map DateSpan MixedAmount)
|
||||
calculateAccountChanges ropts q startbals colps = acctchanges
|
||||
where
|
||||
-- Transpose to get each account's balance changes across all columns.
|
||||
acctchanges = transposeMap colacctchanges <> (zeros <$ startbals)
|
||||
|
||||
colacctchanges :: Map DateSpan (HashMap ClippedAccountName MixedAmount) =
|
||||
dbg'' "colacctchanges" $ fmap (acctChangesFromPostings ropts q) colps
|
||||
|
||||
zeros = nullmixedamt <$ colacctchanges
|
||||
|
||||
|
||||
-- | Generates a simple non-columnar BalanceReport, but using multiBalanceReport,
|
||||
-- in order to support --historical. Does not support tree-mode boring parent eliding.
|
||||
@ -403,6 +412,20 @@ balanceReportFromMultiBalanceReport opts q j = (rows', total)
|
||||
) | PeriodicReportRow a d amts _ _ <- rows]
|
||||
total = headDef nullmixedamt totals
|
||||
|
||||
|
||||
-- | Transpose a Map of HashMaps to a HashMap of Maps.
|
||||
transposeMap :: Map DateSpan (HashMap AccountName MixedAmount)
|
||||
-> HashMap AccountName (Map DateSpan MixedAmount)
|
||||
transposeMap xs = M.foldrWithKey addSpan mempty xs
|
||||
where
|
||||
addSpan span acctmap seen = HM.foldrWithKey (addAcctSpan span) seen acctmap
|
||||
|
||||
addAcctSpan span acct a = HM.alter f acct
|
||||
where f = Just . M.insert span a . fromMaybe emptySpanMap
|
||||
|
||||
emptySpanMap = nullmixedamt <$ xs
|
||||
|
||||
|
||||
-- Local debug helper
|
||||
-- add a prefix to this function's debug output
|
||||
dbg s = let p = "multiBalanceReport" in Hledger.Utils.dbg3 (p++" "++s)
|
||||
@ -411,7 +434,6 @@ dbg'' s = let p = "multiBalanceReport" in Hledger.Utils.dbg5 (p++" "++s)
|
||||
-- dbg = const id -- exclude this function from debug output
|
||||
|
||||
-- common rendering helper, XXX here for now
|
||||
|
||||
tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String
|
||||
tableAsText (ReportOpts{pretty_tables_ = pretty}) showcell =
|
||||
unlines
|
||||
|
@ -1,10 +1,10 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.33.0.
|
||||
-- This file has been generated from package.yaml by hpack version 0.33.1.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
--
|
||||
-- hash: c30491f8c77b1d38a1992455cc9c340cbcb17e95ec5c07085f9987b289747ba1
|
||||
-- hash: dd7c200231996bc96dfb65f042843355e9f7db7002d68c953ada6e89cedd5cc5
|
||||
|
||||
name: hledger-lib
|
||||
version: 1.18.99
|
||||
@ -149,6 +149,7 @@ library
|
||||
, timeit
|
||||
, transformers >=0.2
|
||||
, uglymemo
|
||||
, unordered-containers >=0.2
|
||||
, utf8-string >=0.3.5
|
||||
default-language: Haskell2010
|
||||
|
||||
@ -202,6 +203,7 @@ test-suite doctest
|
||||
, timeit
|
||||
, transformers >=0.2
|
||||
, uglymemo
|
||||
, unordered-containers >=0.2
|
||||
, utf8-string >=0.3.5
|
||||
if (impl(ghc < 8.2))
|
||||
buildable: False
|
||||
@ -257,6 +259,7 @@ test-suite unittest
|
||||
, timeit
|
||||
, transformers >=0.2
|
||||
, uglymemo
|
||||
, unordered-containers >=0.2
|
||||
, utf8-string >=0.3.5
|
||||
buildable: True
|
||||
default-language: Haskell2010
|
||||
|
@ -82,6 +82,7 @@ dependencies:
|
||||
- time >=1.5
|
||||
- timeit
|
||||
- transformers >=0.2
|
||||
- unordered-containers >=0.2
|
||||
- uglymemo
|
||||
- utf8-string >=0.3.5
|
||||
- extra >=1.6.3
|
||||
|
Loading…
Reference in New Issue
Block a user