    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE QuasiQuotes #-}

    module Hledger.Cli.Commands.Close (
      closemode
     ,close
    )
    where

    import Control.Monad (when)
    import Data.Maybe
    import Data.String.Here
    import Data.Time.Calendar
    import Hledger
    import Hledger.Cli.CliOptions
    import System.Console.CmdArgs.Explicit as C

    closemode = hledgerCommandMode
      [here| close equity
    Print a "closing balances" transaction that brings all accounts (or with
    query arguments, just the matched accounts) to a zero (historical) balance,
    followed by an opposite "opening balances" transaction that restores the
    balances from zero.

    FLAGS

    The opening transaction is useful to carry over asset/liability balances
    if you choose to start a new journal file, eg yearly. The closing transaction
    can be a useful complement, allowing you to optionally include old files
    (for more history) without disturbing the asset/liability balances
    (since the closing/opening pairs cancel out).

    This command may also be useful for closing out expense/income accounts
    for a period (ie "closing the books" in accounting).

    Both transactions include balance assertions for the closed/reopened accounts.
    You probably shouldn't use status or realness queries (eg -C or -R) with this
    command, or the balance assertions will require that query to pass.

    By default, the closing transaction is dated yesterday, with balances
    calculated as of end of yesterday, and the opening transaction is dated today.
    To close on some other date, use: `hledger close -e OPENINGDATE ...`.
    (-p or date: can also be used, the begin date is ignored.)

    You can chose to print just one of the transactions with `--opening`
    or `--closing`.

    For example, carrying asset/liability balances into a new file for 2018:
    ```
    $ hledger close -f 2017.journal -e 2018/1/1 ^assets ^liab >>2017.journal
    # cut & paste the opening transaction from 2017.journal to a new 2018.journal
    # now:
    $ hledger bs -f 2018.journal                   # correct balances
    $ hledger bs -f 2018.journal -f 2017.journal   # still correct
    $ hledger bs -f 2017.journal not:desc:closing  # must exclude closing txn
    ```

    Transactions spanning the closing date may complicate matters. Eg, if
    closing at end of 2017:
    ```
    2017/12/31
        expenses:food          1
        assets:bank:checking  -1  ; date:2018/1/1
    ```
      |]
      [flagNone ["opening"] (\opts -> setboolopt "opening" opts) "show just opening transaction"
      ,flagNone ["closing"] (\opts -> setboolopt "closing" opts) "show just closing transaction"
      ]
      [generalflagsgroup1]
      []
      ([], Just $ argsFlag "[QUERY]")

    close CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do
      today <- getCurrentDay
      let
          (opening, closing) =
            case (boolopt "opening" rawopts, boolopt "closing" rawopts) of
              (False, False) -> (True, True) -- by default show both opening and closing
              (o, c) -> (o, c)
          ropts_ = ropts{balancetype_=HistoricalBalance, accountlistmode_=ALFlat}
          q = queryFromOpts today ropts_
          openingdate = fromMaybe today $ queryEndDate False q
          closingdate = addDays (-1) openingdate
          (acctbals,_) = balanceReportFromMultiBalanceReport ropts_ q j
          balancingamt = negate $ sum $ map (\(_,_,_,b) -> normaliseMixedAmountSquashPricesForDisplay b) acctbals
          ps = [posting{paccount=a
                       ,pamount=mixed [b]
                       ,pbalanceassertion=Just assertion{ baamount=b }
                       }
               |(a,_,_,mb) <- acctbals
               ,b <- amounts $ normaliseMixedAmountSquashPricesForDisplay mb
               ]
               ++ [posting{paccount="equity:opening balances", pamount=balancingamt}]
          nps = [posting{paccount=a
                        ,pamount=mixed [negate b]
                        ,pbalanceassertion=Just assertion{ baamount=b{aquantity=0} }
                        }
                |(a,_,_,mb) <- acctbals
                ,b <- amounts $ normaliseMixedAmountSquashPricesForDisplay mb
                ]
               ++ [posting{paccount="equity:closing balances", pamount=negate balancingamt}]
      when closing $ putStr $ showTransaction (nulltransaction{tdate=closingdate, tdescription="closing balances", tpostings=nps})
      when opening $ putStr $ showTransaction (nulltransaction{tdate=openingdate, tdescription="opening balances", tpostings=ps})