{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Hledger.Reports.MultiBalanceReport (
MultiBalanceReport,
MultiBalanceReportRow,
multiBalanceReport,
multiBalanceReportWith,
compoundBalanceReport,
compoundBalanceReportWith,
sortRows,
sortRowsLike,
makeReportQuery,
getPostingsByColumn,
getPostings,
startingBalances,
generateMultiBalanceReport,
tests_MultiBalanceReport
)
where
import Control.Monad (guard)
import Data.Foldable (toList)
import Data.List (sortOn, transpose)
import Data.List.NonEmpty (NonEmpty(..))
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 (fromMaybe, mapMaybe)
import Data.Ord (Down(..))
#if !(MIN_VERSION_base(4,11,0))
import Data.Semigroup ((<>))
#endif
import Data.Semigroup (sconcat)
import Data.Time.Calendar (Day, addDays, fromGregorian)
import Safe (lastDef, minimumMay)
import Hledger.Data
import Hledger.Query
import Hledger.Utils hiding (dbg3,dbg4,dbg5)
import qualified Hledger.Utils
import Hledger.Read (mamountp')
import Hledger.Reports.ReportOptions
import Hledger.Reports.ReportTypes
dbg3 :: [Char] -> a -> a
dbg3 [Char]
s = let p :: [Char]
p = [Char]
"multiBalanceReport" in [Char] -> a -> a
forall a. Show a => [Char] -> a -> a
Hledger.Utils.dbg3 ([Char]
p[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++[Char]
" "[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++[Char]
s)
dbg4 :: [Char] -> a -> a
dbg4 [Char]
s = let p :: [Char]
p = [Char]
"multiBalanceReport" in [Char] -> a -> a
forall a. Show a => [Char] -> a -> a
Hledger.Utils.dbg4 ([Char]
p[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++[Char]
" "[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++[Char]
s)
dbg5 :: [Char] -> a -> a
dbg5 [Char]
s = let p :: [Char]
p = [Char]
"multiBalanceReport" in [Char] -> a -> a
forall a. Show a => [Char] -> a -> a
Hledger.Utils.dbg5 ([Char]
p[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++[Char]
" "[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++[Char]
s)
type MultiBalanceReport = PeriodicReport DisplayName MixedAmount
type MultiBalanceReportRow = PeriodicReportRow DisplayName MixedAmount
type ClippedAccountName = AccountName
multiBalanceReport :: ReportSpec -> Journal -> MultiBalanceReport
multiBalanceReport :: ReportSpec -> Journal -> MultiBalanceReport
multiBalanceReport ReportSpec
rspec Journal
j = ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith ReportSpec
rspec Journal
j (Bool -> Journal -> PriceOracle
journalPriceOracle Bool
infer Journal
j)
where infer :: Bool
infer = ReportOpts -> Bool
infer_value_ (ReportOpts -> Bool) -> ReportOpts -> Bool
forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
rsOpts ReportSpec
rspec
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith ReportSpec
rspec' Journal
j PriceOracle
priceoracle = MultiBalanceReport
report
where
reportspan :: DateSpan
reportspan = [Char] -> DateSpan -> DateSpan
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportspan" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ Journal -> ReportSpec -> DateSpan
reportSpan Journal
j ReportSpec
rspec'
rspec :: ReportSpec
rspec = [Char] -> ReportSpec -> ReportSpec
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportopts" (ReportSpec -> ReportSpec) -> ReportSpec -> ReportSpec
forall a b. (a -> b) -> a -> b
$ ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec' DateSpan
reportspan
colps :: Map DateSpan [Posting]
colps = [Char] -> Map DateSpan [Posting] -> Map DateSpan [Posting]
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"colps" (Map DateSpan [Posting] -> Map DateSpan [Posting])
-> Map DateSpan [Posting] -> Map DateSpan [Posting]
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> DateSpan -> Map DateSpan [Posting]
getPostingsByColumn ReportSpec
rspec Journal
j DateSpan
reportspan
startbals :: HashMap AccountName Account
startbals = [Char]
-> HashMap AccountName Account -> HashMap AccountName Account
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"startbals" (HashMap AccountName Account -> HashMap AccountName Account)
-> HashMap AccountName Account -> HashMap AccountName Account
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal
-> PriceOracle
-> DateSpan
-> HashMap AccountName Account
startingBalances ReportSpec
rspec Journal
j PriceOracle
priceoracle DateSpan
reportspan
report :: MultiBalanceReport
report = [Char] -> MultiBalanceReport -> MultiBalanceReport
forall a. Show a => [Char] -> a -> a
dbg4 [Char]
"multiBalanceReportWith" (MultiBalanceReport -> MultiBalanceReport)
-> MultiBalanceReport -> MultiBalanceReport
forall a b. (a -> b) -> a -> b
$
ReportSpec
-> Journal
-> PriceOracle
-> Map DateSpan [Posting]
-> HashMap AccountName Account
-> MultiBalanceReport
generateMultiBalanceReport ReportSpec
rspec Journal
j PriceOracle
priceoracle Map DateSpan [Posting]
colps HashMap AccountName Account
startbals
compoundBalanceReport :: ReportSpec -> Journal -> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReport :: ReportSpec
-> Journal
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReport ReportSpec
rspec Journal
j = ReportSpec
-> Journal
-> PriceOracle
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
forall a.
ReportSpec
-> Journal
-> PriceOracle
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReportWith ReportSpec
rspec Journal
j (Bool -> Journal -> PriceOracle
journalPriceOracle Bool
infer Journal
j)
where infer :: Bool
infer = ReportOpts -> Bool
infer_value_ (ReportOpts -> Bool) -> ReportOpts -> Bool
forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
rsOpts ReportSpec
rspec
compoundBalanceReportWith :: ReportSpec -> Journal -> PriceOracle
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReportWith :: ReportSpec
-> Journal
-> PriceOracle
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReportWith ReportSpec
rspec' Journal
j PriceOracle
priceoracle [CBCSubreportSpec a]
subreportspecs = CompoundPeriodicReport a MixedAmount
cbr
where
reportspan :: DateSpan
reportspan = [Char] -> DateSpan -> DateSpan
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportspan" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ Journal -> ReportSpec -> DateSpan
reportSpan Journal
j ReportSpec
rspec'
rspec :: ReportSpec
rspec = [Char] -> ReportSpec -> ReportSpec
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportopts" (ReportSpec -> ReportSpec) -> ReportSpec -> ReportSpec
forall a b. (a -> b) -> a -> b
$ ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec' DateSpan
reportspan
colps :: Map DateSpan [Posting]
colps = [Char] -> Map DateSpan [Posting] -> Map DateSpan [Posting]
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"colps" (Map DateSpan [Posting] -> Map DateSpan [Posting])
-> Map DateSpan [Posting] -> Map DateSpan [Posting]
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> DateSpan -> Map DateSpan [Posting]
getPostingsByColumn ReportSpec
rspec Journal
j DateSpan
reportspan
startbals :: HashMap AccountName Account
startbals = [Char]
-> HashMap AccountName Account -> HashMap AccountName Account
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"startbals" (HashMap AccountName Account -> HashMap AccountName Account)
-> HashMap AccountName Account -> HashMap AccountName Account
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal
-> PriceOracle
-> DateSpan
-> HashMap AccountName Account
startingBalances ReportSpec
rspec Journal
j PriceOracle
priceoracle DateSpan
reportspan
subreports :: [(AccountName, PeriodicReport a MixedAmount, Bool)]
subreports = (CBCSubreportSpec a
-> (AccountName, PeriodicReport a MixedAmount, Bool))
-> [CBCSubreportSpec a]
-> [(AccountName, PeriodicReport a MixedAmount, Bool)]
forall a b. (a -> b) -> [a] -> [b]
map CBCSubreportSpec a
-> (AccountName, PeriodicReport a MixedAmount, Bool)
forall a.
CBCSubreportSpec a
-> (AccountName, PeriodicReport a MixedAmount, Bool)
generateSubreport [CBCSubreportSpec a]
subreportspecs
where
generateSubreport :: CBCSubreportSpec a
-> (AccountName, PeriodicReport a MixedAmount, Bool)
generateSubreport CBCSubreportSpec{Bool
AccountName
Journal -> Query
ReportOpts -> ReportOpts
MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreportincreasestotal :: forall a. CBCSubreportSpec a -> Bool
cbcsubreporttransform :: forall a.
CBCSubreportSpec a
-> MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreportoptions :: forall a. CBCSubreportSpec a -> ReportOpts -> ReportOpts
cbcsubreportquery :: forall a. CBCSubreportSpec a -> Journal -> Query
cbcsubreporttitle :: forall a. CBCSubreportSpec a -> AccountName
cbcsubreportincreasestotal :: Bool
cbcsubreporttransform :: MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreportoptions :: ReportOpts -> ReportOpts
cbcsubreportquery :: Journal -> Query
cbcsubreporttitle :: AccountName
..} =
( AccountName
cbcsubreporttitle
, MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreporttransform (MultiBalanceReport -> PeriodicReport a MixedAmount)
-> MultiBalanceReport -> PeriodicReport a MixedAmount
forall a b. (a -> b) -> a -> b
$
ReportSpec
-> Journal
-> PriceOracle
-> Map DateSpan [Posting]
-> HashMap AccountName Account
-> MultiBalanceReport
generateMultiBalanceReport ReportSpec
rspec{rsOpts :: ReportOpts
rsOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle Map DateSpan [Posting]
colps' HashMap AccountName Account
startbals'
, Bool
cbcsubreportincreasestotal
)
where
colps' :: Map DateSpan [Posting]
colps' = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter (Query -> Posting -> Bool
matchesPosting Query
q) ([Posting] -> [Posting])
-> Map DateSpan [Posting] -> Map DateSpan [Posting]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Map DateSpan [Posting]
colps
startbals' :: HashMap AccountName Account
startbals' = (AccountName -> Account -> Bool)
-> HashMap AccountName Account -> HashMap AccountName Account
forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
HM.filterWithKey (\AccountName
k Account
_ -> Query -> AccountName -> Bool
matchesAccount Query
q AccountName
k) HashMap AccountName Account
startbals
ropts :: ReportOpts
ropts = ReportOpts -> ReportOpts
cbcsubreportoptions (ReportOpts -> ReportOpts) -> ReportOpts -> ReportOpts
forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
rsOpts ReportSpec
rspec
q :: Query
q = Journal -> Query
cbcsubreportquery Journal
j
overalltotals :: PeriodicReportRow () MixedAmount
overalltotals = case [(AccountName, PeriodicReport a MixedAmount, Bool)]
subreports of
[] -> ()
-> [MixedAmount]
-> MixedAmount
-> MixedAmount
-> PeriodicReportRow () MixedAmount
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow () [] MixedAmount
nullmixedamt MixedAmount
nullmixedamt
((AccountName, PeriodicReport a MixedAmount, Bool)
r:[(AccountName, PeriodicReport a MixedAmount, Bool)]
rs) -> NonEmpty (PeriodicReportRow () MixedAmount)
-> PeriodicReportRow () MixedAmount
forall a. Semigroup a => NonEmpty a -> a
sconcat (NonEmpty (PeriodicReportRow () MixedAmount)
-> PeriodicReportRow () MixedAmount)
-> NonEmpty (PeriodicReportRow () MixedAmount)
-> PeriodicReportRow () MixedAmount
forall a b. (a -> b) -> a -> b
$ ((AccountName, PeriodicReport a MixedAmount, Bool)
-> PeriodicReportRow () MixedAmount)
-> NonEmpty (AccountName, PeriodicReport a MixedAmount, Bool)
-> NonEmpty (PeriodicReportRow () MixedAmount)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (AccountName, PeriodicReport a MixedAmount, Bool)
-> PeriodicReportRow () MixedAmount
forall b a a.
Num b =>
(a, PeriodicReport a b, Bool) -> PeriodicReportRow () b
subreportTotal ((AccountName, PeriodicReport a MixedAmount, Bool)
r(AccountName, PeriodicReport a MixedAmount, Bool)
-> [(AccountName, PeriodicReport a MixedAmount, Bool)]
-> NonEmpty (AccountName, PeriodicReport a MixedAmount, Bool)
forall a. a -> [a] -> NonEmpty a
:|[(AccountName, PeriodicReport a MixedAmount, Bool)]
rs)
where
subreportTotal :: (a, PeriodicReport a b, Bool) -> PeriodicReportRow () b
subreportTotal (a
_, PeriodicReport a b
sr, Bool
increasestotal) =
(if Bool
increasestotal then PeriodicReportRow () b -> PeriodicReportRow () b
forall a. a -> a
id else (b -> b) -> PeriodicReportRow () b -> PeriodicReportRow () b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap b -> b
forall a. Num a => a -> a
negate) (PeriodicReportRow () b -> PeriodicReportRow () b)
-> PeriodicReportRow () b -> PeriodicReportRow () b
forall a b. (a -> b) -> a -> b
$ PeriodicReport a b -> PeriodicReportRow () b
forall a b. PeriodicReport a b -> PeriodicReportRow () b
prTotals PeriodicReport a b
sr
cbr :: CompoundPeriodicReport a MixedAmount
cbr = AccountName
-> [DateSpan]
-> [(AccountName, PeriodicReport a MixedAmount, Bool)]
-> PeriodicReportRow () MixedAmount
-> CompoundPeriodicReport a MixedAmount
forall a b.
AccountName
-> [DateSpan]
-> [(AccountName, PeriodicReport a b, Bool)]
-> PeriodicReportRow () b
-> CompoundPeriodicReport a b
CompoundPeriodicReport AccountName
"" (Map DateSpan [Posting] -> [DateSpan]
forall k a. Map k a -> [k]
M.keys Map DateSpan [Posting]
colps) [(AccountName, PeriodicReport a MixedAmount, Bool)]
subreports PeriodicReportRow () MixedAmount
overalltotals
startingBalances :: ReportSpec -> Journal -> PriceOracle -> DateSpan -> HashMap AccountName Account
startingBalances :: ReportSpec
-> Journal
-> PriceOracle
-> DateSpan
-> HashMap AccountName Account
startingBalances rspec :: ReportSpec
rspec@ReportSpec{rsQuery :: ReportSpec -> Query
rsQuery=Query
query,rsOpts :: ReportSpec -> ReportOpts
rsOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle DateSpan
reportspan =
(Map DateSpan Account -> Account)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName Account
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Account -> DateSpan -> Map DateSpan Account -> Account
forall k a. Ord k => a -> k -> Map k a -> a
M.findWithDefault Account
nullacct DateSpan
precedingspan) HashMap AccountName (Map DateSpan Account)
acctmap
where
acctmap :: HashMap AccountName (Map DateSpan Account)
acctmap = ReportSpec
-> Journal
-> PriceOracle
-> HashMap AccountName Account
-> Map DateSpan [Posting]
-> HashMap AccountName (Map DateSpan Account)
calculateReportMatrix ReportSpec
rspec' Journal
j PriceOracle
priceoracle HashMap AccountName Account
forall a. Monoid a => a
mempty
(Map DateSpan [Posting]
-> HashMap AccountName (Map DateSpan Account))
-> ([(Posting, Day)] -> Map DateSpan [Posting])
-> [(Posting, Day)]
-> HashMap AccountName (Map DateSpan Account)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DateSpan -> [Posting] -> Map DateSpan [Posting]
forall k a. k -> a -> Map k a
M.singleton DateSpan
precedingspan ([Posting] -> Map DateSpan [Posting])
-> ([(Posting, Day)] -> [Posting])
-> [(Posting, Day)]
-> Map DateSpan [Posting]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Posting, Day) -> Posting) -> [(Posting, Day)] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map (Posting, Day) -> Posting
forall a b. (a, b) -> a
fst ([(Posting, Day)] -> HashMap AccountName (Map DateSpan Account))
-> [(Posting, Day)] -> HashMap AccountName (Map DateSpan Account)
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> [(Posting, Day)]
getPostings ReportSpec
rspec' Journal
j
rspec' :: ReportSpec
rspec' = ReportSpec
rspec{rsQuery :: Query
rsQuery=Query
startbalq,rsOpts :: ReportOpts
rsOpts=ReportOpts
ropts'}
ropts' :: ReportOpts
ropts' = case ReportOpts -> Maybe ValuationType
value_ ReportOpts
ropts of
Just (AtEnd Maybe AccountName
_) -> ReportOpts
ropts''{value_ :: Maybe ValuationType
value_=Maybe ValuationType
forall a. Maybe a
Nothing}
Maybe ValuationType
_ -> ReportOpts
ropts''
where ropts'' :: ReportOpts
ropts'' = ReportOpts
ropts{period_ :: Period
period_=Period
precedingperiod, no_elide_ :: Bool
no_elide_=ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts AccountListMode -> AccountListMode -> Bool
forall a. Eq a => a -> a -> Bool
== AccountListMode
ALTree}
startbalq :: Query
startbalq = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"startbalq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query
datelessq, Query
precedingspanq]
datelessq :: Query
datelessq = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"datelessq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not (Bool -> Bool) -> (Query -> Bool) -> Query -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Bool
queryIsDateOrDate2) Query
query
precedingperiod :: Period
precedingperiod = DateSpan -> Period
dateSpanAsPeriod (DateSpan -> Period) -> (Period -> DateSpan) -> Period -> Period
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DateSpan -> DateSpan -> DateSpan
spanIntersect DateSpan
precedingspan (DateSpan -> DateSpan)
-> (Period -> DateSpan) -> Period -> DateSpan
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
Period -> DateSpan
periodAsDateSpan (Period -> Period) -> Period -> Period
forall a b. (a -> b) -> a -> b
$ ReportOpts -> Period
period_ ReportOpts
ropts
precedingspan :: DateSpan
precedingspan = Maybe Day -> Maybe Day -> DateSpan
DateSpan Maybe Day
forall a. Maybe a
Nothing (Maybe Day -> DateSpan) -> Maybe Day -> DateSpan
forall a b. (a -> b) -> a -> b
$ DateSpan -> Maybe Day
spanStart DateSpan
reportspan
precedingspanq :: Query
precedingspanq = (if ReportOpts -> Bool
date2_ ReportOpts
ropts then DateSpan -> Query
Date2 else DateSpan -> Query
Date) (DateSpan -> Query) -> DateSpan -> Query
forall a b. (a -> b) -> a -> b
$ case DateSpan
precedingspan of
DateSpan Maybe Day
Nothing Maybe Day
Nothing -> DateSpan
emptydatespan
DateSpan
a -> DateSpan
a
makeReportQuery :: ReportSpec -> DateSpan -> ReportSpec
makeReportQuery :: ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec DateSpan
reportspan
| DateSpan
reportspan DateSpan -> DateSpan -> Bool
forall a. Eq a => a -> a -> Bool
== DateSpan
nulldatespan = ReportSpec
rspec
| Bool
otherwise = ReportSpec
rspec{rsQuery :: Query
rsQuery=Query
query}
where
query :: Query
query = Query -> Query
simplifyQuery (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query -> Query
dateless (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Query
rsQuery ReportSpec
rspec, Query
reportspandatesq]
reportspandatesq :: Query
reportspandatesq = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportspandatesq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
dateqcons DateSpan
reportspan
dateless :: Query -> Query
dateless = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"dateless" (Query -> Query) -> (Query -> Query) -> Query -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not (Bool -> Bool) -> (Query -> Bool) -> Query -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Bool
queryIsDateOrDate2)
dateqcons :: DateSpan -> Query
dateqcons = if ReportOpts -> Bool
date2_ (ReportSpec -> ReportOpts
rsOpts ReportSpec
rspec) then DateSpan -> Query
Date2 else DateSpan -> Query
Date
getPostingsByColumn :: ReportSpec -> Journal -> DateSpan -> Map DateSpan [Posting]
getPostingsByColumn :: ReportSpec -> Journal -> DateSpan -> Map DateSpan [Posting]
getPostingsByColumn ReportSpec
rspec Journal
j DateSpan
reportspan = Map DateSpan [Posting]
columns
where
[(Posting, Day)]
ps :: [(Posting, Day)] = [Char] -> [(Posting, Day)] -> [(Posting, Day)]
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"getPostingsByColumn" ([(Posting, Day)] -> [(Posting, Day)])
-> [(Posting, Day)] -> [(Posting, Day)]
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> [(Posting, Day)]
getPostings ReportSpec
rspec Journal
j
colspans :: [DateSpan]
colspans = [Char] -> [DateSpan] -> [DateSpan]
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"colspans" ([DateSpan] -> [DateSpan]) -> [DateSpan] -> [DateSpan]
forall a b. (a -> b) -> a -> b
$ Interval -> DateSpan -> [DateSpan]
splitSpan (ReportOpts -> Interval
interval_ (ReportOpts -> Interval) -> ReportOpts -> Interval
forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
rsOpts ReportSpec
rspec) DateSpan
reportspan
addPosting :: (a, Day) -> Map DateSpan [a] -> Map DateSpan [a]
addPosting (a
p, Day
d) = (Map DateSpan [a] -> Map DateSpan [a])
-> (DateSpan -> Map DateSpan [a] -> Map DateSpan [a])
-> Maybe DateSpan
-> Map DateSpan [a]
-> Map DateSpan [a]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Map DateSpan [a] -> Map DateSpan [a]
forall a. a -> a
id (([a] -> [a]) -> DateSpan -> Map DateSpan [a] -> Map DateSpan [a]
forall k a. Ord k => (a -> a) -> k -> Map k a -> Map k a
M.adjust (a
pa -> [a] -> [a]
forall a. a -> [a] -> [a]
:)) (Maybe DateSpan -> Map DateSpan [a] -> Map DateSpan [a])
-> Maybe DateSpan -> Map DateSpan [a] -> Map DateSpan [a]
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> Day -> Maybe DateSpan
latestSpanContaining [DateSpan]
colspans Day
d
emptyMap :: Map DateSpan [a]
emptyMap = [(DateSpan, [a])] -> Map DateSpan [a]
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList ([(DateSpan, [a])] -> Map DateSpan [a])
-> ([[a]] -> [(DateSpan, [a])]) -> [[a]] -> Map DateSpan [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [DateSpan] -> [[a]] -> [(DateSpan, [a])]
forall a b. [a] -> [b] -> [(a, b)]
zip [DateSpan]
colspans ([[a]] -> Map DateSpan [a]) -> [[a]] -> Map DateSpan [a]
forall a b. (a -> b) -> a -> b
$ [a] -> [[a]]
forall a. a -> [a]
repeat []
columns :: Map DateSpan [Posting]
columns = ((Posting, Day)
-> Map DateSpan [Posting] -> Map DateSpan [Posting])
-> Map DateSpan [Posting]
-> [(Posting, Day)]
-> Map DateSpan [Posting]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (Posting, Day) -> Map DateSpan [Posting] -> Map DateSpan [Posting]
forall a. (a, Day) -> Map DateSpan [a] -> Map DateSpan [a]
addPosting Map DateSpan [Posting]
forall a. Map DateSpan [a]
emptyMap [(Posting, Day)]
ps
getPostings :: ReportSpec -> Journal -> [(Posting, Day)]
getPostings :: ReportSpec -> Journal -> [(Posting, Day)]
getPostings ReportSpec{rsQuery :: ReportSpec -> Query
rsQuery=Query
query,rsOpts :: ReportSpec -> ReportOpts
rsOpts=ReportOpts
ropts} =
(Posting -> (Posting, Day)) -> [Posting] -> [(Posting, Day)]
forall a b. (a -> b) -> [a] -> [b]
map (\Posting
p -> (Posting
p, Posting -> Day
date Posting
p)) ([Posting] -> [(Posting, Day)])
-> (Journal -> [Posting]) -> Journal -> [(Posting, Day)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
Journal -> [Posting]
journalPostings (Journal -> [Posting])
-> (Journal -> Journal) -> Journal -> [Posting]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
Query -> Journal -> Journal
filterJournalAmounts Query
symq (Journal -> Journal) -> (Journal -> Journal) -> Journal -> Journal
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
Query -> Journal -> Journal
filterJournalPostings Query
reportq
where
symq :: Query
symq = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"symq" (Query -> Query) -> (Query -> Query) -> Query -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsSym (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"requested q" Query
query
reportq :: Query
reportq = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ Query -> Query
depthless Query
query
depthless :: Query -> Query
depthless = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"depthless" (Query -> Query) -> (Query -> Query) -> Query -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not (Bool -> Bool) -> (Query -> Bool) -> Query -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Bool
queryIsDepth)
date :: Posting -> Day
date = case ReportOpts -> WhichDate
whichDateFromOpts ReportOpts
ropts of
WhichDate
PrimaryDate -> Posting -> Day
postingDate
WhichDate
SecondaryDate -> Posting -> Day
postingDate2
acctChangesFromPostings :: ReportSpec -> [Posting] -> HashMap ClippedAccountName Account
acctChangesFromPostings :: ReportSpec -> [Posting] -> HashMap AccountName Account
acctChangesFromPostings ReportSpec{rsQuery :: ReportSpec -> Query
rsQuery=Query
query,rsOpts :: ReportSpec -> ReportOpts
rsOpts=ReportOpts
ropts} [Posting]
ps =
[(AccountName, Account)] -> HashMap AccountName Account
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList [(Account -> AccountName
aname Account
a, Account
a) | Account
a <- [Account]
as]
where
as :: [Account]
as = [Account] -> [Account]
filterAccounts ([Account] -> [Account])
-> ([Account] -> [Account]) -> [Account] -> [Account]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [Account] -> [Account]
forall a. Int -> [a] -> [a]
drop Int
1 ([Account] -> [Account]) -> [Account] -> [Account]
forall a b. (a -> b) -> a -> b
$ [Posting] -> [Account]
accountsFromPostings [Posting]
ps
filterAccounts :: [Account] -> [Account]
filterAccounts = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> (Account -> Bool) -> [Account] -> [Account]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Query
depthq Query -> AccountName -> Bool
`matchesAccount`) (AccountName -> Bool)
-> (Account -> AccountName) -> Account -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account -> AccountName
aname)
AccountListMode
ALFlat -> Maybe Int -> [Account] -> [Account]
clipAccountsAndAggregate (Query -> Maybe Int
queryDepth Query
depthq) ([Account] -> [Account])
-> ([Account] -> [Account]) -> [Account] -> [Account]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
(Account -> Bool) -> [Account] -> [Account]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Int
0Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<) (Int -> Bool) -> (Account -> Int) -> Account -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account -> Int
anumpostings)
depthq :: Query
depthq = [Char] -> Query -> Query
forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"depthq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth Query
query
calculateReportMatrix :: ReportSpec -> Journal -> PriceOracle
-> HashMap ClippedAccountName Account
-> Map DateSpan [Posting]
-> HashMap ClippedAccountName (Map DateSpan Account)
calculateReportMatrix :: ReportSpec
-> Journal
-> PriceOracle
-> HashMap AccountName Account
-> Map DateSpan [Posting]
-> HashMap AccountName (Map DateSpan Account)
calculateReportMatrix rspec :: ReportSpec
rspec@ReportSpec{rsOpts :: ReportSpec -> ReportOpts
rsOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle HashMap AccountName Account
startbals Map DateSpan [Posting]
colps =
(AccountName -> Map DateSpan Account -> Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall k v1 v2. (k -> v1 -> v2) -> HashMap k v1 -> HashMap k v2
HM.mapWithKey AccountName -> Map DateSpan Account -> Map DateSpan Account
rowbals HashMap AccountName (Map DateSpan Account)
allchanges
where
rowbals :: AccountName -> Map DateSpan Account -> Map DateSpan Account
rowbals AccountName
name Map DateSpan Account
changes = [Char] -> Map DateSpan Account -> Map DateSpan Account
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"rowbals" (Map DateSpan Account -> Map DateSpan Account)
-> Map DateSpan Account -> Map DateSpan Account
forall a b. (a -> b) -> a -> b
$ case ReportOpts -> BalanceType
balancetype_ ReportOpts
ropts of
BalanceType
PeriodChange -> Map DateSpan Account
changeamts
BalanceType
CumulativeChange -> (DateSpan -> Account -> Account)
-> Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum DateSpan -> Account -> Account
avalue Account
nullacct Map DateSpan Account
changeamts
BalanceType
HistoricalBalance -> Map DateSpan Account
historical
where
changeamts :: Map DateSpan Account
changeamts = case ReportOpts -> ReportType
reporttype_ ReportOpts
ropts of
ReportType
ChangeReport -> (DateSpan -> Account -> Account)
-> Map DateSpan Account -> Map DateSpan Account
forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey DateSpan -> Account -> Account
avalue Map DateSpan Account
changes
ReportType
BudgetReport -> (DateSpan -> Account -> Account)
-> Map DateSpan Account -> Map DateSpan Account
forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey DateSpan -> Account -> Account
avalue Map DateSpan Account
changes
ReportType
ValueChangeReport -> Account -> Map DateSpan Account -> Map DateSpan Account
forall k. Account -> Map k Account -> Map k Account
periodChanges Account
valuedStart Map DateSpan Account
historical
historical :: Map DateSpan Account
historical = (DateSpan -> Account -> Account)
-> Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum DateSpan -> Account -> Account
avalue Account
startingBalance Map DateSpan Account
changes
startingBalance :: Account
startingBalance = Account -> AccountName -> HashMap AccountName Account -> Account
forall k v. (Eq k, Hashable k) => v -> k -> HashMap k v -> v
HM.lookupDefault Account
nullacct AccountName
name HashMap AccountName Account
startbals
valuedStart :: Account
valuedStart = DateSpan -> Account -> Account
avalue (Maybe Day -> Maybe Day -> DateSpan
DateSpan Maybe Day
forall a. Maybe a
Nothing Maybe Day
historicalDate) Account
startingBalance
allchanges :: HashMap AccountName (Map DateSpan Account)
allchanges = ((Map DateSpan Account
-> Map DateSpan Account -> Map DateSpan Account
forall a. Semigroup a => a -> a -> a
<>Map DateSpan Account
zeros) (Map DateSpan Account -> Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashMap AccountName (Map DateSpan Account)
acctchanges) HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall a. Semigroup a => a -> a -> a
<> (Map DateSpan Account
zeros Map DateSpan Account
-> HashMap AccountName Account
-> HashMap AccountName (Map DateSpan Account)
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ HashMap AccountName Account
startbals)
acctchanges :: HashMap AccountName (Map DateSpan Account)
acctchanges = [Char]
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"acctchanges" (HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account))
-> (HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account))
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
addElided (HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account))
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall a b. (a -> b) -> a -> b
$ Map DateSpan (HashMap AccountName Account)
-> HashMap AccountName (Map DateSpan Account)
forall a.
Map DateSpan (HashMap AccountName a)
-> HashMap AccountName (Map DateSpan a)
transposeMap Map DateSpan (HashMap AccountName Account)
colacctchanges
colacctchanges :: Map DateSpan (HashMap AccountName Account)
colacctchanges = [Char]
-> Map DateSpan (HashMap AccountName Account)
-> Map DateSpan (HashMap AccountName Account)
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"colacctchanges" (Map DateSpan (HashMap AccountName Account)
-> Map DateSpan (HashMap AccountName Account))
-> Map DateSpan (HashMap AccountName Account)
-> Map DateSpan (HashMap AccountName Account)
forall a b. (a -> b) -> a -> b
$ ([Posting] -> HashMap AccountName Account)
-> Map DateSpan [Posting]
-> Map DateSpan (HashMap AccountName Account)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ReportSpec -> [Posting] -> HashMap AccountName Account
acctChangesFromPostings ReportSpec
rspec) Map DateSpan [Posting]
valuedps
valuedps :: Map DateSpan [Posting]
valuedps = (DateSpan -> [Posting] -> [Posting])
-> Map DateSpan [Posting] -> Map DateSpan [Posting]
forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey (\DateSpan
colspan -> (Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map (DateSpan -> Posting -> Posting
pvalue DateSpan
colspan)) Map DateSpan [Posting]
colps
(DateSpan -> Posting -> Posting
pvalue, DateSpan -> Account -> Account
avalue) = ReportSpec
-> Journal
-> PriceOracle
-> (DateSpan -> Posting -> Posting, DateSpan -> Account -> Account)
postingAndAccountValuations ReportSpec
rspec Journal
j PriceOracle
priceoracle
addElided :: HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
addElided = if Query -> Maybe Int
queryDepth (ReportSpec -> Query
rsQuery ReportSpec
rspec) Maybe Int -> Maybe Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int -> Maybe Int
forall a. a -> Maybe a
Just Int
0 then AccountName
-> Map DateSpan Account
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
HM.insert AccountName
"..." Map DateSpan Account
zeros else HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall a. a -> a
id
historicalDate :: Maybe Day
historicalDate = [Day] -> Maybe Day
forall a. Ord a => [a] -> Maybe a
minimumMay ([Day] -> Maybe Day) -> [Day] -> Maybe Day
forall a b. (a -> b) -> a -> b
$ (DateSpan -> Maybe Day) -> [DateSpan] -> [Day]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe DateSpan -> Maybe Day
spanStart [DateSpan]
colspans
zeros :: Map DateSpan Account
zeros = [(DateSpan, Account)] -> Map DateSpan Account
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(DateSpan
span, Account
nullacct) | DateSpan
span <- [DateSpan]
colspans]
colspans :: [DateSpan]
colspans = Map DateSpan [Posting] -> [DateSpan]
forall k a. Map k a -> [k]
M.keys Map DateSpan [Posting]
colps
generateMultiBalanceReport :: ReportSpec -> Journal -> PriceOracle
-> Map DateSpan [Posting] -> HashMap AccountName Account
-> MultiBalanceReport
generateMultiBalanceReport :: ReportSpec
-> Journal
-> PriceOracle
-> Map DateSpan [Posting]
-> HashMap AccountName Account
-> MultiBalanceReport
generateMultiBalanceReport rspec :: ReportSpec
rspec@ReportSpec{rsOpts :: ReportSpec -> ReportOpts
rsOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle Map DateSpan [Posting]
colps HashMap AccountName Account
startbals =
MultiBalanceReport
report
where
matrix :: HashMap AccountName (Map DateSpan Account)
matrix = ReportSpec
-> Journal
-> PriceOracle
-> HashMap AccountName Account
-> Map DateSpan [Posting]
-> HashMap AccountName (Map DateSpan Account)
calculateReportMatrix ReportSpec
rspec Journal
j PriceOracle
priceoracle HashMap AccountName Account
startbals Map DateSpan [Posting]
colps
displaynames :: HashMap AccountName DisplayName
displaynames = [Char]
-> HashMap AccountName DisplayName
-> HashMap AccountName DisplayName
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"displaynames" (HashMap AccountName DisplayName
-> HashMap AccountName DisplayName)
-> HashMap AccountName DisplayName
-> HashMap AccountName DisplayName
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName DisplayName
displayedAccounts ReportSpec
rspec HashMap AccountName (Map DateSpan Account)
matrix
rows :: [PeriodicReportRow DisplayName MixedAmount]
rows = [Char]
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"rows" ([PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount])
-> ([PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount])
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (if ReportOpts -> Bool
invert_ ReportOpts
ropts then (PeriodicReportRow DisplayName MixedAmount
-> PeriodicReportRow DisplayName MixedAmount)
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map ((MixedAmount -> MixedAmount)
-> PeriodicReportRow DisplayName MixedAmount
-> PeriodicReportRow DisplayName MixedAmount
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MixedAmount -> MixedAmount
forall a. Num a => a -> a
negate) else [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a. a -> a
id)
([PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount])
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> HashMap AccountName DisplayName
-> HashMap AccountName (Map DateSpan Account)
-> [PeriodicReportRow DisplayName MixedAmount]
buildReportRows ReportOpts
ropts HashMap AccountName DisplayName
displaynames HashMap AccountName (Map DateSpan Account)
matrix
totalsrow :: PeriodicReportRow () MixedAmount
totalsrow = [Char]
-> PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"totalsrow" (PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount)
-> PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> [PeriodicReportRow DisplayName MixedAmount]
-> PeriodicReportRow () MixedAmount
calculateTotalsRow ReportOpts
ropts [PeriodicReportRow DisplayName MixedAmount]
rows
sortedrows :: [PeriodicReportRow DisplayName MixedAmount]
sortedrows = [Char]
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"sortedrows" ([PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount])
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> Journal
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortRows ReportOpts
ropts Journal
j [PeriodicReportRow DisplayName MixedAmount]
rows
report :: MultiBalanceReport
report = ReportOpts -> MultiBalanceReport -> MultiBalanceReport
reportPercent ReportOpts
ropts (MultiBalanceReport -> MultiBalanceReport)
-> MultiBalanceReport -> MultiBalanceReport
forall a b. (a -> b) -> a -> b
$ [DateSpan]
-> [PeriodicReportRow DisplayName MixedAmount]
-> PeriodicReportRow () MixedAmount
-> MultiBalanceReport
forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport (Map DateSpan [Posting] -> [DateSpan]
forall k a. Map k a -> [k]
M.keys Map DateSpan [Posting]
colps) [PeriodicReportRow DisplayName MixedAmount]
sortedrows PeriodicReportRow () MixedAmount
totalsrow
buildReportRows :: ReportOpts
-> HashMap AccountName DisplayName
-> HashMap AccountName (Map DateSpan Account)
-> [MultiBalanceReportRow]
buildReportRows :: ReportOpts
-> HashMap AccountName DisplayName
-> HashMap AccountName (Map DateSpan Account)
-> [PeriodicReportRow DisplayName MixedAmount]
buildReportRows ReportOpts
ropts HashMap AccountName DisplayName
displaynames =
HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
-> [PeriodicReportRow DisplayName MixedAmount]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
-> [PeriodicReportRow DisplayName MixedAmount])
-> (HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount))
-> HashMap AccountName (Map DateSpan Account)
-> [PeriodicReportRow DisplayName MixedAmount]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (AccountName
-> Map DateSpan Account
-> Maybe (PeriodicReportRow DisplayName MixedAmount))
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
forall k v1 v2.
(k -> v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2
HM.mapMaybeWithKey AccountName
-> Map DateSpan Account
-> Maybe (PeriodicReportRow DisplayName MixedAmount)
forall (t :: * -> *).
Foldable t =>
AccountName
-> t Account -> Maybe (PeriodicReportRow DisplayName MixedAmount)
mkRow
where
mkRow :: AccountName
-> t Account -> Maybe (PeriodicReportRow DisplayName MixedAmount)
mkRow AccountName
name t Account
accts = do
DisplayName
displayname <- AccountName -> HashMap AccountName DisplayName -> Maybe DisplayName
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup AccountName
name HashMap AccountName DisplayName
displaynames
PeriodicReportRow DisplayName MixedAmount
-> Maybe (PeriodicReportRow DisplayName MixedAmount)
forall (m :: * -> *) a. Monad m => a -> m a
return (PeriodicReportRow DisplayName MixedAmount
-> Maybe (PeriodicReportRow DisplayName MixedAmount))
-> PeriodicReportRow DisplayName MixedAmount
-> Maybe (PeriodicReportRow DisplayName MixedAmount)
forall a b. (a -> b) -> a -> b
$ DisplayName
-> [MixedAmount]
-> MixedAmount
-> MixedAmount
-> PeriodicReportRow DisplayName MixedAmount
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow DisplayName
displayname [MixedAmount]
rowbals MixedAmount
rowtot MixedAmount
rowavg
where
rowbals :: [MixedAmount]
rowbals = (Account -> MixedAmount) -> [Account] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map Account -> MixedAmount
balance ([Account] -> [MixedAmount]) -> [Account] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ t Account -> [Account]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList t Account
accts
rowtot :: MixedAmount
rowtot = case ReportOpts -> BalanceType
balancetype_ ReportOpts
ropts of
BalanceType
PeriodChange -> [MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [MixedAmount]
rowbals
BalanceType
_ -> MixedAmount -> [MixedAmount] -> MixedAmount
forall a. a -> [a] -> a
lastDef MixedAmount
0 [MixedAmount]
rowbals
rowavg :: MixedAmount
rowavg = [MixedAmount] -> MixedAmount
averageMixedAmounts [MixedAmount]
rowbals
balance :: Account -> MixedAmount
balance = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of AccountListMode
ALTree -> Account -> MixedAmount
aibalance; AccountListMode
ALFlat -> Account -> MixedAmount
aebalance
displayedAccounts :: ReportSpec -> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName DisplayName
displayedAccounts :: ReportSpec
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName DisplayName
displayedAccounts ReportSpec{rsQuery :: ReportSpec -> Query
rsQuery=Query
query,rsOpts :: ReportSpec -> ReportOpts
rsOpts=ReportOpts
ropts} HashMap AccountName (Map DateSpan Account)
valuedaccts
| Int
depth Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = AccountName -> DisplayName -> HashMap AccountName DisplayName
forall k v. Hashable k => k -> v -> HashMap k v
HM.singleton AccountName
"..." (DisplayName -> HashMap AccountName DisplayName)
-> DisplayName -> HashMap AccountName DisplayName
forall a b. (a -> b) -> a -> b
$ AccountName -> AccountName -> Int -> DisplayName
DisplayName AccountName
"..." AccountName
"..." Int
1
| Bool
otherwise = (AccountName -> Map DateSpan Account -> DisplayName)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName DisplayName
forall k v1 v2. (k -> v1 -> v2) -> HashMap k v1 -> HashMap k v2
HM.mapWithKey (\AccountName
a Map DateSpan Account
_ -> AccountName -> DisplayName
displayedName AccountName
a) HashMap AccountName (Map DateSpan Account)
displayedAccts
where
displayedAccts :: HashMap AccountName (Map DateSpan Account)
displayedAccts = (if Int
depth Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall a. a -> a
id else (AccountName -> Map DateSpan Account -> Bool)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
HM.filterWithKey AccountName -> Map DateSpan Account -> Bool
forall (t :: * -> *).
Foldable t =>
AccountName -> t Account -> Bool
keep) HashMap AccountName (Map DateSpan Account)
valuedaccts
where
keep :: AccountName -> t Account -> Bool
keep AccountName
name t Account
amts = AccountName -> t Account -> Bool
forall (t :: * -> *).
Foldable t =>
AccountName -> t Account -> Bool
isInteresting AccountName
name t Account
amts Bool -> Bool -> Bool
|| AccountName
name AccountName -> HashMap AccountName Int -> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
`HM.member` HashMap AccountName Int
interestingParents
displayedName :: AccountName -> DisplayName
displayedName AccountName
name = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> AccountName -> AccountName -> Int -> DisplayName
DisplayName AccountName
name AccountName
leaf (Int -> DisplayName) -> (Int -> Int) -> Int -> DisplayName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> DisplayName) -> Int -> DisplayName
forall a b. (a -> b) -> a -> b
$ Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
boringParents
AccountListMode
ALFlat -> AccountName -> AccountName -> Int -> DisplayName
DisplayName AccountName
name AccountName
droppedName Int
1
where
droppedName :: AccountName
droppedName = Int -> AccountName -> AccountName
accountNameDrop (ReportOpts -> Int
drop_ ReportOpts
ropts) AccountName
name
leaf :: AccountName
leaf = [AccountName] -> AccountName
accountNameFromComponents ([AccountName] -> AccountName)
-> ([AccountName] -> [AccountName]) -> [AccountName] -> AccountName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [AccountName] -> [AccountName]
forall a. [a] -> [a]
reverse ([AccountName] -> [AccountName])
-> ([AccountName] -> [AccountName])
-> [AccountName]
-> [AccountName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (AccountName -> AccountName) -> [AccountName] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map AccountName -> AccountName
accountLeafName ([AccountName] -> AccountName) -> [AccountName] -> AccountName
forall a b. (a -> b) -> a -> b
$
AccountName
droppedName AccountName -> [AccountName] -> [AccountName]
forall a. a -> [a] -> [a]
: (AccountName -> Bool) -> [AccountName] -> [AccountName]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile AccountName -> Bool
notDisplayed [AccountName]
parents
level :: Int
level = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ AccountName -> Int
accountNameLevel AccountName
name Int -> Int -> Int
forall a. Num a => a -> a -> a
- ReportOpts -> Int
drop_ ReportOpts
ropts
parents :: [AccountName]
parents = Int -> [AccountName] -> [AccountName]
forall a. Int -> [a] -> [a]
take (Int
level Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ([AccountName] -> [AccountName]) -> [AccountName] -> [AccountName]
forall a b. (a -> b) -> a -> b
$ AccountName -> [AccountName]
parentAccountNames AccountName
name
boringParents :: Int
boringParents = if ReportOpts -> Bool
no_elide_ ReportOpts
ropts then Int
0 else [AccountName] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([AccountName] -> Int) -> [AccountName] -> Int
forall a b. (a -> b) -> a -> b
$ (AccountName -> Bool) -> [AccountName] -> [AccountName]
forall a. (a -> Bool) -> [a] -> [a]
filter AccountName -> Bool
notDisplayed [AccountName]
parents
notDisplayed :: AccountName -> Bool
notDisplayed = Bool -> Bool
not (Bool -> Bool) -> (AccountName -> Bool) -> AccountName -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (AccountName -> HashMap AccountName (Map DateSpan Account) -> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
`HM.member` HashMap AccountName (Map DateSpan Account)
displayedAccts)
isInteresting :: AccountName -> t Account -> Bool
isInteresting AccountName
name t Account
amts =
Int
d Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
depth
Bool -> Bool -> Bool
&& ((ReportOpts -> Bool
empty_ ReportOpts
ropts Bool -> Bool -> Bool
&& (Account -> Bool) -> t Account -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ([Account] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Account] -> Bool) -> (Account -> [Account]) -> Account -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account -> [Account]
asubs) t Account
amts)
Bool -> Bool -> Bool
|| Bool -> Bool
not ((Account -> MixedAmount) -> t Account -> Bool
forall (t :: * -> *) a.
Foldable t =>
(a -> MixedAmount) -> t a -> Bool
isZeroRow Account -> MixedAmount
balance t Account
amts))
where
d :: Int
d = AccountName -> Int
accountNameLevel AccountName
name
balance :: Account -> MixedAmount
balance | AccountListMode
ALTree <- ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts, Int
d Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
depth = Account -> MixedAmount
aibalance
| Bool
otherwise = Account -> MixedAmount
aebalance
interestingParents :: HashMap AccountName Int
interestingParents = [Char] -> HashMap AccountName Int -> HashMap AccountName Int
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"interestingParents" (HashMap AccountName Int -> HashMap AccountName Int)
-> HashMap AccountName Int -> HashMap AccountName Int
forall a b. (a -> b) -> a -> b
$ case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> (AccountName -> Int -> Bool)
-> HashMap AccountName Int -> HashMap AccountName Int
forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
HM.filterWithKey AccountName -> Int -> Bool
hasEnoughSubs HashMap AccountName Int
numSubs
AccountListMode
ALFlat -> HashMap AccountName Int
forall a. Monoid a => a
mempty
where
hasEnoughSubs :: AccountName -> Int -> Bool
hasEnoughSubs AccountName
name Int
nsubs = Int
nsubs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
minSubs Bool -> Bool -> Bool
&& AccountName -> Int
accountNameLevel AccountName
name Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> ReportOpts -> Int
drop_ ReportOpts
ropts
minSubs :: Int
minSubs = if ReportOpts -> Bool
no_elide_ ReportOpts
ropts then Int
1 else Int
2
isZeroRow :: (a -> MixedAmount) -> t a -> Bool
isZeroRow a -> MixedAmount
balance = (a -> Bool) -> t a -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (MixedAmount -> Bool
mixedAmountLooksZero (MixedAmount -> Bool) -> (a -> MixedAmount) -> a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> MixedAmount
balance)
depth :: Int
depth = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
forall a. Bounded a => a
maxBound (Maybe Int -> Int) -> Maybe Int -> Int
forall a b. (a -> b) -> a -> b
$ Query -> Maybe Int
queryDepth Query
query
numSubs :: HashMap AccountName Int
numSubs = [AccountName] -> HashMap AccountName Int
subaccountTallies ([AccountName] -> HashMap AccountName Int)
-> (HashMap AccountName (Map DateSpan Account) -> [AccountName])
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap AccountName (Map DateSpan Account) -> [AccountName]
forall k v. HashMap k v -> [k]
HM.keys (HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName Int)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName Int
forall a b. (a -> b) -> a -> b
$ (AccountName -> Map DateSpan Account -> Bool)
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName (Map DateSpan Account)
forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
HM.filterWithKey AccountName -> Map DateSpan Account -> Bool
forall (t :: * -> *).
Foldable t =>
AccountName -> t Account -> Bool
isInteresting HashMap AccountName (Map DateSpan Account)
valuedaccts
sortRows :: ReportOpts -> Journal -> [MultiBalanceReportRow] -> [MultiBalanceReportRow]
sortRows :: ReportOpts
-> Journal
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortRows ReportOpts
ropts Journal
j
| ReportOpts -> Bool
sort_amount_ ReportOpts
ropts, AccountListMode
ALTree <- ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts = [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortTreeMBRByAmount
| ReportOpts -> Bool
sort_amount_ ReportOpts
ropts, AccountListMode
ALFlat <- ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts = [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortFlatMBRByAmount
| Bool
otherwise = [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortMBRByAccountDeclaration
where
sortTreeMBRByAmount :: [MultiBalanceReportRow] -> [MultiBalanceReportRow]
sortTreeMBRByAmount :: [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortTreeMBRByAmount [PeriodicReportRow DisplayName MixedAmount]
rows = (AccountName -> Maybe (PeriodicReportRow DisplayName MixedAmount))
-> [AccountName] -> [PeriodicReportRow DisplayName MixedAmount]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (AccountName
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
-> Maybe (PeriodicReportRow DisplayName MixedAmount)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
`HM.lookup` HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
rowMap) [AccountName]
sortedanames
where
accounttree :: Account
accounttree = AccountName -> [AccountName] -> Account
accountTree AccountName
"root" ([AccountName] -> Account) -> [AccountName] -> Account
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName MixedAmount -> AccountName)
-> [PeriodicReportRow DisplayName MixedAmount] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName [PeriodicReportRow DisplayName MixedAmount]
rows
rowMap :: HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
rowMap = [(AccountName, PeriodicReportRow DisplayName MixedAmount)]
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList ([(AccountName, PeriodicReportRow DisplayName MixedAmount)]
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount))
-> [(AccountName, PeriodicReportRow DisplayName MixedAmount)]
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName MixedAmount
-> (AccountName, PeriodicReportRow DisplayName MixedAmount))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [(AccountName, PeriodicReportRow DisplayName MixedAmount)]
forall a b. (a -> b) -> [a] -> [b]
map (\PeriodicReportRow DisplayName MixedAmount
row -> (PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName PeriodicReportRow DisplayName MixedAmount
row, PeriodicReportRow DisplayName MixedAmount
row)) [PeriodicReportRow DisplayName MixedAmount]
rows
accounttreewithbals :: Account
accounttreewithbals = (Account -> Account) -> Account -> Account
mapAccounts Account -> Account
setibalance Account
accounttree
setibalance :: Account -> Account
setibalance Account
a = Account
a{aibalance :: MixedAmount
aibalance = MixedAmount
-> (PeriodicReportRow DisplayName MixedAmount -> MixedAmount)
-> Maybe (PeriodicReportRow DisplayName MixedAmount)
-> MixedAmount
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ([MixedAmount] -> MixedAmount)
-> ([Account] -> [MixedAmount]) -> [Account] -> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Account -> MixedAmount) -> [Account] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map Account -> MixedAmount
aibalance ([Account] -> MixedAmount) -> [Account] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Account -> [Account]
asubs Account
a) PeriodicReportRow DisplayName MixedAmount -> MixedAmount
forall a b. PeriodicReportRow a b -> b
prrTotal (Maybe (PeriodicReportRow DisplayName MixedAmount) -> MixedAmount)
-> Maybe (PeriodicReportRow DisplayName MixedAmount) -> MixedAmount
forall a b. (a -> b) -> a -> b
$
AccountName
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
-> Maybe (PeriodicReportRow DisplayName MixedAmount)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup (Account -> AccountName
aname Account
a) HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
rowMap}
sortedaccounttree :: Account
sortedaccounttree = NormalSign -> Account -> Account
sortAccountTreeByAmount (NormalSign -> Maybe NormalSign -> NormalSign
forall a. a -> Maybe a -> a
fromMaybe NormalSign
NormallyPositive (Maybe NormalSign -> NormalSign) -> Maybe NormalSign -> NormalSign
forall a b. (a -> b) -> a -> b
$ ReportOpts -> Maybe NormalSign
normalbalance_ ReportOpts
ropts) Account
accounttreewithbals
sortedanames :: [AccountName]
sortedanames = (Account -> AccountName) -> [Account] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map Account -> AccountName
aname ([Account] -> [AccountName]) -> [Account] -> [AccountName]
forall a b. (a -> b) -> a -> b
$ Int -> [Account] -> [Account]
forall a. Int -> [a] -> [a]
drop Int
1 ([Account] -> [Account]) -> [Account] -> [Account]
forall a b. (a -> b) -> a -> b
$ Account -> [Account]
flattenAccounts Account
sortedaccounttree
sortFlatMBRByAmount :: [MultiBalanceReportRow] -> [MultiBalanceReportRow]
sortFlatMBRByAmount :: [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortFlatMBRByAmount = case NormalSign -> Maybe NormalSign -> NormalSign
forall a. a -> Maybe a -> a
fromMaybe NormalSign
NormallyPositive (Maybe NormalSign -> NormalSign) -> Maybe NormalSign -> NormalSign
forall a b. (a -> b) -> a -> b
$ ReportOpts -> Maybe NormalSign
normalbalance_ ReportOpts
ropts of
NormalSign
NormallyPositive -> (PeriodicReportRow DisplayName MixedAmount
-> (Down MixedAmount, AccountName))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\PeriodicReportRow DisplayName MixedAmount
r -> (MixedAmount -> Down MixedAmount
forall a. a -> Down a
Down (MixedAmount -> Down MixedAmount)
-> MixedAmount -> Down MixedAmount
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow DisplayName MixedAmount -> MixedAmount
forall a. PeriodicReportRow a MixedAmount -> MixedAmount
amt PeriodicReportRow DisplayName MixedAmount
r, PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName PeriodicReportRow DisplayName MixedAmount
r))
NormalSign
NormallyNegative -> (PeriodicReportRow DisplayName MixedAmount
-> (MixedAmount, AccountName))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\PeriodicReportRow DisplayName MixedAmount
r -> (PeriodicReportRow DisplayName MixedAmount -> MixedAmount
forall a. PeriodicReportRow a MixedAmount -> MixedAmount
amt PeriodicReportRow DisplayName MixedAmount
r, PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName PeriodicReportRow DisplayName MixedAmount
r))
where amt :: PeriodicReportRow a MixedAmount -> MixedAmount
amt = MixedAmount -> MixedAmount
normaliseMixedAmountSquashPricesForDisplay (MixedAmount -> MixedAmount)
-> (PeriodicReportRow a MixedAmount -> MixedAmount)
-> PeriodicReportRow a MixedAmount
-> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeriodicReportRow a MixedAmount -> MixedAmount
forall a b. PeriodicReportRow a b -> b
prrTotal
sortMBRByAccountDeclaration :: [MultiBalanceReportRow] -> [MultiBalanceReportRow]
sortMBRByAccountDeclaration :: [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortMBRByAccountDeclaration [PeriodicReportRow DisplayName MixedAmount]
rows = [AccountName]
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall b.
[AccountName]
-> [PeriodicReportRow DisplayName b]
-> [PeriodicReportRow DisplayName b]
sortRowsLike [AccountName]
sortedanames [PeriodicReportRow DisplayName MixedAmount]
rows
where
sortedanames :: [AccountName]
sortedanames = Journal -> Bool -> [AccountName] -> [AccountName]
sortAccountNamesByDeclaration Journal
j (ReportOpts -> Bool
tree_ ReportOpts
ropts) ([AccountName] -> [AccountName]) -> [AccountName] -> [AccountName]
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName MixedAmount -> AccountName)
-> [PeriodicReportRow DisplayName MixedAmount] -> [AccountName]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName [PeriodicReportRow DisplayName MixedAmount]
rows
calculateTotalsRow :: ReportOpts -> [MultiBalanceReportRow] -> PeriodicReportRow () MixedAmount
calculateTotalsRow :: ReportOpts
-> [PeriodicReportRow DisplayName MixedAmount]
-> PeriodicReportRow () MixedAmount
calculateTotalsRow ReportOpts
ropts [PeriodicReportRow DisplayName MixedAmount]
rows =
()
-> [MixedAmount]
-> MixedAmount
-> MixedAmount
-> PeriodicReportRow () MixedAmount
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow () [MixedAmount]
coltotals MixedAmount
grandtotal MixedAmount
grandaverage
where
isTopRow :: PeriodicReportRow DisplayName a -> Bool
isTopRow PeriodicReportRow DisplayName a
row = ReportOpts -> Bool
flat_ ReportOpts
ropts Bool -> Bool -> Bool
|| Bool -> Bool
not ((AccountName -> Bool) -> [AccountName] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (AccountName
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
-> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
`HM.member` HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
rowMap) [AccountName]
parents)
where parents :: [AccountName]
parents = [AccountName] -> [AccountName]
forall a. [a] -> [a]
init ([AccountName] -> [AccountName])
-> (AccountName -> [AccountName]) -> AccountName -> [AccountName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AccountName -> [AccountName]
expandAccountName (AccountName -> [AccountName]) -> AccountName -> [AccountName]
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow DisplayName a -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName PeriodicReportRow DisplayName a
row
rowMap :: HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
rowMap = [(AccountName, PeriodicReportRow DisplayName MixedAmount)]
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList ([(AccountName, PeriodicReportRow DisplayName MixedAmount)]
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount))
-> [(AccountName, PeriodicReportRow DisplayName MixedAmount)]
-> HashMap AccountName (PeriodicReportRow DisplayName MixedAmount)
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName MixedAmount
-> (AccountName, PeriodicReportRow DisplayName MixedAmount))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [(AccountName, PeriodicReportRow DisplayName MixedAmount)]
forall a b. (a -> b) -> [a] -> [b]
map (\PeriodicReportRow DisplayName MixedAmount
row -> (PeriodicReportRow DisplayName MixedAmount -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName PeriodicReportRow DisplayName MixedAmount
row, PeriodicReportRow DisplayName MixedAmount
row)) [PeriodicReportRow DisplayName MixedAmount]
rows
colamts :: [[MixedAmount]]
colamts = [[MixedAmount]] -> [[MixedAmount]]
forall a. [[a]] -> [[a]]
transpose ([[MixedAmount]] -> [[MixedAmount]])
-> ([PeriodicReportRow DisplayName MixedAmount] -> [[MixedAmount]])
-> [PeriodicReportRow DisplayName MixedAmount]
-> [[MixedAmount]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PeriodicReportRow DisplayName MixedAmount -> [MixedAmount])
-> [PeriodicReportRow DisplayName MixedAmount] -> [[MixedAmount]]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount -> [MixedAmount]
forall a b. PeriodicReportRow a b -> [b]
prrAmounts ([PeriodicReportRow DisplayName MixedAmount] -> [[MixedAmount]])
-> [PeriodicReportRow DisplayName MixedAmount] -> [[MixedAmount]]
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName MixedAmount -> Bool)
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a. (a -> Bool) -> [a] -> [a]
filter PeriodicReportRow DisplayName MixedAmount -> Bool
forall a. PeriodicReportRow DisplayName a -> Bool
isTopRow [PeriodicReportRow DisplayName MixedAmount]
rows
[MixedAmount]
coltotals :: [MixedAmount] = [Char] -> [MixedAmount] -> [MixedAmount]
forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"coltotals" ([MixedAmount] -> [MixedAmount]) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ ([MixedAmount] -> MixedAmount) -> [[MixedAmount]] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map [MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [[MixedAmount]]
colamts
grandtotal :: MixedAmount
grandtotal = case ReportOpts -> BalanceType
balancetype_ ReportOpts
ropts of
BalanceType
PeriodChange -> [MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [MixedAmount]
coltotals
BalanceType
_ -> MixedAmount -> [MixedAmount] -> MixedAmount
forall a. a -> [a] -> a
lastDef MixedAmount
0 [MixedAmount]
coltotals
grandaverage :: MixedAmount
grandaverage = [MixedAmount] -> MixedAmount
averageMixedAmounts [MixedAmount]
coltotals
reportPercent :: ReportOpts -> MultiBalanceReport -> MultiBalanceReport
reportPercent :: ReportOpts -> MultiBalanceReport -> MultiBalanceReport
reportPercent ReportOpts
ropts report :: MultiBalanceReport
report@(PeriodicReport [DateSpan]
spans [PeriodicReportRow DisplayName MixedAmount]
rows PeriodicReportRow () MixedAmount
totalrow)
| ReportOpts -> Bool
percent_ ReportOpts
ropts = [DateSpan]
-> [PeriodicReportRow DisplayName MixedAmount]
-> PeriodicReportRow () MixedAmount
-> MultiBalanceReport
forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport [DateSpan]
spans ((PeriodicReportRow DisplayName MixedAmount
-> PeriodicReportRow DisplayName MixedAmount)
-> [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount
-> PeriodicReportRow DisplayName MixedAmount
forall a.
PeriodicReportRow a MixedAmount -> PeriodicReportRow a MixedAmount
percentRow [PeriodicReportRow DisplayName MixedAmount]
rows) (PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount
forall a.
PeriodicReportRow a MixedAmount -> PeriodicReportRow a MixedAmount
percentRow PeriodicReportRow () MixedAmount
totalrow)
| Bool
otherwise = MultiBalanceReport
report
where
percentRow :: PeriodicReportRow a MixedAmount -> PeriodicReportRow a MixedAmount
percentRow (PeriodicReportRow a
name [MixedAmount]
rowvals MixedAmount
rowtotal MixedAmount
rowavg) =
a
-> [MixedAmount]
-> MixedAmount
-> MixedAmount
-> PeriodicReportRow a MixedAmount
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow a
name
((MixedAmount -> MixedAmount -> MixedAmount)
-> [MixedAmount] -> [MixedAmount] -> [MixedAmount]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith MixedAmount -> MixedAmount -> MixedAmount
perdivide [MixedAmount]
rowvals ([MixedAmount] -> [MixedAmount]) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow () MixedAmount -> [MixedAmount]
forall a b. PeriodicReportRow a b -> [b]
prrAmounts PeriodicReportRow () MixedAmount
totalrow)
(MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
rowtotal (MixedAmount -> MixedAmount) -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow () MixedAmount -> MixedAmount
forall a b. PeriodicReportRow a b -> b
prrTotal PeriodicReportRow () MixedAmount
totalrow)
(MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
rowavg (MixedAmount -> MixedAmount) -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ PeriodicReportRow () MixedAmount -> MixedAmount
forall a b. PeriodicReportRow a b -> b
prrAverage PeriodicReportRow () MixedAmount
totalrow)
transposeMap :: Map DateSpan (HashMap AccountName a)
-> HashMap AccountName (Map DateSpan a)
transposeMap :: Map DateSpan (HashMap AccountName a)
-> HashMap AccountName (Map DateSpan a)
transposeMap = (DateSpan
-> HashMap AccountName a
-> HashMap AccountName (Map DateSpan a)
-> HashMap AccountName (Map DateSpan a))
-> HashMap AccountName (Map DateSpan a)
-> Map DateSpan (HashMap AccountName a)
-> HashMap AccountName (Map DateSpan a)
forall k a b. (k -> a -> b -> b) -> b -> Map k a -> b
M.foldrWithKey DateSpan
-> HashMap AccountName a
-> HashMap AccountName (Map DateSpan a)
-> HashMap AccountName (Map DateSpan a)
forall k k a.
(Hashable k, Ord k, Eq k) =>
k -> HashMap k a -> HashMap k (Map k a) -> HashMap k (Map k a)
addSpan HashMap AccountName (Map DateSpan a)
forall a. Monoid a => a
mempty
where
addSpan :: k -> HashMap k a -> HashMap k (Map k a) -> HashMap k (Map k a)
addSpan k
span HashMap k a
acctmap HashMap k (Map k a)
seen = (k -> a -> HashMap k (Map k a) -> HashMap k (Map k a))
-> HashMap k (Map k a) -> HashMap k a -> HashMap k (Map k a)
forall k v a. (k -> v -> a -> a) -> a -> HashMap k v -> a
HM.foldrWithKey (k -> k -> a -> HashMap k (Map k a) -> HashMap k (Map k a)
forall k k a.
(Eq k, Hashable k, Ord k) =>
k -> k -> a -> HashMap k (Map k a) -> HashMap k (Map k a)
addAcctSpan k
span) HashMap k (Map k a)
seen HashMap k a
acctmap
addAcctSpan :: k -> k -> a -> HashMap k (Map k a) -> HashMap k (Map k a)
addAcctSpan k
span k
acct a
a = (Maybe (Map k a) -> Maybe (Map k a))
-> k -> HashMap k (Map k a) -> HashMap k (Map k a)
forall k v.
(Eq k, Hashable k) =>
(Maybe v -> Maybe v) -> k -> HashMap k v -> HashMap k v
HM.alter Maybe (Map k a) -> Maybe (Map k a)
f k
acct
where f :: Maybe (Map k a) -> Maybe (Map k a)
f = Map k a -> Maybe (Map k a)
forall a. a -> Maybe a
Just (Map k a -> Maybe (Map k a))
-> (Maybe (Map k a) -> Map k a)
-> Maybe (Map k a)
-> Maybe (Map k a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. k -> a -> Map k a -> Map k a
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert k
span a
a (Map k a -> Map k a)
-> (Maybe (Map k a) -> Map k a) -> Maybe (Map k a) -> Map k a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map k a -> Maybe (Map k a) -> Map k a
forall a. a -> Maybe a -> a
fromMaybe Map k a
forall a. Monoid a => a
mempty
sortRowsLike :: [AccountName] -> [PeriodicReportRow DisplayName b] -> [PeriodicReportRow DisplayName b]
sortRowsLike :: [AccountName]
-> [PeriodicReportRow DisplayName b]
-> [PeriodicReportRow DisplayName b]
sortRowsLike [AccountName]
sortedas [PeriodicReportRow DisplayName b]
rows = (AccountName -> Maybe (PeriodicReportRow DisplayName b))
-> [AccountName] -> [PeriodicReportRow DisplayName b]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (AccountName
-> HashMap AccountName (PeriodicReportRow DisplayName b)
-> Maybe (PeriodicReportRow DisplayName b)
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
`HM.lookup` HashMap AccountName (PeriodicReportRow DisplayName b)
rowMap) [AccountName]
sortedas
where rowMap :: HashMap AccountName (PeriodicReportRow DisplayName b)
rowMap = [(AccountName, PeriodicReportRow DisplayName b)]
-> HashMap AccountName (PeriodicReportRow DisplayName b)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList ([(AccountName, PeriodicReportRow DisplayName b)]
-> HashMap AccountName (PeriodicReportRow DisplayName b))
-> [(AccountName, PeriodicReportRow DisplayName b)]
-> HashMap AccountName (PeriodicReportRow DisplayName b)
forall a b. (a -> b) -> a -> b
$ (PeriodicReportRow DisplayName b
-> (AccountName, PeriodicReportRow DisplayName b))
-> [PeriodicReportRow DisplayName b]
-> [(AccountName, PeriodicReportRow DisplayName b)]
forall a b. (a -> b) -> [a] -> [b]
map (\PeriodicReportRow DisplayName b
row -> (PeriodicReportRow DisplayName b -> AccountName
forall a. PeriodicReportRow DisplayName a -> AccountName
prrFullName PeriodicReportRow DisplayName b
row, PeriodicReportRow DisplayName b
row)) [PeriodicReportRow DisplayName b]
rows
subaccountTallies :: [AccountName] -> HashMap AccountName Int
subaccountTallies :: [AccountName] -> HashMap AccountName Int
subaccountTallies = (AccountName -> HashMap AccountName Int -> HashMap AccountName Int)
-> HashMap AccountName Int
-> [AccountName]
-> HashMap AccountName Int
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr AccountName -> HashMap AccountName Int -> HashMap AccountName Int
forall v.
Num v =>
AccountName -> HashMap AccountName v -> HashMap AccountName v
incrementParent HashMap AccountName Int
forall a. Monoid a => a
mempty ([AccountName] -> HashMap AccountName Int)
-> ([AccountName] -> [AccountName])
-> [AccountName]
-> HashMap AccountName Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [AccountName] -> [AccountName]
expandAccountNames
where
incrementParent :: AccountName -> HashMap AccountName v -> HashMap AccountName v
incrementParent AccountName
a = (v -> v -> v)
-> AccountName
-> v
-> HashMap AccountName v
-> HashMap AccountName v
forall k v.
(Eq k, Hashable k) =>
(v -> v -> v) -> k -> v -> HashMap k v -> HashMap k v
HM.insertWith v -> v -> v
forall a. Num a => a -> a -> a
(+) (AccountName -> AccountName
parentAccountName AccountName
a) v
1
perdivide :: MixedAmount -> MixedAmount -> MixedAmount
perdivide :: MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
a MixedAmount
b = MixedAmount -> Maybe MixedAmount -> MixedAmount
forall a. a -> Maybe a -> a
fromMaybe ([Char] -> MixedAmount
forall a. [Char] -> a
error' [Char]
errmsg) (Maybe MixedAmount -> MixedAmount)
-> Maybe MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ do
Amount
a' <- MixedAmount -> Maybe Amount
unifyMixedAmount MixedAmount
a
Amount
b' <- MixedAmount -> Maybe Amount
unifyMixedAmount MixedAmount
b
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Amount -> Bool
amountIsZero Amount
a' Bool -> Bool -> Bool
|| Amount -> Bool
amountIsZero Amount
b' Bool -> Bool -> Bool
|| Amount -> AccountName
acommodity Amount
a' AccountName -> AccountName -> Bool
forall a. Eq a => a -> a -> Bool
== Amount -> AccountName
acommodity Amount
b'
MixedAmount -> Maybe MixedAmount
forall (m :: * -> *) a. Monad m => a -> m a
return (MixedAmount -> Maybe MixedAmount)
-> MixedAmount -> Maybe MixedAmount
forall a b. (a -> b) -> a -> b
$ [Amount] -> MixedAmount
mixed [Quantity -> Amount
per (Quantity -> Amount) -> Quantity -> Amount
forall a b. (a -> b) -> a -> b
$ if Amount -> Quantity
aquantity Amount
b' Quantity -> Quantity -> Bool
forall a. Eq a => a -> a -> Bool
== Quantity
0 then Quantity
0 else Amount -> Quantity
aquantity Amount
a' Quantity -> Quantity -> Quantity
forall a. Fractional a => a -> a -> a
/ Quantity -> Quantity
forall a. Num a => a -> a
abs (Amount -> Quantity
aquantity Amount
b') Quantity -> Quantity -> Quantity
forall a. Num a => a -> a -> a
* Quantity
100]
where errmsg :: [Char]
errmsg = [Char]
"Cannot calculate percentages if accounts have different commodities (Hint: Try --cost, -V or similar flags.)"
sumAcct :: Account -> Account -> Account
sumAcct :: Account -> Account -> Account
sumAcct Account{aibalance :: Account -> MixedAmount
aibalance=MixedAmount
i1,aebalance :: Account -> MixedAmount
aebalance=MixedAmount
e1} a :: Account
a@Account{aibalance :: Account -> MixedAmount
aibalance=MixedAmount
i2,aebalance :: Account -> MixedAmount
aebalance=MixedAmount
e2} =
Account
a{aibalance :: MixedAmount
aibalance = MixedAmount
i1 MixedAmount -> MixedAmount -> MixedAmount
forall a. Num a => a -> a -> a
+ MixedAmount
i2, aebalance :: MixedAmount
aebalance = MixedAmount
e1 MixedAmount -> MixedAmount -> MixedAmount
forall a. Num a => a -> a -> a
+ MixedAmount
e2}
subtractAcct :: Account -> Account -> Account
subtractAcct :: Account -> Account -> Account
subtractAcct a :: Account
a@Account{aibalance :: Account -> MixedAmount
aibalance=MixedAmount
i1,aebalance :: Account -> MixedAmount
aebalance=MixedAmount
e1} Account{aibalance :: Account -> MixedAmount
aibalance=MixedAmount
i2,aebalance :: Account -> MixedAmount
aebalance=MixedAmount
e2} =
Account
a{aibalance :: MixedAmount
aibalance = MixedAmount
i1 MixedAmount -> MixedAmount -> MixedAmount
forall a. Num a => a -> a -> a
- MixedAmount
i2, aebalance :: MixedAmount
aebalance = MixedAmount
e1 MixedAmount -> MixedAmount -> MixedAmount
forall a. Num a => a -> a -> a
- MixedAmount
e2}
periodChanges :: Account -> Map k Account -> Map k Account
periodChanges :: Account -> Map k Account -> Map k Account
periodChanges Account
start Map k Account
amtmap =
[(k, Account)] -> Map k Account
forall k a. [(k, a)] -> Map k a
M.fromDistinctAscList ([(k, Account)] -> Map k Account)
-> ([Account] -> [(k, Account)]) -> [Account] -> Map k Account
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [k] -> [Account] -> [(k, Account)]
forall a b. [a] -> [b] -> [(a, b)]
zip [k]
dates ([Account] -> Map k Account) -> [Account] -> Map k Account
forall a b. (a -> b) -> a -> b
$ (Account -> Account -> Account)
-> [Account] -> [Account] -> [Account]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Account -> Account -> Account
subtractAcct [Account]
amts (Account
startAccount -> [Account] -> [Account]
forall a. a -> [a] -> [a]
:[Account]
amts)
where ([k]
dates, [Account]
amts) = [(k, Account)] -> ([k], [Account])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(k, Account)] -> ([k], [Account]))
-> [(k, Account)] -> ([k], [Account])
forall a b. (a -> b) -> a -> b
$ Map k Account -> [(k, Account)]
forall k a. Map k a -> [(k, a)]
M.toAscList Map k Account
amtmap
cumulativeSum :: (DateSpan -> Account -> Account) -> Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum :: (DateSpan -> Account -> Account)
-> Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum DateSpan -> Account -> Account
value Account
start = (Account, Map DateSpan Account) -> Map DateSpan Account
forall a b. (a, b) -> b
snd ((Account, Map DateSpan Account) -> Map DateSpan Account)
-> (Map DateSpan Account -> (Account, Map DateSpan Account))
-> Map DateSpan Account
-> Map DateSpan Account
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Account -> DateSpan -> Account -> (Account, Account))
-> Account
-> Map DateSpan Account
-> (Account, Map DateSpan Account)
forall a k b c.
(a -> k -> b -> (a, c)) -> a -> Map k b -> (a, Map k c)
M.mapAccumWithKey Account -> DateSpan -> Account -> (Account, Account)
accumValued Account
start
where accumValued :: Account -> DateSpan -> Account -> (Account, Account)
accumValued Account
startAmt DateSpan
date Account
newAmt = let s :: Account
s = Account -> Account -> Account
sumAcct Account
startAmt Account
newAmt in (Account
s, DateSpan -> Account -> Account
value DateSpan
date Account
s)
postingAndAccountValuations :: ReportSpec -> Journal -> PriceOracle
-> (DateSpan -> Posting -> Posting, DateSpan -> Account -> Account)
postingAndAccountValuations :: ReportSpec
-> Journal
-> PriceOracle
-> (DateSpan -> Posting -> Posting, DateSpan -> Account -> Account)
postingAndAccountValuations ReportSpec{rsToday :: ReportSpec -> Day
rsToday=Day
today, rsOpts :: ReportSpec -> ReportOpts
rsOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle = case ReportOpts -> Maybe ValuationType
value_ ReportOpts
ropts of
Just (AtEnd Maybe AccountName
_) -> ((Posting -> Posting) -> DateSpan -> Posting -> Posting
forall a b. a -> b -> a
const Posting -> Posting
forall a. a -> a
id, Costing -> Maybe ValuationType -> DateSpan -> Account -> Account
avalue' (ReportOpts -> Costing
cost_ ReportOpts
ropts) (ReportOpts -> Maybe ValuationType
value_ ReportOpts
ropts))
Maybe ValuationType
_ -> (Costing -> Maybe ValuationType -> DateSpan -> Posting -> Posting
pvalue' (ReportOpts -> Costing
cost_ ReportOpts
ropts) (ReportOpts -> Maybe ValuationType
value_ ReportOpts
ropts), (Account -> Account) -> DateSpan -> Account -> Account
forall a b. a -> b -> a
const Account -> Account
forall a. a -> a
id)
where
avalue' :: Costing -> Maybe ValuationType -> DateSpan -> Account -> Account
avalue' Costing
c Maybe ValuationType
v DateSpan
span Account
a = Account
a{aibalance :: MixedAmount
aibalance = MixedAmount -> MixedAmount
value (Account -> MixedAmount
aibalance Account
a), aebalance :: MixedAmount
aebalance = MixedAmount -> MixedAmount
value (Account -> MixedAmount
aebalance Account
a)}
where value :: MixedAmount -> MixedAmount
value = PriceOracle
-> Map AccountName AmountStyle
-> Day
-> Day
-> Day
-> Costing
-> Maybe ValuationType
-> MixedAmount
-> MixedAmount
mixedAmountApplyCostValuation PriceOracle
priceoracle Map AccountName AmountStyle
styles (DateSpan -> Day
end DateSpan
span) Day
today ([Char] -> Day
forall a. HasCallStack => [Char] -> a
error [Char]
"multiBalanceReport: did not expect amount valuation to be called ") Costing
c Maybe ValuationType
v
pvalue' :: Costing -> Maybe ValuationType -> DateSpan -> Posting -> Posting
pvalue' Costing
c Maybe ValuationType
v DateSpan
span = PriceOracle
-> Map AccountName AmountStyle
-> Day
-> Day
-> Costing
-> Maybe ValuationType
-> Posting
-> Posting
postingApplyCostValuation PriceOracle
priceoracle Map AccountName AmountStyle
styles (DateSpan -> Day
end DateSpan
span) Day
today Costing
c Maybe ValuationType
v
end :: DateSpan -> Day
end = Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe ([Char] -> Day
forall a. HasCallStack => [Char] -> a
error [Char]
"multiBalanceReport: expected all spans to have an end date")
(Maybe Day -> Day) -> (DateSpan -> Maybe Day) -> DateSpan -> Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Day -> Day) -> Maybe Day -> Maybe Day
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Integer -> Day -> Day
addDays (-Integer
1)) (Maybe Day -> Maybe Day)
-> (DateSpan -> Maybe Day) -> DateSpan -> Maybe Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DateSpan -> Maybe Day
spanEnd
styles :: Map AccountName AmountStyle
styles = Journal -> Map AccountName AmountStyle
journalCommodityStyles Journal
j
tests_MultiBalanceReport :: TestTree
tests_MultiBalanceReport = [Char] -> [TestTree] -> TestTree
tests [Char]
"MultiBalanceReport" [
let
amt0 :: Amount
amt0 = Amount :: AccountName
-> Quantity -> Bool -> AmountStyle -> Maybe AmountPrice -> Amount
Amount {acommodity :: AccountName
acommodity=AccountName
"$", aquantity :: Quantity
aquantity=Quantity
0, aprice :: Maybe AmountPrice
aprice=Maybe AmountPrice
forall a. Maybe a
Nothing, astyle :: AmountStyle
astyle=AmountStyle :: Side
-> Bool
-> AmountPrecision
-> Maybe Char
-> Maybe DigitGroupStyle
-> AmountStyle
AmountStyle {ascommodityside :: Side
ascommodityside = Side
L, ascommodityspaced :: Bool
ascommodityspaced = Bool
False, asprecision :: AmountPrecision
asprecision = Word8 -> AmountPrecision
Precision Word8
2, asdecimalpoint :: Maybe Char
asdecimalpoint = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'.', asdigitgroups :: Maybe DigitGroupStyle
asdigitgroups = Maybe DigitGroupStyle
forall a. Maybe a
Nothing}, aismultiplier :: Bool
aismultiplier=Bool
False}
(ReportSpec
rspec,Journal
journal) gives :: (ReportSpec, Journal)
-> ([PeriodicReportRow DisplayName MixedAmount], MixedAmount)
-> IO ()
`gives` ([PeriodicReportRow DisplayName MixedAmount], MixedAmount)
r = do
let rspec' :: ReportSpec
rspec' = ReportSpec
rspec{rsQuery :: Query
rsQuery=[Query] -> Query
And [ReportOpts -> Query
queryFromFlags (ReportOpts -> Query) -> ReportOpts -> Query
forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
rsOpts ReportSpec
rspec, ReportSpec -> Query
rsQuery ReportSpec
rspec]}
([PeriodicReportRow DisplayName MixedAmount]
eitems, MixedAmount
etotal) = ([PeriodicReportRow DisplayName MixedAmount], MixedAmount)
r
(PeriodicReport [DateSpan]
_ [PeriodicReportRow DisplayName MixedAmount]
aitems PeriodicReportRow () MixedAmount
atotal) = ReportSpec -> Journal -> MultiBalanceReport
multiBalanceReport ReportSpec
rspec' Journal
journal
showw :: PeriodicReportRow DisplayName MixedAmount
-> (AccountName, AccountName, Int, [[Char]], [Char], [Char])
showw (PeriodicReportRow DisplayName
a [MixedAmount]
lAmt MixedAmount
amt MixedAmount
amt')
= (DisplayName -> AccountName
displayFull DisplayName
a, DisplayName -> AccountName
displayName DisplayName
a, DisplayName -> Int
displayDepth DisplayName
a, (MixedAmount -> [Char]) -> [MixedAmount] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map MixedAmount -> [Char]
showMixedAmountDebug [MixedAmount]
lAmt, MixedAmount -> [Char]
showMixedAmountDebug MixedAmount
amt, MixedAmount -> [Char]
showMixedAmountDebug MixedAmount
amt')
((PeriodicReportRow DisplayName MixedAmount
-> (AccountName, AccountName, Int, [[Char]], [Char], [Char]))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [(AccountName, AccountName, Int, [[Char]], [Char], [Char])]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount
-> (AccountName, AccountName, Int, [[Char]], [Char], [Char])
showw [PeriodicReportRow DisplayName MixedAmount]
aitems) [(AccountName, AccountName, Int, [[Char]], [Char], [Char])]
-> [(AccountName, AccountName, Int, [[Char]], [Char], [Char])]
-> IO ()
forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= ((PeriodicReportRow DisplayName MixedAmount
-> (AccountName, AccountName, Int, [[Char]], [Char], [Char]))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [(AccountName, AccountName, Int, [[Char]], [Char], [Char])]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount
-> (AccountName, AccountName, Int, [[Char]], [Char], [Char])
showw [PeriodicReportRow DisplayName MixedAmount]
eitems)
MixedAmount -> [Char]
showMixedAmountDebug (PeriodicReportRow () MixedAmount -> MixedAmount
forall a b. PeriodicReportRow a b -> b
prrTotal PeriodicReportRow () MixedAmount
atotal) [Char] -> [Char] -> IO ()
forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= MixedAmount -> [Char]
showMixedAmountDebug MixedAmount
etotal
in
[Char] -> [TestTree] -> TestTree
tests [Char]
"multiBalanceReport" [
[Char] -> IO () -> TestTree
test [Char]
"null journal" (IO () -> TestTree) -> IO () -> TestTree
forall a b. (a -> b) -> a -> b
$
(ReportSpec
defreportspec, Journal
nulljournal) (ReportSpec, Journal)
-> ([PeriodicReportRow DisplayName MixedAmount], MixedAmount)
-> IO ()
`gives` ([], [Amount] -> MixedAmount
Mixed [Amount
nullamt])
,[Char] -> IO () -> TestTree
test [Char]
"with -H on a populated period" (IO () -> TestTree) -> IO () -> TestTree
forall a b. (a -> b) -> a -> b
$
(ReportSpec
defreportspec{rsOpts :: ReportOpts
rsOpts=ReportOpts
defreportopts{period_ :: Period
period_= Day -> Day -> Period
PeriodBetween (Integer -> Int -> Int -> Day
fromGregorian Integer
2008 Int
1 Int
1) (Integer -> Int -> Int -> Day
fromGregorian Integer
2008 Int
1 Int
2), balancetype_ :: BalanceType
balancetype_=BalanceType
HistoricalBalance}}, Journal
samplejournal) (ReportSpec, Journal)
-> ([PeriodicReportRow DisplayName MixedAmount], MixedAmount)
-> IO ()
`gives`
(
[ DisplayName
-> [MixedAmount]
-> MixedAmount
-> MixedAmount
-> PeriodicReportRow DisplayName MixedAmount
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow (AccountName -> DisplayName
flatDisplayName AccountName
"assets:bank:checking") [[Char] -> MixedAmount
mamountp' [Char]
"$1.00"] ([Char] -> MixedAmount
mamountp' [Char]
"$1.00") ([Amount] -> MixedAmount
Mixed [Amount
amt0 {aquantity :: Quantity
aquantity=Quantity
1}])
, DisplayName
-> [MixedAmount]
-> MixedAmount
-> MixedAmount
-> PeriodicReportRow DisplayName MixedAmount
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow (AccountName -> DisplayName
flatDisplayName AccountName
"income:salary") [[Char] -> MixedAmount
mamountp' [Char]
"$-1.00"] ([Char] -> MixedAmount
mamountp' [Char]
"$-1.00") ([Amount] -> MixedAmount
Mixed [Amount
amt0 {aquantity :: Quantity
aquantity=(-Quantity
1)}])
],
[Char] -> MixedAmount
mamountp' [Char]
"$0.00")
]
]