{-# LANGUAGE FlexibleInstances, RecordWildCards, ScopedTypeVariables, OverloadedStrings, DeriveGeneric #-}
module Hledger.Reports.MultiBalanceReport (
MultiBalanceReport(..),
MultiBalanceReportRow,
multiBalanceReport,
multiBalanceReportWith,
balanceReportFromMultiBalanceReport,
mbrNegate,
mbrNormaliseSign,
multiBalanceReportSpan,
tableAsText,
tests_MultiBalanceReport
)
where
import GHC.Generics (Generic)
import Control.DeepSeq (NFData)
import Data.List
import Data.Maybe
import Data.Ord
import Data.Time.Calendar
import Safe
import Text.Tabular as T
import Text.Tabular.AsciiWide
import Hledger.Data
import Hledger.Query
import Hledger.Utils
import Hledger.Read (mamountp')
import Hledger.Reports.ReportOptions
import Hledger.Reports.BalanceReport
newtype MultiBalanceReport =
MultiBalanceReport ([DateSpan]
,[MultiBalanceReportRow]
,MultiBalanceReportTotals
)
deriving ((forall x. MultiBalanceReport -> Rep MultiBalanceReport x)
-> (forall x. Rep MultiBalanceReport x -> MultiBalanceReport)
-> Generic MultiBalanceReport
forall x. Rep MultiBalanceReport x -> MultiBalanceReport
forall x. MultiBalanceReport -> Rep MultiBalanceReport x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep MultiBalanceReport x -> MultiBalanceReport
$cfrom :: forall x. MultiBalanceReport -> Rep MultiBalanceReport x
Generic)
type MultiBalanceReportRow = (AccountName, AccountName, Int, [MixedAmount], MixedAmount, MixedAmount)
type MultiBalanceReportTotals = ([MixedAmount], MixedAmount, MixedAmount)
instance NFData MultiBalanceReport
instance Show MultiBalanceReport where
show :: MultiBalanceReport -> String
show (MultiBalanceReport (spans :: [DateSpan]
spans, items :: [MultiBalanceReportRow]
items, totals :: MultiBalanceReportTotals
totals)) =
"MultiBalanceReport (ignore extra quotes):\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++ (String, [String], MultiBalanceReportTotals) -> String
forall a. Show a => a -> String
pshow ([DateSpan] -> String
forall a. Show a => a -> String
show [DateSpan]
spans, (MultiBalanceReportRow -> String)
-> [MultiBalanceReportRow] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map MultiBalanceReportRow -> String
forall a. Show a => a -> String
show [MultiBalanceReportRow]
items, MultiBalanceReportTotals
totals)
type ClippedAccountName = AccountName
multiBalanceReport :: ReportOpts -> Query -> Journal -> MultiBalanceReport
multiBalanceReport :: ReportOpts -> Query -> Journal -> MultiBalanceReport
multiBalanceReport ropts :: ReportOpts
ropts q :: Query
q j :: Journal
j = ReportOpts -> Query -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith ReportOpts
ropts Query
q Journal
j (Journal -> PriceOracle
journalPriceOracle Journal
j)
multiBalanceReportWith :: ReportOpts -> Query -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith :: ReportOpts -> Query -> Journal -> PriceOracle -> MultiBalanceReport
multiBalanceReportWith ropts :: ReportOpts
ropts@ReportOpts{..} q :: Query
q j :: Journal
j@Journal{..} priceoracle :: PriceOracle
priceoracle =
(if Bool
invert_ then MultiBalanceReport -> MultiBalanceReport
mbrNegate else MultiBalanceReport -> MultiBalanceReport
forall a. a -> a
id) (MultiBalanceReport -> MultiBalanceReport)
-> MultiBalanceReport -> MultiBalanceReport
forall a b. (a -> b) -> a -> b
$
([DateSpan], [MultiBalanceReportRow], MultiBalanceReportTotals)
-> MultiBalanceReport
MultiBalanceReport ([DateSpan]
colspans, [MultiBalanceReportRow]
mappedsortedrows, MultiBalanceReportTotals
mappedtotalsrow)
where
dbg1 :: String -> a -> a
dbg1 s :: String
s = let p :: String
p = "multiBalanceReport" in String -> a -> a
forall a. Show a => String -> a -> a
Hledger.Utils.dbg1 (String
pString -> ShowS
forall a. [a] -> [a] -> [a]
++" "String -> ShowS
forall a. [a] -> [a] -> [a]
++String
s)
symq :: Query
symq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "symq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsSym (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "requested q" Query
q
depthq :: Query
depthq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "depthq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ (Query -> Bool) -> Query -> Query
filterQuery Query -> Bool
queryIsDepth Query
q
depth :: Int
depth = Query -> Int
queryDepth Query
depthq
depthless :: Query -> Query
depthless = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "depthless" (Query -> Query) -> (Query -> Query) -> Query -> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query -> Bool) -> Query -> Query
filterQuery (Bool -> Bool
not (Bool -> Bool) -> (Query -> Bool) -> Query -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query -> Bool
queryIsDepth)
datelessq :: Query
datelessq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "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
q
dateqcons :: DateSpan -> Query
dateqcons = if Bool
date2_ then DateSpan -> Query
Date2 else DateSpan -> Query
Date
requestedspan :: DateSpan
requestedspan = String -> DateSpan -> DateSpan
forall a. Show a => String -> a -> a
dbg1 "requestedspan" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ Bool -> Query -> DateSpan
queryDateSpan Bool
date2_ Query
q
requestedspan' :: DateSpan
requestedspan' = String -> DateSpan -> DateSpan
forall a. Show a => String -> a -> a
dbg1 "requestedspan'" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ DateSpan
requestedspan DateSpan -> DateSpan -> DateSpan
`spanDefaultsFrom` Bool -> Journal -> DateSpan
journalDateSpan Bool
date2_ Journal
j
intervalspans :: [DateSpan]
intervalspans = String -> [DateSpan] -> [DateSpan]
forall a. Show a => String -> a -> a
dbg1 "intervalspans" ([DateSpan] -> [DateSpan]) -> [DateSpan] -> [DateSpan]
forall a b. (a -> b) -> a -> b
$ Interval -> DateSpan -> [DateSpan]
splitSpan Interval
interval_ DateSpan
requestedspan'
reportspan :: DateSpan
reportspan = String -> DateSpan -> DateSpan
forall a. Show a => String -> a -> a
dbg1 "reportspan" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ Maybe Day -> Maybe Day -> DateSpan
DateSpan (Maybe Day -> (DateSpan -> Maybe Day) -> Maybe DateSpan -> Maybe Day
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Maybe Day
forall a. Maybe a
Nothing DateSpan -> Maybe Day
spanStart (Maybe DateSpan -> Maybe Day) -> Maybe DateSpan -> Maybe Day
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> Maybe DateSpan
forall a. [a] -> Maybe a
headMay [DateSpan]
intervalspans)
(Maybe Day -> (DateSpan -> Maybe Day) -> Maybe DateSpan -> Maybe Day
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Maybe Day
forall a. Maybe a
Nothing DateSpan -> Maybe Day
spanEnd (Maybe DateSpan -> Maybe Day) -> Maybe DateSpan -> Maybe Day
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> Maybe DateSpan
forall a. [a] -> Maybe a
lastMay [DateSpan]
intervalspans)
mreportstart :: Maybe Day
mreportstart = DateSpan -> Maybe Day
spanStart DateSpan
reportspan
reportq :: Query
reportq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "reportq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ Query -> Query
depthless (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$
if DateSpan
reportspan DateSpan -> DateSpan -> Bool
forall a. Eq a => a -> a -> Bool
== DateSpan
nulldatespan
then Query
q
else [Query] -> Query
And [Query
datelessq, Query
reportspandatesq]
where
reportspandatesq :: Query
reportspandatesq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "reportspandatesq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ DateSpan -> Query
dateqcons DateSpan
reportspan
[DateSpan]
colspans :: [DateSpan] = String -> [DateSpan] -> [DateSpan]
forall a. Show a => String -> a -> a
dbg1 "colspans" ([DateSpan] -> [DateSpan]) -> [DateSpan] -> [DateSpan]
forall a b. (a -> b) -> a -> b
$ Interval -> DateSpan -> [DateSpan]
splitSpan Interval
interval_ DateSpan
displayspan
where
displayspan :: DateSpan
displayspan
| Bool
empty_ = String -> DateSpan -> DateSpan
forall a. Show a => String -> a -> a
dbg1 "displayspan (-E)" DateSpan
reportspan
| Bool
otherwise = String -> DateSpan -> DateSpan
forall a. Show a => String -> a -> a
dbg1 "displayspan" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ DateSpan
requestedspan DateSpan -> DateSpan -> DateSpan
`spanIntersect` DateSpan
matchedspan
matchedspan :: DateSpan
matchedspan = String -> DateSpan -> DateSpan
forall a. Show a => String -> a -> a
dbg1 "matchedspan" (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ WhichDate -> [Posting] -> DateSpan
postingsDateSpan' (ReportOpts -> WhichDate
whichDateFromOpts ReportOpts
ropts) [Posting]
ps
j' :: Journal
j' = ReportOpts -> Journal -> Journal
journalSelectingAmountFromOpts ReportOpts
ropts Journal
j
[(Text, MixedAmount)]
startbals :: [(AccountName, MixedAmount)] = String -> [(Text, MixedAmount)] -> [(Text, MixedAmount)]
forall a. Show a => String -> a -> a
dbg1 "startbals" ([(Text, MixedAmount)] -> [(Text, MixedAmount)])
-> [(Text, MixedAmount)] -> [(Text, MixedAmount)]
forall a b. (a -> b) -> a -> b
$ ((Text, Text, Int, MixedAmount) -> (Text, MixedAmount))
-> [(Text, Text, Int, MixedAmount)] -> [(Text, MixedAmount)]
forall a b. (a -> b) -> [a] -> [b]
map (\(a :: Text
a,_,_,b :: MixedAmount
b) -> (Text
a,MixedAmount
b)) [(Text, Text, Int, MixedAmount)]
startbalanceitems
where
(startbalanceitems :: [(Text, Text, Int, MixedAmount)]
startbalanceitems,_) = String
-> ([(Text, Text, Int, MixedAmount)], MixedAmount)
-> ([(Text, Text, Int, MixedAmount)], MixedAmount)
forall a. Show a => String -> a -> a
dbg1 "starting balance report" (([(Text, Text, Int, MixedAmount)], MixedAmount)
-> ([(Text, Text, Int, MixedAmount)], MixedAmount))
-> ([(Text, Text, Int, MixedAmount)], MixedAmount)
-> ([(Text, Text, Int, MixedAmount)], MixedAmount)
forall a b. (a -> b) -> a -> b
$ ReportOpts
-> Query
-> Journal
-> ([(Text, Text, Int, MixedAmount)], MixedAmount)
balanceReport ReportOpts
ropts''{value_ :: Maybe ValuationType
value_=Maybe ValuationType
forall a. Maybe a
Nothing, percent_ :: Bool
percent_=Bool
False} Query
startbalq Journal
j'
where
ropts' :: ReportOpts
ropts' | ReportOpts -> Bool
tree_ ReportOpts
ropts = ReportOpts
ropts{no_elide_ :: Bool
no_elide_=Bool
True}
| Bool
otherwise = ReportOpts
ropts{accountlistmode_ :: AccountListMode
accountlistmode_=AccountListMode
ALFlat}
ropts'' :: ReportOpts
ropts'' = ReportOpts
ropts'{period_ :: Period
period_ = Period
precedingperiod}
where
precedingperiod :: Period
precedingperiod = DateSpan -> Period
dateSpanAsPeriod (DateSpan -> Period) -> DateSpan -> Period
forall a b. (a -> b) -> a -> b
$ DateSpan -> DateSpan -> DateSpan
spanIntersect (Maybe Day -> Maybe Day -> DateSpan
DateSpan Maybe Day
forall a. Maybe a
Nothing Maybe Day
mreportstart) (DateSpan -> DateSpan) -> DateSpan -> DateSpan
forall a b. (a -> b) -> a -> b
$ Period -> DateSpan
periodAsDateSpan Period
period_
startbalq :: Query
startbalq = String -> Query -> Query
forall a. Show a => String -> a -> a
dbg1 "startbalq" (Query -> Query) -> Query -> Query
forall a b. (a -> b) -> a -> b
$ [Query] -> Query
And [Query
datelessq, DateSpan -> Query
dateqcons DateSpan
precedingspan]
where
precedingspan :: DateSpan
precedingspan = case Maybe Day
mreportstart of
Just d :: Day
d -> Maybe Day -> Maybe Day -> DateSpan
DateSpan Maybe Day
forall a. Maybe a
Nothing (Day -> Maybe Day
forall a. a -> Maybe a
Just Day
d)
Nothing -> DateSpan
emptydatespan
startaccts :: [Text]
startaccts = String -> [Text] -> [Text]
forall a. Show a => String -> a -> a
dbg1 "startaccts" ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ ((Text, MixedAmount) -> Text) -> [(Text, MixedAmount)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text, MixedAmount) -> Text
forall a b. (a, b) -> a
fst [(Text, MixedAmount)]
startbals
startingBalanceFor :: Text -> MixedAmount
startingBalanceFor a :: Text
a = MixedAmount -> Maybe MixedAmount -> MixedAmount
forall a. a -> Maybe a -> a
fromMaybe MixedAmount
nullmixedamt (Maybe MixedAmount -> MixedAmount)
-> Maybe MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, MixedAmount)] -> Maybe MixedAmount
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
a [(Text, MixedAmount)]
startbals
[Posting]
ps :: [Posting] =
String -> [Posting] -> [Posting]
forall a. Show a => String -> a -> a
dbg1 "ps" ([Posting] -> [Posting]) -> [Posting] -> [Posting]
forall a b. (a -> b) -> a -> b
$
Journal -> [Posting]
journalPostings (Journal -> [Posting]) -> Journal -> [Posting]
forall a b. (a -> b) -> a -> b
$
Query -> Journal -> Journal
filterJournalAmounts Query
symq (Journal -> Journal) -> Journal -> Journal
forall a b. (a -> b) -> a -> b
$
Query -> Journal -> Journal
filterJournalPostings Query
reportq (Journal -> Journal) -> Journal -> Journal
forall a b. (a -> b) -> a -> b
$
Journal
j'
[([Posting], Maybe Day)]
colps :: [([Posting], Maybe Day)] =
String -> [([Posting], Maybe Day)] -> [([Posting], Maybe Day)]
forall a. Show a => String -> a -> a
dbg1 "colps"
[((Posting -> Bool) -> [Posting] -> [Posting]
forall a. (a -> Bool) -> [a] -> [a]
filter (WhichDate -> DateSpan -> Posting -> Bool
isPostingInDateSpan' (ReportOpts -> WhichDate
whichDateFromOpts ReportOpts
ropts) DateSpan
s) [Posting]
ps, DateSpan -> Maybe Day
spanEnd DateSpan
s) | DateSpan
s <- [DateSpan]
colspans]
acctChangesFromPostings :: [Posting] -> [(ClippedAccountName, MixedAmount)]
acctChangesFromPostings :: [Posting] -> [(Text, MixedAmount)]
acctChangesFromPostings ps :: [Posting]
ps = [(Account -> Text
aname Account
a, (if ReportOpts -> Bool
tree_ ReportOpts
ropts then Account -> MixedAmount
aibalance else Account -> MixedAmount
aebalance) Account
a) | Account
a <- [Account]
as]
where
as :: [Account]
as = [Account] -> [Account]
depthLimit ([Account] -> [Account]) -> [Account] -> [Account]
forall a b. (a -> b) -> a -> b
$
(if ReportOpts -> Bool
tree_ ReportOpts
ropts then [Account] -> [Account]
forall a. a -> a
id else (Account -> Bool) -> [Account] -> [Account]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>0)(Int -> Bool) -> (Account -> Int) -> Account -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Account -> Int
anumpostings)) ([Account] -> [Account]) -> [Account] -> [Account]
forall a b. (a -> b) -> a -> b
$
Int -> [Account] -> [Account]
forall a. Int -> [a] -> [a]
drop 1 ([Account] -> [Account]) -> [Account] -> [Account]
forall a b. (a -> b) -> a -> b
$ [Posting] -> [Account]
accountsFromPostings [Posting]
ps
depthLimit :: [Account] -> [Account]
depthLimit
| ReportOpts -> Bool
tree_ ReportOpts
ropts = (Account -> Bool) -> [Account] -> [Account]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Query
depthq Query -> Text -> Bool
`matchesAccount`)(Text -> Bool) -> (Account -> Text) -> Account -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.Account -> Text
aname)
| Bool
otherwise = Int -> [Account] -> [Account]
clipAccountsAndAggregate Int
depth
[[(Text, MixedAmount)]]
colacctchanges :: [[(ClippedAccountName, MixedAmount)]] =
String -> [[(Text, MixedAmount)]] -> [[(Text, MixedAmount)]]
forall a. Show a => String -> a -> a
dbg1 "colacctchanges" ([[(Text, MixedAmount)]] -> [[(Text, MixedAmount)]])
-> [[(Text, MixedAmount)]] -> [[(Text, MixedAmount)]]
forall a b. (a -> b) -> a -> b
$ (([Posting], Maybe Day) -> [(Text, MixedAmount)])
-> [([Posting], Maybe Day)] -> [[(Text, MixedAmount)]]
forall a b. (a -> b) -> [a] -> [b]
map ([Posting] -> [(Text, MixedAmount)]
acctChangesFromPostings ([Posting] -> [(Text, MixedAmount)])
-> (([Posting], Maybe Day) -> [Posting])
-> ([Posting], Maybe Day)
-> [(Text, MixedAmount)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Posting], Maybe Day) -> [Posting]
forall a b. (a, b) -> a
fst) [([Posting], Maybe Day)]
colps
[Text]
displayaccts :: [ClippedAccountName] =
String -> [Text] -> [Text]
forall a. Show a => String -> a -> a
dbg1 "displayaccts" ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$
(if ReportOpts -> Bool
tree_ ReportOpts
ropts then [Text] -> [Text]
expandAccountNames else [Text] -> [Text]
forall a. a -> a
id) ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$
[Text] -> [Text]
forall a. Eq a => [a] -> [a]
nub ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ (Text -> Text) -> [Text] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Text -> Text
clipOrEllipsifyAccountName Int
depth) ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$
if Bool
empty_ Bool -> Bool -> Bool
|| BalanceType
balancetype_ BalanceType -> BalanceType -> Bool
forall a. Eq a => a -> a -> Bool
== BalanceType
HistoricalBalance
then [Text] -> [Text]
forall a. Eq a => [a] -> [a]
nub ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Text] -> [Text]
forall a. Ord a => [a] -> [a]
sort ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Text]
startaccts [Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
++ [Text]
allpostedaccts
else [Text]
allpostedaccts
where
[Text]
allpostedaccts :: [AccountName] = String -> [Text] -> [Text]
forall a. Show a => String -> a -> a
dbg1 "allpostedaccts" ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Text] -> [Text]
forall a. Ord a => [a] -> [a]
sort ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ [Posting] -> [Text]
accountNamesFromPostings [Posting]
ps
[[(Text, MixedAmount)]]
colallacctchanges :: [[(ClippedAccountName, MixedAmount)]] =
String -> [[(Text, MixedAmount)]] -> [[(Text, MixedAmount)]]
forall a. Show a => String -> a -> a
dbg1 "colallacctchanges"
[((Text, MixedAmount) -> (Text, MixedAmount) -> Ordering)
-> [(Text, MixedAmount)] -> [(Text, MixedAmount)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((Text, MixedAmount) -> Text)
-> (Text, MixedAmount) -> (Text, MixedAmount) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (Text, MixedAmount) -> Text
forall a b. (a, b) -> a
fst) ([(Text, MixedAmount)] -> [(Text, MixedAmount)])
-> [(Text, MixedAmount)] -> [(Text, MixedAmount)]
forall a b. (a -> b) -> a -> b
$
((Text, MixedAmount) -> (Text, MixedAmount) -> Bool)
-> [(Text, MixedAmount)]
-> [(Text, MixedAmount)]
-> [(Text, MixedAmount)]
forall a. (a -> a -> Bool) -> [a] -> [a] -> [a]
unionBy (\(a :: Text
a,_) (a' :: Text
a',_) -> Text
a Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
a') [(Text, MixedAmount)]
postedacctchanges [(Text, MixedAmount)]
zeroes
| [(Text, MixedAmount)]
postedacctchanges <- [[(Text, MixedAmount)]]
colacctchanges]
where zeroes :: [(Text, MixedAmount)]
zeroes = [(Text
a, MixedAmount
nullmixedamt) | Text
a <- [Text]
displayaccts]
[(Text, [MixedAmount])]
acctchanges :: [(ClippedAccountName, [MixedAmount])] =
String -> [(Text, [MixedAmount])] -> [(Text, [MixedAmount])]
forall a. Show a => String -> a -> a
dbg1 "acctchanges"
[(Text
a, ((Text, MixedAmount) -> MixedAmount)
-> [(Text, MixedAmount)] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map (Text, MixedAmount) -> MixedAmount
forall a b. (a, b) -> b
snd [(Text, MixedAmount)]
abs) | abs :: [(Text, MixedAmount)]
abs@((a :: Text
a,_):_) <- [[(Text, MixedAmount)]] -> [[(Text, MixedAmount)]]
forall a. [[a]] -> [[a]]
transpose [[(Text, MixedAmount)]]
colallacctchanges]
[MultiBalanceReportRow]
rows :: [MultiBalanceReportRow] =
String -> [MultiBalanceReportRow] -> [MultiBalanceReportRow]
forall a. Show a => String -> a -> a
dbg1 "rows" ([MultiBalanceReportRow] -> [MultiBalanceReportRow])
-> [MultiBalanceReportRow] -> [MultiBalanceReportRow]
forall a b. (a -> b) -> a -> b
$
[(Text
a, Text -> Text
accountLeafName Text
a, Text -> Int
accountNameLevel Text
a, [MixedAmount]
valuedrowbals, MixedAmount
rowtot, MixedAmount
rowavg)
| (a :: Text
a,changes :: [MixedAmount]
changes) <- String -> [(Text, [MixedAmount])] -> [(Text, [MixedAmount])]
forall a. Show a => String -> a -> a
dbg1 "acctchanges" [(Text, [MixedAmount])]
acctchanges
, let rowbals :: [MixedAmount]
rowbals = String -> [MixedAmount] -> [MixedAmount]
forall a. Show a => String -> a -> a
dbg1 "rowbals" ([MixedAmount] -> [MixedAmount]) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ case BalanceType
balancetype_ of
PeriodChange -> [MixedAmount]
changes
CumulativeChange -> Int -> [MixedAmount] -> [MixedAmount]
forall a. Int -> [a] -> [a]
drop 1 ([MixedAmount] -> [MixedAmount]) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ (MixedAmount -> MixedAmount -> MixedAmount)
-> MixedAmount -> [MixedAmount] -> [MixedAmount]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl MixedAmount -> MixedAmount -> MixedAmount
forall a. Num a => a -> a -> a
(+) 0 [MixedAmount]
changes
HistoricalBalance -> Int -> [MixedAmount] -> [MixedAmount]
forall a. Int -> [a] -> [a]
drop 1 ([MixedAmount] -> [MixedAmount]) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ (MixedAmount -> MixedAmount -> MixedAmount)
-> MixedAmount -> [MixedAmount] -> [MixedAmount]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl MixedAmount -> MixedAmount -> MixedAmount
forall a. Num a => a -> a -> a
(+) (Text -> MixedAmount
startingBalanceFor Text
a) [MixedAmount]
changes
, let valuedrowbals :: [MixedAmount]
valuedrowbals = String -> [MixedAmount] -> [MixedAmount]
forall a. Show a => String -> a -> a
dbg1 "valuedrowbals" ([MixedAmount] -> [MixedAmount]) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ [Day -> MixedAmount -> MixedAmount
avalue Day
periodlastday MixedAmount
amt | (amt :: MixedAmount
amt,periodlastday :: Day
periodlastday) <- [MixedAmount] -> [Day] -> [(MixedAmount, Day)]
forall a b. [a] -> [b] -> [(a, b)]
zip [MixedAmount]
rowbals [Day]
lastdays]
, let rowtot :: MixedAmount
rowtot = if BalanceType
balancetype_BalanceType -> BalanceType -> Bool
forall a. Eq a => a -> a -> Bool
==BalanceType
PeriodChange then [MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [MixedAmount]
valuedrowbals else 0
, let rowavg :: MixedAmount
rowavg = [MixedAmount] -> MixedAmount
averageMixedAmounts [MixedAmount]
valuedrowbals
, Bool
empty_ Bool -> Bool -> Bool
|| Int
depth Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 0 Bool -> Bool -> Bool
|| (MixedAmount -> Bool) -> [MixedAmount] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Bool -> Bool
not (Bool -> Bool) -> (MixedAmount -> Bool) -> MixedAmount -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MixedAmount -> Bool
isZeroMixedAmount) [MixedAmount]
valuedrowbals
]
where
avalue :: Day -> MixedAmount -> MixedAmount
avalue periodlast :: Day
periodlast =
(MixedAmount -> MixedAmount)
-> (ValuationType -> MixedAmount -> MixedAmount)
-> Maybe ValuationType
-> MixedAmount
-> MixedAmount
forall b a. b -> (a -> b) -> Maybe a -> b
maybe MixedAmount -> MixedAmount
forall a. a -> a
id (PriceOracle
-> Map Text AmountStyle
-> Day
-> Maybe Day
-> Day
-> Bool
-> ValuationType
-> MixedAmount
-> MixedAmount
mixedAmountApplyValuation PriceOracle
priceoracle Map Text AmountStyle
styles Day
periodlast Maybe Day
mreportlast Day
today Bool
multiperiod) Maybe ValuationType
value_
where
styles :: Map Text AmountStyle
styles = Journal -> Map Text AmountStyle
journalCommodityStyles Journal
j
mreportlast :: Maybe Day
mreportlast = ReportOpts -> Maybe Day
reportPeriodLastDay ReportOpts
ropts
today :: Day
today = Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe (String -> Day
forall a. String -> a
error' "multiBalanceReport: could not pick a valuation date, ReportOpts today_ is unset") Maybe Day
today_
multiperiod :: Bool
multiperiod = Interval
interval_ Interval -> Interval -> Bool
forall a. Eq a => a -> a -> Bool
/= Interval
NoInterval
lastdays :: [Day]
lastdays =
(DateSpan -> Day) -> [DateSpan] -> [Day]
forall a b. (a -> b) -> [a] -> [b]
map ((Day -> (Day -> Day) -> Maybe Day -> Day
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
(String -> Day
forall a. String -> a
error' "multiBalanceReport: expected all spans to have an end date")
(Year -> Day -> Day
addDays (-1)))
(Maybe Day -> Day) -> (DateSpan -> Maybe Day) -> DateSpan -> Day
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DateSpan -> Maybe Day
spanEnd) [DateSpan]
colspans
[MultiBalanceReportRow]
sortedrows :: [MultiBalanceReportRow] =
String -> [MultiBalanceReportRow] -> [MultiBalanceReportRow]
forall a. Show a => String -> a -> a
dbg1 "sortedrows" ([MultiBalanceReportRow] -> [MultiBalanceReportRow])
-> [MultiBalanceReportRow] -> [MultiBalanceReportRow]
forall a b. (a -> b) -> a -> b
$
[MultiBalanceReportRow] -> [MultiBalanceReportRow]
forall b c d f.
[(Text, b, c, d, MixedAmount, f)]
-> [(Text, b, c, d, MixedAmount, f)]
sortrows [MultiBalanceReportRow]
rows
where
sortrows :: [(Text, b, c, d, MixedAmount, f)]
-> [(Text, b, c, d, MixedAmount, f)]
sortrows
| Bool
sort_amount_ Bool -> Bool -> Bool
&& AccountListMode
accountlistmode_ AccountListMode -> AccountListMode -> Bool
forall a. Eq a => a -> a -> Bool
== AccountListMode
ALTree = [(Text, b, c, d, MixedAmount, f)]
-> [(Text, b, c, d, MixedAmount, f)]
forall b c d f.
[(Text, b, c, d, MixedAmount, f)]
-> [(Text, b, c, d, MixedAmount, f)]
sortTreeMBRByAmount
| Bool
sort_amount_ = [(Text, b, c, d, MixedAmount, f)]
-> [(Text, b, c, d, MixedAmount, f)]
forall a b c d f.
[(a, b, c, d, MixedAmount, f)] -> [(a, b, c, d, MixedAmount, f)]
sortFlatMBRByAmount
| Bool
otherwise = [(Text, b, c, d, MixedAmount, f)]
-> [(Text, b, c, d, MixedAmount, f)]
forall b c d e f.
[(Text, b, c, d, e, f)] -> [(Text, b, c, d, e, f)]
sortMBRByAccountDeclaration
where
sortTreeMBRByAmount :: [(Text, b, c, d, MixedAmount, f)]
-> [(Text, b, c, d, MixedAmount, f)]
sortTreeMBRByAmount rows :: [(Text, b, c, d, MixedAmount, f)]
rows = [(Text, b, c, d, MixedAmount, f)]
sortedrows
where
anamesandrows :: [(Text, (Text, b, c, d, MixedAmount, f))]
anamesandrows = [((Text, b, c, d, MixedAmount, f) -> Text
forall a b c d e f. (a, b, c, d, e, f) -> a
first6 (Text, b, c, d, MixedAmount, f)
r, (Text, b, c, d, MixedAmount, f)
r) | (Text, b, c, d, MixedAmount, f)
r <- [(Text, b, c, d, MixedAmount, f)]
rows]
anames :: [Text]
anames = ((Text, (Text, b, c, d, MixedAmount, f)) -> Text)
-> [(Text, (Text, b, c, d, MixedAmount, f))] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text, (Text, b, c, d, MixedAmount, f)) -> Text
forall a b. (a, b) -> a
fst [(Text, (Text, b, c, d, MixedAmount, f))]
anamesandrows
atotals :: [(Text, MixedAmount)]
atotals = [(Text
a,MixedAmount
tot) | (a :: Text
a,_,_,_,tot :: MixedAmount
tot,_) <- [(Text, b, c, d, MixedAmount, f)]
rows]
accounttree :: Account
accounttree = Text -> [Text] -> Account
accountTree "root" [Text]
anames
accounttreewithbals :: Account
accounttreewithbals = (Account -> Account) -> Account -> Account
mapAccounts Account -> Account
setibalance Account
accounttree
where
setibalance :: Account -> Account
setibalance a :: Account
a = Account
a{aibalance :: MixedAmount
aibalance=MixedAmount -> Maybe MixedAmount -> MixedAmount
forall a. a -> Maybe a -> a
fromMaybe (String -> MixedAmount
forall a. HasCallStack => String -> a
error "sortTreeMBRByAmount 1") (Maybe MixedAmount -> MixedAmount)
-> Maybe MixedAmount -> MixedAmount
forall a b. (a -> b) -> a -> b
$ Text -> [(Text, MixedAmount)] -> Maybe MixedAmount
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup (Account -> Text
aname Account
a) [(Text, MixedAmount)]
atotals}
sortedaccounttree :: Account
sortedaccounttree = NormalSign -> Account -> Account
sortAccountTreeByAmount (NormalSign -> Maybe NormalSign -> NormalSign
forall a. a -> Maybe a -> a
fromMaybe NormalSign
NormallyPositive Maybe NormalSign
normalbalance_) Account
accounttreewithbals
sortedanames :: [Text]
sortedanames = (Account -> Text) -> [Account] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Account -> Text
aname ([Account] -> [Text]) -> [Account] -> [Text]
forall a b. (a -> b) -> a -> b
$ Int -> [Account] -> [Account]
forall a. Int -> [a] -> [a]
drop 1 ([Account] -> [Account]) -> [Account] -> [Account]
forall a b. (a -> b) -> a -> b
$ Account -> [Account]
flattenAccounts Account
sortedaccounttree
sortedrows :: [(Text, b, c, d, MixedAmount, f)]
sortedrows = [Text]
-> [(Text, (Text, b, c, d, MixedAmount, f))]
-> [(Text, b, c, d, MixedAmount, f)]
forall b. [Text] -> [(Text, b)] -> [b]
sortAccountItemsLike [Text]
sortedanames [(Text, (Text, b, c, d, MixedAmount, f))]
anamesandrows
sortFlatMBRByAmount :: [(a, b, c, d, MixedAmount, f)] -> [(a, b, c, d, MixedAmount, f)]
sortFlatMBRByAmount = ((a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f) -> Ordering)
-> [(a, b, c, d, MixedAmount, f)] -> [(a, b, c, d, MixedAmount, f)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f) -> Ordering)
-> (a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f)
-> Ordering
forall a c. (a -> a -> c) -> a -> a -> c
maybeflip (((a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f) -> Ordering)
-> (a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f)
-> Ordering)
-> ((a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f) -> Ordering)
-> (a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f)
-> Ordering
forall a b. (a -> b) -> a -> b
$ ((a, b, c, d, MixedAmount, f) -> MixedAmount)
-> (a, b, c, d, MixedAmount, f)
-> (a, b, c, d, MixedAmount, f)
-> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (MixedAmount -> MixedAmount
normaliseMixedAmountSquashPricesForDisplay (MixedAmount -> MixedAmount)
-> ((a, b, c, d, MixedAmount, f) -> MixedAmount)
-> (a, b, c, d, MixedAmount, f)
-> MixedAmount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, b, c, d, MixedAmount, f) -> MixedAmount
forall a b c d e f. (a, b, c, d, e, f) -> e
fifth6))
where
maybeflip :: (a -> a -> c) -> a -> a -> c
maybeflip = if Maybe NormalSign
normalbalance_ Maybe NormalSign -> Maybe NormalSign -> Bool
forall a. Eq a => a -> a -> Bool
== NormalSign -> Maybe NormalSign
forall a. a -> Maybe a
Just NormalSign
NormallyNegative then (a -> a -> c) -> a -> a -> c
forall a. a -> a
id else (a -> a -> c) -> a -> a -> c
forall a b c. (a -> b -> c) -> b -> a -> c
flip
sortMBRByAccountDeclaration :: [(Text, b, c, d, e, f)] -> [(Text, b, c, d, e, f)]
sortMBRByAccountDeclaration rows :: [(Text, b, c, d, e, f)]
rows = [(Text, b, c, d, e, f)]
sortedrows
where
anamesandrows :: [(Text, (Text, b, c, d, e, f))]
anamesandrows = [((Text, b, c, d, e, f) -> Text
forall a b c d e f. (a, b, c, d, e, f) -> a
first6 (Text, b, c, d, e, f)
r, (Text, b, c, d, e, f)
r) | (Text, b, c, d, e, f)
r <- [(Text, b, c, d, e, f)]
rows]
anames :: [Text]
anames = ((Text, (Text, b, c, d, e, f)) -> Text)
-> [(Text, (Text, b, c, d, e, f))] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text, (Text, b, c, d, e, f)) -> Text
forall a b. (a, b) -> a
fst [(Text, (Text, b, c, d, e, f))]
anamesandrows
sortedanames :: [Text]
sortedanames = Journal -> Bool -> [Text] -> [Text]
sortAccountNamesByDeclaration Journal
j (ReportOpts -> Bool
tree_ ReportOpts
ropts) [Text]
anames
sortedrows :: [(Text, b, c, d, e, f)]
sortedrows = [Text]
-> [(Text, (Text, b, c, d, e, f))] -> [(Text, b, c, d, e, f)]
forall b. [Text] -> [(Text, b)] -> [b]
sortAccountItemsLike [Text]
sortedanames [(Text, (Text, b, c, d, e, f))]
anamesandrows
highestlevelaccts :: [Text]
highestlevelaccts = [Text
a | Text
a <- [Text]
displayaccts, Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
displayaccts) ([Text] -> Bool) -> [Text] -> Bool
forall a b. (a -> b) -> a -> b
$ [Text] -> [Text]
forall a. [a] -> [a]
init ([Text] -> [Text]) -> [Text] -> [Text]
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
expandAccountName Text
a]
colamts :: [[MixedAmount]]
colamts = [[MixedAmount]] -> [[MixedAmount]]
forall a. [[a]] -> [[a]]
transpose [[MixedAmount]
bs | (a :: Text
a,_,_,bs :: [MixedAmount]
bs,_,_) <- [MultiBalanceReportRow]
rows, Bool -> Bool
not (ReportOpts -> Bool
tree_ ReportOpts
ropts) Bool -> Bool -> Bool
|| Text
a Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
highestlevelaccts]
[MixedAmount]
coltotals :: [MixedAmount] =
String -> [MixedAmount] -> [MixedAmount]
forall a. Show a => String -> a -> a
dbg1 "coltotals" ([MixedAmount] -> [MixedAmount]) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> a -> b
$ ([MixedAmount] -> MixedAmount) -> [[MixedAmount]] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map [MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [[MixedAmount]]
colamts
[grandtotal :: MixedAmount
grandtotal,grandaverage :: MixedAmount
grandaverage] =
let amts :: [MixedAmount]
amts = (([MixedAmount] -> MixedAmount) -> MixedAmount)
-> [[MixedAmount] -> MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map (([MixedAmount] -> MixedAmount) -> [MixedAmount] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ ([MixedAmount] -> MixedAmount) -> [[MixedAmount]] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map [MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [[MixedAmount]]
colamts)
[if BalanceType
balancetype_BalanceType -> BalanceType -> Bool
forall a. Eq a => a -> a -> Bool
==BalanceType
PeriodChange then [MixedAmount] -> MixedAmount
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum else MixedAmount -> [MixedAmount] -> MixedAmount
forall a b. a -> b -> a
const 0
,[MixedAmount] -> MixedAmount
averageMixedAmounts
]
in [MixedAmount]
amts
MultiBalanceReportTotals
totalsrow :: MultiBalanceReportTotals =
String -> MultiBalanceReportTotals -> MultiBalanceReportTotals
forall a. Show a => String -> a -> a
dbg1 "totalsrow" ([MixedAmount]
coltotals, MixedAmount
grandtotal, MixedAmount
grandaverage)
[MultiBalanceReportRow]
mappedsortedrows :: [MultiBalanceReportRow] =
if Bool -> Bool
not Bool
percent_ then [MultiBalanceReportRow]
sortedrows
else String -> [MultiBalanceReportRow] -> [MultiBalanceReportRow]
forall a. Show a => String -> a -> a
dbg1 "mappedsortedrows"
[(Text
aname, Text
alname, Int
alevel, (MixedAmount -> MixedAmount -> MixedAmount)
-> [MixedAmount] -> [MixedAmount] -> [MixedAmount]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith MixedAmount -> MixedAmount -> MixedAmount
perdivide [MixedAmount]
rowvals [MixedAmount]
coltotals, MixedAmount
rowtotal MixedAmount -> MixedAmount -> MixedAmount
`perdivide` MixedAmount
grandtotal, MixedAmount
rowavg MixedAmount -> MixedAmount -> MixedAmount
`perdivide` MixedAmount
grandaverage)
| (aname :: Text
aname, alname :: Text
alname, alevel :: Int
alevel, rowvals :: [MixedAmount]
rowvals, rowtotal :: MixedAmount
rowtotal, rowavg :: MixedAmount
rowavg) <- [MultiBalanceReportRow]
sortedrows
]
MultiBalanceReportTotals
mappedtotalsrow :: MultiBalanceReportTotals =
if Bool -> Bool
not Bool
percent_ then MultiBalanceReportTotals
totalsrow
else String -> MultiBalanceReportTotals -> MultiBalanceReportTotals
forall a. Show a => String -> a -> a
dbg1 "mappedtotalsrow" (
(MixedAmount -> MixedAmount) -> [MixedAmount] -> [MixedAmount]
forall a b. (a -> b) -> [a] -> [b]
map (\t :: MixedAmount
t -> MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
t MixedAmount
t) [MixedAmount]
coltotals,
MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
grandtotal MixedAmount
grandtotal,
MixedAmount -> MixedAmount -> MixedAmount
perdivide MixedAmount
grandaverage MixedAmount
grandaverage)
mbrNormaliseSign :: NormalSign -> MultiBalanceReport -> MultiBalanceReport
mbrNormaliseSign :: NormalSign -> MultiBalanceReport -> MultiBalanceReport
mbrNormaliseSign NormallyNegative = MultiBalanceReport -> MultiBalanceReport
mbrNegate
mbrNormaliseSign _ = MultiBalanceReport -> MultiBalanceReport
forall a. a -> a
id
mbrNegate :: MultiBalanceReport -> MultiBalanceReport
mbrNegate (MultiBalanceReport (colspans :: [DateSpan]
colspans, rows :: [MultiBalanceReportRow]
rows, totalsrow :: MultiBalanceReportTotals
totalsrow)) =
([DateSpan], [MultiBalanceReportRow], MultiBalanceReportTotals)
-> MultiBalanceReport
MultiBalanceReport ([DateSpan]
colspans, (MultiBalanceReportRow -> MultiBalanceReportRow)
-> [MultiBalanceReportRow] -> [MultiBalanceReportRow]
forall a b. (a -> b) -> [a] -> [b]
map MultiBalanceReportRow -> MultiBalanceReportRow
forall b e f a b c.
(Num b, Num e, Num f) =>
(a, b, c, [b], e, f) -> (a, b, c, [b], e, f)
mbrRowNegate [MultiBalanceReportRow]
rows, MultiBalanceReportTotals -> MultiBalanceReportTotals
forall b b c. (Num b, Num b, Num c) => ([b], b, c) -> ([b], b, c)
mbrTotalsRowNegate MultiBalanceReportTotals
totalsrow)
where
mbrRowNegate :: (a, b, c, [b], e, f) -> (a, b, c, [b], e, f)
mbrRowNegate (acct :: a
acct,shortacct :: b
shortacct,indent :: c
indent,amts :: [b]
amts,tot :: e
tot,avg :: f
avg) = (a
acct,b
shortacct,c
indent,(b -> b) -> [b] -> [b]
forall a b. (a -> b) -> [a] -> [b]
map b -> b
forall a. Num a => a -> a
negate [b]
amts,-e
tot,-f
avg)
mbrTotalsRowNegate :: ([b], b, c) -> ([b], b, c)
mbrTotalsRowNegate (amts :: [b]
amts,tot :: b
tot,avg :: c
avg) = ((b -> b) -> [b] -> [b]
forall a b. (a -> b) -> [a] -> [b]
map b -> b
forall a. Num a => a -> a
negate [b]
amts,-b
tot,-c
avg)
multiBalanceReportSpan :: MultiBalanceReport -> DateSpan
multiBalanceReportSpan :: MultiBalanceReport -> DateSpan
multiBalanceReportSpan (MultiBalanceReport ([], _, _)) = Maybe Day -> Maybe Day -> DateSpan
DateSpan Maybe Day
forall a. Maybe a
Nothing Maybe Day
forall a. Maybe a
Nothing
multiBalanceReportSpan (MultiBalanceReport (colspans :: [DateSpan]
colspans, _, _)) = Maybe Day -> Maybe Day -> DateSpan
DateSpan (DateSpan -> Maybe Day
spanStart (DateSpan -> Maybe Day) -> DateSpan -> Maybe Day
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> DateSpan
forall a. [a] -> a
head [DateSpan]
colspans) (DateSpan -> Maybe Day
spanEnd (DateSpan -> Maybe Day) -> DateSpan -> Maybe Day
forall a b. (a -> b) -> a -> b
$ [DateSpan] -> DateSpan
forall a. [a] -> a
last [DateSpan]
colspans)
balanceReportFromMultiBalanceReport :: ReportOpts -> Query -> Journal -> BalanceReport
balanceReportFromMultiBalanceReport :: ReportOpts
-> Query
-> Journal
-> ([(Text, Text, Int, MixedAmount)], MixedAmount)
balanceReportFromMultiBalanceReport opts :: ReportOpts
opts q :: Query
q j :: Journal
j = ([(Text, Text, Int, MixedAmount)]
rows', MixedAmount
total)
where
MultiBalanceReport (_, rows :: [MultiBalanceReportRow]
rows, (totals :: [MixedAmount]
totals, _, _)) = ReportOpts -> Query -> Journal -> MultiBalanceReport
multiBalanceReport ReportOpts
opts Query
q Journal
j
rows' :: [(Text, Text, Int, MixedAmount)]
rows' = [(Text
a
,if ReportOpts -> Bool
flat_ ReportOpts
opts then Text
a else Text
a'
,if ReportOpts -> Bool
tree_ ReportOpts
opts then Int
dInt -> Int -> Int
forall a. Num a => a -> a -> a
-1 else 0
, MixedAmount -> [MixedAmount] -> MixedAmount
forall a. a -> [a] -> a
headDef MixedAmount
nullmixedamt [MixedAmount]
amts
) | (a :: Text
a,a' :: Text
a',d :: Int
d, amts :: [MixedAmount]
amts, _, _) <- [MultiBalanceReportRow]
rows]
total :: MixedAmount
total = MixedAmount -> [MixedAmount] -> MixedAmount
forall a. a -> [a] -> a
headDef MixedAmount
nullmixedamt [MixedAmount]
totals
tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String
tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String
tableAsText (ReportOpts{pretty_tables_ :: ReportOpts -> Bool
pretty_tables_ = Bool
pretty}) showcell :: a -> String
showcell =
[String] -> String
unlines
([String] -> String)
-> (Table String String a -> [String])
-> Table String String a
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> [String]
forall a. [[a]] -> [[a]]
trimborder
([String] -> [String])
-> (Table String String a -> [String])
-> Table String String a
-> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
(String -> [String])
-> (Table String String a -> String)
-> Table String String a
-> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool
-> ShowS
-> ShowS
-> (a -> String)
-> Table String String a
-> String
forall rh ch a.
Bool
-> (rh -> String)
-> (ch -> String)
-> (a -> String)
-> Table rh ch a
-> String
render Bool
pretty ShowS
forall a. a -> a
id ShowS
forall a. a -> a
id a -> String
showcell
(Table String String a -> String)
-> (Table String String a -> Table String String a)
-> Table String String a
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Table String String a -> Table String String a
forall ch a. Table String ch a -> Table String ch a
align
where
trimborder :: [[a]] -> [[a]]
trimborder = Int -> [[a]] -> [[a]]
forall a. Int -> [a] -> [a]
drop 1 ([[a]] -> [[a]]) -> ([[a]] -> [[a]]) -> [[a]] -> [[a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [[a]] -> [[a]]
forall a. [a] -> [a]
init ([[a]] -> [[a]]) -> ([[a]] -> [[a]]) -> [[a]] -> [[a]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([a] -> [a]) -> [[a]] -> [[a]]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop 1 ([a] -> [a]) -> ([a] -> [a]) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [a]
forall a. [a] -> [a]
init)
align :: Table String ch a -> Table String ch a
align (Table l :: Header String
l t :: Header ch
t d :: [[a]]
d) = Header String -> Header ch -> [[a]] -> Table String ch a
forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Table Header String
l' Header ch
t [[a]]
d
where
acctswidth :: Int
acctswidth = [Int] -> Int
forall a. Integral a => [a] -> a
maximum' ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (String -> Int) -> [String] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map String -> Int
strWidth (Header String -> [String]
forall h. Header h -> [h]
headerContents Header String
l)
l' :: Header String
l' = Int -> ShowS
padRightWide Int
acctswidth ShowS -> Header String -> Header String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Header String
l
tests_MultiBalanceReport :: TestTree
tests_MultiBalanceReport = String -> [TestTree] -> TestTree
tests "MultiBalanceReport" [
let
amt0 :: Amount
amt0 = Amount :: Text
-> Quantity -> Bool -> AmountStyle -> Maybe AmountPrice -> Amount
Amount {acommodity :: Text
acommodity="$", aquantity :: Quantity
aquantity=0, aprice :: Maybe AmountPrice
aprice=Maybe AmountPrice
forall a. Maybe a
Nothing, astyle :: AmountStyle
astyle=$WAmountStyle :: Side
-> Bool
-> Int
-> Maybe Char
-> Maybe DigitGroupStyle
-> AmountStyle
AmountStyle {ascommodityside :: Side
ascommodityside = Side
L, ascommodityspaced :: Bool
ascommodityspaced = Bool
False, asprecision :: Int
asprecision = 2, asdecimalpoint :: Maybe Char
asdecimalpoint = Char -> Maybe Char
forall a. a -> Maybe a
Just '.', asdigitgroups :: Maybe DigitGroupStyle
asdigitgroups = Maybe DigitGroupStyle
forall a. Maybe a
Nothing}, aismultiplier :: Bool
aismultiplier=Bool
False}
(opts :: ReportOpts
opts,journal :: Journal
journal) gives :: (ReportOpts, Journal)
-> ([MultiBalanceReportRow], MixedAmount) -> IO ()
`gives` r :: ([MultiBalanceReportRow], MixedAmount)
r = do
let (eitems :: [MultiBalanceReportRow]
eitems, etotal :: MixedAmount
etotal) = ([MultiBalanceReportRow], MixedAmount)
r
(MultiBalanceReport (_, aitems :: [MultiBalanceReportRow]
aitems, atotal :: MultiBalanceReportTotals
atotal)) = ReportOpts -> Query -> Journal -> MultiBalanceReport
multiBalanceReport ReportOpts
opts (Day -> ReportOpts -> Query
queryFromOpts Day
nulldate ReportOpts
opts) Journal
journal
showw :: (a, b, c, [MixedAmount], MixedAmount, MixedAmount)
-> (a, b, c, [String], String, String)
showw (acct :: a
acct,acct' :: b
acct',indent :: c
indent,lAmt :: [MixedAmount]
lAmt,amt :: MixedAmount
amt,amt' :: MixedAmount
amt') = (a
acct, b
acct', c
indent, (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')
((MultiBalanceReportRow
-> (Text, Text, Int, [String], String, String))
-> [MultiBalanceReportRow]
-> [(Text, Text, Int, [String], String, String)]
forall a b. (a -> b) -> [a] -> [b]
map MultiBalanceReportRow
-> (Text, Text, Int, [String], String, String)
forall a b c.
(a, b, c, [MixedAmount], MixedAmount, MixedAmount)
-> (a, b, c, [String], String, String)
showw [MultiBalanceReportRow]
aitems) [(Text, Text, Int, [String], String, String)]
-> [(Text, Text, Int, [String], String, String)] -> IO ()
forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= ((MultiBalanceReportRow
-> (Text, Text, Int, [String], String, String))
-> [MultiBalanceReportRow]
-> [(Text, Text, Int, [String], String, String)]
forall a b. (a -> b) -> [a] -> [b]
map MultiBalanceReportRow
-> (Text, Text, Int, [String], String, String)
forall a b c.
(a, b, c, [MixedAmount], MixedAmount, MixedAmount)
-> (a, b, c, [String], String, String)
showw [MultiBalanceReportRow]
eitems)
((\(_, b :: MixedAmount
b, _) -> MixedAmount -> String
showMixedAmountDebug MixedAmount
b) MultiBalanceReportTotals
atotal) String -> String -> IO ()
forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
@?= (MixedAmount -> String
showMixedAmountDebug MixedAmount
etotal)
in
String -> [TestTree] -> TestTree
tests "multiBalanceReport" [
String -> IO () -> TestTree
test "null journal" (IO () -> TestTree) -> IO () -> TestTree
forall a b. (a -> b) -> a -> b
$
(ReportOpts
defreportopts, Journal
nulljournal) (ReportOpts, Journal)
-> ([MultiBalanceReportRow], MixedAmount) -> IO ()
`gives` ([], [Amount] -> MixedAmount
Mixed [Amount
nullamt])
,String -> IO () -> TestTree
test "with -H on a populated period" (IO () -> TestTree) -> IO () -> TestTree
forall a b. (a -> b) -> a -> b
$
(ReportOpts
defreportopts{period_ :: Period
period_= Day -> Day -> Period
PeriodBetween (Year -> Int -> Int -> Day
fromGregorian 2008 1 1) (Year -> Int -> Int -> Day
fromGregorian 2008 1 2), balancetype_ :: BalanceType
balancetype_=BalanceType
HistoricalBalance}, Journal
samplejournal) (ReportOpts, Journal)
-> ([MultiBalanceReportRow], MixedAmount) -> IO ()
`gives`
(
[
("assets:bank:checking", "checking", 3, [String -> MixedAmount
mamountp' "$1.00"] , [Amount] -> MixedAmount
Mixed [Amount
nullamt], [Amount] -> MixedAmount
Mixed [Amount
amt0 {aquantity :: Quantity
aquantity=1}])
,("income:salary" ,"salary" , 2, [String -> MixedAmount
mamountp' "$-1.00"], [Amount] -> MixedAmount
Mixed [Amount
nullamt], [Amount] -> MixedAmount
Mixed [Amount
amt0 {aquantity :: Quantity
aquantity=(-1)}])
],
[Amount] -> MixedAmount
Mixed [Amount
nullamt])
]
]