1 {-| 
    2 Hledger.Cli re-exports the options, utilities and commands provided by the
    3 hledger command-line program.
    4 -}
    5 
    6 module Hledger.Cli (
    7                      module Hledger.Cli.Add,
    8                      module Hledger.Cli.Balance,
    9                      module Hledger.Cli.Convert,
   10                      module Hledger.Cli.Histogram,
   11                      module Hledger.Cli.Print,
   12                      module Hledger.Cli.Register,
   13                      module Hledger.Cli.Stats,
   14                      module Hledger.Cli.Options,
   15                      module Hledger.Cli.Utils,
   16                      module Hledger.Cli.Version,
   17                      tests_Hledger_Cli
   18               )
   19 where
   20 import Control.Monad
   21 import qualified Data.Map as Map
   22 import Data.Time.Calendar
   23 import System.Time (ClockTime(TOD))
   24 import Test.HUnit
   25 
   26 import Hledger
   27 import Hledger.Cli.Add
   28 import Hledger.Cli.Balance
   29 import Hledger.Cli.Convert
   30 import Hledger.Cli.Histogram
   31 import Hledger.Cli.Print
   32 import Hledger.Cli.Register
   33 import Hledger.Cli.Stats
   34 import Hledger.Cli.Options
   35 import Hledger.Cli.Utils
   36 import Hledger.Cli.Version
   37 
   38 -- | hledger and hledger-lib's unit tests aggregated from all modules
   39 -- plus some more which are easier to define here for now.
   40 tests_Hledger_Cli :: Test
   41 tests_Hledger_Cli = TestList
   42  [
   43     tests_Hledger_Data
   44    ,tests_Hledger_Read
   45    -- ,tests_Hledger_Cli_Add
   46    -- ,tests_Hledger_Cli_Balance
   47    ,tests_Hledger_Cli_Convert
   48    -- ,tests_Hledger_Cli_Histogram
   49    ,tests_Hledger_Cli_Options
   50    -- ,tests_Hledger_Cli_Print
   51    ,tests_Hledger_Cli_Register
   52    -- ,tests_Hledger_Cli_Stats
   53 
   54 
   55    ,"account directive" ~:
   56    let sameParse str1 str2 = do j1 <- readJournal Nothing str1 >>= either error' return
   57                                 j2 <- readJournal Nothing str2 >>= either error' return
   58                                 j1 `is` j2{filereadtime=filereadtime j1, files=files j1, jContext=jContext j1}
   59    in TestList
   60    [
   61     "account directive 1" ~: sameParse 
   62                           "2008/12/07 One\n  test:from  $-1\n  test:to  $1\n"
   63                           "!account test\n2008/12/07 One\n  from  $-1\n  to  $1\n"
   64 
   65    ,"account directive 2" ~: sameParse 
   66                            "2008/12/07 One\n  test:foo:from  $-1\n  test:foo:to  $1\n"
   67                            "!account test\n!account foo\n2008/12/07 One\n  from  $-1\n  to  $1\n"
   68 
   69    ,"account directive 3" ~: sameParse 
   70                            "2008/12/07 One\n  test:from  $-1\n  test:to  $1\n"
   71                            "!account test\n!account foo\n!end\n2008/12/07 One\n  from  $-1\n  to  $1\n"
   72 
   73    ,"account directive 4" ~: sameParse 
   74                            ("2008/12/07 One\n  alpha  $-1\n  beta  $1\n" ++
   75                             "!account outer\n2008/12/07 Two\n  aigh  $-2\n  bee  $2\n" ++
   76                             "!account inner\n2008/12/07 Three\n  gamma  $-3\n  delta  $3\n" ++
   77                             "!end\n2008/12/07 Four\n  why  $-4\n  zed  $4\n" ++
   78                             "!end\n2008/12/07 Five\n  foo  $-5\n  bar  $5\n"
   79                            )
   80                            ("2008/12/07 One\n  alpha  $-1\n  beta  $1\n" ++
   81                             "2008/12/07 Two\n  outer:aigh  $-2\n  outer:bee  $2\n" ++
   82                             "2008/12/07 Three\n  outer:inner:gamma  $-3\n  outer:inner:delta  $3\n" ++
   83                             "2008/12/07 Four\n  outer:why  $-4\n  outer:zed  $4\n" ++
   84                             "2008/12/07 Five\n  foo  $-5\n  bar  $5\n"
   85                            )
   86 
   87    ,"account directive should preserve \"virtual\" posting type" ~: do
   88       j <- readJournal Nothing "!account test\n2008/12/07 One\n  (from)  $-1\n  (to)  $1\n" >>= either error' return
   89       let p = head $ tpostings $ head $ jtxns j
   90       assertBool "" $ (paccount p) == "test:from"
   91       assertBool "" $ (ptype p) == VirtualPosting
   92 
   93    ]
   94 
   95    ,"account aliases" ~: do
   96       Right j <- readJournal Nothing "!alias expenses = equity:draw:personal\n1/1\n (expenses:food)  1\n"
   97       let p = head $ tpostings $ head $ jtxns j
   98       assertBool "" $ paccount p == "equity:draw:personal:food"
   99 
  100   ,"ledgerAccountNames" ~:
  101     ledgerAccountNames ledger7 `is`
  102      ["assets","assets:cash","assets:checking","assets:saving","equity","equity:opening balances",
  103       "expenses","expenses:food","expenses:food:dining","expenses:phone","expenses:vacation",
  104       "liabilities","liabilities:credit cards","liabilities:credit cards:discover"]
  105 
  106   ,"balance report tests" ~:
  107    let opts `gives` es = do
  108         j <- samplejournal
  109         d <- getCurrentDay
  110         accountsReportAsText opts (accountsReport opts (optsToFilterSpec opts d) j) `is` es
  111    in TestList
  112    [
  113     "balance report with no args" ~:
  114     defreportopts `gives`
  115     ["                 $-1  assets"
  116     ,"                  $1    bank:saving"
  117     ,"                 $-2    cash"
  118     ,"                  $2  expenses"
  119     ,"                  $1    food"
  120     ,"                  $1    supplies"
  121     ,"                 $-2  income"
  122     ,"                 $-1    gifts"
  123     ,"                 $-1    salary"
  124     ,"                  $1  liabilities:debts"
  125     ,"--------------------"
  126     ,"                   0"
  127     ]
  128 
  129    ,"balance report can be limited with --depth" ~:
  130     defreportopts{depth_=Just 1} `gives`
  131     ["                 $-1  assets"
  132     ,"                  $2  expenses"
  133     ,"                 $-2  income"
  134     ,"                  $1  liabilities"
  135     ,"--------------------"
  136     ,"                   0"
  137     ]
  138     
  139    ,"balance report with account pattern o" ~:
  140     defreportopts{patterns_=["o"]} `gives`
  141     ["                  $1  expenses:food"
  142     ,"                 $-2  income"
  143     ,"                 $-1    gifts"
  144     ,"                 $-1    salary"
  145     ,"--------------------"
  146     ,"                 $-1"
  147     ]
  148 
  149    ,"balance report with account pattern o and --depth 1" ~:
  150     defreportopts{patterns_=["o"],depth_=Just 1} `gives`
  151     ["                  $1  expenses"
  152     ,"                 $-2  income"
  153     ,"--------------------"
  154     ,"                 $-1"
  155     ]
  156 
  157    ,"balance report with account pattern a" ~:
  158     defreportopts{patterns_=["a"]} `gives`
  159     ["                 $-1  assets"
  160     ,"                  $1    bank:saving"
  161     ,"                 $-2    cash"
  162     ,"                 $-1  income:salary"
  163     ,"                  $1  liabilities:debts"
  164     ,"--------------------"
  165     ,"                 $-1"
  166     ]
  167 
  168    ,"balance report with account pattern e" ~:
  169     defreportopts{patterns_=["e"]} `gives`
  170     ["                 $-1  assets"
  171     ,"                  $1    bank:saving"
  172     ,"                 $-2    cash"
  173     ,"                  $2  expenses"
  174     ,"                  $1    food"
  175     ,"                  $1    supplies"
  176     ,"                 $-2  income"
  177     ,"                 $-1    gifts"
  178     ,"                 $-1    salary"
  179     ,"                  $1  liabilities:debts"
  180     ,"--------------------"
  181     ,"                   0"
  182     ]
  183 
  184    ,"balance report with unmatched parent of two matched subaccounts" ~: 
  185     defreportopts{patterns_=["cash","saving"]} `gives`
  186     ["                 $-1  assets"
  187     ,"                  $1    bank:saving"
  188     ,"                 $-2    cash"
  189     ,"--------------------"
  190     ,"                 $-1"
  191     ]
  192 
  193    ,"balance report with multi-part account name" ~: 
  194     defreportopts{patterns_=["expenses:food"]} `gives`
  195     ["                  $1  expenses:food"
  196     ,"--------------------"
  197     ,"                  $1"
  198     ]
  199 
  200    ,"balance report with negative account pattern" ~:
  201     defreportopts{patterns_=["not:assets"]} `gives`
  202     ["                  $2  expenses"
  203     ,"                  $1    food"
  204     ,"                  $1    supplies"
  205     ,"                 $-2  income"
  206     ,"                 $-1    gifts"
  207     ,"                 $-1    salary"
  208     ,"                  $1  liabilities:debts"
  209     ,"--------------------"
  210     ,"                  $1"
  211     ]
  212 
  213    ,"balance report negative account pattern always matches full name" ~: 
  214     defreportopts{patterns_=["not:e"]} `gives`
  215     ["--------------------"
  216     ,"                   0"
  217     ]
  218 
  219    ,"balance report negative patterns affect totals" ~: 
  220     defreportopts{patterns_=["expenses","not:food"]} `gives`
  221     ["                  $1  expenses:supplies"
  222     ,"--------------------"
  223     ,"                  $1"
  224     ]
  225 
  226    ,"balance report with -E shows zero-balance accounts" ~:
  227     defreportopts{patterns_=["assets"],empty_=True} `gives`
  228     ["                 $-1  assets"
  229     ,"                  $1    bank"
  230     ,"                   0      checking"
  231     ,"                  $1      saving"
  232     ,"                 $-2    cash"
  233     ,"--------------------"
  234     ,"                 $-1"
  235     ]
  236 
  237    ,"balance report with cost basis" ~: do
  238       j <- (readJournal Nothing $ unlines
  239              [""
  240              ,"2008/1/1 test           "
  241              ,"  a:b          10h @ $50"
  242              ,"  c:d                   "
  243              ]) >>= either error' return
  244       let j' = journalCanonicaliseAmounts $ journalConvertAmountsToCost j -- enable cost basis adjustment
  245       accountsReportAsText defreportopts (accountsReport defreportopts nullfilterspec j') `is`
  246         ["                $500  a:b"
  247         ,"               $-500  c:d"
  248         ,"--------------------"
  249         ,"                   0"
  250         ]
  251 
  252    ,"balance report elides zero-balance root account(s)" ~: do
  253       j <- readJournal'
  254              (unlines
  255               ["2008/1/1 one"
  256               ,"  test:a  1"
  257               ,"  test:b"
  258               ])
  259       accountsReportAsText defreportopts (accountsReport defreportopts nullfilterspec j) `is`
  260         ["                   1  test:a"
  261         ,"                  -1  test:b"
  262         ,"--------------------"
  263         ,"                   0"
  264         ]
  265 
  266    ]
  267 
  268   ,"journalCanonicaliseAmounts" ~:
  269    "use the greatest precision" ~:
  270     (map precision $ journalAmountAndPriceCommodities $ journalCanonicaliseAmounts $ journalWithAmounts ["1","2.00"]) `is` [2,2]
  271 
  272   ,"commodities" ~:
  273     Map.elems (commodities ledger7) `is` [Commodity {symbol="$", side=L, spaced=False, decimalpoint='.', precision=2, separator=',', separatorpositions=[]}]
  274 
  275   -- don't know what this should do
  276   -- ,"elideAccountName" ~: do
  277   --    (elideAccountName 50 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"
  278   --     `is` "aa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa")
  279   --    (elideAccountName 20 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"
  280   --     `is` "aa:aa:aaaaaaaaaaaaaa")
  281 
  282   ,"default year" ~: do
  283     j <- readJournal Nothing defaultyear_journal_str >>= either error' return
  284     tdate (head $ jtxns j) `is` fromGregorian 2009 1 1
  285     return ()
  286 
  287   ,"print report tests" ~: TestList
  288   [
  289 
  290    "print expenses" ~:
  291    do 
  292     let opts = defreportopts{patterns_=["expenses"]}
  293     j <- samplejournal
  294     d <- getCurrentDay
  295     showTransactions opts (optsToFilterSpec opts d) j `is` unlines
  296      ["2008/06/03 * eat & shop"
  297      ,"    expenses:food                $1"
  298      ,"    expenses:supplies            $1"
  299      ,"    assets:cash                 $-2"
  300      ,""
  301      ]
  302 
  303   , "print report with depth arg" ~:
  304    do 
  305     let opts = defreportopts{depth_=Just 2}
  306     j <- samplejournal
  307     d <- getCurrentDay
  308     showTransactions opts (optsToFilterSpec opts d) j `is` unlines
  309       ["2008/01/01 income"
  310       ,"    income:salary           $-1"
  311       ,""
  312       ,"2008/06/01 gift"
  313       ,"    income:gifts           $-1"
  314       ,""
  315       ,"2008/06/03 * eat & shop"
  316       ,"    expenses:food                $1"
  317       ,"    expenses:supplies            $1"
  318       ,"    assets:cash                 $-2"
  319       ,""
  320       ,"2008/12/31 * pay off"
  321       ,"    liabilities:debts            $1"
  322       ,""
  323       ]
  324 
  325   ]
  326 
  327   ,"register report tests" ~:
  328   let registerdates = filter (not . null) .  map (strip . take 10) . lines
  329   in
  330   TestList
  331   [
  332 
  333    "register report with no args" ~:
  334    do 
  335     j <- samplejournal
  336     let opts = defreportopts
  337     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  338      ["2008/01/01 income               assets:bank:checking             $1           $1"
  339      ,"                                income:salary                   $-1            0"
  340      ,"2008/06/01 gift                 assets:bank:checking             $1           $1"
  341      ,"                                income:gifts                    $-1            0"
  342      ,"2008/06/02 save                 assets:bank:saving               $1           $1"
  343      ,"                                assets:bank:checking            $-1            0"
  344      ,"2008/06/03 eat & shop           expenses:food                    $1           $1"
  345      ,"                                expenses:supplies                $1           $2"
  346      ,"                                assets:cash                     $-2            0"
  347      ,"2008/12/31 pay off              liabilities:debts                $1           $1"
  348      ,"                                assets:bank:checking            $-1            0"
  349      ]
  350 
  351   ,"register report with cleared option" ~:
  352    do 
  353     let opts = defreportopts{cleared_=True}
  354     j <- readJournal' sample_journal_str
  355     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  356      ["2008/06/03 eat & shop           expenses:food                    $1           $1"
  357      ,"                                expenses:supplies                $1           $2"
  358      ,"                                assets:cash                     $-2            0"
  359      ,"2008/12/31 pay off              liabilities:debts                $1           $1"
  360      ,"                                assets:bank:checking            $-1            0"
  361      ]
  362 
  363   ,"register report with uncleared option" ~:
  364    do 
  365     let opts = defreportopts{uncleared_=True}
  366     j <- readJournal' sample_journal_str
  367     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  368      ["2008/01/01 income               assets:bank:checking             $1           $1"
  369      ,"                                income:salary                   $-1            0"
  370      ,"2008/06/01 gift                 assets:bank:checking             $1           $1"
  371      ,"                                income:gifts                    $-1            0"
  372      ,"2008/06/02 save                 assets:bank:saving               $1           $1"
  373      ,"                                assets:bank:checking            $-1            0"
  374      ]
  375 
  376   ,"register report sorts by date" ~:
  377    do 
  378     j <- readJournal' $ unlines
  379         ["2008/02/02 a"
  380         ,"  b  1"
  381         ,"  c"
  382         ,""
  383         ,"2008/01/01 d"
  384         ,"  e  1"
  385         ,"  f"
  386         ]
  387     let opts = defreportopts
  388     registerdates (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` ["2008/01/01","2008/02/02"]
  389 
  390   ,"register report with account pattern" ~:
  391    do
  392     j <- samplejournal
  393     let opts = defreportopts{patterns_=["cash"]}
  394     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  395      ["2008/06/03 eat & shop           assets:cash                     $-2          $-2"
  396      ]
  397 
  398   ,"register report with account pattern, case insensitive" ~:
  399    do 
  400     j <- samplejournal
  401     let opts = defreportopts{patterns_=["cAsH"]}
  402     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  403      ["2008/06/03 eat & shop           assets:cash                     $-2          $-2"
  404      ]
  405 
  406   ,"register report with display expression" ~:
  407    do 
  408     j <- samplejournal
  409     let gives displayexpr = 
  410             (registerdates (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is`)
  411                 where opts = defreportopts{display_=Just displayexpr}
  412     "d<[2008/6/2]"  `gives` ["2008/01/01","2008/06/01"]
  413     "d<=[2008/6/2]" `gives` ["2008/01/01","2008/06/01","2008/06/02"]
  414     "d=[2008/6/2]"  `gives` ["2008/06/02"]
  415     "d>=[2008/6/2]" `gives` ["2008/06/02","2008/06/03","2008/12/31"]
  416     "d>[2008/6/2]"  `gives` ["2008/06/03","2008/12/31"]
  417 
  418   ,"register report with period expression" ~:
  419    do 
  420     j <- samplejournal
  421     let periodexpr `gives` dates = do
  422           j' <- samplejournal
  423           registerdates (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j') `is` dates
  424               where opts = defreportopts{period_=maybePeriod date1 periodexpr}
  425     ""     `gives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"]
  426     "2008" `gives` ["2008/01/01","2008/06/01","2008/06/02","2008/06/03","2008/12/31"]
  427     "2007" `gives` []
  428     "june" `gives` ["2008/06/01","2008/06/02","2008/06/03"]
  429     "monthly" `gives` ["2008/01/01","2008/06/01","2008/12/01"]
  430     "quarterly" `gives` ["2008/01/01","2008/04/01","2008/10/01"]
  431     let opts = defreportopts{period_=maybePeriod date1 "yearly"}
  432     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  433      ["2008/01/01 - 2008/12/31         assets:bank:saving               $1           $1"
  434      ,"                                assets:cash                     $-2          $-1"
  435      ,"                                expenses:food                    $1            0"
  436      ,"                                expenses:supplies                $1           $1"
  437      ,"                                income:gifts                    $-1            0"
  438      ,"                                income:salary                   $-1          $-1"
  439      ,"                                liabilities:debts                $1            0"
  440      ]
  441     let opts = defreportopts{period_=maybePeriod date1 "quarterly"}
  442     registerdates (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` ["2008/01/01","2008/04/01","2008/10/01"]
  443     let opts = defreportopts{period_=maybePeriod date1 "quarterly",empty_=True}
  444     registerdates (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` ["2008/01/01","2008/04/01","2008/07/01","2008/10/01"]
  445 
  446   ]
  447 
  448   , "register report with depth arg" ~:
  449    do 
  450     j <- samplejournal
  451     let opts = defreportopts{depth_=Just 2}
  452     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  453      ["2008/01/01 income               assets:bank                      $1           $1"
  454      ,"                                income:salary                   $-1            0"
  455      ,"2008/06/01 gift                 assets:bank                      $1           $1"
  456      ,"                                income:gifts                    $-1            0"
  457      ,"2008/06/02 save                 assets:bank                      $1           $1"
  458      ,"                                assets:bank                     $-1            0"
  459      ,"2008/06/03 eat & shop           expenses:food                    $1           $1"
  460      ,"                                expenses:supplies                $1           $2"
  461      ,"                                assets:cash                     $-2            0"
  462      ,"2008/12/31 pay off              liabilities:debts                $1           $1"
  463      ,"                                assets:bank                     $-1            0"
  464      ]
  465 
  466   ,"show dollars" ~: show (dollars 1) ~?= "$1.00"
  467 
  468   ,"show hours" ~: show (hours 1) ~?= "1.0h"
  469 
  470   ,"unicode in balance layout" ~: do
  471     j <- readJournal'
  472       "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n"
  473     let opts = defreportopts
  474     accountsReportAsText opts (accountsReport opts (optsToFilterSpec opts date1) j) `is`
  475       ["                -100  актив:наличные"
  476       ,"                 100  расходы:покупки"
  477       ,"--------------------"
  478       ,"                   0"
  479       ]
  480 
  481   ,"unicode in register layout" ~: do
  482     j <- readJournal'
  483       "2009/01/01 * медвежья шкура\n  расходы:покупки  100\n  актив:наличные\n"
  484     let opts = defreportopts
  485     (postingsReportAsText opts $ postingsReport opts (optsToFilterSpec opts date1) j) `is` unlines
  486       ["2009/01/01 медвежья шкура       расходы:покупки                 100          100"
  487       ,"                                актив:наличные                 -100            0"]
  488 
  489   ,"subAccounts" ~: do
  490     l <- liftM (journalToLedger nullfilterspec) samplejournal
  491     let a = ledgerAccount l "assets"
  492     map aname (ledgerSubAccounts l a) `is` ["assets:bank","assets:cash"]
  493 
  494  ]
  495 
  496   
  497 -- fixtures/test data
  498 
  499 date1 = parsedate "2008/11/26"
  500 -- t1 = LocalTime date1 midday
  501 
  502 samplejournal = readJournal' sample_journal_str
  503 
  504 sample_journal_str = unlines
  505  ["; A sample journal file."
  506  ,";"
  507  ,"; Sets up this account tree:"
  508  ,"; assets"
  509  ,";   bank"
  510  ,";     checking"
  511  ,";     saving"
  512  ,";   cash"
  513  ,"; expenses"
  514  ,";   food"
  515  ,";   supplies"
  516  ,"; income"
  517  ,";   gifts"
  518  ,";   salary"
  519  ,"; liabilities"
  520  ,";   debts"
  521  ,""
  522  ,"2008/01/01 income"
  523  ,"    assets:bank:checking  $1"
  524  ,"    income:salary"
  525  ,""
  526  ,"2008/06/01 gift"
  527  ,"    assets:bank:checking  $1"
  528  ,"    income:gifts"
  529  ,""
  530  ,"2008/06/02 save"
  531  ,"    assets:bank:saving  $1"
  532  ,"    assets:bank:checking"
  533  ,""
  534  ,"2008/06/03 * eat & shop"
  535  ,"    expenses:food      $1"
  536  ,"    expenses:supplies  $1"
  537  ,"    assets:cash"
  538  ,""
  539  ,"2008/12/31 * pay off"
  540  ,"    liabilities:debts  $1"
  541  ,"    assets:bank:checking"
  542  ,""
  543  ,""
  544  ,";final comment"
  545  ]
  546 
  547 defaultyear_journal_str = unlines
  548  ["Y2009"
  549  ,""
  550  ,"01/01 A"
  551  ,"    a  $1"
  552  ,"    b"
  553  ]
  554 
  555 -- write_sample_journal = writeFile "sample.journal" sample_journal_str
  556 
  557 -- entry2_str = unlines
  558 --  ["2007/01/27 * joes diner"
  559 --  ,"    expenses:food:dining                      $10.00"
  560 --  ,"    expenses:gifts                            $10.00"
  561 --  ,"    assets:checking                          $-20.00"
  562 --  ,""
  563 --  ]
  564 
  565 -- entry3_str = unlines
  566 --  ["2007/01/01 * opening balance"
  567 --  ,"    assets:cash                                $4.82"
  568 --  ,"    equity:opening balances"
  569 --  ,""
  570 --  ,"2007/01/01 * opening balance"
  571 --  ,"    assets:cash                                $4.82"
  572 --  ,"    equity:opening balances"
  573 --  ,""
  574 --  ,"2007/01/28 coopportunity"
  575 --  ,"  expenses:food:groceries                 $47.18"
  576 --  ,"  assets:checking"
  577 --  ,""
  578 --  ]
  579 
  580 -- periodic_entry1_str = unlines
  581 --  ["~ monthly from 2007/2/2"
  582 --  ,"  assets:saving            $200.00"
  583 --  ,"  assets:checking"
  584 --  ,""
  585 --  ]
  586 
  587 -- periodic_entry2_str = unlines
  588 --  ["~ monthly from 2007/2/2"
  589 --  ,"  assets:saving            $200.00         ;auto savings"
  590 --  ,"  assets:checking"
  591 --  ,""
  592 --  ]
  593 
  594 -- periodic_entry3_str = unlines
  595 --  ["~ monthly from 2007/01/01"
  596 --  ,"    assets:cash                                $4.82"
  597 --  ,"    equity:opening balances"
  598 --  ,""
  599 --  ,"~ monthly from 2007/01/01"
  600 --  ,"    assets:cash                                $4.82"
  601 --  ,"    equity:opening balances"
  602 --  ,""
  603 --  ]
  604 
  605 -- journal1_str = unlines
  606 --  [""
  607 --  ,"2007/01/27 * joes diner"
  608 --  ,"  expenses:food:dining                    $10.00"
  609 --  ,"  expenses:gifts                          $10.00"
  610 --  ,"  assets:checking                        $-20.00"
  611 --  ,""
  612 --  ,""
  613 --  ,"2007/01/28 coopportunity"
  614 --  ,"  expenses:food:groceries                 $47.18"
  615 --  ,"  assets:checking                        $-47.18"
  616 --  ,""
  617 --  ,""
  618 --  ]
  619 
  620 -- journal2_str = unlines
  621 --  [";comment"
  622 --  ,"2007/01/27 * joes diner"
  623 --  ,"  expenses:food:dining                    $10.00"
  624 --  ,"  assets:checking                        $-47.18"
  625 --  ,""
  626 --  ]
  627 
  628 -- journal3_str = unlines
  629 --  ["2007/01/27 * joes diner"
  630 --  ,"  expenses:food:dining                    $10.00"
  631 --  ,";intra-entry comment"
  632 --  ,"  assets:checking                        $-47.18"
  633 --  ,""
  634 --  ]
  635 
  636 -- journal4_str = unlines
  637 --  ["!include \"somefile\""
  638 --  ,"2007/01/27 * joes diner"
  639 --  ,"  expenses:food:dining                    $10.00"
  640 --  ,"  assets:checking                        $-47.18"
  641 --  ,""
  642 --  ]
  643 
  644 -- journal5_str = ""
  645 
  646 -- journal6_str = unlines
  647 --  ["~ monthly from 2007/1/21"
  648 --  ,"    expenses:entertainment  $16.23        ;netflix"
  649 --  ,"    assets:checking"
  650 --  ,""
  651 --  ,"; 2007/01/01 * opening balance"
  652 --  ,";     assets:saving                            $200.04"
  653 --  ,";     equity:opening balances                         "
  654 --  ,""
  655 --  ]
  656 
  657 -- journal7_str = unlines
  658 --  ["2007/01/01 * opening balance"
  659 --  ,"    assets:cash                                $4.82"
  660 --  ,"    equity:opening balances                         "
  661 --  ,""
  662 --  ,"2007/01/01 * opening balance"
  663 --  ,"    income:interest                                $-4.82"
  664 --  ,"    equity:opening balances                         "
  665 --  ,""
  666 --  ,"2007/01/02 * ayres suites"
  667 --  ,"    expenses:vacation                        $179.92"
  668 --  ,"    assets:checking                                 "
  669 --  ,""
  670 --  ,"2007/01/02 * auto transfer to savings"
  671 --  ,"    assets:saving                            $200.00"
  672 --  ,"    assets:checking                                 "
  673 --  ,""
  674 --  ,"2007/01/03 * poquito mas"
  675 --  ,"    expenses:food:dining                       $4.82"
  676 --  ,"    assets:cash                                     "
  677 --  ,""
  678 --  ,"2007/01/03 * verizon"
  679 --  ,"    expenses:phone                            $95.11"
  680 --  ,"    assets:checking                                 "
  681 --  ,""
  682 --  ,"2007/01/03 * discover"
  683 --  ,"    liabilities:credit cards:discover         $80.00"
  684 --  ,"    assets:checking                                 "
  685 --  ,""
  686 --  ,"2007/01/04 * blue cross"
  687 --  ,"    expenses:health:insurance                 $90.00"
  688 --  ,"    assets:checking                                 "
  689 --  ,""
  690 --  ,"2007/01/05 * village market liquor"
  691 --  ,"    expenses:food:dining                       $6.48"
  692 --  ,"    assets:checking                                 "
  693 --  ,""
  694 --  ]
  695 
  696 journal7 = Journal
  697           [] 
  698           [] 
  699           [
  700            txnTieKnot $ Transaction {
  701              tdate=parsedate "2007/01/01",
  702              teffectivedate=Nothing,
  703              tstatus=False,
  704              tcode="*",
  705              tdescription="opening balance",
  706              tcomment="",
  707              tmetadata=[],
  708              tpostings=[
  709               Posting {
  710                 pstatus=False,
  711                 paccount="assets:cash",
  712                 pamount=(Mixed [dollars 4.82]),
  713                 pcomment="",
  714                 ptype=RegularPosting,
  715                 pmetadata=[],
  716                 ptransaction=Nothing
  717               },
  718               Posting {
  719                 pstatus=False,
  720                 paccount="equity:opening balances",
  721                 pamount=(Mixed [dollars (-4.82)]),
  722                 pcomment="",
  723                 ptype=RegularPosting,
  724                 pmetadata=[],
  725                 ptransaction=Nothing
  726               }
  727              ],
  728              tpreceding_comment_lines=""
  729            }
  730           ,
  731            txnTieKnot $ Transaction {
  732              tdate=parsedate "2007/02/01",
  733              teffectivedate=Nothing,
  734              tstatus=False,
  735              tcode="*",
  736              tdescription="ayres suites",
  737              tcomment="",
  738              tmetadata=[],
  739              tpostings=[
  740               Posting {
  741                 pstatus=False,
  742                 paccount="expenses:vacation",
  743                 pamount=(Mixed [dollars 179.92]),
  744                 pcomment="",
  745                 ptype=RegularPosting,
  746                 pmetadata=[],
  747                 ptransaction=Nothing
  748               },
  749               Posting {
  750                 pstatus=False,
  751                 paccount="assets:checking",
  752                 pamount=(Mixed [dollars (-179.92)]),
  753                 pcomment="",
  754                 ptype=RegularPosting,
  755                 pmetadata=[],
  756                 ptransaction=Nothing
  757               }
  758              ],
  759              tpreceding_comment_lines=""
  760            }
  761           ,
  762            txnTieKnot $ Transaction {
  763              tdate=parsedate "2007/01/02",
  764              teffectivedate=Nothing,
  765              tstatus=False,
  766              tcode="*",
  767              tdescription="auto transfer to savings",
  768              tcomment="",
  769              tmetadata=[],
  770              tpostings=[
  771               Posting {
  772                 pstatus=False,
  773                 paccount="assets:saving",
  774                 pamount=(Mixed [dollars 200]),
  775                 pcomment="",
  776                 ptype=RegularPosting,
  777                 pmetadata=[],
  778                 ptransaction=Nothing
  779               },
  780               Posting {
  781                 pstatus=False,
  782                 paccount="assets:checking",
  783                 pamount=(Mixed [dollars (-200)]),
  784                 pcomment="",
  785                 ptype=RegularPosting,
  786                 pmetadata=[],
  787                 ptransaction=Nothing
  788               }
  789              ],
  790              tpreceding_comment_lines=""
  791            }
  792           ,
  793            txnTieKnot $ Transaction {
  794              tdate=parsedate "2007/01/03",
  795              teffectivedate=Nothing,
  796              tstatus=False,
  797              tcode="*",
  798              tdescription="poquito mas",
  799              tcomment="",
  800              tmetadata=[],
  801              tpostings=[
  802               Posting {
  803                 pstatus=False,
  804                 paccount="expenses:food:dining",
  805                 pamount=(Mixed [dollars 4.82]),
  806                 pcomment="",
  807                 ptype=RegularPosting,
  808                 pmetadata=[],
  809                 ptransaction=Nothing
  810               },
  811               Posting {
  812                 pstatus=False,
  813                 paccount="assets:cash",
  814                 pamount=(Mixed [dollars (-4.82)]),
  815                 pcomment="",
  816                 ptype=RegularPosting,
  817                 pmetadata=[],
  818                 ptransaction=Nothing
  819               }
  820              ],
  821              tpreceding_comment_lines=""
  822            }
  823           ,
  824            txnTieKnot $ Transaction {
  825              tdate=parsedate "2007/01/03",
  826              teffectivedate=Nothing,
  827              tstatus=False,
  828              tcode="*",
  829              tdescription="verizon",
  830              tcomment="",
  831              tmetadata=[],
  832              tpostings=[
  833               Posting {
  834                 pstatus=False,
  835                 paccount="expenses:phone",
  836                 pamount=(Mixed [dollars 95.11]),
  837                 pcomment="",
  838                 ptype=RegularPosting,
  839                 pmetadata=[],
  840                 ptransaction=Nothing
  841               },
  842               Posting {
  843                 pstatus=False,
  844                 paccount="assets:checking",
  845                 pamount=(Mixed [dollars (-95.11)]),
  846                 pcomment="",
  847                 ptype=RegularPosting,
  848                 pmetadata=[],
  849                 ptransaction=Nothing
  850               }
  851              ],
  852              tpreceding_comment_lines=""
  853            }
  854           ,
  855            txnTieKnot $ Transaction {
  856              tdate=parsedate "2007/01/03",
  857              teffectivedate=Nothing,
  858              tstatus=False,
  859              tcode="*",
  860              tdescription="discover",
  861              tcomment="",
  862              tmetadata=[],
  863              tpostings=[
  864               Posting {
  865                 pstatus=False,
  866                 paccount="liabilities:credit cards:discover",
  867                 pamount=(Mixed [dollars 80]),
  868                 pcomment="",
  869                 ptype=RegularPosting,
  870                 pmetadata=[],
  871                 ptransaction=Nothing
  872               },
  873               Posting {
  874                 pstatus=False,
  875                 paccount="assets:checking",
  876                 pamount=(Mixed [dollars (-80)]),
  877                 pcomment="",
  878                 ptype=RegularPosting,
  879                 pmetadata=[],
  880                 ptransaction=Nothing
  881               }
  882              ],
  883              tpreceding_comment_lines=""
  884            }
  885           ]
  886           []
  887           []
  888           ""
  889           nullctx
  890           []
  891           (TOD 0 0)
  892 
  893 ledger7 = journalToLedger nullfilterspec journal7
  894 
  895 -- journal8_str = unlines
  896 --  ["2008/1/1 test           "
  897 --  ,"  a:b          10h @ $40"
  898 --  ,"  c:d                   "
  899 --  ,""
  900 --  ]
  901 
  902 -- timelogentry1_str  = "i 2007/03/11 16:19:00 hledger\n"
  903 -- timelogentry1 = TimeLogEntry In (parsedatetime "2007/03/11 16:19:00") "hledger"
  904 
  905 -- timelogentry2_str  = "o 2007/03/11 16:30:00\n"
  906 -- timelogentry2 = TimeLogEntry Out (parsedatetime "2007/03/11 16:30:00") ""
  907 
  908 -- a1 = Mixed [(hours 1){price=Just $ Mixed [Amount (comm "$") 10 Nothing]}]
  909 -- a2 = Mixed [(hours 2){price=Just $ Mixed [Amount (comm "EUR") 10 Nothing]}]
  910 -- a3 = Mixed $ amounts a1 ++ amounts a2
  911 
  912 journalWithAmounts :: [String] -> Journal
  913 journalWithAmounts as =
  914         Journal
  915         []
  916         []
  917         [t | a <- as, let t = nulltransaction{tdescription=a,tpostings=[nullposting{pamount=parse a,ptransaction=Just t}]}]
  918         []
  919         []
  920         ""
  921         nullctx
  922         []
  923         (TOD 0 0)
  924     where parse = fromparse . parseWithCtx nullctx someamount