{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Hledger.Cli.CompoundBalanceCommand (
CompoundBalanceCommandSpec(..)
,compoundBalanceCommandMode
,compoundBalanceCommand
) where
import Data.List (foldl')
import Data.Maybe (fromMaybe, mapMaybe)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import Data.Time.Calendar (Day, addDays)
import System.Console.CmdArgs.Explicit as C
import Hledger.Read.CsvReader (CSV, printCSV)
import Lucid as L hiding (value_)
import Text.Tabular.AsciiWide as Tab hiding (render)
import Hledger
import Hledger.Cli.Commands.Balance
import Hledger.Cli.CliOptions
import Hledger.Cli.Utils (unsupportedOutputFormatError, writeOutputLazyText)
data CompoundBalanceCommandSpec = CompoundBalanceCommandSpec {
CompoundBalanceCommandSpec -> [Char]
cbcdoc :: CommandDoc,
CompoundBalanceCommandSpec -> [Char]
cbctitle :: String,
CompoundBalanceCommandSpec -> [CBCSubreportSpec DisplayName]
cbcqueries :: [CBCSubreportSpec DisplayName],
CompoundBalanceCommandSpec -> BalanceAccumulation
cbcaccum :: BalanceAccumulation
}
compoundBalanceCommandMode :: CompoundBalanceCommandSpec -> Mode RawOpts
compoundBalanceCommandMode :: CompoundBalanceCommandSpec -> Mode RawOpts
compoundBalanceCommandMode CompoundBalanceCommandSpec{[Char]
[CBCSubreportSpec DisplayName]
BalanceAccumulation
cbcaccum :: BalanceAccumulation
cbcqueries :: [CBCSubreportSpec DisplayName]
cbctitle :: [Char]
cbcdoc :: [Char]
cbcaccum :: CompoundBalanceCommandSpec -> BalanceAccumulation
cbcqueries :: CompoundBalanceCommandSpec -> [CBCSubreportSpec DisplayName]
cbctitle :: CompoundBalanceCommandSpec -> [Char]
cbcdoc :: CompoundBalanceCommandSpec -> [Char]
..} =
[Char]
-> [Flag RawOpts]
-> [([Char], [Flag RawOpts])]
-> [Flag RawOpts]
-> ([Arg RawOpts], Maybe (Arg RawOpts))
-> Mode RawOpts
hledgerCommandMode
[Char]
cbcdoc
([forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"sum"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"sum")
[Char]
"show sum of posting amounts (default)"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"valuechange"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"valuechange")
[Char]
"show total change of period-end historical balance value (caused by deposits, withdrawals, market price fluctuations)"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"gain"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"gain")
[Char]
"show unrealised capital gain/loss (historical balance value minus cost basis)"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"budget"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"budget")
[Char]
"show sum of posting amounts compared to budget goals defined by periodic transactions\n "
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"change"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"change")
([Char]
"accumulate amounts from column start to column end (in multicolumn reports)"
forall a. [a] -> [a] -> [a]
++ BalanceAccumulation -> [Char]
defaultMarker BalanceAccumulation
PerPeriod)
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"cumulative"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"cumulative")
([Char]
"accumulate amounts from report start (specified by e.g. -b/--begin) to column end"
forall a. [a] -> [a] -> [a]
++ BalanceAccumulation -> [Char]
defaultMarker BalanceAccumulation
Cumulative)
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"historical",[Char]
"H"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"historical")
([Char]
"accumulate amounts from journal start to column end (includes postings before report start date)"
forall a. [a] -> [a] -> [a]
++ BalanceAccumulation -> [Char]
defaultMarker BalanceAccumulation
Historical forall a. [a] -> [a] -> [a]
++ [Char]
"\n ")
]
forall a. [a] -> [a] -> [a]
++ Bool -> [Flag RawOpts]
flattreeflags Bool
True forall a. [a] -> [a] -> [a]
++
[forall a. [[Char]] -> Update a -> [Char] -> [Char] -> Flag a
flagReq [[Char]
"drop"] (\[Char]
s RawOpts
opts -> forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ [Char] -> [Char] -> RawOpts -> RawOpts
setopt [Char]
"drop" [Char]
s RawOpts
opts) [Char]
"N" [Char]
"flat mode: omit N leading account name parts"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"declared"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"declared") [Char]
"include non-parent declared accounts (best used with -E)"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"average",[Char]
"A"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"average") [Char]
"show a row average column (in multicolumn reports)"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"row-total",[Char]
"T"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"row-total") [Char]
"show a row total column (in multicolumn reports)"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"no-total",[Char]
"N"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"no-total") [Char]
"omit the final total row"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"no-elide"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"no-elide") [Char]
"don't squash boring parent accounts (in tree mode)"
,forall a. [[Char]] -> Update a -> [Char] -> [Char] -> Flag a
flagReq [[Char]
"format"] (\[Char]
s RawOpts
opts -> forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ [Char] -> [Char] -> RawOpts -> RawOpts
setopt [Char]
"format" [Char]
s RawOpts
opts) [Char]
"FORMATSTR" [Char]
"use this custom line format (in simple reports)"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"sort-amount",[Char]
"S"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"sort-amount") [Char]
"sort by amount instead of account code/name"
,forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"percent", [Char]
"%"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"percent") [Char]
"express values in percentage of each column's total"
,forall a. [[Char]] -> Update a -> [Char] -> [Char] -> Flag a
flagReq [[Char]
"layout"] (\[Char]
s RawOpts
opts -> forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ [Char] -> [Char] -> RawOpts -> RawOpts
setopt [Char]
"layout" [Char]
s RawOpts
opts) [Char]
"ARG"
([[Char]] -> [Char]
unlines
[[Char]
"how to show multi-commodity amounts:"
,[Char]
"'wide[,WIDTH]': all commodities on one line"
,[Char]
"'tall' : each commodity on a new line"
,[Char]
"'bare' : bare numbers, symbols in a column"
])
,[[Char]] -> Flag RawOpts
outputFormatFlag [[Char]
"txt",[Char]
"html",[Char]
"csv",[Char]
"json"]
,Flag RawOpts
outputFileFlag
])
[([Char], [Flag RawOpts])
generalflagsgroup1]
([Flag RawOpts]
hiddenflags forall a. [a] -> [a] -> [a]
++
[ forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
flagNone [[Char]
"commodity-column"] ([Char] -> RawOpts -> RawOpts
setboolopt [Char]
"commodity-column")
[Char]
"show commodity symbols in a separate column, amounts as bare numbers, one row per commodity"
])
([], forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ [Char] -> Arg RawOpts
argsFlag [Char]
"[QUERY]")
where
defaultMarker :: BalanceAccumulation -> String
defaultMarker :: BalanceAccumulation -> [Char]
defaultMarker BalanceAccumulation
bacc | BalanceAccumulation
bacc forall a. Eq a => a -> a -> Bool
== BalanceAccumulation
cbcaccum = [Char]
" (default)"
| Bool
otherwise = [Char]
""
compoundBalanceCommand :: CompoundBalanceCommandSpec -> (CliOpts -> Journal -> IO ())
compoundBalanceCommand :: CompoundBalanceCommandSpec -> CliOpts -> Journal -> IO ()
compoundBalanceCommand CompoundBalanceCommandSpec{[Char]
[CBCSubreportSpec DisplayName]
BalanceAccumulation
cbcaccum :: BalanceAccumulation
cbcqueries :: [CBCSubreportSpec DisplayName]
cbctitle :: [Char]
cbcdoc :: [Char]
cbcaccum :: CompoundBalanceCommandSpec -> BalanceAccumulation
cbcqueries :: CompoundBalanceCommandSpec -> [CBCSubreportSpec DisplayName]
cbctitle :: CompoundBalanceCommandSpec -> [Char]
cbcdoc :: CompoundBalanceCommandSpec -> [Char]
..} opts :: CliOpts
opts@CliOpts{reportspec_ :: CliOpts -> ReportSpec
reportspec_=ReportSpec
rspec, rawopts_ :: CliOpts -> RawOpts
rawopts_=RawOpts
rawopts} Journal
j = do
CliOpts -> Text -> IO ()
writeOutputLazyText CliOpts
opts forall a b. (a -> b) -> a -> b
$ CompoundPeriodicReport DisplayName MixedAmount -> Text
render CompoundPeriodicReport DisplayName MixedAmount
cbr
where
ropts :: ReportOpts
ropts@ReportOpts{Bool
Int
[Text]
[Status]
Maybe Int
Maybe Text
Maybe ConversionOp
Maybe ValuationType
Maybe NormalSign
BalanceCalculation
BalanceAccumulation
AccountListMode
Layout
StringFormat
Period
Interval
period_ :: ReportOpts -> Period
interval_ :: ReportOpts -> Interval
statuses_ :: ReportOpts -> [Status]
conversionop_ :: ReportOpts -> Maybe ConversionOp
value_ :: ReportOpts -> Maybe ValuationType
infer_prices_ :: ReportOpts -> Bool
depth_ :: ReportOpts -> Maybe Int
date2_ :: ReportOpts -> Bool
empty_ :: ReportOpts -> Bool
no_elide_ :: ReportOpts -> Bool
real_ :: ReportOpts -> Bool
format_ :: ReportOpts -> StringFormat
pretty_ :: ReportOpts -> Bool
querystring_ :: ReportOpts -> [Text]
average_ :: ReportOpts -> Bool
related_ :: ReportOpts -> Bool
txn_dates_ :: ReportOpts -> Bool
balancecalc_ :: ReportOpts -> BalanceCalculation
balanceaccum_ :: ReportOpts -> BalanceAccumulation
budgetpat_ :: ReportOpts -> Maybe Text
accountlistmode_ :: ReportOpts -> AccountListMode
drop_ :: ReportOpts -> Int
declared_ :: ReportOpts -> Bool
row_total_ :: ReportOpts -> Bool
no_total_ :: ReportOpts -> Bool
show_costs_ :: ReportOpts -> Bool
sort_amount_ :: ReportOpts -> Bool
percent_ :: ReportOpts -> Bool
invert_ :: ReportOpts -> Bool
normalbalance_ :: ReportOpts -> Maybe NormalSign
color_ :: ReportOpts -> Bool
transpose_ :: ReportOpts -> Bool
layout_ :: ReportOpts -> Layout
layout_ :: Layout
transpose_ :: Bool
color_ :: Bool
normalbalance_ :: Maybe NormalSign
invert_ :: Bool
percent_ :: Bool
sort_amount_ :: Bool
show_costs_ :: Bool
no_total_ :: Bool
row_total_ :: Bool
declared_ :: Bool
drop_ :: Int
accountlistmode_ :: AccountListMode
budgetpat_ :: Maybe Text
balanceaccum_ :: BalanceAccumulation
balancecalc_ :: BalanceCalculation
txn_dates_ :: Bool
related_ :: Bool
average_ :: Bool
querystring_ :: [Text]
pretty_ :: Bool
format_ :: StringFormat
real_ :: Bool
no_elide_ :: Bool
empty_ :: Bool
date2_ :: Bool
depth_ :: Maybe Int
infer_prices_ :: Bool
value_ :: Maybe ValuationType
conversionop_ :: Maybe ConversionOp
statuses_ :: [Status]
interval_ :: Interval
period_ :: Period
..} = ReportSpec -> ReportOpts
_rsReportOpts ReportSpec
rspec
mbalanceAccumulationOverride :: Maybe BalanceAccumulation
mbalanceAccumulationOverride = RawOpts -> Maybe BalanceAccumulation
balanceAccumulationOverride RawOpts
rawopts
balanceaccumulation :: BalanceAccumulation
balanceaccumulation = forall a. a -> Maybe a -> a
fromMaybe BalanceAccumulation
cbcaccum Maybe BalanceAccumulation
mbalanceAccumulationOverride
ropts' :: ReportOpts
ropts' = ReportOpts
ropts{balanceaccum_ :: BalanceAccumulation
balanceaccum_=BalanceAccumulation
balanceaccumulation}
title :: Text
title =
[Char] -> Text
T.pack [Char]
cbctitle
forall a. Semigroup a => a -> a -> a
<> Text
" "
forall a. Semigroup a => a -> a -> a
<> Text
titledatestr
forall a. Semigroup a => a -> a -> a
<> forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" (Text
" "forall a. Semigroup a => a -> a -> a
<>) Maybe Text
mtitleclarification
forall a. Semigroup a => a -> a -> a
<> Text
valuationdesc
where
titledatestr :: Text
titledatestr = case BalanceAccumulation
balanceaccumulation of
BalanceAccumulation
Historical -> [Day] -> Text
showEndDates [Day]
enddates
BalanceAccumulation
_ -> DateSpan -> Text
showDateSpan DateSpan
requestedspan
where
enddates :: [Day]
enddates = forall a b. (a -> b) -> [a] -> [b]
map (Integer -> Day -> Day
addDays (-Integer
1)) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe DateSpan -> Maybe Day
spanEnd forall a b. (a -> b) -> a -> b
$ forall a b. CompoundPeriodicReport a b -> [DateSpan]
cbrDates CompoundPeriodicReport DisplayName MixedAmount
cbr
requestedspan :: DateSpan
requestedspan = forall a b. (a, b) -> a
fst forall a b. (a -> b) -> a -> b
$ Journal -> ReportSpec -> (DateSpan, [DateSpan])
reportSpan Journal
j ReportSpec
rspec
mtitleclarification :: Maybe Text
mtitleclarification = case (BalanceCalculation
balancecalc_, BalanceAccumulation
balanceaccumulation, Maybe BalanceAccumulation
mbalanceAccumulationOverride) of
(BalanceCalculation
CalcValueChange, BalanceAccumulation
PerPeriod, Maybe BalanceAccumulation
_ ) -> forall a. a -> Maybe a
Just Text
"(Period-End Value Changes)"
(BalanceCalculation
CalcValueChange, BalanceAccumulation
Cumulative, Maybe BalanceAccumulation
_ ) -> forall a. a -> Maybe a
Just Text
"(Cumulative Period-End Value Changes)"
(BalanceCalculation
CalcGain, BalanceAccumulation
PerPeriod, Maybe BalanceAccumulation
_ ) -> forall a. a -> Maybe a
Just Text
"(Incremental Gain)"
(BalanceCalculation
CalcGain, BalanceAccumulation
Cumulative, Maybe BalanceAccumulation
_ ) -> forall a. a -> Maybe a
Just Text
"(Cumulative Gain)"
(BalanceCalculation
CalcGain, BalanceAccumulation
Historical, Maybe BalanceAccumulation
_ ) -> forall a. a -> Maybe a
Just Text
"(Historical Gain)"
(BalanceCalculation
_, BalanceAccumulation
_, Just BalanceAccumulation
PerPeriod ) -> forall a. a -> Maybe a
Just Text
"(Balance Changes)"
(BalanceCalculation
_, BalanceAccumulation
_, Just BalanceAccumulation
Cumulative) -> forall a. a -> Maybe a
Just Text
"(Cumulative Ending Balances)"
(BalanceCalculation
_, BalanceAccumulation
_, Just BalanceAccumulation
Historical) -> forall a. a -> Maybe a
Just Text
"(Historical Ending Balances)"
(BalanceCalculation, BalanceAccumulation,
Maybe BalanceAccumulation)
_ -> forall a. Maybe a
Nothing
valuationdesc :: Text
valuationdesc =
(case Maybe ConversionOp
conversionop_ of
Just ConversionOp
ToCost -> Text
", converted to cost"
Maybe ConversionOp
_ -> Text
"")
forall a. Semigroup a => a -> a -> a
<> (case Maybe ValuationType
value_ of
Just (AtThen Maybe Text
_mc) -> Text
", valued at posting date"
Just (AtEnd Maybe Text
_mc) | Bool
changingValuation -> Text
""
Just (AtEnd Maybe Text
_mc) -> Text
", valued at period ends"
Just (AtNow Maybe Text
_mc) -> Text
", current value"
Just (AtDate Day
today Maybe Text
_mc) -> Text
", valued at " forall a. Semigroup a => a -> a -> a
<> Day -> Text
showDate Day
today
Maybe ValuationType
Nothing -> Text
"")
changingValuation :: Bool
changingValuation = case (BalanceCalculation
balancecalc_, BalanceAccumulation
balanceaccum_) of
(BalanceCalculation
CalcValueChange, BalanceAccumulation
PerPeriod) -> Bool
True
(BalanceCalculation
CalcValueChange, BalanceAccumulation
Cumulative) -> Bool
True
(BalanceCalculation, BalanceAccumulation)
_ -> Bool
False
cbr' :: CompoundPeriodicReport DisplayName MixedAmount
cbr' = forall a.
ReportSpec
-> Journal
-> [CBCSubreportSpec a]
-> CompoundPeriodicReport a MixedAmount
compoundBalanceReport ReportSpec
rspec{_rsReportOpts :: ReportOpts
_rsReportOpts=ReportOpts
ropts'} Journal
j [CBCSubreportSpec DisplayName]
cbcqueries
cbr :: CompoundPeriodicReport DisplayName MixedAmount
cbr = CompoundPeriodicReport DisplayName MixedAmount
cbr'{cbrTitle :: Text
cbrTitle=Text
title}
render :: CompoundPeriodicReport DisplayName MixedAmount -> Text
render = case CliOpts -> [Char]
outputFormatFromOpts CliOpts
opts of
[Char]
"txt" -> ReportOpts
-> CompoundPeriodicReport DisplayName MixedAmount -> Text
compoundBalanceReportAsText ReportOpts
ropts'
[Char]
"csv" -> CSV -> Text
printCSV forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> CSV
compoundBalanceReportAsCsv ReportOpts
ropts'
[Char]
"html" -> forall a. Html a -> Text
L.renderText forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReportOpts
-> CompoundPeriodicReport DisplayName MixedAmount
-> HtmlT Identity ()
compoundBalanceReportAsHtml ReportOpts
ropts'
[Char]
"json" -> forall a. ToJSON a => a -> Text
toJsonText
[Char]
x -> forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char] -> [Char]
unsupportedOutputFormatError [Char]
x
showEndDates :: [Day] -> T.Text
showEndDates :: [Day] -> Text
showEndDates [Day]
es = case [Day]
es of
(Day
e:Day
_:[Day]
_) -> Day -> Text
showDate Day
e forall a. Semigroup a => a -> a -> a
<> Text
".." forall a. Semigroup a => a -> a -> a
<> Day -> Text
showDate (forall a. [a] -> a
last [Day]
es)
[Day
e] -> Day -> Text
showDate Day
e
[] -> Text
""
compoundBalanceReportAsText :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> TL.Text
compoundBalanceReportAsText :: ReportOpts
-> CompoundPeriodicReport DisplayName MixedAmount -> Text
compoundBalanceReportAsText ReportOpts
ropts
(CompoundPeriodicReport Text
title [DateSpan]
_colspans [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports PeriodicReportRow () MixedAmount
netrow) =
Builder -> Text
TB.toLazyText forall a b. (a -> b) -> a -> b
$
Text -> Builder
TB.fromText Text
title forall a. Semigroup a => a -> a -> a
<> Text -> Builder
TB.fromText Text
"\n\n" forall a. Semigroup a => a -> a -> a
<>
ReportOpts -> Table Text Text WideBuilder -> Builder
balanceReportTableAsText ReportOpts
ropts Table Text Text WideBuilder
bigtable'
where
bigtable :: Table Text Text WideBuilder
bigtable =
case forall a b. (a -> b) -> [a] -> [b]
map (forall {c}.
ReportOpts
-> (Text, PeriodicReport DisplayName MixedAmount, c)
-> Table Text Text WideBuilder
subreportAsTable ReportOpts
ropts) [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports of
[] -> forall rh ch a. Table rh ch a
Tab.empty
Table Text Text WideBuilder
r:[Table Text Text WideBuilder]
rs -> forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (forall rh ch a ch2.
Properties -> Table rh ch a -> Table rh ch2 a -> Table rh ch a
concatTables Properties
DoubleLine) Table Text Text WideBuilder
r [Table Text Text WideBuilder]
rs
bigtable' :: Table Text Text WideBuilder
bigtable'
| ReportOpts -> Bool
no_total_ ReportOpts
ropts Bool -> Bool -> Bool
|| forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports forall a. Eq a => a -> a -> Bool
== Int
1 =
Table Text Text WideBuilder
bigtable
| Bool
otherwise =
let totalrows :: [[WideBuilder]]
totalrows = forall a.
ReportOpts -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
multiBalanceRowAsTableText ReportOpts
ropts PeriodicReportRow () MixedAmount
netrow
rh :: Header Text
rh = forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
NoLine forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall h. h -> Header h
Header (Text
"Net:" forall a. a -> [a] -> [a]
: forall a. Int -> a -> [a]
replicate (forall (t :: * -> *) a. Foldable t => t a -> Int
length [[WideBuilder]]
totalrows forall a. Num a => a -> a -> a
- Int
1) Text
"")
ch :: Header [a]
ch = forall h. h -> Header h
Header []
in ((forall rh ch a ch2.
Properties -> Table rh ch a -> Table rh ch2 a -> Table rh ch a
concatTables Properties
Tab.DoubleLine) Table Text Text WideBuilder
bigtable forall a b. (a -> b) -> a -> b
$ forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Table Header Text
rh forall {a}. Header [a]
ch [[WideBuilder]]
totalrows)
subreportAsTable :: ReportOpts
-> (Text, PeriodicReport DisplayName MixedAmount, c)
-> Table Text Text WideBuilder
subreportAsTable ReportOpts
ropts1 (Text
title1, PeriodicReport DisplayName MixedAmount
r, c
_) = Table Text Text WideBuilder
t
where
Table Header Text
lefthdrs Header Text
tophdrs [[WideBuilder]]
cells = ReportOpts
-> PeriodicReport DisplayName MixedAmount
-> Table Text Text WideBuilder
balanceReportAsTable ReportOpts
ropts1 PeriodicReport DisplayName MixedAmount
r
t :: Table Text Text WideBuilder
t = forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
Table (forall h. Properties -> [Header h] -> Header h
Tab.Group Properties
Tab.SingleLine [forall h. h -> Header h
Tab.Header Text
title1, Header Text
lefthdrs]) Header Text
tophdrs ([]forall a. a -> [a] -> [a]
:[[WideBuilder]]
cells)
compoundBalanceReportAsCsv :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> CSV
compoundBalanceReportAsCsv :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> CSV
compoundBalanceReportAsCsv ReportOpts
ropts (CompoundPeriodicReport Text
title [DateSpan]
colspans [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports PeriodicReportRow () MixedAmount
netrow) =
CSV -> CSV
addtotals forall a b. (a -> b) -> a -> b
$
forall {a}. IsString a => a -> [a]
padRow Text
title
forall a. a -> [a] -> [a]
: ( Text
"Account"
forall a. a -> [a] -> [a]
: [Text
"Commodity" | ReportOpts -> Layout
layout_ ReportOpts
ropts forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare]
forall a. [a] -> [a] -> [a]
++ forall a b. (a -> b) -> [a] -> [b]
map (BalanceAccumulation -> [DateSpan] -> DateSpan -> Text
reportPeriodName (ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts) [DateSpan]
colspans) [DateSpan]
colspans
forall a. [a] -> [a] -> [a]
++ (if ReportOpts -> Bool
row_total_ ReportOpts
ropts then [Text
"Total"] else [])
forall a. [a] -> [a] -> [a]
++ (if ReportOpts -> Bool
average_ ReportOpts
ropts then [Text
"Average"] else [])
)
forall a. a -> [a] -> [a]
: forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (forall {c}.
ReportOpts
-> (Text, PeriodicReport DisplayName MixedAmount, c) -> CSV
subreportAsCsv ReportOpts
ropts) [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports
where
subreportAsCsv :: ReportOpts
-> (Text, PeriodicReport DisplayName MixedAmount, c) -> CSV
subreportAsCsv ReportOpts
ropts1 (Text
subreporttitle, PeriodicReport DisplayName MixedAmount
multibalreport, c
_) =
forall {a}. IsString a => a -> [a]
padRow Text
subreporttitle forall a. a -> [a] -> [a]
:
forall a. [a] -> [a]
tail (ReportOpts -> PeriodicReport DisplayName MixedAmount -> CSV
multiBalanceReportAsCsv ReportOpts
ropts1 PeriodicReport DisplayName MixedAmount
multibalreport)
padRow :: a -> [a]
padRow a
s = forall a. Int -> [a] -> [a]
take Int
numcols forall a b. (a -> b) -> a -> b
$ a
s forall a. a -> [a] -> [a]
: forall a. a -> [a]
repeat a
""
where
numcols :: Int
numcols
| forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports = Int
1
| Bool
otherwise =
(Int
1 forall a. Num a => a -> a -> a
+) forall a b. (a -> b) -> a -> b
$
(if ReportOpts -> Layout
layout_ ReportOpts
ropts forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare then (Int
1forall a. Num a => a -> a -> a
+) else forall a. a -> a
id) forall a b. (a -> b) -> a -> b
$
(if ReportOpts -> Bool
row_total_ ReportOpts
ropts then (Int
1forall a. Num a => a -> a -> a
+) else forall a. a -> a
id) forall a b. (a -> b) -> a -> b
$
(if ReportOpts -> Bool
average_ ReportOpts
ropts then (Int
1forall a. Num a => a -> a -> a
+) else forall a. a -> a
id) forall a b. (a -> b) -> a -> b
$
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum forall a b. (a -> b) -> a -> b
$
forall a b. (a -> b) -> [a] -> [b]
map (forall (t :: * -> *) a. Foldable t => t a -> Int
length forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. PeriodicReport a b -> [DateSpan]
prDates forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {a} {b} {c}. (a, b, c) -> b
second3) [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports
addtotals :: CSV -> CSV
addtotals
| ReportOpts -> Bool
no_total_ ReportOpts
ropts Bool -> Bool -> Bool
|| forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports forall a. Eq a => a -> a -> Bool
== Int
1 = forall a. a -> a
id
| Bool
otherwise = (forall a. [a] -> [a] -> [a]
++ forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text
"Net:" forall a. a -> [a] -> [a]
: ) (forall a.
ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> CSV
multiBalanceRowAsCsvText ReportOpts
ropts [DateSpan]
colspans PeriodicReportRow () MixedAmount
netrow))
compoundBalanceReportAsHtml :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> Html ()
compoundBalanceReportAsHtml :: ReportOpts
-> CompoundPeriodicReport DisplayName MixedAmount
-> HtmlT Identity ()
compoundBalanceReportAsHtml ReportOpts
ropts CompoundPeriodicReport DisplayName MixedAmount
cbr =
let
CompoundPeriodicReport Text
title [DateSpan]
colspans [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports PeriodicReportRow () MixedAmount
netrow = CompoundPeriodicReport DisplayName MixedAmount
cbr
colspanattr :: Attribute
colspanattr = Text -> Attribute
colspan_ forall a b. (a -> b) -> a -> b
$ [Char] -> Text
T.pack forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> [Char]
show forall a b. (a -> b) -> a -> b
$
Int
1 forall a. Num a => a -> a -> a
+ forall (t :: * -> *) a. Foldable t => t a -> Int
length [DateSpan]
colspans forall a. Num a => a -> a -> a
+ (if ReportOpts -> Bool
row_total_ ReportOpts
ropts then Int
1 else Int
0) forall a. Num a => a -> a -> a
+ (if ReportOpts -> Bool
average_ ReportOpts
ropts then Int
1 else Int
0)
leftattr :: Attribute
leftattr = forall arg result. TermRaw arg result => arg -> result
style_ Text
"text-align:left"
blankrow :: HtmlT Identity ()
blankrow = forall arg result. Term arg result => arg -> result
tr_ forall a b. (a -> b) -> a -> b
$ forall arg result. Term arg result => arg -> result
td_ [Attribute
colspanattr] forall a b. (a -> b) -> a -> b
$ forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
toHtmlRaw ([Char]
" "::String)
titlerows :: [HtmlT Identity ()]
titlerows =
(forall arg result. Term arg result => arg -> result
tr_ forall a b. (a -> b) -> a -> b
$ forall arg result. Term arg result => arg -> result
th_ [Attribute
colspanattr, Attribute
leftattr] forall a b. (a -> b) -> a -> b
$ forall arg result. Term arg result => arg -> result
h2_ forall a b. (a -> b) -> a -> b
$ forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
toHtml Text
title)
forall a. a -> [a] -> [a]
: [[Text] -> HtmlT Identity ()
thRow forall a b. (a -> b) -> a -> b
$
Text
"" forall a. a -> [a] -> [a]
: [Text
"Commodity" | ReportOpts -> Layout
layout_ ReportOpts
ropts forall a. Eq a => a -> a -> Bool
== Layout
LayoutBare] forall a. [a] -> [a] -> [a]
++
forall a b. (a -> b) -> [a] -> [b]
map (BalanceAccumulation -> [DateSpan] -> DateSpan -> Text
reportPeriodName (ReportOpts -> BalanceAccumulation
balanceaccum_ ReportOpts
ropts) [DateSpan]
colspans) [DateSpan]
colspans
forall a. [a] -> [a] -> [a]
++ (if ReportOpts -> Bool
row_total_ ReportOpts
ropts then [Text
"Total"] else [])
forall a. [a] -> [a] -> [a]
++ (if ReportOpts -> Bool
average_ ReportOpts
ropts then [Text
"Average"] else [])
]
thRow :: [T.Text] -> Html ()
thRow :: [Text] -> HtmlT Identity ()
thRow = forall arg result. Term arg result => arg -> result
tr_ forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall arg result. Term arg result => arg -> result
th_ forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
toHtml)
subreportrows :: (T.Text, MultiBalanceReport, Bool) -> [Html ()]
subreportrows :: (Text, PeriodicReport DisplayName MixedAmount, Bool)
-> [HtmlT Identity ()]
subreportrows (Text
subreporttitle, PeriodicReport DisplayName MixedAmount
mbr, Bool
_increasestotal) =
let
(HtmlT Identity ()
_,[HtmlT Identity ()]
bodyrows,[HtmlT Identity ()]
mtotalsrows) = ReportOpts
-> PeriodicReport DisplayName MixedAmount
-> (HtmlT Identity (), [HtmlT Identity ()], [HtmlT Identity ()])
multiBalanceReportHtmlRows ReportOpts
ropts PeriodicReport DisplayName MixedAmount
mbr
in
[forall arg result. Term arg result => arg -> result
tr_ forall a b. (a -> b) -> a -> b
$ forall arg result. Term arg result => arg -> result
th_ [Attribute
colspanattr, Attribute
leftattr] forall a b. (a -> b) -> a -> b
$ forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
toHtml Text
subreporttitle]
forall a. [a] -> [a] -> [a]
++ [HtmlT Identity ()]
bodyrows
forall a. [a] -> [a] -> [a]
++ [HtmlT Identity ()]
mtotalsrows
forall a. [a] -> [a] -> [a]
++ [HtmlT Identity ()
blankrow]
totalrows :: [HtmlT Identity ()]
totalrows | ReportOpts -> Bool
no_total_ ReportOpts
ropts Bool -> Bool -> Bool
|| forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports forall a. Eq a => a -> a -> Bool
== Int
1 = []
| Bool
otherwise = ReportOpts -> [Text] -> HtmlT Identity ()
multiBalanceReportHtmlFootRow ReportOpts
ropts forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((Text
"Net:" forall a. a -> [a] -> [a]
:) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a.
ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> CSV
multiBalanceRowAsCsvText ReportOpts
ropts [DateSpan]
colspans PeriodicReportRow () MixedAmount
netrow)
in do
forall arg result. TermRaw arg result => arg -> result
style_ ([Text] -> Text
T.unlines [Text
""
,Text
"td { padding:0 0.5em; }"
,Text
"td:nth-child(1) { white-space:nowrap; }"
,Text
"tr:nth-child(even) td { background-color:#eee; }"
])
forall (m :: * -> *). Applicative m => [Attribute] -> HtmlT m ()
link_ [Text -> Attribute
rel_ Text
"stylesheet", Text -> Attribute
href_ Text
"hledger.css"]
forall arg result. Term arg result => arg -> result
table_ forall a b. (a -> b) -> a -> b
$ forall a. Monoid a => [a] -> a
mconcat forall a b. (a -> b) -> a -> b
$
[HtmlT Identity ()]
titlerows
forall a. [a] -> [a] -> [a]
++ [HtmlT Identity ()
blankrow]
forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Text, PeriodicReport DisplayName MixedAmount, Bool)
-> [HtmlT Identity ()]
subreportrows [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
subreports
forall a. [a] -> [a] -> [a]
++ [HtmlT Identity ()]
totalrows