{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE CPP #-}
module Hledger.Cli.Commands (
findCommand
,testcmd
,builtinCommands
,builtinCommandNames
,printCommandsList
,tests_Commands
,module Hledger.Cli.Commands.Accounts
,module Hledger.Cli.Commands.Activity
,module Hledger.Cli.Commands.Add
,module Hledger.Cli.Commands.Balance
,module Hledger.Cli.Commands.Balancesheet
,module Hledger.Cli.Commands.Balancesheetequity
,module Hledger.Cli.Commands.Cashflow
,module Hledger.Cli.Commands.Checkdates
,module Hledger.Cli.Commands.Checkdupes
,module Hledger.Cli.Commands.Close
,module Hledger.Cli.Commands.Help
,module Hledger.Cli.Commands.Import
,module Hledger.Cli.Commands.Incomestatement
,module Hledger.Cli.Commands.Prices
,module Hledger.Cli.Commands.Print
,module Hledger.Cli.Commands.Printunique
,module Hledger.Cli.Commands.Register
,module Hledger.Cli.Commands.Registermatch
,module Hledger.Cli.Commands.Rewrite
,module Hledger.Cli.Commands.Stats
,module Hledger.Cli.Commands.Tags
)
where
import Data.Default
import Data.List
import Data.List.Split (splitOn)
#if !(MIN_VERSION_base(4,11,0))
import Data.Monoid ((<>))
#endif
import Data.String.Here
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Calendar
import qualified EasyTest
import System.Console.CmdArgs.Explicit as C
import System.Exit
import Hledger
import Hledger.Cli.CliOptions
import Hledger.Cli.Version
import Hledger.Cli.Commands.Accounts
import Hledger.Cli.Commands.Activity
import Hledger.Cli.Commands.Add
import Hledger.Cli.Commands.Balance
import Hledger.Cli.Commands.Balancesheet
import Hledger.Cli.Commands.Balancesheetequity
import Hledger.Cli.Commands.Cashflow
import Hledger.Cli.Commands.Checkdates
import Hledger.Cli.Commands.Checkdupes
import Hledger.Cli.Commands.Close
import Hledger.Cli.Commands.Files
import Hledger.Cli.Commands.Help
import Hledger.Cli.Commands.Import
import Hledger.Cli.Commands.Incomestatement
import Hledger.Cli.Commands.Prices
import Hledger.Cli.Commands.Print
import Hledger.Cli.Commands.Printunique
import Hledger.Cli.Commands.Register
import Hledger.Cli.Commands.Registermatch
import Hledger.Cli.Commands.Rewrite
import Hledger.Cli.Commands.Roi
import Hledger.Cli.Commands.Stats
import Hledger.Cli.Commands.Tags
builtinCommands :: [(Mode RawOpts, CliOpts -> Journal -> IO ())]
builtinCommands = [
(accountsmode , accounts)
,(activitymode , activity)
,(addmode , add)
,(balancemode , balance)
,(balancesheetmode , balancesheet)
,(balancesheetequitymode , balancesheetequity)
,(cashflowmode , cashflow)
,(checkdatesmode , checkdates)
,(checkdupesmode , checkdupes)
,(closemode , close)
,(helpmode , help')
,(importmode , importcmd)
,(filesmode , files)
,(incomestatementmode , incomestatement)
,(pricesmode , prices)
,(printmode , print')
,(printuniquemode , printunique)
,(registermode , register)
,(registermatchmode , registermatch)
,(rewritemode , rewrite)
,(roimode , roi)
,(statsmode , stats)
,(tagsmode , tags)
,(testmode , testcmd)
]
builtinCommandNames :: [String]
builtinCommandNames = concatMap (modeNames . fst) builtinCommands
findCommand :: String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())
findCommand cmdname = find (elem cmdname . modeNames . fst) builtinCommands
commandsList :: String
commandsList = [here|
-------------------------------------------------------------------------------
PROGVERSION, commands available:
Data entry:
add add transactions using console ui
iadd add transactions using curses ui
import add new transactions from one or more import files
edit open a text editor on some part of the journal
Statements:
balancesheet (bs) show a simple balance sheet with net worth
balancesheetequity (bse) show a detailed balance sheet with equity
cashflow (cf) show a cashflow statement
incomestatement (is) show an income statement
Basic reports:
accounts (a) show account names
activity show a chart of posting counts per interval
aregister (ar, areg) show transactions in a single account
balance (b, bal) show account balance changes or ending balances
prices show market price records
print (p, txns) show transactions/journal entries
register (r, reg) show postings to one or more accounts
stats show journal statistics
tags show tag names
UIs:
ui start curses ui
web start web ui
Generating data:
close generate balance-resetting transactions
interest generate interest transactions
rewrite generate automated postings on matched transactions
Other/experimental:
api start web api server
autosync download/deduplicate/convert OFX data
budget add automated postings/txns/bucket accts
chart generate simple balance pie charts
check check more powerful balance assertions
check-dates check transactions are ordered by date
check-dupes check for accounts with the same leaf name
diff compare account transactions in two journal files
irr calculate internal rate of return of an investment
print-unique show only transactions with unique descriptions
register-match show best matching transaction for a description
test run self tests
OTHER
Help:
help show any of the hledger manuals in various formats
hledger CMD -h show command usage
hledger -h show general usage
-------------------------------------------------------------------------------
|]
printCommandsList :: [String] -> IO ()
printCommandsList addonsFound =
putStr $
regexReplace "PROGVERSION" (prognameandversion) $
regexReplace "OTHER" (unlines $ map (' ':) unknownCommandsFound) $
unlines $ concatMap adjustline $ lines $
cmdlist
where
cmdlist = commandsList
commandsFound = builtinCommandNames ++ addonsFound
unknownCommandsFound = addonsFound \\ knownCommands
adjustline l | " hledger " `isPrefixOf` l = [l]
adjustline (' ':l) | not $ w `elem` commandsFound = []
where w = takeWhile (not . (`elem` ['|',' '])) l
adjustline l = [l]
knownCommands :: [String]
knownCommands = sort $ commandsFromCommandsList commandsList
commandsFromCommandsList :: String -> [String]
commandsFromCommandsList s =
concatMap (splitOn "|") [w | ' ':l <- lines s, let w:_ = words l]
testmode = hledgerCommandMode
[here| test
Run the unit tests built in to hledger-lib and hledger,
printing results on stdout and exiting with success or failure.
If a scope argument is provided, only tests in that (exact, case-sensitive)
scope are run.
If a numeric second argument is provided, it will set the randomness seed,
for any tests which use randomness.
FLAGS
|]
[]
[generalflagsgroup3]
[]
([], Just $ argsFlag "[TESTPATTERN] [SEED]")
testcmd :: CliOpts -> Journal -> IO ()
testcmd opts _undefined = do
let args = words' $ query_ $ reportopts_ opts
e <- runEasytests args $ EasyTest.tests [tests_Hledger, tests_Commands]
if e then exitFailure else exitSuccess
tests_Commands = tests "Commands" [
tests_Balance
,tests_Register
,test "apply account directive" $ do
let ignoresourcepos j = j{jtxns=map (\t -> t{tsourcepos=nullsourcepos}) (jtxns j)}
let sameParse str1 str2 = do j1 <- io $ readJournal def Nothing str1 >>= either error' (return . ignoresourcepos)
j2 <- io $ readJournal def Nothing str2 >>= either error' (return . ignoresourcepos)
j1 `is` j2{jlastreadtime=jlastreadtime j1, jfiles=jfiles j1}
sameParse
("2008/12/07 One\n alpha $-1\n beta $1\n" <>
"apply account outer\n2008/12/07 Two\n aigh $-2\n bee $2\n" <>
"apply account inner\n2008/12/07 Three\n gamma $-3\n delta $3\n" <>
"end apply account\n2008/12/07 Four\n why $-4\n zed $4\n" <>
"end apply account\n2008/12/07 Five\n foo $-5\n bar $5\n"
)
("2008/12/07 One\n alpha $-1\n beta $1\n" <>
"2008/12/07 Two\n outer:aigh $-2\n outer:bee $2\n" <>
"2008/12/07 Three\n outer:inner:gamma $-3\n outer:inner:delta $3\n" <>
"2008/12/07 Four\n outer:why $-4\n outer:zed $4\n" <>
"2008/12/07 Five\n foo $-5\n bar $5\n"
)
,test "apply account directive should preserve \"virtual\" posting type" $ do
j <- io $ readJournal def Nothing "apply account test\n2008/12/07 One\n (from) $-1\n (to) $1\n" >>= either error' return
let p = head $ tpostings $ head $ jtxns j
paccount p `is` "test:from"
ptype p `is` VirtualPosting
,test "account aliases" $ do
j <- io $ readJournal def Nothing "!alias expenses = equity:draw:personal\n1/1\n (expenses:food) 1\n" >>= either error' return
let p = head $ tpostings $ head $ jtxns j
paccount p `is` "equity:draw:personal:food"
,test "ledgerAccountNames" $
ledgerAccountNames ledger7 `is`
["assets","assets:cash","assets:checking","assets:saving","equity","equity:opening balances",
"expenses","expenses:food","expenses:food:dining","expenses:phone","expenses:vacation",
"liabilities","liabilities:credit cards","liabilities:credit cards:discover"]
,test "default year" $ do
j <- io $ readJournal def Nothing defaultyear_journal_txt >>= either error' return
tdate (head $ jtxns j) `is` fromGregorian 2009 1 1
,test "show dollars" $ showAmount (usd 1) `is` "$1.00"
,test "show hours" $ showAmount (hrs 1) `is` "1.00h"
]
defaultyear_journal_txt :: Text
defaultyear_journal_txt = T.unlines
["Y2009"
,""
,"01/01 A"
," a $1"
," b"
]
journal7 :: Journal
journal7 = nulljournal {jtxns =
[
txnTieKnot Transaction {
tindex=0,
tsourcepos=nullsourcepos,
tdate=parsedate "2007/01/01",
tdate2=Nothing,
tstatus=Unmarked,
tcode="*",
tdescription="opening balance",
tcomment="",
ttags=[],
tpostings=
["assets:cash" `post` usd 4.82
,"equity:opening balances" `post` usd (-4.82)
],
tpreceding_comment_lines=""
}
,
txnTieKnot Transaction {
tindex=0,
tsourcepos=nullsourcepos,
tdate=parsedate "2007/02/01",
tdate2=Nothing,
tstatus=Unmarked,
tcode="*",
tdescription="ayres suites",
tcomment="",
ttags=[],
tpostings=
["expenses:vacation" `post` usd 179.92
,"assets:checking" `post` usd (-179.92)
],
tpreceding_comment_lines=""
}
,
txnTieKnot Transaction {
tindex=0,
tsourcepos=nullsourcepos,
tdate=parsedate "2007/01/02",
tdate2=Nothing,
tstatus=Unmarked,
tcode="*",
tdescription="auto transfer to savings",
tcomment="",
ttags=[],
tpostings=
["assets:saving" `post` usd 200
,"assets:checking" `post` usd (-200)
],
tpreceding_comment_lines=""
}
,
txnTieKnot Transaction {
tindex=0,
tsourcepos=nullsourcepos,
tdate=parsedate "2007/01/03",
tdate2=Nothing,
tstatus=Unmarked,
tcode="*",
tdescription="poquito mas",
tcomment="",
ttags=[],
tpostings=
["expenses:food:dining" `post` usd 4.82
,"assets:cash" `post` usd (-4.82)
],
tpreceding_comment_lines=""
}
,
txnTieKnot Transaction {
tindex=0,
tsourcepos=nullsourcepos,
tdate=parsedate "2007/01/03",
tdate2=Nothing,
tstatus=Unmarked,
tcode="*",
tdescription="verizon",
tcomment="",
ttags=[],
tpostings=
["expenses:phone" `post` usd 95.11
,"assets:checking" `post` usd (-95.11)
],
tpreceding_comment_lines=""
}
,
txnTieKnot Transaction {
tindex=0,
tsourcepos=nullsourcepos,
tdate=parsedate "2007/01/03",
tdate2=Nothing,
tstatus=Unmarked,
tcode="*",
tdescription="discover",
tcomment="",
ttags=[],
tpostings=
["liabilities:credit cards:discover" `post` usd 80
,"assets:checking" `post` usd (-80)
],
tpreceding_comment_lines=""
}
]
}
ledger7 :: Ledger
ledger7 = ledgerFromJournal Any journal7