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
|
where
|
||||||
|
|
||||||
|
import Data.Foldable (toList)
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.List.Extra (nubSort)
|
import Data.List.Extra (nubSort)
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import qualified Data.HashMap.Strict as HM
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
@ -125,12 +128,9 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
startbals = dbg' "startbals" $ startingBalances ropts q j' reportspan
|
startbals = dbg' "startbals" $ startingBalances ropts q j' reportspan
|
||||||
-- The matched accounts with a starting balance. All of these should appear
|
-- 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.
|
-- 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.
|
-- Helpers to look up an account's starting balance.
|
||||||
startingBalanceFor a = fromMaybe nullmixedamt $ lookup a startbals
|
startingBalanceFor a = HM.lookupDefault nullmixedamt a startbals
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
-- 3. Gather postings for each column.
|
|
||||||
|
|
||||||
-- Postings matching the query within the report period.
|
-- Postings matching the query within the report period.
|
||||||
ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings ropts reportq j'
|
ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings ropts reportq j'
|
||||||
@ -142,21 +142,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
-- Group postings into their columns.
|
-- Group postings into their columns.
|
||||||
colps = dbg'' "colps" $ calculateColumns colspans ps
|
colps = dbg'' "colps" $ calculateColumns colspans ps
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
-- Each account's balance changes across all columns.
|
||||||
-- 4. Calculate account balance changes in each column.
|
acctchanges = dbg'' "acctchanges" $ calculateAccountChanges ropts q startbals colps
|
||||||
|
|
||||||
-- 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
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- 5. Gather the account balance changes into a regular matrix including the accounts
|
-- 5. Gather the account balance changes into a regular matrix including the accounts
|
||||||
@ -173,16 +160,6 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
where
|
where
|
||||||
allpostedaccts :: [AccountName] =
|
allpostedaccts :: [AccountName] =
|
||||||
dbg'' "allpostedaccts" . sort . accountNamesFromPostings $ map fst ps
|
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.
|
-- 6. Build the report rows.
|
||||||
@ -191,7 +168,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle =
|
|||||||
rows :: [MultiBalanceReportRow] =
|
rows :: [MultiBalanceReportRow] =
|
||||||
dbg'' "rows" $
|
dbg'' "rows" $
|
||||||
[ PeriodicReportRow a (accountNameLevel a) valuedrowbals rowtot rowavg
|
[ 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,
|
-- The row amounts to be displayed: per-period changes,
|
||||||
-- zero-based cumulative totals, or
|
-- zero-based cumulative totals, or
|
||||||
-- starting-balance-based historical balances.
|
-- 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.
|
-- Balances at report start date, from all earlier postings which otherwise match the query.
|
||||||
-- These balances are unvalued except maybe converted to cost.
|
-- These balances are unvalued except maybe converted to cost.
|
||||||
startingBalances :: ReportOpts -> Query -> Journal -> DateSpan -> [(AccountName, MixedAmount)]
|
startingBalances :: ReportOpts -> Query -> Journal -> DateSpan -> HashMap AccountName MixedAmount
|
||||||
startingBalances ropts q j reportspan = map (\(a,_,_,b) -> (a,b)) startbalanceitems
|
startingBalances ropts q j reportspan = HM.fromList $ map (\(a,_,_,b) -> (a,b)) startbalanceitems
|
||||||
where
|
where
|
||||||
(startbalanceitems,_) = dbg'' "starting balance report" $
|
(startbalanceitems,_) = dbg'' "starting balance report" $
|
||||||
balanceReport ropts''{value_=Nothing, percent_=False} startbalq j
|
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
|
addPosting (p, d) = maybe id (M.adjust (p:)) $ latestSpanContaining colspans d
|
||||||
emptyMap = M.fromList . zip colspans $ repeat []
|
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,
|
-- | Generates a simple non-columnar BalanceReport, but using multiBalanceReport,
|
||||||
-- in order to support --historical. Does not support tree-mode boring parent eliding.
|
-- 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]
|
) | PeriodicReportRow a d amts _ _ <- rows]
|
||||||
total = headDef nullmixedamt totals
|
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
|
-- Local debug helper
|
||||||
-- add a prefix to this function's debug output
|
-- add a prefix to this function's debug output
|
||||||
dbg s = let p = "multiBalanceReport" in Hledger.Utils.dbg3 (p++" "++s)
|
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
|
-- dbg = const id -- exclude this function from debug output
|
||||||
|
|
||||||
-- common rendering helper, XXX here for now
|
-- common rendering helper, XXX here for now
|
||||||
|
|
||||||
tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String
|
tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String
|
||||||
tableAsText (ReportOpts{pretty_tables_ = pretty}) showcell =
|
tableAsText (ReportOpts{pretty_tables_ = pretty}) showcell =
|
||||||
unlines
|
unlines
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
cabal-version: 1.12
|
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
|
-- see: https://github.com/sol/hpack
|
||||||
--
|
--
|
||||||
-- hash: c30491f8c77b1d38a1992455cc9c340cbcb17e95ec5c07085f9987b289747ba1
|
-- hash: dd7c200231996bc96dfb65f042843355e9f7db7002d68c953ada6e89cedd5cc5
|
||||||
|
|
||||||
name: hledger-lib
|
name: hledger-lib
|
||||||
version: 1.18.99
|
version: 1.18.99
|
||||||
@ -149,6 +149,7 @@ library
|
|||||||
, timeit
|
, timeit
|
||||||
, transformers >=0.2
|
, transformers >=0.2
|
||||||
, uglymemo
|
, uglymemo
|
||||||
|
, unordered-containers >=0.2
|
||||||
, utf8-string >=0.3.5
|
, utf8-string >=0.3.5
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
@ -202,6 +203,7 @@ test-suite doctest
|
|||||||
, timeit
|
, timeit
|
||||||
, transformers >=0.2
|
, transformers >=0.2
|
||||||
, uglymemo
|
, uglymemo
|
||||||
|
, unordered-containers >=0.2
|
||||||
, utf8-string >=0.3.5
|
, utf8-string >=0.3.5
|
||||||
if (impl(ghc < 8.2))
|
if (impl(ghc < 8.2))
|
||||||
buildable: False
|
buildable: False
|
||||||
@ -257,6 +259,7 @@ test-suite unittest
|
|||||||
, timeit
|
, timeit
|
||||||
, transformers >=0.2
|
, transformers >=0.2
|
||||||
, uglymemo
|
, uglymemo
|
||||||
|
, unordered-containers >=0.2
|
||||||
, utf8-string >=0.3.5
|
, utf8-string >=0.3.5
|
||||||
buildable: True
|
buildable: True
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|||||||
@ -82,6 +82,7 @@ dependencies:
|
|||||||
- time >=1.5
|
- time >=1.5
|
||||||
- timeit
|
- timeit
|
||||||
- transformers >=0.2
|
- transformers >=0.2
|
||||||
|
- unordered-containers >=0.2
|
||||||
- uglymemo
|
- uglymemo
|
||||||
- utf8-string >=0.3.5
|
- utf8-string >=0.3.5
|
||||||
- extra >=1.6.3
|
- extra >=1.6.3
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user