.gitignore
[comptalang.git] / cli / Hcompta / CLI / Command / Balance.hs
index d3416f5281dbd11da7e072017509bcfc5c4806c9..86ca6bfe953844b73d1e05aaa107fc6cfbeaf061 100644 (file)
@@ -1,24 +1,42 @@
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
 module Hcompta.CLI.Command.Balance where
 
-import           Control.Applicative (Const(..))
-import           Prelude hiding (foldr)
-import           Control.Monad (liftM, forM_)
+import           Control.Applicative ((<*), Const(..), Applicative(..))
+import           Control.Arrow (first, (+++), (&&&), (***))
+import           Control.DeepSeq (NFData)
+import           Control.Monad (Monad(..), liftM, mapM)
 import           Control.Monad.IO.Class (liftIO)
-import           Control.Monad.Trans.Except (runExceptT)
-import qualified Data.Either
-import qualified Data.Foldable
-import           Data.Foldable (foldr)
-import qualified Data.Map.Strict as Data.Map
-import           Data.Monoid ((<>))
+import           Data.Bool
+import           Data.Data
+import           Data.Decimal (Decimal)
+import           Data.Either (Either(..), partitionEithers)
+import           Data.Eq (Eq(..))
+import           Data.Foldable (Foldable(..), any)
+import           Data.Function (($), (.), const, on)
+import           Data.Functor (Functor(..), (<$>))
+import           Data.List ((++), repeat)
+-- import           Data.List.NonEmpty (NonEmpty(..))
+import           Data.Map.Strict (Map)
+import qualified Data.Map.Strict as Map
+import           Data.Maybe (Maybe(..))
+import           Data.Monoid (Monoid(..))
+import           Data.Ord (Ord(..), Ordering(..))
 import qualified Data.Strict.Maybe as Strict
-import qualified Data.Text.Lazy as TL
+import           Data.String (String)
+import           Data.Text (Text)
 import qualified Data.Time.Clock as Time
+import           Data.Tuple (fst, snd)
+import           Prelude (Bounded(..), FilePath, IO, Num(..), id, flip, unlines, zipWith)
 import           System.Console.GetOpt
                  ( ArgDescr(..)
                  , OptDescr(..)
@@ -28,484 +46,1099 @@ import           System.Environment as Env (getProgName)
 import           System.Exit (exitSuccess)
 import qualified System.IO as IO
 import qualified Text.Parsec
+import           Text.Show (Show(..))
 
-import           Hcompta.Account (Account)
+import           Hcompta.Account (Account_Tags)
 import qualified Hcompta.Account as Account
-import qualified Hcompta.Account.Read as Account.Read
-import           Hcompta.Amount (Amount)
-import qualified Hcompta.Amount as Amount
-import qualified Hcompta.Amount.Write as Amount.Write
-import           Hcompta.Amount.Unit (Unit)
 import qualified Hcompta.Balance as Balance
 import qualified Hcompta.CLI.Args as Args
-import           Hcompta.CLI.Context (Context)
-import qualified Hcompta.CLI.Context as Context
-import qualified Hcompta.CLI.Format.Ledger as CLI.Ledger
+import qualified Hcompta.CLI.Context as C
+import qualified Hcompta.CLI.Env as CLI.Env
+import           Hcompta.CLI.Format (Format(..), Formats)
+import qualified Hcompta.CLI.Format as Format
+import           Hcompta.CLI.Format.JCC ()
+import           Hcompta.CLI.Format.Ledger ()
 import qualified Hcompta.CLI.Lang as Lang
-import qualified Hcompta.CLI.Lib.Leijen.Table as Table
+import qualified Hcompta.CLI.Lib.Leijen.Table as Leijen.Table
 import qualified Hcompta.CLI.Write as Write
+import qualified Hcompta.Chart as Chart
+import           Hcompta.Date (Date)
 import qualified Hcompta.Date as Date
 import qualified Hcompta.Filter as Filter
+import qualified Hcompta.Filter.Amount as Filter.Amount
 import qualified Hcompta.Filter.Read as Filter.Read
-import qualified Hcompta.Format.Ledger as Ledger
-import qualified Hcompta.Format.Ledger.Journal as Ledger.Journal
-import qualified Hcompta.Format.Ledger.Read as Ledger.Read
-import qualified Hcompta.Format.Ledger.Write as Ledger.Write
+import qualified Hcompta.Format.JCC as JCC
+import qualified Hcompta.Format.Ledger      as Ledger
+import qualified Hcompta.Format.Ledger.Read as Ledger
 import           Hcompta.Lib.Leijen (toDoc, ToDoc(..))
 import qualified Hcompta.Lib.Leijen as W
+import qualified Hcompta.Lib.Parsec as R
 import           Hcompta.Lib.TreeMap (TreeMap)
 import qualified Hcompta.Lib.TreeMap as TreeMap
+import           Hcompta.Polarize (Polarized)
+import qualified Hcompta.Polarize as Polarize
 import qualified Hcompta.Posting as Posting
+import qualified Hcompta.Quantity as Quantity
+import           Hcompta.Unit (Unit(..))
 
-data Ctx
- =   Ctx
- { ctx_filter_balance      :: Filter.Simplified
-                              (Filter.Filter_Bool
-                              (Filter.Filter_Balance
-                              (Account, Amount.Sum Amount)))
- , ctx_filter_posting      :: Filter.Simplified
-                              (Filter.Filter_Bool
-                              (Filter.Filter_Posting
-                              Ledger.Posting))
- , ctx_filter_transaction  :: Filter.Simplified
-                              (Filter.Filter_Bool
-                              (Filter.Filter_Transaction
-                              Ledger.Transaction))
+-- type Sum = (Ledger.Unit, Polarize.Polarized Ledger.Quantity)
+
+data Context
+ =   Context
+ { ctx_filter_transaction :: forall t.
+                             ( Filter.Transaction t
+                             , Filter.Amount_Quantity
+                               (Posting.Posting_Amount
+                               (Filter.Transaction_Posting t))
+                               ~ Filter.Amount.Quantity
+                             ) => Filter.Simplified
+                                  (Filter.Filter_Bool
+                                  (Filter.Filter_Transaction t))
+ , ctx_filter_balance     :: forall b.
+                             ( Filter.Balance b
+                             , Filter.Amount_Quantity
+                               (Filter.Balance_Amount b)
+                               ~ Filter.Amount.Quantity
+                             ) => Filter.Simplified
+                                  (Filter.Filter_Bool
+                                  (Filter.Filter_Balance b))
+ -- , ctx_filter_posting      :: CLI.Format.Filter_Posting
  , ctx_heritage            :: Bool
  , ctx_input               :: [FilePath]
+ , ctx_input_format        :: Formats
+ , ctx_output              :: [(Write.Mode, FilePath)]
+ , ctx_output_format       :: (Maybe Formats, Output_Format)
  , ctx_reduce_date         :: Bool
  , ctx_redundant           :: Bool
  , ctx_total_by_unit       :: Bool
- , ctx_format_output       :: Format_Output
- , ctx_account_equilibrium :: Account
- } deriving (Show)
+ , ctx_account_equilibrium :: (JCC.Account, JCC.Account)
+ } -- deriving (Show)
 
-data Format_Output
- =   Format_Output_Table
- |   Format_Output_Transaction { negate_transaction :: Bool }
+data Output_Format
+ =   Output_Format_Table
+ |   Output_Format_Transaction Lang.Exercise_OC
  deriving (Eq, Show)
 
-nil :: Context -> Ctx
-nil context =
-       Ctx
-        { ctx_filter_balance      = mempty
-        , ctx_filter_posting      = mempty
-        , ctx_filter_transaction  = mempty
+context :: C.Context -> Context
+context c =
+       Context
+        { ctx_filter_transaction  = Filter.Simplified $ Right True
+        , ctx_filter_balance      = Filter.Simplified $ Right True
+        -- , ctx_filter_posting      = mempty
         , ctx_heritage            = True
         , ctx_input               = []
+        , ctx_input_format        = mempty
+        , ctx_output              = []
+        , ctx_output_format       = (Nothing, Output_Format_Table)
         , ctx_reduce_date         = True
         , ctx_redundant           = False
         , ctx_total_by_unit       = True
-        , ctx_format_output       = Format_Output_Table
-        , ctx_account_equilibrium = Account.account
-                (TL.toStrict $ W.displayT $ W.renderOneLine False $
-                       toDoc (Context.lang context) Lang.Message_Equilibrium)
-                []
+        , ctx_account_equilibrium =
+               let e = C.translate c Lang.Account_Equilibrium
+               in (e, e)
         }
 
-usage :: IO String
-usage = do
+usage :: C.Context -> IO String
+usage = do
        bin <- Env.getProgName
-       let pad = replicate (length bin) ' '
        return $ unlines $
-               [ "SYNTAX "
-               , "  "++bin++" balance [-i JOURNAL_FILE]"
-               , "  "++pad++"         [-b BALANCE_FILTER]"
-               , "  "++pad++"         [-p POSTING_FILTER]"
-               , "  "++pad++"         [-t TRANSACTION_FILTER]"
-               , "  "++pad++"         [JOURNAL_FILE] [...]"
+               [ C.translate c Lang.Section_Description
+               , "  "++C.translate c Lang.Help_Command_Balance
+               , ""
+               , C.translate c Lang.Section_Syntax
+               , "  "++bin++" balance ["++C.translate c Lang.Type_Option++"] [...]"++
+                                    " ["++C.translate c Lang.Type_File_Journal++"] [...]"
                , ""
-               , usageInfo "OPTIONS" options
+               , usageInfo (C.translate c Lang.Section_Options) (options c)
                ]
 
-options :: Args.Options Ctx
-options =
+options :: C.Context -> Args.Options Context
+options =
        [ Option "b" ["filter-balance"]
-        (ReqArg (\s context ctx -> do
-               ctx_filter_balance <-
-                       liftM ((ctx_filter_balance ctx <>) . Filter.simplify) $
-                               liftIO $ Filter.Read.read Filter.Read.filter_balance s
-                               >>= \f -> case f of
-                                Left  ko -> Write.fatal context $ ko
-                                Right ok -> return ok
-               return $ ctx{ctx_filter_balance}) "FILTER")
-        "filter at balance level, multiple uses are merged with a logical AND"
-       , Option "p" ["filter-posting"]
-        (ReqArg (\s context ctx -> do
-               ctx_filter_posting <-
-                       liftM ((ctx_filter_posting ctx <>) . Filter.simplify) $
-                       liftIO $ Filter.Read.read Filter.Read.filter_posting s
-                       >>= \f -> case f of
-                        Left  ko -> Write.fatal context $ ko
-                        Right ok -> return ok
-               return $ ctx{ctx_filter_posting}) "FILTER")
-        "filter at posting level, multiple uses are merged with a logical AND"
+        (ReqArg (\s ctx -> do
+               filter <-
+                       R.runParserT_with_Error
+                        Filter.Read.filter_balance
+                        Filter.Read.context "" s
+               case filter of
+                Left (ko::[R.Error Filter.Read.Error]) -> Write.fatal c ko
+                Right flt ->
+                       return $
+                               ctx{ctx_filter_balance =
+                                       Filter.and (ctx_filter_balance ctx) $
+                                       (Filter.simplify $
+                                               Filter.Read.get_Forall_Filter_Balance_Decimal <$> flt)
+                                }) $
+               C.translate c Lang.Type_Filter_Balance) $
+               C.translate c Lang.Help_Option_Filter_Balance
+       {-, Option "p" ["filter-posting"]
+        (ReqArg (\s ctx -> do
+               read <- liftIO $ Filter.Read.read Filter.Read.filter_posting s
+               case read of
+                Left ko -> Write.fatal c ko
+                Right filter -> return $
+                       ctx{ctx_filter_posting =
+                               (ctx_filter_posting ctx <>) $
+                               CLI.Format.All
+                                (Filter.simplify $ Filter.Read.get_Forall_Filter_Posting_Decimal <$> filter)
+                                (Filter.simplify $ Filter.Read.get_Forall_Filter_Posting_Decimal <$> filter)
+                        }) $
+               C.translate c Lang.Type_Filter_Posting) $
+               C.translate c Lang.Help_Option_Filter_Posting
+       -}
        , Option "t" ["filter-transaction"]
-        (ReqArg (\s context ctx -> do
-               ctx_filter_transaction <-
-                       liftM ((ctx_filter_transaction ctx <>) . Filter.simplify) $
-                       liftIO $ Filter.Read.read Filter.Read.filter_transaction s
-                       >>= \f -> case f of
-                        Left  ko -> Write.fatal context $ ko
-                        Right ok -> return ok
-               return $ ctx{ctx_filter_transaction}) "FILTER")
-        "filter at transaction level, multiple uses are merged with a logical AND"
+        (ReqArg (\s ctx -> do
+               filter <-
+                       R.runParserT_with_Error
+                        Filter.Read.filter_transaction
+                        Filter.Read.context "" s
+               case filter of
+                Left ko -> Write.fatal c ko
+                Right flt ->
+                       return $
+                               ctx{ctx_filter_transaction =
+                                       Filter.and (ctx_filter_transaction ctx) $
+                                       (Filter.simplify $
+                                               Filter.Read.get_Forall_Filter_Transaction_Decimal <$> flt)
+                                }) $
+               C.translate c Lang.Type_Filter_Transaction) $
+               C.translate c Lang.Help_Option_Filter_Transaction
        , Option "h" ["help"]
-        (NoArg (\_context _ctx -> do
-               usage >>= IO.hPutStr IO.stderr
-               exitSuccess))
-        "show this help"
+        (NoArg (\_ctx -> do
+               usage >>= IO.hPutStr IO.stderr
+               exitSuccess)) $
+               C.translate c Lang.Help_Option_Help
        , Option "i" ["input"]
-        (ReqArg (\s _context ctx -> do
-               return $ ctx{ctx_input=s:ctx_input ctx}) "FILE")
-        "read data from given file, multiple uses merge the data as would a concatenation do"
+        (ReqArg (\s ctx -> do
+               return $ ctx{ctx_input=s:ctx_input ctx}) $
+               C.translate c Lang.Type_File_Journal) $
+               C.translate c Lang.Help_Option_Input
+       , Option "f" ["input-format"]
+        (OptArg (\arg ctx -> do
+               ctx_input_format <- case arg of
+                Nothing       -> return $ Format_JCC ()
+                Just "jcc"    -> return $ Format_JCC ()
+                Just "ledger" -> return $ Format_Ledger ()
+                Just _        -> Write.fatal c $
+                       W.text "--input-format option expects \"jcc\", or \"ledger\" as value"
+               return $ ctx{ctx_input_format})
+         "[jcc|ledger]")
+         "input format"
+       , Option "o" ["output"]
+        (ReqArg (\s ctx -> do
+               return $ ctx{ctx_output=(Write.Mode_Append, s):ctx_output ctx}) $
+               C.translate c Lang.Type_File) $
+               C.translate c Lang.Help_Option_Output
+       , Option "O" ["overwrite"]
+        (ReqArg (\s ctx -> do
+               return $ ctx{ctx_output=(Write.Mode_Over, s):ctx_output ctx}) $
+               C.translate c Lang.Type_File) $
+               C.translate c Lang.Help_Option_Overwrite
        {- NOTE: not used so far.
        , Option "" ["reduce-date"]
-        (OptArg (\arg context ctx -> do
+        (OptArg (\arg ctx -> do
                ctx_reduce_date <- case arg of
                 Nothing    -> return $ True
                 Just "yes" -> return $ True
                 Just "no"  -> return $ False
-                Just _     -> Write.fatal context $
+                Just _     -> Write.fatal c $
                        W.text "--reduce-date option expects \"yes\", or \"no\" as value"
                return $ ctx{ctx_reduce_date})
          "[yes|no]")
         "use advanced date reducer to speed up filtering"
        -}
        , Option "" ["redundant"]
-        (OptArg (\arg context ctx -> do
+        (OptArg (\arg ctx -> do
                ctx_redundant <- case arg of
                 Nothing    -> return $ True
                 Just "yes" -> return $ True
                 Just "no"  -> return $ False
-                Just _     -> Write.fatal context $
-                       W.text "--redundant option expects \"yes\", or \"no\" as value"
+                Just _     -> Write.fatal c Lang.Error_Option_Balance_Redundant
                return $ ctx{ctx_redundant})
-         "[yes|no]")
-        "also print accounts with zero amount or the same amounts than its ascending account"
+         "[no|yes]") $
+               C.translate c Lang.Help_Option_Balance_Redundant
        , Option "" ["heritage"]
-        (OptArg (\arg context ctx -> do
+        (OptArg (\arg ctx -> do
                ctx_heritage <- case arg of
                 Nothing    -> return $ True
                 Just "yes" -> return $ True
                 Just "no"  -> return $ False
-                Just _     -> Write.fatal context $
-                       W.text "--heritage option expects \"yes\", or \"no\" as value"
+                Just _     -> Write.fatal c Lang.Error_Option_Balance_Heritage
                return $ ctx{ctx_heritage})
-         "[yes|no]")
-        "propagate amounts to ascending accounts"
+         "[yes|no]") $
+               C.translate c Lang.Help_Option_Balance_Heritage
        , Option "" ["total"]
-        (OptArg (\arg context ctx -> do
+        (OptArg (\arg ctx -> do
                ctx_total_by_unit <- case arg of
                 Nothing    -> return $ True
                 Just "yes" -> return $ True
                 Just "no"  -> return $ False
-                Just _     -> Write.fatal context $
-                       W.text "--total option expects \"yes\", or \"no\" as value"
+                Just _     -> Write.fatal c Lang.Error_Option_Balance_Total
                return $ ctx{ctx_total_by_unit})
-         "[yes|no]")
-        "calculate totals by unit"
-       , Option "f" ["format"]
-        (ReqArg (\arg context ctx -> do
-               ctx_format_output <- case arg of
-                "table" -> return $ Format_Output_Table
-                "open"  -> return $ Format_Output_Transaction False
-                "close" -> return $ Format_Output_Transaction True
-                _       -> Write.fatal context $
-                       W.text "--format option expects \"close\", \"open\", or \"table\" as value"
-               return $ ctx{ctx_format_output})
-         "[close|open|table]")
-        "select output format"
-       , Option "" ["equilibrium"]
-        (ReqArg (\arg context ctx -> do
+         "[yes|no]") $
+               C.translate c Lang.Help_Option_Balance_Total
+       , Option "F" ["output-format"]
+        (ReqArg (\arg ctx -> do
+               ctx_output_format <- case arg of
+                "table"        -> return $ (Nothing                , Output_Format_Table)
+                "table.jcc"    -> return $ (Just $ Format_JCC    (), Output_Format_Table)
+                "table.ledger" -> return $ (Just $ Format_Ledger (), Output_Format_Table)
+                "open"         -> return $ (Nothing                , Output_Format_Transaction Lang.Exercise_Opening)
+                "open.jcc"     -> return $ (Just $ Format_JCC    (), Output_Format_Transaction Lang.Exercise_Opening)
+                "open.ledger"  -> return $ (Just $ Format_Ledger (), Output_Format_Transaction Lang.Exercise_Opening)
+                "close"        -> return $ (Nothing                , Output_Format_Transaction Lang.Exercise_Closing)
+                "close.jcc"    -> return $ (Just $ Format_JCC    (), Output_Format_Transaction Lang.Exercise_Closing)
+                "close.ledger" -> return $ (Just $ Format_Ledger (), Output_Format_Transaction Lang.Exercise_Closing)
+                _              -> Write.fatal c Lang.Error_Option_Balance_Format
+               return $ ctx{ctx_output_format})
+         "[table|close|open][.jcc|.ledger]") $
+               C.translate c Lang.Help_Option_Balance_Format
+       , Option "" ["eq"]
+        (ReqArg (\arg ctx -> do
                ctx_account_equilibrium <-
+                       fmap (\e -> (e, e)) $
                        case Text.Parsec.runParser
-                                (Account.Read.account <* Text.Parsec.eof)
+                                (Ledger.read_account <* Text.Parsec.eof)
                                 () "" arg of
                         Right acct -> return acct
-                        _          -> Write.fatal context $
-                               W.text "--equilibrium option expects a valid account name"
-               return $ ctx{ctx_account_equilibrium})
-         "ACCOUNT")
-        "specify account equilibrating a close or open balance"
+                        _          -> Write.fatal c Lang.Error_Option_Equilibrium
+               return $ ctx{ctx_account_equilibrium}) $
+               C.translate c Lang.Type_Account) $
+               C.translate c Lang.Help_Option_Equilibrium
+       , Option "" ["eq-credit"]
+        (ReqArg (\arg ctx -> do
+               ctx_account_equilibrium <-
+                       fmap (\e -> (fst $ ctx_account_equilibrium ctx, e)) $
+                       case Text.Parsec.runParser
+                                (Ledger.read_account <* Text.Parsec.eof)
+                                () "" arg of
+                        Right acct -> return acct
+                        _          -> Write.fatal c Lang.Error_Option_Equilibrium_Credit
+               return $ ctx{ctx_account_equilibrium}) $
+               C.translate c Lang.Type_Account) $
+               C.translate c Lang.Help_Option_Equilibrium_Credit
+       , Option "" ["eq-debit"]
+        (ReqArg (\arg ctx -> do
+               ctx_account_equilibrium <-
+                       fmap (\e -> (e, snd $ ctx_account_equilibrium ctx)) $
+                       case Text.Parsec.runParser
+                                (Ledger.read_account <* Text.Parsec.eof)
+                                () "" arg of
+                        Right acct -> return acct
+                        _          -> Write.fatal c Lang.Error_Option_Equilibrium_Debit
+               return $ ctx{ctx_account_equilibrium}) $
+               C.translate c Lang.Type_Account) $
+               C.translate c Lang.Help_Option_Equilibrium_Debit
        ]
 
-run :: Context.Context -> [String] -> IO ()
-run context args = do
-       (ctx, inputs) <- Args.parse context usage options (nil context, args)
-       read_journals <-
-               liftM Data.Either.partitionEithers $ do
-               CLI.Ledger.paths context $ ctx_input ctx ++ inputs
-               >>= do
-                       mapM $ \path -> do
-                               liftIO $ runExceptT $ Ledger.Read.file
-                                (Ledger.Read.context ( ctx_filter_transaction ctx
-                                                     , ctx_filter_posting     ctx )
-                                                     Ledger.journal)
-                                path
-                               >>= \x -> case x of
-                                Left  ko -> return $ Left (path, ko)
-                                Right ok -> return $ Right ok
-       case read_journals of
-        (errs@(_:_), _journals) ->
-               forM_ errs $ \(_path, err) -> do
-                       Write.fatal context $ err
-        ([], journals) -> do
-               Write.debug context $ "filter: transaction: " ++ show (ctx_filter_transaction ctx)
-               Write.debug context $ "filter: posting: " ++ show (ctx_filter_posting ctx)
-               Write.debug context $ "filter: balance: " ++ show (ctx_filter_balance ctx)
-               style_color <- Write.with_color context IO.stdout
-               case ctx_format_output ctx of
-                Format_Output_Transaction nt -> do
-                       let balance_by_account =
-                               ledger_balance_by_account_filter ctx $
-                               ledger_balance_by_account ctx journals
-                       let Balance.Balance_by_Unit balance_by_unit =
-                               ledger_balance_by_unit ctx $
-                               ledger_balance_by_account_filter ctx balance_by_account
-                       let posting_equilibrium =
-                               (Ledger.posting $ ctx_account_equilibrium ctx)
-                                { Ledger.posting_amounts =
-                                       flip Data.Map.map balance_by_unit $
-                                               (if nt then id else negate)
-                                               . Amount.sum_balance
-                                               . Balance.unit_sum_amount
-                                , Ledger.posting_comments=
-                                        [ TL.toStrict $ W.displayT $ W.renderOneLine False $
-                                               toDoc (Context.lang context) $
-                                               Lang.Message_Equilibrium_posting
-                                        ]
-                                }
-                       now <- liftM (\d -> d{Time.utctDayTime=0}) $ Date.now
-                       let transaction =
-                               Ledger.transaction
-                                { Ledger.transaction_description=
-                                       TL.toStrict $ W.displayT $ W.renderOneLine False $
-                                       toDoc (Context.lang context) $
-                                       Lang.Message_Balance_Description nt
-                                , Ledger.transaction_dates=(now, [])
-                                , Ledger.transaction_postings=
-                                       (if null $ Ledger.posting_amounts posting_equilibrium
-                                       then id
-                                       else
-                                               Data.Map.insertWith (++)
-                                                (ctx_account_equilibrium ctx)
-                                                [posting_equilibrium]) $
-                                       TreeMap.flatten_with_Path
-                                        (\posting_account (Balance.Account_Sum amount_by_unit) ->
-                                               [(Ledger.posting posting_account)
-                                                { Ledger.posting_amounts =
-                                                       flip fmap amount_by_unit $
-                                                               (if nt then negate else id)
-                                                               . Amount.sum_balance
-                                                }
-                                               ]
-                                        )
-                                        balance_by_account
-                                }
-                       let sty = Ledger.Write.Style
-                                { Ledger.Write.style_align = True -- ctx_align ctx
-                                , Ledger.Write.style_color
-                                }
-                       Ledger.Write.put sty IO.stdout $ do
-                       Ledger.Write.transaction transaction
-                Format_Output_Table -> do
-                       let ( table_balance_by_account
-                                 , Balance.Balance_by_Unit balance_by_unit
-                                 ) =
-                               case ledger_balance_by_account ctx journals of
-                                b | ctx_heritage ctx ->
-                                       let bb = ledger_balance_by_account_expanded ctx b in
-                                       ( table_by_account ctx Balance.inclusive bb
-                                       , ledger_balance_by_unit_expanded ctx bb
-                                       )
-                                b ->
-                                       let bb = ledger_balance_by_account_filter ctx b in
-                                       ( table_by_account ctx id bb
-                                       , ledger_balance_by_unit ctx bb
-                                       )
-                       W.displayIO IO.stdout $ do
-                       W.renderPretty style_color 1.0 maxBound $ do
-                       toDoc () $ do
-                       let title =
-                               TL.toStrict . W.displayT .
-                               W.renderCompact False .
-                               toDoc (Context.lang context)
-                       zipWith id
-                        [ Table.column (title Lang.Message_Debit)   Table.Align_Right
-                        , Table.column (title Lang.Message_Credit)  Table.Align_Right
-                        , Table.column (title Lang.Message_Balance) Table.Align_Right
-                        , Table.column (title Lang.Message_Account) Table.Align_Left
-                        ] $ do
-                               table_balance_by_account $ do
-                               case ctx_total_by_unit ctx of
-                                False -> repeat []
-                                True -> do
-                                       zipWith (:)
-                                        [ Table.Cell_Line '=' 0
-                                        , Table.Cell_Line '=' 0
-                                        , Table.Cell_Line '=' 0
-                                        , Table.Cell_Line ' ' 0
-                                        ] $ do
-                                       flip table_by_unit (repeat []) $
-                                               Data.Map.map
-                                                Balance.unit_sum_amount
-                                                balance_by_unit
-
-ledger_balance_by_account
- :: Ctx
- -> [ Ledger.Journal (Const (Balance.Balance_by_Account (Amount.Sum Amount)) Ledger.Transaction) ]
- -> Balance.Balance_by_Account (Amount.Sum Amount)
-ledger_balance_by_account _ctx =
-       Data.Foldable.foldl'
-        (flip $ Ledger.Journal.fold
-                (\Ledger.Journal{Ledger.journal_transactions=Const b} ->
-                       mappend b))
-        mempty
-
-ledger_balance_by_account_filter
- :: Ctx
- -> Balance.Balance_by_Account (Amount.Sum Amount)
- -> Balance.Balance_by_Account (Amount.Sum Amount)
-ledger_balance_by_account_filter ctx =
-       case Filter.simplified $ ctx_filter_balance ctx of
-        Right True -> id
-        Right False -> const mempty
-        Left flt ->
-               TreeMap.filter_with_Path $ \acct ->
-                       Data.Foldable.any (Filter.test flt . (acct,)) .
-                       Balance.get_Account_Sum
-
-ledger_balance_by_account_expanded
- :: Ctx
- -> Balance.Balance_by_Account (Amount.Sum Amount)
- -> Balance.Expanded           (Amount.Sum Amount)
-ledger_balance_by_account_expanded ctx =
-       case Filter.simplified $ ctx_filter_balance ctx of
-        Right True  -> id
-        Right False -> const mempty
-        Left flt ->
-               TreeMap.filter_with_Path_and_Node
-                (\node acct balance ->
-                       let descendants = TreeMap.nodes
-                                (TreeMap.node_descendants node) in
-                       let is_worth =
+run :: C.Context -> [String] -> IO ()
+run c args = do
+       (ctx, inputs) <-
+               first (\x ->
+                       case ctx_output x of
+                        [] -> x{ctx_output=[(Write.Mode_Append, "-")]}
+                        _  -> x) <$>
+               Args.parse c usage options (context c, args)
+       input_paths <- CLI.Env.paths c $ ctx_input ctx ++ inputs
+       read_journals <- mapM (liftIO . journal_read ctx) input_paths
+       case partitionEithers read_journals of
+        (errs@(_:_), _journals) -> Write.fatals c errs
+        ([], (journals::[Forall_Journal_Balance_by_Account])) -> do
+               let bal_by_account =
+                       mconcat $
+                       fmap Format.journal_flatten $
+                       case fst $ ctx_output_format ctx of
+                        Just f -> Format.journal_empty f:journals
+                        Nothing -> journals
+               now <- Date.now
+               with_color <- Write.with_color c IO.stdout
+               W.displayIO IO.stdout $
+                       W.renderPretty with_color 1.0 maxBound $
+                       case snd $ ctx_output_format ctx of
+                        Output_Format_Table ->
+                               toDoc () $ Leijen.Table.table_of (c, ctx) bal_by_account
+                        Output_Format_Transaction oc ->
+                               journal_equilibrium_transaction
+                                (Const bal_by_account::Const Forall_Journal_Balance_by_Account ())
+                                c ctx oc now
+               {-
+               Write.debug c $ "filter: transaction: " ++ show (ctx_filter_transaction ctx)
+               Write.debug c $ "filter: posting: " ++ show (ctx_filter_posting ctx)
+               Write.debug c $ "filter: balance: " ++ show (ctx_filter_balance ctx)
+               let sty = Write.style { Write.style_pretty = True }
+               -}
+
+instance Leijen.Table.Table_of (C.Context, Context) Forall_Journal_Balance_by_Account where
+       table_of (c, ctx) bal_by_account =
+               let lang = C.lang c in
+               let (rows_by_account, rows_by_unit) =
+                       case ctx_heritage ctx of
+                        True  -> rows_of_balance_by_account $ expand bal_by_account
+                        False -> rows_of_balance_by_account          bal_by_account in
+               zipWith id
+                [ Leijen.Table.column (Lang.translate lang Lang.Title_Debit)   Leijen.Table.Align_Right
+                , Leijen.Table.column (Lang.translate lang Lang.Title_Credit)  Leijen.Table.Align_Right
+                , Leijen.Table.column (Lang.translate lang Lang.Title_Balance) Leijen.Table.Align_Right
+                , Leijen.Table.column (Lang.translate lang Lang.Title_Account) Leijen.Table.Align_Left
+                ] $
+               rows_by_account $
+               (if ctx_total_by_unit ctx
+                then zipWith (:)
+                        [ Leijen.Table.Cell_Line '=' 0
+                        , Leijen.Table.Cell_Line '=' 0
+                        , Leijen.Table.Cell_Line '=' 0
+                        , Leijen.Table.Cell_Line ' ' 0
+                        ] . rows_by_unit
+                else id) $
+               repeat []
+               where
+                       expand
+                        :: Forall_Journal_Balance_by_Account
+                        -> Forall_Journal_Balance_by_Account_Expanded
+                       expand = Format.journal_wrap
+                       rows_of_balance_by_account
+                        :: ( Format.Journal_Filter Context (Const bal_by_account) ()
+                           , Format.Journal_Wrap bal_by_account Forall_Journal_Balance_by_Unit
+                           , Format.Journal_Leijen_Table_Cells (Const bal_by_account) ()
+                           )
+                        => bal_by_account
+                        -> ( [[Leijen.Table.Cell]] -> [[Leijen.Table.Cell]]
+                           , [[Leijen.Table.Cell]] -> [[Leijen.Table.Cell]] )
+                       rows_of_balance_by_account =
+                               (***) Format.journal_leijen_table_cells
+                                     Format.journal_leijen_table_cells .
+                               (&&&) id sum_by_unit .
+                               Format.journal_filter ctx .
+                               Const
+                               where
+                                       sum_by_unit
+                                        :: Format.Journal_Wrap bal_by_account Forall_Journal_Balance_by_Unit
+                                        => Const bal_by_account ()
+                                        -> Const Forall_Journal_Balance_by_Unit ()
+                                       sum_by_unit = Const . Format.journal_wrap . getConst
+
+
+
+
+
+
+
+
+
+
+-- * 'Balance.Balance_by_Account'
+
+-- ** Type 'Format_Balance_by_Account'
+
+type Format_Journal_Balance_by_Account
+ = Format
+   (   JCC.Journal Balance_by_Account_JCC)
+   (Ledger.Journal Balance_by_Account_Ledger)
+
+-- JCC
+type Balance_by_Account_JCC
+ = Balance.Balance_by_Account JCC.Account_Section
+                              JCC.Unit
+                              (Polarized JCC.Quantity)
+instance Format.Journal (JCC.Journal Balance_by_Account_JCC) where
+       type Journal_Format   (JCC.Journal Balance_by_Account_JCC)
+        = Format_Journal_Balance_by_Account
+       journal_format = Format_JCC
+
+-- Ledger
+type Balance_by_Account_Ledger
+ = Balance.Balance_by_Account Ledger.Account_Section
+                              Ledger.Unit
+                              (Polarized Ledger.Quantity)
+instance Format.Journal (Ledger.Journal Balance_by_Account_Ledger) where
+       type Journal_Format   (Ledger.Journal Balance_by_Account_Ledger)
+        = Format_Journal_Balance_by_Account
+       journal_format = Format_Ledger
+
+-- ** Class 'Journal_Balance_by_Account'
+
+class
+ ( Format.Journal (j m)
+ , Format.Journal_Format (j m) ~ Format_Journal_Balance_by_Account
+ , Format.Journal_Read j
+ , Format.Journal_Monoid (j m)
+ , Format.Journal_Leijen_Table_Cells j m
+ , Format.Journal_Wrap (j m) Forall_Journal_Balance_by_Account_Expanded
+ , Format.Journal_Wrap (j m) Forall_Journal_Balance_by_Unit
+ , Format.Journal_Filter Context j m
+ , Journal_Equilibrium_Transaction j m
+ ) => Journal_Balance_by_Account j m
+
+instance Journal_Balance_by_Account    JCC.Journal Balance_by_Account_JCC
+instance Journal_Balance_by_Account Ledger.Journal Balance_by_Account_Ledger
+
+-- ** Type 'Forall_Journal_Balance_by_Account'
+
+data    Forall_Journal_Balance_by_Account
+ = forall j m. Journal_Balance_by_Account  j m
+ =>     Forall_Journal_Balance_by_Account (j m)
+
+instance Format.Journal Forall_Journal_Balance_by_Account where
+       type Journal_Format   Forall_Journal_Balance_by_Account = Format_Journal_Balance_by_Account
+       journal_format
+        (Forall_Journal_Balance_by_Account j) =
+               Format.journal_format j
+instance Format.Journal_Empty Forall_Journal_Balance_by_Account where
+       journal_empty f =
+               case f of
+                Format_JCC    () -> Forall_Journal_Balance_by_Account (mempty::JCC.Journal Balance_by_Account_JCC)
+                Format_Ledger () -> Forall_Journal_Balance_by_Account (mempty::Ledger.Journal Balance_by_Account_Ledger)
+instance Format.Journal_Monoid Forall_Journal_Balance_by_Account where
+       journal_flatten
+        (Forall_Journal_Balance_by_Account j) =
+               Forall_Journal_Balance_by_Account $
+               Format.journal_flatten j
+       journal_fold f (Forall_Journal_Balance_by_Account j) =
+               Format.journal_fold (f . Forall_Journal_Balance_by_Account) j
+instance Monoid Forall_Journal_Balance_by_Account where
+       mempty = Forall_Journal_Balance_by_Account (mempty::JCC.Journal Balance_by_Account_JCC)
+       mappend x y =
+               case (mappend `on` Format.journal_format) x y of
+                Format_JCC    j -> Forall_Journal_Balance_by_Account j
+                Format_Ledger j -> Forall_Journal_Balance_by_Account j
+       mconcat js =
+               case js of
+                [] -> mempty
+                j:jn -> foldl' mappend j jn
+
+-- ** 'journal_read'
+
+type Journal_Filter_Simplified transaction
+ = Filter.Simplified
+   (Filter.Filter_Bool
+   (Filter.Filter_Transaction transaction))
+type Journal_Read_Cons txn
+ = txn -> Filter.Filtered (Journal_Filter_Simplified txn) txn
+journal_read
+ :: Context -> FilePath
+ -> IO (Either (Format.Message W.Doc) Forall_Journal_Balance_by_Account)
+journal_read ctx =
+       case ctx_input_format ctx of
+        Format_JCC () ->
+               let wrap (j::JCC.Journal Balance_by_Account_JCC)
+                        = Forall_Journal_Balance_by_Account j in
+               let cons :: Journal_Read_Cons (JCC.Charted JCC.Transaction)
+                        = Filter.Filtered (ctx_filter_transaction ctx) in
+               liftM ((+++) Format.Message wrap) .
+               Format.journal_read cons
+        Format_Ledger () ->
+               let wrap (j::Ledger.Journal Balance_by_Account_Ledger)
+                        = Forall_Journal_Balance_by_Account j in
+               let cons :: Journal_Read_Cons (Ledger.Charted Ledger.Transaction)
+                        = Filter.Filtered (ctx_filter_transaction ctx) in
+               liftM ((+++) Format.Message wrap) .
+               Format.journal_read cons
+
+{-
+-- ** Type family 'Balance_by_Account'
+
+type family Balance_by_Account (j:: * -> *) m
+type instance Balance_by_Account
+   j (Balance.Expanded           as u (Polarized q))
+ = j (Balance.Balance_by_Account as u (Polarized q))
+type instance Balance_by_Account
+   (Const Forall_Journal_Balance_by_Account_Expanded) ()
+ = (Const Forall_Journal_Balance_by_Account         ) ()
+-}
+
+-- Instances 'Format.Journal_Filter'
+
+instance
+ ( Functor j
+ , Format.Journal_Chart j
+ , as ~ Format.Journal_Account_Section j
+ , Data as
+ , Filter.Account (Account_Tags, TreeMap.Path as)
+ , NFData as
+ , Ord as
+ , Show as
+ , q ~ Format.Journal_Quantity j
+ , Format.Journal_Quantity j ~ Decimal
+ , Quantity.Addable q
+ , Quantity.Zero q
+ , Unit u
+ ) => Format.Journal_Filter Context j (Balance.Balance_by_Account as u (Polarized q)) where
+       journal_filter ctx j =
+               case Filter.simplified $ ctx_filter_balance ctx of
+                Right True | ctx_redundant ctx -> j
+                Right True ->
+                       TreeMap.filter_with_Path_and_Node
+                        (\n _p -> is_worth n) <$> j
+                Right False -> const mempty <$> j
+                Left flt ->
+                       TreeMap.map_Maybe_with_Path_and_Node
+                        (\node account (Balance.Account_Sum bal) ->
+                               (if is_worth node bal then id else const Strict.Nothing) $
+                               case Map.mapMaybeWithKey
+                                (\unit qty ->
+                                       if Filter.test flt
+                                                ( (Chart.account_tags account (Format.journal_chart j), account)
+                                                , (unit, qty)
+                                                )
+                                       then Just qty
+                                       else Nothing
+                                ) bal of
+                                m | Map.null m -> Strict.Nothing
+                                m -> Strict.Just $ Balance.Account_Sum m
+                        ) <$> j
+               where
+                       is_worth
+                        :: (Ord k0, Foldable t0, Quantity.Addable a0, Quantity.Zero a0)
+                        => TreeMap.Node k0 x0
+                        -> t0 (Polarized a0)
+                        -> Bool
+                       is_worth _node bal =
+                               ctx_redundant ctx
+                               -- NOTE: worth if no descendant
+                               -- but Account's exclusive
+                               -- has at least a non-zero Amount
+                               || Data.Foldable.any
+                                        (not . Quantity.quantity_null . Polarize.depolarize)
+                                        bal
+instance Format.Journal_Filter Context (Const Forall_Journal_Balance_by_Account) () where
+       journal_filter ctx
+        (Const (Forall_Journal_Balance_by_Account j)) =
+               Const $ Forall_Journal_Balance_by_Account $
+               Format.journal_filter ctx j
+
+-- Instances 'Format.Journal_Leijen_Table_Cells'
+
+instance
+ ( Format.Journal_Content j
+ , Journal j
+ , as ~ Format.Journal_Account_Section j
+ , Ord as
+ , Quantity.Addable (Format.Journal_Quantity j)
+ , Leijen.Table.Cell_of_forall_param j (TreeMap.Path as)
+ , Balance_Account_Sum amt
+ , Balance_Account_Sum_Unit amt ~ Format.Journal_Unit j
+ , Balance_Account_Sum_Quantity amt ~ Polarized (Format.Journal_Quantity j)
+ ) => Format.Journal_Leijen_Table_Cells j (TreeMap as amt) where
+       journal_leijen_table_cells jnl =
+               flip (TreeMap.foldr_with_Path
+                (\account balance rows ->
+                       let Balance.Account_Sum bal = balance_by_account_sum balance in
+                       Map.foldrWithKey
+                        (\unit qty ->
+                               zipWith (:)
+                                [ cell_of $ (unit,) <$> Polarize.polarized_positive qty
+                                , cell_of $ (unit,) <$> Polarize.polarized_negative qty
+                                , cell_of (unit, Polarize.depolarize qty)
+                                , cell_of account
+                                ]
+                        )
+                        rows bal
+                ))
+                (Format.journal_content jnl)
+               where
+                       cell_of :: Leijen.Table.Cell_of_forall_param j x => x -> Leijen.Table.Cell
+                       cell_of = Leijen.Table.cell_of_forall_param jnl
+instance Format.Journal_Leijen_Table_Cells (Const Forall_Journal_Balance_by_Account) () where
+       journal_leijen_table_cells
+        (Const (Forall_Journal_Balance_by_Account j)) =
+               Format.journal_leijen_table_cells j
+
+-- ** Class 'Balance_Account_Sum'
+
+-- | A class to get a 'Balance.Account_Sum'
+--   when operating on 'Balance.Balance_by_Account'
+--   or 'Balance.Expanded' 'Balance.inclusive' field.
+class Balance_Account_Sum amt where
+       type Balance_Account_Sum_Unit     amt
+       type Balance_Account_Sum_Quantity amt
+       balance_by_account_sum
+        :: amt -> Balance.Account_Sum (Balance_Account_Sum_Unit amt)
+                                      (Balance_Account_Sum_Quantity amt)
+instance Balance_Account_Sum (Balance.Account_Sum u q) where
+       type Balance_Account_Sum_Unit     (Balance.Account_Sum u q) = u
+       type Balance_Account_Sum_Quantity (Balance.Account_Sum u q) = q
+       balance_by_account_sum = id
+instance Balance_Account_Sum (Balance.Account_Sum_Expanded u q) where
+       type Balance_Account_Sum_Unit     (Balance.Account_Sum_Expanded u q) = u
+       type Balance_Account_Sum_Quantity (Balance.Account_Sum_Expanded u q) = q
+       balance_by_account_sum = Balance.inclusive
+
+
+
+
+
+
+
+
+
+
+-- * 'Balance.Expanded'
+
+-- ** Type 'Format_Journal_Balance_by_Account_Expanded'
+
+type Format_Journal_Balance_by_Account_Expanded
+ = Format
+   (   JCC.Journal Balance_by_Account_Expanded_JCC)
+   (Ledger.Journal Balance_by_Account_Expanded_Ledger)
+
+-- JCC
+type Balance_by_Account_Expanded_JCC
+ = Balance.Expanded JCC.Account_Section
+                    JCC.Unit
+                    (Polarized JCC.Quantity)
+instance Format.Journal (JCC.Journal Balance_by_Account_Expanded_JCC) where
+       type Journal_Format   (JCC.Journal Balance_by_Account_Expanded_JCC)
+        = Format_Journal_Balance_by_Account_Expanded
+       journal_format = Format_JCC
+
+-- Ledger
+type Balance_by_Account_Expanded_Ledger
+ = Balance.Expanded Ledger.Account_Section
+                    Ledger.Unit
+                    (Polarized Ledger.Quantity)
+instance Format.Journal (Ledger.Journal Balance_by_Account_Expanded_Ledger) where
+       type Journal_Format   (Ledger.Journal Balance_by_Account_Expanded_Ledger)
+        = Format_Journal_Balance_by_Account_Expanded
+       journal_format = Format_Ledger
+
+-- ** Class 'Journal_Balance_by_Account_Expanded'
+
+class
+ ( Format.Journal (j m)
+ , Format.Journal_Format (j m) ~ Format_Journal_Balance_by_Account_Expanded
+ , Format.Journal_Leijen_Table_Cells j m
+ , Format.Journal_Wrap (j m) Forall_Journal_Balance_by_Unit
+ , Format.Journal_Filter Context j m
+ ) => Journal_Balance_by_Account_Expanded j m
+
+instance Journal_Balance_by_Account_Expanded    JCC.Journal Balance_by_Account_Expanded_JCC
+instance Journal_Balance_by_Account_Expanded Ledger.Journal Balance_by_Account_Expanded_Ledger
+
+-- ** Type 'Forall_Journal_Balance_by_Account_Expanded'
+
+data    Forall_Journal_Balance_by_Account_Expanded
+ = forall j m. Journal_Balance_by_Account_Expanded  j m
+ =>     Forall_Journal_Balance_by_Account_Expanded (j m)
+
+instance Format.Journal Forall_Journal_Balance_by_Account_Expanded where
+       type Journal_Format   Forall_Journal_Balance_by_Account_Expanded = Format_Journal_Balance_by_Account_Expanded
+       journal_format
+        (Forall_Journal_Balance_by_Account_Expanded j) =
+               Format.journal_format j
+
+-- Instances 'Format.Journal_Filter'
+
+instance
+ ( Functor j
+ , Format.Journal_Chart j
+ , as ~ Format.Journal_Account_Section j
+ , Data as
+ , Filter.Account (Account_Tags, TreeMap.Path as)
+ , NFData as
+ , Ord as
+ , Show as
+ , q ~ Format.Journal_Quantity j
+ , Format.Journal_Quantity j ~ Decimal
+ , Quantity.Addable q
+ , Quantity.Zero q
+ , Unit u
+ ) => Format.Journal_Filter Context j (Balance.Expanded as u (Polarized q)) where
+       journal_filter ctx j =
+               case Filter.simplified $ ctx_filter_balance ctx of
+                Right True | ctx_redundant ctx -> j
+                Right True ->
+                       TreeMap.filter_with_Path_and_Node
+                        (const . is_worth) <$> j
+                Right False -> const mempty <$> j
+                Left flt ->
+                       TreeMap.map_Maybe_with_Path_and_Node
+                        (\node account bal ->
+                               (if is_worth node bal then id else const Strict.Nothing) $
+                               case Map.mapMaybeWithKey
+                                (\unit qty ->
+                                       if Filter.test flt
+                                                ( (Chart.account_tags account (Format.journal_chart j), account)
+                                                , (unit, qty)
+                                                )
+                                       then Just qty
+                                       else Nothing
+                                ) (Balance.get_Account_Sum $ Balance.inclusive bal) of
+                                m | Map.null m -> Strict.Nothing
+                                m -> Strict.Just $ bal{Balance.inclusive=Balance.Account_Sum m}
+                        ) <$> j
+               where
+                       is_worth node bal =
+                               let descendants = TreeMap.nodes
+                                        (TreeMap.node_descendants node) in
                                ctx_redundant ctx
                                -- NOTE: worth if no descendant
                                -- but Account's inclusive
                                -- has at least a non-zero Amount
-                               || (Data.Map.null descendants &&
-                                        (Data.Foldable.any
-                                                (not . Amount.is_zero . Amount.sum_balance)
-                                                (Balance.get_Account_Sum $ Balance.inclusive balance)))
+                               || (Map.null descendants
+                                        && Data.Foldable.any
+                                                (not . Quantity.quantity_null . Polarize.depolarize)
+                                                (Balance.get_Account_Sum $ Balance.inclusive bal))
                                -- NOTE: worth if Account's exclusive
                                -- has at least a non-zero Amount
                                || (Data.Foldable.any
-                                        (not . Amount.is_zero . Amount.sum_balance)
-                                        (Balance.get_Account_Sum $ Balance.exclusive balance))
+                                        (not . Quantity.quantity_null . Polarize.depolarize)
+                                        (Balance.get_Account_Sum $ Balance.exclusive bal))
                                -- NOTE: worth if Account has at least more than
                                -- one descendant Account whose inclusive
                                -- has at least a non-zero Amount
-                               || Data.Map.size
-                                        ( Data.Map.filter
+                               || Map.size
+                                        ( Map.filter
                                                 ( Strict.maybe False
                                                         ( Data.Foldable.any
-                                                                (not . Amount.is_zero . Amount.sum_balance)
+                                                                (not . Quantity.quantity_null . Polarize.depolarize)
                                                         . Balance.get_Account_Sum
                                                         . Balance.inclusive )
                                                 . TreeMap.node_value )
                                                 descendants
                                         ) > 1
-                       in
-                       (&&) is_worth $
-                       Data.Foldable.any (Filter.test flt . (acct,)) $
-                               Balance.get_Account_Sum $
-                               Balance.inclusive balance
-                )
-       . Balance.expanded
-
-ledger_balance_by_unit
- :: Ctx
- -> Balance.Balance_by_Account (Amount.Sum Amount)
- -> Balance.Balance_by_Unit    (Amount.Sum Amount)
-ledger_balance_by_unit _ctx =
-       flip Balance.by_unit_of_by_account mempty
-
-ledger_balance_by_unit_expanded
- :: Ctx
- -> Balance.Expanded        (Amount.Sum Amount)
- -> Balance.Balance_by_Unit (Amount.Sum Amount)
-ledger_balance_by_unit_expanded _ctx =
-       flip Balance.by_unit_of_expanded mempty
-
-table_by_account
- :: Ctx
- -> (amount -> Balance.Account_Sum (Amount.Sum Amount))
- -> TreeMap Account.Name amount
- -> [[Table.Cell]]
- -> [[Table.Cell]]
-table_by_account _ctx get_Account_Sum =
-       let posting_type = Posting.Posting_Type_Regular in
-       flip $ TreeMap.foldr_with_Path
-        (\account balance rows ->
-               foldr
-                (\(amount_positive, amount_negative, amount) ->
+instance Format.Journal_Filter Context (Const Forall_Journal_Balance_by_Account_Expanded) () where
+       journal_filter ctx
+        (Const (Forall_Journal_Balance_by_Account_Expanded j)) =
+               Const $ Forall_Journal_Balance_by_Account_Expanded $
+               Format.journal_filter ctx j
+
+-- Instances 'Format.Journal_Leijen_Table_Cells'
+
+instance Format.Journal_Leijen_Table_Cells (Const Forall_Journal_Balance_by_Account_Expanded) x where
+       journal_leijen_table_cells
+        (Const (Forall_Journal_Balance_by_Account_Expanded j)) =
+               Format.journal_leijen_table_cells j
+
+-- Instances Balance.Balance_by_Account -> Balance.Expanded
+
+instance
+ ( Functor j
+ , Journal_Balance_by_Account_Expanded j (Balance.Expanded as u q)
+ -- NOTE: constraints from Balance.expanded
+ , Ord as
+ , Ord u
+ , Quantity.Addable q
+ ) => Format.Journal_Wrap (j (Balance.Balance_by_Account as u q))
+                          Forall_Journal_Balance_by_Account_Expanded where
+       journal_wrap =
+               Forall_Journal_Balance_by_Account_Expanded .
+               fmap Balance.expanded
+
+instance Format.Journal_Wrap Forall_Journal_Balance_by_Account
+                             Forall_Journal_Balance_by_Account_Expanded where
+       journal_wrap (Forall_Journal_Balance_by_Account j) = Format.journal_wrap j
+
+
+
+
+
+
+
+
+
+
+
+-- * 'Balance.Balance_by_Unit'
+
+type Format_Journal_Balance_by_Unit
+ = Format
+   (   JCC.Journal Balance_by_Unit_JCC)
+   (Ledger.Journal Balance_by_Unit_Ledger)
+
+-- JCC
+type Balance_by_Unit_JCC
+ = Balance.Balance_by_Unit JCC.Account
+                           JCC.Unit
+                           (Polarized JCC.Quantity)
+instance Format.Journal (JCC.Journal Balance_by_Unit_JCC) where
+       type Journal_Format   (JCC.Journal Balance_by_Unit_JCC)
+        = Format_Journal_Balance_by_Unit
+       journal_format = Format_JCC
+
+-- Ledger
+type Balance_by_Unit_Ledger
+ = Balance.Balance_by_Unit Ledger.Account
+                           Ledger.Unit
+                           (Polarized Ledger.Quantity)
+instance Format.Journal (Ledger.Journal Balance_by_Unit_Ledger) where
+       type Journal_Format   (Ledger.Journal Balance_by_Unit_Ledger)
+        = Format_Journal_Balance_by_Unit
+       journal_format = Format_Ledger
+
+-- ** Class 'Journal_Balance_by_Unit'
+
+class
+ ( Format.Journal (j m)
+ , Format.Journal_Format (j m) ~ Format_Journal_Balance_by_Unit
+ , Format.Journal_Leijen_Table_Cells j m
+ -- , Journal_Equilibrium_Postings j m
+ )
+ => Journal_Balance_by_Unit j m
+
+instance Journal_Balance_by_Unit    JCC.Journal Balance_by_Unit_JCC
+instance Journal_Balance_by_Unit Ledger.Journal Balance_by_Unit_Ledger
+
+-- ** Type 'Forall_Journal_Balance_by_Unit'
+
+data    Forall_Journal_Balance_by_Unit
+ = forall j m. Journal_Balance_by_Unit  j m
+ =>     Forall_Journal_Balance_by_Unit (j m)
+
+instance Format.Journal Forall_Journal_Balance_by_Unit where
+       type Journal_Format   Forall_Journal_Balance_by_Unit = Format_Journal_Balance_by_Unit
+       journal_format (Forall_Journal_Balance_by_Unit j) = Format.journal_format j
+
+-- Instances Balance.Balance_by_Account -> Balance.Balance_by_Unit
+
+instance
+ ( Functor j
+ , Journal_Balance_by_Unit j (Balance.Balance_by_Unit (Account.Account_Path as) u q)
+ -- NOTE: constraints from Balance.by_unit_of_by_account
+ , Account.Account (Account.Account_Path as)
+ , Ord as
+ , Ord u
+ , Quantity.Addable q
+ ) => Format.Journal_Wrap (j (Balance.Balance_by_Account as u q))
+                          Forall_Journal_Balance_by_Unit where
+       journal_wrap =
+               Forall_Journal_Balance_by_Unit .
+               fmap (flip Balance.by_unit_of_by_account mempty)
+
+instance Format.Journal_Wrap Forall_Journal_Balance_by_Account
+                             Forall_Journal_Balance_by_Unit where
+       journal_wrap
+        (Forall_Journal_Balance_by_Account j) =
+               Format.journal_wrap j
+
+-- Instances Balance.Expanded -> Balance.Balance_by_Unit
+
+instance
+ ( Functor j
+ , Journal_Balance_by_Unit j (Balance.Balance_by_Unit (Account.Account_Path as) u q)
+ -- NOTE: constraints from Balance.by_unit_of_expanded
+ , Account.Account (Account.Account_Path as)
+ , Ord as
+ , Ord u
+ , Quantity.Addable q
+ ) => Format.Journal_Wrap (j (Balance.Expanded as u q))
+                          Forall_Journal_Balance_by_Unit where
+       journal_wrap =
+               Forall_Journal_Balance_by_Unit .
+               fmap (flip Balance.by_unit_of_expanded mempty)
+
+instance Format.Journal_Wrap Forall_Journal_Balance_by_Account_Expanded
+                             Forall_Journal_Balance_by_Unit where
+       journal_wrap
+        (Forall_Journal_Balance_by_Account_Expanded j) =
+               Format.journal_wrap j
+
+-- Instances 'Format.Journal_Leijen_Table_Cells'
+
+instance
+ ( Format.Journal_Content j
+ , Journal j
+ , a ~ Format.Journal_Account j
+ , Account.Account a
+ , u ~ Format.Journal_Unit j
+ , Ord u
+ , q ~ Format.Journal_Quantity j
+ , Quantity.Addable (Format.Journal_Quantity j)
+ ) => Format.Journal_Leijen_Table_Cells j (Balance.Balance_by_Unit a u (Polarized q)) where
+       journal_leijen_table_cells jnl acc =
+               let Balance.Balance_by_Unit bal = Format.journal_content jnl in
+               Map.foldrWithKey
+                (\unit amt ->
+                       let qty = Balance.unit_sum_quantity amt in
                        zipWith (:)
-                               [ Table.cell
-                                { Table.cell_content = maybe W.empty Amount.Write.amount  amount_positive
-                                , Table.cell_width   = maybe 0 Amount.Write.amount_length amount_positive
-                                }
-                               , Table.cell
-                                { Table.cell_content = maybe W.empty Amount.Write.amount  amount_negative
-                                , Table.cell_width   = maybe 0 Amount.Write.amount_length amount_negative
-                                }
-                               , Table.cell
-                                { Table.cell_content = Amount.Write.amount        $ amount
-                                , Table.cell_width   = Amount.Write.amount_length $ amount
-                                }
-                               , Table.cell
-                                { Table.cell_content = Ledger.Write.account        posting_type account
-                                , Table.cell_width   = Ledger.Write.account_length posting_type account
-                                }
+                               [ Leijen.Table.cell_of_forall_param jnl $ (unit,) <$> Polarize.polarized_positive qty
+                               , Leijen.Table.cell_of_forall_param jnl $ (unit,) <$> Polarize.polarized_negative qty
+                               , Leijen.Table.cell_of_forall_param jnl (unit, Polarize.depolarize qty)
+                               , Leijen.Table.cell
                                ]
-                )
-                rows $
-               let bal = Balance.get_Account_Sum $ get_Account_Sum balance in
-               Data.Map.foldrWithKey
-                (\unit amount acc ->
-                       ( maybe Nothing Amount.sum_positive $ Data.Map.lookup unit $ bal
-                       , maybe Nothing Amount.sum_negative $ Data.Map.lookup unit $ bal
-                       , Amount.sum_balance amount
-                       ) : acc
-                ) [] $ bal
-        )
-
-table_by_unit
- :: Data.Map.Map Unit (Amount.Sum Amount)
- -> [[Table.Cell]]
- -> [[Table.Cell]]
-table_by_unit =
-       flip $ foldr
-        (\amount_sum ->
-               zipWith (:)
-                       [ let amt = Amount.sum_positive amount_sum in
-                               Table.cell
-                                { Table.cell_content = maybe W.empty Amount.Write.amount  amt
-                                , Table.cell_width   = maybe 0 Amount.Write.amount_length amt
-                                }
-                       , let amt = Amount.sum_negative amount_sum in
-                               Table.cell
-                                { Table.cell_content = maybe W.empty Amount.Write.amount  amt
-                                , Table.cell_width   = maybe 0 Amount.Write.amount_length amt
-                                }
-                       , let amt = Amount.sum_balance amount_sum in
-                               Table.cell
-                                { Table.cell_content = Amount.Write.amount        amt
-                                , Table.cell_width   = Amount.Write.amount_length amt
-                                }
-                       , Table.cell
-                                { Table.cell_content = W.empty
-                                , Table.cell_width   = 0
-                                }
+                ) acc bal
+instance Format.Journal_Leijen_Table_Cells (Const Forall_Journal_Balance_by_Unit) () where
+       journal_leijen_table_cells
+        (Const (Forall_Journal_Balance_by_Unit j)) =
+               Format.journal_leijen_table_cells j
+
+
+
+
+
+
+
+
+
+-- * Class 'Journal'
+
+class
+ ( Leijen.Table.Cell_of_forall_param j (Format.Journal_Unit j, Format.Journal_Quantity j)
+ , W.Leijen_of_forall_param j [Format.Journal_Transaction j]
+ ) => Journal (j:: * -> *) where
+       journal_posting
+        :: forall m. j m
+        -> Account.Account_Path (Format.Journal_Account_Section j)
+        -> Map (Format.Journal_Unit j)
+               (Format.Journal_Quantity j)
+        -> [Text] -- ^ Comments
+        -> Format.Journal_Posting j
+       journal_transaction
+        :: forall m. j m
+        -> Text -- ^ Wording
+        -> (Date, [Date])
+        -> Map (Account.Account_Path (Format.Journal_Account_Section j))
+               [Format.Journal_Posting j]
+        -> Format.Journal_Transaction j
+
+instance Journal JCC.Journal where
+       journal_posting _j acct
+        posting_amounts
+        posting_comments =
+               (JCC.posting acct)
+                { JCC.posting_amounts
+                , JCC.posting_comments
+                }
+       journal_transaction _j
+        transaction_wording
+        transaction_dates
+        transaction_postings =
+               JCC.transaction
+                { JCC.transaction_wording
+                , JCC.transaction_dates
+                , JCC.transaction_postings
+                }
+instance Journal Ledger.Journal where
+       journal_posting _j acct
+        posting_amounts
+        posting_comments =
+               (Ledger.posting acct)
+                { Ledger.posting_amounts
+                , Ledger.posting_comments
+                }
+       journal_transaction _j
+        transaction_wording
+        transaction_dates
+        transaction_postings =
+               Ledger.transaction
+                { Ledger.transaction_wording
+                , Ledger.transaction_dates
+                , Ledger.transaction_postings
+                }
+
+
+
+
+
+
+
+
+
+
+
+
+
+-- * Class 'Journal_Equilibrium_Transaction'
+
+class Journal_Equilibrium_Transaction j m where
+       journal_equilibrium_transaction
+        :: j m
+        -> C.Context
+        -> Context
+        -> Lang.Exercise_OC
+        -> Date
+        -> W.Doc
+
+instance
+ ( Format.Journal_Content j
+ , Journal j
+ , as ~ Format.Journal_Account_Section j
+ , Format.Journal_Account_Section j ~ Text
+ , Format.Journal_Account j ~ TreeMap.Path Text
+ , Num quantity
+ , quantity ~ Format.Journal_Quantity j
+ , Ord unit
+ , Ord quantity
+ , Quantity.Zero (Format.Journal_Quantity j)
+ , Quantity.Addable (Format.Journal_Quantity j)
+ , unit ~ Format.Journal_Unit j
+ ) => Journal_Equilibrium_Transaction
+ j (Balance.Balance_by_Account as unit (Polarized quantity)) where
+       journal_equilibrium_transaction
+        j c ctx oc now =
+               let bal_by_account = Format.journal_content j in
+               let Balance.Balance_by_Unit bal_by_unit =
+                       Balance.by_unit_of_by_account bal_by_account mempty in
+               let postings =
+                       Map.foldlWithKey
+                        (\acc unit Balance.Unit_Sum{Balance.unit_sum_quantity} ->
+                               let qty =
+                                       (case oc of
+                                        Lang.Exercise_Closing -> id
+                                        Lang.Exercise_Opening -> negate) $
+                                       Polarize.depolarize unit_sum_quantity in
+                               case Quantity.quantity_sign qty of
+                                LT ->
+                                       let account = snd $ ctx_account_equilibrium ctx in
+                                       Map.insertWith mappend account
+                                        [journal_posting j account
+                                                (Map.singleton unit qty)
+                                                [ Lang.translate (C.lang c) Lang.Comment_Equilibrium ]]
+                                        acc
+                                EQ -> acc
+                                GT ->
+                                       let account = fst $ ctx_account_equilibrium ctx in
+                                       Map.insertWith mappend account
+                                        [journal_posting j account
+                                                (Map.singleton unit qty)
+                                                [ Lang.translate (C.lang c) Lang.Comment_Equilibrium ]]
+                                        acc
+                        )
+                        Map.empty
+                        bal_by_unit
+                in
+               W.leijen_of_forall_param j [
+               journal_transaction j
+                (Lang.translate (C.lang c) (Lang.Description_Exercise oc))
+                (now{Time.utctDayTime=0}, []) $
+               Map.unionWith mappend postings $
+               TreeMap.flatten_with_Path
+                (\posting_account (Balance.Account_Sum amount_by_unit) ->
+                       [ journal_posting j posting_account
+                                (flip fmap amount_by_unit $
+                                       (case oc of
+                                        Lang.Exercise_Closing -> negate
+                                        Lang.Exercise_Opening -> id)
+                                       . Polarize.depolarize)
+                                []
                        ]
-        )
+                )
+                bal_by_account
+                ]
+
+instance Journal_Equilibrium_Transaction (Const Forall_Journal_Balance_by_Account) () where
+       journal_equilibrium_transaction
+        (Const (Forall_Journal_Balance_by_Account j)) =
+               journal_equilibrium_transaction j
+
+{-
+instance
+  ToDoc (C.Context, Context, Date, Lang.Exercise_OC)
+        ( Forall_Journal_Balance_by_Account
+        , Forall_Journal_Balance_by_Unit ) where
+       toDoc c
+        ( Forall_Journal_Balance_by_Account bal_by_account
+        , Forall_Journal_Balance_by_Unit    bal_by_unit
+        ) =
+               toDoc c (bal_by_account, bal_by_unit)
+-}