{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
module Hledger.Reports.MultiBalanceReport (
MultiBalanceReport,
MultiBalanceReportRow,
multiBalanceReport,
multiBalanceReportWith,
compoundBalanceReport,
compoundBalanceReportWith,
makeReportQuery,
getPostings,
generateMultiBalanceAccount,
generatePeriodicReport,
makePeriodicReportRow,
tests_MultiBalanceReport
)
where
#if !MIN_VERSION_base(4,18,0)
import Control.Applicative (liftA2)
#endif
import Control.Monad (guard)
import Data.Foldable (toList)
import Data.List (sortOn)
import Data.List.NonEmpty (NonEmpty((:|)))
import qualified Data.HashSet as HS
import qualified Data.IntMap.Strict as IM
import qualified Data.IntSet as IS
import Data.Maybe (fromMaybe, isJust, mapMaybe)
import Data.Ord (Down(..))
import Data.Semigroup (sconcat)
import Data.These (these)
import Data.Time.Calendar (Day(..), addDays, fromGregorian)
import Data.Traversable (mapAccumL)
import Hledger.Data
import Hledger.Query
import Hledger.Utils
import Hledger.Reports.ReportOptions
import Hledger.Reports.ReportTypes
type MultiBalanceReport = PeriodicReport DisplayName MixedAmount
type MultiBalanceReportRow = PeriodicReportRow DisplayName MixedAmount
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_prices_ (ReportOpts -> Bool) -> ReportOpts -> Bool
forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith :: ReportSpec -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith ReportSpec
rspec' Journal
j PriceOracle
priceoracle = MultiBalanceReport
report
where
(DateSpan
reportspan, [DateSpan]
colspans) = String -> (DateSpan, [DateSpan]) -> (DateSpan, [DateSpan])
forall a. Show a => String -> a -> a
dbg5 String
"multiBalanceReportWith reportSpan" ((DateSpan, [DateSpan]) -> (DateSpan, [DateSpan]))
-> (DateSpan, [DateSpan]) -> (DateSpan, [DateSpan])
forall a b. (a -> b) -> a -> b
$ Journal -> ReportSpec -> (DateSpan, [DateSpan])
reportSpan Journal
j ReportSpec
rspec'
rspec :: ReportSpec
rspec = String -> ReportSpec -> ReportSpec
forall a. Show a => String -> a -> a
dbg3 String
"multiBalanceReportWith rspec" (ReportSpec -> ReportSpec) -> ReportSpec -> ReportSpec
forall a b. (a -> b) -> a -> b
$ ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec' DateSpan
reportspan
ps :: [Posting]
ps = String -> [Posting] -> [Posting]
forall a. Show a => String -> a -> a
dbg5 String
"multiBalanceReportWith ps" ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
getPostings ReportSpec
rspec Journal
j PriceOracle
priceoracle DateSpan
reportspan
acct :: Account BalanceData
acct = String -> Account BalanceData -> Account BalanceData
forall a. Show a => String -> a -> a
dbg5 String
"multiBalanceReportWith acct" (Account BalanceData -> Account BalanceData)
-> Account BalanceData -> Account BalanceData
forall a b. (a -> b) -> a -> b
$ ReportSpec
-> Journal
-> PriceOracle
-> [DateSpan]
-> [Posting]
-> Account BalanceData
generateMultiBalanceAccount ReportSpec
rspec Journal
j PriceOracle
priceoracle [DateSpan]
colspans [Posting]
ps
report :: MultiBalanceReport
report = String -> MultiBalanceReport -> MultiBalanceReport
forall a. Show a => String -> a -> a
dbg4 String
"multiBalanceReportWith report" (MultiBalanceReport -> MultiBalanceReport)
-> MultiBalanceReport -> MultiBalanceReport
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> [DateSpan] -> Account BalanceData -> MultiBalanceReport
generateMultiBalanceReport (ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec) [DateSpan]
colspans Account BalanceData
acct
compoundBalanceReport :: ReportSpec -> Journal -> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReport :: forall a.
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_prices_ (ReportOpts -> Bool) -> ReportOpts -> Bool
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) = String -> (DateSpan, [DateSpan]) -> (DateSpan, [DateSpan])
forall a. Show a => String -> a -> a
dbg5 String
"compoundBalanceReportWith reportSpan" ((DateSpan, [DateSpan]) -> (DateSpan, [DateSpan]))
-> (DateSpan, [DateSpan]) -> (DateSpan, [DateSpan])
forall a b. (a -> b) -> a -> b
$ Journal -> ReportSpec -> (DateSpan, [DateSpan])
reportSpan Journal
j ReportSpec
rspec'
rspec :: ReportSpec
rspec = String -> ReportSpec -> ReportSpec
forall a. Show a => String -> a -> a
dbg3 String
"compoundBalanceReportWith rspec" (ReportSpec -> ReportSpec) -> ReportSpec -> ReportSpec
forall a b. (a -> b) -> a -> b
$ ReportSpec -> DateSpan -> ReportSpec
makeReportQuery ReportSpec
rspec' DateSpan
reportspan
ps :: [Posting]
ps = String -> [Posting] -> [Posting]
forall a. Show a => String -> a -> a
dbg5 String
"compoundBalanceReportWith ps" ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
getPostings ReportSpec
rspec Journal
j PriceOracle
priceoracle DateSpan
reportspan
subreports :: [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
subreports = (CBCSubreportSpec a
-> (CommoditySymbol, PeriodicReport a MixedAmount, Bool))
-> [CBCSubreportSpec a]
-> [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
forall a b. (a -> b) -> [a] -> [b]
map CBCSubreportSpec a
-> (CommoditySymbol, PeriodicReport a MixedAmount, Bool)
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
cbcsubreporttitle :: CommoditySymbol
cbcsubreportquery :: Query
cbcsubreportoptions :: ReportOpts -> ReportOpts
cbcsubreporttransform :: MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreportincreasestotal :: Bool
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
..} =
( CommoditySymbol
cbcsubreporttitle
, MultiBalanceReport -> PeriodicReport a MixedAmount
cbcsubreporttransform (MultiBalanceReport -> PeriodicReport a MixedAmount)
-> MultiBalanceReport -> PeriodicReport a MixedAmount
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> [DateSpan] -> Account BalanceData -> MultiBalanceReport
generateMultiBalanceReport ReportOpts
ropts [DateSpan]
colspans Account BalanceData
acct
, Bool
cbcsubreportincreasestotal
)
where
ropts :: ReportOpts
ropts = ReportOpts -> ReportOpts
cbcsubreportoptions (ReportOpts -> ReportOpts) -> ReportOpts -> ReportOpts
forall a b. (a -> b) -> a -> b
$ ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec
rspecsub :: ReportSpec
rspecsub = ReportSpec
rspec{_rsReportOpts=ropts, _rsQuery=And [cbcsubreportquery, _rsQuery rspec]}
subreportps :: [Posting]
subreportps = (Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter ((CommoditySymbol -> Maybe AccountType) -> Query -> Posting -> Bool
matchesPostingExtra (Journal -> CommoditySymbol -> Maybe AccountType
journalAccountType Journal
j) Query
cbcsubreportquery) [Posting]
ps
acct :: Account BalanceData
acct = ReportSpec
-> Journal
-> PriceOracle
-> [DateSpan]
-> [Posting]
-> Account BalanceData
generateMultiBalanceAccount ReportSpec
rspecsub Journal
j PriceOracle
priceoracle [DateSpan]
colspans [Posting]
subreportps
overalltotals :: PeriodicReportRow () MixedAmount
overalltotals = case [(CommoditySymbol, 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
((CommoditySymbol, PeriodicReport a MixedAmount, Bool)
r:[(CommoditySymbol, 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
$ ((CommoditySymbol, PeriodicReport a MixedAmount, Bool)
-> PeriodicReportRow () MixedAmount)
-> NonEmpty (CommoditySymbol, PeriodicReport a MixedAmount, Bool)
-> NonEmpty (PeriodicReportRow () MixedAmount)
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CommoditySymbol, PeriodicReport a MixedAmount, Bool)
-> PeriodicReportRow () MixedAmount
forall {a} {a}.
(a, PeriodicReport a MixedAmount, Bool)
-> PeriodicReportRow () MixedAmount
subreportTotal ((CommoditySymbol, PeriodicReport a MixedAmount, Bool)
r(CommoditySymbol, PeriodicReport a MixedAmount, Bool)
-> [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
-> NonEmpty (CommoditySymbol, PeriodicReport a MixedAmount, Bool)
forall 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 PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount
forall a. a -> a
id else (MixedAmount -> MixedAmount)
-> PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount
forall a b.
(a -> b) -> PeriodicReportRow () a -> PeriodicReportRow () b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MixedAmount -> MixedAmount
maNegate) (PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount)
-> PeriodicReportRow () MixedAmount
-> PeriodicReportRow () MixedAmount
forall a b. (a -> b) -> a -> b
$ PeriodicReport a MixedAmount -> PeriodicReportRow () MixedAmount
forall a b. PeriodicReport a b -> PeriodicReportRow () b
prTotals PeriodicReport a MixedAmount
sr
cbr :: CompoundPeriodicReport a MixedAmount
cbr = CommoditySymbol
-> [DateSpan]
-> [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
-> PeriodicReportRow () MixedAmount
-> CompoundPeriodicReport a MixedAmount
forall a b.
CommoditySymbol
-> [DateSpan]
-> [(CommoditySymbol, PeriodicReport a b, Bool)]
-> PeriodicReportRow () b
-> CompoundPeriodicReport a b
CompoundPeriodicReport CommoditySymbol
"" [DateSpan]
colspans [(CommoditySymbol, PeriodicReport a MixedAmount, Bool)]
subreports PeriodicReportRow () MixedAmount
overalltotals
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}
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 = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg3 String
"makeReportQuery reportspandatesq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
dateqcons DateSpan
reportspan
dateless :: Query -> Query
dateless = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg3 String
"makeReportQuery 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
_rsReportOpts ReportSpec
rspec) then DateSpan -> Query
Date2 else DateSpan -> Query
Date
getPostings :: ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
getPostings :: ReportSpec -> Journal -> PriceOracle -> DateSpan -> [Posting]
getPostings rspec :: ReportSpec
rspec@ReportSpec{_rsQuery :: ReportSpec -> Query
_rsQuery=Query
query, _rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle DateSpan
reportspan =
[Posting] -> [Posting]
setPostingsCount
([Posting] -> [Posting])
-> (Journal -> [Posting]) -> Journal -> [Posting]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Journal -> [Posting]
journalPostings
(Journal -> [Posting]) -> Journal -> [Posting]
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Journal -> PriceOracle -> Journal
journalValueAndFilterPostingsWith ReportSpec
rspec' Journal
j PriceOracle
priceoracle
where
setPostingsCount :: [Posting] -> [Posting]
setPostingsCount = case ReportOpts -> BalanceCalculation
balancecalc_ ReportOpts
ropts of
BalanceCalculation
CalcPostingsCount -> (Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map ((MixedAmount -> MixedAmount) -> Posting -> Posting
postingTransformAmount (MixedAmount -> MixedAmount -> MixedAmount
forall a b. a -> b -> a
const (MixedAmount -> MixedAmount -> MixedAmount)
-> MixedAmount -> MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ [Amount] -> MixedAmount
forall (t :: * -> *). Foldable t => t Amount -> MixedAmount
mixed [Quantity -> Amount
num Quantity
1]))
BalanceCalculation
_ -> [Posting] -> [Posting]
forall a. a -> a
id
rspec' :: ReportSpec
rspec' = ReportSpec
rspec{_rsQuery=fullreportq,_rsReportOpts=ropts'}
ropts' :: ReportOpts
ropts' = if Maybe (Maybe CommoditySymbol) -> Bool
forall a. Maybe a -> Bool
isJust (ReportOpts -> Maybe (Maybe CommoditySymbol)
valuationAfterSum ReportOpts
ropts)
then ReportOpts
ropts{period_=dateSpanAsPeriod fullreportspan, value_=Nothing, conversionop_=Just NoConversionOp}
else ReportOpts
ropts{period_=dateSpanAsPeriod fullreportspan}
fullreportq :: Query
fullreportq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg3 String
"getPostings fullreportq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query
datelessq, Query
fullreportspanq]
datelessq :: Query
datelessq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg3 String
"getPostings 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
depthlessq
depthlessq :: Query
depthlessq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg3 String
"getPostings depthlessq" (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
queryIsDepth) Query
query
fullreportspan :: DateSpan
fullreportspan = if ReportOpts -> Bool
requiresHistorical ReportOpts
ropts then Maybe EFDay -> Maybe EFDay -> DateSpan
DateSpan Maybe EFDay
forall a. Maybe a
Nothing (Day -> EFDay
Exact (Day -> EFDay) -> Maybe Day -> Maybe EFDay
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DateSpan -> Maybe Day
spanEnd DateSpan
reportspan) else DateSpan
reportspan
fullreportspanq :: Query
fullreportspanq = (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
fullreportspan of
DateSpan Maybe EFDay
Nothing Maybe EFDay
Nothing -> DateSpan
emptydatespan
DateSpan
a -> DateSpan
a
generateMultiBalanceAccount :: ReportSpec -> Journal -> PriceOracle -> [DateSpan] -> [Posting] -> Account BalanceData
generateMultiBalanceAccount :: ReportSpec
-> Journal
-> PriceOracle
-> [DateSpan]
-> [Posting]
-> Account BalanceData
generateMultiBalanceAccount rspec :: ReportSpec
rspec@ReportSpec{_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle [DateSpan]
colspans =
(if (ReportOpts -> Bool
declared_ ReportOpts
ropts Bool -> Bool -> Bool
&& ReportOpts -> Bool
empty_ ReportOpts
ropts) then ReportSpec -> Journal -> Account BalanceData -> Account BalanceData
forall a.
Monoid a =>
ReportSpec -> Journal -> Account a -> Account a
addDeclaredAccounts ReportSpec
rspec Journal
j else Account BalanceData -> Account BalanceData
forall a. a -> a
id)
(Account BalanceData -> Account BalanceData)
-> ([Posting] -> Account BalanceData)
-> [Posting]
-> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (if ReportOpts -> Bool
invert_ ReportOpts
ropts then (BalanceData -> BalanceData)
-> Account BalanceData -> Account BalanceData
forall a b. (a -> b) -> Account a -> Account b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((MixedAmount -> MixedAmount) -> BalanceData -> BalanceData
mapBalanceData MixedAmount -> MixedAmount
maNegate) else Account BalanceData -> Account BalanceData
forall a. a -> a
id)
(Account BalanceData -> Account BalanceData)
-> ([Posting] -> Account BalanceData)
-> [Posting]
-> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReportSpec -> Account BalanceData -> Account BalanceData
markAccountBoring ReportSpec
rspec
(Account BalanceData -> Account BalanceData)
-> ([Posting] -> Account BalanceData)
-> [Posting]
-> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Account BalanceData -> Account BalanceData)
-> Account BalanceData -> Account BalanceData
forall a. (Account a -> Account a) -> Account a -> Account a
mapAccounts (Journal -> Account BalanceData -> Account BalanceData
forall a. Journal -> Account a -> Account a
accountSetDeclarationInfo Journal
j)
(Account BalanceData -> Account BalanceData)
-> ([Posting] -> Account BalanceData)
-> [Posting]
-> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReportSpec
-> Journal
-> PriceOracle
-> [DateSpan]
-> [Posting]
-> Account BalanceData
calculateReportAccount ReportSpec
rspec Journal
j PriceOracle
priceoracle [DateSpan]
colspans
([Posting] -> Account BalanceData)
-> ([Posting] -> [Posting]) -> [Posting] -> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Posting -> Posting) -> [Posting] -> [Posting]
forall a b. (a -> b) -> [a] -> [b]
map Posting -> Posting
clipPosting
where
clipPosting :: Posting -> Posting
clipPosting Posting
p = Posting
p{paccount = clipOrEllipsifyAccountName depthSpec $ paccount p}
depthSpec :: DepthSpec
depthSpec = String -> DepthSpec -> DepthSpec
forall a. Show a => String -> a -> a
dbg3 String
"generateMultiBalanceAccount depthSpec"
(DepthSpec -> DepthSpec)
-> (Query -> DepthSpec) -> Query -> DepthSpec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> DepthSpec
queryDepth (Query -> DepthSpec) -> (Query -> Query) -> Query -> DepthSpec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth (Query -> DepthSpec) -> Query -> DepthSpec
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Query
_rsQuery ReportSpec
rspec
addDeclaredAccounts :: Monoid a => ReportSpec -> Journal -> Account a -> Account a
addDeclaredAccounts :: forall a.
Monoid a =>
ReportSpec -> Journal -> Account a -> Account a
addDeclaredAccounts ReportSpec
rspec Journal
j Account a
acct =
(a -> a) -> (a -> a) -> (a -> a -> a) -> These a a -> a
forall a c b.
(a -> c) -> (b -> c) -> (a -> b -> c) -> These a b -> c
these a -> a
forall a. a -> a
id a -> a
forall a. a -> a
id a -> a -> a
forall a b. a -> b -> a
const (These a a -> a) -> Account (These a a) -> Account a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Account a -> Account a -> Account (These a a)
forall a b. Account a -> Account b -> Account (These a b)
mergeAccounts Account a
acct Account a
declaredTree
where
declaredTree :: Account a
declaredTree =
(Account a -> Account a) -> Account a -> Account a
forall a. (Account a -> Account a) -> Account a -> Account a
mapAccounts (\Account a
a -> Account a
a{aboring = not $ aname a `HS.member` HS.fromList declaredAccounts}) (Account a -> Account a) -> Account a -> Account a
forall a b. (a -> b) -> a -> b
$
CommoditySymbol -> PeriodData a -> [CommoditySymbol] -> Account a
forall a.
CommoditySymbol -> PeriodData a -> [CommoditySymbol] -> Account a
accountTreeFromBalanceAndNames CommoditySymbol
"root" (a
forall a. Monoid a => a
mempty a -> PeriodData a -> PeriodData a
forall a b. a -> PeriodData b -> PeriodData a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Account a -> PeriodData a
forall a. Account a -> PeriodData a
adata Account a
acct) [CommoditySymbol]
declaredAccounts
declaredAccounts :: [CommoditySymbol]
declaredAccounts =
(CommoditySymbol -> CommoditySymbol)
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> [a] -> [b]
map (DepthSpec -> CommoditySymbol -> CommoditySymbol
clipOrEllipsifyAccountName DepthSpec
depthSpec) ([CommoditySymbol] -> [CommoditySymbol])
-> ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol]
-> [CommoditySymbol]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
(CommoditySymbol -> Bool) -> [CommoditySymbol] -> [CommoditySymbol]
forall a. (a -> Bool) -> [a] -> [a]
filter ((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] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$
Journal -> [CommoditySymbol]
journalAccountNamesDeclared Journal
j
accttypetagsq :: Query
accttypetagsq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg3 String
"addDeclaredAccounts accttypetagsq" (Query -> Query) -> (Query -> Query) -> Query -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
(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) -> Query -> Query
forall a b. (a -> b) -> a -> b
$
ReportSpec -> Query
_rsQuery ReportSpec
rspec
depthSpec :: DepthSpec
depthSpec = Query -> DepthSpec
queryDepth (Query -> DepthSpec) -> (Query -> Query) -> Query -> DepthSpec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth (Query -> DepthSpec) -> Query -> DepthSpec
forall a b. (a -> b) -> a -> b
$ ReportSpec -> Query
_rsQuery ReportSpec
rspec
calculateReportAccount :: ReportSpec -> Journal -> PriceOracle -> [DateSpan] -> [Posting] -> Account BalanceData
calculateReportAccount :: ReportSpec
-> Journal
-> PriceOracle
-> [DateSpan]
-> [Posting]
-> Account BalanceData
calculateReportAccount rspec :: ReportSpec
rspec@ReportSpec{_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts} Journal
j PriceOracle
priceoracle [DateSpan]
colspans [Posting]
ps =
(PeriodData BalanceData -> PeriodData BalanceData)
-> Account BalanceData -> Account BalanceData
forall a. (PeriodData a -> PeriodData a) -> Account a -> Account a
mapPeriodData PeriodData BalanceData -> PeriodData BalanceData
rowbals Account BalanceData
changesAcct
where
rowbals :: PeriodData BalanceData -> PeriodData BalanceData
rowbals :: PeriodData BalanceData -> PeriodData BalanceData
rowbals PeriodData BalanceData
unvaluedChanges = case ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts of
BalanceAccumulation
PerPeriod -> PeriodData BalanceData
changes
BalanceAccumulation
Cumulative -> PeriodData BalanceData
cumulative
BalanceAccumulation
Historical -> PeriodData BalanceData
historical
where
changes :: PeriodData BalanceData
changes = case ReportOpts -> BalanceCalculation
balancecalc_ ReportOpts
ropts of
BalanceCalculation
CalcChange -> PeriodData BalanceData -> PeriodData BalanceData
avalue PeriodData BalanceData
unvaluedChanges
BalanceCalculation
CalcBudget -> PeriodData BalanceData -> PeriodData BalanceData
avalue PeriodData BalanceData
unvaluedChanges
BalanceCalculation
CalcValueChange -> PeriodData BalanceData -> PeriodData BalanceData
forall (t :: * -> *).
Traversable t =>
t BalanceData -> t BalanceData
periodChanges PeriodData BalanceData
historical
BalanceCalculation
CalcGain -> PeriodData BalanceData -> PeriodData BalanceData
forall (t :: * -> *).
Traversable t =>
t BalanceData -> t BalanceData
periodChanges PeriodData BalanceData
historical
BalanceCalculation
CalcPostingsCount -> PeriodData BalanceData -> PeriodData BalanceData
avalue PeriodData BalanceData
unvaluedChanges
historical :: PeriodData BalanceData
historical = PeriodData BalanceData -> PeriodData BalanceData
avalue (PeriodData BalanceData -> PeriodData BalanceData)
-> PeriodData BalanceData -> PeriodData BalanceData
forall a b. (a -> b) -> a -> b
$ PeriodData BalanceData -> PeriodData BalanceData
forall (t :: * -> *).
Traversable t =>
t BalanceData -> t BalanceData
cumulativeSum PeriodData BalanceData
unvaluedChanges
cumulative :: PeriodData BalanceData
cumulative = PeriodData BalanceData -> PeriodData BalanceData
forall (t :: * -> *).
Traversable t =>
t BalanceData -> t BalanceData
cumulativeSum PeriodData BalanceData
changes{pdpre = mempty}
avalue :: PeriodData BalanceData -> PeriodData BalanceData
avalue = ReportOpts
-> Journal
-> PriceOracle
-> [DateSpan]
-> PeriodData BalanceData
-> PeriodData BalanceData
periodDataValuation ReportOpts
ropts Journal
j PriceOracle
priceoracle [DateSpan]
colspans
changesAcct :: Account BalanceData
changesAcct = (Account BalanceData -> String)
-> Account BalanceData -> Account BalanceData
forall a. (a -> String) -> a -> a
dbg5With (\Account BalanceData
x -> String
"calculateReportAccount changesAcct\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Account BalanceData -> String
forall a. Show a => Account a -> String
showAccounts Account BalanceData
x) (Account BalanceData -> Account BalanceData)
-> (Account BalanceData -> Account BalanceData)
-> Account BalanceData
-> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
(PeriodData BalanceData -> PeriodData BalanceData)
-> Account BalanceData -> Account BalanceData
forall a. (PeriodData a -> PeriodData a) -> Account a -> Account a
mapPeriodData (IntSet -> PeriodData BalanceData -> PeriodData BalanceData
forall a. Monoid a => IntSet -> PeriodData a -> PeriodData a
padPeriodData IntSet
intervalStarts) (Account BalanceData -> Account BalanceData)
-> Account BalanceData -> Account BalanceData
forall a b. (a -> b) -> a -> b
$
(Posting -> Maybe Day) -> [Posting] -> Account BalanceData
accountFromPostings Posting -> Maybe Day
getIntervalStartDate [Posting]
ps
getIntervalStartDate :: Posting -> Maybe Day
getIntervalStartDate Posting
p = Key -> Day
intToDay (Key -> Day) -> Maybe Key -> Maybe Day
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Key -> IntSet -> Maybe Key
IS.lookupLE (Day -> Key
dayToInt (Day -> Key) -> Day -> Key
forall a b. (a -> b) -> a -> b
$ Posting -> Day
getPostingDate Posting
p) IntSet
intervalStarts
getPostingDate :: Posting -> Day
getPostingDate = WhichDate -> Posting -> Day
postingDateOrDate2 (ReportOpts -> WhichDate
whichDate (ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec))
intervalStarts :: IntSet
intervalStarts = [Key] -> IntSet
IS.fromList ([Key] -> IntSet) -> ([Day] -> [Key]) -> [Day] -> IntSet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Day -> Key) -> [Day] -> [Key]
forall a b. (a -> b) -> [a] -> [b]
map Day -> Key
dayToInt ([Day] -> IntSet) -> [Day] -> IntSet
forall a b. (a -> b) -> a -> b
$ case (DateSpan -> Maybe Day) -> [DateSpan] -> [Day]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe DateSpan -> Maybe Day
spanStart [DateSpan]
colspans of
[] -> [Day
nulldate]
[Day]
xs -> [Day]
xs
dayToInt :: Day -> Key
dayToInt = Integer -> Key
forall a. Num a => Integer -> a
fromInteger (Integer -> Key) -> (Day -> Integer) -> Day -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Day -> Integer
toModifiedJulianDay
intToDay :: Key -> Day
intToDay = Integer -> Day
ModifiedJulianDay (Integer -> Day) -> (Key -> Integer) -> Key -> Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Key -> Integer
forall a. Integral a => a -> Integer
toInteger
periodDataValuation :: ReportOpts -> Journal -> PriceOracle -> [DateSpan]
-> PeriodData BalanceData -> PeriodData BalanceData
periodDataValuation :: ReportOpts
-> Journal
-> PriceOracle
-> [DateSpan]
-> PeriodData BalanceData
-> PeriodData BalanceData
periodDataValuation ReportOpts
ropts Journal
j PriceOracle
priceoracle [DateSpan]
colspans =
(Day -> BalanceData -> BalanceData)
-> PeriodData Day
-> PeriodData BalanceData
-> PeriodData BalanceData
forall a b c.
(a -> b -> c) -> PeriodData a -> PeriodData b -> PeriodData c
opPeriodData Day -> BalanceData -> BalanceData
valueBalanceData PeriodData Day
balanceDataPeriodEnds
where
valueBalanceData :: Day -> BalanceData -> BalanceData
valueBalanceData :: Day -> BalanceData -> BalanceData
valueBalanceData Day
d = (MixedAmount -> MixedAmount) -> BalanceData -> BalanceData
mapBalanceData (Day -> MixedAmount -> MixedAmount
valueMixedAmount Day
d)
valueMixedAmount :: Day -> MixedAmount -> MixedAmount
valueMixedAmount :: Day -> MixedAmount -> MixedAmount
valueMixedAmount = ReportOpts
-> Journal -> PriceOracle -> Day -> MixedAmount -> MixedAmount
mixedAmountApplyValuationAfterSumFromOptsWith ReportOpts
ropts Journal
j PriceOracle
priceoracle
balanceDataPeriodEnds :: PeriodData Day
balanceDataPeriodEnds :: PeriodData Day
balanceDataPeriodEnds = String -> PeriodData Day -> PeriodData Day
forall a. Show a => String -> a -> a
dbg5 String
"balanceDataPeriodEnds" (PeriodData Day -> PeriodData Day)
-> PeriodData Day -> PeriodData Day
forall a b. (a -> b) -> a -> b
$ case [DateSpan]
colspans of
[DateSpan Maybe EFDay
Nothing Maybe EFDay
Nothing] -> Day -> [(Day, Day)] -> PeriodData Day
forall a. a -> [(Day, a)] -> PeriodData a
periodDataFromList Day
nulldate [(Day
nulldate, Day
nulldate)]
DateSpan
h:[DateSpan]
ds -> Day -> [(Day, Day)] -> PeriodData Day
forall a. a -> [(Day, a)] -> PeriodData a
periodDataFromList ((Maybe Day, Maybe Day) -> Day
forall {b}. (Maybe Day, b) -> Day
makeJustFst ((Maybe Day, Maybe Day) -> Day) -> (Maybe Day, Maybe Day) -> Day
forall a b. (a -> b) -> a -> b
$ DateSpan -> (Maybe Day, Maybe Day)
boundaries DateSpan
h) ([(Day, Day)] -> PeriodData Day) -> [(Day, Day)] -> PeriodData Day
forall a b. (a -> b) -> a -> b
$ (DateSpan -> (Day, Day)) -> [DateSpan] -> [(Day, Day)]
forall a b. (a -> b) -> [a] -> [b]
map ((Maybe Day, Maybe Day) -> (Day, Day)
forall {a}. (Maybe a, Maybe Day) -> (a, Day)
makeJust ((Maybe Day, Maybe Day) -> (Day, Day))
-> (DateSpan -> (Maybe Day, Maybe Day)) -> DateSpan -> (Day, Day)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DateSpan -> (Maybe Day, Maybe Day)
boundaries) (DateSpan
hDateSpan -> [DateSpan] -> [DateSpan]
forall a. a -> [a] -> [a]
:[DateSpan]
ds)
[] -> String -> PeriodData Day
forall a. String -> a
error' String
"balanceDataPeriodEnds: Shouldn't have empty colspans"
where
boundaries :: DateSpan -> (Maybe Day, Maybe Day)
boundaries DateSpan
spn = (DateSpan -> Maybe Day
spanStart DateSpan
spn, DateSpan -> Maybe Day
spanEnd DateSpan
spn)
makeJust :: (Maybe a, Maybe Day) -> (a, Day)
makeJust (Just a
x, Just Day
y) = (a
x, Integer -> Day -> Day
addDays (-Integer
1) Day
y)
makeJust (Maybe a, Maybe Day)
_ = String -> (a, Day)
forall a. String -> a
error' String
"balanceDataPeriodEnds: expected all non-initial spans to have start and end dates"
makeJustFst :: (Maybe Day, b) -> Day
makeJustFst (Just Day
x, b
_) = Integer -> Day -> Day
addDays (-Integer
1) Day
x
makeJustFst (Maybe Day, b)
_ = String -> Day
forall a. String -> a
error' String
"balanceDataPeriodEnds: expected initial span to have an end date"
markAccountBoring :: ReportSpec -> Account BalanceData -> Account BalanceData
markAccountBoring :: ReportSpec -> Account BalanceData -> Account BalanceData
markAccountBoring ReportSpec{_rsQuery :: ReportSpec -> Query
_rsQuery=Query
query,_rsReportOpts :: ReportSpec -> ReportOpts
_rsReportOpts=ReportOpts
ropts}
| Bool
qdepthIsZero = Bool -> Account BalanceData -> Account BalanceData
forall {a}. Bool -> Account a -> Account a
markBoring Bool
False (Account BalanceData -> Account BalanceData)
-> (Account BalanceData -> Account BalanceData)
-> Account BalanceData
-> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Account BalanceData -> Account BalanceData)
-> Account BalanceData -> Account BalanceData
forall a. (Account a -> Account a) -> Account a -> Account a
mapAccounts (Bool -> Account BalanceData -> Account BalanceData
forall {a}. Bool -> Account a -> Account a
markBoring Bool
True)
| Bool
otherwise = Bool -> Account BalanceData -> Account BalanceData
forall {a}. Bool -> Account a -> Account a
markBoring Bool
True (Account BalanceData -> Account BalanceData)
-> (Account BalanceData -> Account BalanceData)
-> Account BalanceData
-> Account BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Account BalanceData -> Account BalanceData)
-> Account BalanceData -> Account BalanceData
forall a. (Account a -> Account a) -> Account a -> Account a
mapAccounts ((Account BalanceData -> Bool)
-> Account BalanceData -> Account BalanceData
forall {a}. (Account a -> Bool) -> Account a -> Account a
markBoringBy ((Bool -> Bool -> Bool)
-> (Account BalanceData -> Bool)
-> (Account BalanceData -> Bool)
-> Account BalanceData
-> Bool
forall a b c.
(a -> b -> c)
-> (Account BalanceData -> a)
-> (Account BalanceData -> b)
-> Account BalanceData
-> c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 Bool -> Bool -> Bool
(&&) Account BalanceData -> Bool
isBoring Account BalanceData -> Bool
forall a. Account a -> Bool
isBoringParent))
where
isBoring :: Account BalanceData -> Bool
isBoring :: Account BalanceData -> Bool
isBoring Account BalanceData
acct = Bool
tooDeep Bool -> Bool -> Bool
|| Bool
allZeros
where
tooDeep :: Bool
tooDeep = Key
d Key -> Key -> Bool
forall a. Ord a => a -> a -> Bool
> Key
qdepth
allZeros :: Bool
allZeros = (BalanceData -> MixedAmount) -> IntMap BalanceData -> Bool
forall {t :: * -> *} {a}.
Foldable t =>
(a -> MixedAmount) -> t a -> Bool
isZeroRow BalanceData -> MixedAmount
balance IntMap BalanceData
amts Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
keepEmptyAccount
keepEmptyAccount :: Bool
keepEmptyAccount = ReportOpts -> Bool
empty_ ReportOpts
ropts Bool -> Bool -> Bool
&& Account BalanceData -> Bool
keepWhenEmpty Account BalanceData
acct
amts :: IntMap BalanceData
amts = PeriodData BalanceData -> IntMap BalanceData
forall a. PeriodData a -> IntMap a
pdperiods (PeriodData BalanceData -> IntMap BalanceData)
-> PeriodData BalanceData -> IntMap BalanceData
forall a b. (a -> b) -> a -> b
$ Account BalanceData -> PeriodData BalanceData
forall a. Account a -> PeriodData a
adata Account BalanceData
acct
d :: Key
d = CommoditySymbol -> Key
accountNameLevel (CommoditySymbol -> Key) -> CommoditySymbol -> Key
forall a b. (a -> b) -> a -> b
$ Account BalanceData -> CommoditySymbol
forall a. Account a -> CommoditySymbol
aname Account BalanceData
acct
qdepth :: Key
qdepth = Key -> Maybe Key -> Key
forall a. a -> Maybe a -> a
fromMaybe Key
forall a. Bounded a => a
maxBound (Maybe Key -> Key)
-> (CommoditySymbol -> Maybe Key) -> CommoditySymbol -> Key
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DepthSpec -> CommoditySymbol -> Maybe Key
getAccountNameClippedDepth DepthSpec
depthspec (CommoditySymbol -> Key) -> CommoditySymbol -> Key
forall a b. (a -> b) -> a -> b
$ Account BalanceData -> CommoditySymbol
forall a. Account a -> CommoditySymbol
aname Account BalanceData
acct
balance :: BalanceData -> MixedAmount
balance = MixedAmount -> MixedAmount
maybeStripPrices (MixedAmount -> MixedAmount)
-> (BalanceData -> MixedAmount) -> BalanceData -> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree | Key
d Key -> Key -> Bool
forall a. Eq a => a -> a -> Bool
== Key
qdepth -> BalanceData -> MixedAmount
bdincludingsubs
AccountListMode
_ -> BalanceData -> MixedAmount
bdexcludingsubs
isBoringParent :: Account a -> Bool
isBoringParent :: forall a. Account a -> Bool
isBoringParent Account a
acct = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> Bool
notEnoughSubs Bool -> Bool -> Bool
|| Bool
droppedAccount
AccountListMode
ALFlat -> Bool
True
where
notEnoughSubs :: Bool
notEnoughSubs = [Account a] -> Key
forall a. [a] -> Key
forall (t :: * -> *) a. Foldable t => t a -> Key
length [Account a]
interestingSubs Key -> Key -> Bool
forall a. Ord a => a -> a -> Bool
< Key
minimumSubs
droppedAccount :: Bool
droppedAccount = CommoditySymbol -> Key
accountNameLevel (Account a -> CommoditySymbol
forall a. Account a -> CommoditySymbol
aname Account a
acct) Key -> Key -> Bool
forall a. Ord a => a -> a -> Bool
<= ReportOpts -> Key
drop_ ReportOpts
ropts
interestingSubs :: [Account a]
interestingSubs = (Account a -> Bool) -> [Account a] -> [Account a]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Account a -> Bool) -> Account a -> Bool
forall a. (Account a -> Bool) -> Account a -> Bool
anyAccounts (Bool -> Bool
not (Bool -> Bool) -> (Account a -> Bool) -> Account a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account a -> Bool
forall a. Account a -> Bool
aboring)) ([Account a] -> [Account a]) -> [Account a] -> [Account a]
forall a b. (a -> b) -> a -> b
$ Account a -> [Account a]
forall a. Account a -> [Account a]
asubs Account a
acct
minimumSubs :: Key
minimumSubs = if ReportOpts -> Bool
no_elide_ ReportOpts
ropts then Key
1 else Key
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)
keepWhenEmpty :: Account BalanceData -> Bool
keepWhenEmpty = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALFlat -> (BalanceData -> Bool) -> IntMap BalanceData -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((Key
0Key -> Key -> Bool
forall a. Ord a => a -> a -> Bool
<) (Key -> Bool) -> (BalanceData -> Key) -> BalanceData -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BalanceData -> Key
bdnumpostings) (IntMap BalanceData -> Bool)
-> (Account BalanceData -> IntMap BalanceData)
-> Account BalanceData
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeriodData BalanceData -> IntMap BalanceData
forall a. PeriodData a -> IntMap a
pdperiods (PeriodData BalanceData -> IntMap BalanceData)
-> (Account BalanceData -> PeriodData BalanceData)
-> Account BalanceData
-> IntMap BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account BalanceData -> PeriodData BalanceData
forall a. Account a -> PeriodData a
adata
AccountListMode
ALTree -> [Account BalanceData] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Account BalanceData] -> Bool)
-> (Account BalanceData -> [Account BalanceData])
-> Account BalanceData
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account BalanceData -> [Account BalanceData]
forall a. Account a -> [Account a]
asubs
maybeStripPrices :: MixedAmount -> MixedAmount
maybeStripPrices = if ReportOpts -> Maybe ConversionOp
conversionop_ ReportOpts
ropts Maybe ConversionOp -> Maybe ConversionOp -> Bool
forall a. Eq a => a -> a -> Bool
== ConversionOp -> Maybe ConversionOp
forall a. a -> Maybe a
Just ConversionOp
NoConversionOp then MixedAmount -> MixedAmount
forall a. a -> a
id else MixedAmount -> MixedAmount
mixedAmountStripCosts
qdepthIsZero :: Bool
qdepthIsZero = DepthSpec
depthspec DepthSpec -> DepthSpec -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Key -> [(Regexp, Key)] -> DepthSpec
DepthSpec (Key -> Maybe Key
forall a. a -> Maybe a
Just Key
0) []
depthspec :: DepthSpec
depthspec = Query -> DepthSpec
queryDepth Query
query
markBoring :: Bool -> Account a -> Account a
markBoring Bool
v Account a
a = Account a
a{aboring = v}
markBoringBy :: (Account a -> Bool) -> Account a -> Account a
markBoringBy Account a -> Bool
f Account a
a = Account a
a{aboring = f a}
generateMultiBalanceReport :: ReportOpts -> [DateSpan] -> Account BalanceData -> MultiBalanceReport
generateMultiBalanceReport :: ReportOpts
-> [DateSpan] -> Account BalanceData -> MultiBalanceReport
generateMultiBalanceReport ReportOpts
ropts [DateSpan]
colspans =
ReportOpts -> MultiBalanceReport -> MultiBalanceReport
reportPercent ReportOpts
ropts (MultiBalanceReport -> MultiBalanceReport)
-> (Account BalanceData -> MultiBalanceReport)
-> Account BalanceData
-> MultiBalanceReport
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account BalanceData
-> PeriodicReportRow a MixedAmount)
-> (BalanceData -> MixedAmount)
-> (MixedAmount -> MixedAmount)
-> ReportOpts
-> [DateSpan]
-> Account BalanceData
-> MultiBalanceReport
forall c b.
Show c =>
(forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account b
-> PeriodicReportRow a c)
-> (b -> MixedAmount)
-> (c -> MixedAmount)
-> ReportOpts
-> [DateSpan]
-> Account b
-> PeriodicReport DisplayName c
generatePeriodicReport ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account BalanceData
-> PeriodicReportRow a MixedAmount
forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account BalanceData
-> PeriodicReportRow a MixedAmount
makeMultiBalanceReportRow BalanceData -> MixedAmount
bdincludingsubs MixedAmount -> MixedAmount
forall a. a -> a
id ReportOpts
ropts [DateSpan]
colspans
generatePeriodicReport :: Show c =>
(forall a. ReportOpts -> (BalanceData -> MixedAmount) -> a -> Account b -> PeriodicReportRow a c)
-> (b -> MixedAmount) -> (c -> MixedAmount)
-> ReportOpts -> [DateSpan] -> Account b -> PeriodicReport DisplayName c
generatePeriodicReport :: forall c b.
Show c =>
(forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account b
-> PeriodicReportRow a c)
-> (b -> MixedAmount)
-> (c -> MixedAmount)
-> ReportOpts
-> [DateSpan]
-> Account b
-> PeriodicReport DisplayName c
generatePeriodicReport forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account b
-> PeriodicReportRow a c
makeRow b -> MixedAmount
treeAmt c -> MixedAmount
flatAmt ReportOpts
ropts [DateSpan]
colspans Account b
acct =
[DateSpan]
-> [PeriodicReportRow DisplayName c]
-> PeriodicReportRow () c
-> PeriodicReport DisplayName c
forall a b.
[DateSpan]
-> [PeriodicReportRow a b]
-> PeriodicReportRow () b
-> PeriodicReport a b
PeriodicReport [DateSpan]
colspans (Account b -> [PeriodicReportRow DisplayName c]
buildAndSort Account b
acct) PeriodicReportRow () c
totalsrow
where
buildAndSort :: Account b -> [PeriodicReportRow DisplayName c]
buildAndSort = String
-> [PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c]
forall a. Show a => String -> a -> a
dbg5 String
"generatePeriodicReport buildAndSort" ([PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c])
-> (Account b -> [PeriodicReportRow DisplayName c])
-> Account b
-> [PeriodicReportRow DisplayName c]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree | ReportOpts -> Bool
sort_amount_ ReportOpts
ropts -> Account b -> [PeriodicReportRow DisplayName c]
buildRows (Account b -> [PeriodicReportRow DisplayName c])
-> (Account b -> Account b)
-> Account b
-> [PeriodicReportRow DisplayName c]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account b -> Account b
sortTreeByAmount
AccountListMode
ALFlat | ReportOpts -> Bool
sort_amount_ ReportOpts
ropts -> [PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c]
sortFlatByAmount ([PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c])
-> (Account b -> [PeriodicReportRow DisplayName c])
-> Account b
-> [PeriodicReportRow DisplayName c]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account b -> [PeriodicReportRow DisplayName c]
buildRows
AccountListMode
_ -> Account b -> [PeriodicReportRow DisplayName c]
buildRows (Account b -> [PeriodicReportRow DisplayName c])
-> (Account b -> Account b)
-> Account b
-> [PeriodicReportRow DisplayName c]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account b -> Account b
forall a. Account a -> Account a
sortAccountTreeByDeclaration
buildRows :: Account b -> [PeriodicReportRow DisplayName c]
buildRows = (ReportOpts
-> (BalanceData -> MixedAmount)
-> DisplayName
-> Account b
-> PeriodicReportRow DisplayName c)
-> ReportOpts -> Account b -> [PeriodicReportRow DisplayName c]
forall b c.
(ReportOpts
-> (BalanceData -> MixedAmount)
-> DisplayName
-> Account b
-> PeriodicReportRow DisplayName c)
-> ReportOpts -> Account b -> [PeriodicReportRow DisplayName c]
buildReportRows ReportOpts
-> (BalanceData -> MixedAmount)
-> DisplayName
-> Account b
-> PeriodicReportRow DisplayName c
forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account b
-> PeriodicReportRow a c
makeRow ReportOpts
ropts
totalsrow :: PeriodicReportRow () c
totalsrow = String -> PeriodicReportRow () c -> PeriodicReportRow () c
forall a. Show a => String -> a -> a
dbg5 String
"generatePeriodicReport totalsrow" (PeriodicReportRow () c -> PeriodicReportRow () c)
-> PeriodicReportRow () c -> PeriodicReportRow () c
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> (BalanceData -> MixedAmount)
-> ()
-> Account b
-> PeriodicReportRow () c
forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account b
-> PeriodicReportRow a c
makeRow ReportOpts
ropts BalanceData -> MixedAmount
bdincludingsubs () Account b
acct
sortTreeByAmount :: Account b -> Account b
sortTreeByAmount = 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 -> (Account b -> (Down MixedAmount, CommoditySymbol))
-> Account b -> Account b
forall b a. Ord b => (Account a -> b) -> Account a -> Account a
sortAccountTreeOn (\Account b
r -> (MixedAmount -> Down MixedAmount
forall a. a -> Down a
Down (MixedAmount -> Down MixedAmount)
-> MixedAmount -> Down MixedAmount
forall a b. (a -> b) -> a -> b
$ Account b -> MixedAmount
amt Account b
r, Account b -> CommoditySymbol
forall a. Account a -> CommoditySymbol
aname Account b
r))
NormalSign
NormallyNegative -> (Account b -> (MixedAmount, CommoditySymbol))
-> Account b -> Account b
forall b a. Ord b => (Account a -> b) -> Account a -> Account a
sortAccountTreeOn (\Account b
r -> (Account b -> MixedAmount
amt Account b
r, Account b -> CommoditySymbol
forall a. Account a -> CommoditySymbol
aname Account b
r))
where
amt :: Account b -> MixedAmount
amt = MixedAmount -> MixedAmount
mixedAmountStripCosts (MixedAmount -> MixedAmount)
-> (Account b -> MixedAmount) -> Account b -> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IntMap MixedAmount -> MixedAmount
sortKey (IntMap MixedAmount -> MixedAmount)
-> (Account b -> IntMap MixedAmount) -> Account b -> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (b -> MixedAmount) -> IntMap b -> IntMap MixedAmount
forall a b. (a -> b) -> IntMap a -> IntMap b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap b -> MixedAmount
treeAmt (IntMap b -> IntMap MixedAmount)
-> (Account b -> IntMap b) -> Account b -> IntMap MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeriodData b -> IntMap b
forall a. PeriodData a -> IntMap a
pdperiods (PeriodData b -> IntMap b)
-> (Account b -> PeriodData b) -> Account b -> IntMap b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Account b -> PeriodData b
forall a. Account a -> PeriodData a
adata
sortKey :: IntMap MixedAmount -> MixedAmount
sortKey = case ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts of
BalanceAccumulation
PerPeriod -> IntMap MixedAmount -> MixedAmount
forall (t :: * -> *). Foldable t => t MixedAmount -> MixedAmount
maSum
BalanceAccumulation
_ -> MixedAmount
-> ((Key, MixedAmount) -> MixedAmount)
-> Maybe (Key, MixedAmount)
-> MixedAmount
forall b a. b -> (a -> b) -> Maybe a -> b
maybe MixedAmount
nullmixedamt (Key, MixedAmount) -> MixedAmount
forall a b. (a, b) -> b
snd (Maybe (Key, MixedAmount) -> MixedAmount)
-> (IntMap MixedAmount -> Maybe (Key, MixedAmount))
-> IntMap MixedAmount
-> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IntMap MixedAmount -> Maybe (Key, MixedAmount)
forall a. IntMap a -> Maybe (Key, a)
IM.lookupMax
sortFlatByAmount :: [PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c]
sortFlatByAmount = 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 c
-> (Down MixedAmount, CommoditySymbol))
-> [PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\PeriodicReportRow DisplayName c
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 c -> MixedAmount
forall {a}. PeriodicReportRow a c -> MixedAmount
amt PeriodicReportRow DisplayName c
r, PeriodicReportRow DisplayName c -> CommoditySymbol
forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName PeriodicReportRow DisplayName c
r))
NormalSign
NormallyNegative -> (PeriodicReportRow DisplayName c -> (MixedAmount, CommoditySymbol))
-> [PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\PeriodicReportRow DisplayName c
r -> (PeriodicReportRow DisplayName c -> MixedAmount
forall {a}. PeriodicReportRow a c -> MixedAmount
amt PeriodicReportRow DisplayName c
r, PeriodicReportRow DisplayName c -> CommoditySymbol
forall a. PeriodicReportRow DisplayName a -> CommoditySymbol
prrFullName PeriodicReportRow DisplayName c
r))
where amt :: PeriodicReportRow a c -> MixedAmount
amt = MixedAmount -> MixedAmount
mixedAmountStripCosts (MixedAmount -> MixedAmount)
-> (PeriodicReportRow a c -> MixedAmount)
-> PeriodicReportRow a c
-> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. c -> MixedAmount
flatAmt (c -> MixedAmount)
-> (PeriodicReportRow a c -> c)
-> PeriodicReportRow a c
-> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeriodicReportRow a c -> c
forall a b. PeriodicReportRow a b -> b
prrTotal
buildReportRows :: forall b c.
(ReportOpts -> (BalanceData -> MixedAmount) -> DisplayName -> Account b -> PeriodicReportRow DisplayName c)
-> ReportOpts -> Account b -> [PeriodicReportRow DisplayName c]
buildReportRows :: forall b c.
(ReportOpts
-> (BalanceData -> MixedAmount)
-> DisplayName
-> Account b
-> PeriodicReportRow DisplayName c)
-> ReportOpts -> Account b -> [PeriodicReportRow DisplayName c]
buildReportRows ReportOpts
-> (BalanceData -> MixedAmount)
-> DisplayName
-> Account b
-> PeriodicReportRow DisplayName c
makeRow ReportOpts
ropts = Bool
-> Key -> Key -> Account b -> [PeriodicReportRow DisplayName c]
mkRows Bool
True (-ReportOpts -> Key
drop_ ReportOpts
ropts) Key
0
where
mkRows :: Bool -> Int -> Int -> Account b -> [PeriodicReportRow DisplayName c]
mkRows :: Bool
-> Key -> Key -> Account b -> [PeriodicReportRow DisplayName c]
mkRows Bool
isRoot Key
d Key
boringParents Account b
acct
| Account b -> Bool
forall a. Account a -> Bool
aboring Account b
acct Bool -> Bool -> Bool
&& Bool
isRoot = Key -> Key -> [PeriodicReportRow DisplayName c]
buildSubrows Key
d Key
0
| Account b -> Bool
forall a. Account a -> Bool
aboring Account b
acct Bool -> Bool -> Bool
&& Key
d Key -> Key -> Bool
forall a. Ord a => a -> a -> Bool
< Key
0 = Key -> Key -> [PeriodicReportRow DisplayName c]
buildSubrows (Key
d Key -> Key -> Key
forall a. Num a => a -> a -> a
+ Key
1) Key
0
| Account b -> Bool
forall a. Account a -> Bool
aboring Account b
acct Bool -> Bool -> Bool
&& Bool
canOmitParents = Key -> Key -> [PeriodicReportRow DisplayName c]
buildSubrows Key
d (Key
boringParents Key -> Key -> Key
forall a. Num a => a -> a -> a
+ Key
1)
| Bool
otherwise = ReportOpts
-> (BalanceData -> MixedAmount)
-> DisplayName
-> Account b
-> PeriodicReportRow DisplayName c
makeRow ReportOpts
ropts BalanceData -> MixedAmount
balance DisplayName
displayname Account b
acct PeriodicReportRow DisplayName c
-> [PeriodicReportRow DisplayName c]
-> [PeriodicReportRow DisplayName c]
forall a. a -> [a] -> [a]
: Key -> Key -> [PeriodicReportRow DisplayName c]
buildSubrows (Key
d Key -> Key -> Key
forall a. Num a => a -> a -> a
+ Key
1) Key
0
where
displayname :: DisplayName
displayname = Key -> Key -> CommoditySymbol -> DisplayName
displayedName Key
d Key
boringParents (CommoditySymbol -> DisplayName) -> CommoditySymbol -> DisplayName
forall a b. (a -> b) -> a -> b
$ Account b -> CommoditySymbol
forall a. Account a -> CommoditySymbol
aname Account b
acct
buildSubrows :: Key -> Key -> [PeriodicReportRow DisplayName c]
buildSubrows Key
i Key
b = (Account b -> [PeriodicReportRow DisplayName c])
-> [Account b] -> [PeriodicReportRow DisplayName c]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Bool
-> Key -> Key -> Account b -> [PeriodicReportRow DisplayName c]
mkRows Bool
False Key
i Key
b) ([Account b] -> [PeriodicReportRow DisplayName c])
-> [Account b] -> [PeriodicReportRow DisplayName c]
forall a b. (a -> b) -> a -> b
$ Account b -> [Account b]
forall a. Account a -> [Account a]
asubs Account b
acct
canOmitParents :: Bool
canOmitParents = ReportOpts -> Bool
flat_ ReportOpts
ropts Bool -> Bool -> Bool
|| Bool -> Bool
not (ReportOpts -> Bool
no_elide_ ReportOpts
ropts)
balance :: BalanceData -> MixedAmount
balance = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> BalanceData -> MixedAmount
bdincludingsubs
AccountListMode
ALFlat -> BalanceData -> MixedAmount
bdexcludingsubs
displayedName :: Key -> Key -> CommoditySymbol -> DisplayName
displayedName Key
d Key
boringParents CommoditySymbol
name
| Key
d Key -> Key -> Bool
forall a. Eq a => a -> a -> Bool
== Key
0 Bool -> Bool -> Bool
&& CommoditySymbol
name CommoditySymbol -> CommoditySymbol -> Bool
forall a. Eq a => a -> a -> Bool
== CommoditySymbol
"root" = CommoditySymbol -> CommoditySymbol -> Key -> DisplayName
DisplayName CommoditySymbol
"..." CommoditySymbol
"..." Key
0
| Bool
otherwise = case ReportOpts -> AccountListMode
accountlistmode_ ReportOpts
ropts of
AccountListMode
ALTree -> CommoditySymbol -> CommoditySymbol -> Key -> DisplayName
DisplayName CommoditySymbol
name CommoditySymbol
leaf (Key -> DisplayName) -> Key -> DisplayName
forall a b. (a -> b) -> a -> b
$ Key -> Key -> Key
forall a. Ord a => a -> a -> a
max Key
0 Key
d
AccountListMode
ALFlat -> CommoditySymbol -> CommoditySymbol -> Key -> DisplayName
DisplayName CommoditySymbol
name CommoditySymbol
droppedName Key
0
where
leaf :: CommoditySymbol
leaf = [CommoditySymbol] -> CommoditySymbol
accountNameFromComponents
([CommoditySymbol] -> CommoditySymbol)
-> ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol]
-> CommoditySymbol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CommoditySymbol] -> [CommoditySymbol]
forall a. [a] -> [a]
reverse ([CommoditySymbol] -> [CommoditySymbol])
-> ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol]
-> [CommoditySymbol]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Key -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Key -> [a] -> [a]
take (Key
boringParents Key -> Key -> Key
forall a. Num a => a -> a -> a
+ Key
1) ([CommoditySymbol] -> [CommoditySymbol])
-> ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol]
-> [CommoditySymbol]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [CommoditySymbol] -> [CommoditySymbol]
forall a. [a] -> [a]
reverse
([CommoditySymbol] -> CommoditySymbol)
-> [CommoditySymbol] -> CommoditySymbol
forall a b. (a -> b) -> a -> b
$ CommoditySymbol -> [CommoditySymbol]
accountNameComponents CommoditySymbol
droppedName
droppedName :: CommoditySymbol
droppedName = Key -> CommoditySymbol -> CommoditySymbol
accountNameDrop (ReportOpts -> Key
drop_ ReportOpts
ropts) CommoditySymbol
name
makeMultiBalanceReportRow :: ReportOpts -> (BalanceData -> MixedAmount)
-> a -> Account BalanceData -> PeriodicReportRow a MixedAmount
makeMultiBalanceReportRow :: forall a.
ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account BalanceData
-> PeriodicReportRow a MixedAmount
makeMultiBalanceReportRow = MixedAmount
-> (IntMap MixedAmount -> (MixedAmount, MixedAmount))
-> ReportOpts
-> (BalanceData -> MixedAmount)
-> a
-> Account BalanceData
-> PeriodicReportRow a MixedAmount
forall c b a.
c
-> (IntMap c -> (c, c))
-> ReportOpts
-> (b -> c)
-> a
-> Account b
-> PeriodicReportRow a c
makePeriodicReportRow MixedAmount
nullmixedamt IntMap MixedAmount -> (MixedAmount, MixedAmount)
forall (f :: * -> *).
Foldable f =>
f MixedAmount -> (MixedAmount, MixedAmount)
sumAndAverageMixedAmounts
makePeriodicReportRow :: c -> (IM.IntMap c -> (c, c))
-> ReportOpts -> (b -> c)
-> a -> Account b -> PeriodicReportRow a c
makePeriodicReportRow :: forall c b a.
c
-> (IntMap c -> (c, c))
-> ReportOpts
-> (b -> c)
-> a
-> Account b
-> PeriodicReportRow a c
makePeriodicReportRow c
nullEntry IntMap c -> (c, c)
totalAndAverage ReportOpts
ropts b -> c
balance a
name Account b
acct =
a -> [c] -> c -> c -> PeriodicReportRow a c
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow a
name (IntMap c -> [c]
forall a. IntMap a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList IntMap c
rowbals) c
rowtotal c
avg
where
rowbals :: IntMap c
rowbals = (b -> c) -> IntMap b -> IntMap c
forall a b. (a -> b) -> IntMap a -> IntMap b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap b -> c
balance (IntMap b -> IntMap c)
-> (PeriodData b -> IntMap b) -> PeriodData b -> IntMap c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeriodData b -> IntMap b
forall a. PeriodData a -> IntMap a
pdperiods (PeriodData b -> IntMap c) -> PeriodData b -> IntMap c
forall a b. (a -> b) -> a -> b
$ Account b -> PeriodData b
forall a. Account a -> PeriodData a
adata Account b
acct
(c
total, c
avg) = IntMap c -> (c, c)
totalAndAverage IntMap c
rowbals
rowtotal :: c
rowtotal = case ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts of
BalanceAccumulation
PerPeriod -> c
total
BalanceAccumulation
_ -> c -> ((Key, c) -> c) -> Maybe (Key, c) -> c
forall b a. b -> (a -> b) -> Maybe a -> b
maybe c
nullEntry (Key, c) -> c
forall a b. (a, b) -> b
snd (Maybe (Key, c) -> c) -> Maybe (Key, c) -> c
forall a b. (a -> b) -> a -> b
$ IntMap c -> Maybe (Key, c)
forall a. IntMap a -> Maybe (Key, a)
IM.lookupMax IntMap c
rowbals
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)
perdivide :: MixedAmount -> MixedAmount -> MixedAmount
perdivide :: MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
a MixedAmount
b = MixedAmount -> Maybe MixedAmount -> MixedAmount
forall a. a -> Maybe a -> a
fromMaybe (String -> MixedAmount
forall a. String -> a
error' String
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 -> CommoditySymbol
acommodity Amount
a' CommoditySymbol -> CommoditySymbol -> Bool
forall a. Eq a => a -> a -> Bool
== Amount -> CommoditySymbol
acommodity Amount
b'
MixedAmount -> Maybe MixedAmount
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (MixedAmount -> Maybe MixedAmount)
-> MixedAmount -> Maybe MixedAmount
forall a b. (a -> b) -> a -> b
$ [Amount] -> MixedAmount
forall (t :: * -> *). Foldable t => t 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 :: String
errmsg = String
"Cannot calculate percentages if accounts have different commodities (Hint: Try --cost, -V or similar flags.)"
cumulativeSum :: Traversable t => t BalanceData -> t BalanceData
cumulativeSum :: forall (t :: * -> *).
Traversable t =>
t BalanceData -> t BalanceData
cumulativeSum = (BalanceData, t BalanceData) -> t BalanceData
forall a b. (a, b) -> b
snd ((BalanceData, t BalanceData) -> t BalanceData)
-> (t BalanceData -> (BalanceData, t BalanceData))
-> t BalanceData
-> t BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (BalanceData -> BalanceData -> (BalanceData, BalanceData))
-> BalanceData -> t BalanceData -> (BalanceData, t BalanceData)
forall (t :: * -> *) s a b.
Traversable t =>
(s -> a -> (s, b)) -> s -> t a -> (s, t b)
mapAccumL (\BalanceData
prev BalanceData
new -> let z :: BalanceData
z = BalanceData
prev BalanceData -> BalanceData -> BalanceData
forall a. Semigroup a => a -> a -> a
<> BalanceData
new in (BalanceData
z, BalanceData
z)) BalanceData
forall a. Monoid a => a
mempty
periodChanges :: Traversable t => t BalanceData -> t BalanceData
periodChanges :: forall (t :: * -> *).
Traversable t =>
t BalanceData -> t BalanceData
periodChanges = (BalanceData, t BalanceData) -> t BalanceData
forall a b. (a, b) -> b
snd ((BalanceData, t BalanceData) -> t BalanceData)
-> (t BalanceData -> (BalanceData, t BalanceData))
-> t BalanceData
-> t BalanceData
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (BalanceData -> BalanceData -> (BalanceData, BalanceData))
-> BalanceData -> t BalanceData -> (BalanceData, t BalanceData)
forall (t :: * -> *) s a b.
Traversable t =>
(s -> a -> (s, b)) -> s -> t a -> (s, t b)
mapAccumL (\BalanceData
prev BalanceData
new -> (BalanceData
new, (MixedAmount -> MixedAmount -> MixedAmount)
-> BalanceData -> BalanceData -> BalanceData
opBalanceData MixedAmount -> MixedAmount -> MixedAmount
maMinus BalanceData
new BalanceData
prev)) BalanceData
forall a. Monoid a => a
mempty
tests_MultiBalanceReport :: TestTree
tests_MultiBalanceReport = String -> [TestTree] -> TestTree
testGroup String
"MultiBalanceReport" [
let
amt0 :: Amount
amt0 = Amount {acommodity :: CommoditySymbol
acommodity=CommoditySymbol
"$", aquantity :: Quantity
aquantity=Quantity
0, acost :: Maybe AmountCost
acost=Maybe AmountCost
forall a. Maybe a
Nothing,
astyle :: AmountStyle
astyle=AmountStyle {ascommodityside :: Side
ascommodityside = Side
L, ascommodityspaced :: Bool
ascommodityspaced = Bool
False, asdigitgroups :: Maybe DigitGroupStyle
asdigitgroups = Maybe DigitGroupStyle
forall a. Maybe a
Nothing,
asdecimalmark :: Maybe Char
asdecimalmark = Char -> Maybe Char
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=And [queryFromFlags $ _rsReportOpts rspec, _rsQuery 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, Key, [String], String,
String)
showw (PeriodicReportRow DisplayName
a [MixedAmount]
lAmt MixedAmount
amt MixedAmount
amt')
= (DisplayName -> CommoditySymbol
displayFull DisplayName
a, DisplayName -> CommoditySymbol
displayName DisplayName
a, DisplayName -> Key
displayIndent DisplayName
a, (MixedAmount -> String) -> [MixedAmount] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map MixedAmount -> String
showMixedAmountDebug [MixedAmount]
lAmt, MixedAmount -> String
showMixedAmountDebug MixedAmount
amt, MixedAmount -> String
showMixedAmountDebug MixedAmount
amt')
((PeriodicReportRow DisplayName MixedAmount
-> (CommoditySymbol, CommoditySymbol, Key, [String], String,
String))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [(CommoditySymbol, CommoditySymbol, Key, [String], String,
String)]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount
-> (CommoditySymbol, CommoditySymbol, Key, [String], String,
String)
showw [PeriodicReportRow DisplayName MixedAmount]
aitems) [(CommoditySymbol, CommoditySymbol, Key, [String], String, String)]
-> [(CommoditySymbol, CommoditySymbol, Key, [String], String,
String)]
-> IO ()
forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= ((PeriodicReportRow DisplayName MixedAmount
-> (CommoditySymbol, CommoditySymbol, Key, [String], String,
String))
-> [PeriodicReportRow DisplayName MixedAmount]
-> [(CommoditySymbol, CommoditySymbol, Key, [String], String,
String)]
forall a b. (a -> b) -> [a] -> [b]
map PeriodicReportRow DisplayName MixedAmount
-> (CommoditySymbol, CommoditySymbol, Key, [String], String,
String)
showw [PeriodicReportRow DisplayName MixedAmount]
eitems)
MixedAmount -> String
showMixedAmountDebug (PeriodicReportRow () MixedAmount -> MixedAmount
forall a b. PeriodicReportRow a b -> b
prrTotal PeriodicReportRow () MixedAmount
atotal) String -> String -> IO ()
forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= MixedAmount -> String
showMixedAmountDebug MixedAmount
etotal
in
String -> [TestTree] -> TestTree
testGroup String
"multiBalanceReport" [
String -> IO () -> TestTree
testCase String
"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` ([], MixedAmount
nullmixedamt)
,String -> IO () -> TestTree
testCase String
"with -H on a populated period" (IO () -> TestTree) -> IO () -> TestTree
forall a b. (a -> b) -> a -> b
$
(ReportSpec
defreportspec{_rsReportOpts=defreportopts{period_= PeriodBetween (fromGregorian 2008 1 1) (fromGregorian 2008 1 2), balanceaccum_=Historical}}, 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 (CommoditySymbol -> DisplayName
flatDisplayName CommoditySymbol
"assets:bank:checking") [Amount -> MixedAmount
mixedAmount (Amount -> MixedAmount) -> Amount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd Quantity
1] (Amount -> MixedAmount
mixedAmount (Amount -> MixedAmount) -> Amount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd Quantity
1) (Amount -> MixedAmount
mixedAmount Amount
amt0{aquantity=1})
, DisplayName
-> [MixedAmount]
-> MixedAmount
-> MixedAmount
-> PeriodicReportRow DisplayName MixedAmount
forall a b. a -> [b] -> b -> b -> PeriodicReportRow a b
PeriodicReportRow (CommoditySymbol -> DisplayName
flatDisplayName CommoditySymbol
"income:salary") [Amount -> MixedAmount
mixedAmount (Amount -> MixedAmount) -> Amount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd (-Quantity
1)] (Amount -> MixedAmount
mixedAmount (Amount -> MixedAmount) -> Amount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd (-Quantity
1)) (Amount -> MixedAmount
mixedAmount Amount
amt0{aquantity=(-1)})
],
Amount -> MixedAmount
mixedAmount (Amount -> MixedAmount) -> Amount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Quantity -> Amount
usd Quantity
0)
]
]