    {-|

    A ledger-compatible @balance@ command, with additional support for
    multi-column reports.

    Here is a description/specification for the balance command.  See also
    "Hledger.Reports" -> \"Balance reports\".

    /Basic balance report/

    With no report interval (@--monthly@ etc.), hledger's balance
    command emulates ledger's, showing accounts indented according to
    hierarchy, along with their total amount posted (including subaccounts).

    Here's an example. With @examples/sample.journal@, which defines the following account tree:

    @
     assets
       bank
         checking
         saving
       cash
     expenses
       food
       supplies
     income
       gifts
       salary
     liabilities
       debts
    @

    the basic @balance@ command gives this output:

    @
     $ hledger -f sample.journal balance
                     $-1  assets
                      $1    bank:saving
                     $-2    cash
                      $2  expenses
                      $1    food
                      $1    supplies
                     $-2  income
                     $-1    gifts
                     $-1    salary
                      $1  liabilities:debts
    --------------------
                       0
    @

    Subaccounts are displayed indented below their parent. Only the account leaf name (the final part) is shown.
    (With @--flat@, account names are shown in full and unindented.)

    Each account's \"balance\" is the sum of postings in that account and any subaccounts during the report period.
    When the report period includes all transactions, this is equivalent to the account's current balance.

    The overall total of the highest-level displayed accounts is shown below the line.
    (The @--no-total/-N@ flag prevents this.)

    /Eliding and omitting/

    Accounts which have a zero balance, and no non-zero subaccount
    balances, are normally omitted from the report.
    (The @--empty/-E@ flag forces such accounts to be displayed.)
    Eg, above @checking@ is omitted because it has a zero balance and no subaccounts.

    Accounts which have a single subaccount also being displayed, with the same balance,
    are normally elided into the subaccount's line.
    (The @--no-elide@ flag prevents this.)
    Eg, above @bank@ is elided to @bank:saving@ because it has only a
    single displayed subaccount (@saving@) and their balance is the same
    ($1). Similarly, @liabilities@ is elided to @liabilities:debts@.

    /Date limiting/

    The default report period is that of the whole journal, including all
    known transactions. The @--begin\/-b@, @--end\/-e@, @--period\/-p@
    options or @date:@/@date2:@ patterns can be used to report only
    on transactions before and/or after specified dates.

    /Depth limiting/

    The @--depth@ option can be used to limit the depth of the balance report.
    Eg, to see just the top level accounts (still including their subaccount balances):

    @
    $ hledger -f sample.journal balance --depth 1
                     $-1  assets
                      $2  expenses
                     $-2  income
                      $1  liabilities
    --------------------
                       0
    @

    /Account limiting/

    With one or more account pattern arguments, the report is restricted
    to accounts whose name matches one of the patterns, plus their parents
    and subaccounts. Eg, adding the pattern @o@ to the first example gives:

    @
     $ hledger -f sample.journal balance o
                      $1  expenses:food
                     $-2  income
                     $-1    gifts
                     $-1    salary
    --------------------
                     $-1
    @

    * The @o@ pattern matched @food@ and @income@, so they are shown.

    * @food@'s parent (@expenses@) is shown even though the pattern didn't
      match it, to clarify the hierarchy. The usual eliding rules cause it to be elided here.

    * @income@'s subaccounts are also shown.

    /Multi-column balance report/

    hledger's balance command will show multiple columns when a reporting
    interval is specified (eg with @--monthly@), one column for each sub-period.

    There are three accumulation strategies for multi-column balance report, indicated by
    the heading:

    * A \"period balance\" (or \"flow\") report (with @--change@, the default) shows the
      change of account balance in each period, which is equivalent to the sum of postings
      in each period. Here, checking's balance increased by 10 in Feb:

      > Change of balance (flow):
      >
      >                  Jan   Feb   Mar
      > assets:checking   20    10    -5

    * A \"cumulative balance\" report (with @--cumulative@) shows the accumulated ending balance
      across periods, starting from zero at the report's start date.
      Here, 30 is the sum of checking postings during Jan and Feb:

      > Ending balance (cumulative):
      >
      >                  Jan   Feb   Mar
      > assets:checking   20    30    25

    * A \"historical balance\" report (with @--historical/-H@) also shows ending balances,
      but it includes the starting balance from any postings before the report start date.
      Here, 130 is the balance from all checking postings at the end of Feb, including
      pre-Jan postings which created a starting balance of 100:

      > Ending balance (historical):
      >
      >                  Jan   Feb   Mar
      > assets:checking  120   130   125

    /Eliding and omitting, 2/

    Here's a (imperfect?) specification for the eliding/omitting behaviour:

    * Each account is normally displayed on its own line.

    * An account less deep than the report's max depth, with just one
    interesting subaccount, and the same balance as the subaccount, is
    non-interesting, and prefixed to the subaccount's line, unless
    @--no-elide@ is in effect.

    * An account with a zero inclusive balance and less than two interesting
    subaccounts is not displayed at all, unless @--empty@ is in effect.

    * Multi-column balance reports show full account names with no eliding
      (like @--flat@). Accounts (and periods) are omitted as described below.

    /Which accounts to show in balance reports/

    By default:

    * single-column: accounts with non-zero balance in report period.
                     (With @--flat@: accounts with non-zero balance and postings.)

    * change:        accounts with postings and non-zero period balance in any period

    * cumulative:    accounts with non-zero cumulative balance in any period

    * historical:    accounts with non-zero historical balance in any period

    With @-E/--empty@:

    * single-column: accounts with postings in report period

    * change:        accounts with postings in report period

    * cumulative:    accounts with postings in report period

    * historical:    accounts with non-zero starting balance +
                     accounts with postings in report period

    /Which periods (columns) to show in balance reports/

    An empty period/column is one where no report account has any postings.
    A zero period/column is one where no report account has a non-zero period balance.

    Currently,

    by default:

    * single-column: N/A

    * change:        all periods within the overall report period,
                     except for leading and trailing empty periods

    * cumulative:    all periods within the overall report period,
                     except for leading and trailing empty periods

    * historical:    all periods within the overall report period,
                     except for leading and trailing empty periods

    With @-E/--empty@:

    * single-column: N/A

    * change:        all periods within the overall report period

    * cumulative:    all periods within the overall report period

    * historical:    all periods within the overall report period

    /What to show in empty cells/

    An empty periodic balance report cell is one which has no corresponding postings.
    An empty cumulative/historical balance report cell is one which has no corresponding
    or prior postings, ie the account doesn't exist yet.
    Currently, empty cells show 0.

    -}

    {-# LANGUAGE ExtendedDefaultRules #-}
    {-# LANGUAGE NamedFieldPuns       #-}
    {-# LANGUAGE OverloadedStrings    #-}
    {-# LANGUAGE RecordWildCards      #-}
    {-# LANGUAGE ScopedTypeVariables  #-}
    {-# LANGUAGE TemplateHaskell      #-}

    module Hledger.Cli.Commands.Balance (
      balancemode
     ,balance
     ,balanceReportAsText
     ,balanceReportAsCsv
     ,balanceReportItemAsText
     ,multiBalanceRowAsCsvText
     ,multiBalanceRowAsTableText
     ,multiBalanceReportAsText
     ,multiBalanceReportAsCsv
     ,multiBalanceReportAsHtml
     ,multiBalanceReportHtmlRows
     ,multiBalanceReportHtmlFootRow
     ,balanceReportAsTable
     ,balanceReportTableAsText
     ,tests_Balance
    ) where

    import Data.Default (def)
    import Data.List (transpose, transpose)
    import qualified Data.Set as S
    import Data.Maybe (fromMaybe)
    import qualified Data.Text as T
    import qualified Data.Text.Lazy as TL
    import qualified Data.Text.Lazy.Builder as TB
    import Data.Time (addDays, fromGregorian)
    import System.Console.CmdArgs.Explicit as C
    import Lucid as L hiding (value_)
    import Safe (headMay, maximumMay)
    import Text.Tabular.AsciiWide
        (Align(..), Cell(..), Table(..), TableOpts(..), cellWidth, concatTables,
        renderColumns, renderRowB, textCell)
    import qualified Text.Tabular.AsciiWide as Tab

    import Hledger
    import Hledger.Cli.CliOptions
    import Hledger.Cli.Utils
    import Hledger.Read.CsvUtils (CSV, printCSV, printTSV)

    -- | Command line options for this command.
    balancemode :: Mode RawOpts
    balancemode = CommandDoc
    -> [Flag RawOpts]
    -> [(CommandDoc, [Flag RawOpts])]
    -> [Flag RawOpts]
    -> ([Arg RawOpts], Maybe (Arg RawOpts))
    -> Mode RawOpts
    hledgerCommandMode
      $(embedFileRelative "Hledger/Cli/Commands/Balance.txt")
      (
        -- https://hledger.org/dev/hledger.html#calculation-type :
        [[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "sum"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "sum")
          CommandDoc
    "show sum of posting amounts (default)"
        -- XXX --budget[=DESCPAT], --forecast[=PERIODEXP], could be more consistent
        ,CommandDoc
    -> [CommandDoc]
    -> Update RawOpts
    -> CommandDoc
    -> CommandDoc
    -> Flag RawOpts
    forall a.
    CommandDoc
    -> [CommandDoc] -> Update a -> CommandDoc -> CommandDoc -> Flag a
    flagOpt CommandDoc
    "" [CommandDoc
    "budget"] (\CommandDoc
    s RawOpts
    opts -> RawOpts -> Either CommandDoc RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either CommandDoc RawOpts)
    -> RawOpts -> Either CommandDoc RawOpts
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> CommandDoc -> RawOpts -> RawOpts
    setopt CommandDoc
    "budget" CommandDoc
    s RawOpts
    opts) CommandDoc
    "DESCPAT"
          ([CommandDoc] -> CommandDoc
    unlines
          [ CommandDoc
    "show sum of posting amounts together with budget goals defined by periodic"
          , CommandDoc
    "transactions. With a DESCPAT argument (must be separated by = not space),"
          , CommandDoc
    "use only periodic transactions with matching description"
          , CommandDoc
    "(case insensitive substring match)."
          ])
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "valuechange"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "valuechange")
          CommandDoc
    "show total change of value of period-end historical balances (caused by deposits, withdrawals, market price fluctuations)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "gain"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "gain")
          CommandDoc
    "show unrealised capital gain/loss (historical balance value minus cost basis)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "count"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "count") CommandDoc
    "show the count of postings"
        -- https://hledger.org/dev/hledger.html#accumulation-type :
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "change"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "change")
          CommandDoc
    "accumulate amounts from column start to column end (in multicolumn reports, default)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "cumulative"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "cumulative")
          CommandDoc
    "accumulate amounts from report start (specified by e.g. -b/--begin) to column end"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "historical",CommandDoc
    "H"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "historical")
          CommandDoc
    "accumulate amounts from journal start to column end (includes postings before report start date)\n "
        ]
        -- other options specific to this command:
        [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]
    ++
        [[CommandDoc]
    -> Update RawOpts -> CommandDoc -> CommandDoc -> Flag RawOpts
    forall a.
    [CommandDoc] -> Update a -> CommandDoc -> CommandDoc -> Flag a
    flagReq  [CommandDoc
    "drop"] (\CommandDoc
    s RawOpts
    opts -> RawOpts -> Either CommandDoc RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either CommandDoc RawOpts)
    -> RawOpts -> Either CommandDoc RawOpts
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> CommandDoc -> RawOpts -> RawOpts
    setopt CommandDoc
    "drop" CommandDoc
    s RawOpts
    opts) CommandDoc
    "N" CommandDoc
    "omit N leading account name parts (in flat mode)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "declared"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "declared") CommandDoc
    "include non-parent declared accounts (best used with -E)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "average",CommandDoc
    "A"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "average") CommandDoc
    "show a row average column (in multicolumn reports)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "related",CommandDoc
    "r"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "related") CommandDoc
    "show postings' siblings instead"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "row-total",CommandDoc
    "T"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "row-total") CommandDoc
    "show a row total column (in multicolumn reports)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "summary-only"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "summary-only") CommandDoc
    "display only row summaries (e.g. row total, average) (in multicolumn reports)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "no-total",CommandDoc
    "N"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "no-total") CommandDoc
    "omit the final total row"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "no-elide"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "no-elide") CommandDoc
    "don't squash boring parent accounts (in tree mode)"
        ,[CommandDoc]
    -> Update RawOpts -> CommandDoc -> CommandDoc -> Flag RawOpts
    forall a.
    [CommandDoc] -> Update a -> CommandDoc -> CommandDoc -> Flag a
    flagReq  [CommandDoc
    "format"] (\CommandDoc
    s RawOpts
    opts -> RawOpts -> Either CommandDoc RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either CommandDoc RawOpts)
    -> RawOpts -> Either CommandDoc RawOpts
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> CommandDoc -> RawOpts -> RawOpts
    setopt CommandDoc
    "format" CommandDoc
    s RawOpts
    opts) CommandDoc
    "FORMATSTR" CommandDoc
    "use this custom line format (in simple reports)"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "sort-amount",CommandDoc
    "S"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "sort-amount") CommandDoc
    "sort by amount instead of account code/name (in flat mode). With multiple columns, sorts by the row total, or by row average if that is displayed."
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "percent", CommandDoc
    "%"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "percent") CommandDoc
    "express values in percentage of each column's total"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "invert"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "invert") CommandDoc
    "display all amounts with reversed sign"
        ,[CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "transpose"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "transpose") CommandDoc
    "transpose rows and columns"
        ,[CommandDoc]
    -> Update RawOpts -> CommandDoc -> CommandDoc -> Flag RawOpts
    forall a.
    [CommandDoc] -> Update a -> CommandDoc -> CommandDoc -> Flag a
    flagReq  [CommandDoc
    "layout"] (\CommandDoc
    s RawOpts
    opts -> RawOpts -> Either CommandDoc RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either CommandDoc RawOpts)
    -> RawOpts -> Either CommandDoc RawOpts
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> CommandDoc -> RawOpts -> RawOpts
    setopt CommandDoc
    "layout" CommandDoc
    s RawOpts
    opts) CommandDoc
    "ARG"
          ([CommandDoc] -> CommandDoc
    unlines
            [CommandDoc
    "how to lay out multi-commodity amounts and the overall table:"
            ,CommandDoc
    "'wide[,WIDTH]': commodities on one line"
            ,CommandDoc
    "'tall'        : commodities on separate lines"
            ,CommandDoc
    "'bare'        : commodity symbols in one column"
            ,CommandDoc
    "'tidy'        : every attribute in its own column"
            ])
        -- output:
        ,[CommandDoc] -> Flag RawOpts
    outputFormatFlag [CommandDoc
    "txt",CommandDoc
    "html",CommandDoc
    "csv",CommandDoc
    "tsv",CommandDoc
    "json"]
        ,Flag RawOpts
    outputFileFlag
        ]
      )
      [(CommandDoc, [Flag RawOpts])
    generalflagsgroup1]
      ([Flag RawOpts]
    hiddenflags [Flag RawOpts] -> [Flag RawOpts] -> [Flag RawOpts]
    forall a. [a] -> [a] -> [a]
    ++
        [ [CommandDoc] -> (RawOpts -> RawOpts) -> CommandDoc -> Flag RawOpts
    forall a. [CommandDoc] -> (a -> a) -> CommandDoc -> Flag a
    flagNone [CommandDoc
    "commodity-column"] (CommandDoc -> RawOpts -> RawOpts
    setboolopt CommandDoc
    "commodity-column")
          CommandDoc
    "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
    $ CommandDoc -> Arg RawOpts
    argsFlag CommandDoc
    "[QUERY]")

    -- | The balance command, prints a balance report.
    balance :: CliOpts -> Journal -> IO ()
    balance :: CliOpts -> Journal -> IO ()
    balance opts :: CliOpts
    opts@CliOpts{reportspec_ :: CliOpts -> ReportSpec
    reportspec_=ReportSpec
    rspec} Journal
    j = case BalanceCalculation
    balancecalc_ of
        BalanceCalculation
    CalcBudget -> do  -- single or multi period budget report
          let rspan :: DateSpan
    rspan = (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
              budgetreport :: BudgetReport
    budgetreport = Map Text AmountStyle -> BudgetReport -> BudgetReport
    forall a. HasAmounts a => Map Text AmountStyle -> a -> a
    styleAmounts Map Text AmountStyle
    styles (BudgetReport -> BudgetReport) -> BudgetReport -> BudgetReport
    forall a b. (a -> b) -> a -> b
    $ ReportSpec -> BalancingOpts -> DateSpan -> Journal -> BudgetReport
    budgetReport ReportSpec
    rspec (InputOpts -> BalancingOpts
    balancingopts_ (InputOpts -> BalancingOpts) -> InputOpts -> BalancingOpts
    forall a b. (a -> b) -> a -> b
    $ CliOpts -> InputOpts
    inputopts_ CliOpts
    opts) DateSpan
    rspan Journal
    j
              render :: BudgetReport -> Text
    render = case CommandDoc
    fmt of
                CommandDoc
    "txt"  -> ReportOpts -> BudgetReport -> Text
    budgetReportAsText ReportOpts
    ropts
                CommandDoc
    "json" -> (Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>Text
    "\n") (Text -> Text) -> (BudgetReport -> Text) -> BudgetReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . BudgetReport -> Text
    forall a. ToJSON a => a -> Text
    toJsonText
                CommandDoc
    "csv"  -> [CsvRecord] -> Text
    printCSV ([CsvRecord] -> Text)
    -> (BudgetReport -> [CsvRecord]) -> BudgetReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> BudgetReport -> [CsvRecord]
    budgetReportAsCsv ReportOpts
    ropts
                CommandDoc
    "tsv"  -> [CsvRecord] -> Text
    printTSV ([CsvRecord] -> Text)
    -> (BudgetReport -> [CsvRecord]) -> BudgetReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> BudgetReport -> [CsvRecord]
    budgetReportAsCsv ReportOpts
    ropts
                CommandDoc
    _      -> CommandDoc -> BudgetReport -> Text
    forall a. CommandDoc -> a
    error' (CommandDoc -> BudgetReport -> Text)
    -> CommandDoc -> BudgetReport -> Text
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> CommandDoc
    unsupportedOutputFormatError CommandDoc
    fmt
          CliOpts -> Text -> IO ()
    writeOutputLazyText CliOpts
    opts (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ BudgetReport -> Text
    render BudgetReport
    budgetreport

        BalanceCalculation
    _ | Bool
    multiperiod -> do  -- multi period balance report
            let report :: MultiBalanceReport
    report = Map Text AmountStyle -> MultiBalanceReport -> MultiBalanceReport
    forall a. HasAmounts a => Map Text AmountStyle -> a -> a
    styleAmounts Map Text AmountStyle
    styles (MultiBalanceReport -> MultiBalanceReport)
    -> MultiBalanceReport -> MultiBalanceReport
    forall a b. (a -> b) -> a -> b
    $ ReportSpec -> Journal -> MultiBalanceReport
    multiBalanceReport ReportSpec
    rspec Journal
    j
                render :: MultiBalanceReport -> Text
    render = case CommandDoc
    fmt of
                  CommandDoc
    "txt"  -> ReportOpts -> MultiBalanceReport -> Text
    multiBalanceReportAsText ReportOpts
    ropts
                  CommandDoc
    "csv"  -> [CsvRecord] -> Text
    printCSV ([CsvRecord] -> Text)
    -> (MultiBalanceReport -> [CsvRecord])
    -> MultiBalanceReport
    -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> MultiBalanceReport -> [CsvRecord]
    multiBalanceReportAsCsv ReportOpts
    ropts
                  CommandDoc
    "tsv"  -> [CsvRecord] -> Text
    printTSV ([CsvRecord] -> Text)
    -> (MultiBalanceReport -> [CsvRecord])
    -> MultiBalanceReport
    -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> MultiBalanceReport -> [CsvRecord]
    multiBalanceReportAsCsv ReportOpts
    ropts
                  CommandDoc
    "html" -> (Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>Text
    "\n") (Text -> Text)
    -> (MultiBalanceReport -> Text) -> MultiBalanceReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Html () -> Text
    forall a. Html a -> Text
    L.renderText (Html () -> Text)
    -> (MultiBalanceReport -> Html ()) -> MultiBalanceReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> MultiBalanceReport -> Html ()
    multiBalanceReportAsHtml ReportOpts
    ropts
                  CommandDoc
    "json" -> (Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>Text
    "\n") (Text -> Text)
    -> (MultiBalanceReport -> Text) -> MultiBalanceReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MultiBalanceReport -> Text
    forall a. ToJSON a => a -> Text
    toJsonText
                  CommandDoc
    _      -> Text -> MultiBalanceReport -> Text
    forall a b. a -> b -> a
    const (Text -> MultiBalanceReport -> Text)
    -> Text -> MultiBalanceReport -> Text
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> Text
    forall a. CommandDoc -> a
    error' (CommandDoc -> Text) -> CommandDoc -> Text
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> CommandDoc
    unsupportedOutputFormatError CommandDoc
    fmt  -- PARTIAL:
            CliOpts -> Text -> IO ()
    writeOutputLazyText CliOpts
    opts (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ MultiBalanceReport -> Text
    render MultiBalanceReport
    report

        BalanceCalculation
    _ -> do  -- single period simple balance report
            let report :: BalanceReport
    report = Map Text AmountStyle -> BalanceReport -> BalanceReport
    forall a. HasAmounts a => Map Text AmountStyle -> a -> a
    styleAmounts Map Text AmountStyle
    styles (BalanceReport -> BalanceReport) -> BalanceReport -> BalanceReport
    forall a b. (a -> b) -> a -> b
    $ ReportSpec -> Journal -> BalanceReport
    balanceReport ReportSpec
    rspec Journal
    j -- simple Ledger-style balance report
                render :: ReportOpts -> BalanceReport -> Text
    render = case CommandDoc
    fmt of
                  CommandDoc
    "txt"  -> \ReportOpts
    ropts1 -> Builder -> Text
    TB.toLazyText (Builder -> Text)
    -> (BalanceReport -> Builder) -> BalanceReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> BalanceReport -> Builder
    balanceReportAsText ReportOpts
    ropts1
                  CommandDoc
    "csv"  -> \ReportOpts
    ropts1 -> [CsvRecord] -> Text
    printCSV ([CsvRecord] -> Text)
    -> (BalanceReport -> [CsvRecord]) -> BalanceReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> BalanceReport -> [CsvRecord]
    balanceReportAsCsv ReportOpts
    ropts1
                  CommandDoc
    "tsv"  -> \ReportOpts
    ropts1 -> [CsvRecord] -> Text
    printTSV ([CsvRecord] -> Text)
    -> (BalanceReport -> [CsvRecord]) -> BalanceReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ReportOpts -> BalanceReport -> [CsvRecord]
    balanceReportAsCsv ReportOpts
    ropts1
                  -- "html" -> \ropts -> (<>"\n") . L.renderText . multiBalanceReportAsHtml ropts . balanceReportAsMultiBalanceReport ropts
                  CommandDoc
    "json" -> (BalanceReport -> Text) -> ReportOpts -> BalanceReport -> Text
    forall a b. a -> b -> a
    const ((BalanceReport -> Text) -> ReportOpts -> BalanceReport -> Text)
    -> (BalanceReport -> Text) -> ReportOpts -> BalanceReport -> Text
    forall a b. (a -> b) -> a -> b
    $ (Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>Text
    "\n") (Text -> Text) -> (BalanceReport -> Text) -> BalanceReport -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . BalanceReport -> Text
    forall a. ToJSON a => a -> Text
    toJsonText
                  CommandDoc
    _      -> CommandDoc -> ReportOpts -> BalanceReport -> Text
    forall a. CommandDoc -> a
    error' (CommandDoc -> ReportOpts -> BalanceReport -> Text)
    -> CommandDoc -> ReportOpts -> BalanceReport -> Text
    forall a b. (a -> b) -> a -> b
    $ CommandDoc -> CommandDoc
    unsupportedOutputFormatError CommandDoc
    fmt  -- PARTIAL:
            CliOpts -> Text -> IO ()
    writeOutputLazyText CliOpts
    opts (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ ReportOpts -> BalanceReport -> Text
    render ReportOpts
    ropts BalanceReport
    report
      where
        styles :: Map Text AmountStyle
    styles = Rounding -> Journal -> Map Text AmountStyle
    journalCommodityStylesWith Rounding
    HardRounding Journal
    j
        ropts :: ReportOpts
    ropts@ReportOpts{Bool
    Int
    CsvRecord
    [Status]
    Maybe Int
    Maybe Text
    Maybe NormalSign
    Maybe ValuationType
    Maybe ConversionOp
    Interval
    Period
    StringFormat
    Layout
    AccountListMode
    BalanceAccumulation
    BalanceCalculation
    balancecalc_ :: 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_ :: CsvRecord
    average_ :: Bool
    related_ :: Bool
    txn_dates_ :: Bool
    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 -> CsvRecord
    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
        -- Tidy csv/tsv should be consistent between single period and multiperiod reports.
        multiperiod :: Bool
    multiperiod = Interval
    interval_ Interval -> Interval -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Interval
    NoInterval Bool -> Bool -> Bool
    || (Layout
    layout_ Layout -> Layout -> Bool
    forall a. Eq a => a -> a -> Bool
    == Layout
    LayoutTidy Bool -> Bool -> Bool
    && Bool
    delimited)
        delimited :: Bool
    delimited   = CommandDoc
    fmt CommandDoc -> CommandDoc -> Bool
    forall a. Eq a => a -> a -> Bool
    == CommandDoc
    "csv" Bool -> Bool -> Bool
    || CommandDoc
    fmt CommandDoc -> CommandDoc -> Bool
    forall a. Eq a => a -> a -> Bool
    == CommandDoc
    "tsv"
        fmt :: CommandDoc
    fmt         = CliOpts -> CommandDoc
    outputFormatFromOpts CliOpts
    opts

    -- XXX this allows rough HTML rendering of a flat BalanceReport, but it can't handle tree mode etc.
    -- -- | Convert a BalanceReport to a MultiBalanceReport.
    -- balanceReportAsMultiBalanceReport :: ReportOpts -> BalanceReport -> MultiBalanceReport
    -- balanceReportAsMultiBalanceReport _ropts (britems, brtotal) =
    --   let
    --     mbrrows =
    --       [PeriodicReportRow{
    --           prrName    = flatDisplayName brfullname
    --         , prrAmounts = [bramt]
    --         , prrTotal   = bramt
    --         , prrAverage = bramt
    --         }
    --       | (brfullname, _, _, bramt) <- britems
    --       ]
    --   in
    --     PeriodicReport{
    --         prDates  = [nulldatespan]
    --       , prRows   = mbrrows
    --       , prTotals = PeriodicReportRow{
    --            prrName=()
    --           ,prrAmounts=[brtotal]
    --           ,prrTotal=brtotal
    --           ,prrAverage=brtotal
    --           }
    --       }

    -- XXX should all the per-report, per-format rendering code live in the command module,
    -- like the below, or in the report module, like budgetReportAsText/budgetReportAsCsv ?

    -- rendering single-column balance reports

    -- | Render a single-column balance report as CSV.
    balanceReportAsCsv :: ReportOpts -> BalanceReport -> CSV
    balanceReportAsCsv :: ReportOpts -> BalanceReport -> [CsvRecord]
    balanceReportAsCsv ReportOpts
    opts ([BalanceReportItem]
    items, Change
    total) =
        CsvRecord
    headers CsvRecord -> [CsvRecord] -> [CsvRecord]
    forall a. a -> [a] -> [a]
    : (BalanceReportItem -> [CsvRecord])
    -> [BalanceReportItem] -> [CsvRecord]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap (\(Text
    a, Text
    _, Int
    _, Change
    b) -> Text -> Change -> [CsvRecord]
    rows Text
    a Change
    b) [BalanceReportItem]
    items [CsvRecord] -> [CsvRecord] -> [CsvRecord]
    forall a. [a] -> [a] -> [a]
    ++ if ReportOpts -> Bool
    no_total_ ReportOpts
    opts then [] else Text -> Change -> [CsvRecord]
    rows Text
    "total" Change
    total
      where
        headers :: CsvRecord
    headers = Text
    "account" Text -> CsvRecord -> CsvRecord
    forall a. a -> [a] -> [a]
    : case ReportOpts -> Layout
    layout_ ReportOpts
    opts of
          Layout
    LayoutBare -> [Text
    "commodity", Text
    "balance"]
          Layout
    _          -> [Text
    "balance"]
        rows :: AccountName -> MixedAmount -> [[T.Text]]
        rows :: Text -> Change -> [CsvRecord]
    rows Text
    name Change
    ma = case ReportOpts -> Layout
    layout_ ReportOpts
    opts of
          Layout
    LayoutBare ->
              (Amount -> CsvRecord) -> [Amount] -> [CsvRecord]
    forall a b. (a -> b) -> [a] -> [b]
    map (\Amount
    a -> [Text -> Text
    showName Text
    name, Amount -> Text
    acommodity Amount
    a, Change -> Text
    renderAmount (Change -> Text) -> Change -> Text
    forall a b. (a -> b) -> a -> b
    $ Amount -> Change
    mixedAmount Amount
    a])
              ([Amount] -> [CsvRecord])
    -> (Change -> [Amount]) -> Change -> [CsvRecord]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Change -> [Amount]
    amounts (Change -> [CsvRecord]) -> Change -> [CsvRecord]
    forall a b. (a -> b) -> a -> b
    $ Change -> Change
    mixedAmountStripPrices Change
    ma
          Layout
    _ -> [[Text -> Text
    showName Text
    name, Change -> Text
    renderAmount Change
    ma]]

        showName :: Text -> Text
    showName = Int -> Text -> Text
    accountNameDrop (ReportOpts -> Int
    drop_ ReportOpts
    opts)
        renderAmount :: Change -> Text
    renderAmount Change
    amt = WideBuilder -> Text
    wbToText (WideBuilder -> Text) -> WideBuilder -> Text
    forall a b. (a -> b) -> a -> b
    $ AmountDisplayOpts -> Change -> WideBuilder
    showMixedAmountB AmountDisplayOpts
    bopts Change
    amt
          where
            bopts :: AmountDisplayOpts
    bopts = AmountDisplayOpts
    csvDisplay{displayCommodity=showcomm, displayCommodityOrder = commorder}
            (Bool
    showcomm, Maybe CsvRecord
    commorder)
              | ReportOpts -> Layout
    layout_ ReportOpts
    opts Layout -> Layout -> Bool
    forall a. Eq a => a -> a -> Bool
    == Layout
    LayoutBare = (Bool
    False, CsvRecord -> Maybe CsvRecord
    forall a. a -> Maybe a
    Just (CsvRecord -> Maybe CsvRecord) -> CsvRecord -> Maybe CsvRecord
    forall a b. (a -> b) -> a -> b
    $ Set Text -> CsvRecord
    forall a. Set a -> [a]
    S.toList (Set Text -> CsvRecord) -> Set Text -> CsvRecord
    forall a b. (a -> b) -> a -> b
    $ Change -> Set Text
    maCommodities Change
    amt)
              | Bool
    otherwise                  = (Bool
    True, Maybe CsvRecord
    forall a. Maybe a
    Nothing)

    -- | Render a single-column balance report as plain text.
    balanceReportAsText :: ReportOpts -> BalanceReport -> TB.Builder
    balanceReportAsText :: ReportOpts -> BalanceReport -> Builder
    balanceReportAsText ReportOpts
    opts (([BalanceReportItem]
    items, Change
    total)) = case ReportOpts -> Layout
    layout_ ReportOpts
    opts of
        Layout
    LayoutBare | Bool
    iscustom -> CommandDoc -> Builder
    forall a. CommandDoc -> a
    error' CommandDoc
    "Custom format not supported with commodity columns"  -- PARTIAL:
        Layout
    LayoutBare -> ReportOpts -> BalanceReport -> Builder
    balanceReportAsText' ReportOpts
    opts (([BalanceReportItem]
    items, Change
    total))
        Layout
    _ -> [Builder] -> Builder
    unlinesB [Builder]
    ls Builder -> Builder -> Builder
    forall a. Semigroup a => a -> a -> a
    <> [Builder] -> Builder
    unlinesB (if ReportOpts -> Bool
    no_total_ ReportOpts
    opts then [] else [Builder
    overline, Builder
    totalLines])
      where
        ([Builder]
    ls, [[Int]]
    sizes) = [(Builder, [Int])] -> ([Builder], [[Int]])
    forall a b. [(a, b)] -> ([a], [b])
    unzip ([(Builder, [Int])] -> ([Builder], [[Int]]))
    -> [(Builder, [Int])] -> ([Builder], [[Int]])
    forall a b. (a -> b) -> a -> b
    $ (BalanceReportItem -> (Builder, [Int]))
    -> [BalanceReportItem] -> [(Builder, [Int])]
    forall a b. (a -> b) -> [a] -> [b]
    map (ReportOpts -> BalanceReportItem -> (Builder, [Int])
    balanceReportItemAsText ReportOpts
    opts) [BalanceReportItem]
    items
        -- abuse renderBalanceReportItem to render the total with similar format
        (Builder
    totalLines, [Int]
    _) = ReportOpts -> (Text, Int, Change) -> (Builder, [Int])
    renderBalanceReportItem ReportOpts
    opts (Text
    "",Int
    0,Change
    total)
        -- with a custom format, extend the line to the full report width;
        -- otherwise show the usual 20-char line for compatibility
        iscustom :: Bool
    iscustom = case ReportOpts -> StringFormat
    format_ ReportOpts
    opts of
            OneLine       ((FormatField Bool
    _ Maybe Int
    _ Maybe Int
    _ ReportItemField
    TotalField):[StringFormatComponent]
    _) -> Bool
    False
            TopAligned    ((FormatField Bool
    _ Maybe Int
    _ Maybe Int
    _ ReportItemField
    TotalField):[StringFormatComponent]
    _) -> Bool
    False
            BottomAligned ((FormatField Bool
    _ Maybe Int
    _ Maybe Int
    _ ReportItemField
    TotalField):[StringFormatComponent]
    _) -> Bool
    False
            StringFormat
    _ -> Bool
    True
        overlinewidth :: Int
    overlinewidth = if Bool
    iscustom then [Int] -> Int
    forall a. Num a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
    sum (([Int] -> Int) -> [[Int]] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map [Int] -> Int
    forall a. Integral a => [a] -> a
    maximum' ([[Int]] -> [Int]) -> [[Int]] -> [Int]
    forall a b. (a -> b) -> a -> b
    $ [[Int]] -> [[Int]]
    forall a. [[a]] -> [[a]]
    transpose [[Int]]
    sizes) else Int
    20
        overline :: Builder
    overline   = Text -> Builder
    TB.fromText (Text -> Builder) -> Text -> Builder
    forall a b. (a -> b) -> a -> b
    $ Int -> Text -> Text
    T.replicate Int
    overlinewidth Text
    "-"

    -- | Render a single-column balance report as plain text in commodity-column mode
    balanceReportAsText' :: ReportOpts -> BalanceReport -> TB.Builder
    balanceReportAsText' :: ReportOpts -> BalanceReport -> Builder
    balanceReportAsText' ReportOpts
    opts (([BalanceReportItem]
    items, Change
    total)) =
      [Builder] -> Builder
    unlinesB ([Builder] -> Builder)
    -> ([[Cell]] -> [Builder]) -> [[Cell]] -> Builder
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ([Cell] -> Builder) -> [[Cell]] -> [Builder]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (TableOpts -> [Int] -> Header Cell -> Builder
    renderColumns TableOpts
    forall a. Default a => a
    def{tableBorders=False} [Int]
    sizes (Header Cell -> Builder)
    -> ([Cell] -> Header Cell) -> [Cell] -> Builder
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    .  Properties -> [Header Cell] -> Header Cell
    forall h. Properties -> [Header h] -> Header h
    Tab.Group Properties
    Tab.NoLine ([Header Cell] -> Header Cell)
    -> ([Cell] -> [Header Cell]) -> [Cell] -> Header Cell
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Cell -> Header Cell) -> [Cell] -> [Header Cell]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Cell -> Header Cell
    forall h. h -> Header h
    Tab.Header) ([[Cell]] -> Builder) -> [[Cell]] -> Builder
    forall a b. (a -> b) -> a -> b
    $
        [[Cell]]
    ls [[Cell]] -> [[Cell]] -> [[Cell]]
    forall a. [a] -> [a] -> [a]
    ++ [[[Cell]]] -> [[Cell]]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [[[Cell
    overline], [Cell]
    totalline] | Bool -> Bool
    not (ReportOpts -> Bool
    no_total_ ReportOpts
    opts)]
      where
        render :: (a, Text, Int, Change) -> [Cell]
    render (a
    _, Text
    acctname, Int
    dep, Change
    amt) =
            [ Align -> [WideBuilder] -> Cell
    Cell Align
    TopRight [WideBuilder]
    damts
            , Align -> [WideBuilder] -> Cell
    Cell Align
    TopLeft ((Text -> WideBuilder) -> CsvRecord -> [WideBuilder]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Text -> WideBuilder
    wbFromText CsvRecord
    cs)
            , Align -> [WideBuilder] -> Cell
    Cell Align
    TopLeft (Int -> WideBuilder -> [WideBuilder]
    forall a. Int -> a -> [a]
    replicate ([WideBuilder] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [WideBuilder]
    damts Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) WideBuilder
    forall a. Monoid a => a
    mempty [WideBuilder] -> [WideBuilder] -> [WideBuilder]
    forall a. [a] -> [a] -> [a]
    ++ [Text -> WideBuilder
    wbFromText Text
    dispname]) ]
          where dopts :: AmountDisplayOpts
    dopts = AmountDisplayOpts
    oneLine{displayCommodity=layout_ opts /= LayoutBare, displayCommodityOrder=Just cs, displayColour=color_ opts}
                cs :: CsvRecord
    cs    = if Change -> Bool
    mixedAmountLooksZero Change
    amt then [Text
    ""] else Set Text -> CsvRecord
    forall a. Set a -> [a]
    S.toList (Set Text -> CsvRecord) -> Set Text -> CsvRecord
    forall a b. (a -> b) -> a -> b
    $ Change -> Set Text
    maCommodities Change
    amt
                dispname :: Text
    dispname = Int -> Text -> Text
    T.replicate ((Int
    dep Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    2) Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    acctname
                damts :: [WideBuilder]
    damts = AmountDisplayOpts -> Change -> [WideBuilder]
    showMixedAmountLinesB AmountDisplayOpts
    dopts Change
    amt
        ls :: [[Cell]]
    ls = (BalanceReportItem -> [Cell]) -> [BalanceReportItem] -> [[Cell]]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap BalanceReportItem -> [Cell]
    forall {a}. (a, Text, Int, Change) -> [Cell]
    render [BalanceReportItem]
    items
        totalline :: [Cell]
    totalline = (CommandDoc, Text, Int, Change) -> [Cell]
    forall {a}. (a, Text, Int, Change) -> [Cell]
    render (CommandDoc
    "", Text
    "", Int
    0, Change
    total)
        sizes :: [Int]
    sizes = Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe Int
    0 (Maybe Int -> Int) -> ([Cell] -> Maybe Int) -> [Cell] -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Int] -> Maybe Int
    forall a. Ord a => [a] -> Maybe a
    maximumMay ([Int] -> Maybe Int) -> ([Cell] -> [Int]) -> [Cell] -> Maybe Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Cell -> Int) -> [Cell] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map Cell -> Int
    cellWidth ([Cell] -> Int) -> [[Cell]] -> [Int]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$>
            [[Cell]] -> [[Cell]]
    forall a. [[a]] -> [[a]]
    transpose ([[Cell]
    totalline | Bool -> Bool
    not (ReportOpts -> Bool
    no_total_ ReportOpts
    opts)] [[Cell]] -> [[Cell]] -> [[Cell]]
    forall a. [a] -> [a] -> [a]
    ++ [[Cell]]
    ls)
        overline :: Cell
    overline = Align -> [WideBuilder] -> Cell
    Cell Align
    TopLeft ([WideBuilder] -> Cell)
    -> (Maybe Int -> [WideBuilder]) -> Maybe Int -> Cell
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . WideBuilder -> [WideBuilder]
    forall a. a -> [a]
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (WideBuilder -> [WideBuilder])
    -> (Maybe Int -> WideBuilder) -> Maybe Int -> [WideBuilder]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> WideBuilder
    wbFromText (Text -> WideBuilder)
    -> (Maybe Int -> Text) -> Maybe Int -> WideBuilder
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Int -> Text -> Text) -> Text -> Int -> Text
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Int -> Text -> Text
    T.replicate Text
    "-" (Int -> Text) -> (Maybe Int -> Int) -> Maybe Int -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe Int
    0 (Maybe Int -> Cell) -> Maybe Int -> Cell
    forall a b. (a -> b) -> a -> b
    $ [Int] -> Maybe Int
    forall a. [a] -> Maybe a
    headMay [Int]
    sizes

    {-
    :r
    This implementation turned out to be a bit convoluted but implements the following algorithm for formatting:

    - If there is a single amount, print it with the account name directly:
    - Otherwise, only print the account name on the last line.

        a         USD 1   ; Account 'a' has a single amount
                  EUR -1
        b         USD -1  ; Account 'b' has two amounts. The account name is printed on the last line.
    -}
    -- | Render one balance report line item as plain text suitable for console output (or
    -- whatever string format is specified). Note, prices will not be rendered, and
    -- differently-priced quantities of the same commodity will appear merged.
    -- The output will be one or more lines depending on the format and number of commodities.
    balanceReportItemAsText :: ReportOpts -> BalanceReportItem -> (TB.Builder, [Int])
    balanceReportItemAsText :: ReportOpts -> BalanceReportItem -> (Builder, [Int])
    balanceReportItemAsText ReportOpts
    opts (Text
    _, Text
    accountName, Int
    dep, Change
    amt) =
      ReportOpts -> (Text, Int, Change) -> (Builder, [Int])
    renderBalanceReportItem ReportOpts
    opts (Text
    accountName, Int
    dep, Change
    amt)

    -- | Render a balance report item using the given StringFormat, generating one or more lines of text.
    renderBalanceReportItem :: ReportOpts -> (AccountName, Int, MixedAmount) -> (TB.Builder, [Int])
    renderBalanceReportItem :: ReportOpts -> (Text, Int, Change) -> (Builder, [Int])
    renderBalanceReportItem ReportOpts
    opts (Text
    acctname, Int
    dep, Change
    total) =
      case ReportOpts -> StringFormat
    format_ ReportOpts
    opts of
          OneLine       [StringFormatComponent]
    comps -> [Cell] -> (Builder, [Int])
    renderRow' ([Cell] -> (Builder, [Int])) -> [Cell] -> (Builder, [Int])
    forall a b. (a -> b) -> a -> b
    $ Bool -> Bool -> [StringFormatComponent] -> [Cell]
    render Bool
    True  Bool
    True  [StringFormatComponent]
    comps
          TopAligned    [StringFormatComponent]
    comps -> [Cell] -> (Builder, [Int])
    renderRow' ([Cell] -> (Builder, [Int])) -> [Cell] -> (Builder, [Int])
    forall a b. (a -> b) -> a -> b
    $ Bool -> Bool -> [StringFormatComponent] -> [Cell]
    render Bool
    True  Bool
    False [StringFormatComponent]
    comps
          BottomAligned [StringFormatComponent]
    comps -> [Cell] -> (Builder, [Int])
    renderRow' ([Cell] -> (Builder, [Int])) -> [Cell] -> (Builder, [Int])
    forall a b. (a -> b) -> a -> b
    $ Bool -> Bool -> [StringFormatComponent] -> [Cell]
    render Bool
    False Bool
    False [StringFormatComponent]
    comps
      where
        renderRow' :: [Cell] -> (Builder, [Int])
    renderRow' [Cell]
    is = ( TableOpts -> Header Cell -> Builder
    renderRowB TableOpts
    forall a. Default a => a
    def{tableBorders=False, borderSpaces=False}
                          (Header Cell -> Builder)
    -> ([Header Cell] -> Header Cell) -> [Header Cell] -> Builder
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Properties -> [Header Cell] -> Header Cell
    forall h. Properties -> [Header h] -> Header h
    Tab.Group Properties
    Tab.NoLine ([Header Cell] -> Builder) -> [Header Cell] -> Builder
    forall a b. (a -> b) -> a -> b
    $ (Cell -> Header Cell) -> [Cell] -> [Header Cell]
    forall a b. (a -> b) -> [a] -> [b]
    map Cell -> Header Cell
    forall h. h -> Header h
    Tab.Header [Cell]
    is
                        , (Cell -> Int) -> [Cell] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map Cell -> Int
    cellWidth [Cell]
    is )

        render :: Bool -> Bool -> [StringFormatComponent] -> [Cell]
    render Bool
    topaligned Bool
    oneline = (StringFormatComponent -> Cell)
    -> [StringFormatComponent] -> [Cell]
    forall a b. (a -> b) -> [a] -> [b]
    map (Bool
    -> Bool
    -> ReportOpts
    -> (Text, Int, Change)
    -> StringFormatComponent
    -> Cell
    renderComponent Bool
    topaligned Bool
    oneline ReportOpts
    opts (Text
    acctname, Int
    dep, Change
    total))

    -- | Render one StringFormat component for a balance report item.
    renderComponent :: Bool -> Bool -> ReportOpts -> (AccountName, Int, MixedAmount) -> StringFormatComponent -> Cell
    renderComponent :: Bool
    -> Bool
    -> ReportOpts
    -> (Text, Int, Change)
    -> StringFormatComponent
    -> Cell
    renderComponent Bool
    _ Bool
    _ ReportOpts
    _ (Text, Int, Change)
    _ (FormatLiteral Text
    s) = Align -> Text -> Cell
    textCell Align
    TopLeft Text
    s
    renderComponent Bool
    topaligned Bool
    oneline ReportOpts
    opts (Text
    acctname, Int
    dep, Change
    total) (FormatField Bool
    ljust Maybe Int
    mmin Maybe Int
    mmax ReportItemField
    field) = case ReportItemField
    field of
        ReportItemField
    DepthSpacerField -> Align -> [WideBuilder] -> Cell
    Cell Align
    align [Builder -> Int -> WideBuilder
    WideBuilder (Text -> Builder
    TB.fromText (Text -> Builder) -> Text -> Builder
    forall a b. (a -> b) -> a -> b
    $ Int -> Text -> Text
    T.replicate Int
    d Text
    " ") Int
    d]
                            where d :: Int
    d = (Int -> Int) -> (Int -> Int -> Int) -> Maybe Int -> Int -> Int
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Int -> Int
    forall a. a -> a
    id Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    min Maybe Int
    mmax (Int -> Int) -> Int -> Int
    forall a b. (a -> b) -> a -> b
    $ Int
    dep Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int -> Maybe Int -> Int
    forall a. a -> Maybe a -> a
    fromMaybe Int
    1 Maybe Int
    mmin
        ReportItemField
    AccountField     -> Align -> Text -> Cell
    textCell Align
    align (Text -> Cell) -> Text -> Cell
    forall a b. (a -> b) -> a -> b
    $ Bool -> Maybe Int -> Maybe Int -> Text -> Text
    formatText Bool
    ljust Maybe Int
    mmin Maybe Int
    mmax Text
    acctname
        ReportItemField
    TotalField       -> Align -> [WideBuilder] -> Cell
    Cell Align
    align ([WideBuilder] -> Cell)
    -> (WideBuilder -> [WideBuilder]) -> WideBuilder -> Cell
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . WideBuilder -> [WideBuilder]
    forall a. a -> [a]
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (WideBuilder -> Cell) -> WideBuilder -> Cell
    forall a b. (a -> b) -> a -> b
    $ AmountDisplayOpts -> Change -> WideBuilder
    showMixedAmountB AmountDisplayOpts
    dopts Change
    total
        ReportItemField
    _                -> Align -> [WideBuilder] -> Cell
    Cell Align
    align [WideBuilder
    forall a. Monoid a => a
    mempty]
      where
        align :: Align
    align | Bool
    topaligned Bool -> Bool -> Bool
    && Bool
    ljust = Align
    TopLeft
              | Bool
    topaligned          = Align
    TopRight
              | Bool
    ljust               = Align
    BottomLeft
              | Bool
    otherwise           = Align
    BottomRight
        dopts :: AmountDisplayOpts
    dopts = AmountDisplayOpts
    noCost{displayCommodity = layout_ opts /= LayoutBare
                      ,displayOneLine   = oneline
                      ,displayMinWidth  = mmin
                      ,displayMaxWidth  = mmax
                      ,displayColour    = color_ opts
                      }

    -- rendering multi-column balance reports

    -- | Render a multi-column balance report as CSV.
    -- The CSV will always include the initial headings row,
    -- and will include the final totals row unless --no-total is set.
    multiBalanceReportAsCsv :: ReportOpts -> MultiBalanceReport -> CSV
    multiBalanceReportAsCsv :: ReportOpts -> MultiBalanceReport -> [CsvRecord]
    multiBalanceReportAsCsv opts :: ReportOpts
    opts@ReportOpts{Bool
    Int
    CsvRecord
    [Status]
    Maybe Int
    Maybe Text
    Maybe NormalSign
    Maybe ValuationType
    Maybe ConversionOp
    Interval
    Period
    StringFormat
    Layout
    AccountListMode
    BalanceAccumulation
    BalanceCalculation
    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 -> CsvRecord
    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
    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_ :: CsvRecord
    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
    ..} MultiBalanceReport
    report = [CsvRecord] -> [CsvRecord]
    forall a. [[a]] -> [[a]]
    maybeTranspose [CsvRecord]
    allRows
      where
        allRows :: [CsvRecord]
    allRows = case Layout
    layout_ of
          Layout
    LayoutTidy -> [CsvRecord]
    rows  -- tidy csv should not include totals or averages
          Layout
    _ -> [CsvRecord]
    rows [CsvRecord] -> [CsvRecord] -> [CsvRecord]
    forall a. [a] -> [a] -> [a]
    ++ [CsvRecord]
    totals
        ([CsvRecord]
    rows, [CsvRecord]
    totals) = ReportOpts -> MultiBalanceReport -> ([CsvRecord], [CsvRecord])
    multiBalanceReportAsCsv' ReportOpts
    opts MultiBalanceReport
    report
        maybeTranspose :: [[a]] -> [[a]]
    maybeTranspose = if Bool
    transpose_ then [[a]] -> [[a]]
    forall a. [[a]] -> [[a]]
    transpose else [[a]] -> [[a]]
    forall a. a -> a
    id

    multiBalanceReportAsCsv' :: ReportOpts -> MultiBalanceReport -> (CSV, CSV)
    multiBalanceReportAsCsv' :: ReportOpts -> MultiBalanceReport -> ([CsvRecord], [CsvRecord])
    multiBalanceReportAsCsv' opts :: ReportOpts
    opts@ReportOpts{Bool
    Int
    CsvRecord
    [Status]
    Maybe Int
    Maybe Text
    Maybe NormalSign
    Maybe ValuationType
    Maybe ConversionOp
    Interval
    Period
    StringFormat
    Layout
    AccountListMode
    BalanceAccumulation
    BalanceCalculation
    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 -> CsvRecord
    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
    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_ :: CsvRecord
    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
    ..} (PeriodicReport [DateSpan]
    colspans [PeriodicReportRow DisplayName Change]
    items PeriodicReportRow () Change
    tr) =
        (CsvRecord
    headers CsvRecord -> [CsvRecord] -> [CsvRecord]
    forall a. a -> [a] -> [a]
    : (PeriodicReportRow DisplayName Change -> [CsvRecord])
    -> [PeriodicReportRow DisplayName Change] -> [CsvRecord]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap PeriodicReportRow DisplayName Change -> [CsvRecord]
    fullRowAsTexts [PeriodicReportRow DisplayName Change]
    items, [CsvRecord]
    totalrows)
      where
        headers :: CsvRecord
    headers = Text
    "account" Text -> CsvRecord -> CsvRecord
    forall a. a -> [a] -> [a]
    : case Layout
    layout_ of
          Layout
    LayoutTidy -> [Text
    "period", Text
    "start_date", Text
    "end_date", Text
    "commodity", Text
    "value"]
          Layout
    LayoutBare -> Text
    "commodity" Text -> CsvRecord -> CsvRecord
    forall a. a -> [a] -> [a]
    : CsvRecord
    dateHeaders
          Layout
    _          -> CsvRecord
    dateHeaders
        dateHeaders :: CsvRecord
    dateHeaders = (DateSpan -> Text) -> [DateSpan] -> CsvRecord
    forall a b. (a -> b) -> [a] -> [b]
    map DateSpan -> Text
    showDateSpan [DateSpan]
    colspans CsvRecord -> CsvRecord -> CsvRecord
    forall a. [a] -> [a] -> [a]
    ++ [Text
    "total" | Bool
    row_total_] CsvRecord -> CsvRecord -> CsvRecord
    forall a. [a] -> [a] -> [a]
    ++ [Text
    "average" | Bool
    average_]

        fullRowAsTexts :: PeriodicReportRow DisplayName Change -> [CsvRecord]
    fullRowAsTexts PeriodicReportRow DisplayName Change
    row = (CsvRecord -> CsvRecord) -> [CsvRecord] -> [CsvRecord]
    forall a b. (a -> b) -> [a] -> [b]
    map (PeriodicReportRow DisplayName Change -> Text
    forall {a}. PeriodicReportRow DisplayName a -> Text
    showName PeriodicReportRow DisplayName Change
    row Text -> CsvRecord -> CsvRecord
    forall a. a -> [a] -> [a]
    :) ([CsvRecord] -> [CsvRecord]) -> [CsvRecord] -> [CsvRecord]
    forall a b. (a -> b) -> a -> b
    $ ReportOpts
    -> [DateSpan]
    -> PeriodicReportRow DisplayName Change
    -> [CsvRecord]
    forall a.
    ReportOpts
    -> [DateSpan] -> PeriodicReportRow a Change -> [CsvRecord]
    multiBalanceRowAsCsvText ReportOpts
    opts [DateSpan]
    colspans PeriodicReportRow DisplayName Change
    row
        showName :: PeriodicReportRow DisplayName a -> Text
    showName = Int -> Text -> Text
    accountNameDrop Int
    drop_ (Text -> Text)
    -> (PeriodicReportRow DisplayName a -> Text)
    -> PeriodicReportRow DisplayName a
    -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PeriodicReportRow DisplayName a -> Text
    forall {a}. PeriodicReportRow DisplayName a -> Text
    prrFullName
        totalrows :: [CsvRecord]
    totalrows
          | Bool
    no_total_ = [CsvRecord]
    forall a. Monoid a => a
    mempty
          | Bool
    otherwise = (CsvRecord -> CsvRecord) -> [CsvRecord] -> [CsvRecord]
    forall a b. (a -> b) -> [a] -> [b]
    map (Text
    "total" Text -> CsvRecord -> CsvRecord
    forall a. a -> [a] -> [a]
    :) ([CsvRecord] -> [CsvRecord]) -> [CsvRecord] -> [CsvRecord]
    forall a b. (a -> b) -> a -> b
    $ ReportOpts
    -> [DateSpan] -> PeriodicReportRow () Change -> [CsvRecord]
    forall a.
    ReportOpts
    -> [DateSpan] -> PeriodicReportRow a Change -> [CsvRecord]
    multiBalanceRowAsCsvText ReportOpts
    opts [DateSpan]
    colspans PeriodicReportRow () Change
    tr

    -- | Render a multi-column balance report as HTML.
    multiBalanceReportAsHtml :: ReportOpts -> MultiBalanceReport -> Html ()
    multiBalanceReportAsHtml :: ReportOpts -> MultiBalanceReport -> Html ()
    multiBalanceReportAsHtml ReportOpts
    ropts MultiBalanceReport
    mbr =
      let
        (Html ()
    headingsrow,[Html ()]
    bodyrows,[Html ()]
    mtotalsrows) = ReportOpts -> MultiBalanceReport -> (Html (), [Html ()], [Html ()])
    multiBalanceReportHtmlRows ReportOpts
    ropts MultiBalanceReport
    mbr
      in
        Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    table_ (Html () -> Html ()) -> Html () -> Html ()
    forall a b. (a -> b) -> a -> b
    $ [Html ()] -> Html ()
    forall a. Monoid a => [a] -> a
    mconcat ([Html ()] -> Html ()) -> [Html ()] -> Html ()
    forall a b. (a -> b) -> a -> b
    $
             [Html ()
    headingsrow]
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [Html ()]
    bodyrows
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [Html ()]
    mtotalsrows

    -- | Render the HTML table rows for a MultiBalanceReport.
    -- Returns the heading row, 0 or more body rows, and the totals row if enabled.
    multiBalanceReportHtmlRows :: ReportOpts -> MultiBalanceReport -> (Html (), [Html ()], [Html ()])
    multiBalanceReportHtmlRows :: ReportOpts -> MultiBalanceReport -> (Html (), [Html ()], [Html ()])
    multiBalanceReportHtmlRows ReportOpts
    ropts MultiBalanceReport
    mbr =
      let
        -- TODO: should the commodity_column be displayed as a subaccount in this case as well?
        (CsvRecord
    headingsrow:[CsvRecord]
    bodyrows, [CsvRecord]
    mtotalsrows)
          | ReportOpts -> Bool
    transpose_ ReportOpts
    ropts = CommandDoc -> ([CsvRecord], [CsvRecord])
    forall a. CommandDoc -> a
    error' CommandDoc
    "Sorry, --transpose with HTML output is not yet supported"  -- PARTIAL:
          | Bool
    otherwise = ReportOpts -> MultiBalanceReport -> ([CsvRecord], [CsvRecord])
    multiBalanceReportAsCsv' ReportOpts
    ropts MultiBalanceReport
    mbr
      in
        (ReportOpts -> CsvRecord -> Html ()
    multiBalanceReportHtmlHeadRow ReportOpts
    ropts CsvRecord
    headingsrow
        ,(CsvRecord -> Html ()) -> [CsvRecord] -> [Html ()]
    forall a b. (a -> b) -> [a] -> [b]
    map (ReportOpts -> CsvRecord -> Html ()
    multiBalanceReportHtmlBodyRow ReportOpts
    ropts) [CsvRecord]
    bodyrows
        ,ReportOpts -> CsvRecord -> Html ()
    multiBalanceReportHtmlFootRow ReportOpts
    ropts (CsvRecord -> Html ()) -> [CsvRecord] -> [Html ()]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [CsvRecord]
    mtotalsrows -- TODO pad totals row with zeros when there are
        )

    -- | Render one MultiBalanceReport heading row as a HTML table row.
    multiBalanceReportHtmlHeadRow :: ReportOpts -> [T.Text] -> Html ()
    multiBalanceReportHtmlHeadRow :: ReportOpts -> CsvRecord -> Html ()
    multiBalanceReportHtmlHeadRow ReportOpts
    _ [] = Html ()
    forall a. Monoid a => a
    mempty  -- shouldn't happen
    multiBalanceReportHtmlHeadRow ReportOpts
    ropts (Text
    acct:CsvRecord
    cells) =
      let
        defstyle :: Attribute
    defstyle = Text -> Attribute
    forall arg result. TermRaw arg result => arg -> result
    style_ Text
    ""
        (CsvRecord
    amts,CsvRecord
    tot,CsvRecord
    avg)
          | ReportOpts -> Bool
    row_total_ ReportOpts
    ropts Bool -> Bool -> Bool
    && ReportOpts -> Bool
    average_ ReportOpts
    ropts = (CsvRecord
    ini2,  CsvRecord
    sndlst2, CsvRecord
    lst2)
          | ReportOpts -> Bool
    row_total_ ReportOpts
    ropts                   = (CsvRecord
    ini1,  CsvRecord
    lst1,    [])
          |                     ReportOpts -> Bool
    average_ ReportOpts
    ropts = (CsvRecord
    ini1,  [],      CsvRecord
    lst1)
          | Bool
    otherwise                          = (CsvRecord
    cells, [],      [])
          where
            n :: Int
    n = CsvRecord -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length CsvRecord
    cells
            (CsvRecord
    ini1,CsvRecord
    lst1)    = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt (Int
    nInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1) CsvRecord
    cells
            (CsvRecord
    ini2, CsvRecord
    rest)   = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt (Int
    nInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    2) CsvRecord
    cells
            (CsvRecord
    sndlst2,CsvRecord
    lst2) = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt Int
    1 CsvRecord
    rest

      in
        Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    tr_ (Html () -> Html ()) -> Html () -> Html ()
    forall a b. (a -> b) -> a -> b
    $ [Html ()] -> Html ()
    forall a. Monoid a => [a] -> a
    mconcat ([Html ()] -> Html ()) -> [Html ()] -> Html ()
    forall a b. (a -> b) -> a -> b
    $
              [Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "account"]              (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    acct)
           Html () -> [Html ()] -> [Html ()]
    forall a. a -> [a] -> [a]
    : [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "", Attribute
    defstyle]           (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    amts]
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "rowtotal", Attribute
    defstyle]   (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    tot]
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "rowaverage", Attribute
    defstyle] (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    avg]

    -- | Render one MultiBalanceReport data row as a HTML table row.
    multiBalanceReportHtmlBodyRow :: ReportOpts -> [T.Text] -> Html ()
    multiBalanceReportHtmlBodyRow :: ReportOpts -> CsvRecord -> Html ()
    multiBalanceReportHtmlBodyRow ReportOpts
    _ [] = Html ()
    forall a. Monoid a => a
    mempty  -- shouldn't happen
    multiBalanceReportHtmlBodyRow ReportOpts
    ropts (Text
    label:CsvRecord
    cells) =
      let
        defstyle :: Attribute
    defstyle = Text -> Attribute
    forall arg result. TermRaw arg result => arg -> result
    style_ Text
    "text-align:right"
        (CsvRecord
    amts,CsvRecord
    tot,CsvRecord
    avg)
          | ReportOpts -> Bool
    row_total_ ReportOpts
    ropts Bool -> Bool -> Bool
    && ReportOpts -> Bool
    average_ ReportOpts
    ropts = (CsvRecord
    ini2,  CsvRecord
    sndlst2, CsvRecord
    lst2)
          | ReportOpts -> Bool
    row_total_ ReportOpts
    ropts                   = (CsvRecord
    ini1,  CsvRecord
    lst1,    [])
          |                     ReportOpts -> Bool
    average_ ReportOpts
    ropts = (CsvRecord
    ini1,  [],      CsvRecord
    lst1)
          | Bool
    otherwise                          = (CsvRecord
    cells, [],      [])
          where
            n :: Int
    n = CsvRecord -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length CsvRecord
    cells
            (CsvRecord
    ini1,CsvRecord
    lst1)    = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt (Int
    nInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1) CsvRecord
    cells
            (CsvRecord
    ini2, CsvRecord
    rest)   = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt (Int
    nInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    2) CsvRecord
    cells
            (CsvRecord
    sndlst2,CsvRecord
    lst2) = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt Int
    1 CsvRecord
    rest
      in
        Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    tr_ (Html () -> Html ()) -> Html () -> Html ()
    forall a b. (a -> b) -> a -> b
    $ [Html ()] -> Html ()
    forall a. Monoid a => [a] -> a
    mconcat ([Html ()] -> Html ()) -> [Html ()] -> Html ()
    forall a b. (a -> b) -> a -> b
    $
              [Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "account", Text -> Attribute
    forall arg result. TermRaw arg result => arg -> result
    style_ Text
    "text-align:left"]  (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    label)
           Html () -> [Html ()] -> [Html ()]
    forall a. a -> [a] -> [a]
    : [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "amount", Attribute
    defstyle]            (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    amts]
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "amount rowtotal", Attribute
    defstyle]   (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    tot]
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    td_ [Text -> Attribute
    class_ Text
    "amount rowaverage", Attribute
    defstyle] (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    avg]

    -- | Render one MultiBalanceReport totals row as a HTML table row.
    multiBalanceReportHtmlFootRow :: ReportOpts -> [T.Text] -> Html ()
    multiBalanceReportHtmlFootRow :: ReportOpts -> CsvRecord -> Html ()
    multiBalanceReportHtmlFootRow ReportOpts
    _ropts [] = Html ()
    forall a. Monoid a => a
    mempty
    -- TODO pad totals row with zeros when subreport is empty
    --  multiBalanceReportHtmlFootRow ropts $
    --     ""
    --   : repeat nullmixedamt zeros
    --  ++ (if row_total_ ropts then [nullmixedamt] else [])
    --  ++ (if average_ ropts   then [nullmixedamt]   else [])
    multiBalanceReportHtmlFootRow ReportOpts
    ropts (Text
    acct:CsvRecord
    cells) =
      let
        defstyle :: Attribute
    defstyle = Text -> Attribute
    forall arg result. TermRaw arg result => arg -> result
    style_ Text
    "text-align:right"
        (CsvRecord
    amts,CsvRecord
    tot,CsvRecord
    avg)
          | ReportOpts -> Bool
    row_total_ ReportOpts
    ropts Bool -> Bool -> Bool
    && ReportOpts -> Bool
    average_ ReportOpts
    ropts = (CsvRecord
    ini2,  CsvRecord
    sndlst2, CsvRecord
    lst2)
          | ReportOpts -> Bool
    row_total_ ReportOpts
    ropts                   = (CsvRecord
    ini1,  CsvRecord
    lst1,    [])
          |                     ReportOpts -> Bool
    average_ ReportOpts
    ropts = (CsvRecord
    ini1,  [],      CsvRecord
    lst1)
          | Bool
    otherwise                          = (CsvRecord
    cells, [],      [])
          where
            n :: Int
    n = CsvRecord -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length CsvRecord
    cells
            (CsvRecord
    ini1,CsvRecord
    lst1)    = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt (Int
    nInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    1) CsvRecord
    cells
            (CsvRecord
    ini2, CsvRecord
    rest)   = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt (Int
    nInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    2) CsvRecord
    cells
            (CsvRecord
    sndlst2,CsvRecord
    lst2) = Int -> CsvRecord -> (CsvRecord, CsvRecord)
    forall a. Int -> [a] -> ([a], [a])
    splitAt Int
    1 CsvRecord
    rest
      in
        Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    tr_ (Html () -> Html ()) -> Html () -> Html ()
    forall a b. (a -> b) -> a -> b
    $ [Html ()] -> Html ()
    forall a. Monoid a => [a] -> a
    mconcat ([Html ()] -> Html ()) -> [Html ()] -> Html ()
    forall a b. (a -> b) -> a -> b
    $
              [Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    th_ [Text -> Attribute
    forall arg result. TermRaw arg result => arg -> result
    style_ Text
    "text-align:left"]             (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    acct)
           Html () -> [Html ()] -> [Html ()]
    forall a. a -> [a] -> [a]
    : [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    th_ [Text -> Attribute
    class_ Text
    "amount coltotal", Attribute
    defstyle]   (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    amts]
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    th_ [Text -> Attribute
    class_ Text
    "amount coltotal", Attribute
    defstyle]   (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    tot]
          [Html ()] -> [Html ()] -> [Html ()]
    forall a. [a] -> [a] -> [a]
    ++ [[Attribute] -> Html () -> Html ()
    forall arg result. Term arg result => arg -> result
    th_ [Text -> Attribute
    class_ Text
    "amount colaverage", Attribute
    defstyle] (Text -> Html ()
    forall a (m :: * -> *). (ToHtml a, Monad m) => a -> HtmlT m ()
    forall (m :: * -> *). Monad m => Text -> HtmlT m ()
    toHtml Text
    a) | Text
    a <- CsvRecord
    avg]

    --thRow :: [String] -> Html ()
    --thRow = tr_ . mconcat . map (th_ . toHtml)

    -- | Render a multi-column balance report as plain text suitable for console output.
    multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> TL.Text
    multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> Text
    multiBalanceReportAsText ropts :: ReportOpts
    ropts@ReportOpts{Bool
    Int
    CsvRecord
    [Status]
    Maybe Int
    Maybe Text
    Maybe NormalSign
    Maybe ValuationType
    Maybe ConversionOp
    Interval
    Period
    StringFormat
    Layout
    AccountListMode
    BalanceAccumulation
    BalanceCalculation
    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 -> CsvRecord
    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
    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_ :: CsvRecord
    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
    ..} MultiBalanceReport
    r = 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 (ReportOpts -> MultiBalanceReport -> Table Text Text WideBuilder
    balanceReportAsTable ReportOpts
    ropts MultiBalanceReport
    r)
      where
        title :: Text
    title = Text
    mtitle Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    " in " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> DateSpan -> Text
    showDateSpan (MultiBalanceReport -> DateSpan
    forall a b. PeriodicReport a b -> DateSpan
    periodicReportSpan MultiBalanceReport
    r) Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    valuationdesc Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ":"

        mtitle :: Text
    mtitle = case (BalanceCalculation
    balancecalc_, BalanceAccumulation
    balanceaccum_) of
            (BalanceCalculation
    CalcValueChange, BalanceAccumulation
    PerPeriod  ) -> Text
    "Period-end value changes"
            (BalanceCalculation
    CalcValueChange, BalanceAccumulation
    Cumulative ) -> Text
    "Cumulative period-end value changes"
            (BalanceCalculation
    CalcGain,        BalanceAccumulation
    PerPeriod  ) -> Text
    "Incremental gain"
            (BalanceCalculation
    CalcGain,        BalanceAccumulation
    Cumulative ) -> Text
    "Cumulative gain"
            (BalanceCalculation
    CalcGain,        BalanceAccumulation
    Historical ) -> Text
    "Historical gain"
            (BalanceCalculation
    _,               BalanceAccumulation
    PerPeriod  ) -> Text
    "Balance changes"
            (BalanceCalculation
    _,               BalanceAccumulation
    Cumulative ) -> Text
    "Ending balances (cumulative)"
            (BalanceCalculation
    _,               BalanceAccumulation
    Historical)  -> Text
    "Ending balances (historical)"
        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
    d Maybe Text
    _mc)  -> Text
    ", valued at " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Day -> Text
    showDate Day
    d
                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

    -- | Build a 'Table' from a multi-column balance report.
    balanceReportAsTable :: ReportOpts -> MultiBalanceReport -> Table T.Text T.Text WideBuilder
    balanceReportAsTable :: ReportOpts -> MultiBalanceReport -> Table Text Text WideBuilder
    balanceReportAsTable opts :: ReportOpts
    opts@ReportOpts{Bool
    summary_only_ :: ReportOpts -> Bool
    summary_only_ :: Bool
    summary_only_, Bool
    average_ :: ReportOpts -> Bool
    average_ :: Bool
    average_, Bool
    row_total_ :: ReportOpts -> Bool
    row_total_ :: Bool
    row_total_, BalanceAccumulation
    balanceaccum_ :: ReportOpts -> BalanceAccumulation
    balanceaccum_ :: BalanceAccumulation
    balanceaccum_}
        (PeriodicReport [DateSpan]
    spans [PeriodicReportRow DisplayName Change]
    items PeriodicReportRow () Change
    tr) =
       Table Text Text WideBuilder -> Table Text Text WideBuilder
    forall {rh} {a}. Table rh rh a -> Table rh rh a
    maybetranspose (Table Text Text WideBuilder -> Table Text Text WideBuilder)
    -> Table Text Text WideBuilder -> Table Text Text WideBuilder
    forall a b. (a -> b) -> a -> b
    $
       Table Text Text WideBuilder -> Table Text Text WideBuilder
    forall {ch}. Table Text ch WideBuilder -> Table Text ch WideBuilder
    addtotalrow (Table Text Text WideBuilder -> Table Text Text WideBuilder)
    -> Table Text Text WideBuilder -> Table Text Text WideBuilder
    forall a b. (a -> b) -> a -> b
    $
       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.NoLine ([Header Text] -> Header Text) -> [Header Text] -> Header Text
    forall a b. (a -> b) -> a -> b
    $ (Text -> Header Text) -> CsvRecord -> [Header Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> Header Text
    forall h. h -> Header h
    Tab.Header ([CsvRecord] -> CsvRecord
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [CsvRecord]
    accts))
         (Properties -> [Header Text] -> Header Text
    forall h. Properties -> [Header h] -> Header h
    Tab.Group Properties
    Tab.NoLine ([Header Text] -> Header Text) -> [Header Text] -> Header Text
    forall a b. (a -> b) -> a -> b
    $ (Text -> Header Text) -> CsvRecord -> [Header Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> Header Text
    forall h. h -> Header h
    Tab.Header CsvRecord
    colheadings)
         ([[[WideBuilder]]] -> [[WideBuilder]]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat [[[WideBuilder]]]
    rows)
      where
        totalscolumn :: Bool
    totalscolumn = Bool
    row_total_ Bool -> Bool -> Bool
    && BalanceAccumulation
    balanceaccum_ BalanceAccumulation -> [BalanceAccumulation] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `notElem` [BalanceAccumulation
    Cumulative, BalanceAccumulation
    Historical]
        colheadings :: CsvRecord
    colheadings = [Text
    "Commodity" | ReportOpts -> Layout
    layout_ ReportOpts
    opts Layout -> Layout -> Bool
    forall a. Eq a => a -> a -> Bool
    == Layout
    LayoutBare]
                      CsvRecord -> CsvRecord -> CsvRecord
    forall a. [a] -> [a] -> [a]
    ++ (if Bool -> Bool
    not Bool
    summary_only_ then (DateSpan -> Text) -> [DateSpan] -> CsvRecord
    forall a b. (a -> b) -> [a] -> [b]
    map (BalanceAccumulation -> [DateSpan] -> DateSpan -> Text
    reportPeriodName BalanceAccumulation
    balanceaccum_ [DateSpan]
    spans) [DateSpan]
    spans else [])
                      CsvRecord -> CsvRecord -> CsvRecord
    forall a. [a] -> [a] -> [a]
    ++ [Text
    "  Total" | Bool
    totalscolumn]
                      CsvRecord -> CsvRecord -> CsvRecord
    forall a. [a] -> [a] -> [a]
    ++ [Text
    "Average" | Bool
    average_]
        fullRowAsTexts :: PeriodicReportRow DisplayName Change
    -> (CsvRecord, [[WideBuilder]])
    fullRowAsTexts PeriodicReportRow DisplayName Change
    row =
          let rs :: [[WideBuilder]]
    rs = ReportOpts
    -> PeriodicReportRow DisplayName Change -> [[WideBuilder]]
    forall a.
    ReportOpts -> PeriodicReportRow a Change -> [[WideBuilder]]
    multiBalanceRowAsTableText ReportOpts
    opts PeriodicReportRow DisplayName Change
    row
           in (Int -> Text -> CsvRecord
    forall a. Int -> a -> [a]
    replicate ([[WideBuilder]] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [[WideBuilder]]
    rs) (PeriodicReportRow DisplayName Change -> Text
    forall {a}. PeriodicReportRow DisplayName a -> Text
    renderacct PeriodicReportRow DisplayName Change
    row), [[WideBuilder]]
    rs)
        ([CsvRecord]
    accts, [[[WideBuilder]]]
    rows) = [(CsvRecord, [[WideBuilder]])] -> ([CsvRecord], [[[WideBuilder]]])
    forall a b. [(a, b)] -> ([a], [b])
    unzip ([(CsvRecord, [[WideBuilder]])]
     -> ([CsvRecord], [[[WideBuilder]]]))
    -> [(CsvRecord, [[WideBuilder]])]
    -> ([CsvRecord], [[[WideBuilder]]])
    forall a b. (a -> b) -> a -> b
    $ (PeriodicReportRow DisplayName Change
     -> (CsvRecord, [[WideBuilder]]))
    -> [PeriodicReportRow DisplayName Change]
    -> [(CsvRecord, [[WideBuilder]])]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap PeriodicReportRow DisplayName Change
    -> (CsvRecord, [[WideBuilder]])
    fullRowAsTexts [PeriodicReportRow DisplayName Change]
    items
        renderacct :: PeriodicReportRow DisplayName a -> Text
    renderacct PeriodicReportRow DisplayName a
    row =
            Int -> Text -> Text
    T.replicate ((PeriodicReportRow DisplayName a -> Int
    forall a. PeriodicReportRow DisplayName a -> Int
    prrDepth PeriodicReportRow DisplayName a
    row Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    2) Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> PeriodicReportRow DisplayName a -> Text
    forall {a}. PeriodicReportRow DisplayName a -> Text
    prrDisplayName PeriodicReportRow DisplayName a
    row
        addtotalrow :: Table Text ch WideBuilder -> Table Text ch WideBuilder
    addtotalrow
          | ReportOpts -> Bool
    no_total_ ReportOpts
    opts = Table Text ch WideBuilder -> Table Text ch WideBuilder
    forall a. a -> a
    id
          | Bool
    otherwise =
            let totalrows :: [[WideBuilder]]
    totalrows = ReportOpts -> PeriodicReportRow () Change -> [[WideBuilder]]
    forall a.
    ReportOpts -> PeriodicReportRow a Change -> [[WideBuilder]]
    multiBalanceRowAsTableText ReportOpts
    opts PeriodicReportRow () Change
    tr
                rh :: Header Text
    rh = Properties -> [Header Text] -> Header Text
    forall h. Properties -> [Header h] -> Header h
    Tab.Group Properties
    Tab.NoLine ([Header Text] -> Header Text)
    -> (Header Text -> [Header Text]) -> Header Text -> Header Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> Header Text -> [Header Text]
    forall a. Int -> a -> [a]
    replicate ([[WideBuilder]] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [[WideBuilder]]
    totalrows) (Header Text -> Header Text) -> Header Text -> Header Text
    forall a b. (a -> b) -> a -> b
    $ Text -> Header Text
    forall h. h -> Header h
    Tab.Header Text
    ""
                ch :: Header [a]
    ch = [a] -> Header [a]
    forall h. h -> Header h
    Tab.Header [] -- ignored
             in ((Table Text ch WideBuilder
     -> Table Text [Any] WideBuilder -> Table Text ch WideBuilder)
    -> Table Text [Any] WideBuilder
    -> Table Text ch WideBuilder
    -> Table Text ch WideBuilder
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip (Properties
    -> Table Text ch WideBuilder
    -> Table Text [Any] WideBuilder
    -> Table Text ch WideBuilder
    forall rh ch a ch2.
    Properties -> Table rh ch a -> Table rh ch2 a -> Table rh ch a
    concatTables Properties
    Tab.SingleLine) (Table Text [Any] WideBuilder
     -> Table Text ch WideBuilder -> Table Text ch WideBuilder)
    -> Table Text [Any] WideBuilder
    -> Table Text ch WideBuilder
    -> Table Text ch 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)
        maybetranspose :: Table rh rh a -> Table rh rh a
    maybetranspose | ReportOpts -> Bool
    transpose_ ReportOpts
    opts = \(Table Header rh
    rh Header rh
    ch [[a]]
    vals) -> Header rh -> Header rh -> [[a]] -> Table rh rh a
    forall rh ch a. Header rh -> Header ch -> [[a]] -> Table rh ch a
    Table Header rh
    ch Header rh
    rh ([[a]] -> [[a]]
    forall a. [[a]] -> [[a]]
    transpose [[a]]
    vals)
                       | Bool
    otherwise       = Table rh rh a -> Table rh rh a
    forall a. a -> a
    id

    multiBalanceRowAsWbs :: AmountDisplayOpts -> ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
    multiBalanceRowAsWbs :: forall a.
    AmountDisplayOpts
    -> ReportOpts
    -> [DateSpan]
    -> PeriodicReportRow a Change
    -> [[WideBuilder]]
    multiBalanceRowAsWbs AmountDisplayOpts
    bopts ReportOpts{Bool
    Int
    CsvRecord
    [Status]
    Maybe Int
    Maybe Text
    Maybe NormalSign
    Maybe ValuationType
    Maybe ConversionOp
    Interval
    Period
    StringFormat
    Layout
    AccountListMode
    BalanceAccumulation
    BalanceCalculation
    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 -> CsvRecord
    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
    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_ :: CsvRecord
    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
    ..} [DateSpan]
    colspans (PeriodicReportRow a
    _ [Change]
    as Change
    rowtot Change
    rowavg) =
        case Layout
    layout_ of
          LayoutWide Maybe Int
    width -> [(Change -> WideBuilder) -> [Change] -> [WideBuilder]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (AmountDisplayOpts -> Change -> WideBuilder
    showMixedAmountB AmountDisplayOpts
    bopts{displayMaxWidth=width}) [Change]
    allamts]
          Layout
    LayoutTall       -> WideBuilder -> [[WideBuilder]] -> [[WideBuilder]]
    forall a. a -> [[a]] -> [[a]]
    paddedTranspose WideBuilder
    forall a. Monoid a => a
    mempty
                               ([[WideBuilder]] -> [[WideBuilder]])
    -> ([Change] -> [[WideBuilder]]) -> [Change] -> [[WideBuilder]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Change -> [WideBuilder]) -> [Change] -> [[WideBuilder]]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (AmountDisplayOpts -> Change -> [WideBuilder]
    showMixedAmountLinesB AmountDisplayOpts
    bopts{displayMaxWidth=Nothing})
                               ([Change] -> [[WideBuilder]]) -> [Change] -> [[WideBuilder]]
    forall a b. (a -> b) -> a -> b
    $ [Change]
    allamts
          Layout
    LayoutBare       -> (WideBuilder -> [WideBuilder] -> [WideBuilder])
    -> [WideBuilder] -> [[WideBuilder]] -> [[WideBuilder]]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith (:) ((Text -> WideBuilder) -> CsvRecord -> [WideBuilder]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Text -> WideBuilder
    wbFromText CsvRecord
    cs)  -- add symbols
                               ([[WideBuilder]] -> [[WideBuilder]])
    -> ([Change] -> [[WideBuilder]]) -> [Change] -> [[WideBuilder]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [[WideBuilder]] -> [[WideBuilder]]
    forall a. [[a]] -> [[a]]
    transpose                         -- each row becomes a list of Text quantities
                               ([[WideBuilder]] -> [[WideBuilder]])
    -> ([Change] -> [[WideBuilder]]) -> [Change] -> [[WideBuilder]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Change -> [WideBuilder]) -> [Change] -> [[WideBuilder]]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (AmountDisplayOpts -> Change -> [WideBuilder]
    showMixedAmountLinesB AmountDisplayOpts
    bopts{displayCommodity=False, displayCommodityOrder=Just cs, displayMinWidth=Nothing})
                               ([Change] -> [[WideBuilder]]) -> [Change] -> [[WideBuilder]]
    forall a b. (a -> b) -> a -> b
    $ [Change]
    allamts
          Layout
    LayoutTidy       -> [[[WideBuilder]]] -> [[WideBuilder]]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat
                               ([[[WideBuilder]]] -> [[WideBuilder]])
    -> ([Change] -> [[[WideBuilder]]]) -> [Change] -> [[WideBuilder]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (DateSpan -> [[WideBuilder]] -> [[WideBuilder]])
    -> [DateSpan] -> [[[WideBuilder]]] -> [[[WideBuilder]]]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith (([WideBuilder] -> [WideBuilder])
    -> [[WideBuilder]] -> [[WideBuilder]]
    forall a b. (a -> b) -> [a] -> [b]
    map (([WideBuilder] -> [WideBuilder])
     -> [[WideBuilder]] -> [[WideBuilder]])
    -> (DateSpan -> [WideBuilder] -> [WideBuilder])
    -> DateSpan
    -> [[WideBuilder]]
    -> [[WideBuilder]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DateSpan -> [WideBuilder] -> [WideBuilder]
    addDateColumns) [DateSpan]
    colspans
                               ([[[WideBuilder]]] -> [[[WideBuilder]]])
    -> ([Change] -> [[[WideBuilder]]]) -> [Change] -> [[[WideBuilder]]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Change -> [[WideBuilder]]) -> [Change] -> [[[WideBuilder]]]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ( (Text -> WideBuilder -> [WideBuilder])
    -> CsvRecord -> [WideBuilder] -> [[WideBuilder]]
    forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
    zipWith (\Text
    c WideBuilder
    a -> [Text -> WideBuilder
    wbFromText Text
    c, WideBuilder
    a]) CsvRecord
    cs
                                      ([WideBuilder] -> [[WideBuilder]])
    -> (Change -> [WideBuilder]) -> Change -> [[WideBuilder]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . AmountDisplayOpts -> Change -> [WideBuilder]
    showMixedAmountLinesB AmountDisplayOpts
    bopts{displayCommodity=False, displayCommodityOrder=Just cs, displayMinWidth=Nothing})
                               ([Change] -> [[WideBuilder]]) -> [Change] -> [[WideBuilder]]
    forall a b. (a -> b) -> a -> b
    $ [Change]
    as  -- Do not include totals column or average for tidy output, as this
                                     -- complicates the data representation and can be easily calculated
      where
        totalscolumn :: Bool
    totalscolumn = Bool
    row_total_ Bool -> Bool -> Bool
    && BalanceAccumulation
    balanceaccum_ BalanceAccumulation -> [BalanceAccumulation] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `notElem` [BalanceAccumulation
    Cumulative, BalanceAccumulation
    Historical]
        cs :: CsvRecord
    cs = if (Change -> Bool) -> [Change] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    all Change -> Bool
    mixedAmountLooksZero [Change]
    allamts then [Text
    ""] else Set Text -> CsvRecord
    forall a. Set a -> [a]
    S.toList (Set Text -> CsvRecord) -> Set Text -> CsvRecord
    forall a b. (a -> b) -> a -> b
    $ (Change -> Set Text) -> [Change] -> Set Text
    forall m a. Monoid m => (a -> m) -> [a] -> m
    forall (t :: * -> *) m a.
    (Foldable t, Monoid m) =>
    (a -> m) -> t a -> m
    foldMap Change -> Set Text
    maCommodities [Change]
    allamts
        allamts :: [Change]
    allamts = (if Bool -> Bool
    not Bool
    summary_only_ then [Change]
    as else []) [Change] -> [Change] -> [Change]
    forall a. [a] -> [a] -> [a]
    ++
                    [Change
    rowtot | Bool
    totalscolumn Bool -> Bool -> Bool
    && Bool -> Bool
    not ([Change] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Change]
    as)] [Change] -> [Change] -> [Change]
    forall a. [a] -> [a] -> [a]
    ++
                    [Change
    rowavg | Bool
    average_ Bool -> Bool -> Bool
    && Bool -> Bool
    not ([Change] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Change]
    as)]
        addDateColumns :: DateSpan -> [WideBuilder] -> [WideBuilder]
    addDateColumns spn :: DateSpan
    spn@(DateSpan Maybe EFDay
    s Maybe EFDay
    e) = (Text -> WideBuilder
    wbFromText (DateSpan -> Text
    showDateSpan DateSpan
    spn) WideBuilder -> [WideBuilder] -> [WideBuilder]
    forall a. a -> [a] -> [a]
    :)
                                           ([WideBuilder] -> [WideBuilder])
    -> ([WideBuilder] -> [WideBuilder])
    -> [WideBuilder]
    -> [WideBuilder]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text -> WideBuilder
    wbFromText (Text -> (EFDay -> Text) -> Maybe EFDay -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    "" EFDay -> Text
    showEFDate Maybe EFDay
    s) WideBuilder -> [WideBuilder] -> [WideBuilder]
    forall a. a -> [a] -> [a]
    :)
                                           ([WideBuilder] -> [WideBuilder])
    -> ([WideBuilder] -> [WideBuilder])
    -> [WideBuilder]
    -> [WideBuilder]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text -> WideBuilder
    wbFromText (Text -> (EFDay -> Text) -> Maybe EFDay -> Text
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe Text
    "" (EFDay -> Text
    showEFDate (EFDay -> Text) -> (EFDay -> EFDay) -> EFDay -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Day -> Day) -> EFDay -> EFDay
    modifyEFDay (Integer -> Day -> Day
    addDays (-Integer
    1))) Maybe EFDay
    e) WideBuilder -> [WideBuilder] -> [WideBuilder]
    forall a. a -> [a] -> [a]
    :)

        paddedTranspose :: a -> [[a]] -> [[a]]
        paddedTranspose :: forall a. a -> [[a]] -> [[a]]
    paddedTranspose a
    _ [] = [[]]
        paddedTranspose a
    n [[a]]
    as1 = Int -> [[a]] -> [[a]]
    forall a. Int -> [a] -> [a]
    take ([Int] -> Int
    forall a. Ord a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    maximum ([Int] -> Int) -> ([[a]] -> [Int]) -> [[a]] -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ([a] -> Int) -> [[a]] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map [a] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length ([[a]] -> Int) -> [[a]] -> Int
    forall a b. (a -> b) -> a -> b
    $ [[a]]
    as1) ([[a]] -> [[a]]) -> ([[a]] -> [[a]]) -> [[a]] -> [[a]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [[a]] -> [[a]]
    trans ([[a]] -> [[a]]) -> [[a]] -> [[a]]
    forall a b. (a -> b) -> a -> b
    $ [[a]]
    as1
            where
              trans :: [[a]] -> [[a]]
    trans ([] : [[a]]
    xss)  = (a
    n a -> [a] -> [a]
    forall a. a -> [a] -> [a]
    : ([a] -> a) -> [[a]] -> [a]
    forall a b. (a -> b) -> [a] -> [b]
    map [a] -> a
    h [[a]]
    xss) [a] -> [[a]] -> [[a]]
    forall a. a -> [a] -> [a]
    :  [[a]] -> [[a]]
    trans ([a
    n] [a] -> [[a]] -> [[a]]
    forall a. a -> [a] -> [a]
    : ([a] -> [a]) -> [[a]] -> [[a]]
    forall a b. (a -> b) -> [a] -> [b]
    map [a] -> [a]
    t [[a]]
    xss)
              trans ((a
    x : [a]
    xs) : [[a]]
    xss) = (a
    x a -> [a] -> [a]
    forall a. a -> [a] -> [a]
    : ([a] -> a) -> [[a]] -> [a]
    forall a b. (a -> b) -> [a] -> [b]
    map [a] -> a
    h [[a]]
    xss) [a] -> [[a]] -> [[a]]
    forall a. a -> [a] -> [a]
    : [[a]] -> [[a]]
    trans ([a] -> [a]
    m [a]
    xs [a] -> [[a]] -> [[a]]
    forall a. a -> [a] -> [a]
    : ([a] -> [a]) -> [[a]] -> [[a]]
    forall a b. (a -> b) -> [a] -> [b]
    map [a] -> [a]
    t [[a]]
    xss)
              trans [] = []
              h :: [a] -> a
    h (a
    x:[a]
    _) = a
    x
              h [] = a
    n
              t :: [a] -> [a]
    t (a
    _:[a]
    xs) = [a]
    xs
              t [] = [a
    n]
              m :: [a] -> [a]
    m (a
    x:[a]
    xs) = a
    xa -> [a] -> [a]
    forall a. a -> [a] -> [a]
    :[a]
    xs
              m [] = [a
    n]

    multiBalanceRowAsCsvText :: ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[T.Text]]
    multiBalanceRowAsCsvText :: forall a.
    ReportOpts
    -> [DateSpan] -> PeriodicReportRow a Change -> [CsvRecord]
    multiBalanceRowAsCsvText ReportOpts
    opts [DateSpan]
    colspans = ([WideBuilder] -> CsvRecord) -> [[WideBuilder]] -> [CsvRecord]
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap ((WideBuilder -> Text) -> [WideBuilder] -> CsvRecord
    forall a b. (a -> b) -> [a] -> [b]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap WideBuilder -> Text
    wbToText) ([[WideBuilder]] -> [CsvRecord])
    -> (PeriodicReportRow a Change -> [[WideBuilder]])
    -> PeriodicReportRow a Change
    -> [CsvRecord]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . AmountDisplayOpts
    -> ReportOpts
    -> [DateSpan]
    -> PeriodicReportRow a Change
    -> [[WideBuilder]]
    forall a.
    AmountDisplayOpts
    -> ReportOpts
    -> [DateSpan]
    -> PeriodicReportRow a Change
    -> [[WideBuilder]]
    multiBalanceRowAsWbs AmountDisplayOpts
    csvDisplay ReportOpts
    opts [DateSpan]
    colspans

    multiBalanceRowAsTableText :: ReportOpts -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
    multiBalanceRowAsTableText :: forall a.
    ReportOpts -> PeriodicReportRow a Change -> [[WideBuilder]]
    multiBalanceRowAsTableText ReportOpts
    opts = AmountDisplayOpts
    -> ReportOpts
    -> [DateSpan]
    -> PeriodicReportRow a Change
    -> [[WideBuilder]]
    forall a.
    AmountDisplayOpts
    -> ReportOpts
    -> [DateSpan]
    -> PeriodicReportRow a Change
    -> [[WideBuilder]]
    multiBalanceRowAsWbs AmountDisplayOpts
    oneLine{displayColour=color_ opts} ReportOpts
    opts []

    tests_Balance :: TestTree
    tests_Balance = CommandDoc -> [TestTree] -> TestTree
    testGroup CommandDoc
    "Balance" [

       CommandDoc -> [TestTree] -> TestTree
    testGroup CommandDoc
    "balanceReportAsText" [
        CommandDoc -> IO () -> TestTree
    testCase CommandDoc
    "unicode in balance layout" (IO () -> TestTree) -> IO () -> TestTree
    forall a b. (a -> b) -> a -> b
    $ do
          Journal
    j <- Text -> IO Journal
    readJournal' Text
    "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n"
          let rspec :: ReportSpec
    rspec = ReportSpec
    defreportspec{_rsReportOpts=defreportopts{no_total_=True}}
          Builder -> Text
    TB.toLazyText (ReportOpts -> BalanceReport -> Builder
    balanceReportAsText (ReportSpec -> ReportOpts
    _rsReportOpts ReportSpec
    rspec) (ReportSpec -> Journal -> BalanceReport
    balanceReport ReportSpec
    rspec{_rsDay=fromGregorian 2008 11 26} Journal
    j))
            Text -> Text -> IO ()
    forall a. (Eq a, Show a, HasCallStack) => a -> a -> IO ()
    @?=
            [Text] -> Text
    TL.unlines
            [Text
    "                -100  актив:наличные"
            ,Text
    "                 100  расходы:покупки"
            ]
        ]

      ]