{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Hledger.Reports.MultiBalanceReport (
MultiBalanceReport,
MultiBalanceReportRow,
multiBalanceReport,
multiBalanceReportWith,
compoundBalanceReport,
compoundBalanceReportWith,
sortRows,
sortRowsLike,
makeReportQuery,
getPostingsByColumn,
getPostings,
startingPostings,
generateMultiBalanceReport,
balanceReportTableAsText,
tests_MultiBalanceReport
)
where
import Control.Monad (guard)
import Data.Bifunctor (second)
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, isJust, mapMaybe)
import Data.Ord (Down(..))
import Data.Semigroup (sconcat)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Time.Calendar (fromGregorian)
import Safe (lastDef, minimumMay)
import Data.Default (def)
import qualified Data.Text as T
import qualified Data.Text.Lazy.Builder as TB
import qualified Text.Tabular.AsciiWide as Tab
import Hledger.Data
import Hledger.Query
import Hledger.Utils hiding (dbg3,dbg4,dbg5)
import qualified Hledger.Utils
import Hledger.Reports.ReportOptions
import Hledger.Reports.ReportTypes
dbg3 :: [Char] -> a -> a
dbg3 [Char]
s = let p :: [Char]
p = [Char]
"multiBalanceReport" in forall a. Show a => [Char] -> a -> a
Hledger.Utils.dbg3 ([Char]
pforall a. [a] -> [a] -> [a]
++[Char]
" "forall a. [a] -> [a] -> [a]
++[Char]
s)
dbg4 :: [Char] -> a -> a
dbg4 [Char]
s = let p :: [Char]
p = [Char]
"multiBalanceReport" in forall a. Show a => [Char] -> a -> a
Hledger.Utils.dbg4 ([Char]
pforall a. [a] -> [a] -> [a]
++[Char]
" "forall a. [a] -> [a] -> [a]
++[Char]
s)
dbg5 :: [Char] -> a -> a
dbg5 [Char]
s = let p :: [Char]
p = [Char]
"multiBalanceReport" in forall a. Show a => [Char] -> a -> a
Hledger.Utils.dbg5 ([Char]
pforall a. [a] -> [a] -> [a]
++[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
-> Set CommoditySymbol
-> MultiBalanceReport
multiBalanceReportWith ReportSpec
rspec Journal
j (Bool -> Journal -> PriceOracle
journalPriceOracle Bool
infer Journal
j) forall a. Monoid a => a
mempty
where infer :: Bool
infer = ReportOpts -> Bool
infer_prices_ forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> Set AccountName -> MultiBalanceReport
multiBalanceReportWith :: ReportSpec
-> Journal
-> PriceOracle
-> Set CommoditySymbol
-> MultiBalanceReport
multiBalanceReportWith ReportSpec
rspec' Journal
j PriceOracle
priceoracle Set CommoditySymbol
unelidableaccts = MultiBalanceReport
report
where
(DateSpan
reportspan, [DateSpan]
colspans) = Journal -> ReportSpec -> (DateSpan, [DateSpan])
reportSpan Journal
j ReportSpec
rspec'
rspec :: ReportSpec
rspec = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportopts" forall a b. (a -> b) -> a -> b
$ ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec' DateSpan
reportspan
colps :: [(DateSpan, [Posting])]
colps = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"colps" forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal -> PriceOracle -> [DateSpan] -> [(DateSpan, [Posting])]
getPostingsByColumn ReportSpec
rspec Journal
j PriceOracle
priceoracle [DateSpan]
colspans
startbals :: HashMap CommoditySymbol Account
startbals = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"startbals" forall a b. (a -> b) -> a -> b
$
ReportSpec
-> Journal
-> PriceOracle
-> [Posting]
-> HashMap CommoditySymbol Account
startingBalances ReportSpec
rspec Journal
j PriceOracle
priceoracle forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
startingPostings ReportSpec
rspec Journal
j PriceOracle
priceoracle DateSpan
reportspan
report :: MultiBalanceReport
report = forall a. Show a => [Char] -> a -> a
dbg4 [Char]
"multiBalanceReportWith" forall a b. (a -> b) -> a -> b
$
ReportSpec
-> Journal
-> PriceOracle
-> Set CommoditySymbol
-> [(DateSpan, [Posting])]
-> HashMap CommoditySymbol Account
-> MultiBalanceReport
generateMultiBalanceReport ReportSpec
rspec Journal
j PriceOracle
priceoracle Set CommoditySymbol
unelidableaccts [(DateSpan, [Posting])]
colps HashMap CommoditySymbol Account
startbals
compoundBalanceReport :: ReportSpec -> Journal -> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReport :: forall a.
ReportSpec
-> Journal
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReport ReportSpec
rspec Journal
j = 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_prices_ forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec
compoundBalanceReportWith :: ReportSpec -> Journal -> PriceOracle
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReportWith :: forall a.
ReportSpec
-> Journal
-> PriceOracle
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReportWith ReportSpec
rspec' Journal
j PriceOracle
priceoracle [CBCSubreportSpec a]
subreportspecs = CompoundPeriodicReport a MixedAmount
cbr
where
(DateSpan
reportspan, [DateSpan]
colspans) = Journal -> ReportSpec -> (DateSpan, [DateSpan])
reportSpan Journal
j ReportSpec
rspec'
rspec :: ReportSpec
rspec = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportopts" forall a b. (a -> b) -> a -> b
$ ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec' DateSpan
reportspan
colps :: [(DateSpan, [Posting])]
colps = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"colps" forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal -> PriceOracle -> [DateSpan] -> [(DateSpan, [Posting])]
getPostingsByColumn ReportSpec
rspec Journal
j PriceOracle
priceoracle [DateSpan]
colspans
startps :: [Posting]
startps = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"startps" forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
startingPostings ReportSpec
rspec Journal
j PriceOracle
priceoracle DateSpan
reportspan
subreports :: [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
subreports = forall a b. (a -> b) -> [a] -> [b]
map forall {a}.
CBCSubreportSpec a
-> (CommoditySymbol, PeriodicReport a MixedAmount, Bool)
generateSubreport [CBCSubreportSpec a]
subreportspecs
where
generateSubreport :: CBCSubreportSpec a
-> (CommoditySymbol, PeriodicReport a MixedAmount, Bool)
generateSubreport CBCSubreportSpec{Bool
CommoditySymbol
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 -> Query
cbcsubreporttitle :: forall a. CBCSubreportSpec a -> CommoditySymbol
cbcsubreportincreasestotal :: Bool
cbcsubreporttransform :: MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreportoptions :: ReportOpts -> ReportOpts
cbcsubreportquery :: Query
cbcsubreporttitle :: CommoditySymbol
..} =
( CommoditySymbol
cbcsubreporttitle
, MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreporttransform forall a b. (a -> b) -> a -> b
$
ReportSpec
-> Journal
-> PriceOracle
-> Set CommoditySymbol
-> [(DateSpan, [Posting])]
-> HashMap CommoditySymbol Account
-> MultiBalanceReport
generateMultiBalanceReport ReportSpec
rspecsub Journal
j PriceOracle
priceoracle forall a. Monoid a => a
mempty [(DateSpan, [Posting])]
colps' HashMap CommoditySymbol Account
startbals'
, Bool
cbcsubreportincreasestotal
)
where
ropts :: ReportOpts
ropts = ReportOpts -> ReportOpts
cbcsubreportoptions forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec
rspecsub :: ReportSpec
rspecsub = ReportSpec
rspec{_rsReportOpts :: ReportOpts
_rsReportOpts=ReportOpts
ropts, _rsQuery :: Query
_rsQuery=[Query] -> Query
And [Query
cbcsubreportquery, ReportSpec -> Query
_rsQuery ReportSpec
rspec]}
startbals' :: HashMap CommoditySymbol Account
startbals' = ReportSpec
-> Journal
-> PriceOracle
-> [Posting]
-> HashMap CommoditySymbol Account
startingBalances ReportSpec
rspecsub Journal
j PriceOracle
priceoracle forall a b. (a -> b) -> a -> b
$
forall a. (a -> Bool) -> [a] -> [a]
filter ((CommoditySymbol -> Maybe AccountType) -> Query -> Posting -> Bool
matchesPostingExtra (Journal -> CommoditySymbol -> Maybe AccountType
journalAccountType Journal
j) Query
cbcsubreportquery) [Posting]
startps
colps' :: [(DateSpan, [Posting])]
colps' = forall a b. (a -> b) -> [a] -> [b]
map (forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter ((CommoditySymbol -> Maybe AccountType) -> Query -> Posting -> Bool
matchesPostingExtra (Journal -> CommoditySymbol -> Maybe AccountType
journalAccountType Journal
j) Query
cbcsubreportquery)) [(DateSpan, [Posting])]
colps
overalltotals :: PeriodicReportRow () MixedAmount
overalltotals = case [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
subreports of
[] -> forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow () [] MixedAmount
nullmixedamt MixedAmount
nullmixedamt
((CommoditySymbol, PeriodicReport a MixedAmount, Bool)
r:[(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
rs) -> forall a. Semigroup a => NonEmpty a -> a
sconcat forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall {a} {a}.
(a, PeriodicReport a MixedAmount, Bool)
-> PeriodicReportRow () MixedAmount
subreportTotal ((CommoditySymbol, PeriodicReport a MixedAmount, Bool)
rforall a. a -> [a] -> NonEmpty a
:|[(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
rs)
where
subreportTotal :: (a, PeriodicReport a MixedAmount, Bool)
-> PeriodicReportRow () MixedAmount
subreportTotal (a
_, PeriodicReport a MixedAmount
sr, Bool
increasestotal) =
(if Bool
increasestotal then forall a. a -> a
id else forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MixedAmount -> MixedAmount
maNegate) forall a b. (a -> b) -> a -> b
$ forall a b. PeriodicReport a b -> PeriodicReportRow () b
prTotals PeriodicReport a MixedAmount
sr
cbr :: CompoundPeriodicReport a MixedAmount
cbr = forall a b.
CommoditySymbol
-> [DateSpan]
-> [(CommoditySymbol, PeriodicReport a b, Bool)]
-> PeriodicReportRow () b
-> CompoundPeriodicReport a b
CompoundPeriodicReport CommoditySymbol
"" (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst [(DateSpan, [Posting])]
colps) [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
subreports PeriodicReportRow () MixedAmount
overalltotals
startingBalances :: ReportSpec -> Journal -> PriceOracle -> [Posting]
-> HashMap AccountName Account
startingBalances :: ReportSpec
-> Journal
-> PriceOracle
-> [Posting]
-> HashMap CommoditySymbol Account
startingBalances ReportSpec
rspec Journal
j PriceOracle
priceoracle [Posting]
ps =
forall k a. Ord k => a -> k -> Map k a -> a
M.findWithDefault Account
nullacct DateSpan
emptydatespan
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReportSpec
-> Journal
-> PriceOracle
-> HashMap CommoditySymbol Account
-> [(DateSpan, [Posting])]
-> HashMap CommoditySymbol (Map DateSpan Account)
calculateReportMatrix ReportSpec
rspec Journal
j PriceOracle
priceoracle forall a. Monoid a => a
mempty [(DateSpan
emptydatespan, [Posting]
ps)]
startingPostings :: ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
startingPostings :: ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
startingPostings rspec :: ReportSpec
rspec@ReportSpec{_rsQuery :: ReportSpec -> Query
_rsQuery=Query
query,_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle DateSpan
reportspan =
ReportSpec -> Journal -> PriceOracle -> [Posting]
getPostings ReportSpec
rspec' Journal
j PriceOracle
priceoracle
where
rspec' :: ReportSpec
rspec' = ReportSpec
rspec{_rsQuery :: Query
_rsQuery=Query
startbalq,_rsReportOpts :: ReportOpts
_rsReportOpts=ReportOpts
ropts'}
ropts' :: ReportOpts
ropts' = case ReportOpts -> Maybe ValuationType
value_ ReportOpts
ropts of
Just (AtEnd Maybe CommoditySymbol
_) -> ReportOpts
ropts{period_ :: Period
period_=Period
precedingperiod, value_ :: Maybe ValuationType
value_=forall a. Maybe a
Nothing}
Maybe ValuationType
_ -> ReportOpts
ropts{period_ :: Period
period_=Period
precedingperiod}
startbalq :: Query
startbalq = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"startbalq" forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query
datelessq, Query
precedingspanq]
datelessq :: Query
datelessq = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"datelessq" forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Bool
queryIsDateOrDate2) Query
query
precedingperiod :: Period
precedingperiod = DateSpan -> Period
dateSpanAsPeriod forall b c a. (b -> c) -> (a -> b) -> a -> c
. DateSpan -> DateSpan -> DateSpan
spanIntersect DateSpan
precedingspan forall b c a. (b -> c) -> (a -> b) -> a -> c
.
Period -> DateSpan
periodAsDateSpan forall a b. (a -> b) -> a -> b
$ ReportOpts -> Period
period_ ReportOpts
ropts
precedingspan :: DateSpan
precedingspan = Maybe EFDay -> Maybe EFDay -> DateSpan
DateSpan forall a. Maybe a
Nothing (Day -> EFDay
Exact forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DateSpan -> Maybe Day
spanStart DateSpan
reportspan)
precedingspanq :: Query
precedingspanq = (if ReportOpts -> Bool
date2_ ReportOpts
ropts then DateSpan -> Query
Date2 else DateSpan -> Query
Date) forall a b. (a -> b) -> a -> b
$ case DateSpan
precedingspan of
DateSpan Maybe EFDay
Nothing Maybe EFDay
Nothing -> DateSpan
emptydatespan
DateSpan
a -> DateSpan
a
makeReportQuery :: ReportSpec -> DateSpan -> ReportSpec
makeReportQuery :: ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec DateSpan
reportspan
| DateSpan
reportspan 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 forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query -> Query
dateless forall a b. (a -> b) -> a -> b
$ ReportSpec -> Query
_rsQuery ReportSpec
rspec, Query
reportspandatesq]
reportspandatesq :: Query
reportspandatesq = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"reportspandatesq" forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
dateqcons DateSpan
reportspan
dateless :: Query -> Query
dateless = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"dateless" forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Bool
queryIsDateOrDate2)
dateqcons :: DateSpan -> Query
dateqcons = if ReportOpts -> Bool
date2_ (ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec) then DateSpan -> Query
Date2 else DateSpan -> Query
Date
getPostingsByColumn :: ReportSpec -> Journal -> PriceOracle -> [DateSpan] -> [(DateSpan, [Posting])]
getPostingsByColumn :: ReportSpec
-> Journal -> PriceOracle -> [DateSpan] -> [(DateSpan, [Posting])]
getPostingsByColumn ReportSpec
rspec Journal
j PriceOracle
priceoracle [DateSpan]
colspans =
forall a.
Bool -> (a -> Day) -> [DateSpan] -> [a] -> [(DateSpan, [a])]
groupByDateSpan Bool
True Posting -> Day
getDate [DateSpan]
colspans [Posting]
ps
where
ps :: [Posting]
ps = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"getPostingsByColumn" forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> PriceOracle -> [Posting]
getPostings ReportSpec
rspec Journal
j PriceOracle
priceoracle
getDate :: Posting -> Day
getDate = WhichDate -> Posting -> Day
postingDateOrDate2 (ReportOpts -> WhichDate
whichDate (ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec))
getPostings :: ReportSpec -> Journal -> PriceOracle -> [Posting]
getPostings :: ReportSpec -> Journal -> PriceOracle -> [Posting]
getPostings rspec :: ReportSpec
rspec@ReportSpec{_rsQuery :: ReportSpec -> Query
_rsQuery=Query
query, _rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle =
Journal -> [Posting]
journalPostings forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> PriceOracle -> Journal
journalValueAndFilterPostingsWith ReportSpec
rspec' Journal
j PriceOracle
priceoracle
where
rspec' :: ReportSpec
rspec' = ReportSpec
rspec{_rsQuery :: Query
_rsQuery=Query
depthless, _rsReportOpts :: ReportOpts
_rsReportOpts = ReportOpts
ropts'}
ropts' :: ReportOpts
ropts' = if forall a. Maybe a -> Bool
isJust (ReportOpts -> Maybe (Maybe CommoditySymbol)
valuationAfterSum ReportOpts
ropts)
then ReportOpts
ropts{value_ :: Maybe ValuationType
value_=forall a. Maybe a
Nothing, conversionop_ :: Maybe ConversionOp
conversionop_=forall a. a -> Maybe a
Just ConversionOp
NoConversionOp}
else ReportOpts
ropts
depthless :: Query
depthless = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"depthless" forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Bool
queryIsDepth) Query
query
acctChanges :: ReportSpec -> Journal -> [Posting] -> HashMap ClippedAccountName Account
acctChanges :: ReportSpec
-> Journal -> [Posting] -> HashMap CommoditySymbol Account
acctChanges ReportSpec{_rsQuery :: ReportSpec -> Query
_rsQuery=Query
query,_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts{AccountListMode
accountlistmode_ :: ReportOpts -> AccountListMode
accountlistmode_ :: AccountListMode
accountlistmode_, Bool
declared_ :: ReportOpts -> Bool
declared_ :: Bool
declared_}} Journal
j [Posting]
ps =
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList [(Account -> CommoditySymbol
aname Account
a, Account
a) | Account
a <- [Account]
accts]
where
ps' :: [Posting]
ps' = [Posting]
ps forall a. [a] -> [a] -> [a]
++ if Bool
declared_ then [Posting]
declaredacctps else []
where
declaredacctps :: [Posting]
declaredacctps =
[Posting
nullposting{paccount :: CommoditySymbol
paccount=CommoditySymbol
a}
| CommoditySymbol
a <- Journal -> [CommoditySymbol]
journalLeafAccountNamesDeclared Journal
j
, (CommoditySymbol -> Maybe AccountType)
-> (CommoditySymbol -> [Tag]) -> Query -> CommoditySymbol -> Bool
matchesAccountExtra (Journal -> CommoditySymbol -> Maybe AccountType
journalAccountType Journal
j) (Journal -> CommoditySymbol -> [Tag]
journalAccountTags Journal
j) Query
accttypetagsq CommoditySymbol
a
]
where
accttypetagsq :: Query
accttypetagsq = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"accttypetagsq" forall a b. (a -> b) -> a -> b
$
(Query -> Bool) -> Query -> Query
filterQueryOrNotQuery (\Query
q -> Query -> Bool
queryIsAcct Query
q Bool -> Bool -> Bool
|| Query -> Bool
queryIsType Query
q Bool -> Bool -> Bool
|| Query -> Bool
queryIsTag Query
q) Query
query
filterbydepth :: [Account] -> [Account]
filterbydepth = case AccountListMode
accountlistmode_ of
AccountListMode
ALTree -> forall a. (a -> Bool) -> [a] -> [a]
filter ((Query
depthq Query -> CommoditySymbol -> Bool
`matchesAccount`) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account -> CommoditySymbol
aname)
AccountListMode
ALFlat -> Maybe Int -> [Account] -> [Account]
clipAccountsAndAggregate (Query -> Maybe Int
queryDepth Query
depthq)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter ((Int
0forall a. Ord a => a -> a -> Bool
<) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account -> Int
anumpostings)
where depthq :: Query
depthq = forall a. Show a => [Char] -> a -> a
dbg3 [Char]
"depthq" forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth Query
query
accts :: [Account]
accts = [Account] -> [Account]
filterbydepth forall a b. (a -> b) -> a -> b
$ forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ [Posting] -> [Account]
accountsFromPostings [Posting]
ps'
calculateReportMatrix :: ReportSpec -> Journal -> PriceOracle
-> HashMap ClippedAccountName Account
-> [(DateSpan, [Posting])]
-> HashMap ClippedAccountName (Map DateSpan Account)
calculateReportMatrix :: ReportSpec
-> Journal
-> PriceOracle
-> HashMap CommoditySymbol Account
-> [(DateSpan, [Posting])]
-> HashMap CommoditySymbol (Map DateSpan Account)
calculateReportMatrix rspec :: ReportSpec
rspec@ReportSpec{_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle HashMap CommoditySymbol Account
startbals [(DateSpan, [Posting])]
colps =
forall k v1 v2. (k -> v1 -> v2) -> HashMap k v1 -> HashMap k v2
HM.mapWithKey CommoditySymbol -> Map DateSpan Account -> Map DateSpan Account
rowbals HashMap CommoditySymbol (Map DateSpan Account)
allchanges
where
rowbals :: CommoditySymbol -> Map DateSpan Account -> Map DateSpan Account
rowbals CommoditySymbol
name Map DateSpan Account
unvaluedChanges = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"rowbals" forall a b. (a -> b) -> a -> b
$ case ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts of
BalanceAccumulation
PerPeriod -> Map DateSpan Account
changes
BalanceAccumulation
Cumulative -> Map DateSpan Account
cumulative
BalanceAccumulation
Historical -> Map DateSpan Account
historical
where
changes :: Map DateSpan Account
changes = case ReportOpts -> BalanceCalculation
balancecalc_ ReportOpts
ropts of
BalanceCalculation
CalcChange -> forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey DateSpan -> Account -> Account
avalue Map DateSpan Account
unvaluedChanges
BalanceCalculation
CalcBudget -> forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey DateSpan -> Account -> Account
avalue Map DateSpan Account
unvaluedChanges
BalanceCalculation
CalcValueChange -> forall k. Account -> Map k Account -> Map k Account
periodChanges Account
valuedStart Map DateSpan Account
historical
BalanceCalculation
CalcGain -> forall k. Account -> Map k Account -> Map k Account
periodChanges Account
valuedStart Map DateSpan Account
historical
BalanceCalculation
CalcPostingsCount -> forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey DateSpan -> Account -> Account
avalue Map DateSpan Account
unvaluedChanges
historical :: Map DateSpan Account
historical = forall k a b. (k -> a -> b) -> Map k a -> Map k b
M.mapWithKey DateSpan -> Account -> Account
avalue forall a b. (a -> b) -> a -> b
$ Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum Account
startingBalance Map DateSpan Account
unvaluedChanges
cumulative :: Map DateSpan Account
cumulative = Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum Account
nullacct Map DateSpan Account
changes
startingBalance :: Account
startingBalance = forall k v. (Eq k, Hashable k) => v -> k -> HashMap k v -> v
HM.lookupDefault Account
nullacct CommoditySymbol
name HashMap CommoditySymbol Account
startbals
valuedStart :: Account
valuedStart = DateSpan -> Account -> Account
avalue (Maybe EFDay -> Maybe EFDay -> DateSpan
DateSpan forall a. Maybe a
Nothing (Day -> EFDay
Exact forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Day
historicalDate)) Account
startingBalance
colacctchanges :: [(DateSpan, HashMap CommoditySymbol Account)]
colacctchanges = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"colacctchanges" forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal -> [Posting] -> HashMap CommoditySymbol Account
acctChanges ReportSpec
rspec Journal
j) [(DateSpan, [Posting])]
colps :: [(DateSpan, HashMap ClippedAccountName Account)]
acctchanges :: HashMap CommoditySymbol (Map DateSpan Account)
acctchanges = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"acctchanges" forall a b. (a -> b) -> a -> b
$ forall a.
[(DateSpan, HashMap CommoditySymbol a)]
-> HashMap CommoditySymbol (Map DateSpan a)
transposeMap [(DateSpan, HashMap CommoditySymbol Account)]
colacctchanges :: HashMap AccountName (Map DateSpan Account)
allchanges :: HashMap CommoditySymbol (Map DateSpan Account)
allchanges = ((forall a. Semigroup a => a -> a -> a
<>Map DateSpan Account
zeros) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashMap CommoditySymbol (Map DateSpan Account)
acctchanges) forall a. Semigroup a => a -> a -> a
<> (Map DateSpan Account
zeros forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ HashMap CommoditySymbol Account
startbals)
avalue :: DateSpan -> Account -> Account
avalue = (MixedAmount -> MixedAmount) -> Account -> Account
acctApplyBoth forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReportOpts
-> Journal -> PriceOracle -> DateSpan -> MixedAmount -> MixedAmount
mixedAmountApplyValuationAfterSumFromOptsWith ReportOpts
ropts Journal
j PriceOracle
priceoracle
acctApplyBoth :: (MixedAmount -> MixedAmount) -> Account -> Account
acctApplyBoth MixedAmount -> MixedAmount
f Account
a = Account
a{aibalance :: MixedAmount
aibalance = MixedAmount -> MixedAmount
f forall a b. (a -> b) -> a -> b
$ Account -> MixedAmount
aibalance Account
a, aebalance :: MixedAmount
aebalance = MixedAmount -> MixedAmount
f forall a b. (a -> b) -> a -> b
$ Account -> MixedAmount
aebalance Account
a}
historicalDate :: Maybe Day
historicalDate = forall a. Ord a => [a] -> Maybe a
minimumMay forall a b. (a -> b) -> a -> b
$ forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe DateSpan -> Maybe Day
spanStart [DateSpan]
colspans
zeros :: Map DateSpan Account
zeros = forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(DateSpan
spn, Account
nullacct) | DateSpan
spn <- [DateSpan]
colspans]
colspans :: [DateSpan]
colspans = forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst [(DateSpan, [Posting])]
colps
generateMultiBalanceReport :: ReportSpec -> Journal -> PriceOracle -> Set AccountName
-> [(DateSpan, [Posting])] -> HashMap AccountName Account
-> MultiBalanceReport
generateMultiBalanceReport :: ReportSpec
-> Journal
-> PriceOracle
-> Set CommoditySymbol
-> [(DateSpan, [Posting])]
-> HashMap CommoditySymbol Account
-> MultiBalanceReport
generateMultiBalanceReport rspec :: ReportSpec
rspec@ReportSpec{_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle Set CommoditySymbol
unelidableaccts [(DateSpan, [Posting])]
colps0 HashMap CommoditySymbol Account
startbals =
MultiBalanceReport
report
where
colps :: [(DateSpan, [Posting])]
colps =
if ReportOpts -> BalanceCalculation
balancecalc_ ReportOpts
ropts forall a. Eq a => a -> a -> Bool
== BalanceCalculation
CalcPostingsCount
then forall a b. (a -> b) -> [a] -> [b]
map (forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second (forall a b. (a -> b) -> [a] -> [b]
map ((MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *). Foldable t => t Amount -> MixedAmount
mixed [Quantity -> Amount
num Quantity
1])))) [(DateSpan, [Posting])]
colps0
else [(DateSpan, [Posting])]
colps0
matrix :: HashMap CommoditySymbol (Map DateSpan Account)
matrix = ReportSpec
-> Journal
-> PriceOracle
-> HashMap CommoditySymbol Account
-> [(DateSpan, [Posting])]
-> HashMap CommoditySymbol (Map DateSpan Account)
calculateReportMatrix ReportSpec
rspec Journal
j PriceOracle
priceoracle HashMap CommoditySymbol Account
startbals [(DateSpan, [Posting])]
colps
displaynames :: HashMap CommoditySymbol DisplayName
displaynames = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"displaynames" forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Set CommoditySymbol
-> HashMap CommoditySymbol (Map DateSpan Account)
-> HashMap CommoditySymbol DisplayName
displayedAccounts ReportSpec
rspec Set CommoditySymbol
unelidableaccts HashMap CommoditySymbol (Map DateSpan Account)
matrix
rows :: [PeriodicReportRow DisplayName MixedAmount]
rows = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"rows" forall b c a. (b -> c) -> (a -> b) -> a -> c
. (if ReportOpts -> Bool
invert_ ReportOpts
ropts then forall a b. (a -> b) -> [a] -> [b]
map (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MixedAmount -> MixedAmount
maNegate) else forall a. a -> a
id)
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> HashMap CommoditySymbol DisplayName
-> HashMap CommoditySymbol (Map DateSpan Account)
-> [PeriodicReportRow DisplayName MixedAmount]
buildReportRows ReportOpts
ropts HashMap CommoditySymbol DisplayName
displaynames HashMap CommoditySymbol (Map DateSpan Account)
matrix
totalsrow :: PeriodicReportRow () MixedAmount
totalsrow = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"totalsrow" 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 = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"sortedrows" 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 forall a b. (a -> b) -> a -> b
$ forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst [(DateSpan, [Posting])]
colps) [PeriodicReportRow DisplayName MixedAmount]
sortedrows PeriodicReportRow () MixedAmount
totalsrow
buildReportRows :: ReportOpts
-> HashMap AccountName DisplayName
-> HashMap AccountName (Map DateSpan Account)
-> [MultiBalanceReportRow]
buildReportRows :: ReportOpts
-> HashMap CommoditySymbol DisplayName
-> HashMap CommoditySymbol (Map DateSpan Account)
-> [PeriodicReportRow DisplayName MixedAmount]
buildReportRows ReportOpts
ropts HashMap CommoditySymbol DisplayName
displaynames =
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k v1 v2.
(k -> v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2
HM.mapMaybeWithKey forall {t :: * -> *}.
Foldable t =>
CommoditySymbol
-> t Account -> Maybe (PeriodicReportRow DisplayName MixedAmount)
mkRow
where
mkRow :: CommoditySymbol
-> t Account -> Maybe (PeriodicReportRow DisplayName MixedAmount)
mkRow CommoditySymbol
name t Account
accts = do
DisplayName
displayname <- forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup CommoditySymbol
name HashMap CommoditySymbol DisplayName
displaynames
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow DisplayName
displayname [MixedAmount]
rowbals MixedAmount
rowtot MixedAmount
rowavg
where
rowbals :: [MixedAmount]
rowbals = forall a b. (a -> b) -> [a] -> [b]
map Account -> MixedAmount
balance forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> [a]
toList t Account
accts
rowtot :: MixedAmount
rowtot = case ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts of
BalanceAccumulation
PerPeriod -> forall (t :: * -> *). Foldable t => t MixedAmount -> MixedAmount
maSum [MixedAmount]
rowbals
BalanceAccumulation
_ -> forall a. a -> [a] -> a
lastDef MixedAmount
nullmixedamt [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
-> Set AccountName
-> HashMap AccountName (Map DateSpan Account)
-> HashMap AccountName DisplayName
displayedAccounts :: ReportSpec
-> Set CommoditySymbol
-> HashMap CommoditySymbol (Map DateSpan Account)
-> HashMap CommoditySymbol DisplayName
displayedAccounts ReportSpec{_rsQuery :: ReportSpec -> Query
_rsQuery=Query
query,_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Set CommoditySymbol
unelidableaccts HashMap CommoditySymbol (Map DateSpan Account)
valuedaccts
| Int
qdepth forall a. Eq a => a -> a -> Bool
== Int
0 = forall k v. Hashable k => k -> v -> HashMap k v
HM.singleton CommoditySymbol
"..." forall a b. (a -> b) -> a -> b
$ CommoditySymbol -> CommoditySymbol -> Int -> DisplayName
DisplayName CommoditySymbol
"..." CommoditySymbol
"..." Int
1
| Bool
otherwise = forall k v1 v2. (k -> v1 -> v2) -> HashMap k v1 -> HashMap k v2
HM.mapWithKey (\CommoditySymbol
a Map DateSpan Account
_ -> CommoditySymbol -> DisplayName
displayedName CommoditySymbol
a) HashMap CommoditySymbol (Map DateSpan Account)
displayedAccts
where
displayedAccts :: HashMap CommoditySymbol (Map DateSpan Account)
displayedAccts = (if Int
qdepth forall a. Eq a => a -> a -> Bool
== Int
0 then forall a. a -> a
id else forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
HM.filterWithKey forall {t :: * -> *}.
Foldable t =>
CommoditySymbol -> t Account -> Bool
keep) HashMap CommoditySymbol (Map DateSpan Account)
valuedaccts
where
keep :: CommoditySymbol -> t Account -> Bool
keep CommoditySymbol
name t Account
amts = forall {t :: * -> *}.
Foldable t =>
CommoditySymbol -> t Account -> Bool
isInteresting CommoditySymbol
name t Account
amts Bool -> Bool -> Bool
|| CommoditySymbol
name forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
`HM.member` HashMap CommoditySymbol Int
interestingParents
displayedName :: CommoditySymbol -> DisplayName
displayedName CommoditySymbol
name = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> CommoditySymbol -> CommoditySymbol -> Int -> DisplayName
DisplayName CommoditySymbol
name CommoditySymbol
leaf forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Ord a => a -> a -> a
max Int
0 forall a b. (a -> b) -> a -> b
$ Int
level forall a. Num a => a -> a -> a
- Int
boringParents
AccountListMode
ALFlat -> CommoditySymbol -> CommoditySymbol -> Int -> DisplayName
DisplayName CommoditySymbol
name CommoditySymbol
droppedName Int
1
where
droppedName :: CommoditySymbol
droppedName = Int -> CommoditySymbol -> CommoditySymbol
accountNameDrop (ReportOpts -> Int
drop_ ReportOpts
ropts) CommoditySymbol
name
leaf :: CommoditySymbol
leaf = [CommoditySymbol] -> CommoditySymbol
accountNameFromComponents forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map CommoditySymbol -> CommoditySymbol
accountLeafName forall a b. (a -> b) -> a -> b
$
CommoditySymbol
droppedName forall a. a -> [a] -> [a]
: forall a. (a -> Bool) -> [a] -> [a]
takeWhile CommoditySymbol -> Bool
notDisplayed [CommoditySymbol]
parents
level :: Int
level = forall a. Ord a => a -> a -> a
max Int
0 forall a b. (a -> b) -> a -> b
$ CommoditySymbol -> Int
accountNameLevel CommoditySymbol
name forall a. Num a => a -> a -> a
- ReportOpts -> Int
drop_ ReportOpts
ropts
parents :: [CommoditySymbol]
parents = forall a. Int -> [a] -> [a]
take (Int
level forall a. Num a => a -> a -> a
- Int
1) forall a b. (a -> b) -> a -> b
$ CommoditySymbol -> [CommoditySymbol]
parentAccountNames CommoditySymbol
name
boringParents :: Int
boringParents = if ReportOpts -> Bool
no_elide_ ReportOpts
ropts then Int
0 else forall (t :: * -> *) a. Foldable t => t a -> Int
length forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter CommoditySymbol -> Bool
notDisplayed [CommoditySymbol]
parents
notDisplayed :: CommoditySymbol -> Bool
notDisplayed = Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
`HM.member` HashMap CommoditySymbol (Map DateSpan Account)
displayedAccts)
isInteresting :: CommoditySymbol -> t Account -> Bool
isInteresting CommoditySymbol
name t Account
amts =
Int
d forall a. Ord a => a -> a -> Bool
<= Int
qdepth
Bool -> Bool -> Bool
&& ( CommoditySymbol
name forall a. Ord a => a -> Set a -> Bool
`Set.member` Set CommoditySymbol
unelidableaccts
Bool -> Bool -> Bool
||(ReportOpts -> Bool
empty_ ReportOpts
ropts Bool -> Bool -> Bool
&& t Account -> Bool
keepWhenEmpty t Account
amts)
Bool -> Bool -> Bool
|| Bool -> Bool
not (forall {t :: * -> *} {a}.
Foldable t =>
(a -> MixedAmount) -> t a -> Bool
isZeroRow Account -> MixedAmount
balance t Account
amts)
)
where
d :: Int
d = CommoditySymbol -> Int
accountNameLevel CommoditySymbol
name
keepWhenEmpty :: t Account -> Bool
keepWhenEmpty = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALFlat -> forall a b. a -> b -> a
const Bool
True
AccountListMode
ALTree -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account -> [Account]
asubs)
balance :: Account -> MixedAmount
balance = MixedAmount -> MixedAmount
maybeStripPrices forall b c a. (b -> c) -> (a -> b) -> a -> c
. case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree | Int
d forall a. Eq a => a -> a -> Bool
== Int
qdepth -> Account -> MixedAmount
aibalance
AccountListMode
_ -> Account -> MixedAmount
aebalance
where maybeStripPrices :: MixedAmount -> MixedAmount
maybeStripPrices = if ReportOpts -> Maybe ConversionOp
conversionop_ ReportOpts
ropts forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just ConversionOp
NoConversionOp then forall a. a -> a
id else MixedAmount -> MixedAmount
mixedAmountStripPrices
interestingParents :: HashMap CommoditySymbol Int
interestingParents = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"interestingParents" forall a b. (a -> b) -> a -> b
$ case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
HM.filterWithKey CommoditySymbol -> Int -> Bool
hasEnoughSubs HashMap CommoditySymbol Int
numSubs
AccountListMode
ALFlat -> forall a. Monoid a => a
mempty
where
hasEnoughSubs :: CommoditySymbol -> Int -> Bool
hasEnoughSubs CommoditySymbol
name Int
nsubs = Int
nsubs forall a. Ord a => a -> a -> Bool
>= Int
minSubs Bool -> Bool -> Bool
&& CommoditySymbol -> Int
accountNameLevel CommoditySymbol
name 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 = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (MixedAmount -> Bool
mixedAmountLooksZero forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> MixedAmount
balance)
qdepth :: Int
qdepth = forall a. a -> Maybe a -> a
fromMaybe forall a. Bounded a => a
maxBound forall a b. (a -> b) -> a -> b
$ Query -> Maybe Int
queryDepth Query
query
numSubs :: HashMap CommoditySymbol Int
numSubs = [CommoditySymbol] -> HashMap CommoditySymbol Int
subaccountTallies forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k v. HashMap k v -> [k]
HM.keys forall a b. (a -> b) -> a -> b
$ forall k v. (k -> v -> Bool) -> HashMap k v -> HashMap k v
HM.filterWithKey forall {t :: * -> *}.
Foldable t =>
CommoditySymbol -> t Account -> Bool
isInteresting HashMap CommoditySymbol (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 = forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
`HM.lookup` HashMap CommoditySymbol (PeriodicReportRow DisplayName MixedAmount)
rowMap) [CommoditySymbol]
sortedanames
where
accounttree :: Account
accounttree = CommoditySymbol -> [CommoditySymbol] -> Account
accountTree CommoditySymbol
"root" forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName [PeriodicReportRow DisplayName MixedAmount]
rows
rowMap :: HashMap CommoditySymbol (PeriodicReportRow DisplayName MixedAmount)
rowMap = forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (\PeriodicReportRow DisplayName MixedAmount
row -> (forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
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 = forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (t :: * -> *). Foldable t => t MixedAmount -> MixedAmount
maSum forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Account -> MixedAmount
aibalance forall a b. (a -> b) -> a -> b
$ Account -> [Account]
asubs Account
a) forall a b. PeriodicReportRow a b -> b
prrTotal forall a b. (a -> b) -> a -> b
$
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup (Account -> CommoditySymbol
aname Account
a) HashMap CommoditySymbol (PeriodicReportRow DisplayName MixedAmount)
rowMap}
sortedaccounttree :: Account
sortedaccounttree = NormalSign -> Account -> Account
sortAccountTreeByAmount (forall a. a -> Maybe a -> a
fromMaybe NormalSign
NormallyPositive forall a b. (a -> b) -> a -> b
$ ReportOpts -> Maybe NormalSign
normalbalance_ ReportOpts
ropts) Account
accounttreewithbals
sortedanames :: [CommoditySymbol]
sortedanames = forall a b. (a -> b) -> [a] -> [b]
map Account -> CommoditySymbol
aname forall a b. (a -> b) -> a -> b
$ forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ Account -> [Account]
flattenAccounts Account
sortedaccounttree
sortFlatMBRByAmount :: [MultiBalanceReportRow] -> [MultiBalanceReportRow]
sortFlatMBRByAmount :: [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortFlatMBRByAmount = case forall a. a -> Maybe a -> a
fromMaybe NormalSign
NormallyPositive forall a b. (a -> b) -> a -> b
$ ReportOpts -> Maybe NormalSign
normalbalance_ ReportOpts
ropts of
NormalSign
NormallyPositive -> forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\PeriodicReportRow DisplayName MixedAmount
r -> (forall a. a -> Down a
Down forall a b. (a -> b) -> a -> b
$ forall {a}. PeriodicReportRow a MixedAmount -> MixedAmount
amt PeriodicReportRow DisplayName MixedAmount
r, forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName PeriodicReportRow DisplayName MixedAmount
r))
NormalSign
NormallyNegative -> forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\PeriodicReportRow DisplayName MixedAmount
r -> (forall {a}. PeriodicReportRow a MixedAmount -> MixedAmount
amt PeriodicReportRow DisplayName MixedAmount
r, forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName PeriodicReportRow DisplayName MixedAmount
r))
where amt :: PeriodicReportRow a MixedAmount -> MixedAmount
amt = MixedAmount -> MixedAmount
mixedAmountStripPrices forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. PeriodicReportRow a b -> b
prrTotal
sortMBRByAccountDeclaration :: [MultiBalanceReportRow] -> [MultiBalanceReportRow]
sortMBRByAccountDeclaration :: [PeriodicReportRow DisplayName MixedAmount]
-> [PeriodicReportRow DisplayName MixedAmount]
sortMBRByAccountDeclaration [PeriodicReportRow DisplayName MixedAmount]
rows = forall b.
[CommoditySymbol]
-> [PeriodicReportRow DisplayName b]
-> [PeriodicReportRow DisplayName b]
sortRowsLike [CommoditySymbol]
sortedanames [PeriodicReportRow DisplayName MixedAmount]
rows
where
sortedanames :: [CommoditySymbol]
sortedanames = Journal -> Bool -> [CommoditySymbol] -> [CommoditySymbol]
sortAccountNamesByDeclaration Journal
j (ReportOpts -> Bool
tree_ ReportOpts
ropts) forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName [PeriodicReportRow DisplayName MixedAmount]
rows
calculateTotalsRow :: ReportOpts -> [MultiBalanceReportRow] -> PeriodicReportRow () MixedAmount
calculateTotalsRow :: ReportOpts
-> [PeriodicReportRow DisplayName MixedAmount]
-> PeriodicReportRow () MixedAmount
calculateTotalsRow ReportOpts
ropts [PeriodicReportRow DisplayName MixedAmount]
rows =
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 (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
`HM.member` HashMap CommoditySymbol (PeriodicReportRow DisplayName MixedAmount)
rowMap) [CommoditySymbol]
parents)
where parents :: [CommoditySymbol]
parents = forall a. [a] -> [a]
init forall b c a. (b -> c) -> (a -> b) -> a -> c
. CommoditySymbol -> [CommoditySymbol]
expandAccountName forall a b. (a -> b) -> a -> b
$ forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName PeriodicReportRow DisplayName a
row
rowMap :: HashMap CommoditySymbol (PeriodicReportRow DisplayName MixedAmount)
rowMap = forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (\PeriodicReportRow DisplayName MixedAmount
row -> (forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName PeriodicReportRow DisplayName MixedAmount
row, PeriodicReportRow DisplayName MixedAmount
row)) [PeriodicReportRow DisplayName MixedAmount]
rows
colamts :: [[MixedAmount]]
colamts = forall a. [[a]] -> [[a]]
transpose forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall a b. PeriodicReportRow a b -> [b]
prrAmounts forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter forall {a}. PeriodicReportRow DisplayName a -> Bool
isTopRow [PeriodicReportRow DisplayName MixedAmount]
rows
[MixedAmount]
coltotals :: [MixedAmount] = forall a. Show a => [Char] -> a -> a
dbg5 [Char]
"coltotals" forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall (t :: * -> *). Foldable t => t MixedAmount -> MixedAmount
maSum [[MixedAmount]]
colamts
grandtotal :: MixedAmount
grandtotal = case ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts of
BalanceAccumulation
PerPeriod -> forall (t :: * -> *). Foldable t => t MixedAmount -> MixedAmount
maSum [MixedAmount]
coltotals
BalanceAccumulation
_ -> forall a. a -> [a] -> a
lastDef MixedAmount
nullmixedamt [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 = forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport [DateSpan]
spans (forall a b. (a -> b) -> [a] -> [b]
map forall {a}.
PeriodicReportRow a MixedAmount -> PeriodicReportRow a MixedAmount
percentRow [PeriodicReportRow DisplayName MixedAmount]
rows) (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) =
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow a
name
(forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith MixedAmount -> MixedAmount -> MixedAmount
perdivide [MixedAmount]
rowvals forall a b. (a -> b) -> a -> b
$ forall a b. PeriodicReportRow a b -> [b]
prrAmounts PeriodicReportRow () MixedAmount
totalrow)
(MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
rowtotal forall a b. (a -> b) -> a -> b
$ forall a b. PeriodicReportRow a b -> b
prrTotal PeriodicReportRow () MixedAmount
totalrow)
(MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
rowavg forall a b. (a -> b) -> a -> b
$ forall a b. PeriodicReportRow a b -> b
prrAverage PeriodicReportRow () MixedAmount
totalrow)
transposeMap :: [(DateSpan, HashMap AccountName a)]
-> HashMap AccountName (Map DateSpan a)
transposeMap :: forall a.
[(DateSpan, HashMap CommoditySymbol a)]
-> HashMap CommoditySymbol (Map DateSpan a)
transposeMap = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry forall {k} {p} {v}.
(Hashable k, Ord p) =>
p -> HashMap k v -> HashMap k (Map p v) -> HashMap k (Map p v)
addSpan) forall a. Monoid a => a
mempty
where
addSpan :: p -> HashMap k v -> HashMap k (Map p v) -> HashMap k (Map p v)
addSpan p
spn HashMap k v
acctmap HashMap k (Map p v)
seen = forall k v a. (k -> v -> a -> a) -> a -> HashMap k v -> a
HM.foldrWithKey (forall {k} {p} {p}.
(Hashable k, Ord p) =>
p -> k -> p -> HashMap k (Map p p) -> HashMap k (Map p p)
addAcctSpan p
spn) HashMap k (Map p v)
seen HashMap k v
acctmap
addAcctSpan :: p -> k -> p -> HashMap k (Map p p) -> HashMap k (Map p p)
addAcctSpan p
spn k
acct p
a = forall k v.
(Eq k, Hashable k) =>
(Maybe v -> Maybe v) -> k -> HashMap k v -> HashMap k v
HM.alter Maybe (Map p p) -> Maybe (Map p p)
f k
acct
where f :: Maybe (Map p p) -> Maybe (Map p p)
f = forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert p
spn p
a forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a -> a
fromMaybe forall a. Monoid a => a
mempty
sortRowsLike :: [AccountName] -> [PeriodicReportRow DisplayName b] -> [PeriodicReportRow DisplayName b]
sortRowsLike :: forall b.
[CommoditySymbol]
-> [PeriodicReportRow DisplayName b]
-> [PeriodicReportRow DisplayName b]
sortRowsLike [CommoditySymbol]
sortedas [PeriodicReportRow DisplayName b]
rows = forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
`HM.lookup` HashMap CommoditySymbol (PeriodicReportRow DisplayName b)
rowMap) [CommoditySymbol]
sortedas
where rowMap :: HashMap CommoditySymbol (PeriodicReportRow DisplayName b)
rowMap = forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (\PeriodicReportRow DisplayName b
row -> (forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName PeriodicReportRow DisplayName b
row, PeriodicReportRow DisplayName b
row)) [PeriodicReportRow DisplayName b]
rows
subaccountTallies :: [AccountName] -> HashMap AccountName Int
subaccountTallies :: [CommoditySymbol] -> HashMap CommoditySymbol Int
subaccountTallies = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr forall {v}.
Num v =>
CommoditySymbol
-> HashMap CommoditySymbol v -> HashMap CommoditySymbol v
incrementParent forall a. Monoid a => a
mempty forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CommoditySymbol] -> [CommoditySymbol]
expandAccountNames
where incrementParent :: CommoditySymbol
-> HashMap CommoditySymbol v -> HashMap CommoditySymbol v
incrementParent CommoditySymbol
a = forall k v.
(Eq k, Hashable k) =>
(v -> v -> v) -> k -> v -> HashMap k v -> HashMap k v
HM.insertWith forall a. Num a => a -> a -> a
(+) (CommoditySymbol -> CommoditySymbol
parentAccountName CommoditySymbol
a) v
1
perdivide :: MixedAmount -> MixedAmount -> MixedAmount
perdivide :: MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
a MixedAmount
b = forall a. a -> Maybe a -> a
fromMaybe (forall a. [Char] -> a
error' [Char]
errmsg) forall a b. (a -> b) -> a -> b
$ do
Amount
a' <- MixedAmount -> Maybe Amount
unifyMixedAmount MixedAmount
a
Amount
b' <- MixedAmount -> Maybe Amount
unifyMixedAmount MixedAmount
b
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ Amount -> Bool
amountIsZero Amount
a' Bool -> Bool -> Bool
|| Amount -> Bool
amountIsZero Amount
b' Bool -> Bool -> Bool
|| Amount -> CommoditySymbol
acommodity Amount
a' forall a. Eq a => a -> a -> Bool
== Amount -> CommoditySymbol
acommodity Amount
b'
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *). Foldable t => t Amount -> MixedAmount
mixed [Quantity -> Amount
per forall a b. (a -> b) -> a -> b
$ if Amount -> Quantity
aquantity Amount
b' forall a. Eq a => a -> a -> Bool
== Quantity
0 then Quantity
0 else Amount -> Quantity
aquantity Amount
a' forall a. Fractional a => a -> a -> a
/ forall a. Num a => a -> a
abs (Amount -> Quantity
aquantity Amount
b') 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
`maPlus` MixedAmount
i2, aebalance :: MixedAmount
aebalance = MixedAmount
e1 MixedAmount -> MixedAmount -> MixedAmount
`maPlus` 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
`maMinus` MixedAmount
i2, aebalance :: MixedAmount
aebalance = MixedAmount
e1 MixedAmount -> MixedAmount -> MixedAmount
`maMinus` MixedAmount
e2}
periodChanges :: Account -> Map k Account -> Map k Account
periodChanges :: forall k. Account -> Map k Account -> Map k Account
periodChanges Account
start Map k Account
amtmap =
forall k a. [(k, a)] -> Map k a
M.fromDistinctAscList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. [a] -> [b] -> [(a, b)]
zip [k]
dates forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Account -> Account -> Account
subtractAcct [Account]
amts (Account
startforall a. a -> [a] -> [a]
:[Account]
amts)
where ([k]
dates, [Account]
amts) = forall a b. [(a, b)] -> ([a], [b])
unzip forall a b. (a -> b) -> a -> b
$ forall k a. Map k a -> [(k, a)]
M.toAscList Map k Account
amtmap
cumulativeSum :: Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum :: Account -> Map DateSpan Account -> Map DateSpan Account
cumulativeSum Account
start = forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c k. (a -> b -> (a, c)) -> a -> Map k b -> (a, Map k c)
M.mapAccum (\Account
a Account
b -> let s :: Account
s = Account -> Account -> Account
sumAcct Account
a Account
b in (Account
s, Account
s)) Account
start
balanceReportTableAsText :: ReportOpts -> Tab.Table T.Text T.Text WideBuilder -> TB.Builder
balanceReportTableAsText :: ReportOpts
-> Table CommoditySymbol CommoditySymbol WideBuilder -> Builder
balanceReportTableAsText ReportOpts{Bool
Int
[CommoditySymbol]
[Status]
Maybe Int
Maybe CommoditySymbol
Maybe NormalSign
Maybe ValuationType
Maybe ConversionOp
Interval
Period
StringFormat
Layout
AccountListMode
BalanceAccumulation
BalanceCalculation
layout_ :: ReportOpts -> Layout
transpose_ :: ReportOpts -> Bool
color_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
summary_only_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
budgetpat_ :: ReportOpts -> Maybe CommoditySymbol
txn_dates_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
average_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [CommoditySymbol]
pretty_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
real_ :: ReportOpts -> Bool
depth_ :: ReportOpts -> Maybe Int
statuses_ :: ReportOpts -> [Status]
interval_ :: ReportOpts -> Interval
layout_ :: Layout
transpose_ :: Bool
color_ :: Bool
normalbalance_ :: Maybe NormalSign
invert_ :: Bool
percent_ :: Bool
sort_amount_ :: Bool
show_costs_ :: Bool
summary_only_ :: Bool
no_total_ :: Bool
row_total_ :: Bool
declared_ :: Bool
drop_ :: Int
accountlistmode_ :: AccountListMode
budgetpat_ :: Maybe CommoditySymbol
balanceaccum_ :: BalanceAccumulation
balancecalc_ :: BalanceCalculation
txn_dates_ :: Bool
related_ :: Bool
average_ :: Bool
querystring_ :: [CommoditySymbol]
pretty_ :: Bool
format_ :: StringFormat
real_ :: Bool
no_elide_ :: Bool
empty_ :: Bool
date2_ :: Bool
depth_ :: Maybe Int
infer_prices_ :: Bool
value_ :: Maybe ValuationType
conversionop_ :: Maybe ConversionOp
statuses_ :: [Status]
interval_ :: Interval
period_ :: Period
percent_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
sort_amount_ :: ReportOpts -> Bool
empty_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
drop_ :: ReportOpts -> Int
invert_ :: ReportOpts -> Bool
balancecalc_ :: ReportOpts -> BalanceCalculation
balanceaccum_ :: ReportOpts -> BalanceAccumulation
declared_ :: ReportOpts -> Bool
accountlistmode_ :: ReportOpts -> AccountListMode
conversionop_ :: ReportOpts -> Maybe ConversionOp
date2_ :: ReportOpts -> Bool
period_ :: ReportOpts -> Period
value_ :: ReportOpts -> Maybe ValuationType
infer_prices_ :: ReportOpts -> Bool
..} =
forall a ch rh.
Show a =>
TableOpts
-> ([ch] -> [Cell])
-> ((rh, [a]) -> (Cell, [Cell]))
-> Table rh ch a
-> Builder
Tab.renderTableByRowsB forall a. Default a => a
def{tableBorders :: Bool
Tab.tableBorders=Bool
False, prettyTable :: Bool
Tab.prettyTable=Bool
pretty_} [CommoditySymbol] -> [Cell]
renderCh (CommoditySymbol, [WideBuilder]) -> (Cell, [Cell])
renderRow
where
renderCh :: [CommoditySymbol] -> [Cell]
renderCh
| Layout
layout_ forall a. Eq a => a -> a -> Bool
/= Layout
LayoutBare Bool -> Bool -> Bool
|| Bool
transpose_ = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Align -> CommoditySymbol -> Cell
Tab.textCell Align
Tab.TopRight)
| Bool
otherwise = forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith forall a b. (a -> b) -> a -> b
($) (Align -> CommoditySymbol -> Cell
Tab.textCell Align
Tab.TopLeft forall a. a -> [a] -> [a]
: forall a. a -> [a]
repeat (Align -> CommoditySymbol -> Cell
Tab.textCell Align
Tab.TopRight))
renderRow :: (CommoditySymbol, [WideBuilder]) -> (Cell, [Cell])
renderRow (CommoditySymbol
rh, [WideBuilder]
row)
| Layout
layout_ forall a. Eq a => a -> a -> Bool
/= Layout
LayoutBare Bool -> Bool -> Bool
|| Bool
transpose_ =
(Align -> CommoditySymbol -> Cell
Tab.textCell Align
Tab.TopLeft CommoditySymbol
rh, forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Align -> [WideBuilder] -> Cell
Tab.Cell Align
Tab.TopRight forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Applicative f => a -> f a
pure) [WideBuilder]
row)
| Bool
otherwise =
(Align -> CommoditySymbol -> Cell
Tab.textCell Align
Tab.TopLeft CommoditySymbol
rh, forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith forall a b. (a -> b) -> a -> b
($) (Align -> [WideBuilder] -> Cell
Tab.Cell Align
Tab.TopLeft forall a. a -> [a] -> [a]
: forall a. a -> [a]
repeat (Align -> [WideBuilder] -> Cell
Tab.Cell Align
Tab.TopRight)) (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall (f :: * -> *) a. Applicative f => a -> f a
pure [WideBuilder]
row))
tests_MultiBalanceReport :: TestTree
tests_MultiBalanceReport = [Char] -> [TestTree] -> TestTree
testGroup [Char]
"MultiBalanceReport" [
let
amt0 :: Amount
amt0 = Amount {acommodity :: CommoditySymbol
acommodity=CommoditySymbol
"$", aquantity :: Quantity
aquantity=Quantity
0, aprice :: Maybe AmountPrice
aprice=forall a. Maybe a
Nothing,
astyle :: AmountStyle
astyle=AmountStyle {ascommodityside :: Side
ascommodityside = Side
L, ascommodityspaced :: Bool
ascommodityspaced = Bool
False, asdigitgroups :: Maybe DigitGroupStyle
asdigitgroups = forall a. Maybe a
Nothing,
asdecimalmark :: Maybe Char
asdecimalmark = forall a. a -> Maybe a
Just Char
'.', asprecision :: AmountPrecision
asprecision = Word8 -> AmountPrecision
Precision Word8
2, asrounding :: Rounding
asrounding = Rounding
NoRounding}}
(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 forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
_rsReportOpts 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
-> (CommoditySymbol, CommoditySymbol, Int, [[Char]], [Char],
[Char])
showw (PeriodicReportRow DisplayName
a [MixedAmount]
lAmt MixedAmount
amt MixedAmount
amt')
= (DisplayName -> CommoditySymbol
displayFull DisplayName
a, DisplayName -> CommoditySymbol
displayName DisplayName
a, DisplayName -> Int
displayDepth DisplayName
a, forall a b. (a -> b) -> [a] -> [b]
map MixedAmount -> [Char]
showMixedAmountDebug [MixedAmount]
lAmt, MixedAmount -> [Char]
showMixedAmountDebug MixedAmount
amt, MixedAmount -> [Char]
showMixedAmountDebug MixedAmount
amt')
(forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount
-> (CommoditySymbol, CommoditySymbol, Int, [[Char]], [Char],
[Char])
showw [PeriodicReportRow DisplayName MixedAmount]
aitems) forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= (forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount
-> (CommoditySymbol, CommoditySymbol, Int, [[Char]], [Char],
[Char])
showw [PeriodicReportRow DisplayName MixedAmount]
eitems)
MixedAmount -> [Char]
showMixedAmountDebug (forall a b. PeriodicReportRow a b -> b
prrTotal PeriodicReportRow () MixedAmount
atotal) forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= MixedAmount -> [Char]
showMixedAmountDebug MixedAmount
etotal
in
[Char] -> [TestTree] -> TestTree
testGroup [Char]
"multiBalanceReport" [
[Char] -> IO () -> TestTree
testCase [Char]
"null journal" forall a b. (a -> b) -> a -> b
$
(ReportSpec
defreportspec, Journal
nulljournal) (ReportSpec, Journal)
-> ([PeriodicReportRow DisplayName MixedAmount], MixedAmount)
-> IO ()
`gives` ([], MixedAmount
nullmixedamt)
,[Char] -> IO () -> TestTree
testCase [Char]
"with -H on a populated period" forall a b. (a -> b) -> a -> b
$
(ReportSpec
defreportspec{_rsReportOpts :: ReportOpts
_rsReportOpts=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), balanceaccum_ :: BalanceAccumulation
balanceaccum_=BalanceAccumulation
Historical}}, Journal
samplejournal) (ReportSpec, Journal)
-> ([PeriodicReportRow DisplayName MixedAmount], MixedAmount)
-> IO ()
`gives`
(
[ forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow (CommoditySymbol -> DisplayName
flatDisplayName CommoditySymbol
"assets:bank:checking") [Amount -> MixedAmount
mixedAmount forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd Quantity
1] (Amount -> MixedAmount
mixedAmount forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd Quantity
1) (Amount -> MixedAmount
mixedAmount Amount
amt0{aquantity :: Quantity
aquantity=Quantity
1})
, forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow (CommoditySymbol -> DisplayName
flatDisplayName CommoditySymbol
"income:salary") [Amount -> MixedAmount
mixedAmount forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd (-Quantity
1)] (Amount -> MixedAmount
mixedAmount forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd (-Quantity
1)) (Amount -> MixedAmount
mixedAmount Amount
amt0{aquantity :: Quantity
aquantity=(-Quantity
1)})
],
Amount -> MixedAmount
mixedAmount forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd Quantity
0)
]
]