    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE RecordWildCards   #-}
    {-|

    Common helpers for making multi-section balance report commands
    like balancesheet, cashflow, and incomestatement.

    -}

    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.CsvUtils (CSV, printCSV, printTSV)
    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)

    -- | Description of a compound balance report command,
    -- from which we generate the command's cmdargs mode and IO action.
    -- A compound balance report command shows one or more sections/subreports,
    -- each with its own title and subtotals row, in a certain order,
    -- plus a grand totals row if there's more than one section.
    -- Examples are the balancesheet, cashflow and incomestatement commands.
    --
    -- Compound balance reports do sign normalisation: they show all account balances
    -- as normally positive, unlike the ordinary BalanceReport and most hledger commands
    -- which show income/liability/equity balances as normally negative.
    -- Each subreport specifies the normal sign of its amounts, and whether
    -- it should be added to or subtracted from the grand total.
    --
    data CompoundBalanceCommandSpec = CompoundBalanceCommandSpec {
      CompoundBalanceCommandSpec -> [Char]
    cbcdoc      :: CommandDoc,                      -- ^ the command's name(s) and documentation
      CompoundBalanceCommandSpec -> [Char]
    cbctitle    :: String,                          -- ^ overall report title
      CompoundBalanceCommandSpec -> [CBCSubreportSpec DisplayName]
    cbcqueries  :: [CBCSubreportSpec DisplayName],  -- ^ subreport details
      CompoundBalanceCommandSpec -> BalanceAccumulation
    cbcaccum    :: BalanceAccumulation              -- ^ how to accumulate balances (per-period, cumulative, historical)
                                                      --   (overrides command line flags)
    }

    -- | Generate a cmdargs option-parsing mode from a compound balance command
    -- specification.
    compoundBalanceCommandMode :: CompoundBalanceCommandSpec -> Mode RawOpts
    compoundBalanceCommandMode :: CompoundBalanceCommandSpec -> Mode RawOpts
    compoundBalanceCommandMode CompoundBalanceCommandSpec{[Char]
    [CBCSubreportSpec DisplayName]
    BalanceAccumulation
    cbcdoc :: CompoundBalanceCommandSpec -> [Char]
    cbctitle :: CompoundBalanceCommandSpec -> [Char]
    cbcqueries :: CompoundBalanceCommandSpec -> [CBCSubreportSpec DisplayName]
    cbcaccum :: CompoundBalanceCommandSpec -> BalanceAccumulation
    cbcdoc :: [Char]
    cbctitle :: [Char]
    cbcqueries :: [CBCSubreportSpec DisplayName]
    cbcaccum :: BalanceAccumulation
    ..} =
      [Char]
    -> [Flag RawOpts]
    -> [([Char], [Flag RawOpts])]
    -> [Flag RawOpts]
    -> ([Arg RawOpts], Maybe (Arg RawOpts))
    -> Mode RawOpts
    hledgerCommandMode
       [Char]
    cbcdoc
       ([[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
    flagNone [[Char]
    "sum"] ([Char] -> RawOpts -> RawOpts
    setboolopt [Char]
    "sum")
          [Char]
    "show sum of posting amounts (default)"
       ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
       ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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 "

       ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
               [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ BalanceAccumulation -> [Char]
    defaultMarker BalanceAccumulation
    PerPeriod)
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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"
               [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ BalanceAccumulation -> [Char]
    defaultMarker BalanceAccumulation
    Cumulative)
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
               [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ BalanceAccumulation -> [Char]
    defaultMarker BalanceAccumulation
    Historical [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    "\n ")
        ]
        [Flag RawOpts] -> [Flag RawOpts] -> [Flag RawOpts]
    forall a. [a] -> [a] -> [a]
    ++ Bool -> [Flag RawOpts]
    flattreeflags Bool
    True [Flag RawOpts] -> [Flag RawOpts] -> [Flag RawOpts]
    forall a. [a] -> [a] -> [a]
    ++
        [[[Char]] -> Update RawOpts -> [Char] -> [Char] -> Flag RawOpts
    forall a. [[Char]] -> Update a -> [Char] -> [Char] -> Flag a
    flagReq  [[Char]
    "drop"] (\[Char]
    s RawOpts
    opts -> RawOpts -> Either [Char] RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either [Char] RawOpts)
    -> RawOpts -> Either [Char] RawOpts
    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"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    forall a. [[Char]] -> (a -> a) -> [Char] -> Flag a
    flagNone [[Char]
    "summary-only"] ([Char] -> RawOpts -> RawOpts
    setboolopt [Char]
    "summary-only") [Char]
    "display only row summaries (e.g. row total, average) (in multicolumn reports)"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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)"
        ,[[Char]] -> Update RawOpts -> [Char] -> [Char] -> Flag RawOpts
    forall a. [[Char]] -> Update a -> [Char] -> [Char] -> Flag a
    flagReq  [[Char]
    "format"] (\[Char]
    s RawOpts
    opts -> RawOpts -> Either [Char] RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either [Char] RawOpts)
    -> RawOpts -> Either [Char] RawOpts
    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)"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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"
        ,[[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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"
        ,[[Char]] -> Update RawOpts -> [Char] -> [Char] -> Flag RawOpts
    forall a. [[Char]] -> Update a -> [Char] -> [Char] -> Flag a
    flagReq  [[Char]
    "layout"] (\[Char]
    s RawOpts
    opts -> RawOpts -> Either [Char] RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either [Char] RawOpts)
    -> RawOpts -> Either [Char] RawOpts
    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]
    "tsv",[Char]
    "json"]
        ,Flag RawOpts
    outputFileFlag
        ])
        [([Char], [Flag RawOpts])
    generalflagsgroup1]
        ([Flag RawOpts]
    hiddenflags [Flag RawOpts] -> [Flag RawOpts] -> [Flag RawOpts]
    forall a. [a] -> [a] -> [a]
    ++
          [ [[Char]] -> (RawOpts -> RawOpts) -> [Char] -> Flag RawOpts
    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"
          ])
        ([], Arg RawOpts -> Maybe (Arg RawOpts)
    forall a. a -> Maybe a
    Just (Arg RawOpts -> Maybe (Arg RawOpts))
    -> Arg RawOpts -> Maybe (Arg RawOpts)
    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 BalanceAccumulation -> BalanceAccumulation -> Bool
    forall a. Eq a => a -> a -> Bool
    == BalanceAccumulation
    cbcaccum = [Char]
    " (default)"
                          | Bool
    otherwise        = [Char]
    ""

    -- | Generate a runnable command from a compound balance command specification.
    compoundBalanceCommand :: CompoundBalanceCommandSpec -> (CliOpts -> Journal -> IO ())
    compoundBalanceCommand :: CompoundBalanceCommandSpec -> CliOpts -> Journal -> IO ()
    compoundBalanceCommand CompoundBalanceCommandSpec{[Char]
    [CBCSubreportSpec DisplayName]
    BalanceAccumulation
    cbcdoc :: CompoundBalanceCommandSpec -> [Char]
    cbctitle :: CompoundBalanceCommandSpec -> [Char]
    cbcqueries :: CompoundBalanceCommandSpec -> [CBCSubreportSpec DisplayName]
    cbcaccum :: CompoundBalanceCommandSpec -> BalanceAccumulation
    cbcdoc :: [Char]
    cbctitle :: [Char]
    cbcqueries :: [CBCSubreportSpec DisplayName]
    cbcaccum :: BalanceAccumulation
    ..} 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 (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ CompoundPeriodicReport DisplayName MixedAmount -> Text
    render (CompoundPeriodicReport DisplayName MixedAmount -> Text)
    -> CompoundPeriodicReport DisplayName MixedAmount -> Text
    forall a b. (a -> b) -> a -> b
    $ Map Text AmountStyle
    -> CompoundPeriodicReport DisplayName MixedAmount
    -> CompoundPeriodicReport DisplayName MixedAmount
    forall a. HasAmounts a => Map Text AmountStyle -> a -> a
    styleAmounts Map Text AmountStyle
    styles CompoundPeriodicReport DisplayName MixedAmount
    cbr
      where
        styles :: Map Text AmountStyle
    styles = Rounding -> Journal -> Map Text AmountStyle
    journalCommodityStylesWith Rounding
    HardRounding Journal
    j
        ropts :: ReportOpts
    ropts@ReportOpts{Bool
    Int
    [Text]
    [Status]
    Maybe Int
    Maybe Text
    Maybe NormalSign
    Maybe ValuationType
    Maybe ConversionOp
    Interval
    Period
    StringFormat
    Layout
    AccountListMode
    BalanceAccumulation
    BalanceCalculation
    period_ :: Period
    interval_ :: Interval
    statuses_ :: [Status]
    conversionop_ :: Maybe ConversionOp
    value_ :: Maybe ValuationType
    infer_prices_ :: Bool
    depth_ :: Maybe Int
    date2_ :: Bool
    empty_ :: Bool
    no_elide_ :: Bool
    real_ :: Bool
    format_ :: StringFormat
    pretty_ :: Bool
    querystring_ :: [Text]
    average_ :: Bool
    related_ :: Bool
    txn_dates_ :: Bool
    balancecalc_ :: BalanceCalculation
    balanceaccum_ :: BalanceAccumulation
    budgetpat_ :: Maybe Text
    accountlistmode_ :: AccountListMode
    drop_ :: Int
    declared_ :: Bool
    row_total_ :: Bool
    no_total_ :: Bool
    summary_only_ :: Bool
    show_costs_ :: Bool
    sort_amount_ :: Bool
    percent_ :: Bool
    invert_ :: Bool
    normalbalance_ :: Maybe NormalSign
    color_ :: Bool
    transpose_ :: Bool
    layout_ :: Layout
    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
    summary_only_ :: 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
    ..} = ReportSpec -> ReportOpts
    _rsReportOpts ReportSpec
    rspec
        -- use the default balance type for this report, unless the user overrides
        mbalanceAccumulationOverride :: Maybe BalanceAccumulation
    mbalanceAccumulationOverride = RawOpts -> Maybe BalanceAccumulation
    balanceAccumulationOverride RawOpts
    rawopts
        balanceaccumulation :: BalanceAccumulation
    balanceaccumulation = BalanceAccumulation
    -> Maybe BalanceAccumulation -> BalanceAccumulation
    forall a. a -> Maybe a -> a
    fromMaybe BalanceAccumulation
    cbcaccum Maybe BalanceAccumulation
    mbalanceAccumulationOverride
        -- Set balance type in the report options.
        ropts' :: ReportOpts
    ropts' = ReportOpts
    ropts{balanceaccum_=balanceaccumulation}

        title :: Text
    title =
          [Char] -> Text
    T.pack [Char]
    cbctitle
          Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    " "
          Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    titledatestr
          Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> (Text -> Text) -> Maybe Text -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    "" (Text
    " "Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>) Maybe Text
    mtitleclarification
          Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    valuationdesc
          where

            -- XXX #1078 the title of ending balance reports
            -- (Historical) should mention the end date(s) shown as
            -- column heading(s) (not the date span of the transactions).
            -- Also the dates should not be simplified (it should show
            -- "2008/01/01-2008/12/31", not "2008").
            titledatestr :: Text
    titledatestr = case BalanceAccumulation
    balanceaccumulation of
                BalanceAccumulation
    Historical -> [Day] -> Text
    showEndDates [Day]
    enddates
                BalanceAccumulation
    _          -> DateSpan -> Text
    showDateSpan DateSpan
    requestedspan
              where
                enddates :: [Day]
    enddates = (Day -> Day) -> [Day] -> [Day]
    forall a b. (a -> b) -> [a] -> [b]
    map (Integer -> Day -> Day
    addDays (-Integer
    1)) ([Day] -> [Day]) -> ([DateSpan] -> [Day]) -> [DateSpan] -> [Day]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (DateSpan -> Maybe Day) -> [DateSpan] -> [Day]
    forall a b. (a -> Maybe b) -> [a] -> [b]
    mapMaybe DateSpan -> Maybe Day
    spanEnd ([DateSpan] -> [Day]) -> [DateSpan] -> [Day]
    forall a b. (a -> b) -> a -> b
    $ CompoundPeriodicReport DisplayName MixedAmount -> [DateSpan]
    forall a b. CompoundPeriodicReport a b -> [DateSpan]
    cbrDates CompoundPeriodicReport DisplayName MixedAmount
    cbr  -- these spans will always have a definite end date
                requestedspan :: DateSpan
    requestedspan = (DateSpan, [DateSpan]) -> DateSpan
    forall a b. (a, b) -> a
    fst ((DateSpan, [DateSpan]) -> DateSpan)
    -> (DateSpan, [DateSpan]) -> DateSpan
    forall a b. (a -> b) -> a -> b
    $ Journal -> ReportSpec -> (DateSpan, [DateSpan])
    reportSpan Journal
    j ReportSpec
    rspec

            -- when user overrides, add an indication to the report title
            -- Do we need to deal with overridden BalanceCalculation?
            mtitleclarification :: Maybe Text
    mtitleclarification = case (BalanceCalculation
    balancecalc_, BalanceAccumulation
    balanceaccumulation, Maybe BalanceAccumulation
    mbalanceAccumulationOverride) of
                (BalanceCalculation
    CalcValueChange, BalanceAccumulation
    PerPeriod,  Maybe BalanceAccumulation
    _              ) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Period-End Value Changes)"
                (BalanceCalculation
    CalcValueChange, BalanceAccumulation
    Cumulative, Maybe BalanceAccumulation
    _              ) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Cumulative Period-End Value Changes)"
                (BalanceCalculation
    CalcGain,        BalanceAccumulation
    PerPeriod,  Maybe BalanceAccumulation
    _              ) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Incremental Gain)"
                (BalanceCalculation
    CalcGain,        BalanceAccumulation
    Cumulative, Maybe BalanceAccumulation
    _              ) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Cumulative Gain)"
                (BalanceCalculation
    CalcGain,        BalanceAccumulation
    Historical, Maybe BalanceAccumulation
    _              ) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Historical Gain)"
                (BalanceCalculation
    _,               BalanceAccumulation
    _,          Just BalanceAccumulation
    PerPeriod ) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Balance Changes)"
                (BalanceCalculation
    _,               BalanceAccumulation
    _,          Just BalanceAccumulation
    Cumulative) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Cumulative Ending Balances)"
                (BalanceCalculation
    _,               BalanceAccumulation
    _,          Just BalanceAccumulation
    Historical) -> Text -> Maybe Text
    forall a. a -> Maybe a
    Just Text
    "(Historical Ending Balances)"
                (BalanceCalculation, BalanceAccumulation,
     Maybe BalanceAccumulation)
    _                                              -> Maybe Text
    forall a. Maybe a
    Nothing

            valuationdesc :: Text
    valuationdesc =
              (case Maybe ConversionOp
    conversionop_ of
                   Just ConversionOp
    ToCost -> Text
    ", converted to cost"
                   Maybe ConversionOp
    _           -> Text
    "")
              Text -> Text -> 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 " Text -> Text -> Text
    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

        -- make a CompoundBalanceReport.
        cbr' :: CompoundPeriodicReport DisplayName MixedAmount
    cbr' = ReportSpec
    -> Journal
    -> [CBCSubreportSpec DisplayName]
    -> CompoundPeriodicReport DisplayName MixedAmount
    forall a.
    ReportSpec
    -> Journal
    -> [CBCSubreportSpec a]
    -> CompoundPeriodicReport a MixedAmount
    compoundBalanceReport ReportSpec
    rspec{_rsReportOpts=ropts'} Journal
    j [CBCSubreportSpec DisplayName]
    cbcqueries
        cbr :: CompoundPeriodicReport DisplayName MixedAmount
    cbr  = CompoundPeriodicReport DisplayName MixedAmount
    cbr'{cbrTitle=title}

        -- render appropriately
        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"  -> [[Text]] -> Text
    printCSV ([[Text]] -> Text)
    -> (CompoundPeriodicReport DisplayName MixedAmount -> [[Text]])
    -> CompoundPeriodicReport DisplayName MixedAmount
    -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts
    -> CompoundPeriodicReport DisplayName MixedAmount -> [[Text]]
    compoundBalanceReportAsCsv ReportOpts
    ropts'
          [Char]
    "tsv"  -> [[Text]] -> Text
    printTSV ([[Text]] -> Text)
    -> (CompoundPeriodicReport DisplayName MixedAmount -> [[Text]])
    -> CompoundPeriodicReport DisplayName MixedAmount
    -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts
    -> CompoundPeriodicReport DisplayName MixedAmount -> [[Text]]
    compoundBalanceReportAsCsv ReportOpts
    ropts'
          [Char]
    "html" -> HtmlT Identity () -> Text
    forall a. Html a -> Text
    L.renderText (HtmlT Identity () -> Text)
    -> (CompoundPeriodicReport DisplayName MixedAmount
        -> HtmlT Identity ())
    -> CompoundPeriodicReport DisplayName MixedAmount
    -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts
    -> CompoundPeriodicReport DisplayName MixedAmount
    -> HtmlT Identity ()
    compoundBalanceReportAsHtml ReportOpts
    ropts'
          [Char]
    "json" -> CompoundPeriodicReport DisplayName MixedAmount -> Text
    forall a. ToJSON a => a -> Text
    toJsonText
          [Char]
    x      -> [Char] -> CompoundPeriodicReport DisplayName MixedAmount -> Text
    forall a. [Char] -> a
    error' ([Char] -> CompoundPeriodicReport DisplayName MixedAmount -> Text)
    -> [Char] -> CompoundPeriodicReport DisplayName MixedAmount -> Text
    forall a b. (a -> b) -> a -> b
    $ [Char] -> [Char]
    unsupportedOutputFormatError [Char]
    x

    -- | Summarise one or more (inclusive) end dates, in a way that's
    -- visually different from showDateSpan, suggesting discrete end dates
    -- rather than a continuous span.
    showEndDates :: [Day] -> T.Text
    showEndDates :: [Day] -> Text
    showEndDates [Day]
    es = case [Day]
    es of
      -- cf showPeriod
      (Day
    e:Day
    _:[Day]
    _) -> Day -> Text
    showDate Day
    e Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ".." Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Day -> Text
    showDate ([Day] -> Day
    forall a. HasCallStack => [a] -> a
    last [Day]
    es)
      [Day
    e]     -> Day -> Text
    showDate Day
    e
      []      -> Text
    ""

    -- | Render a compound balance report as plain text suitable for console output.
    {- Eg:
    Balance Sheet

                 ||  2017/12/31    Total  Average
    =============++===============================
     Assets      ||
    -------------++-------------------------------
     assets:b    ||           1        1        1
    -------------++-------------------------------
                 ||           1        1        1
    =============++===============================
     Liabilities ||
    -------------++-------------------------------
    -------------++-------------------------------
                 ||
    =============++===============================
     Total       ||           1        1        1

    -}
    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 (Builder -> Text) -> Builder -> Text
    forall a b. (a -> b) -> a -> b
    $
          Text -> Builder
    TB.fromText Text
    title Builder -> Builder -> Builder
    forall a. Semigroup a => a -> a -> a
    <> Text -> Builder
    TB.fromText Text
    "\n\n" Builder -> Builder -> Builder
    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 ((Text, PeriodicReport DisplayName MixedAmount, Bool)
     -> Table Text Text WideBuilder)
    -> [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    -> [Table Text Text WideBuilder]
    forall a b. (a -> b) -> [a] -> [b]
    map (ReportOpts
    -> (Text, PeriodicReport DisplayName MixedAmount, Bool)
    -> Table Text Text WideBuilder
    forall {c}.
    ReportOpts
    -> (Text, PeriodicReport DisplayName MixedAmount, c)
    -> Table Text Text WideBuilder
    subreportAsTable ReportOpts
    ropts) [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports of
            []   -> Table Text Text WideBuilder
    forall rh ch a. Table rh ch a
    Tab.empty
            Table Text Text WideBuilder
    r:[Table Text Text WideBuilder]
    rs -> (Table Text Text WideBuilder
     -> Table Text Text WideBuilder -> Table Text Text WideBuilder)
    -> Table Text Text WideBuilder
    -> [Table Text Text WideBuilder]
    -> Table Text Text WideBuilder
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl' (Properties
    -> Table Text Text WideBuilder
    -> Table Text Text WideBuilder
    -> Table Text Text WideBuilder
    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
    || [(Text, PeriodicReport DisplayName MixedAmount, Bool)] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 =
              Table Text Text WideBuilder
    bigtable
          | Bool
    otherwise =
            let totalrows :: [[WideBuilder]]
    totalrows = ReportOpts -> PeriodicReportRow () MixedAmount -> [[WideBuilder]]
    forall a.
    ReportOpts -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
    multiBalanceRowAsTableText ReportOpts
    ropts PeriodicReportRow () MixedAmount
    netrow
                rh :: Header Text
    rh = Properties -> [Header Text] -> Header Text
    forall h. Properties -> [Header h] -> Header h
    Tab.Group Properties
    NoLine ([Header Text] -> Header Text) -> [Header Text] -> Header Text
    forall a b. (a -> b) -> a -> b
    $ (Text -> Header Text) -> [Text] -> [Header Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> Header Text
    forall h. h -> Header h
    Header (Text
    "Net:" Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : Int -> Text -> [Text]
    forall a. Int -> a -> [a]
    replicate ([[WideBuilder]] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [[WideBuilder]]
    totalrows Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) Text
    "")
                ch :: Header [a]
    ch = [a] -> Header [a]
    forall h. h -> Header h
    Header [] -- ignored
             in ((Properties
    -> Table Text Text WideBuilder
    -> Table Text [Any] WideBuilder
    -> Table Text Text WideBuilder
    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 (Table Text [Any] WideBuilder -> Table Text Text WideBuilder)
    -> Table Text [Any] WideBuilder -> Table Text Text WideBuilder
    forall a b. (a -> b) -> a -> b
    $ Header Text
    -> Header [Any] -> [[WideBuilder]] -> Table Text [Any] WideBuilder
    forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
    Table Header Text
    rh Header [Any]
    forall {a}. Header [a]
    ch [[WideBuilder]]
    totalrows)

        -- | Convert a named multi balance report to a table suitable for
        -- concatenating with others to make a compound balance report table.
        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
            -- convert to table
            Table Header Text
    lefthdrs Header Text
    tophdrs [[WideBuilder]]
    cells = ReportOpts
    -> PeriodicReport DisplayName MixedAmount
    -> Table Text Text WideBuilder
    balanceReportAsTable ReportOpts
    ropts1 PeriodicReport DisplayName MixedAmount
    r
            -- tweak the layout
            t :: Table Text Text WideBuilder
    t = Header Text
    -> Header Text -> [[WideBuilder]] -> Table Text Text WideBuilder
    forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
    Table (Properties -> [Header Text] -> Header Text
    forall h. Properties -> [Header h] -> Header h
    Tab.Group Properties
    Tab.SingleLine [Text -> Header Text
    forall h. h -> Header h
    Tab.Header Text
    title1, Header Text
    lefthdrs]) Header Text
    tophdrs ([][WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
    forall a. a -> [a] -> [a]
    :[[WideBuilder]]
    cells)

    -- | Render a compound balance report as CSV.
    -- Subreports' CSV is concatenated, with the headings rows replaced by a
    -- subreport title row, and an overall title row, one headings row, and an
    -- optional overall totals row is added.
    compoundBalanceReportAsCsv :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> CSV
    compoundBalanceReportAsCsv :: ReportOpts
    -> CompoundPeriodicReport DisplayName MixedAmount -> [[Text]]
    compoundBalanceReportAsCsv ReportOpts
    ropts (CompoundPeriodicReport Text
    title [DateSpan]
    colspans [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports PeriodicReportRow () MixedAmount
    netrow) =
        [[Text]] -> [[Text]]
    addtotals ([[Text]] -> [[Text]]) -> [[Text]] -> [[Text]]
    forall a b. (a -> b) -> a -> b
    $
          Text -> [Text]
    forall {a}. IsString a => a -> [a]
    padRow Text
    title
          [Text] -> [[Text]] -> [[Text]]
    forall a. a -> [a] -> [a]
    : ( Text
    "Account"
            Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : [Text
    "Commodity" | ReportOpts -> Layout
    layout_ ReportOpts
    ropts Layout -> Layout -> Bool
    forall a. Eq a => a -> a -> Bool
    == Layout
    LayoutBare]
            [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (DateSpan -> Text) -> [DateSpan] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (BalanceAccumulation -> [DateSpan] -> DateSpan -> Text
    reportPeriodName (ReportOpts -> BalanceAccumulation
    balanceaccum_ ReportOpts
    ropts) [DateSpan]
    colspans) [DateSpan]
    colspans
            [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (if ReportOpts -> Bool
    row_total_ ReportOpts
    ropts then [Text
    "Total"] else [])
            [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (if ReportOpts -> Bool
    average_ ReportOpts
    ropts then [Text
    "Average"] else [])
            )
          [Text] -> [[Text]] -> [[Text]]
    forall a. a -> [a] -> [a]
    : ((Text, PeriodicReport DisplayName MixedAmount, Bool) -> [[Text]])
    -> [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    -> [[Text]]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap (ReportOpts
    -> (Text, PeriodicReport DisplayName MixedAmount, Bool) -> [[Text]]
    forall {c}.
    ReportOpts
    -> (Text, PeriodicReport DisplayName MixedAmount, c) -> [[Text]]
    subreportAsCsv ReportOpts
    ropts) [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports
      where
        -- | Add a subreport title row and drop the heading row.
        subreportAsCsv :: ReportOpts
    -> (Text, PeriodicReport DisplayName MixedAmount, c) -> [[Text]]
    subreportAsCsv ReportOpts
    ropts1 (Text
    subreporttitle, PeriodicReport DisplayName MixedAmount
    multibalreport, c
    _) =
          Text -> [Text]
    forall {a}. IsString a => a -> [a]
    padRow Text
    subreporttitle [Text] -> [[Text]] -> [[Text]]
    forall a. a -> [a] -> [a]
    :
          [[Text]] -> [[Text]]
    forall a. HasCallStack => [a] -> [a]
    tail (ReportOpts -> PeriodicReport DisplayName MixedAmount -> [[Text]]
    multiBalanceReportAsCsv ReportOpts
    ropts1 PeriodicReport DisplayName MixedAmount
    multibalreport)
        padRow :: a -> [a]
    padRow a
    s = Int -> [a] -> [a]
    forall a. Int -> [a] -> [a]
    take Int
    numcols ([a] -> [a]) -> [a] -> [a]
    forall a b. (a -> b) -> a -> b
    $ a
    s a -> [a] -> [a]
    forall a. a -> [a] -> [a]
    : a -> [a]
    forall a. a -> [a]
    repeat a
    ""
          where
            numcols :: Int
    numcols
              | [(Text, PeriodicReport DisplayName MixedAmount, Bool)] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports = Int
    1
              | Bool
    otherwise =
                (Int
    1 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    +) (Int -> Int) -> Int -> Int
    forall a b. (a -> b) -> a -> b
    $ -- account name column
                (if ReportOpts -> Layout
    layout_ ReportOpts
    ropts Layout -> Layout -> Bool
    forall a. Eq a => a -> a -> Bool
    == Layout
    LayoutBare then (Int
    1Int -> Int -> Int
    forall a. Num a => a -> a -> a
    +) else Int -> Int
    forall a. a -> a
    id) (Int -> Int) -> Int -> Int
    forall a b. (a -> b) -> a -> b
    $
                (if ReportOpts -> Bool
    row_total_ ReportOpts
    ropts then (Int
    1Int -> Int -> Int
    forall a. Num a => a -> a -> a
    +) else Int -> Int
    forall a. a -> a
    id) (Int -> Int) -> Int -> Int
    forall a b. (a -> b) -> a -> b
    $
                (if ReportOpts -> Bool
    average_ ReportOpts
    ropts then (Int
    1Int -> Int -> Int
    forall a. Num a => a -> a -> a
    +) else Int -> Int
    forall a. a -> a
    id) (Int -> Int) -> Int -> Int
    forall a b. (a -> b) -> a -> b
    $
                [Int] -> Int
    forall a. Ord a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    maximum ([Int] -> Int) -> [Int] -> Int
    forall a b. (a -> b) -> a -> b
    $ -- depends on non-null subreports
                ((Text, PeriodicReport DisplayName MixedAmount, Bool) -> Int)
    -> [(Text, PeriodicReport DisplayName MixedAmount, Bool)] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map ([DateSpan] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length ([DateSpan] -> Int)
    -> ((Text, PeriodicReport DisplayName MixedAmount, Bool)
        -> [DateSpan])
    -> (Text, PeriodicReport DisplayName MixedAmount, Bool)
    -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PeriodicReport DisplayName MixedAmount -> [DateSpan]
    forall a b. PeriodicReport a b -> [DateSpan]
    prDates (PeriodicReport DisplayName MixedAmount -> [DateSpan])
    -> ((Text, PeriodicReport DisplayName MixedAmount, Bool)
        -> PeriodicReport DisplayName MixedAmount)
    -> (Text, PeriodicReport DisplayName MixedAmount, Bool)
    -> [DateSpan]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text, PeriodicReport DisplayName MixedAmount, Bool)
    -> PeriodicReport DisplayName MixedAmount
    forall {a} {b} {c}. (a, b, c) -> b
    second3) [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports
        addtotals :: [[Text]] -> [[Text]]
    addtotals
          | ReportOpts -> Bool
    no_total_ ReportOpts
    ropts Bool -> Bool -> Bool
    || [(Text, PeriodicReport DisplayName MixedAmount, Bool)] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 = [[Text]] -> [[Text]]
    forall a. a -> a
    id
          | Bool
    otherwise = ([[Text]] -> [[Text]] -> [[Text]]
    forall a. [a] -> [a] -> [a]
    ++ ([Text] -> [Text]) -> [[Text]] -> [[Text]]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Text
    "Net:" Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : ) (ReportOpts
    -> [DateSpan] -> PeriodicReportRow () MixedAmount -> [[Text]]
    forall a.
    ReportOpts
    -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[Text]]
    multiBalanceRowAsCsvText ReportOpts
    ropts [DateSpan]
    colspans PeriodicReportRow () MixedAmount
    netrow))

    -- | Render a compound balance report as HTML.
    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_ (Text -> Attribute) -> Text -> Attribute
    forall a b. (a -> b) -> a -> b
    $ [Char] -> Text
    T.pack ([Char] -> Text) -> [Char] -> Text
    forall a b. (a -> b) -> a -> b
    $ Int -> [Char]
    forall a. Show a => a -> [Char]
    show (Int -> [Char]) -> Int -> [Char]
    forall a b. (a -> b) -> a -> b
    $ [Int] -> Int
    forall a. Num a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
    sum [
          Int
    1,
          [DateSpan] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [DateSpan]
    colspans,
          if ReportOpts -> Bool
    row_total_ ReportOpts
    ropts then Int
    1 else Int
    0,
          if ReportOpts -> Bool
    average_ ReportOpts
    ropts then Int
    1 else Int
    0,
          if ReportOpts -> Layout
    layout_ ReportOpts
    ropts Layout -> Layout -> Bool
    forall a. Eq a => a -> a -> Bool
    == Layout
    LayoutBare then Int
    1 else Int
    0
          ]
        leftattr :: Attribute
    leftattr = Text -> Attribute
    forall arg result. TermRaw arg result => arg -> result
    style_ Text
    "text-align:left"
        blankrow :: HtmlT Identity ()
    blankrow = HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    tr_ (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ [Attribute] -> HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    td_ [Attribute
    colspanattr] (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ [Char] -> HtmlT Identity ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => [Char] -> HtmlT m ()
    toHtmlRaw ([Char]
    "&nbsp;"::String)

        titlerows :: [HtmlT Identity ()]
    titlerows =
          (HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    tr_ (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ [Attribute] -> HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    th_ [Attribute
    colspanattr, Attribute
    leftattr] (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    h2_ (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ Text -> HtmlT Identity ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    title)
          HtmlT Identity () -> [HtmlT Identity ()] -> [HtmlT Identity ()]
    forall a. a -> [a] -> [a]
    : [[Text] -> HtmlT Identity ()
    thRow ([Text] -> HtmlT Identity ()) -> [Text] -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $
             Text
    "" Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : [Text
    "Commodity" | ReportOpts -> Layout
    layout_ ReportOpts
    ropts Layout -> Layout -> Bool
    forall a. Eq a => a -> a -> Bool
    == Layout
    LayoutBare] [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++
             (DateSpan -> Text) -> [DateSpan] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (BalanceAccumulation -> [DateSpan] -> DateSpan -> Text
    reportPeriodName (ReportOpts -> BalanceAccumulation
    balanceaccum_ ReportOpts
    ropts) [DateSpan]
    colspans) [DateSpan]
    colspans
             [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (if ReportOpts -> Bool
    row_total_ ReportOpts
    ropts then [Text
    "Total"] else [])
             [Text] -> [Text] -> [Text]
    forall a. [a] -> [a] -> [a]
    ++ (if ReportOpts -> Bool
    average_ ReportOpts
    ropts then [Text
    "Average"] else [])
            ]

        thRow :: [T.Text] -> Html ()
        thRow :: [Text] -> HtmlT Identity ()
    thRow = HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    tr_ (HtmlT Identity () -> HtmlT Identity ())
    -> ([Text] -> HtmlT Identity ()) -> [Text] -> HtmlT Identity ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [HtmlT Identity ()] -> HtmlT Identity ()
    forall a. Monoid a => [a] -> a
    mconcat ([HtmlT Identity ()] -> HtmlT Identity ())
    -> ([Text] -> [HtmlT Identity ()]) -> [Text] -> HtmlT Identity ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text -> HtmlT Identity ()) -> [Text] -> [HtmlT Identity ()]
    forall a b. (a -> b) -> [a] -> [b]
    map (HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    th_ (HtmlT Identity () -> HtmlT Identity ())
    -> (Text -> HtmlT Identity ()) -> Text -> HtmlT Identity ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> HtmlT Identity ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml)

        -- Make rows for a subreport: its title row, not the headings row,
        -- the data rows, any totals row, and a blank row for whitespace.
        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
               [HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    tr_ (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ [Attribute] -> HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    th_ [Attribute
    colspanattr, Attribute
    leftattr] (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ Text -> HtmlT Identity ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    subreporttitle]
            [HtmlT Identity ()] -> [HtmlT Identity ()] -> [HtmlT Identity ()]
    forall a. [a] -> [a] -> [a]
    ++ [HtmlT Identity ()]
    bodyrows
            [HtmlT Identity ()] -> [HtmlT Identity ()] -> [HtmlT Identity ()]
    forall a. [a] -> [a] -> [a]
    ++ [HtmlT Identity ()]
    mtotalsrows
            [HtmlT Identity ()] -> [HtmlT Identity ()] -> [HtmlT Identity ()]
    forall a. [a] -> [a] -> [a]
    ++ [HtmlT Identity ()
    blankrow]

        totalrows :: [HtmlT Identity ()]
    totalrows | ReportOpts -> Bool
    no_total_ ReportOpts
    ropts Bool -> Bool -> Bool
    || [(Text, PeriodicReport DisplayName MixedAmount, Bool)] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    subreports Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1 = []
          | Bool
    otherwise = ReportOpts -> [Text] -> HtmlT Identity ()
    multiBalanceReportHtmlFootRow ReportOpts
    ropts ([Text] -> HtmlT Identity ()) -> [[Text]] -> [HtmlT Identity ()]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> ((Text
    "Net:" Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    :) ([Text] -> [Text]) -> [[Text]] -> [[Text]]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> ReportOpts
    -> [DateSpan] -> PeriodicReportRow () MixedAmount -> [[Text]]
    forall a.
    ReportOpts
    -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[Text]]
    multiBalanceRowAsCsvText ReportOpts
    ropts [DateSpan]
    colspans PeriodicReportRow () MixedAmount
    netrow)
      in do
        Text -> HtmlT Identity ()
    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; }"
          ])
        [Attribute] -> HtmlT Identity ()
    forall (m :: * -> *). Applicative m => [Attribute] -> HtmlT m ()
    link_ [Text -> Attribute
    rel_ Text
    "stylesheet", Text -> Attribute
    href_ Text
    "hledger.css"]
        HtmlT Identity () -> HtmlT Identity ()
    forall arg result. Term arg result => arg -> result
    table_ (HtmlT Identity () -> HtmlT Identity ())
    -> HtmlT Identity () -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $ [HtmlT Identity ()] -> HtmlT Identity ()
    forall a. Monoid a => [a] -> a
    mconcat ([HtmlT Identity ()] -> HtmlT Identity ())
    -> [HtmlT Identity ()] -> HtmlT Identity ()
    forall a b. (a -> b) -> a -> b
    $
             [HtmlT Identity ()]
    titlerows
          [HtmlT Identity ()] -> [HtmlT Identity ()] -> [HtmlT Identity ()]
    forall a. [a] -> [a] -> [a]
    ++ [HtmlT Identity ()
    blankrow]
          [HtmlT Identity ()] -> [HtmlT Identity ()] -> [HtmlT Identity ()]
    forall a. [a] -> [a] -> [a]
    ++ ((Text, PeriodicReport DisplayName MixedAmount, Bool)
     -> [HtmlT Identity ()])
    -> [(Text, PeriodicReport DisplayName MixedAmount, Bool)]
    -> [HtmlT Identity ()]
    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
          [HtmlT Identity ()] -> [HtmlT Identity ()] -> [HtmlT Identity ()]
    forall a. [a] -> [a] -> [a]
    ++ [HtmlT Identity ()]
    totalrows