hledger 1.3.1 → 1.4
raw patch · 70 files changed
+5781/−4346 lines, 70 filesdep +Diffdep +utility-htdep −ghc-primdep −old-localedep −shakespeare-textdep ~ansi-terminaldep ~hledger-libdep ~megaparsecPVP ok
version bump matches the API change (PVP)
Dependencies added: Diff, utility-ht
Dependencies removed: ghc-prim, old-locale, shakespeare-text
Dependency ranges changed: ansi-terminal, hledger-lib, megaparsec, shakespeare, time
API changes (from Hackage documentation)
- Hledger.Cli.Accounts: accounts :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Accounts: accountsmode :: Mode RawOpts
- Hledger.Cli.Accounts: tests_Hledger_Cli_Accounts :: Test
- Hledger.Cli.Add: EntryState :: CliOpts -> [String] -> Day -> Day -> Journal -> Maybe Transaction -> [Posting] -> EntryState
- Hledger.Cli.Add: RestartTransactionException :: RestartTransactionException
- Hledger.Cli.Add: [esArgs] :: EntryState -> [String]
- Hledger.Cli.Add: [esDefDate] :: EntryState -> Day
- Hledger.Cli.Add: [esJournal] :: EntryState -> Journal
- Hledger.Cli.Add: [esOpts] :: EntryState -> CliOpts
- Hledger.Cli.Add: [esPostings] :: EntryState -> [Posting]
- Hledger.Cli.Add: [esSimilarTransaction] :: EntryState -> Maybe Transaction
- Hledger.Cli.Add: [esToday] :: EntryState -> Day
- Hledger.Cli.Add: accountCompleter :: Journal -> String -> CompletionFunc IO
- Hledger.Cli.Add: accountWizard :: EntryState -> Wizard Haskeline String
- Hledger.Cli.Add: add :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Add: addmode :: Mode RawOpts
- Hledger.Cli.Add: amountAndCommentWizard :: EntryState -> Wizard Haskeline (Amount, Text)
- Hledger.Cli.Add: amountCompleter :: String -> CompletionFunc IO
- Hledger.Cli.Add: appendToJournalFileOrStdout :: FilePath -> String -> IO ()
- Hledger.Cli.Add: capitalize :: String -> String
- Hledger.Cli.Add: compareDescriptions :: Text -> Text -> Double
- Hledger.Cli.Add: compareStrings :: String -> String -> Double
- Hledger.Cli.Add: completer :: [String] -> String -> CompletionFunc IO
- Hledger.Cli.Add: confirmedTransactionWizard :: EntryState -> Wizard Haskeline Transaction
- Hledger.Cli.Add: data EntryState
- Hledger.Cli.Add: data RestartTransactionException
- Hledger.Cli.Add: dateAndCodeWizard :: EntryState -> Wizard Haskeline (Day, Text)
- Hledger.Cli.Add: dateCompleter :: String -> CompletionFunc IO
- Hledger.Cli.Add: defEntryState :: EntryState
- Hledger.Cli.Add: defaultTo' :: a -> Wizard Haskeline a -> Wizard Haskeline a
- Hledger.Cli.Add: descriptionAndCommentWizard :: EntryState -> Wizard Haskeline (Text, Text)
- Hledger.Cli.Add: descriptionCompleter :: Journal -> String -> CompletionFunc IO
- Hledger.Cli.Add: ensureOneNewlineTerminated :: String -> String
- Hledger.Cli.Add: getAndAddTransactions :: EntryState -> IO ()
- Hledger.Cli.Add: green :: [Char] -> [Char]
- Hledger.Cli.Add: instance GHC.Exception.Exception Hledger.Cli.Add.RestartTransactionException
- Hledger.Cli.Add: instance GHC.Show.Show Hledger.Cli.Add.EntryState
- Hledger.Cli.Add: instance GHC.Show.Show Hledger.Cli.Add.RestartTransactionException
- Hledger.Cli.Add: journalAddTransaction :: Journal -> CliOpts -> Transaction -> IO Journal
- Hledger.Cli.Add: letterPairs :: [a] -> [[a]]
- Hledger.Cli.Add: maybeExit :: Wizard Haskeline String -> Wizard Haskeline String
- Hledger.Cli.Add: maybeRestartTransaction :: Wizard Haskeline String -> Wizard Haskeline String
- Hledger.Cli.Add: postingWizard :: EntryState -> Wizard Haskeline (Maybe Posting)
- Hledger.Cli.Add: postingsBalanced :: [Posting] -> Bool
- Hledger.Cli.Add: postingsWizard :: EntryState -> Wizard Haskeline [Posting]
- Hledger.Cli.Add: registerFromString :: String -> IO String
- Hledger.Cli.Add: showDefault :: [Char] -> [Char]
- Hledger.Cli.Add: showHelp :: IO ()
- Hledger.Cli.Add: similarTransaction :: EntryState -> Text -> Maybe Transaction
- Hledger.Cli.Add: transactionWizard :: EntryState -> Wizard Haskeline Transaction
- Hledger.Cli.Add: transactionsSimilarTo :: Journal -> Query -> Text -> [(Double, Transaction)]
- Hledger.Cli.Add: withCompletion :: (:<:) WithSettings b => CompletionFunc IO -> Wizard b a -> Wizard b a
- Hledger.Cli.Add: wordLetterPairs :: String -> [[Char]]
- Hledger.Cli.Balance: balance :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Balance: balanceReportAsTable :: ReportOpts -> MultiBalanceReport -> Table String String MixedAmount
- Hledger.Cli.Balance: balanceReportAsText :: ReportOpts -> BalanceReport -> String
- Hledger.Cli.Balance: balanceReportItemAsText :: ReportOpts -> StringFormat -> BalanceReportItem -> [String]
- Hledger.Cli.Balance: balancemode :: Mode RawOpts
- Hledger.Cli.Balance: multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> String
- Hledger.Cli.Balance: renderBalanceReportTable :: ReportOpts -> Table String String MixedAmount -> String
- Hledger.Cli.Balance: tests_Hledger_Cli_Balance :: Test
- Hledger.Cli.Balancesheet: balancesheet :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Balancesheet: balancesheetmode :: Mode RawOpts
- Hledger.Cli.Balancesheet: tests_Hledger_Cli_Balancesheet :: Test
- Hledger.Cli.Cashflow: cashflow :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Cashflow: cashflowmode :: Mode RawOpts
- Hledger.Cli.Cashflow: tests_Hledger_Cli_Cashflow :: Test
- Hledger.Cli.CliOptions: [alias_] :: CliOpts -> [String]
- Hledger.Cli.CliOptions: [ignore_assertions_] :: CliOpts -> Bool
- Hledger.Cli.CliOptions: [rules_file_] :: CliOpts -> Maybe FilePath
- Hledger.Cli.Help: help' :: CliOpts -> IO ()
- Hledger.Cli.Help: helpmode :: Mode RawOpts
- Hledger.Cli.Histogram: activitymode :: Mode RawOpts
- Hledger.Cli.Histogram: barchar :: Char
- Hledger.Cli.Histogram: countBar :: Foldable t => t a -> [Char]
- Hledger.Cli.Histogram: histogram :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Histogram: printDayWith :: (PrintfType t, PrintfArg t1) => (t2 -> t1) -> (DateSpan, t2) -> t
- Hledger.Cli.Histogram: showHistogram :: ReportOpts -> Query -> Journal -> String
- Hledger.Cli.Incomestatement: incomestatement :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Incomestatement: incomestatementmode :: Mode RawOpts
- Hledger.Cli.Incomestatement: tests_Hledger_Cli_Incomestatement :: Test
- Hledger.Cli.Info: info' :: CliOpts -> IO ()
- Hledger.Cli.Info: infomode :: Mode RawOpts
- Hledger.Cli.Main: builtinCommandNames :: [String]
- Hledger.Cli.Main: builtinCommands :: [Mode RawOpts]
- Hledger.Cli.Main: commandsFromCommandsList :: String -> [String]
- Hledger.Cli.Main: commandsListTemplate :: String
- Hledger.Cli.Main: knownCommands :: [String]
- Hledger.Cli.Main: oldconvertmode :: Mode RawOpts
- Hledger.Cli.Main: printCommandsList :: [String] -> IO ()
- Hledger.Cli.Man: man :: CliOpts -> IO ()
- Hledger.Cli.Man: manmode :: Mode RawOpts
- Hledger.Cli.Print: originalTransaction :: Transaction -> Transaction
- Hledger.Cli.Print: print' :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Print: printmode :: Mode RawOpts
- Hledger.Cli.Print: tests_Hledger_Cli_Print :: Test
- Hledger.Cli.Register: postingsReportAsText :: CliOpts -> PostingsReport -> String
- Hledger.Cli.Register: postingsReportItemAsText :: CliOpts -> Int -> Int -> PostingsReportItem -> String
- Hledger.Cli.Register: register :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Register: registermode :: Mode RawOpts
- Hledger.Cli.Register: tests_Hledger_Cli_Register :: Test
- Hledger.Cli.Stats: stats :: CliOpts -> Journal -> IO ()
- Hledger.Cli.Stats: statsmode :: Mode RawOpts
- Hledger.Cli.Tests: test' :: CliOpts -> IO ()
- Hledger.Cli.Tests: testmode :: Mode RawOpts
+ Hledger.Cli.CliOptions: [inputopts_] :: CliOpts -> InputOpts
+ Hledger.Cli.CliOptions: hledgerExecutablesInPath :: IO [String]
+ Hledger.Cli.CliOptions: likelyExecutablesInPath :: IO [String]
+ Hledger.Cli.CliOptions: outputFileFlag :: Flag RawOpts
+ Hledger.Cli.CliOptions: outputFormatFlag :: Flag RawOpts
+ Hledger.Cli.CliOptions: replaceNumericFlags :: [String] -> [String]
+ Hledger.Cli.Commands: builtinCommandNames :: [String]
+ Hledger.Cli.Commands: builtinCommands :: [(Mode RawOpts, CliOpts -> Journal -> IO ())]
+ Hledger.Cli.Commands: findCommand :: String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ())
+ Hledger.Cli.Commands: printCommandsList :: [String] -> IO ()
+ Hledger.Cli.Commands: tests_Hledger_Cli_Commands :: Test
+ Hledger.Cli.Commands.Accounts: accounts :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Accounts: accountsmode :: Mode RawOpts
+ Hledger.Cli.Commands.Accounts: tests_Hledger_Cli_Commands_Accounts :: Test
+ Hledger.Cli.Commands.Activity: activity :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Activity: activitymode :: Mode RawOpts
+ Hledger.Cli.Commands.Activity: barchar :: Char
+ Hledger.Cli.Commands.Activity: countBar :: Foldable t => t a -> [Char]
+ Hledger.Cli.Commands.Activity: printDayWith :: (PrintfType t, PrintfArg t1) => (t2 -> t1) -> (DateSpan, t2) -> t
+ Hledger.Cli.Commands.Activity: showHistogram :: ReportOpts -> Query -> Journal -> String
+ Hledger.Cli.Commands.Add: add :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Add: addmode :: Mode RawOpts
+ Hledger.Cli.Commands.Add: appendToJournalFileOrStdout :: FilePath -> String -> IO ()
+ Hledger.Cli.Commands.Add: instance GHC.Exception.Exception Hledger.Cli.Commands.Add.RestartTransactionException
+ Hledger.Cli.Commands.Add: instance GHC.Show.Show Hledger.Cli.Commands.Add.EntryState
+ Hledger.Cli.Commands.Add: instance GHC.Show.Show Hledger.Cli.Commands.Add.RestartTransactionException
+ Hledger.Cli.Commands.Add: journalAddTransaction :: Journal -> CliOpts -> Transaction -> IO Journal
+ Hledger.Cli.Commands.Add: transactionsSimilarTo :: Journal -> Query -> Text -> [(Double, Transaction)]
+ Hledger.Cli.Commands.Balance: balance :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Balance: balanceReportAsTable :: ReportOpts -> MultiBalanceReport -> Table String String MixedAmount
+ Hledger.Cli.Commands.Balance: balanceReportAsText :: ReportOpts -> BalanceReport -> String
+ Hledger.Cli.Commands.Balance: balanceReportItemAsText :: ReportOpts -> StringFormat -> BalanceReportItem -> [String]
+ Hledger.Cli.Commands.Balance: balancemode :: Mode RawOpts
+ Hledger.Cli.Commands.Balance: multiBalanceReportAsCsv :: ReportOpts -> MultiBalanceReport -> CSV
+ Hledger.Cli.Commands.Balance: multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> String
+ Hledger.Cli.Commands.Balance: renderBalanceReportTable :: ReportOpts -> Table String String MixedAmount -> String
+ Hledger.Cli.Commands.Balance: tests_Hledger_Cli_Commands_Balance :: Test
+ Hledger.Cli.Commands.Balancesheet: balancesheet :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Balancesheet: balancesheetmode :: Mode RawOpts
+ Hledger.Cli.Commands.Balancesheet: tests_Hledger_Cli_Commands_Balancesheet :: Test
+ Hledger.Cli.Commands.Balancesheetequity: balancesheetequity :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Balancesheetequity: balancesheetequitymode :: Mode RawOpts
+ Hledger.Cli.Commands.Cashflow: cashflow :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Cashflow: cashflowmode :: Mode RawOpts
+ Hledger.Cli.Commands.Cashflow: tests_Hledger_Cli_Commands_Cashflow :: Test
+ Hledger.Cli.Commands.Checkdates: checkdates :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Checkdates: checkdatesmode :: Mode RawOpts
+ Hledger.Cli.Commands.Checkdates: tests_Hledger_Cli_Commands_Checkdates :: Test
+ Hledger.Cli.Commands.Checkdupes: checkdupes :: t -> Journal -> IO ()
+ Hledger.Cli.Commands.Checkdupes: checkdupesmode :: Mode RawOpts
+ Hledger.Cli.Commands.Equity: equity :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Equity: equitymode :: Mode RawOpts
+ Hledger.Cli.Commands.Help: help' :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Help: helpmode :: Mode RawOpts
+ Hledger.Cli.Commands.Import: importcmd :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Import: importmode :: Mode RawOpts
+ Hledger.Cli.Commands.Incomestatement: incomestatement :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Incomestatement: incomestatementmode :: Mode RawOpts
+ Hledger.Cli.Commands.Incomestatement: tests_Hledger_Cli_Commands_Incomestatement :: Test
+ Hledger.Cli.Commands.Prices: prices :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Prices: pricesmode :: Mode RawOpts
+ Hledger.Cli.Commands.Print: originalTransaction :: Transaction -> Transaction
+ Hledger.Cli.Commands.Print: print' :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Print: printmode :: Mode RawOpts
+ Hledger.Cli.Commands.Print: tests_Hledger_Cli_Commands_Print :: Test
+ Hledger.Cli.Commands.Printunique: printunique :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Printunique: printuniquemode :: Mode RawOpts
+ Hledger.Cli.Commands.Register: postingsReportAsText :: CliOpts -> PostingsReport -> String
+ Hledger.Cli.Commands.Register: postingsReportItemAsText :: CliOpts -> Int -> Int -> PostingsReportItem -> String
+ Hledger.Cli.Commands.Register: register :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Register: registermode :: Mode RawOpts
+ Hledger.Cli.Commands.Register: tests_Hledger_Cli_Commands_Register :: Test
+ Hledger.Cli.Commands.Registermatch: registermatch :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Registermatch: registermatchmode :: Mode RawOpts
+ Hledger.Cli.Commands.Rewrite: instance Data.Foldable.Foldable Hledger.Cli.Commands.Rewrite.DiffLine
+ Hledger.Cli.Commands.Rewrite: instance Data.Traversable.Traversable Hledger.Cli.Commands.Rewrite.DiffLine
+ Hledger.Cli.Commands.Rewrite: instance GHC.Base.Functor Hledger.Cli.Commands.Rewrite.DiffLine
+ Hledger.Cli.Commands.Rewrite: instance GHC.Show.Show a => GHC.Show.Show (Hledger.Cli.Commands.Rewrite.DiffLine a)
+ Hledger.Cli.Commands.Rewrite: rewrite :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Rewrite: rewritemode :: Mode RawOpts
+ Hledger.Cli.Commands.Stats: stats :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Stats: statsmode :: Mode RawOpts
+ Hledger.Cli.Commands.Tags: tags :: CliOpts -> Journal -> IO ()
+ Hledger.Cli.Commands.Tags: tagsmode :: Mode RawOpts
+ Hledger.Cli.DocFiles: runPagerForTopic :: FilePath -> Topic -> IO ()
+ Hledger.Cli.Utils: anonymiseByOpts :: CliOpts -> Journal -> Journal
+ Hledger.Cli.Utils: pivotByOpts :: CliOpts -> Journal -> Journal
- Hledger.Cli.CliOptions: CliOpts :: RawOpts -> String -> [FilePath] -> Maybe FilePath -> Maybe FilePath -> Maybe String -> [String] -> Bool -> Int -> Bool -> Maybe String -> Int -> ReportOpts -> CliOpts
+ Hledger.Cli.CliOptions: CliOpts :: RawOpts -> String -> [FilePath] -> InputOpts -> ReportOpts -> Maybe FilePath -> Maybe String -> Int -> Bool -> Maybe String -> Int -> CliOpts
- Hledger.Cli.CompoundBalanceCommand: CompoundBalanceCommandSpec :: String -> [String] -> String -> String -> [(String, Journal -> Query)] -> BalanceType -> CompoundBalanceCommandSpec
+ Hledger.Cli.CompoundBalanceCommand: CompoundBalanceCommandSpec :: String -> [String] -> String -> String -> [(String, Journal -> Query, Maybe NormalBalance)] -> BalanceType -> CompoundBalanceCommandSpec
- Hledger.Cli.CompoundBalanceCommand: [cbcqueries] :: CompoundBalanceCommandSpec -> [(String, Journal -> Query)]
+ Hledger.Cli.CompoundBalanceCommand: [cbcqueries] :: CompoundBalanceCommandSpec -> [(String, Journal -> Query, Maybe NormalBalance)]
Files
- CHANGES +76/−0
- Hledger/Cli.hs +3/−418
- Hledger/Cli/Accounts.hs +0/−67
- Hledger/Cli/Add.hs +0/−449
- Hledger/Cli/Balance.hs +0/−545
- Hledger/Cli/Balancesheet.hs +0/−47
- Hledger/Cli/Cashflow.hs +0/−48
- Hledger/Cli/CliOptions.hs +57/−43
- Hledger/Cli/Commands.hs +636/−0
- Hledger/Cli/Commands/Accounts.hs +67/−0
- Hledger/Cli/Commands/Activity.hs +58/−0
- Hledger/Cli/Commands/Add.hs +455/−0
- Hledger/Cli/Commands/Balance.hs +553/−0
- Hledger/Cli/Commands/Balancesheet.hs +47/−0
- Hledger/Cli/Commands/Balancesheetequity.hs +40/−0
- Hledger/Cli/Commands/Cashflow.hs +48/−0
- Hledger/Cli/Commands/Checkdates.hs +85/−0
- Hledger/Cli/Commands/Checkdupes.hs +49/−0
- Hledger/Cli/Commands/Equity.hs +85/−0
- Hledger/Cli/Commands/Help.hs +84/−0
- Hledger/Cli/Commands/Import.hs +60/−0
- Hledger/Cli/Commands/Incomestatement.hs +47/−0
- Hledger/Cli/Commands/Prices.hs +84/−0
- Hledger/Cli/Commands/Print.hs +180/−0
- Hledger/Cli/Commands/Printunique.hs +47/−0
- Hledger/Cli/Commands/Register.hs +204/−0
- Hledger/Cli/Commands/Registermatch.hs +96/−0
- Hledger/Cli/Commands/Rewrite.hs +276/−0
- Hledger/Cli/Commands/Stats.hs +112/−0
- Hledger/Cli/Commands/Tags.hs +34/−0
- Hledger/Cli/CompoundBalanceCommand.hs +230/−96
- Hledger/Cli/DocFiles.hs +14/−4
- Hledger/Cli/Help.hs +0/−42
- Hledger/Cli/Histogram.hs +0/−59
- Hledger/Cli/Incomestatement.hs +0/−47
- Hledger/Cli/Info.hs +0/−41
- Hledger/Cli/Main.hs +83/−256
- Hledger/Cli/Man.hs +0/−41
- Hledger/Cli/Print.hs +0/−180
- Hledger/Cli/Register.hs +0/−204
- Hledger/Cli/Stats.hs +0/−112
- Hledger/Cli/Tests.hs +0/−71
- Hledger/Cli/Utils.hs +10/−33
- README.md +2/−1
- Text/Tabular/AsciiWide.hs +2/−2
- doc/hledger.1 +264/−159
- doc/hledger.1.info +452/−352
- doc/hledger.1.txt +392/−304
- doc/other/hledger-api.1 +2/−17
- doc/other/hledger-api.1.info +5/−14
- doc/other/hledger-api.1.txt +3/−8
- doc/other/hledger-ui.1 +16/−23
- doc/other/hledger-ui.1.info +32/−32
- doc/other/hledger-ui.1.txt +103/−99
- doc/other/hledger-web.1 +16/−23
- doc/other/hledger-web.1.info +20/−20
- doc/other/hledger-web.1.txt +26/−22
- doc/other/hledger_csv.5 +52/−19
- doc/other/hledger_csv.5.info +116/−35
- doc/other/hledger_csv.5.txt +48/−19
- doc/other/hledger_journal.5 +32/−23
- doc/other/hledger_journal.5.info +155/−137
- doc/other/hledger_journal.5.txt +89/−79
- doc/other/hledger_timeclock.5 +1/−1
- doc/other/hledger_timeclock.5.info +2/−2
- doc/other/hledger_timeclock.5.txt +1/−1
- doc/other/hledger_timedot.5 +26/−16
- doc/other/hledger_timedot.5.info +26/−22
- doc/other/hledger_timedot.5.txt +26/−22
- hledger.cabal +52/−91
CHANGES view
@@ -2,6 +2,82 @@ See also the project change log. +# 1.4 (2017/9/30)++* cli: a @FILE argument reads flags & args from FILE, one per line++* cli: reorganized commands list, added some new command aliases:+ accounts: a+ balance: b+ print: p, txns+ register: r++* cli: accept -NUM as a shortcut for --depth=NUM (eg: -2)++* cli: improve command-line help for --date2 (#604)++* cli: make --help and -h the same, drop --man and --info for now (#579)++* help: offers multiple formats, accepts topic substrings.+ The separate info/man commands have been dropped. help now+ chooses an appropriate documentation format as follows: + - it uses info if available, + - otherwise man if available, + - otherwise $PAGER if defined, + - otherwise less if available, + - otherwise it prints on stdout+ - (and it always prints on stdout when piped). + You can override this with the `--info`/`--man`/`--pager`/`--cat` flags.+ (#579)++* bal/bs/cf/is: --sort-amount/-S sorts by largest amount instead of+ account name++* bs/cf/is: support --output-file and --output-format=txt|csv+ The CSV output should be reasonably ok for dragging into a+ spreadsheet and reformatting.++* bal/bs/cf/is: consistent double space between columns, consistent+ single final blank line. Previously, amounts wider than the column+ headings would be separated by only a single space.++* bs/is: don't let an empty subreport disable the grand totals (fixes #588)++* cf: exclude asset accounts with ":fixed" in their name (Christian G. Warden, Simon Michael, #584)++* new balancesheetequity command: like balancesheet but also shows+ equity accounts (Nicholas Niro)++* new import command: adds new transactions seen in one or more input+ files to the main journal file++* print: --new shows only transactions added since last time+ (saves state in .latest.JOURNALFILE file)++* new tags command: lists tags in matched transactions++* most addons formerly shipped in bin/ are now builtin commands. These+ include: check-dates, check-dupes, equity, prices, print-unique,+ register-match, rewrite.++* refactor: new Commands module and subdirectory.+ Builtin commands are now gathered more tightly in a single module,+ Hledger.Cli.Commands, facilitating change. The legacy "convert"+ command has been dropped.++* refactor: BalanceView -> CompoundBalanceCommand++* deps: drop support for directory < 1.2++* deps: allow ansi-terminal 0.7++* deps: drop oldtime flag, require time 1.5+++* deps: simplify shakespeare bounds++* deps: remove ghc < 7.6 support++ # 1.3.1 (2017/8/25) * bs/is: don't let an empty subreport disable the grand totals (#588)
Hledger/Cli.hs view
@@ -10,20 +10,8 @@ {-# LANGUAGE OverloadedStrings #-} module Hledger.Cli (- module Hledger.Cli.Accounts,- module Hledger.Cli.Add,- module Hledger.Cli.Balance,- module Hledger.Cli.Balancesheet,- module Hledger.Cli.Cashflow,- module Hledger.Cli.Help,- module Hledger.Cli.Histogram,- module Hledger.Cli.Incomestatement,- module Hledger.Cli.Info,- module Hledger.Cli.Man,- module Hledger.Cli.Print,- module Hledger.Cli.Register,- module Hledger.Cli.Stats, module Hledger.Cli.CliOptions,+ module Hledger.Cli.Commands, module Hledger.Cli.DocFiles, module Hledger.Cli.Utils, module Hledger.Cli.Version,@@ -32,416 +20,13 @@ module System.Console.CmdArgs.Explicit ) where-import Data.Monoid ((<>))-import Data.Text (Text)-import qualified Data.Text as T-import Data.Time.Calendar import System.Console.CmdArgs.Explicit hiding (Name) -- don't clash with hledger-ui-import Test.HUnit import Hledger-import Hledger.Cli.Accounts-import Hledger.Cli.Add-import Hledger.Cli.Balance-import Hledger.Cli.Balancesheet-import Hledger.Cli.Cashflow-import Hledger.Cli.Histogram-import Hledger.Cli.Help-import Hledger.Cli.Incomestatement-import Hledger.Cli.Info-import Hledger.Cli.Man-import Hledger.Cli.Print-import Hledger.Cli.Register-import Hledger.Cli.Stats import Hledger.Cli.CliOptions+import Hledger.Cli.Commands import Hledger.Cli.DocFiles import Hledger.Cli.Utils import Hledger.Cli.Version --tests_Hledger_Cli :: Test-tests_Hledger_Cli = TestList- [- tests_Hledger- -- ,tests_Hledger_Cli_Add- ,tests_Hledger_Cli_Balance- ,tests_Hledger_Cli_Balancesheet- ,tests_Hledger_Cli_Cashflow- -- ,tests_Hledger_Cli_Histogram- ,tests_Hledger_Cli_Incomestatement- ,tests_Hledger_Cli_CliOptions- -- ,tests_Hledger_Cli_Print- ,tests_Hledger_Cli_Register- -- ,tests_Hledger_Cli_Stats--- ,"apply account directive" ~: - let ignoresourcepos j = j{jtxns=map (\t -> t{tsourcepos=nullsourcepos}) (jtxns j)} in- let sameParse str1 str2 = do j1 <- readJournal Nothing Nothing True Nothing str1 >>= either error' (return . ignoresourcepos)- j2 <- readJournal Nothing Nothing True Nothing str2 >>= either error' (return . ignoresourcepos)- j1 `is` j2{jlastreadtime=jlastreadtime j1, jfiles=jfiles j1} --, jparsestate=jparsestate j1}- in 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"- )-- ,"apply account directive should preserve \"virtual\" posting type" ~: do- j <- readJournal Nothing Nothing True 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- assertBool "" $ paccount p == "test:from"- assertBool "" $ ptype p == VirtualPosting-- ,"account aliases" ~: do- j <- readJournal Nothing Nothing True Nothing "!alias expenses = equity:draw:personal\n1/1\n (expenses:food) 1\n" >>= either error' return- let p = head $ tpostings $ head $ jtxns j- assertBool "" $ paccount p == "equity:draw:personal:food"-- ,"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"]-- -- ,"journalCanonicaliseAmounts" ~:- -- "use the greatest precision" ~:- -- (map asprecision $ journalAmountAndPriceCommodities $ journalCanonicaliseAmounts $ journalWithAmounts ["1","2.00"]) `is` [2,2]-- -- don't know what this should do- -- ,"elideAccountName" ~: do- -- (elideAccountName 50 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"- -- `is` "aa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa")- -- (elideAccountName 20 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"- -- `is` "aa:aa:aaaaaaaaaaaaaa")-- ,"default year" ~: do- j <- readJournal Nothing Nothing True Nothing defaultyear_journal_txt >>= either error' return- tdate (head $ jtxns j) `is` fromGregorian 2009 1 1- return ()-- ,"show dollars" ~: showAmount (usd 1) ~?= "$1.00"-- ,"show hours" ~: showAmount (hrs 1) ~?= "1.00h"-- ]----- fixtures/test data---- date1 = parsedate "2008/11/26"--- t1 = LocalTime date1 midday--{--samplejournal = readJournal' sample_journal_str--sample_journal_str = unlines- ["; A sample journal file."- ,";"- ,"; Sets up this account tree:"- ,"; assets"- ,"; bank"- ,"; checking"- ,"; saving"- ,"; cash"- ,"; expenses"- ,"; food"- ,"; supplies"- ,"; income"- ,"; gifts"- ,"; salary"- ,"; liabilities"- ,"; debts"- ,""- ,"2008/01/01 income"- ," assets:bank:checking $1"- ," income:salary"- ,""- ,"2008/06/01 gift"- ," assets:bank:checking $1"- ," income:gifts"- ,""- ,"2008/06/02 save"- ," assets:bank:saving $1"- ," assets:bank:checking"- ,""- ,"2008/06/03 * eat & shop"- ," expenses:food $1"- ," expenses:supplies $1"- ," assets:cash"- ,""- ,"2008/12/31 * pay off"- ," liabilities:debts $1"- ," assets:bank:checking"- ,""- ,""- ,";final comment"- ]--}--defaultyear_journal_txt :: Text-defaultyear_journal_txt = T.unlines- ["Y2009"- ,""- ,"01/01 A"- ," a $1"- ," b"- ]---- write_sample_journal = writeFile "sample.journal" sample_journal_str---- entry2_str = unlines--- ["2007/01/27 * joes diner"--- ," expenses:food:dining $10.00"--- ," expenses:gifts $10.00"--- ," assets:checking $-20.00"--- ,""--- ]---- entry3_str = unlines--- ["2007/01/01 * opening balance"--- ," assets:cash $4.82"--- ," equity:opening balances"--- ,""--- ,"2007/01/01 * opening balance"--- ," assets:cash $4.82"--- ," equity:opening balances"--- ,""--- ,"2007/01/28 coopportunity"--- ," expenses:food:groceries $47.18"--- ," assets:checking"--- ,""--- ]---- periodic_entry1_str = unlines--- ["~ monthly from 2007/2/2"--- ," assets:saving $200.00"--- ," assets:checking"--- ,""--- ]---- periodic_entry2_str = unlines--- ["~ monthly from 2007/2/2"--- ," assets:saving $200.00 ;auto savings"--- ," assets:checking"--- ,""--- ]---- periodic_entry3_str = unlines--- ["~ monthly from 2007/01/01"--- ," assets:cash $4.82"--- ," equity:opening balances"--- ,""--- ,"~ monthly from 2007/01/01"--- ," assets:cash $4.82"--- ," equity:opening balances"--- ,""--- ]---- journal1_str = unlines--- [""--- ,"2007/01/27 * joes diner"--- ," expenses:food:dining $10.00"--- ," expenses:gifts $10.00"--- ," assets:checking $-20.00"--- ,""--- ,""--- ,"2007/01/28 coopportunity"--- ," expenses:food:groceries $47.18"--- ," assets:checking $-47.18"--- ,""--- ,""--- ]---- journal2_str = unlines--- [";comment"--- ,"2007/01/27 * joes diner"--- ," expenses:food:dining $10.00"--- ," assets:checking $-47.18"--- ,""--- ]---- journal3_str = unlines--- ["2007/01/27 * joes diner"--- ," expenses:food:dining $10.00"--- ,";intra-entry comment"--- ," assets:checking $-47.18"--- ,""--- ]---- journal4_str = unlines--- ["!include \"somefile\""--- ,"2007/01/27 * joes diner"--- ," expenses:food:dining $10.00"--- ," assets:checking $-47.18"--- ,""--- ]---- journal5_str = ""---- journal6_str = unlines--- ["~ monthly from 2007/1/21"--- ," expenses:entertainment $16.23 ;netflix"--- ," assets:checking"--- ,""--- ,"; 2007/01/01 * opening balance"--- ,"; assets:saving $200.04"--- ,"; equity:opening balances "--- ,""--- ]---- journal7_str = unlines--- ["2007/01/01 * opening balance"--- ," assets:cash $4.82"--- ," equity:opening balances "--- ,""--- ,"2007/01/01 * opening balance"--- ," income:interest $-4.82"--- ," equity:opening balances "--- ,""--- ,"2007/01/02 * ayres suites"--- ," expenses:vacation $179.92"--- ," assets:checking "--- ,""--- ,"2007/01/02 * auto transfer to savings"--- ," assets:saving $200.00"--- ," assets:checking "--- ,""--- ,"2007/01/03 * poquito mas"--- ," expenses:food:dining $4.82"--- ," assets:cash "--- ,""--- ,"2007/01/03 * verizon"--- ," expenses:phone $95.11"--- ," assets:checking "--- ,""--- ,"2007/01/03 * discover"--- ," liabilities:credit cards:discover $80.00"--- ," assets:checking "--- ,""--- ,"2007/01/04 * blue cross"--- ," expenses:health:insurance $90.00"--- ," assets:checking "--- ,""--- ,"2007/01/05 * village market liquor"--- ," expenses:food:dining $6.48"--- ," assets:checking "--- ,""--- ]--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+tests_Hledger_Cli = tests_Hledger_Cli_Commands
− Hledger/Cli/Accounts.hs
@@ -1,67 +0,0 @@-{-|--The @accounts@ command lists account names:--- in flat mode (default), it lists the full names of accounts posted to by matched postings,- clipped to the specified depth, possibly with leading components dropped.--- in tree mode, it shows the indented short names of accounts posted to by matched postings,- and their parents, to the specified depth.---}--{-# LANGUAGE OverloadedStrings #-}--module Hledger.Cli.Accounts (- accountsmode- ,accounts- ,tests_Hledger_Cli_Accounts-) where--import Data.List-import Data.Monoid--- import Data.Text (Text)-import qualified Data.Text as T-import System.Console.CmdArgs.Explicit as C-import Test.HUnit--import Hledger-import Prelude hiding (putStrLn)-import Hledger.Utils.UTF8IOCompat (putStrLn)-import Hledger.Cli.CliOptions----- | Command line options for this command.-accountsmode = (defCommandMode $ ["accounts"] ++ aliases) {- modeHelp = "show account names" `withAliases` aliases- ,modeHelpSuffix = [- "This command lists the accounts referenced by matched postings (and in tree mode, their parents as well). The accounts can be depth-clipped (--depth N) or have their leading parts trimmed (--drop N)."- ]- ,modeGroupFlags = C.Group {- groupUnnamed = [- flagNone ["tree"] (\opts -> setboolopt "tree" opts) "show short account names, as a tree"- ,flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show full account names, as a list (default)"- ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts"- ]- ,groupHidden = []- ,groupNamed = [generalflagsgroup1]- }- }- where aliases = []---- | The accounts command.-accounts :: CliOpts -> Journal -> IO ()-accounts CliOpts{reportopts_=ropts} j = do- d <- getCurrentDay- let q = queryFromOpts d ropts- nodepthq = dbg1 "nodepthq" $ filterQuery (not . queryIsDepth) q- depth = dbg1 "depth" $ queryDepth $ filterQuery queryIsDepth q- ps = dbg1 "ps" $ journalPostings $ filterJournalPostings nodepthq j- as = dbg1 "as" $ nub $ filter (not . T.null) $ map (clipAccountName depth) $ sort $ map paccount ps- as' | tree_ ropts = expandAccountNames as- | otherwise = as- render a | tree_ ropts = T.replicate (2 * (accountNameLevel a - 1)) " " <> accountLeafName a- | otherwise = maybeAccountNameDrop ropts a- mapM_ (putStrLn . T.unpack . render) as'--tests_Hledger_Cli_Accounts = TestList []
− Hledger/Cli/Add.hs
@@ -1,449 +0,0 @@-{-|-A history-aware add command to help with data entry.-|-}--{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-do-bind #-}-{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, RecordWildCards, TypeOperators, FlexibleContexts, OverloadedStrings #-}--module Hledger.Cli.Add-where--import Prelude ()-import Prelude.Compat-import Control.Exception as E-import Control.Monad-import Control.Monad.Trans.Class-import Control.Monad.State.Strict (evalState, evalStateT)-import Control.Monad.Trans (liftIO)-import Data.Char (toUpper, toLower)-import Data.Functor.Identity (Identity(..))-import Data.List.Compat-import qualified Data.Set as S-import Data.Maybe-import Data.Text (Text)-import qualified Data.Text as T-import Data.Time.Calendar (Day)-import Data.Typeable (Typeable)-import Safe (headDef, headMay)-import System.Console.CmdArgs.Explicit-import System.Console.Haskeline (runInputT, defaultSettings, setComplete)-import System.Console.Haskeline.Completion-import System.Console.Wizard-import System.Console.Wizard.Haskeline-import System.IO ( stderr, hPutStr, hPutStrLn )-import Text.Megaparsec.Compat-import Text.Printf--import Hledger-import Hledger.Cli.CliOptions-import Hledger.Cli.Register (postingsReportAsText)---addmode = (defCommandMode ["add"]) {- modeHelp = "prompt for transactions and add them to the journal"- ,modeHelpSuffix = ["Defaults come from previous similar transactions; use query patterns to restrict these."]- ,modeGroupFlags = Group {- groupUnnamed = [- flagNone ["no-new-accounts"] (\opts -> setboolopt "no-new-accounts" opts) "don't allow creating new accounts"- ]- ,groupHidden = []- ,groupNamed = [generalflagsgroup2]- }- }---- | State used while entering transactions.-data EntryState = EntryState {- esOpts :: CliOpts -- ^ command line options- ,esArgs :: [String] -- ^ command line arguments remaining to be used as defaults- ,esToday :: Day -- ^ today's date- ,esDefDate :: Day -- ^ the default date for next transaction- ,esJournal :: Journal -- ^ the journal we are adding to- ,esSimilarTransaction :: Maybe Transaction -- ^ the most similar historical txn- ,esPostings :: [Posting] -- ^ postings entered so far in the current txn- } deriving (Show,Typeable)--defEntryState = EntryState {- esOpts = defcliopts- ,esArgs = []- ,esToday = nulldate- ,esDefDate = nulldate- ,esJournal = nulljournal- ,esSimilarTransaction = Nothing- ,esPostings = []-}--data RestartTransactionException = RestartTransactionException deriving (Typeable,Show)-instance Exception RestartTransactionException---- data ShowHelpException = ShowHelpException deriving (Typeable,Show)--- instance Exception ShowHelpException---- | Read multiple transactions from the console, prompting for each--- field, and append them to the journal file. If the journal came--- from stdin, this command has no effect.-add :: CliOpts -> Journal -> IO ()-add opts j- | journalFilePath j == "-" = return ()- | otherwise = do- hPrintf stderr "Adding transactions to journal file %s\n" (journalFilePath j)- showHelp- today <- getCurrentDay- let es = defEntryState{esOpts=opts- ,esArgs=map (T.unpack . stripquotes . T.pack) $ listofstringopt "args" $ rawopts_ opts- ,esToday=today- ,esDefDate=today- ,esJournal=j- }- getAndAddTransactions es `E.catch` (\(_::UnexpectedEOF) -> putStr "")--showHelp = hPutStr stderr $ unlines [- "Any command line arguments will be used as defaults."- ,"Use tab key to complete, readline keys to edit, enter to accept defaults."- ,"An optional (CODE) may follow transaction dates."- ,"An optional ; COMMENT may follow descriptions or amounts."- ,"If you make a mistake, enter < at any prompt to restart the transaction."- ,"To end a transaction, enter . when prompted."- ,"To quit, enter . at a date prompt or press control-d or control-c."- ]---- | Loop reading transactions from the console, prompting, validating--- and appending each one to the journal file, until end of input or--- ctrl-c (then raise an EOF exception). If provided, command-line--- arguments are used as defaults; otherwise defaults come from the--- most similar recent transaction in the journal.-getAndAddTransactions :: EntryState -> IO ()-getAndAddTransactions es@EntryState{..} = (do- mt <- runInputT (setComplete noCompletion defaultSettings) (run $ haskeline $ confirmedTransactionWizard es)- case mt of- Nothing -> fail "urk ?"- Just t -> do- j <- if debug_ esOpts > 0- then do hPrintf stderr "Skipping journal add due to debug mode.\n"- return esJournal- else do j' <- journalAddTransaction esJournal esOpts t- hPrintf stderr "Saved.\n"- return j'- hPrintf stderr "Starting the next transaction (. or ctrl-D/ctrl-C to quit)\n"- getAndAddTransactions es{esJournal=j, esDefDate=tdate t}- )- `E.catch` (\(_::RestartTransactionException) ->- hPrintf stderr "Restarting this transaction.\n" >> getAndAddTransactions es)---- confirmedTransactionWizard :: (ArbitraryIO :<: b, OutputLn :<: b, Line :<: b) => EntryState -> Wizard b Transaction--- confirmedTransactionWizard :: EntryState -> Wizard Haskeline Transaction-confirmedTransactionWizard es@EntryState{..} = do- t <- transactionWizard es- -- liftIO $ hPrintf stderr {- "Transaction entered:\n%s" -} (show t)- output $ show t- y <- let def = "y" in- retryMsg "Please enter y or n." $- parser ((fmap ('y' ==)) . headMay . map toLower . strip) $- defaultTo' def $ nonEmpty $- maybeRestartTransaction $- line $ green $ printf "Save this transaction to the journal ?%s: " (showDefault def)- if y then return t else throw RestartTransactionException--transactionWizard es@EntryState{..} = do- (date,code) <- dateAndCodeWizard es- let es1@EntryState{esArgs=args1} = es{esArgs=drop 1 esArgs, esDefDate=date}- (desc,comment) <- descriptionAndCommentWizard es1- let mbaset = similarTransaction es1 desc- when (isJust mbaset) $ liftIO $ hPrintf stderr "Using this similar transaction for defaults:\n%s" (show $ fromJust mbaset)- let es2 = es1{esArgs=drop 1 args1, esSimilarTransaction=mbaset}- balancedPostingsWizard = do- ps <- postingsWizard es2{esPostings=[]}- let t = nulltransaction{tdate=date- ,tstatus=Unmarked- ,tcode=code- ,tdescription=desc- ,tcomment=comment- ,tpostings=ps- }- case balanceTransaction Nothing t of -- imprecise balancing (?)- Right t' -> return t'- Left err -> liftIO (hPutStrLn stderr $ "\n" ++ (capitalize err) ++ "please re-enter.") >> balancedPostingsWizard- balancedPostingsWizard---- Identify the closest recent match for this description in past transactions.-similarTransaction :: EntryState -> Text -> Maybe Transaction-similarTransaction EntryState{..} desc =- let q = queryFromOptsOnly esToday $ reportopts_ esOpts- historymatches = transactionsSimilarTo esJournal q desc- bestmatch | null historymatches = Nothing- | otherwise = Just $ snd $ head historymatches- in bestmatch--dateAndCodeWizard EntryState{..} = do- let def = headDef (showDate esDefDate) esArgs- retryMsg "A valid hledger smart date is required. Eg: 2014/2/14, 14, yesterday." $- parser (parseSmartDateAndCode esToday) $- withCompletion (dateCompleter def) $- defaultTo' def $ nonEmpty $- maybeExit $- maybeRestartTransaction $- -- maybeShowHelp $- line $ green $ printf "Date%s: " (showDefault def)- where- parseSmartDateAndCode refdate s = either (const Nothing) (\(d,c) -> return (fixSmartDate refdate d, c)) edc- where- edc = runParser (dateandcodep <* eof) "" $ T.pack $ lowercase s- dateandcodep :: SimpleTextParser (SmartDate, Text)- dateandcodep = do- d <- smartdate- c <- optional codep- many spacenonewline- eof- return (d, T.pack $ fromMaybe "" c)- -- defday = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) defdate- -- datestr = showDate $ fixSmartDate defday smtdate--descriptionAndCommentWizard EntryState{..} = do- let def = headDef "" esArgs- s <- withCompletion (descriptionCompleter esJournal def) $- defaultTo' def $ nonEmpty $- maybeRestartTransaction $- line $ green $ printf "Description%s: " (showDefault def)- let (desc,comment) = (T.pack $ strip a, T.pack $ strip $ dropWhile (==';') b) where (a,b) = break (==';') s- return (desc, comment)--postingsWizard es@EntryState{..} = do- mp <- postingWizard es- case mp of Nothing -> return esPostings- Just p -> postingsWizard es{esArgs=drop 2 esArgs, esPostings=esPostings++[p]}--postingWizard es@EntryState{..} = do- acct <- accountWizard es- if acct `elem` [".",""]- then case (esPostings, postingsBalanced esPostings) of- ([],_) -> liftIO (hPutStrLn stderr "Please enter some postings first.") >> postingWizard es- (_,False) -> liftIO (hPutStrLn stderr "Please enter more postings to balance the transaction.") >> postingWizard es- (_,True) -> return Nothing -- no more postings, end of transaction- else do- let es1 = es{esArgs=drop 1 esArgs}- (amt,comment) <- amountAndCommentWizard es1- return $ Just nullposting{paccount=T.pack $ stripbrackets acct- ,pamount=Mixed [amt]- ,pcomment=comment- ,ptype=accountNamePostingType $ T.pack acct- }--postingsBalanced :: [Posting] -> Bool-postingsBalanced ps = isRight $ balanceTransaction Nothing nulltransaction{tpostings=ps}--accountWizard EntryState{..} = do- let pnum = length esPostings + 1- historicalp = maybe Nothing (Just . (!! (pnum-1)) . (++ (repeat nullposting)) . tpostings) esSimilarTransaction- historicalacct = case historicalp of Just p -> showAccountName Nothing (ptype p) (paccount p)- Nothing -> ""- def = headDef historicalacct esArgs- endmsg | canfinish && null def = " (or . or enter to finish this transaction)"- | canfinish = " (or . to finish this transaction)"- | otherwise = ""- retryMsg "A valid hledger account name is required. Eg: assets:cash, expenses:food:eating out." $- parser (parseAccountOrDotOrNull def canfinish) $- withCompletion (accountCompleter esJournal def) $- defaultTo' def $ -- nonEmpty $- maybeRestartTransaction $- line $ green $ printf "Account %d%s%s: " pnum (endmsg::String) (showDefault def)- where- canfinish = not (null esPostings) && postingsBalanced esPostings- parseAccountOrDotOrNull :: String -> Bool -> String -> Maybe String- parseAccountOrDotOrNull _ _ "." = dbg1 $ Just "." -- . always signals end of txn- parseAccountOrDotOrNull "" True "" = dbg1 $ Just "" -- when there's no default and txn is balanced, "" also signals end of txn- parseAccountOrDotOrNull def@(_:_) _ "" = dbg1 $ Just def -- when there's a default, "" means use that- parseAccountOrDotOrNull _ _ s = dbg1 $ fmap T.unpack $- either (const Nothing) validateAccount $- flip evalState esJournal $ runParserT (accountnamep <* eof) "" (T.pack s) -- otherwise, try to parse the input as an accountname- where- validateAccount :: Text -> Maybe Text- validateAccount t | no_new_accounts_ esOpts && not (t `elem` journalAccountNames esJournal) = Nothing- | otherwise = Just t- dbg1 = id -- strace--amountAndCommentWizard EntryState{..} = do- let pnum = length esPostings + 1- (mhistoricalp,followedhistoricalsofar) =- case esSimilarTransaction of- Nothing -> (Nothing,False)- Just Transaction{tpostings=ps} -> (if length ps >= pnum then Just (ps !! (pnum-1)) else Nothing- ,all (\(a,b) -> pamount a == pamount b) $ zip esPostings ps)- def = case (esArgs, mhistoricalp, followedhistoricalsofar) of- (d:_,_,_) -> d- (_,Just hp,True) -> showamt $ pamount hp- _ | pnum > 1 && not (isZeroMixedAmount balancingamt) -> showamt balancingamtfirstcommodity- _ -> ""- retryMsg "A valid hledger amount is required. Eg: 1, $2, 3 EUR, \"4 red apples\"." $- parser parseAmountAndComment $- withCompletion (amountCompleter def) $- defaultTo' def $ nonEmpty $- maybeRestartTransaction $- line $ green $ printf "Amount %d%s: " pnum (showDefault def)- where- parseAmountAndComment s = either (const Nothing) Just $- runParser- (evalStateT (amountandcommentp <* eof) nodefcommodityj)- ""- (T.pack s)- nodefcommodityj = esJournal{jparsedefaultcommodity=Nothing}- amountandcommentp :: JournalParser Identity (Amount, Text)- amountandcommentp = do- a <- amountp- lift (many spacenonewline)- c <- T.pack <$> fromMaybe "" `fmap` optional (char ';' >> many anyChar)- -- eof- return (a,c)- balancingamt = negate $ sum $ map pamount realps where realps = filter isReal esPostings- balancingamtfirstcommodity = Mixed $ take 1 $ amounts balancingamt- showamt =- showMixedAmountWithPrecision- -- what should this be ?- -- 1 maxprecision (show all decimal places or none) ?- -- 2 maxprecisionwithpoint (show all decimal places or .0 - avoids some but not all confusion with thousands separators) ?- -- 3 canonical precision for this commodity in the journal ?- -- 4 maximum precision entered so far in this transaction ?- -- 5 3 or 4, whichever would show the most decimal places ?- -- I think 1 or 4, whichever would show the most decimal places- maxprecisionwithpoint- --- -- let -- (amt,comment) = (strip a, strip $ dropWhile (==';') b) where (a,b) = break (==';') amtcmt- -- a = fromparse $ runParser (amountp <|> return missingamt) (jparsestate esJournal) "" amt- -- awithoutjps = fromparse $ runParser (amountp <|> return missingamt) mempty "" amt- -- defamtaccepted = Just (showAmount a) == mdefamt- -- es2 = if defamtaccepted then es1 else es1{esHistoricalPostings=Nothing}- -- mdefaultcommodityapplied = if acommodity a == acommodity awithoutjps then Nothing else Just $ acommodity a- -- when (isJust mdefaultcommodityapplied) $- -- liftIO $ hPutStrLn stderr $ printf "using default commodity (%s)" (fromJust mdefaultcommodityapplied)--maybeExit = parser (\s -> if s=="." then throw UnexpectedEOF else Just s)--maybeRestartTransaction = parser (\s -> if s=="<" then throw RestartTransactionException else Just s)---- maybeShowHelp :: Wizard Haskeline String -> Wizard Haskeline String--- maybeShowHelp wizard = maybe (liftIO showHelp >> wizard) return $--- parser (\s -> if s=="?" then Nothing else Just s) wizard---- Completion helpers--dateCompleter :: String -> CompletionFunc IO-dateCompleter = completer ["today","tomorrow","yesterday"]--descriptionCompleter :: Journal -> String -> CompletionFunc IO-descriptionCompleter j = completer (map T.unpack $ journalDescriptions j)--accountCompleter :: Journal -> String -> CompletionFunc IO-accountCompleter j = completer (map T.unpack $ journalAccountNamesUsed j)--amountCompleter :: String -> CompletionFunc IO-amountCompleter = completer []---- | Generate a haskeline completion function from the given--- completions and default, that case insensitively completes with--- prefix matches, or infix matches above a minimum length, or--- completes the null string with the default.-completer :: [String] -> String -> CompletionFunc IO-completer completions def = completeWord Nothing "" completionsFor- where- simpleCompletion' s = (simpleCompletion s){isFinished=False}- completionsFor "" = return [simpleCompletion' def]- completionsFor i = return (map simpleCompletion' ciprefixmatches)- where- ciprefixmatches = [c | c <- completions, i `isPrefixOf` c]- -- mixed-case completions require haskeline > 0.7.1.2- -- ciprefixmatches = [c | c <- completions, lowercase i `isPrefixOf` lowercase c]-------------------------------------------------------------------------------------- utilities--defaultTo' = flip defaultTo--withCompletion f = withSettings (setComplete f defaultSettings)--green s = "\ESC[1;32m\STX"++s++"\ESC[0m\STX"--showDefault "" = ""-showDefault s = " [" ++ s ++ "]"---- | Append this transaction to the journal's file and transaction list.-journalAddTransaction :: Journal -> CliOpts -> Transaction -> IO Journal-journalAddTransaction j@Journal{jtxns=ts} opts t = do- let f = journalFilePath j- appendToJournalFileOrStdout f $ showTransactionUnelided t- -- unelided shows all amounts explicitly, in case there's a price, cf #283- when (debug_ opts > 0) $ do- putStrLn $ printf "\nAdded transaction to %s:" f- putStrLn =<< registerFromString (show t)- return j{jtxns=ts++[t]}---- | Append a string, typically one or more transactions, to a journal--- file, or if the file is "-", dump it to stdout. Tries to avoid--- excess whitespace.-appendToJournalFileOrStdout :: FilePath -> String -> IO ()-appendToJournalFileOrStdout f s- | f == "-" = putStr s'- | otherwise = appendFile f s'- where s' = "\n" ++ ensureOneNewlineTerminated s---- | Replace a string's 0 or more terminating newlines with exactly one.-ensureOneNewlineTerminated :: String -> String-ensureOneNewlineTerminated = (++"\n") . reverse . dropWhile (=='\n') . reverse---- | Convert a string of journal data into a register report.-registerFromString :: String -> IO String-registerFromString s = do- d <- getCurrentDay- j <- readJournal' $ T.pack s- return $ postingsReportAsText opts $ postingsReport ropts (queryFromOpts d ropts) j- where- ropts = defreportopts{empty_=True}- opts = defcliopts{reportopts_=ropts}--capitalize :: String -> String-capitalize "" = ""-capitalize (c:cs) = toUpper c : cs---- | Find the most similar and recent transactions matching the given--- transaction description and report query. Transactions are listed--- with their "relevancy" score, most relevant first.-transactionsSimilarTo :: Journal -> Query -> Text -> [(Double,Transaction)]-transactionsSimilarTo j q desc =- sortBy compareRelevanceAndRecency- $ filter ((> threshold).fst)- [(compareDescriptions desc $ tdescription t, t) | t <- ts]- where- compareRelevanceAndRecency (n1,t1) (n2,t2) = compare (n2,tdate t2) (n1,tdate t1)- ts = filter (q `matchesTransaction`) $ jtxns j- threshold = 0---- | Return a similarity measure, from 0 to 1, for two transaction--- descriptions. This is like compareStrings, but first strips out--- any numbers, to improve accuracy eg when there are bank transaction--- ids from imported data.-compareDescriptions :: Text -> Text -> Double-compareDescriptions s t = compareStrings s' t'- where s' = simplify $ T.unpack s- t' = simplify $ T.unpack t- simplify = filter (not . (`elem` ("0123456789" :: String)))---- | Return a similarity measure, from 0 to 1, for two strings. This--- was based on Simon White's string similarity algorithm--- (http://www.catalysoft.com/articles/StrikeAMatch.html), later found--- to be https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient,--- modified to handle short strings better.--- Todo: check out http://nlp.fi.muni.cz/raslan/2008/raslan08.pdf#page=14 .-compareStrings :: String -> String -> Double-compareStrings "" "" = 1-compareStrings (_:[]) "" = 0-compareStrings "" (_:[]) = 0-compareStrings (a:[]) (b:[]) = if toUpper a == toUpper b then 1 else 0-compareStrings s1 s2 = 2 * commonpairs / totalpairs- where- pairs1 = S.fromList $ wordLetterPairs $ uppercase s1- pairs2 = S.fromList $ wordLetterPairs $ uppercase s2- commonpairs = fromIntegral $ S.size $ S.intersection pairs1 pairs2- totalpairs = fromIntegral $ S.size pairs1 + S.size pairs2--wordLetterPairs = concatMap letterPairs . words--letterPairs (a:b:rest) = [a,b] : letterPairs (b:rest)-letterPairs _ = []
− Hledger/Cli/Balance.hs
@@ -1,545 +0,0 @@-{-|--A ledger-compatible @balance@ command, with additional support for-multi-column reports.--Here is a description/specification for the balance command. See also-"Hledger.Reports" -> \"Balance reports\".---/Basic balance report/--With no report interval (@--monthly@ etc.), hledger's balance-command emulates ledger's, showing accounts indented according to-hierarchy, along with their total amount posted (including subaccounts).--Here's an example. With @examples/sample.journal@, which defines the following account tree:--@- assets- bank- checking- saving- cash- expenses- food- supplies- income- gifts- salary- liabilities- debts-@--the basic @balance@ command gives this output:--@- $ hledger -f sample.journal balance- $-1 assets- $1 bank:saving- $-2 cash- $2 expenses- $1 food- $1 supplies- $-2 income- $-1 gifts- $-1 salary- $1 liabilities:debts---------------------- 0-@--Subaccounts are displayed indented below their parent. Only the account leaf name (the final part) is shown.-(With @--flat@, account names are shown in full and unindented.)--Each account's \"balance\" is the sum of postings in that account and any subaccounts during the report period.-When the report period includes all transactions, this is equivalent to the account's current balance.--The overall total of the highest-level displayed accounts is shown below the line.-(The @--no-total/-N@ flag prevents this.)--/Eliding and omitting/--Accounts which have a zero balance, and no non-zero subaccount-balances, are normally omitted from the report.-(The @--empty/-E@ flag forces such accounts to be displayed.)-Eg, above @checking@ is omitted because it has a zero balance and no subaccounts.--Accounts which have a single subaccount also being displayed, with the same balance,-are normally elided into the subaccount's line.-(The @--no-elide@ flag prevents this.)-Eg, above @bank@ is elided to @bank:saving@ because it has only a-single displayed subaccount (@saving@) and their balance is the same-($1). Similarly, @liabilities@ is elided to @liabilities:debts@.--/Date limiting/--The default report period is that of the whole journal, including all-known transactions. The @--begin\/-b@, @--end\/-e@, @--period\/-p@-options or @date:@/@date2:@ patterns can be used to report only-on transactions before and/or after specified dates.--/Depth limiting/--The @--depth@ option can be used to limit the depth of the balance report.-Eg, to see just the top level accounts (still including their subaccount balances):--@-$ hledger -f sample.journal balance --depth 1- $-1 assets- $2 expenses- $-2 income- $1 liabilities---------------------- 0-@--/Account limiting/--With one or more account pattern arguments, the report is restricted-to accounts whose name matches one of the patterns, plus their parents-and subaccounts. Eg, adding the pattern @o@ to the first example gives:--@- $ hledger -f sample.journal balance o- $1 expenses:food- $-2 income- $-1 gifts- $-1 salary---------------------- $-1-@--* The @o@ pattern matched @food@ and @income@, so they are shown.--* @food@'s parent (@expenses@) is shown even though the pattern didn't- match it, to clarify the hierarchy. The usual eliding rules cause it to be elided here.--* @income@'s subaccounts are also shown.--/Multi-column balance report/--hledger's balance command will show multiple columns when a reporting-interval is specified (eg with @--monthly@), one column for each sub-period.--There are three kinds of multi-column balance report, indicated by the heading:--* A \"period balance\" (or \"flow\") report (the default) shows the change of account- balance in each period, which is equivalent to the sum of postings in each- period. Here, checking's balance increased by 10 in Feb:-- > Change of balance (flow):- >- > Jan Feb Mar- > assets:checking 20 10 -5--* A \"cumulative balance\" report (with @--cumulative@) shows the accumulated ending balance- across periods, starting from zero at the report's start date.- Here, 30 is the sum of checking postings during Jan and Feb:-- > Ending balance (cumulative):- >- > Jan Feb Mar- > assets:checking 20 30 25--* A \"historical balance\" report (with @--historical/-H@) also shows ending balances,- but it includes the starting balance from any postings before the report start date.- Here, 130 is the balance from all checking postings at the end of Feb, including- pre-Jan postings which created a starting balance of 100:-- > Ending balance (historical):- >- > Jan Feb Mar- > assets:checking 120 130 125--/Eliding and omitting, 2/--Here's a (imperfect?) specification for the eliding/omitting behaviour:--* Each account is normally displayed on its own line.--* An account less deep than the report's max depth, with just one-interesting subaccount, and the same balance as the subaccount, is-non-interesting, and prefixed to the subaccount's line, unless-@--no-elide@ is in effect.--* An account with a zero inclusive balance and less than two interesting-subaccounts is not displayed at all, unless @--empty@ is in effect.--* Multi-column balance reports show full account names with no eliding- (like @--flat@). Accounts (and periods) are omitted as described below.--/Which accounts to show in balance reports/--By default:--* single-column: accounts with non-zero balance in report period.- (With @--flat@: accounts with non-zero balance and postings.)--* periodic: accounts with postings and non-zero period balance in any period--* cumulative: accounts with non-zero cumulative balance in any period--* historical: accounts with non-zero historical balance in any period--With @-E/--empty@:--* single-column: accounts with postings in report period--* periodic: accounts with postings in report period--* cumulative: accounts with postings in report period--* historical: accounts with non-zero starting balance +- accounts with postings in report period--/Which periods (columns) to show in balance reports/--An empty period/column is one where no report account has any postings.-A zero period/column is one where no report account has a non-zero period balance.--Currently,--by default:--* single-column: N/A--* periodic: all periods within the overall report period,- except for leading and trailing empty periods--* cumulative: all periods within the overall report period,- except for leading and trailing empty periods--* historical: all periods within the overall report period,- except for leading and trailing empty periods--With @-E/--empty@:--* single-column: N/A--* periodic: all periods within the overall report period--* cumulative: all periods within the overall report period--* historical: all periods within the overall report period--/What to show in empty cells/--An empty periodic balance report cell is one which has no corresponding postings.-An empty cumulative/historical balance report cell is one which has no correponding-or prior postings, ie the account doesn't exist yet.-Currently, empty cells show 0.---}--{-# LANGUAGE OverloadedStrings #-}--module Hledger.Cli.Balance (- balancemode- ,balance- ,balanceReportAsText- ,balanceReportItemAsText- ,multiBalanceReportAsText- ,renderBalanceReportTable- ,balanceReportAsTable- ,tests_Hledger_Cli_Balance-) where--import Data.List (intercalate)-import Data.Maybe--- import Data.Monoid-import qualified Data.Text as T-import System.Console.CmdArgs.Explicit as C-import Text.CSV-import Test.HUnit-import Text.Printf (printf)-import Text.Tabular as T-import Text.Tabular.AsciiWide--import Hledger-import Hledger.Cli.CliOptions-import Hledger.Cli.Utils----- | Command line options for this command.-balancemode = (defCommandMode $ ["balance"] ++ aliases) { -- also accept but don't show the common bal alias- modeHelp = "show accounts and balances" `withAliases` aliases- ,modeGroupFlags = C.Group {- groupUnnamed = [- flagNone ["change"] (\opts -> setboolopt "change" opts)- "show balance change in each period (default)"- ,flagNone ["cumulative"] (\opts -> setboolopt "cumulative" opts)- "show balance change accumulated across periods (in multicolumn reports)"- ,flagNone ["historical","H"] (\opts -> setboolopt "historical" opts)- "show historical ending balance in each period (includes postings before report start date)\n "- ,flagNone ["tree"] (\opts -> setboolopt "tree" opts) "show accounts as a tree; amounts include subaccounts (default in simple reports)"- ,flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list; amounts exclude subaccounts except when account is depth-clipped (default in multicolumn reports)\n "- ,flagNone ["average","A"] (\opts -> setboolopt "average" opts) "show a row average column (in multicolumn reports)"- ,flagNone ["row-total","T"] (\opts -> setboolopt "row-total" opts) "show a row total column (in multicolumn reports)"- ,flagNone ["no-total","N"] (\opts -> setboolopt "no-total" opts) "omit the final total row"- ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "omit N leading account name parts (in flat mode)"- ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't squash boring parent accounts (in tree mode)"- ,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format (in simple reports)"- ,flagNone ["pretty-tables"] (\opts -> setboolopt "pretty-tables" opts) "use unicode when displaying tables"- ]- ++ outputflags- ,groupHidden = []- ,groupNamed = [generalflagsgroup1]- }- }- where aliases = ["bal"]---- | The balance command, prints a balance report.-balance :: CliOpts -> Journal -> IO ()-balance opts@CliOpts{reportopts_=ropts} j = do- d <- getCurrentDay- case lineFormatFromOpts ropts of- Left err -> error' $ unlines [err]- Right _ -> do- let format = outputFormatFromOpts opts- interval = interval_ ropts- -- shenanigans: use single/multiBalanceReport when we must,- -- ie when there's a report interval, or --historical or -- cumulative.- -- Otherwise prefer the older balanceReport since it can elide boring parents.- case interval of- NoInterval -> do- let report- -- For --historical/--cumulative, we must use multiBalanceReport.- -- (This forces --no-elide.)- | balancetype_ ropts `elem` [HistoricalBalance, CumulativeChange]- = let ropts' | flat_ ropts = ropts- | otherwise = ropts{accountlistmode_=ALTree}- in singleBalanceReport ropts' (queryFromOpts d ropts) j- | otherwise = balanceReport ropts (queryFromOpts d ropts) j- render = case format of- "csv" -> \ropts r -> (++ "\n") $ printCSV $ balanceReportAsCsv ropts r- _ -> balanceReportAsText- writeOutput opts $ render ropts report- _ -> do- let report = multiBalanceReport ropts (queryFromOpts d ropts) j- render = case format of- "csv" -> \ropts r -> (++ "\n") $ printCSV $ multiBalanceReportAsCsv ropts r- _ -> multiBalanceReportAsText- writeOutput opts $ render ropts report---- single-column balance reports---- | Find the best commodity to convert to when asked to show the--- market value of this commodity on the given date. That is, the one--- in which it has most recently been market-priced, ie the commodity--- mentioned in the most recent applicable historical price directive--- before this date.--- defaultValuationCommodity :: Journal -> Day -> CommoditySymbol -> Maybe CommoditySymbol--- defaultValuationCommodity j d c = mpamount <$> commodityValue j d c---- | Render a single-column balance report as CSV.-balanceReportAsCsv :: ReportOpts -> BalanceReport -> CSV-balanceReportAsCsv opts (items, total) =- ["account","balance"] :- [[T.unpack a, showMixedAmountOneLineWithoutPrice b] | (a, _, _, b) <- items]- ++- if no_total_ opts- then []- else [["total", showMixedAmountOneLineWithoutPrice total]]---- | Render a single-column balance report as plain text.-balanceReportAsText :: ReportOpts -> BalanceReport -> String-balanceReportAsText opts ((items, total)) = unlines $ concat lines ++ t- where- fmt = lineFormatFromOpts opts- lines = case fmt of- Right fmt -> map (balanceReportItemAsText opts fmt) items- Left err -> [[err]]- t = if no_total_ opts- then []- else- case fmt of- Right fmt ->- let- -- abuse renderBalanceReportItem to render the total with similar format- acctcolwidth = maximum' [T.length fullname | (fullname, _, _, _) <- items]- totallines = map rstrip $ renderBalanceReportItem opts fmt (T.replicate (acctcolwidth+1) " ", 0, total)- -- with a custom format, extend the line to the full report width;- -- otherwise show the usual 20-char line for compatibility- overlinewidth | isJust (format_ opts) = maximum' $ map length $ concat lines- | otherwise = defaultTotalFieldWidth- overline = replicate overlinewidth '-'- in overline : totallines- Left _ -> []--tests_balanceReportAsText = [- "balanceReportAsText" ~: do- -- "unicode in balance layout" ~: do- j <- readJournal'- "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"- let opts = defreportopts- balanceReportAsText opts (balanceReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is`- unlines- [" -100 актив:наличные"- ," 100 расходы:покупки"- ,"--------------------"- ," 0"- ]- ]--{--:r-This implementation turned out to be a bit convoluted but implements the following algorithm for formatting:--- If there is a single amount, print it with the account name directly:-- Otherwise, only print the account name on the last line.-- a USD 1 ; Account 'a' has a single amount- EUR -1- b USD -1 ; Account 'b' has two amounts. The account name is printed on the last line.--}--- | Render one balance report line item as plain text suitable for console output (or--- whatever string format is specified). Note, prices will not be rendered, and--- differently-priced quantities of the same commodity will appear merged.--- The output will be one or more lines depending on the format and number of commodities.-balanceReportItemAsText :: ReportOpts -> StringFormat -> BalanceReportItem -> [String]-balanceReportItemAsText opts fmt (_, accountName, depth, amt) =- renderBalanceReportItem opts fmt (- maybeAccountNameDrop opts accountName,- depth,- normaliseMixedAmountSquashPricesForDisplay amt- )---- | Render a balance report item using the given StringFormat, generating one or more lines of text.-renderBalanceReportItem :: ReportOpts -> StringFormat -> (AccountName, Int, MixedAmount) -> [String]-renderBalanceReportItem opts fmt (acctname, depth, total) =- lines $- case fmt of- OneLine comps -> concatOneLine $ render1 comps- TopAligned comps -> concatBottomPadded $ render comps- BottomAligned comps -> concatTopPadded $ render comps- where- render1 = map (renderComponent1 opts (acctname, depth, total))- render = map (renderComponent opts (acctname, depth, total))--defaultTotalFieldWidth = 20---- | Render one StringFormat component for a balance report item.-renderComponent :: ReportOpts -> (AccountName, Int, MixedAmount) -> StringFormatComponent -> String-renderComponent _ _ (FormatLiteral s) = s-renderComponent opts (acctname, depth, total) (FormatField ljust min max field) = case field of- DepthSpacerField -> formatString ljust Nothing max $ replicate d ' '- where d = case min of- Just m -> depth * m- Nothing -> depth- AccountField -> formatString ljust min max (T.unpack acctname)- TotalField -> fitStringMulti min max True False $ showamt total- where- showamt | color_ opts = cshowMixedAmountWithoutPrice- | otherwise = showMixedAmountWithoutPrice- _ -> ""---- | Render one StringFormat component for a balance report item.--- This variant is for use with OneLine string formats; it squashes--- any multi-line rendered values onto one line, comma-and-space separated,--- while still complying with the width spec.-renderComponent1 :: ReportOpts -> (AccountName, Int, MixedAmount) -> StringFormatComponent -> String-renderComponent1 _ _ (FormatLiteral s) = s-renderComponent1 opts (acctname, depth, total) (FormatField ljust min max field) = case field of- AccountField -> formatString ljust min max ((intercalate ", " . lines) (indented (T.unpack acctname)))- where- -- better to indent the account name here rather than use a DepthField component- -- so that it complies with width spec. Uses a fixed indent step size.- indented = ((replicate (depth*2) ' ')++)- TotalField -> fitStringMulti min max True False $ ((intercalate ", " . map strip . lines) (showamt total))- where- showamt | color_ opts = cshowMixedAmountWithoutPrice- | otherwise = showMixedAmountWithoutPrice - _ -> ""---- multi-column balance reports---- | Render a multi-column balance report as CSV.-multiBalanceReportAsCsv :: ReportOpts -> MultiBalanceReport -> CSV-multiBalanceReportAsCsv opts (MultiBalanceReport (colspans, items, (coltotals,tot,avg))) =- ("account" : "short account" : "indent" : map showDateSpan colspans- ++ (if row_total_ opts then ["total"] else [])- ++ (if average_ opts then ["average"] else [])- ) :- [T.unpack a : T.unpack a' : show i :- map showMixedAmountOneLineWithoutPrice- (amts- ++ (if row_total_ opts then [rowtot] else [])- ++ (if average_ opts then [rowavg] else []))- | (a,a',i, amts, rowtot, rowavg) <- items]- ++- if no_total_ opts- then []- else [["totals", "", ""]- ++ map showMixedAmountOneLineWithoutPrice (- coltotals- ++ (if row_total_ opts then [tot] else [])- ++ (if average_ opts then [avg] else [])- )]---- | Render a multi-column balance report as plain text suitable for console output.-multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> String-multiBalanceReportAsText opts r =- printf "%s in %s:" typeStr (showDateSpan $ multiBalanceReportSpan r)- ++ "\n"- ++ renderBalanceReportTable opts tabl- where- tabl = balanceReportAsTable opts r- typeStr :: String- typeStr = case balancetype_ opts of- PeriodChange -> "Balance changes"- CumulativeChange -> "Ending balances (cumulative)"- HistoricalBalance -> "Ending balances (historical)"---- | Given a table representing a multi-column balance report (for example,--- made using 'balanceReportAsTable'), render it in a format suitable for--- console output.-renderBalanceReportTable :: ReportOpts -> Table String String MixedAmount -> String-renderBalanceReportTable (ReportOpts { pretty_tables_ = pretty, color_=usecolor }) = unlines . trimborder . lines- . render pretty id (" " ++) showamt- . align- where- trimborder = ("":) . (++[""]) . drop 1 . init . map (drop 1 . init)- align (Table l t d) = Table l' t d- where- acctswidth = maximum' $ map strWidth (headerContents l)- l' = padRightWide acctswidth <$> l- showamt | usecolor = cshowMixedAmountOneLineWithoutPrice- | otherwise = showMixedAmountOneLineWithoutPrice---- | Build a 'Table' from a multi-column balance report.-balanceReportAsTable :: ReportOpts -> MultiBalanceReport -> Table String String MixedAmount-balanceReportAsTable opts (MultiBalanceReport (colspans, items, (coltotals,tot,avg))) =- addtotalrow $ Table- (T.Group NoLine $ map Header accts)- (T.Group NoLine $ map Header colheadings)- (map rowvals items)- where- mkDate = case balancetype_ opts of- PeriodChange -> showDateSpan- _ -> maybe "" (showDate . prevday) . spanEnd- colheadings = map mkDate colspans- ++ (if row_total_ opts then [" Total"] else [])- ++ (if average_ opts then ["Average"] else [])- accts = map renderacct items- renderacct (a,a',i,_,_,_)- | tree_ opts = replicate ((i-1)*2) ' ' ++ T.unpack a'- | otherwise = T.unpack $ maybeAccountNameDrop opts a- rowvals (_,_,_,as,rowtot,rowavg) = as- ++ (if row_total_ opts then [rowtot] else [])- ++ (if average_ opts then [rowavg] else [])- addtotalrow | no_total_ opts = id- | otherwise = (+----+ (row "" $- coltotals- ++ (if row_total_ opts && not (null coltotals) then [tot] else [])- ++ (if average_ opts && not (null coltotals) then [avg] else [])- ))---- | Figure out the overall date span of a multicolumn balance report.-multiBalanceReportSpan :: MultiBalanceReport -> DateSpan-multiBalanceReportSpan (MultiBalanceReport ([], _, _)) = DateSpan Nothing Nothing-multiBalanceReportSpan (MultiBalanceReport (colspans, _, _)) = DateSpan (spanStart $ head colspans) (spanEnd $ last colspans)---tests_Hledger_Cli_Balance = TestList- tests_balanceReportAsText
− Hledger/Cli/Balancesheet.hs
@@ -1,47 +0,0 @@-{-# LANGUAGE QuasiQuotes, RecordWildCards, NoCPP #-}-{-|--The @balancesheet@ command prints a simple balance sheet.---}--module Hledger.Cli.Balancesheet (- balancesheetmode- ,balancesheet- ,tests_Hledger_Cli_Balancesheet-) where--import Data.String.Here-import System.Console.CmdArgs.Explicit-import Test.HUnit--import Hledger-import Hledger.Cli.CliOptions-import Hledger.Cli.CompoundBalanceCommand--balancesheetSpec = CompoundBalanceCommandSpec {- cbcname = "balancesheet",- cbcaliases = ["bs"],- cbchelp = [here|-This command displays a simple balance sheet, showing historical ending-balances of asset and liability accounts (ignoring any report begin date). -It assumes that these accounts are under a top-level `asset` or `liability`-account (case insensitive, plural forms also allowed).- |],- cbctitle = "Balance Sheet",- cbcqueries = [ ("Assets" , journalAssetAccountQuery),- ("Liabilities", journalLiabilityAccountQuery)- ],- cbctype = HistoricalBalance-}--balancesheetmode :: Mode RawOpts-balancesheetmode = compoundBalanceCommandMode balancesheetSpec--balancesheet :: CliOpts -> Journal -> IO ()-balancesheet = compoundBalanceCommand balancesheetSpec--tests_Hledger_Cli_Balancesheet :: Test-tests_Hledger_Cli_Balancesheet = TestList- [- ]
− Hledger/Cli/Cashflow.hs
@@ -1,48 +0,0 @@-{-# LANGUAGE QuasiQuotes, RecordWildCards, NoCPP #-}-{-|--The @cashflow@ command prints a simplified cashflow statement. It just-shows the change in all "cash" accounts for the period (without the-traditional segmentation into operating, investing, and financing-cash flows.)---}--module Hledger.Cli.Cashflow (- cashflowmode- ,cashflow- ,tests_Hledger_Cli_Cashflow-) where--import Data.String.Here-import System.Console.CmdArgs.Explicit-import Test.HUnit--import Hledger-import Hledger.Cli.CliOptions-import Hledger.Cli.CompoundBalanceCommand--cashflowSpec = CompoundBalanceCommandSpec {- cbcname = "cashflow",- cbcaliases = ["cf"],- cbchelp = [here|-This command displays a simple cashflow statement, showing changes-in "cash" accounts. It assumes that these accounts are under a top-level -`asset` account (case insensitive, plural forms also allowed) and do not -contain `receivable` or `A/R` in their name. - |],- cbctitle = "Cashflow Statement",- cbcqueries = [("Cash flows", journalCashAccountQuery)],- cbctype = PeriodChange-}--cashflowmode :: Mode RawOpts-cashflowmode = compoundBalanceCommandMode cashflowSpec--cashflow :: CliOpts -> Journal -> IO ()-cashflow = compoundBalanceCommand cashflowSpec--tests_Hledger_Cli_Cashflow :: Test-tests_Hledger_Cli_Cashflow = TestList- [- ]
Hledger/Cli/CliOptions.hs view
@@ -15,6 +15,8 @@ inputflags, reportflags, outputflags,+ outputFormatFlag,+ outputFileFlag, generalflagsgroup1, generalflagsgroup2, generalflagsgroup3,@@ -25,6 +27,8 @@ argsFlag, showModeUsage, withAliases,+ likelyExecutablesInPath,+ hledgerExecutablesInPath, -- * CLI options CliOpts(..),@@ -47,6 +51,7 @@ outputFormatFromOpts, defaultWidth, widthFromOpts,+ replaceNumericFlags, -- | For register: registerWidthsFromOpts, maybeAccountNameDrop,@@ -58,8 +63,12 @@ topicForMode, -- * Tests- tests_Hledger_Cli_CliOptions+ tests_Hledger_Cli_CliOptions, +-- -- * Convenience re-exports+-- module Data.String.Here,+-- module System.Console.CmdArgs.Explicit,+-- module Test.HUnit ) where @@ -67,6 +76,7 @@ import Prelude.Compat import qualified Control.Exception as C import Control.Monad (when)+import Data.Char import Data.Default #if !MIN_VERSION_base(4,8,0) import Data.Functor.Compat ((<$>))@@ -76,6 +86,7 @@ import Data.List.Split (splitOneOf) import Data.Ord import Data.Maybe+--import Data.String.Here -- import Data.Text (Text) import qualified Data.Text as T import Safe@@ -102,10 +113,7 @@ -- | Common help flags: --help, --debug, --version... helpflags :: [Flag RawOpts] helpflags = [- flagNone ["h"] (setboolopt "h") "show general usage (or after CMD, command usage)"- ,flagNone ["help"] (setboolopt "help") "show this program's manual as plain text (or after an addon CMD, the add-on's manual)"- ,flagNone ["man"] (setboolopt "man") "show this program's manual with man"- ,flagNone ["info"] (setboolopt "info") "show this program's manual with info"+ flagNone ["help","h"] (setboolopt "help") "show general usage (or after CMD, command usage)" -- ,flagNone ["browse-args"] (setboolopt "browse-args") "use a web UI to select options and build up a command line" ,flagReq ["debug"] (\s opts -> Right $ setopt "debug" s opts) "[N]" "show debug output (levels 1-9, default: 1)" ,flagNone ["version"] (setboolopt "version") "show version information"@@ -137,23 +145,22 @@ ,flagNone ["quarterly","Q"] (setboolopt "quarterly") "multiperiod/multicolumn report by quarter" ,flagNone ["yearly","Y"] (setboolopt "yearly") "multiperiod/multicolumn report by year" ,flagReq ["period","p"] (\s opts -> Right $ setopt "period" s opts) "PERIODEXP" "set start date, end date, and/or report interval all at once (overrides the flags above)"- ,flagNone ["date2"] (setboolopt "date2") "show, and make -b/-e/-p/date: match, secondary dates instead"+ ,flagNone ["date2"] (setboolopt "date2") "match the secondary date instead (see command help for other effects)" ,flagNone ["unmarked","U"] (setboolopt "unmarked") "include only unmarked postings/txns (can combine with -P or -C)" ,flagNone ["pending","P"] (setboolopt "pending") "include only pending postings/txns" ,flagNone ["cleared","C"] (setboolopt "cleared") "include only cleared postings/txns" ,flagNone ["real","R"] (setboolopt "real") "include only non-virtual postings"- ,flagReq ["depth"] (\s opts -> Right $ setopt "depth" s opts) "N" "hide accounts/postings deeper than N"+ ,flagReq ["depth"] (\s opts -> Right $ setopt "depth" s opts) "NUM" "(or -NUM): hide accounts/postings deeper than this" ,flagNone ["empty","E"] (setboolopt "empty") "show items with zero amount, normally hidden" ,flagNone ["cost","B"] (setboolopt "cost") "convert amounts to their cost at transaction time (using the transaction price, if any)" ,flagNone ["value","V"] (setboolopt "value") "convert amounts to their market value on the report end date (using the most recent applicable market price, if any)" ] -- | Common output-related flags: --output-file, --output-format...-outputflags = [- flagReq ["output-format","O"] (\s opts -> Right $ setopt "output-format" s opts) "FMT" "select the output format. Supported formats:\ntxt, csv."- ,flagReq ["output-file","o"] (\s opts -> Right $ setopt "output-file" s opts) "FILE" "write output to FILE. A file extension matching one of the above formats selects that format."- ]+outputflags = [outputFormatFlag, outputFileFlag]+outputFormatFlag = flagReq ["output-format","O"] (\s opts -> Right $ setopt "output-format" s opts) "FMT" "select the output format. Supported formats:\ntxt, csv."+outputFileFlag = flagReq ["output-file","o"] (\s opts -> Right $ setopt "output-file" s opts) "FILE" "write output to FILE. A file extension matching one of the above formats selects that format." argsFlag :: FlagHelp -> Arg RawOpts argsFlag desc = flagArg (\s opts -> Right $ setopt "args" s opts) desc@@ -200,7 +207,7 @@ ,modeGroupFlags = Group { groupNamed = [] ,groupUnnamed = [- flagNone ["h"] (setboolopt "h") "Show usage."+ flagNone ["help"] (setboolopt "help") "Show usage." -- ,flagNone ["help"] (setboolopt "help") "Show long help." ] ,groupHidden = [] -- flags not displayed in the usage@@ -246,11 +253,10 @@ preamble = unlines $ reverse $ dropWhile null $ reverse preamblels postamblelines = dropWhile null $ drop 1 postamblels --- | Build a cmdarg mode suitable for a hledger add-on command,+-- | Build a cmdarg mode for a hledger command, -- from a help template and flag/argument specifications. -- Reduces boilerplate a little, though the complicated cmdargs -- flag and argument specs are still required.--- See the addons in bin/ for examples of usage. hledgerCommandMode :: HelpTemplate -> [Flag RawOpts] -> [(Help, [Flag RawOpts])] -> [Flag RawOpts] -> ([Arg RawOpts], Maybe (Arg RawOpts)) -> Mode RawOpts hledgerCommandMode tmpl ungroupedflags groupedflags hiddenflags args =@@ -336,11 +342,10 @@ rawopts_ :: RawOpts ,command_ :: String ,file_ :: [FilePath]- ,rules_file_ :: Maybe FilePath+ ,inputopts_ :: InputOpts+ ,reportopts_ :: ReportOpts ,output_file_ :: Maybe FilePath ,output_format_ :: Maybe String- ,alias_ :: [String]- ,ignore_assertions_ :: Bool ,debug_ :: Int -- ^ debug level, set by @--debug[=N]@. See also 'Hledger.Utils.debugLevel'. ,no_new_accounts_ :: Bool -- add ,width_ :: Maybe String -- ^ the --width value provided, if any@@ -348,7 +353,6 @@ -- 1. the COLUMNS env var, if set -- 2. the width reported by the terminal, if supported -- 3. the default (80)- ,reportopts_ :: ReportOpts } deriving (Show, Data, Typeable) instance Default CliOpts where def = defcliopts@@ -365,9 +369,7 @@ def def def- def defaultWidth- def -- | Convert possibly encoded option values to regular unicode strings. decodeRawOpts :: RawOpts -> RawOpts@@ -377,12 +379,21 @@ defaultWidth :: Int defaultWidth = 80 +-- | Replace any numeric flags (eg -2) with their long form (--depth 2),+-- as I'm guessing cmdargs doesn't support this directly. +replaceNumericFlags :: [String] -> [String]+replaceNumericFlags = map replace+ where+ replace ('-':ds) | not (null ds) && all isDigit ds = "--depth="++ds+ replace s = s+ -- | Parse raw option string values to the desired final data types. -- Any relative smart dates will be converted to fixed dates based on -- today's date. Parsing failures will raise an error. -- Also records the terminal width, if supported. rawOptsToCliOpts :: RawOpts -> IO CliOpts rawOptsToCliOpts rawopts = checkCliOpts <$> do+ let iopts = rawOptsToInputOpts rawopts ropts <- rawOptsToReportOpts rawopts mcolumns <- readMay <$> getEnvSafe "COLUMNS" mtermwidth <-@@ -397,16 +408,14 @@ rawopts_ = rawopts ,command_ = stringopt "command" rawopts ,file_ = map (T.unpack . stripquotes . T.pack) $ listofstringopt "file" rawopts- ,rules_file_ = maybestringopt "rules-file" rawopts+ ,inputopts_ = iopts+ ,reportopts_ = ropts ,output_file_ = maybestringopt "output-file" rawopts ,output_format_ = maybestringopt "output-format" rawopts- ,alias_ = map (T.unpack . stripquotes . T.pack) $ listofstringopt "alias" rawopts ,debug_ = intopt "debug" rawopts- ,ignore_assertions_ = boolopt "ignore-assertions" rawopts ,no_new_accounts_ = boolopt "no-new-accounts" rawopts -- add ,width_ = maybestringopt "width" rawopts ,available_width_ = availablewidth- ,reportopts_ = ropts } -- | Do final validation of processed opts, raising an error if there is trouble.@@ -424,6 +433,7 @@ -- and returns a CliOpts. Or, with --help or -h present, it prints -- long or short help, and exits the program. -- When --debug is present, also prints some debug output.+-- Note this is not used by the main hledger executable. -- -- The help texts are generated from the mode. -- Long help includes the full usage description generated by cmdargs@@ -443,12 +453,12 @@ -- getHledgerCliOpts :: Mode RawOpts -> IO CliOpts getHledgerCliOpts mode' = do- args' <- getArgs+ args' <- getArgs >>= expandArgsAt let rawopts = either usageError decodeRawOpts $ process mode' args' opts <- rawOptsToCliOpts rawopts debugArgs args' opts- when ("help" `inRawOpts` rawopts_ opts) $ putStr longhelp >> exitSuccess- when ("h" `inRawOpts` rawopts_ opts) $ putStr shorthelp >> exitSuccess+ when ("help" `inRawOpts` rawopts_ opts) $ putStr shorthelp >> exitSuccess+ -- when ("help" `inRawOpts` rawopts_ opts) $ putStr longhelp >> exitSuccess return opts where longhelp = showModeUsage mode'@@ -457,7 +467,7 @@ (reverse $ dropWhile null $ reverse $ takeWhile (not . ("flags:" `isInfixOf`)) $ lines longhelp) ++ [""- ," See --help for full detail, including common hledger options."+ ," See also hledger -h for general hledger options." ] -- | Print debug info about arguments and options if --debug is present. debugArgs :: [String] -> CliOpts -> IO ()@@ -475,7 +485,7 @@ -- | Get the account name aliases from options, if any. aliasesFromOpts :: CliOpts -> [AccountAlias] aliasesFromOpts = map (\a -> fromparse $ runParser accountaliasp ("--alias "++quoteIfNeeded a) $ T.pack a)- . alias_+ . aliases_ . inputopts_ -- | Get the (tilde-expanded, absolute) journal file path from -- 1. options, 2. an environment variable, or 3. the default.@@ -539,7 +549,7 @@ rulesFilePathFromOpts :: CliOpts -> IO (Maybe FilePath) rulesFilePathFromOpts opts = do d <- getCurrentDirectory- maybe (return Nothing) (fmap Just . expandPath d) $ rules_file_ opts+ maybe (return Nothing) (fmap Just . expandPath d) $ mrules_file_ $ inputopts_ opts -- | Get the width in characters to use for console output. -- This comes from the --width option, or the COLUMNS environment@@ -636,23 +646,27 @@ compiledExts = ["",".com",".exe"] --- | Get the sorted unique filenames of all hledger-* executables in--- the current user's PATH. Currently these are: files in any of the--- PATH directories, named hledger-*, with either no extension (and no--- periods in the name) or one of the addonExtensions. Limitations:--- we do not currently check that the file is really a file (not eg a--- directory) or whether it has execute permission.-hledgerExecutablesInPath :: IO [String]-hledgerExecutablesInPath = do+-- | Get all sorted unique filenames in the current user's PATH. +-- We do not currently filter out non-file objects or files without execute permission.+likelyExecutablesInPath :: IO [String]+likelyExecutablesInPath = do pathdirs <- splitOneOf "[:;]" `fmap` getEnvSafe "PATH" pathfiles <- concat `fmap` mapM getDirectoryContentsSafe pathdirs- return $ nub $ sort $ filter isHledgerExeName pathfiles- -- XXX should exclude directories and files without execute permission.+ return $ nub $ sort pathfiles+ -- exclude directories and files without execute permission. -- These will do a stat for each hledger-*, probably ok. -- But they need paths, not just filenames- -- hledgerexes <- filterM doesFileExist hledgernamed- -- hledgerexes' <- filterM isExecutable hledgerexes- -- return hledgerexes+ -- exes' <- filterM doesFileExist exe'+ -- exes'' <- filterM isExecutable exes'+ -- return exes''++-- | Get the sorted unique filenames of all hledger-* executables in+-- the current user's PATH. These are files in any of the PATH directories,+-- named hledger-*, with either no extension (and no periods in the name) +-- or one of the addonExtensions. +-- We do not currently filter out non-file objects or files without execute permission.+hledgerExecutablesInPath :: IO [String]+hledgerExecutablesInPath = filter isHledgerExeName <$> likelyExecutablesInPath -- isExecutable f = getPermissions f >>= (return . executable)
+ Hledger/Cli/Commands.hs view
@@ -0,0 +1,636 @@+{-|+hledger's built-in commands, and helpers for printing the commands list.+-}++{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands (+ findCommand+ ,builtinCommands+ ,builtinCommandNames+ ,printCommandsList+ ,tests_Hledger_Cli_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.Equity+ ,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 Control.Monad+import Data.List+import Data.List.Split (splitOn)+import Data.Monoid ((<>))+import Data.String.Here+import Data.Text (Text)+import qualified Data.Text as T+import Data.Time.Calendar+import System.Console.CmdArgs.Explicit as C+import System.Exit+import Test.HUnit++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.Equity+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.Stats+import Hledger.Cli.Commands.Tags+++-- | The cmdargs subcommand mode and IO action for each builtin command.+-- Command actions take parsed CLI options and a (lazy) finalised journal.+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)+ ,(equitymode , equity)+ ,(helpmode , help')+ ,(importmode , importcmd)+ ,(incomestatementmode , incomestatement)+ ,(pricesmode , prices)+ ,(printmode , print')+ ,(printuniquemode , printunique)+ ,(registermode , register)+ ,(registermatchmode , registermatch)+ ,(rewritemode , rewrite)+ ,(statsmode , stats)+ ,(tagsmode , tags)+ ,(testmode , testcmd)+ ]++-- | All names and aliases of builtin commands.+builtinCommandNames :: [String]+builtinCommandNames = concatMap (modeNames . fst) builtinCommands++-- | Look up a builtin command's mode and action by exact command name or alias. +findCommand :: String -> Maybe (Mode RawOpts, CliOpts -> Journal -> IO ()) +findCommand cmdname = find (elem cmdname . modeNames . fst) builtinCommands ++-- | A template for the commands list, containing entries (indented lines)+-- for all known and some hypothetical builtin and addon commands.+-- These will be filtered based on the commands found at runtime, +-- except those beginning with "hledger", which are not filtered. +-- PROGVERSION is replaced with the program name and version.+-- OTHER is replaced with an entry for each unknown addon command found. +-- +-- The command descriptions here should be kept synced with +-- each command's builtin help and with hledger manual's command list.+--+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:+ equity 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+-------------------------------------------------------------------------------+|]++-- | Print the commands list, modifying the template above based on+-- the currently available addons. Missing addons will be removed, and+-- extra addons will be added under Misc.+printCommandsList :: [String] -> IO ()+printCommandsList addonsFound =+ putStr $+ regexReplace "PROGVERSION" (prognameandversion) $+ regexReplace "OTHER" (unlines $ map (' ':) unknownCommandsFound) $+ -- regexReplace "COUNT" (show cmdcount) $+ unlines $ concatMap adjustline $ lines $+ cmdlist+ where+ cmdlist = commandsList+ -- cmdcount = length $ commandsFromCommandsList cmdlist+ 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++-- | Extract the command names from a commands list like the above:+-- the first word (or words separated by |) of lines beginning with a space.+commandsFromCommandsList :: String -> [String]+commandsFromCommandsList s =+ concatMap (splitOn "|") [w | ' ':l <- lines s, let w:_ = words l]++++-- The test command, defined here so it can access other commands' tests.++testmode = (defCommandMode ["test"]) {+ modeHelp = "run built-in self-tests"+ ,modeArgs = ([], Just $ argsFlag "[REGEXPS]")+ ,modeGroupFlags = Group {+ groupUnnamed = []+ ,groupHidden = [+ flagNone ["tree"] (\opts -> setboolopt "tree" opts) "show tests hierarchically"+ ,flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show tests as a flat list"+ ]+ ,groupNamed = [generalflagsgroup3]+ }+ }++-- | Run some or all hledger-lib and hledger unit tests, and exit with success or failure.+testcmd :: CliOpts -> Journal -> IO ()+testcmd opts _ = do+ let ts = + (if tree_ $ reportopts_ opts then matchedTestsTree else matchedTestsFlat) + opts tests_Hledger_Cli_Commands+ results <- liftM (fst . flip (,) 0) $ runTestTT ts+ if errors results > 0 || failures results > 0+ then exitFailure+ else exitWith ExitSuccess++-- | All or pattern-matched tests, as a flat list to show simple names.+matchedTestsFlat opts = TestList . + filter (matchesAccount (queryFromOpts nulldate $ reportopts_ opts) . T.pack . testName) . + flattenTests++-- | All or pattern-matched tests, in the original suites to show hierarchical names.+matchedTestsTree opts = + filterTests (matchesAccount (queryFromOpts nulldate $ reportopts_ opts) . T.pack . testName) +++-- collected hledger-lib + hledger unit tests++tests_Hledger_Cli_Commands :: Test+tests_Hledger_Cli_Commands = TestList [+ tests_Hledger+ ,tests_Hledger_Cli_CliOptions+ -- ,tests_Hledger_Cli_Commands_Activity+ -- ,tests_Hledger_Cli_Commands_Add+ ,tests_Hledger_Cli_Commands_Balance+ ,tests_Hledger_Cli_Commands_Balancesheet+ ,tests_Hledger_Cli_Commands_Cashflow+ ,tests_Hledger_Cli_Commands_Incomestatement+ ,tests_Hledger_Cli_Commands_Print+ ,tests_Hledger_Cli_Commands_Register+ -- ,tests_Hledger_Cli_Commands_Stats++ -- some more tests easiest to define here:+ + ,"apply account directive" ~: + let ignoresourcepos j = j{jtxns=map (\t -> t{tsourcepos=nullsourcepos}) (jtxns j)} in+ let sameParse str1 str2 = do j1 <- readJournal Nothing Nothing True Nothing str1 >>= either error' (return . ignoresourcepos)+ j2 <- readJournal Nothing Nothing True Nothing str2 >>= either error' (return . ignoresourcepos)+ j1 `is` j2{jlastreadtime=jlastreadtime j1, jfiles=jfiles j1} --, jparsestate=jparsestate j1}+ in 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"+ )++ ,"apply account directive should preserve \"virtual\" posting type" ~: do+ j <- readJournal Nothing Nothing True 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+ assertBool "" $ paccount p == "test:from"+ assertBool "" $ ptype p == VirtualPosting+ + ,"account aliases" ~: do+ j <- readJournal Nothing Nothing True Nothing "!alias expenses = equity:draw:personal\n1/1\n (expenses:food) 1\n" >>= either error' return+ let p = head $ tpostings $ head $ jtxns j+ assertBool "" $ paccount p == "equity:draw:personal:food"++ ,"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"]++ -- ,"journalCanonicaliseAmounts" ~:+ -- "use the greatest precision" ~:+ -- (map asprecision $ journalAmountAndPriceCommodities $ journalCanonicaliseAmounts $ journalWithAmounts ["1","2.00"]) `is` [2,2]++ -- don't know what this should do+ -- ,"elideAccountName" ~: do+ -- (elideAccountName 50 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"+ -- `is` "aa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa")+ -- (elideAccountName 20 "aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa:aaaaaaaaaaaaaaaaaaaa"+ -- `is` "aa:aa:aaaaaaaaaaaaaa")++ ,"default year" ~: do+ j <- readJournal Nothing Nothing True Nothing defaultyear_journal_txt >>= either error' return+ tdate (head $ jtxns j) `is` fromGregorian 2009 1 1+ return ()++ ,"show dollars" ~: showAmount (usd 1) ~?= "$1.00"++ ,"show hours" ~: showAmount (hrs 1) ~?= "1.00h"++ ]+++-- test data++-- date1 = parsedate "2008/11/26"+-- t1 = LocalTime date1 midday++{-+samplejournal = readJournal' sample_journal_str++sample_journal_str = unlines+ ["; A sample journal file."+ ,";"+ ,"; Sets up this account tree:"+ ,"; assets"+ ,"; bank"+ ,"; checking"+ ,"; saving"+ ,"; cash"+ ,"; expenses"+ ,"; food"+ ,"; supplies"+ ,"; income"+ ,"; gifts"+ ,"; salary"+ ,"; liabilities"+ ,"; debts"+ ,""+ ,"2008/01/01 income"+ ," assets:bank:checking $1"+ ," income:salary"+ ,""+ ,"2008/06/01 gift"+ ," assets:bank:checking $1"+ ," income:gifts"+ ,""+ ,"2008/06/02 save"+ ," assets:bank:saving $1"+ ," assets:bank:checking"+ ,""+ ,"2008/06/03 * eat & shop"+ ," expenses:food $1"+ ," expenses:supplies $1"+ ," assets:cash"+ ,""+ ,"2008/12/31 * pay off"+ ," liabilities:debts $1"+ ," assets:bank:checking"+ ,""+ ,""+ ,";final comment"+ ]+-}++defaultyear_journal_txt :: Text+defaultyear_journal_txt = T.unlines+ ["Y2009"+ ,""+ ,"01/01 A"+ ," a $1"+ ," b"+ ]++-- write_sample_journal = writeFile "sample.journal" sample_journal_str++-- entry2_str = unlines+-- ["2007/01/27 * joes diner"+-- ," expenses:food:dining $10.00"+-- ," expenses:gifts $10.00"+-- ," assets:checking $-20.00"+-- ,""+-- ]++-- entry3_str = unlines+-- ["2007/01/01 * opening balance"+-- ," assets:cash $4.82"+-- ," equity:opening balances"+-- ,""+-- ,"2007/01/01 * opening balance"+-- ," assets:cash $4.82"+-- ," equity:opening balances"+-- ,""+-- ,"2007/01/28 coopportunity"+-- ," expenses:food:groceries $47.18"+-- ," assets:checking"+-- ,""+-- ]++-- periodic_entry1_str = unlines+-- ["~ monthly from 2007/2/2"+-- ," assets:saving $200.00"+-- ," assets:checking"+-- ,""+-- ]++-- periodic_entry2_str = unlines+-- ["~ monthly from 2007/2/2"+-- ," assets:saving $200.00 ;auto savings"+-- ," assets:checking"+-- ,""+-- ]++-- periodic_entry3_str = unlines+-- ["~ monthly from 2007/01/01"+-- ," assets:cash $4.82"+-- ," equity:opening balances"+-- ,""+-- ,"~ monthly from 2007/01/01"+-- ," assets:cash $4.82"+-- ," equity:opening balances"+-- ,""+-- ]++-- journal1_str = unlines+-- [""+-- ,"2007/01/27 * joes diner"+-- ," expenses:food:dining $10.00"+-- ," expenses:gifts $10.00"+-- ," assets:checking $-20.00"+-- ,""+-- ,""+-- ,"2007/01/28 coopportunity"+-- ," expenses:food:groceries $47.18"+-- ," assets:checking $-47.18"+-- ,""+-- ,""+-- ]++-- journal2_str = unlines+-- [";comment"+-- ,"2007/01/27 * joes diner"+-- ," expenses:food:dining $10.00"+-- ," assets:checking $-47.18"+-- ,""+-- ]++-- journal3_str = unlines+-- ["2007/01/27 * joes diner"+-- ," expenses:food:dining $10.00"+-- ,";intra-entry comment"+-- ," assets:checking $-47.18"+-- ,""+-- ]++-- journal4_str = unlines+-- ["!include \"somefile\""+-- ,"2007/01/27 * joes diner"+-- ," expenses:food:dining $10.00"+-- ," assets:checking $-47.18"+-- ,""+-- ]++-- journal5_str = ""++-- journal6_str = unlines+-- ["~ monthly from 2007/1/21"+-- ," expenses:entertainment $16.23 ;netflix"+-- ," assets:checking"+-- ,""+-- ,"; 2007/01/01 * opening balance"+-- ,"; assets:saving $200.04"+-- ,"; equity:opening balances "+-- ,""+-- ]++-- journal7_str = unlines+-- ["2007/01/01 * opening balance"+-- ," assets:cash $4.82"+-- ," equity:opening balances "+-- ,""+-- ,"2007/01/01 * opening balance"+-- ," income:interest $-4.82"+-- ," equity:opening balances "+-- ,""+-- ,"2007/01/02 * ayres suites"+-- ," expenses:vacation $179.92"+-- ," assets:checking "+-- ,""+-- ,"2007/01/02 * auto transfer to savings"+-- ," assets:saving $200.00"+-- ," assets:checking "+-- ,""+-- ,"2007/01/03 * poquito mas"+-- ," expenses:food:dining $4.82"+-- ," assets:cash "+-- ,""+-- ,"2007/01/03 * verizon"+-- ," expenses:phone $95.11"+-- ," assets:checking "+-- ,""+-- ,"2007/01/03 * discover"+-- ," liabilities:credit cards:discover $80.00"+-- ," assets:checking "+-- ,""+-- ,"2007/01/04 * blue cross"+-- ," expenses:health:insurance $90.00"+-- ," assets:checking "+-- ,""+-- ,"2007/01/05 * village market liquor"+-- ," expenses:food:dining $6.48"+-- ," assets:checking "+-- ,""+-- ]++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
+ Hledger/Cli/Commands/Accounts.hs view
@@ -0,0 +1,67 @@+{-|++The @accounts@ command lists account names:++- in flat mode (default), it lists the full names of accounts posted to by matched postings,+ clipped to the specified depth, possibly with leading components dropped.++- in tree mode, it shows the indented short names of accounts posted to by matched postings,+ and their parents, to the specified depth.++-}++{-# LANGUAGE OverloadedStrings #-}++module Hledger.Cli.Commands.Accounts (+ accountsmode+ ,accounts+ ,tests_Hledger_Cli_Commands_Accounts+) where++import Data.List+import Data.Monoid+-- import Data.Text (Text)+import qualified Data.Text as T+import System.Console.CmdArgs.Explicit as C+import Test.HUnit++import Hledger+import Prelude hiding (putStrLn)+import Hledger.Utils.UTF8IOCompat (putStrLn)+import Hledger.Cli.CliOptions+++-- | Command line options for this command.+accountsmode = (defCommandMode $ ["accounts"] ++ aliases) {+ modeHelp = "show account names" `withAliases` aliases+ ,modeHelpSuffix = [+ "This command lists the accounts referenced by matched postings (and in tree mode, their parents as well). The accounts can be depth-clipped (--depth N) or have their leading parts trimmed (--drop N)."+ ]+ ,modeGroupFlags = C.Group {+ groupUnnamed = [+ flagNone ["tree"] (\opts -> setboolopt "tree" opts) "show short account names, as a tree"+ ,flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show full account names, as a list (default)"+ ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts"+ ]+ ,groupHidden = []+ ,groupNamed = [generalflagsgroup1]+ }+ }+ where aliases = ["a"]++-- | The accounts command.+accounts :: CliOpts -> Journal -> IO ()+accounts CliOpts{reportopts_=ropts} j = do+ d <- getCurrentDay+ let q = queryFromOpts d ropts+ nodepthq = dbg1 "nodepthq" $ filterQuery (not . queryIsDepth) q+ depth = dbg1 "depth" $ queryDepth $ filterQuery queryIsDepth q+ ps = dbg1 "ps" $ journalPostings $ filterJournalPostings nodepthq j+ as = dbg1 "as" $ nub $ filter (not . T.null) $ map (clipAccountName depth) $ sort $ map paccount ps+ as' | tree_ ropts = expandAccountNames as+ | otherwise = as+ render a | tree_ ropts = T.replicate (2 * (accountNameLevel a - 1)) " " <> accountLeafName a+ | otherwise = maybeAccountNameDrop ropts a+ mapM_ (putStrLn . T.unpack . render) as'++tests_Hledger_Cli_Commands_Accounts = TestList []
+ Hledger/Cli/Commands/Activity.hs view
@@ -0,0 +1,58 @@+{-|++Print a bar chart of posting activity per day, or other report interval. ++-}++module Hledger.Cli.Commands.Activity+where++import Data.List+import Data.Maybe+import Data.Ord+import System.Console.CmdArgs.Explicit+import Text.Printf++import Hledger+import Hledger.Cli.CliOptions+import Prelude hiding (putStr)+import Hledger.Utils.UTF8IOCompat (putStr)++activitymode :: Mode RawOpts+activitymode = (defCommandMode $ ["activity"] ++ aliases) {+ modeHelp = "show an ascii barchart of posting counts per interval (default: daily)" `withAliases` aliases+ ,modeHelpSuffix = []+ ,modeGroupFlags = Group {+ groupUnnamed = []+ ,groupHidden = []+ ,groupNamed = [generalflagsgroup1]+ }+ }+ where aliases = []++barchar :: Char+barchar = '*'++-- | Print a bar chart of number of postings per report interval.+activity :: CliOpts -> Journal -> IO ()+activity CliOpts{reportopts_=ropts} j = do+ d <- getCurrentDay+ putStr $ showHistogram ropts (queryFromOpts d ropts) j++showHistogram :: ReportOpts -> Query -> Journal -> String+showHistogram opts q j = concatMap (printDayWith countBar) spanps+ where+ i = interval_ opts+ interval | i == NoInterval = Days 1+ | otherwise = i+ span' = queryDateSpan (date2_ opts) q `spanDefaultsFrom` journalDateSpan (date2_ opts) j+ spans = filter (DateSpan Nothing Nothing /=) $ splitSpan interval span'+ spanps = [(s, filter (isPostingInDateSpan s) ps) | s <- spans]+ -- same as Register+ -- should count transactions, not postings ?+ -- ps = sortBy (comparing postingDate) $ filterempties $ filter matchapats $ filterdepth $ journalPostings j+ ps = sortBy (comparing postingDate) $ filter (q `matchesPosting`) $ journalPostings j++printDayWith f (DateSpan b _, ps) = printf "%s %s\n" (show $ fromJust b) (f ps)++countBar ps = replicate (length ps) barchar
+ Hledger/Cli/Commands/Add.hs view
@@ -0,0 +1,455 @@+{-|+A history-aware add command to help with data entry.+|-}++{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-do-bind #-}+{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, RecordWildCards, TypeOperators, FlexibleContexts, OverloadedStrings #-}++module Hledger.Cli.Commands.Add (+ addmode+ ,add+ ,appendToJournalFileOrStdout+ ,journalAddTransaction+ ,transactionsSimilarTo+)+where++import Prelude ()+import Prelude.Compat+import Control.Exception as E+import Control.Monad+import Control.Monad.Trans.Class+import Control.Monad.State.Strict (evalState, evalStateT)+import Control.Monad.Trans (liftIO)+import Data.Char (toUpper, toLower)+import Data.Functor.Identity (Identity(..))+import Data.List.Compat+import qualified Data.Set as S+import Data.Maybe+import Data.Text (Text)+import qualified Data.Text as T+import Data.Time.Calendar (Day)+import Data.Typeable (Typeable)+import Safe (headDef, headMay)+import System.Console.CmdArgs.Explicit+import System.Console.Haskeline (runInputT, defaultSettings, setComplete)+import System.Console.Haskeline.Completion+import System.Console.Wizard+import System.Console.Wizard.Haskeline+import System.IO ( stderr, hPutStr, hPutStrLn )+import Text.Megaparsec.Compat+import Text.Printf++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.Commands.Register (postingsReportAsText)+++addmode = (defCommandMode ["add"]) {+ modeHelp = "prompt for transactions and add them to the journal"+ ,modeHelpSuffix = ["Defaults come from previous similar transactions; use query patterns to restrict these."]+ ,modeGroupFlags = Group {+ groupUnnamed = [+ flagNone ["no-new-accounts"] (\opts -> setboolopt "no-new-accounts" opts) "don't allow creating new accounts"+ ]+ ,groupHidden = []+ ,groupNamed = [generalflagsgroup2]+ }+ }++-- | State used while entering transactions.+data EntryState = EntryState {+ esOpts :: CliOpts -- ^ command line options+ ,esArgs :: [String] -- ^ command line arguments remaining to be used as defaults+ ,esToday :: Day -- ^ today's date+ ,esDefDate :: Day -- ^ the default date for next transaction+ ,esJournal :: Journal -- ^ the journal we are adding to+ ,esSimilarTransaction :: Maybe Transaction -- ^ the most similar historical txn+ ,esPostings :: [Posting] -- ^ postings entered so far in the current txn+ } deriving (Show,Typeable)++defEntryState = EntryState {+ esOpts = defcliopts+ ,esArgs = []+ ,esToday = nulldate+ ,esDefDate = nulldate+ ,esJournal = nulljournal+ ,esSimilarTransaction = Nothing+ ,esPostings = []+}++data RestartTransactionException = RestartTransactionException deriving (Typeable,Show)+instance Exception RestartTransactionException++-- data ShowHelpException = ShowHelpException deriving (Typeable,Show)+-- instance Exception ShowHelpException++-- | Read multiple transactions from the console, prompting for each+-- field, and append them to the journal file. If the journal came+-- from stdin, this command has no effect.+add :: CliOpts -> Journal -> IO ()+add opts j+ | journalFilePath j == "-" = return ()+ | otherwise = do+ hPrintf stderr "Adding transactions to journal file %s\n" (journalFilePath j)+ showHelp+ today <- getCurrentDay+ let es = defEntryState{esOpts=opts+ ,esArgs=map (T.unpack . stripquotes . T.pack) $ listofstringopt "args" $ rawopts_ opts+ ,esToday=today+ ,esDefDate=today+ ,esJournal=j+ }+ getAndAddTransactions es `E.catch` (\(_::UnexpectedEOF) -> putStr "")++showHelp = hPutStr stderr $ unlines [+ "Any command line arguments will be used as defaults."+ ,"Use tab key to complete, readline keys to edit, enter to accept defaults."+ ,"An optional (CODE) may follow transaction dates."+ ,"An optional ; COMMENT may follow descriptions or amounts."+ ,"If you make a mistake, enter < at any prompt to restart the transaction."+ ,"To end a transaction, enter . when prompted."+ ,"To quit, enter . at a date prompt or press control-d or control-c."+ ]++-- | Loop reading transactions from the console, prompting, validating+-- and appending each one to the journal file, until end of input or+-- ctrl-c (then raise an EOF exception). If provided, command-line+-- arguments are used as defaults; otherwise defaults come from the+-- most similar recent transaction in the journal.+getAndAddTransactions :: EntryState -> IO ()+getAndAddTransactions es@EntryState{..} = (do+ mt <- runInputT (setComplete noCompletion defaultSettings) (run $ haskeline $ confirmedTransactionWizard es)+ case mt of+ Nothing -> fail "urk ?"+ Just t -> do+ j <- if debug_ esOpts > 0+ then do hPrintf stderr "Skipping journal add due to debug mode.\n"+ return esJournal+ else do j' <- journalAddTransaction esJournal esOpts t+ hPrintf stderr "Saved.\n"+ return j'+ hPrintf stderr "Starting the next transaction (. or ctrl-D/ctrl-C to quit)\n"+ getAndAddTransactions es{esJournal=j, esDefDate=tdate t}+ )+ `E.catch` (\(_::RestartTransactionException) ->+ hPrintf stderr "Restarting this transaction.\n" >> getAndAddTransactions es)++-- confirmedTransactionWizard :: (ArbitraryIO :<: b, OutputLn :<: b, Line :<: b) => EntryState -> Wizard b Transaction+-- confirmedTransactionWizard :: EntryState -> Wizard Haskeline Transaction+confirmedTransactionWizard es@EntryState{..} = do+ t <- transactionWizard es+ -- liftIO $ hPrintf stderr {- "Transaction entered:\n%s" -} (show t)+ output $ show t+ y <- let def = "y" in+ retryMsg "Please enter y or n." $+ parser ((fmap ('y' ==)) . headMay . map toLower . strip) $+ defaultTo' def $ nonEmpty $+ maybeRestartTransaction $+ line $ green $ printf "Save this transaction to the journal ?%s: " (showDefault def)+ if y then return t else throw RestartTransactionException++transactionWizard es@EntryState{..} = do+ (date,code) <- dateAndCodeWizard es+ let es1@EntryState{esArgs=args1} = es{esArgs=drop 1 esArgs, esDefDate=date}+ (desc,comment) <- descriptionAndCommentWizard es1+ let mbaset = similarTransaction es1 desc+ when (isJust mbaset) $ liftIO $ hPrintf stderr "Using this similar transaction for defaults:\n%s" (show $ fromJust mbaset)+ let es2 = es1{esArgs=drop 1 args1, esSimilarTransaction=mbaset}+ balancedPostingsWizard = do+ ps <- postingsWizard es2{esPostings=[]}+ let t = nulltransaction{tdate=date+ ,tstatus=Unmarked+ ,tcode=code+ ,tdescription=desc+ ,tcomment=comment+ ,tpostings=ps+ }+ case balanceTransaction Nothing t of -- imprecise balancing (?)+ Right t' -> return t'+ Left err -> liftIO (hPutStrLn stderr $ "\n" ++ (capitalize err) ++ "please re-enter.") >> balancedPostingsWizard+ balancedPostingsWizard++-- Identify the closest recent match for this description in past transactions.+similarTransaction :: EntryState -> Text -> Maybe Transaction+similarTransaction EntryState{..} desc =+ let q = queryFromOptsOnly esToday $ reportopts_ esOpts+ historymatches = transactionsSimilarTo esJournal q desc+ bestmatch | null historymatches = Nothing+ | otherwise = Just $ snd $ head historymatches+ in bestmatch++dateAndCodeWizard EntryState{..} = do+ let def = headDef (showDate esDefDate) esArgs+ retryMsg "A valid hledger smart date is required. Eg: 2014/2/14, 14, yesterday." $+ parser (parseSmartDateAndCode esToday) $+ withCompletion (dateCompleter def) $+ defaultTo' def $ nonEmpty $+ maybeExit $+ maybeRestartTransaction $+ -- maybeShowHelp $+ line $ green $ printf "Date%s: " (showDefault def)+ where+ parseSmartDateAndCode refdate s = either (const Nothing) (\(d,c) -> return (fixSmartDate refdate d, c)) edc+ where+ edc = runParser (dateandcodep <* eof) "" $ T.pack $ lowercase s+ dateandcodep :: SimpleTextParser (SmartDate, Text)+ dateandcodep = do+ d <- smartdate+ c <- optional codep+ many spacenonewline+ eof+ return (d, T.pack $ fromMaybe "" c)+ -- defday = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) defdate+ -- datestr = showDate $ fixSmartDate defday smtdate++descriptionAndCommentWizard EntryState{..} = do+ let def = headDef "" esArgs+ s <- withCompletion (descriptionCompleter esJournal def) $+ defaultTo' def $ nonEmpty $+ maybeRestartTransaction $+ line $ green $ printf "Description%s: " (showDefault def)+ let (desc,comment) = (T.pack $ strip a, T.pack $ strip $ dropWhile (==';') b) where (a,b) = break (==';') s+ return (desc, comment)++postingsWizard es@EntryState{..} = do+ mp <- postingWizard es+ case mp of Nothing -> return esPostings+ Just p -> postingsWizard es{esArgs=drop 2 esArgs, esPostings=esPostings++[p]}++postingWizard es@EntryState{..} = do+ acct <- accountWizard es+ if acct `elem` [".",""]+ then case (esPostings, postingsBalanced esPostings) of+ ([],_) -> liftIO (hPutStrLn stderr "Please enter some postings first.") >> postingWizard es+ (_,False) -> liftIO (hPutStrLn stderr "Please enter more postings to balance the transaction.") >> postingWizard es+ (_,True) -> return Nothing -- no more postings, end of transaction+ else do+ let es1 = es{esArgs=drop 1 esArgs}+ (amt,comment) <- amountAndCommentWizard es1+ return $ Just nullposting{paccount=T.pack $ stripbrackets acct+ ,pamount=Mixed [amt]+ ,pcomment=comment+ ,ptype=accountNamePostingType $ T.pack acct+ }++postingsBalanced :: [Posting] -> Bool+postingsBalanced ps = isRight $ balanceTransaction Nothing nulltransaction{tpostings=ps}++accountWizard EntryState{..} = do+ let pnum = length esPostings + 1+ historicalp = maybe Nothing (Just . (!! (pnum-1)) . (++ (repeat nullposting)) . tpostings) esSimilarTransaction+ historicalacct = case historicalp of Just p -> showAccountName Nothing (ptype p) (paccount p)+ Nothing -> ""+ def = headDef historicalacct esArgs+ endmsg | canfinish && null def = " (or . or enter to finish this transaction)"+ | canfinish = " (or . to finish this transaction)"+ | otherwise = ""+ retryMsg "A valid hledger account name is required. Eg: assets:cash, expenses:food:eating out." $+ parser (parseAccountOrDotOrNull def canfinish) $+ withCompletion (accountCompleter esJournal def) $+ defaultTo' def $ -- nonEmpty $+ maybeRestartTransaction $+ line $ green $ printf "Account %d%s%s: " pnum (endmsg::String) (showDefault def)+ where+ canfinish = not (null esPostings) && postingsBalanced esPostings+ parseAccountOrDotOrNull :: String -> Bool -> String -> Maybe String+ parseAccountOrDotOrNull _ _ "." = dbg1 $ Just "." -- . always signals end of txn+ parseAccountOrDotOrNull "" True "" = dbg1 $ Just "" -- when there's no default and txn is balanced, "" also signals end of txn+ parseAccountOrDotOrNull def@(_:_) _ "" = dbg1 $ Just def -- when there's a default, "" means use that+ parseAccountOrDotOrNull _ _ s = dbg1 $ fmap T.unpack $+ either (const Nothing) validateAccount $+ flip evalState esJournal $ runParserT (accountnamep <* eof) "" (T.pack s) -- otherwise, try to parse the input as an accountname+ where+ validateAccount :: Text -> Maybe Text+ validateAccount t | no_new_accounts_ esOpts && not (t `elem` journalAccountNames esJournal) = Nothing+ | otherwise = Just t+ dbg1 = id -- strace++amountAndCommentWizard EntryState{..} = do+ let pnum = length esPostings + 1+ (mhistoricalp,followedhistoricalsofar) =+ case esSimilarTransaction of+ Nothing -> (Nothing,False)+ Just Transaction{tpostings=ps} -> (if length ps >= pnum then Just (ps !! (pnum-1)) else Nothing+ ,all (\(a,b) -> pamount a == pamount b) $ zip esPostings ps)+ def = case (esArgs, mhistoricalp, followedhistoricalsofar) of+ (d:_,_,_) -> d+ (_,Just hp,True) -> showamt $ pamount hp+ _ | pnum > 1 && not (isZeroMixedAmount balancingamt) -> showamt balancingamtfirstcommodity+ _ -> ""+ retryMsg "A valid hledger amount is required. Eg: 1, $2, 3 EUR, \"4 red apples\"." $+ parser parseAmountAndComment $+ withCompletion (amountCompleter def) $+ defaultTo' def $ nonEmpty $+ maybeRestartTransaction $+ line $ green $ printf "Amount %d%s: " pnum (showDefault def)+ where+ parseAmountAndComment s = either (const Nothing) Just $+ runParser+ (evalStateT (amountandcommentp <* eof) nodefcommodityj)+ ""+ (T.pack s)+ nodefcommodityj = esJournal{jparsedefaultcommodity=Nothing}+ amountandcommentp :: JournalParser Identity (Amount, Text)+ amountandcommentp = do+ a <- amountp+ lift (many spacenonewline)+ c <- T.pack <$> fromMaybe "" `fmap` optional (char ';' >> many anyChar)+ -- eof+ return (a,c)+ balancingamt = negate $ sum $ map pamount realps where realps = filter isReal esPostings+ balancingamtfirstcommodity = Mixed $ take 1 $ amounts balancingamt+ showamt =+ showMixedAmountWithPrecision+ -- what should this be ?+ -- 1 maxprecision (show all decimal places or none) ?+ -- 2 maxprecisionwithpoint (show all decimal places or .0 - avoids some but not all confusion with thousands separators) ?+ -- 3 canonical precision for this commodity in the journal ?+ -- 4 maximum precision entered so far in this transaction ?+ -- 5 3 or 4, whichever would show the most decimal places ?+ -- I think 1 or 4, whichever would show the most decimal places+ maxprecisionwithpoint+ --+ -- let -- (amt,comment) = (strip a, strip $ dropWhile (==';') b) where (a,b) = break (==';') amtcmt+ -- a = fromparse $ runParser (amountp <|> return missingamt) (jparsestate esJournal) "" amt+ -- awithoutjps = fromparse $ runParser (amountp <|> return missingamt) mempty "" amt+ -- defamtaccepted = Just (showAmount a) == mdefamt+ -- es2 = if defamtaccepted then es1 else es1{esHistoricalPostings=Nothing}+ -- mdefaultcommodityapplied = if acommodity a == acommodity awithoutjps then Nothing else Just $ acommodity a+ -- when (isJust mdefaultcommodityapplied) $+ -- liftIO $ hPutStrLn stderr $ printf "using default commodity (%s)" (fromJust mdefaultcommodityapplied)++maybeExit = parser (\s -> if s=="." then throw UnexpectedEOF else Just s)++maybeRestartTransaction = parser (\s -> if s=="<" then throw RestartTransactionException else Just s)++-- maybeShowHelp :: Wizard Haskeline String -> Wizard Haskeline String+-- maybeShowHelp wizard = maybe (liftIO showHelp >> wizard) return $+-- parser (\s -> if s=="?" then Nothing else Just s) wizard++-- Completion helpers++dateCompleter :: String -> CompletionFunc IO+dateCompleter = completer ["today","tomorrow","yesterday"]++descriptionCompleter :: Journal -> String -> CompletionFunc IO+descriptionCompleter j = completer (map T.unpack $ journalDescriptions j)++accountCompleter :: Journal -> String -> CompletionFunc IO+accountCompleter j = completer (map T.unpack $ journalAccountNamesUsed j)++amountCompleter :: String -> CompletionFunc IO+amountCompleter = completer []++-- | Generate a haskeline completion function from the given+-- completions and default, that case insensitively completes with+-- prefix matches, or infix matches above a minimum length, or+-- completes the null string with the default.+completer :: [String] -> String -> CompletionFunc IO+completer completions def = completeWord Nothing "" completionsFor+ where+ simpleCompletion' s = (simpleCompletion s){isFinished=False}+ completionsFor "" = return [simpleCompletion' def]+ completionsFor i = return (map simpleCompletion' ciprefixmatches)+ where+ ciprefixmatches = [c | c <- completions, i `isPrefixOf` c]+ -- mixed-case completions require haskeline > 0.7.1.2+ -- ciprefixmatches = [c | c <- completions, lowercase i `isPrefixOf` lowercase c]++--------------------------------------------------------------------------------++-- utilities++defaultTo' = flip defaultTo++withCompletion f = withSettings (setComplete f defaultSettings)++green s = "\ESC[1;32m\STX"++s++"\ESC[0m\STX"++showDefault "" = ""+showDefault s = " [" ++ s ++ "]"++-- | Append this transaction to the journal's file and transaction list.+journalAddTransaction :: Journal -> CliOpts -> Transaction -> IO Journal+journalAddTransaction j@Journal{jtxns=ts} opts t = do+ let f = journalFilePath j+ appendToJournalFileOrStdout f $ showTransactionUnelided t+ -- unelided shows all amounts explicitly, in case there's a price, cf #283+ when (debug_ opts > 0) $ do+ putStrLn $ printf "\nAdded transaction to %s:" f+ putStrLn =<< registerFromString (show t)+ return j{jtxns=ts++[t]}++-- | Append a string, typically one or more transactions, to a journal+-- file, or if the file is "-", dump it to stdout. Tries to avoid+-- excess whitespace.+appendToJournalFileOrStdout :: FilePath -> String -> IO ()+appendToJournalFileOrStdout f s+ | f == "-" = putStr s'+ | otherwise = appendFile f s'+ where s' = "\n" ++ ensureOneNewlineTerminated s++-- | Replace a string's 0 or more terminating newlines with exactly one.+ensureOneNewlineTerminated :: String -> String+ensureOneNewlineTerminated = (++"\n") . reverse . dropWhile (=='\n') . reverse++-- | Convert a string of journal data into a register report.+registerFromString :: String -> IO String+registerFromString s = do+ d <- getCurrentDay+ j <- readJournal' $ T.pack s+ return $ postingsReportAsText opts $ postingsReport ropts (queryFromOpts d ropts) j+ where+ ropts = defreportopts{empty_=True}+ opts = defcliopts{reportopts_=ropts}++capitalize :: String -> String+capitalize "" = ""+capitalize (c:cs) = toUpper c : cs++-- | Find the most similar and recent transactions matching the given+-- transaction description and report query. Transactions are listed+-- with their "relevancy" score, most relevant first.+transactionsSimilarTo :: Journal -> Query -> Text -> [(Double,Transaction)]+transactionsSimilarTo j q desc =+ sortBy compareRelevanceAndRecency+ $ filter ((> threshold).fst)+ [(compareDescriptions desc $ tdescription t, t) | t <- ts]+ where+ compareRelevanceAndRecency (n1,t1) (n2,t2) = compare (n2,tdate t2) (n1,tdate t1)+ ts = filter (q `matchesTransaction`) $ jtxns j+ threshold = 0++-- | Return a similarity measure, from 0 to 1, for two transaction+-- descriptions. This is like compareStrings, but first strips out+-- any numbers, to improve accuracy eg when there are bank transaction+-- ids from imported data.+compareDescriptions :: Text -> Text -> Double+compareDescriptions s t = compareStrings s' t'+ where s' = simplify $ T.unpack s+ t' = simplify $ T.unpack t+ simplify = filter (not . (`elem` ("0123456789" :: String)))++-- | Return a similarity measure, from 0 to 1, for two strings. This+-- was based on Simon White's string similarity algorithm+-- (http://www.catalysoft.com/articles/StrikeAMatch.html), later found+-- to be https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient,+-- modified to handle short strings better.+-- Todo: check out http://nlp.fi.muni.cz/raslan/2008/raslan08.pdf#page=14 .+compareStrings :: String -> String -> Double+compareStrings "" "" = 1+compareStrings (_:[]) "" = 0+compareStrings "" (_:[]) = 0+compareStrings (a:[]) (b:[]) = if toUpper a == toUpper b then 1 else 0+compareStrings s1 s2 = 2 * commonpairs / totalpairs+ where+ pairs1 = S.fromList $ wordLetterPairs $ uppercase s1+ pairs2 = S.fromList $ wordLetterPairs $ uppercase s2+ commonpairs = fromIntegral $ S.size $ S.intersection pairs1 pairs2+ totalpairs = fromIntegral $ S.size pairs1 + S.size pairs2++wordLetterPairs = concatMap letterPairs . words++letterPairs (a:b:rest) = [a,b] : letterPairs (b:rest)+letterPairs _ = []
+ Hledger/Cli/Commands/Balance.hs view
@@ -0,0 +1,553 @@+{-|++A ledger-compatible @balance@ command, with additional support for+multi-column reports.++Here is a description/specification for the balance command. See also+"Hledger.Reports" -> \"Balance reports\".+++/Basic balance report/++With no report interval (@--monthly@ etc.), hledger's balance+command emulates ledger's, showing accounts indented according to+hierarchy, along with their total amount posted (including subaccounts).++Here's an example. With @examples/sample.journal@, which defines the following account tree:++@+ assets+ bank+ checking+ saving+ cash+ expenses+ food+ supplies+ income+ gifts+ salary+ liabilities+ debts+@++the basic @balance@ command gives this output:++@+ $ hledger -f sample.journal balance+ $-1 assets+ $1 bank:saving+ $-2 cash+ $2 expenses+ $1 food+ $1 supplies+ $-2 income+ $-1 gifts+ $-1 salary+ $1 liabilities:debts+--------------------+ 0+@++Subaccounts are displayed indented below their parent. Only the account leaf name (the final part) is shown.+(With @--flat@, account names are shown in full and unindented.)++Each account's \"balance\" is the sum of postings in that account and any subaccounts during the report period.+When the report period includes all transactions, this is equivalent to the account's current balance.++The overall total of the highest-level displayed accounts is shown below the line.+(The @--no-total/-N@ flag prevents this.)++/Eliding and omitting/++Accounts which have a zero balance, and no non-zero subaccount+balances, are normally omitted from the report.+(The @--empty/-E@ flag forces such accounts to be displayed.)+Eg, above @checking@ is omitted because it has a zero balance and no subaccounts.++Accounts which have a single subaccount also being displayed, with the same balance,+are normally elided into the subaccount's line.+(The @--no-elide@ flag prevents this.)+Eg, above @bank@ is elided to @bank:saving@ because it has only a+single displayed subaccount (@saving@) and their balance is the same+($1). Similarly, @liabilities@ is elided to @liabilities:debts@.++/Date limiting/++The default report period is that of the whole journal, including all+known transactions. The @--begin\/-b@, @--end\/-e@, @--period\/-p@+options or @date:@/@date2:@ patterns can be used to report only+on transactions before and/or after specified dates.++/Depth limiting/++The @--depth@ option can be used to limit the depth of the balance report.+Eg, to see just the top level accounts (still including their subaccount balances):++@+$ hledger -f sample.journal balance --depth 1+ $-1 assets+ $2 expenses+ $-2 income+ $1 liabilities+--------------------+ 0+@++/Account limiting/++With one or more account pattern arguments, the report is restricted+to accounts whose name matches one of the patterns, plus their parents+and subaccounts. Eg, adding the pattern @o@ to the first example gives:++@+ $ hledger -f sample.journal balance o+ $1 expenses:food+ $-2 income+ $-1 gifts+ $-1 salary+--------------------+ $-1+@++* The @o@ pattern matched @food@ and @income@, so they are shown.++* @food@'s parent (@expenses@) is shown even though the pattern didn't+ match it, to clarify the hierarchy. The usual eliding rules cause it to be elided here.++* @income@'s subaccounts are also shown.++/Multi-column balance report/++hledger's balance command will show multiple columns when a reporting+interval is specified (eg with @--monthly@), one column for each sub-period.++There are three kinds of multi-column balance report, indicated by the heading:++* A \"period balance\" (or \"flow\") report (the default) shows the change of account+ balance in each period, which is equivalent to the sum of postings in each+ period. Here, checking's balance increased by 10 in Feb:++ > Change of balance (flow):+ >+ > Jan Feb Mar+ > assets:checking 20 10 -5++* A \"cumulative balance\" report (with @--cumulative@) shows the accumulated ending balance+ across periods, starting from zero at the report's start date.+ Here, 30 is the sum of checking postings during Jan and Feb:++ > Ending balance (cumulative):+ >+ > Jan Feb Mar+ > assets:checking 20 30 25++* A \"historical balance\" report (with @--historical/-H@) also shows ending balances,+ but it includes the starting balance from any postings before the report start date.+ Here, 130 is the balance from all checking postings at the end of Feb, including+ pre-Jan postings which created a starting balance of 100:++ > Ending balance (historical):+ >+ > Jan Feb Mar+ > assets:checking 120 130 125++/Eliding and omitting, 2/++Here's a (imperfect?) specification for the eliding/omitting behaviour:++* Each account is normally displayed on its own line.++* An account less deep than the report's max depth, with just one+interesting subaccount, and the same balance as the subaccount, is+non-interesting, and prefixed to the subaccount's line, unless+@--no-elide@ is in effect.++* An account with a zero inclusive balance and less than two interesting+subaccounts is not displayed at all, unless @--empty@ is in effect.++* Multi-column balance reports show full account names with no eliding+ (like @--flat@). Accounts (and periods) are omitted as described below.++/Which accounts to show in balance reports/++By default:++* single-column: accounts with non-zero balance in report period.+ (With @--flat@: accounts with non-zero balance and postings.)++* periodic: accounts with postings and non-zero period balance in any period++* cumulative: accounts with non-zero cumulative balance in any period++* historical: accounts with non-zero historical balance in any period++With @-E/--empty@:++* single-column: accounts with postings in report period++* periodic: accounts with postings in report period++* cumulative: accounts with postings in report period++* historical: accounts with non-zero starting balance ++ accounts with postings in report period++/Which periods (columns) to show in balance reports/++An empty period/column is one where no report account has any postings.+A zero period/column is one where no report account has a non-zero period balance.++Currently,++by default:++* single-column: N/A++* periodic: all periods within the overall report period,+ except for leading and trailing empty periods++* cumulative: all periods within the overall report period,+ except for leading and trailing empty periods++* historical: all periods within the overall report period,+ except for leading and trailing empty periods++With @-E/--empty@:++* single-column: N/A++* periodic: all periods within the overall report period++* cumulative: all periods within the overall report period++* historical: all periods within the overall report period++/What to show in empty cells/++An empty periodic balance report cell is one which has no corresponding postings.+An empty cumulative/historical balance report cell is one which has no correponding+or prior postings, ie the account doesn't exist yet.+Currently, empty cells show 0.++-}++{-# LANGUAGE OverloadedStrings #-}++module Hledger.Cli.Commands.Balance (+ balancemode+ ,balance+ ,balanceReportAsText+ ,balanceReportItemAsText+ ,multiBalanceReportAsText+ ,multiBalanceReportAsCsv+ ,renderBalanceReportTable+ ,balanceReportAsTable+ ,tests_Hledger_Cli_Commands_Balance+) where++import Data.List (intercalate)+import Data.Maybe+-- import Data.Monoid+import qualified Data.Text as T+import System.Console.CmdArgs.Explicit as C+import Text.CSV+import Test.HUnit+import Text.Printf (printf)+import Text.Tabular as T+import Text.Tabular.AsciiWide++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.Utils+++-- | Command line options for this command.+balancemode = (defCommandMode $ ["balance"] ++ aliases) { -- also accept but don't show the common bal alias+ modeHelp = "show accounts and balances" `withAliases` aliases+ ,modeGroupFlags = C.Group {+ groupUnnamed = [+ flagNone ["change"] (\opts -> setboolopt "change" opts)+ "show balance change in each period (default)"+ ,flagNone ["cumulative"] (\opts -> setboolopt "cumulative" opts)+ "show balance change accumulated across periods (in multicolumn reports)"+ ,flagNone ["historical","H"] (\opts -> setboolopt "historical" opts)+ "show historical ending balance in each period (includes postings before report start date)\n "+ ,flagNone ["tree"] (\opts -> setboolopt "tree" opts) "show accounts as a tree; amounts include subaccounts (default in simple reports)"+ ,flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show accounts as a list; amounts exclude subaccounts except when account is depth-clipped (default in multicolumn reports)\n "+ ,flagNone ["average","A"] (\opts -> setboolopt "average" opts) "show a row average column (in multicolumn reports)"+ ,flagNone ["row-total","T"] (\opts -> setboolopt "row-total" opts) "show a row total column (in multicolumn reports)"+ ,flagNone ["no-total","N"] (\opts -> setboolopt "no-total" opts) "omit the final total row"+ ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "omit N leading account name parts (in flat mode)"+ ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't squash boring parent accounts (in tree mode)"+ ,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format (in simple reports)"+ ,flagNone ["pretty-tables"] (\opts -> setboolopt "pretty-tables" opts) "use unicode when displaying tables"+ ,flagNone ["sort-amount","S"] (\opts -> setboolopt "sort-amount" opts) "sort by amount instead of account name"+ ]+ ++ outputflags+ ,groupHidden = []+ ,groupNamed = [generalflagsgroup1]+ }+ }+ where aliases = ["b","bal"]++-- | The balance command, prints a balance report.+balance :: CliOpts -> Journal -> IO ()+balance opts@CliOpts{reportopts_=ropts} j = do+ d <- getCurrentDay+ case lineFormatFromOpts ropts of+ Left err -> error' $ unlines [err]+ Right _ -> do+ let format = outputFormatFromOpts opts+ interval = interval_ ropts+ -- XXX shenanigans: use singleBalanceReport or multiBalanceReport when we must, + -- ie when there's a report interval, or when --historical or --cumulative + -- are used (balanceReport doesn't handle those).+ -- Otherwise prefer the older balanceReport since it can elide boring parents.+ -- See also compoundBalanceCommandSingleColumnReport, singleBalanceReport etc.+ case interval of+ NoInterval -> do+ let report+ -- For --historical/--cumulative, we must use multiBalanceReport.+ -- (This forces --no-elide.)+ | balancetype_ ropts `elem` [HistoricalBalance, CumulativeChange]+ = let ropts' | flat_ ropts = ropts+ | otherwise = ropts{accountlistmode_=ALTree}+ in singleBalanceReport ropts' (queryFromOpts d ropts) j+ | otherwise = balanceReport ropts (queryFromOpts d ropts) j+ render = case format of+ "csv" -> \ropts r -> (++ "\n") $ printCSV $ balanceReportAsCsv ropts r+ _ -> balanceReportAsText+ writeOutput opts $ render ropts report+ _ -> do+ let report = multiBalanceReport ropts (queryFromOpts d ropts) j+ render = case format of+ "csv" -> \ropts r -> (++ "\n") $ printCSV $ multiBalanceReportAsCsv ropts r+ _ -> multiBalanceReportAsText+ writeOutput opts $ render ropts report++-- single-column balance reports++-- | Find the best commodity to convert to when asked to show the+-- market value of this commodity on the given date. That is, the one+-- in which it has most recently been market-priced, ie the commodity+-- mentioned in the most recent applicable historical price directive+-- before this date.+-- defaultValuationCommodity :: Journal -> Day -> CommoditySymbol -> Maybe CommoditySymbol+-- defaultValuationCommodity j d c = mpamount <$> commodityValue j d c++-- | Render a single-column balance report as CSV.+balanceReportAsCsv :: ReportOpts -> BalanceReport -> CSV+balanceReportAsCsv opts (items, total) =+ ["account","balance"] :+ [[T.unpack a, showMixedAmountOneLineWithoutPrice b] | (a, _, _, b) <- items]+ +++ if no_total_ opts+ then []+ else [["total", showMixedAmountOneLineWithoutPrice total]]++-- | Render a single-column balance report as plain text.+balanceReportAsText :: ReportOpts -> BalanceReport -> String+balanceReportAsText opts ((items, total)) = unlines $ concat lines ++ t+ where+ fmt = lineFormatFromOpts opts+ lines = case fmt of+ Right fmt -> map (balanceReportItemAsText opts fmt) items+ Left err -> [[err]]+ t = if no_total_ opts+ then []+ else+ case fmt of+ Right fmt ->+ let+ -- abuse renderBalanceReportItem to render the total with similar format+ acctcolwidth = maximum' [T.length fullname | (fullname, _, _, _) <- items]+ totallines = map rstrip $ renderBalanceReportItem opts fmt (T.replicate (acctcolwidth+1) " ", 0, total)+ -- with a custom format, extend the line to the full report width;+ -- otherwise show the usual 20-char line for compatibility+ overlinewidth | isJust (format_ opts) = maximum' $ map length $ concat lines+ | otherwise = defaultTotalFieldWidth+ overline = replicate overlinewidth '-'+ in overline : totallines+ Left _ -> []++tests_balanceReportAsText = [+ "balanceReportAsText" ~: do+ -- "unicode in balance layout" ~: do+ j <- readJournal'+ "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"+ let opts = defreportopts+ balanceReportAsText opts (balanceReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is`+ unlines+ [" -100 актив:наличные"+ ," 100 расходы:покупки"+ ,"--------------------"+ ," 0"+ ]+ ]++{-+:r+This implementation turned out to be a bit convoluted but implements the following algorithm for formatting:++- If there is a single amount, print it with the account name directly:+- Otherwise, only print the account name on the last line.++ a USD 1 ; Account 'a' has a single amount+ EUR -1+ b USD -1 ; Account 'b' has two amounts. The account name is printed on the last line.+-}+-- | Render one balance report line item as plain text suitable for console output (or+-- whatever string format is specified). Note, prices will not be rendered, and+-- differently-priced quantities of the same commodity will appear merged.+-- The output will be one or more lines depending on the format and number of commodities.+balanceReportItemAsText :: ReportOpts -> StringFormat -> BalanceReportItem -> [String]+balanceReportItemAsText opts fmt (_, accountName, depth, amt) =+ renderBalanceReportItem opts fmt (+ maybeAccountNameDrop opts accountName,+ depth,+ normaliseMixedAmountSquashPricesForDisplay amt+ )++-- | Render a balance report item using the given StringFormat, generating one or more lines of text.+renderBalanceReportItem :: ReportOpts -> StringFormat -> (AccountName, Int, MixedAmount) -> [String]+renderBalanceReportItem opts fmt (acctname, depth, total) =+ lines $+ case fmt of+ OneLine comps -> concatOneLine $ render1 comps+ TopAligned comps -> concatBottomPadded $ render comps+ BottomAligned comps -> concatTopPadded $ render comps+ where+ render1 = map (renderComponent1 opts (acctname, depth, total))+ render = map (renderComponent opts (acctname, depth, total))++defaultTotalFieldWidth = 20++-- | Render one StringFormat component for a balance report item.+renderComponent :: ReportOpts -> (AccountName, Int, MixedAmount) -> StringFormatComponent -> String+renderComponent _ _ (FormatLiteral s) = s+renderComponent opts (acctname, depth, total) (FormatField ljust min max field) = case field of+ DepthSpacerField -> formatString ljust Nothing max $ replicate d ' '+ where d = case min of+ Just m -> depth * m+ Nothing -> depth+ AccountField -> formatString ljust min max (T.unpack acctname)+ TotalField -> fitStringMulti min max True False $ showamt total+ where+ showamt | color_ opts = cshowMixedAmountWithoutPrice+ | otherwise = showMixedAmountWithoutPrice+ _ -> ""++-- | Render one StringFormat component for a balance report item.+-- This variant is for use with OneLine string formats; it squashes+-- any multi-line rendered values onto one line, comma-and-space separated,+-- while still complying with the width spec.+renderComponent1 :: ReportOpts -> (AccountName, Int, MixedAmount) -> StringFormatComponent -> String+renderComponent1 _ _ (FormatLiteral s) = s+renderComponent1 opts (acctname, depth, total) (FormatField ljust min max field) = case field of+ AccountField -> formatString ljust min max ((intercalate ", " . lines) (indented (T.unpack acctname)))+ where+ -- better to indent the account name here rather than use a DepthField component+ -- so that it complies with width spec. Uses a fixed indent step size.+ indented = ((replicate (depth*2) ' ')++)+ TotalField -> fitStringMulti min max True False $ ((intercalate ", " . map strip . lines) (showamt total))+ where+ showamt | color_ opts = cshowMixedAmountWithoutPrice+ | otherwise = showMixedAmountWithoutPrice + _ -> ""++-- multi-column balance reports++-- | Render a multi-column balance report as CSV.+multiBalanceReportAsCsv :: ReportOpts -> MultiBalanceReport -> CSV+multiBalanceReportAsCsv opts (MultiBalanceReport (colspans, items, (coltotals,tot,avg))) =+ ("account" : "short account" : "indent" : map showDateSpan colspans+ ++ (if row_total_ opts then ["total"] else [])+ ++ (if average_ opts then ["average"] else [])+ ) :+ [T.unpack a : T.unpack a' : show i :+ map showMixedAmountOneLineWithoutPrice+ (amts+ ++ (if row_total_ opts then [rowtot] else [])+ ++ (if average_ opts then [rowavg] else []))+ | (a,a',i, amts, rowtot, rowavg) <- items]+ +++ if no_total_ opts+ then []+ else [["totals", "", ""]+ ++ map showMixedAmountOneLineWithoutPrice (+ coltotals+ ++ (if row_total_ opts then [tot] else [])+ ++ (if average_ opts then [avg] else [])+ )]++-- | Render a multi-column balance report as plain text suitable for console output.+multiBalanceReportAsText :: ReportOpts -> MultiBalanceReport -> String+multiBalanceReportAsText opts r =+ printf "%s in %s:\n\n" typeStr (showDateSpan $ multiBalanceReportSpan r)+ ++ renderBalanceReportTable opts tabl+ where+ tabl = balanceReportAsTable opts r+ typeStr :: String+ typeStr = case balancetype_ opts of+ PeriodChange -> "Balance changes"+ CumulativeChange -> "Ending balances (cumulative)"+ HistoricalBalance -> "Ending balances (historical)"++-- | Given a table representing a multi-column balance report (for example,+-- made using 'balanceReportAsTable'), render it in a format suitable for+-- console output.+renderBalanceReportTable :: ReportOpts -> Table String String MixedAmount -> String+renderBalanceReportTable (ReportOpts { pretty_tables_ = pretty, color_=usecolor }) = + unlines+ . addtrailingblank+ . trimborder + . lines+ . render pretty id id showamt+ . align+ where+ addtrailingblank = (++[""])+ trimborder = drop 1 . init . map (drop 1 . init)+ align (Table l t d) = Table l' t d+ where+ acctswidth = maximum' $ map strWidth (headerContents l)+ l' = padRightWide acctswidth <$> l+ showamt | usecolor = cshowMixedAmountOneLineWithoutPrice+ | otherwise = showMixedAmountOneLineWithoutPrice++-- | Build a 'Table' from a multi-column balance report.+balanceReportAsTable :: ReportOpts -> MultiBalanceReport -> Table String String MixedAmount+balanceReportAsTable opts (MultiBalanceReport (colspans, items, (coltotals,tot,avg))) =+ addtotalrow $ Table+ (T.Group NoLine $ map Header accts)+ (T.Group NoLine $ map Header colheadings)+ (map rowvals items)+ where+ mkDate = case balancetype_ opts of+ PeriodChange -> showDateSpan+ _ -> maybe "" (showDate . prevday) . spanEnd+ colheadings = map mkDate colspans+ ++ (if row_total_ opts then [" Total"] else [])+ ++ (if average_ opts then ["Average"] else [])+ accts = map renderacct items+ renderacct (a,a',i,_,_,_)+ | tree_ opts = replicate ((i-1)*2) ' ' ++ T.unpack a'+ | otherwise = T.unpack $ maybeAccountNameDrop opts a+ rowvals (_,_,_,as,rowtot,rowavg) = as+ ++ (if row_total_ opts then [rowtot] else [])+ ++ (if average_ opts then [rowavg] else [])+ addtotalrow | no_total_ opts = id+ | otherwise = (+----+ (row "" $+ coltotals+ ++ (if row_total_ opts && not (null coltotals) then [tot] else [])+ ++ (if average_ opts && not (null coltotals) then [avg] else [])+ ))++-- | Figure out the overall date span of a multicolumn balance report.+multiBalanceReportSpan :: MultiBalanceReport -> DateSpan+multiBalanceReportSpan (MultiBalanceReport ([], _, _)) = DateSpan Nothing Nothing+multiBalanceReportSpan (MultiBalanceReport (colspans, _, _)) = DateSpan (spanStart $ head colspans) (spanEnd $ last colspans)+++tests_Hledger_Cli_Commands_Balance = TestList+ tests_balanceReportAsText
+ Hledger/Cli/Commands/Balancesheet.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE QuasiQuotes, RecordWildCards, NoCPP #-}+{-|++The @balancesheet@ command prints a simple balance sheet.++-}++module Hledger.Cli.Commands.Balancesheet (+ balancesheetmode+ ,balancesheet+ ,tests_Hledger_Cli_Commands_Balancesheet+) where++import Data.String.Here+import System.Console.CmdArgs.Explicit+import Test.HUnit++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.CompoundBalanceCommand++balancesheetSpec = CompoundBalanceCommandSpec {+ cbcname = "balancesheet",+ cbcaliases = ["bs"],+ cbchelp = [here|+This command displays a simple balance sheet, showing historical ending+balances of asset and liability accounts (ignoring any report begin date). +It assumes that these accounts are under a top-level `asset` or `liability`+account (case insensitive, plural forms also allowed).+ |],+ cbctitle = "Balance Sheet",+ cbcqueries = [ ("Assets" , journalAssetAccountQuery, Just NormalPositive),+ ("Liabilities", journalLiabilityAccountQuery, Just NormalNegative)+ ],+ cbctype = HistoricalBalance+}++balancesheetmode :: Mode RawOpts+balancesheetmode = compoundBalanceCommandMode balancesheetSpec++balancesheet :: CliOpts -> Journal -> IO ()+balancesheet = compoundBalanceCommand balancesheetSpec++tests_Hledger_Cli_Commands_Balancesheet :: Test+tests_Hledger_Cli_Commands_Balancesheet = TestList+ [+ ]
+ Hledger/Cli/Commands/Balancesheetequity.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE QuasiQuotes, RecordWildCards, NoCPP #-}+{-|++The @balancesheetequity@ command prints a simple balance sheet.++-}++module Hledger.Cli.Commands.Balancesheetequity (+ balancesheetequitymode+ ,balancesheetequity+) where++import Data.String.Here+import System.Console.CmdArgs.Explicit++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.CompoundBalanceCommand++balancesheetequitySpec = CompoundBalanceCommandSpec {+ cbcname = "balancesheetequity",+ cbcaliases = ["bse"],+ cbchelp = [here|This command displays a simple balance sheet, showing historical ending+balances of asset, liability and equity accounts (ignoring any report begin date). +It assumes that these accounts are under a top-level `asset`, `liability` and `equity`+account (plural forms also allowed).+ |],+ cbctitle = "Balance Sheet With Equity",+ cbcqueries = [("Assets", journalAssetAccountQuery, Just NormalPositive),+ ("Liabilities", journalLiabilityAccountQuery, Just NormalNegative),+ ("Equity", journalEquityAccountQuery, Just NormalNegative)+ ],+ cbctype = HistoricalBalance+}++balancesheetequitymode :: Mode RawOpts+balancesheetequitymode = compoundBalanceCommandMode balancesheetequitySpec++balancesheetequity :: CliOpts -> Journal -> IO ()+balancesheetequity = compoundBalanceCommand balancesheetequitySpec
+ Hledger/Cli/Commands/Cashflow.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE QuasiQuotes, RecordWildCards, NoCPP #-}+{-|++The @cashflow@ command prints a simplified cashflow statement. It just+shows the change in all "cash" accounts for the period (without the+traditional segmentation into operating, investing, and financing+cash flows.)++-}++module Hledger.Cli.Commands.Cashflow (+ cashflowmode+ ,cashflow+ ,tests_Hledger_Cli_Commands_Cashflow+) where++import Data.String.Here+import System.Console.CmdArgs.Explicit+import Test.HUnit++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.CompoundBalanceCommand++cashflowSpec = CompoundBalanceCommandSpec {+ cbcname = "cashflow",+ cbcaliases = ["cf"],+ cbchelp = [here|+This command displays a simple cashflow statement, showing changes+in "cash" accounts. It assumes that these accounts are under a top-level +`asset` account (case insensitive, plural forms also allowed) and do not +contain `receivable` or `A/R` in their name. + |],+ cbctitle = "Cashflow Statement",+ cbcqueries = [("Cash flows", journalCashAccountQuery, Just NormalPositive)],+ cbctype = PeriodChange+}++cashflowmode :: Mode RawOpts+cashflowmode = compoundBalanceCommandMode cashflowSpec++cashflow :: CliOpts -> Journal -> IO ()+cashflow = compoundBalanceCommand cashflowSpec++tests_Hledger_Cli_Commands_Cashflow :: Test+tests_Hledger_Cli_Commands_Cashflow = TestList+ [+ ]
+ Hledger/Cli/Commands/Checkdates.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Checkdates (+ checkdatesmode+ ,checkdates+ ,tests_Hledger_Cli_Commands_Checkdates+) where++import Data.String.Here+import Hledger+import Hledger.Cli.CliOptions+import System.Console.CmdArgs.Explicit+import Test.HUnit+import Text.Printf++-- checkdatesmode :: Mode RawOpts+checkdatesmode = hledgerCommandMode+ [here| check-dates+Check that transactions are sorted by increasing date.+With --date2, checks secondary dates instead.+With --strict, dates must also be unique.+With a query, only matched transactions' dates are checked.+Reads the default journal file, or another specified with -f.+FLAGS+ |]+ [flagNone ["strict"] (\opts -> setboolopt "strict" opts) "makes date comparing strict"]+ [generalflagsgroup1]+ []+ ([], Just $ argsFlag "[QUERY]")++checkdates :: CliOpts -> Journal -> IO ()+checkdates CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do+ d <- getCurrentDay+ let ropts_ = ropts{accountlistmode_=ALFlat}+ let q = queryFromOpts d ropts_+ let ts = filter (q `matchesTransaction`) $+ jtxns $ journalSelectingAmountFromOpts ropts j+ let strict = boolopt "strict" rawopts+ let date = transactionDateFn ropts+ let compare a b =+ if strict+ then date a < date b+ else date a <= date b+ case checkTransactions compare ts of+ FoldAcc{fa_previous=Nothing} -> putStrLn "ok (empty journal)"+ FoldAcc{fa_error=Nothing} -> putStrLn "ok"+ FoldAcc{fa_error=Just error, fa_previous=Just previous} ->+ putStrLn $ printf ("ERROR: transaction out of%s date order"+ ++ "\nPrevious date: %s"+ ++ "\nDate: %s"+ ++ "\nLocation: %s"+ ++ "\nTransaction:\n\n%s")+ (if strict then " STRICT" else "")+ (show $ date previous)+ (show $ date error)+ (show $ tsourcepos error)+ (showTransactionUnelided error)++data FoldAcc a b = FoldAcc+ { fa_error :: Maybe a+ , fa_previous :: Maybe b+ }++foldWhile :: (a -> FoldAcc a b -> FoldAcc a b) -> FoldAcc a b -> [a] -> FoldAcc a b+foldWhile _ acc [] = acc+foldWhile fold acc (a:as) =+ case fold a acc of+ acc@FoldAcc{fa_error=Just _} -> acc+ acc -> foldWhile fold acc as++checkTransactions :: (Transaction -> Transaction -> Bool)+ -> [Transaction] -> FoldAcc Transaction Transaction+checkTransactions compare ts =+ foldWhile fold FoldAcc{fa_error=Nothing, fa_previous=Nothing} ts+ where+ fold current acc@FoldAcc{fa_previous=Nothing} = acc{fa_previous=Just current}+ fold current acc@FoldAcc{fa_previous=Just previous} =+ if compare previous current+ then acc{fa_previous=Just current}+ else acc{fa_error=Just current}++tests_Hledger_Cli_Commands_Checkdates :: Test+tests_Hledger_Cli_Commands_Checkdates = TestList+ [+ ]
+ Hledger/Cli/Commands/Checkdupes.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Checkdupes (+ checkdupesmode+ ,checkdupes+) +where++import Data.Function+import Data.List+import Data.String.Here+import qualified Data.Text as T+import Hledger+import Hledger.Cli.CliOptions+import System.Console.CmdArgs.Explicit+import Text.Printf++checkdupesmode :: Mode RawOpts+checkdupesmode = hledgerCommandMode+ [here| check-dupes+Reports account names having the same leaf but different prefixes. +In other words, two or more leaves that are categorized differently.+Reads the default journal file, or another specified as an argument.+An example: http://stefanorodighiero.net/software/hledger-dupes.html+ |]+ []+ [generalflagsgroup1]+ []+ ([], Nothing)++checkdupes _opts j = mapM_ render $ checkdupes' $ accountsNames j++accountsNames :: Journal -> [(String, AccountName)]+accountsNames j = map leafAndAccountName as+ where leafAndAccountName a = (T.unpack $ accountLeafName a, a)+ ps = journalPostings j+ as = nub $ sort $ map paccount ps++checkdupes' :: (Ord k, Eq k) => [(k, v)] -> [(k, [v])]+checkdupes' l = zip dupLeafs dupAccountNames+ where dupLeafs = map (fst . head) d+ dupAccountNames = map (map snd) d+ d = dupes' l+ dupes' = filter ((> 1) . length)+ . groupBy ((==) `on` fst)+ . sortBy (compare `on` fst)++render :: (String, [AccountName]) -> IO ()+render (leafName, accountNameL) = printf "%s as %s\n" leafName (concat $ intersperse ", " (map T.unpack accountNameL))
+ Hledger/Cli/Commands/Equity.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Equity (+ equitymode+ ,equity+) +where++import Data.Maybe+import Data.String.Here+import Data.Time.Calendar+import Hledger+import Hledger.Cli.CliOptions++equitymode = hledgerCommandMode+ [here| equity+Print a "closing balances" transaction that brings all accounts (or with+query arguments, just the matched accounts) to a zero balance, followed by an+opposite "opening balances" transaction that restores the balances from zero.+Such transactions can be useful, eg, for bringing account balances across+file boundaries.++FLAGS++The opening balances transaction is useful to carry over+asset/liability balances if you choose to start a new journal file,+eg at the beginning of the year.++The closing balances transaction is useful to zero out balances in+the old file, which gives you the option of reporting on both files+at once while still seeing correct balances.++Balances are calculated, and the opening transaction is dated, as of+the report end date, which you should specify with -e or date: (and+the closing transaction is dated one day earlier). If a report end+date is not specified, it defaults to today.++Example:+```shell+$ hledger equity -f 2015.journal -e 2016/1/1 assets liabilities >>2015.journal+# move the opening balances transaction to 2016.journal+$ hledger -f 2015.journal bal assets liabilities not:desc:closing # shows correct 2015 balances+$ hledger -f 2016.journal bal assets liabilities # shows correct 2016 balances+$ hledger -f 2015.journal -f 2016.journal bal assets liabilities # still shows correct 2016 balances+```+Open question: how to handle txns spanning a file boundary ? Eg:+```journal+2015/12/30 * food+ expenses:food:dining $10+ assets:bank:checking -$10 ; date:2016/1/4+```+This command might or might not have some connection to the concept of+"closing the books" in accounting.+ |]+ []+ [generalflagsgroup1]+ []+ ([], Just $ argsFlag "[QUERY]")++equity CliOpts{reportopts_=ropts} j = do+ today <- getCurrentDay+ let ropts_ = ropts{accountlistmode_=ALFlat}+ q = queryFromOpts today ropts_+ (acctbals,_) = balanceReport ropts_ q j+ balancingamt = negate $ sum $ map (\(_,_,_,b) -> normaliseMixedAmountSquashPricesForDisplay b) acctbals+ ps = [posting{paccount=a+ ,pamount=mixed [b]+ ,pbalanceassertion=Just b+ }+ |(a,_,_,mb) <- acctbals+ ,b <- amounts $ normaliseMixedAmountSquashPricesForDisplay mb+ ]+ ++ [posting{paccount="equity:opening balances", pamount=balancingamt}]+ enddate = fromMaybe today $ queryEndDate (date2_ ropts_) q+ nps = [posting{paccount=a+ ,pamount=mixed [negate b]+ ,pbalanceassertion=Just b{aquantity=0}+ }+ |(a,_,_,mb) <- acctbals+ ,b <- amounts $ normaliseMixedAmountSquashPricesForDisplay mb+ ]+ ++ [posting{paccount="equity:closing balances", pamount=negate balancingamt}]+ putStr $ showTransaction (nulltransaction{tdate=addDays (-1) enddate, tdescription="closing balances", tpostings=nps})+ putStr $ showTransaction (nulltransaction{tdate=enddate, tdescription="opening balances", tpostings=ps})
+ Hledger/Cli/Commands/Help.hs view
@@ -0,0 +1,84 @@+{-|++The help command.++|-}+--TODO rename manuals+--TODO substring matching++{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Help (++ helpmode+ ,help'++ ) where++import Prelude ()+import Prelude.Compat+import Data.Char+import Data.List+import Data.Maybe+import Data.String.Here+import Safe+import System.Console.CmdArgs.Explicit+import System.Environment+import System.IO++import Hledger.Data.RawOptions+import Hledger.Data.Types+import Hledger.Cli.CliOptions+import Hledger.Cli.DocFiles+--import Hledger.Utils.Debug++helpmode = (defCommandMode $ ["help"] ++ aliases) {+ modeHelp = "show any of the hledger manuals, choosing the most suitable viewer (info, man, a pager, or stdout). With no argument, list the manuals." `withAliases` aliases+ ,modeGroupFlags = Group {+ groupUnnamed = [+ flagNone ["info"] (setboolopt "info") "show the manual with info"+ ,flagNone ["man"] (setboolopt "man") "show the manual with man"+ ,flagNone ["pager"] (setboolopt "pager") "show the manual with $PAGER or less"+ ,flagNone ["cat"] (setboolopt "cat") "show the manual on stdout"+ ,flagNone ["help","h"] (setboolopt "help") "show this help"+ ]+ ,groupHidden = []+ ,groupNamed = []+ }+ ,modeArgs = ([], Just $ argsFlag "[MANUAL]")+}+ where aliases = []++-- | List or display one of the hledger manuals in various formats. +-- You can select a docs viewer with one of the `--info`, `--man`, `--pager`, `--cat` flags.+-- Otherwise it will use the first available of: info, man, $PAGER, less, stdout+-- (and always stdout if output is non-interactive). +help' :: CliOpts -> Journal -> IO ()+help' opts _ = do+ exes <- likelyExecutablesInPath+ pagerprog <- fromMaybe "less" <$> lookupEnv "PAGER"+ interactive <- hIsTerminalDevice stdout+ let+ args = take 1 $ listofstringopt "args" $ rawopts_ opts+ topic = case args of+ [pat] -> headMay [t | t <- docTopics, map toLower pat `isInfixOf` t]+ _ -> Nothing+ [info, man, pager, cat] = + [runInfoForTopic, runManForTopic, runPagerForTopic pagerprog, printHelpForTopic]+ viewer+ | boolopt "info" $ rawopts_ opts = info+ | boolopt "man" $ rawopts_ opts = man+ | boolopt "pager" $ rawopts_ opts = pager+ | boolopt "cat" $ rawopts_ opts = cat+ | not interactive = cat + | "info" `elem` exes = info+ | "man" `elem` exes = man+ | pagerprog `elem` exes = pager+ | otherwise = cat + case topic of+ Nothing -> putStrLn $ [here|+Please choose a manual by typing "hledger help MANUAL" (any substring is ok).+A viewer (info, man, a pager, or stdout) will be auto-selected,+or type "hledger help -h" to see options. Manuals available:+|] ++ "\n " ++ intercalate " " docTopics+ Just t -> viewer t
+ Hledger/Cli/Commands/Import.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Import (+ importmode+ ,importcmd+) +where++import Control.Monad+import Data.List+import Data.Ord+import Data.String.Here+import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.Commands.Add (journalAddTransaction)+-- import Hledger.Cli.Commands.Print (print')+import System.Console.CmdArgs.Explicit+import Text.Printf++importmode = hledgerCommandMode+ [here| import+Read new transactions added to each FILE since last run, and add them to+the main journal file. Or with --dry-run, just print the transactions +that would be added.++Input files are provided as arguments, or glob patterns. So eg to add new +transactions from all CSV files to the main journal: hledger import *.csv++New transactions are detected like print --new (using .latest.FILE state files)++FLAGS+ |]+ [flagNone ["dry-run"] (\opts -> setboolopt "dry-run" opts) "just show the transactions to be imported"] + [generalflagsgroup1]+ []+ ([], Just $ argsFlag "FILE [...]")++importcmd opts@CliOpts{rawopts_=rawopts,inputopts_=iopts} j = do+ let+ inputfiles = listofstringopt "args" rawopts+ dryrun = boolopt "dry-run" rawopts+ iopts' = iopts{new_=True, new_save_=not dryrun}+ case inputfiles of+ [] -> error' "please provide one or more input files as arguments"+ fs -> do+ enewj <- readJournalFilesWithOpts iopts' fs+ case enewj of+ Left e -> error' e + Right newj ->+ case sortBy (comparing tdate) $ jtxns newj of+ [] -> putStrLn "no new transactions"+ newts | dryrun -> do+ printf "would import %d new transactions:\n\n" (length newts)+ -- TODO how to force output here ?+ -- length (jtxns newj) `seq` print' opts{rawopts_=("explicit",""):rawopts} newj+ mapM_ (putStr . showTransactionUnelided) newts+ newts -> do+ foldM (flip journalAddTransaction opts) j newts -- gets forced somehow.. (how ?)+ printf "imported %d new transactions\n" (length newts)
+ Hledger/Cli/Commands/Incomestatement.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, NoCPP #-}+{-|++The @incomestatement@ command prints a simple income statement (profit & loss report).++-}++module Hledger.Cli.Commands.Incomestatement (+ incomestatementmode+ ,incomestatement+ ,tests_Hledger_Cli_Commands_Incomestatement+) where++import Data.String.Here+import System.Console.CmdArgs.Explicit+import Test.HUnit++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.CompoundBalanceCommand++incomestatementSpec = CompoundBalanceCommandSpec {+ cbcname = "incomestatement",+ cbcaliases = ["is"],+ cbchelp = [here|+This command displays a simple income statement, showing revenues+and expenses during a period. It assumes that these accounts are under a +top-level `revenue` or `income` or `expense` account (case insensitive,+plural forms also allowed).+ |],+ cbctitle = "Income Statement",+ cbcqueries = [ ("Revenues", journalIncomeAccountQuery, Just NormalNegative),+ ("Expenses", journalExpenseAccountQuery, Just NormalPositive)+ ],+ cbctype = PeriodChange+}++incomestatementmode :: Mode RawOpts+incomestatementmode = compoundBalanceCommandMode incomestatementSpec++incomestatement :: CliOpts -> Journal -> IO ()+incomestatement = compoundBalanceCommand incomestatementSpec++tests_Hledger_Cli_Commands_Incomestatement :: Test+tests_Hledger_Cli_Commands_Incomestatement = TestList+ [+ ]
+ Hledger/Cli/Commands/Prices.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Prices (+ pricesmode+ ,prices+) +where++import Data.Maybe+import Data.List+import Data.String.Here+import qualified Data.Text as T+import Data.Time+import Hledger+import Hledger.Cli.CliOptions+import System.Console.CmdArgs.Explicit++pricesmode = hledgerCommandMode+ [here| prices+Print all market prices from the journal.+ |]+ [flagNone ["costs"] (setboolopt "costs") "print transaction prices from postings"+ ,flagNone ["inverted-costs"] (setboolopt "inverted-costs") "print transaction inverted prices from postings also"]+ [generalflagsgroup1]+ []+ ([], Nothing)++prices opts j = do+ -- XXX the original hledger-prices script always ignored assertions + let cprices = concatMap postingCosts . allPostings $ j+ icprices = concatMap postingCosts . mapAmount invertPrice . allPostings $ j+ printPrices = mapM_ (putStrLn . showPrice)+ forBoolOpt opt | boolopt opt $ rawopts_ opts = id+ | otherwise = const []+ allPrices = sortOn mpdate . concat $+ [ jmarketprices j+ , forBoolOpt "costs" cprices+ , forBoolOpt "inverted-costs" icprices+ ]+ + printPrices allPrices++showPrice :: MarketPrice -> String+showPrice mp = unwords ["P", show $ mpdate mp, T.unpack . quoteCommoditySymbolIfNeeded $ mpcommodity mp, showAmountWithZeroCommodity $ mpamount mp]++divideAmount' :: Amount -> Quantity -> Amount+divideAmount' a d = a' where+ a' = (a `divideAmount` d) { astyle = style' }+ style' = (astyle a) { asprecision = precision' }+ extPrecision = (1+) . floor . logBase 10 $ (realToFrac d :: Double)+ precision' = extPrecision + asprecision (astyle a)++invertPrice :: Amount -> Amount+invertPrice a =+ case aprice a of+ NoPrice -> a+ UnitPrice pa -> invertPrice+ -- normalize to TotalPrice+ a { aprice = TotalPrice pa' } where+ pa' = (pa `divideAmount` (1 / aquantity a)) { aprice = NoPrice }+ TotalPrice pa ->+ a { aquantity = aquantity pa * signum (aquantity a), acommodity = acommodity pa, aprice = TotalPrice pa' } where+ pa' = pa { aquantity = abs $ aquantity a, acommodity = acommodity a, aprice = NoPrice, astyle = astyle a }++amountCost :: Day -> Amount -> Maybe MarketPrice+amountCost d a =+ case aprice a of+ NoPrice -> Nothing+ UnitPrice pa -> Just+ MarketPrice { mpdate = d, mpcommodity = acommodity a, mpamount = pa }+ TotalPrice pa -> Just+ MarketPrice { mpdate = d, mpcommodity = acommodity a, mpamount = pa `divideAmount'` abs (aquantity a) }++postingCosts :: Posting -> [MarketPrice]+postingCosts p = mapMaybe (amountCost date) . amounts $ pamount p where+ date = fromMaybe (tdate . fromJust $ ptransaction p) $ pdate p++allPostings :: Journal -> [Posting]+allPostings = concatMap tpostings . jtxns++mapAmount :: (Amount -> Amount) -> [Posting] -> [Posting]+mapAmount f = map pf where+ pf p = p { pamount = mf (pamount p) }+ mf = mixed . map f . amounts
+ Hledger/Cli/Commands/Print.hs view
@@ -0,0 +1,180 @@+{-|++A ledger-compatible @print@ command.++-}++{-# LANGUAGE OverloadedStrings #-}++module Hledger.Cli.Commands.Print (+ printmode+ ,print'+ -- ,entriesReportAsText+ ,originalTransaction+ ,tests_Hledger_Cli_Commands_Print+)+where++import Data.Text (Text)+import qualified Data.Text as T+import System.Console.CmdArgs.Explicit+import Test.HUnit+import Text.CSV++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.Utils+import Hledger.Cli.Commands.Add ( transactionsSimilarTo )+++printmode = (defCommandMode $ ["print"] ++ aliases) {+ modeHelp = "show transaction journal entries, sorted by date. With --date2, sort by secondary date instead." `withAliases` aliases+ ,modeGroupFlags = Group {+ groupUnnamed = [+ let arg = "STR" in+ flagReq ["match","m"] (\s opts -> Right $ setopt "match" s opts) arg+ ("show the transaction whose description is most similar to "++arg++", and is most recent")+ ,flagNone ["explicit","x"] (setboolopt "explicit")+ "show all amounts explicitly"+ ,flagNone ["new"] (setboolopt "new")+ "show only newer-dated transactions added in each file since last run"+ ]+ ++ outputflags+ ,groupHidden = []+ ,groupNamed = [generalflagsgroup1]+ }+ }+ where aliases = ["p","txns"]++-- | Print journal transactions in standard format.+print' :: CliOpts -> Journal -> IO ()+print' opts j = do+ case maybestringopt "match" $ rawopts_ opts of+ Nothing -> printEntries opts j+ Just desc -> printMatch opts j $ T.pack desc++printEntries :: CliOpts -> Journal -> IO ()+printEntries opts@CliOpts{reportopts_=ropts} j = do+ d <- getCurrentDay+ let q = queryFromOpts d ropts+ fmt = outputFormatFromOpts opts+ (render, ropts') = case fmt of+ "csv" -> ((++"\n") . printCSV . entriesReportAsCsv, ropts{accountlistmode_=ALFlat})+ _ -> (entriesReportAsText opts, ropts)+ writeOutput opts $ render $ entriesReport ropts' q j++entriesReportAsText :: CliOpts -> EntriesReport -> String+entriesReportAsText opts = concatMap (showTransactionUnelided . gettxn) + where+ gettxn | boolopt "explicit" $ rawopts_ opts = id -- use the fully inferred/explicit txn+ | otherwise = originalTransaction -- use the original txn (more or less)++-- Replace this transaction's postings with the original postings if any, but keep the+-- current possibly rewritten account names.+originalTransaction t = t { tpostings = map originalPostingPreservingAccount $ tpostings t }++-- Get the original posting if any, but keep the current possibly rewritten account name.+originalPostingPreservingAccount p = (originalPosting p) { paccount = paccount p }++-- XXX+-- tests_showTransactions = [+-- "showTransactions" ~: do++-- -- "print expenses" ~:+-- do+-- let opts = defreportopts{query_="expenses"}+-- d <- getCurrentDay+-- showTransactions opts (queryFromOpts d opts) samplejournal `is` unlines+-- ["2008/06/03 * eat & shop"+-- ," expenses:food $1"+-- ," expenses:supplies $1"+-- ," assets:cash $-2"+-- ,""+-- ]++-- -- , "print report with depth arg" ~:+-- do+-- let opts = defreportopts{depth_=Just 2}+-- d <- getCurrentDay+-- showTransactions opts (queryFromOpts d opts) samplejournal `is` unlines+-- ["2008/01/01 income"+-- ," assets:bank:checking $1"+-- ," income:salary $-1"+-- ,""+-- ,"2008/06/01 gift"+-- ," assets:bank:checking $1"+-- ," income:gifts $-1"+-- ,""+-- ,"2008/06/03 * eat & shop"+-- ," expenses:food $1"+-- ," expenses:supplies $1"+-- ," assets:cash $-2"+-- ,""+-- ,"2008/12/31 * pay off"+-- ," liabilities:debts $1"+-- ," assets:bank:checking $-1"+-- ,""+-- ]+-- ]++entriesReportAsCsv :: EntriesReport -> CSV+entriesReportAsCsv txns =+ ["txnidx","date","date2","status","code","description","comment","account","amount","commodity","credit","debit","posting-status","posting-comment"] :+ concatMap transactionToCSV txns++-- | Generate one CSV record per posting, duplicating the common transaction fields.+-- The txnidx field (transaction index) allows postings to be grouped back into transactions.+transactionToCSV :: Transaction -> CSV+transactionToCSV t =+ map (\p -> show idx:date:date2:status:code:description:comment:p)+ (concatMap postingToCSV $ tpostings t)+ where+ idx = tindex t+ description = T.unpack $ tdescription t+ date = showDate (tdate t)+ date2 = maybe "" showDate (tdate2 t)+ status = show $ tstatus t+ code = T.unpack $ tcode t+ comment = chomp $ strip $ T.unpack $ tcomment t++postingToCSV :: Posting -> CSV+postingToCSV p =+ map (\(a@(Amount {aquantity=q,acommodity=c})) ->+ let a_ = a{acommodity=""} in+ let amount = showAmount a_ in+ let commodity = T.unpack c in+ let credit = if q < 0 then showAmount $ negate a_ else "" in+ let debit = if q >= 0 then showAmount a_ else "" in+ account:amount:commodity:credit:debit:status:comment:[])+ amounts+ where+ Mixed amounts = pamount p+ status = show $ pstatus p+ account = showAccountName Nothing (ptype p) (paccount p)+ comment = chomp $ strip $ T.unpack $ pcomment p++-- --match++-- | Print the transaction most closely and recently matching a description+-- (and the query, if any).+printMatch :: CliOpts -> Journal -> Text -> IO ()+printMatch CliOpts{reportopts_=ropts} j desc = do+ d <- getCurrentDay+ let q = queryFromOpts d ropts+ case similarTransaction' j q desc of+ Nothing -> putStrLn "no matches found."+ Just t -> putStr $ showTransactionUnelided t++ where+ -- Identify the closest recent match for this description in past transactions.+ similarTransaction' :: Journal -> Query -> Text -> Maybe Transaction+ similarTransaction' j q desc+ | null historymatches = Nothing+ | otherwise = Just $ snd $ head historymatches+ where+ historymatches = transactionsSimilarTo j q desc++-- tests++tests_Hledger_Cli_Commands_Print = TestList []+ -- tests_showTransactions
+ Hledger/Cli/Commands/Printunique.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE QuasiQuotes #-}++{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Printunique (+ printuniquemode+ ,printunique+) +where++import Data.List+import Data.Ord+import Data.String.Here+import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.Commands.Print++printuniquemode = hledgerCommandMode+ [here| print-unique+Print transactions which do not reuse an already-seen description.++FLAGS++Example:+```shell+$ cat unique.journal+1/1 test+ (acct:one) 1+2/2 test+ (acct:two) 2+$ LEDGER_FILE=unique.journal hledger print-unique+(-f option not supported)+2015/01/01 test+ (acct:one) 1+```+ |]+ []+ [generalflagsgroup1]+ []+ ([], Nothing)++printunique opts j@Journal{jtxns=ts} = do+ print' opts j{jtxns=uniquify ts}+ where+ uniquify = nubBy (\t1 t2 -> thingToCompare t1 == thingToCompare t2) . sortBy (comparing thingToCompare)+ thingToCompare = tdescription+ -- thingToCompare = tdate
+ Hledger/Cli/Commands/Register.hs view
@@ -0,0 +1,204 @@+{-|++A ledger-compatible @register@ command.++-}++{-# LANGUAGE CPP, OverloadedStrings #-}++module Hledger.Cli.Commands.Register (+ registermode+ ,register+ ,postingsReportAsText+ ,postingsReportItemAsText+ -- ,showPostingWithBalanceForVty+ ,tests_Hledger_Cli_Commands_Register+) where++import Data.List+import Data.Maybe+-- import Data.Text (Text)+import qualified Data.Text as T+import System.Console.CmdArgs.Explicit+import Text.CSV+import Test.HUnit++import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.Utils+++registermode = (defCommandMode $ ["register"] ++ aliases) {+ modeHelp = "show postings and running total. With --date2, show and sort by secondary date instead." `withAliases` aliases+ ,modeGroupFlags = Group {+ groupUnnamed = [+ flagNone ["cumulative"] (\opts -> setboolopt "change" opts)+ "show running total from report start date (default)"+ ,flagNone ["historical","H"] (\opts -> setboolopt "historical" opts)+ "show historical running total/balance (includes postings before report start date)\n "+ ,flagNone ["average","A"] (\opts -> setboolopt "average" opts)+ "show running average of posting amounts instead of total (implies --empty)"+ ,flagNone ["related","r"] (\opts -> setboolopt "related" opts) "show postings' siblings instead"+ ,flagReq ["width","w"] (\s opts -> Right $ setopt "width" s opts) "N"+ ("set output width (default: " +++#ifdef mingw32_HOST_OS+ show defaultWidth+#else+ "terminal width"+#endif+ ++ " or $COLUMNS). -wN,M sets description width as well."+ )+ ]+ ++ outputflags+ ,groupHidden = []+ ,groupNamed = [generalflagsgroup1]+ }+ }+ where aliases = ["r","reg"]++-- | Print a (posting) register report.+register :: CliOpts -> Journal -> IO ()+register opts@CliOpts{reportopts_=ropts} j = do+ d <- getCurrentDay+ let fmt = outputFormatFromOpts opts+ render | fmt=="csv" = const ((++"\n") . printCSV . postingsReportAsCsv)+ | otherwise = postingsReportAsText+ writeOutput opts $ render opts $ postingsReport ropts (queryFromOpts d ropts) j++postingsReportAsCsv :: PostingsReport -> CSV+postingsReportAsCsv (_,is) =+ ["txnidx","date","description","account","amount","total"]+ :+ map postingsReportItemAsCsvRecord is++postingsReportItemAsCsvRecord :: PostingsReportItem -> Record+postingsReportItemAsCsvRecord (_, _, _, p, b) = [idx,date,desc,acct,amt,bal]+ where+ idx = show $ maybe 0 tindex $ ptransaction p+ date = showDate $ postingDate p -- XXX csv should show date2 with --date2+ desc = T.unpack $ maybe "" tdescription $ ptransaction p+ acct = bracket $ T.unpack $ paccount p+ where+ bracket = case ptype p of+ BalancedVirtualPosting -> (\s -> "["++s++"]")+ VirtualPosting -> (\s -> "("++s++")")+ _ -> id+ amt = showMixedAmountOneLineWithoutPrice $ pamount p+ bal = showMixedAmountOneLineWithoutPrice b++-- | Render a register report as plain text suitable for console output.+postingsReportAsText :: CliOpts -> PostingsReport -> String+postingsReportAsText opts (_,items) = unlines $ map (postingsReportItemAsText opts amtwidth balwidth) items+ where+ amtwidth = maximumStrict $ 12 : map (strWidth . showMixedAmount . itemamt) items+ balwidth = maximumStrict $ 12 : map (strWidth . showMixedAmount . itembal) items+ itemamt (_,_,_,Posting{pamount=a},_) = a+ itembal (_,_,_,_,a) = a++tests_postingsReportAsText = [+ "postingsReportAsText" ~: do+ -- "unicode in register layout" ~: do+ j <- readJournal'+ "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"+ let opts = defreportopts+ (postingsReportAsText defcliopts $ postingsReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is` unlines+ ["2009/01/01 медвежья шкура расходы:покупки 100 100"+ ," актив:наличные -100 0"]+ ]++-- | Render one register report line item as plain text. Layout is like so:+-- @+-- <---------------- width (specified, terminal width, or 80) -------------------->+-- date (10) description account amount (12) balance (12)+-- DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaa AAAAAAAAAAAA AAAAAAAAAAAA+-- @+-- If description's width is specified, account will use the remaining space.+-- Otherwise, description and account divide up the space equally.+--+-- With a report interval, the layout is like so:+-- @+-- <---------------- width (specified, terminal width, or 80) -------------------->+-- date (21) account amount (12) balance (12)+-- DDDDDDDDDDDDDDDDDDDDD aaaaaaaaaaaaaaaaaaaaaaaaaaaaa AAAAAAAAAAAA AAAAAAAAAAAA+-- @+--+-- date and description are shown for the first posting of a transaction only.+--+-- Returns a string which can be multi-line, eg if the running balance+-- has multiple commodities. Does not yet support formatting control+-- like balance reports.+--+postingsReportItemAsText :: CliOpts -> Int -> Int -> PostingsReportItem -> String+postingsReportItemAsText opts preferredamtwidth preferredbalwidth (mdate, menddate, mdesc, p, b) =+ -- use elide*Width to be wide-char-aware+ -- trace (show (totalwidth, datewidth, descwidth, acctwidth, amtwidth, balwidth)) $+ intercalate "\n" $+ [concat [fitString (Just datewidth) (Just datewidth) True True date+ ," "+ ,fitString (Just descwidth) (Just descwidth) True True desc+ ," "+ ,fitString (Just acctwidth) (Just acctwidth) True True acct+ ," "+ ,fitString (Just amtwidth) (Just amtwidth) True False amtfirstline+ ," "+ ,fitString (Just balwidth) (Just balwidth) True False balfirstline+ ]]+ +++ [concat [spacer+ ,fitString (Just amtwidth) (Just amtwidth) True False a+ ," "+ ,fitString (Just balwidth) (Just balwidth) True False b+ ]+ | (a,b) <- zip amtrest balrest+ ]+ where+ -- calculate widths+ (totalwidth,mdescwidth) = registerWidthsFromOpts opts+ (datewidth, date) = case (mdate,menddate) of+ (Just _, Just _) -> (21, showDateSpan (DateSpan mdate menddate))+ (Nothing, Just _) -> (21, "")+ (Just d, Nothing) -> (10, showDate d)+ _ -> (10, "")+ (amtwidth, balwidth)+ | shortfall <= 0 = (preferredamtwidth, preferredbalwidth)+ | otherwise = (adjustedamtwidth, adjustedbalwidth)+ where+ mincolwidth = 2 -- columns always show at least an ellipsis+ maxamtswidth = max 0 (totalwidth - (datewidth + 1 + mincolwidth + 2 + mincolwidth + 2 + 2))+ shortfall = (preferredamtwidth + preferredbalwidth) - maxamtswidth+ amtwidthproportion = fromIntegral preferredamtwidth / fromIntegral (preferredamtwidth + preferredbalwidth)+ adjustedamtwidth = round $ amtwidthproportion * fromIntegral maxamtswidth+ adjustedbalwidth = maxamtswidth - adjustedamtwidth++ remaining = totalwidth - (datewidth + 1 + 2 + amtwidth + 2 + balwidth)+ (descwidth, acctwidth)+ | hasinterval = (0, remaining - 2)+ | otherwise = (w, remaining - 2 - w)+ where+ hasinterval = isJust menddate+ w = fromMaybe ((remaining - 2) `div` 2) mdescwidth++ -- gather content+ desc = fromMaybe "" mdesc+ acct = parenthesise $ T.unpack $ elideAccountName awidth $ paccount p+ where+ (parenthesise, awidth) =+ case ptype p of+ BalancedVirtualPosting -> (\s -> "["++s++"]", acctwidth-2)+ VirtualPosting -> (\s -> "("++s++")", acctwidth-2)+ _ -> (id,acctwidth)+ amt = showMixedAmountWithoutPrice $ pamount p+ bal = showMixedAmountWithoutPrice b+ -- alternate behaviour, show null amounts as 0 instead of blank+ -- amt = if null amt' then "0" else amt'+ -- bal = if null bal' then "0" else bal'+ (amtlines, ballines) = (lines amt, lines bal)+ (amtlen, ballen) = (length amtlines, length ballines)+ numlines = max 1 (max amtlen ballen)+ (amtfirstline:amtrest) = take numlines $ amtlines ++ repeat "" -- posting amount is top-aligned+ (balfirstline:balrest) = take numlines $ replicate (numlines - ballen) "" ++ ballines -- balance amount is bottom-aligned+ spacer = replicate (totalwidth - (amtwidth + 2 + balwidth)) ' '++tests_Hledger_Cli_Commands_Register :: Test+tests_Hledger_Cli_Commands_Register = TestList+ tests_postingsReportAsText
+ Hledger/Cli/Commands/Registermatch.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Registermatch (+ registermatchmode+ ,registermatch+) +where++import Data.Char (toUpper)+import Data.List+import Data.String.Here+import qualified Data.Text as T+import Hledger+import Hledger.Cli.CliOptions+import Hledger.Cli.Commands.Register++registermatchmode = hledgerCommandMode+ [here| register-match+Print the one posting whose transaction description is closest to DESC, +in the style of the register command.+If there are multiple equally good matches, it shows the most recent.+Query options (options, not arguments) can be used to restrict the search space.+Helps ledger-autosync detect already-seen transactions when importing.+ |]+ []+ [generalflagsgroup1]+ []+ ([], Nothing)++registermatch :: CliOpts -> Journal -> IO ()+registermatch opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j = do+ let args' = listofstringopt "args" rawopts+ case args' of+ [desc] -> do+ d <- getCurrentDay+ let q = queryFromOptsOnly d ropts+ (_,pris) = postingsReport ropts q j+ ps = [p | (_,_,_,p,_) <- pris]+ case similarPosting ps desc of+ Nothing -> putStrLn "no matches found."+ Just p -> putStr $ postingsReportAsText opts ("",[pri])+ where pri = (Just (postingDate p)+ ,Nothing+ ,Just $ T.unpack (maybe "" tdescription $ ptransaction p)+ ,p+ ,0)+ _ -> putStrLn "please provide one description argument."++-- Identify the closest recent match for this description in the given date-sorted postings.+similarPosting :: [Posting] -> String -> Maybe Posting+similarPosting ps desc =+ let matches =+ sortBy compareRelevanceAndRecency+ $ filter ((> threshold).fst)+ [(maybe 0 (\t -> compareDescriptions desc (T.unpack $ tdescription t)) (ptransaction p), p) | p <- ps]+ where+ compareRelevanceAndRecency (n1,p1) (n2,p2) = compare (n2,postingDate p2) (n1,postingDate p1)+ threshold = 0+ in case matches of [] -> Nothing+ m:_ -> Just $ snd m++-- -- Identify the closest recent match for this description in past transactions.+-- similarTransaction :: Journal -> Query -> String -> Maybe Transaction+-- similarTransaction j q desc =+-- case historymatches = transactionsSimilarTo j q desc of+-- ((,t):_) = Just t+-- [] = Nothing++compareDescriptions :: [Char] -> [Char] -> Double+compareDescriptions s t = compareStrings s' t'+ where s' = simplify s+ t' = simplify t+ simplify = filter (not . (`elem` ("0123456789"::String)))++-- | Return a similarity measure, from 0 to 1, for two strings.+-- This is Simon White's letter pairs algorithm from+-- http://www.catalysoft.com/articles/StrikeAMatch.html+-- with a modification for short strings.+compareStrings :: String -> String -> Double+compareStrings "" "" = 1+compareStrings (_:[]) "" = 0+compareStrings "" (_:[]) = 0+compareStrings (a:[]) (b:[]) = if toUpper a == toUpper b then 1 else 0+compareStrings s1 s2 = 2.0 * fromIntegral i / fromIntegral u+ where+ i = length $ intersect pairs1 pairs2+ u = length pairs1 + length pairs2+ pairs1 = wordLetterPairs $ uppercase s1+ pairs2 = wordLetterPairs $ uppercase s2++wordLetterPairs = concatMap letterPairs . words++letterPairs (a:b:rest) = [a,b] : letterPairs (b:rest)+letterPairs _ = []+
+ Hledger/Cli/Commands/Rewrite.hs view
@@ -0,0 +1,276 @@+{-# LANGUAGE OverloadedStrings, LambdaCase, DeriveTraversable, ViewPatterns, QuasiQuotes #-}++module Hledger.Cli.Commands.Rewrite (+ rewritemode+ ,rewrite+) +where++import Control.Monad.Writer+import Data.List (sortOn, foldl')+import Data.String.Here+import qualified Data.Text as T+import Hledger+import Hledger.Data.AutoTransaction (runModifierTransaction)+import Hledger.Cli.CliOptions+import Hledger.Cli.Commands.Print+--import Hledger.Cli hiding (outputflags)+import System.Console.CmdArgs.Explicit+import Text.Printf+import Text.Megaparsec+import qualified Data.Algorithm.Diff as D++rewritemode = hledgerCommandMode+ [here| rewrite+Print all transactions, adding custom postings to the matched ones.++FLAGS++This is a start at a generic rewriter of transaction entries.+It reads the default journal and prints the transactions, like print,+but adds one or more specified postings to any transactions matching QUERY.+The posting amounts can be fixed, or a multiplier of the existing transaction's first posting amount. ++Examples:+```+hledger-rewrite.hs ^income --add-posting '(liabilities:tax) *.33 ; income tax' --add-posting '(reserve:gifts) $100'+hledger-rewrite.hs expenses:gifts --add-posting '(reserve:gifts) *-1"'+hledger-rewrite.hs -f rewrites.hledger+```+rewrites.hledger may consist of entries like:+```+= ^income amt:<0 date:2017+ (liabilities:tax) *0.33 ; tax on income+ (reserve:grocery) *0.25 ; reserve 25% for grocery+ (reserve:) *0.25 ; reserve 25% for grocery+```+Note the single quotes to protect the dollar sign from bash, +and the two spaces between account and amount.++More:++```shell+$ hledger rewrite -- [QUERY] --add-posting "ACCT AMTEXPR" ...+$ hledger rewrite -- ^income --add-posting '(liabilities:tax) *.33'+$ hledger rewrite -- expenses:gifts --add-posting '(budget:gifts) *-1"'+$ hledger rewrite -- ^income --add-posting '(budget:foreign currency) *0.25 JPY; diversify'+```++Argument for `--add-posting` option is a usual posting of transaction with an+exception for amount specification. More precisely, you can use `'*'` (star+symbol) before the amount to indicate that that this is a factor for an+amount of original matched posting. If the amount includes a commodity name,+the new posting amount will be in the new commodity; otherwise, it will be in+the matched posting amount's commodity.++#### Re-write rules in a file++During the run this tool will execute so called+["Automated Transactions"](http://ledger-cli.org/3.0/doc/ledger3.html#Automated-Transactions)+found in any journal it process. I.e instead of specifying this operations in+command line you can put them in a journal file.++```shell+$ rewrite-rules.journal+```++Make contents look like this:++```journal+= ^income+ (liabilities:tax) *.33++= expenses:gifts+ budget:gifts *-1+ assets:budget *1+```++Note that `'='` (equality symbol) that is used instead of date in transactions+you usually write. It indicates the query by which you want to match the+posting to add new ones.++```shell+$ hledger rewrite -- -f input.journal -f rewrite-rules.journal > rewritten-tidy-output.journal+```++This is something similar to the commands pipeline:++```shell+$ hledger rewrite -- -f input.journal '^income' --add-posting '(liabilities:tax) *.33' \+ | hledger rewrite -- -f - expenses:gifts --add-posting 'budget:gifts *-1' \+ --add-posting 'assets:budget *1' \+ > rewritten-tidy-output.journal+```++It is important to understand that relative order of such entries in journal is+important. You can re-use result of previously added postings.++#### Diff output format++To use this tool for batch modification of your journal files you may find+useful output in form of unified diff.++```shell+$ hledger rewrite -- --diff -f examples/sample.journal '^income' --add-posting '(liabilities:tax) *.33'+```++Output might look like:++```diff+--- /tmp/examples/sample.journal++++ /tmp/examples/sample.journal+@@ -18,3 +18,4 @@+ 2008/01/01 income+- assets:bank:checking $1++ assets:bank:checking $1+ income:salary++ (liabilities:tax) 0+@@ -22,3 +23,4 @@+ 2008/06/01 gift+- assets:bank:checking $1++ assets:bank:checking $1+ income:gifts++ (liabilities:tax) 0+```++If you'll pass this through `patch` tool you'll get transactions containing the+posting that matches your query be updated. Note that multiple files might be+update according to list of input files specified via `--file` options and+`include` directives inside of these files.++Be careful. Whole transaction being re-formatted in a style of output from+`hledger print`.++See also: ++https://github.com/simonmichael/hledger/issues/99++ |]+ [flagReq ["add-posting"] (\s opts -> Right $ setopt "add-posting" s opts) "'ACCT AMTEXPR'"+ "add a posting to ACCT, which may be parenthesised. AMTEXPR is either a literal amount, or *N which means the transaction's first matched amount multiplied by N (a decimal number). Two spaces separate ACCT and AMTEXPR."+ ,flagNone ["diff"] (setboolopt "diff") "generate diff suitable as an input for patch tool"+ ]+ [generalflagsgroup1]+ []+ ([], Just $ argsFlag "[QUERY] --add-posting \"ACCT AMTEXPR\" ...")+------------------------------------------------------------------------------++-- TODO regex matching and interpolating matched name in replacement+-- TODO interpolating match groups in replacement+-- TODO allow using this on unbalanced entries, eg to rewrite while editing++rewrite opts@CliOpts{rawopts_=rawopts,reportopts_=ropts} j@Journal{jtxns=ts} = do + d <- getCurrentDay+ let q = queryFromOpts d ropts+ modifier <- modifierTransactionFromOpts rawopts+ -- create re-writer+ let modifiers = modifier : jmodifiertxns j+ -- Note that some query matches require transaction. Thus modifiers+ -- pipeline should include txnTieKnot on every step.+ modifier' = foldr (flip (.) . fmap txnTieKnot . runModifierTransaction q) id modifiers+ -- rewrite matched transactions+ let j' = j{jtxns=map modifier' ts}+ -- run the print command, showing all transactions+ outputFromOpts rawopts opts{reportopts_=ropts{query_=""}} j j'++postingp' :: T.Text -> IO Posting+postingp' t = runErroringJournalParser (postingp Nothing <* eof) t' >>= \case+ Left err -> fail err+ Right p -> return p+ where t' = " " <> t <> "\n" -- inject space and newline for proper parsing++modifierTransactionFromOpts :: RawOpts -> IO ModifierTransaction+modifierTransactionFromOpts opts = do+ postings <- mapM (postingp' . stripquotes . T.pack) $ listofstringopt "add-posting" opts+ return+ ModifierTransaction { mtvalueexpr = T.empty, mtpostings = postings }++outputFromOpts :: RawOpts -> (CliOpts -> Journal -> Journal -> IO ())+outputFromOpts opts+ | boolopt "diff" opts = const diffOutput+ | otherwise = flip (const print')++diffOutput :: Journal -> Journal -> IO ()+diffOutput j j' = do+ let changed = [(originalTransaction t, originalTransaction t') | (t, t') <- zip (jtxns j) (jtxns j'), t /= t']+ putStr $ renderPatch $ map (uncurry $ diffTxn j) changed++type Chunk = (GenericSourcePos, [DiffLine String])++-- | Render list of changed lines as a unified diff+--+-- >>> putStr $ renderPatch [(GenericSourcePos "a" 1 1, [D.First "x", D.Second "y"])]+-- --- a+-- +++ a+-- @@ -1,1 +1,1 @@+-- -x+-- +y+-- >>> putStr $ renderPatch [(GenericSourcePos "a" 1 1, [D.Both "x" "x", D.Second "y"]), (GenericSourcePos "a" 5 1, [D.Second "z"])]+-- --- a+-- +++ a+-- @@ -1,1 +1,2 @@+-- x+-- +y+-- @@ -5,0 +6,1 @@+-- +z+-- >>> putStr $ renderPatch [(GenericSourcePos "a" 1 1, [D.Both "x" "x", D.Second "y"]), (GenericSourcePos "b" 5 1, [D.Second "z"])]+-- --- a+-- +++ a+-- @@ -1,1 +1,2 @@+-- x+-- +y+-- --- b+-- +++ b+-- @@ -5,0 +5,1 @@+-- +z+renderPatch :: [Chunk] -> String+renderPatch = go Nothing . sortOn fst where+ go _ [] = ""+ go Nothing cs@((sourceFilePath -> fp, _):_) = fileHeader fp ++ go (Just (fp, 0)) cs+ go (Just (fp, _)) cs@((sourceFilePath -> fp', _):_) | fp /= fp' = go Nothing cs+ go (Just (fp, offs)) ((sourceFirstLine -> lineno, diffs):cs) = chunkHeader ++ chunk ++ go (Just (fp, offs + adds - dels)) cs+ where+ chunkHeader = printf "@@ -%d,%d +%d,%d @@\n" lineno dels (lineno+offs) adds where+ (dels, adds) = foldl' countDiff (0, 0) diffs+ chunk = concatMap renderLine diffs+ fileHeader fp = printf "--- %s\n+++ %s\n" fp fp++ countDiff (dels, adds) = \case+ Del _ -> (dels + 1, adds)+ Add _ -> (dels , adds + 1)+ Ctx _ -> (dels + 1, adds + 1)++ renderLine = \case+ Del s -> '-' : s ++ "\n"+ Add s -> '+' : s ++ "\n"+ Ctx s -> ' ' : s ++ "\n"++diffTxn :: Journal -> Transaction -> Transaction -> Chunk+diffTxn j t t' =+ case tsourcepos t of+ GenericSourcePos fp lineno _ -> (GenericSourcePos fp (lineno+1) 1, diffs) where+ -- TODO: use range and produce two chunks: one removes part of+ -- original file, other adds transaction to new file with+ -- suffix .ledger (generated). I.e. move transaction from one file to another.+ diffs :: [DiffLine String]+ diffs = concat . map (traverse showPostingLines . mapDiff) $ D.getDiff (tpostings t) (tpostings t')+ pos@(JournalSourcePos fp (line, line')) -> (pos, diffs) where+ -- We do diff for original lines vs generated ones. Often leads+ -- to big diff because of re-format effect.+ diffs :: [DiffLine String]+ diffs = map mapDiff $ D.getDiff source changed'+ source | Just contents <- lookup fp $ jfiles j = map T.unpack . drop (line-1) . take line' $ T.lines contents+ | otherwise = []+ changed = lines $ showTransactionUnelided t'+ changed' | null changed = changed+ | null $ last changed = init changed+ | otherwise = changed++data DiffLine a = Del a | Add a | Ctx a+ deriving (Show, Functor, Foldable, Traversable)++mapDiff :: D.Diff a -> DiffLine a+mapDiff = \case+ D.First x -> Del x+ D.Second x -> Add x+ D.Both x _ -> Ctx x
+ Hledger/Cli/Commands/Stats.hs view
@@ -0,0 +1,112 @@+{-|++Print some statistics for the journal.++-}++{-# LANGUAGE OverloadedStrings #-}++module Hledger.Cli.Commands.Stats (+ statsmode+ ,stats+)+where++import Data.List+import Data.Maybe+import Data.Ord+import Data.HashSet (size, fromList)+-- import Data.Text (Text)+import qualified Data.Text as T+import Data.Time.Calendar+import System.Console.CmdArgs.Explicit+import Text.Printf+import qualified Data.Map as Map++import Hledger+import Hledger.Cli.CliOptions+import Prelude hiding (putStr)+import Hledger.Cli.Utils (writeOutput)+++statsmode = (defCommandMode $ ["stats"] ++ aliases) {+ modeHelp = "show some journal statistics" `withAliases` aliases+ ,modeGroupFlags = Group {+ groupUnnamed = [+ flagReq ["output-file","o"] (\s opts -> Right $ setopt "output-file" s opts) "FILE" "write output to FILE. A file extension matching one of the above formats selects that format."+ ]+ ,groupHidden = []+ ,groupNamed = [generalflagsgroup1]+ }+ }+ where aliases = []++-- like Register.summarisePostings+-- | Print various statistics for the journal.+stats :: CliOpts -> Journal -> IO ()+stats opts@CliOpts{reportopts_=reportopts_} j = do+ d <- getCurrentDay+ let q = queryFromOpts d reportopts_+ l = ledgerFromJournal q j+ reportspan = (ledgerDateSpan l) `spanDefaultsFrom` (queryDateSpan False q)+ intervalspans = splitSpan (interval_ reportopts_) reportspan+ showstats = showLedgerStats l d+ s = intercalate "\n" $ map showstats intervalspans+ writeOutput opts s++showLedgerStats :: Ledger -> Day -> DateSpan -> String+showLedgerStats l today span =+ unlines $ map (\(label,value) -> concatBottomPadded [printf fmt1 label, value]) stats+ where+ fmt1 = "%-" ++ show w1 ++ "s: "+ -- fmt2 = "%-" ++ show w2 ++ "s"+ w1 = maximum $ map (length . fst) stats+ -- w2 = maximum $ map (length . show . snd) stats+ stats = [+ ("Main file" :: String, path) -- ++ " (from " ++ source ++ ")")+ ,("Included files", unlines $ drop 1 $ journalFilePaths j)+ ,("Transactions span", printf "%s to %s (%d days)" (start span) (end span) days)+ ,("Last transaction", maybe "none" show lastdate ++ showelapsed lastelapsed)+ ,("Transactions", printf "%d (%0.1f per day)" tnum txnrate)+ ,("Transactions last 30 days", printf "%d (%0.1f per day)" tnum30 txnrate30)+ ,("Transactions last 7 days", printf "%d (%0.1f per day)" tnum7 txnrate7)+ ,("Payees/descriptions", show $ size $ fromList $ map (tdescription) ts)+ ,("Accounts", printf "%d (depth %d)" acctnum acctdepth)+ ,("Commodities", printf "%s (%s)" (show $ length cs) (T.intercalate ", " cs))+ -- Transactions this month : %(monthtxns)s (last month in the same period: %(lastmonthtxns)s)+ -- Unmarked transactions : %(unmarked)s+ -- Days since reconciliation : %(reconcileelapsed)s+ -- Days since last transaction : %(recentelapsed)s+ ]+ where+ j = ljournal l+ path = journalFilePath j+ ts = sortBy (comparing tdate) $ filter (spanContainsDate span . tdate) $ jtxns j+ as = nub $ map paccount $ concatMap tpostings ts+ cs = Map.keys $ commodityStylesFromAmounts $ concatMap amounts $ map pamount $ concatMap tpostings ts+ lastdate | null ts = Nothing+ | otherwise = Just $ tdate $ last ts+ lastelapsed = maybe Nothing (Just . diffDays today) lastdate+ showelapsed Nothing = ""+ showelapsed (Just days) = printf " (%d %s)" days' direction+ where days' = abs days+ direction | days >= 0 = "days ago" :: String+ | otherwise = "days from now"+ tnum = length ts+ start (DateSpan (Just d) _) = show d+ start _ = ""+ end (DateSpan _ (Just d)) = show d+ end _ = ""+ days = fromMaybe 0 $ daysInSpan span+ txnrate | days==0 = 0+ | otherwise = fromIntegral tnum / fromIntegral days :: Double+ tnum30 = length $ filter withinlast30 ts+ withinlast30 t = d >= addDays (-30) today && (d<=today) where d = tdate t+ txnrate30 = fromIntegral tnum30 / 30 :: Double+ tnum7 = length $ filter withinlast7 ts+ withinlast7 t = d >= addDays (-7) today && (d<=today) where d = tdate t+ txnrate7 = fromIntegral tnum7 / 7 :: Double+ acctnum = length as+ acctdepth | null as = 0+ | otherwise = maximum $ map accountNameLevel as+
+ Hledger/Cli/Commands/Tags.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}++module Hledger.Cli.Commands.Tags (+ tagsmode+ ,tags+) +where++import Data.List+import Data.String.Here+import qualified Data.Text.IO as T+import Hledger+import Hledger.Cli.CliOptions++tagsmode = hledgerCommandMode+ [here| tags+List all the tag names in use.+With a query, only matched transactions' tags are shown.+Reads the default journal file, or another specified with -f.+FLAGS+ |]+ [] -- [flagNone ["strict"] (\opts -> setboolopt "strict" opts) "makes date comparing strict"] -- + [generalflagsgroup1]+ []+ ([], Just $ argsFlag "[QUERY]")++tags CliOpts{rawopts_=_rawopts,reportopts_=ropts} j = do+ d <- getCurrentDay+ let+ q = queryFromOpts d ropts+ ts = filter (q `matchesTransaction`) $ jtxns $ journalSelectingAmountFromOpts ropts j+ tags = nub $ sort $ map fst $ concatMap transactionAllTags ts+ mapM_ T.putStrLn tags
Hledger/Cli/CompoundBalanceCommand.hs view
@@ -12,20 +12,22 @@ ,compoundBalanceCommand ) where -import Control.Monad (unless)-import Data.List (intercalate, foldl', isPrefixOf)+import Data.List (intercalate, foldl') import Data.Maybe (fromMaybe) import Data.Monoid (Sum(..), (<>))+import Data.Tuple.HT (uncurry3) import System.Console.CmdArgs.Explicit as C+import Text.CSV import Text.Tabular as T import Hledger-import Hledger.Cli.Balance+import Hledger.Cli.Commands.Balance import Hledger.Cli.CliOptions+import Hledger.Cli.Utils (writeOutput) -- | Description of a compound balance report command, -- from which we generate the command's cmdargs mode and IO action.--- A compound balance report shows one or more sections/subreports, +-- A compound balance report command shows one or more sections/subreports, -- each with its own title and subtotals row, in a certain order, -- plus a grand totals row if there's more than one section. -- Examples are the balancesheet, cashflow and incomestatement commands.@@ -34,7 +36,9 @@ cbcaliases :: [String], -- ^ command aliases cbchelp :: String, -- ^ command line help cbctitle :: String, -- ^ overall report title- cbcqueries :: [(String, Journal -> Query)], -- ^ title and (journal-parameterised) query for each subreport+ cbcqueries :: [(String, Journal -> Query, Maybe NormalBalance)],+ -- ^ title, journal-parameterised query, and expected normal balance for each subreport.+ -- The normal balance helps --sort-amount know how to sort negative amounts. cbctype :: BalanceType -- ^ the type of "balance" this report shows (overrides command line flags) } @@ -64,6 +68,9 @@ ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't squash boring parent accounts (in tree mode)" ,flagReq ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format (in simple reports)" ,flagNone ["pretty-tables"] (\opts -> setboolopt "pretty-tables" opts) "use unicode when displaying tables"+ ,flagNone ["sort-amount","S"] (\opts -> setboolopt "sort-amount" opts) "sort by amount instead of account name"+ ,outputFormatFlag+ ,outputFileFlag ] ,groupHidden = [] ,groupNamed = [generalflagsgroup1]@@ -76,7 +83,7 @@ -- | Generate a runnable command from a compound balance command specification. compoundBalanceCommand :: CompoundBalanceCommandSpec -> (CliOpts -> Journal -> IO ())-compoundBalanceCommand CompoundBalanceCommandSpec{..} CliOpts{command_=cmd, reportopts_=ropts, rawopts_=rawopts} j = do+compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{command_=cmd, reportopts_=ropts, rawopts_=rawopts} j = do d <- getCurrentDay let -- use the default balance type for this report, unless the user overrides @@ -95,10 +102,12 @@ PeriodChange -> "(Balance Changes)" CumulativeChange -> "(Cumulative Ending Balances)" HistoricalBalance -> "(Historical Ending Balances)"- -- set balance type and possibly tree mode in the report options. Some older notes: - -- For --historical/--cumulative, we must use multiBalanceReport (this forces --no-elide).- -- These settings format the output in a way that we can convert to a normal balance report - -- using singleBalanceReport, see Balance.hs for more information.+ -- Set balance type in the report options.+ -- XXX Also, use tree mode (by default, at least?) if --cumulative/--historical + -- are used in single column mode, since in that situation we will be using + -- singleBalanceReport which does not support eliding boring parents,+ -- and tree mode hides this.. or something.. + -- see also compoundBalanceCommandSingleColumnReport, #565 ropts' | not (flat_ ropts) && interval_ ropts==NoInterval && @@ -107,107 +116,232 @@ | otherwise = ropts{balancetype_=balancetype} userq = queryFromOpts d ropts'+ format = outputFormatFromOpts opts case interval_ ropts' of -- single-column report+ -- TODO refactor, support output format like multi column NoInterval -> do- let (subreportstr, total) = foldMap (uncurry (compoundBalanceCommandSingleColumnReport ropts' userq j)) cbcqueries- putStrLn $ title ++ "\n"- mapM_ putStrLn subreportstr- unless (no_total_ ropts' || cmd=="cashflow") . mapM_ putStrLn $ -- TODO temp- [ "Total:"- , "--------------------"- , padLeftWide 20 $ showamt (getSum total)- ]- where- showamt | color_ ropts' = cshowMixedAmountWithoutPrice- | otherwise = showMixedAmountWithoutPrice- + let+ -- concatenate the rendering and sum the totals from each subreport+ (subreportstr, total) = + foldMap (uncurry3 (compoundBalanceCommandSingleColumnReport ropts' userq j)) cbcqueries++ writeOutput opts $ unlines $+ [title ++ "\n"] +++ subreportstr +++ if (no_total_ ropts' || cmd=="cashflow")+ then []+ else+ [ "Total:"+ , "--------------------"+ , padLeftWide 20 $ showamt (getSum total)+ , ""+ ]+ where+ showamt | color_ ropts' = cshowMixedAmountWithoutPrice+ | otherwise = showMixedAmountWithoutPrice+ -- multi-column report _ -> do let- (subreporttables, subreporttotals, Sum overalltotal) = foldMap (uncurry (compoundBalanceCommandMultiColumnReports ropts' userq j)) cbcqueries- overalltable = case subreporttables of- t1:ts -> foldl' concatTables t1 ts- [] -> T.empty- overalltable'- | no_total_ ropts' || length cbcqueries == 1 =- overalltable- | otherwise =- overalltable- +====+- row "Total" overalltotals'- where- overalltotals = case subreporttotals of- [] -> []- ts -> foldl' (zipWith (+)) zeros ts'- where- -- A subreport might be empty and have no subtotals, count those as zeroes (#588).- -- Varying-length subtotals rows are handled similarly, though that is not expected to happen. - len = maximum $ map length ts- ts' = [take len $ as ++ repeat nullmixedamt | as <- ts]- zeros = replicate len nullmixedamt- overalltotals'- | null overalltotals = []- | otherwise = overalltotals- ++ (if row_total_ ropts' then [overalltotal] else [])- ++ (if average_ ropts' then [overallaverage] else [])- where- overallaverage = overalltotal `divideMixedAmount` fromIntegral (length overalltotals)- putStrLn title- putStrLn $ renderBalanceReportTable ropts' overalltable'---- Add the second table below the first, discarding its column headings.-concatTables (Table hLeft hTop dat) (Table hLeft' _ dat') =- Table (T.Group DoubleLine [hLeft, hLeft']) hTop (dat ++ dat')+ -- make a CompoundBalanceReport+ namedsubreports = + map (\(subreporttitle, subreportq, subreportnormalsign) -> + (subreporttitle, compoundBalanceSubreport ropts' userq j subreportq subreportnormalsign)) + cbcqueries+ subtotalrows = [coltotals | MultiBalanceReport (_,_,(coltotals,_,_)) <- map snd namedsubreports]+ overalltotals = case subtotalrows of+ [] -> ([], nullmixedamt, nullmixedamt)+ rs ->+ -- Sum the subtotals in each column.+ -- A subreport might be empty and have no subtotals, count those as zeros (#588).+ -- Short subtotals rows are also implicitly padded with zeros, though that is not expected to happen. + let+ numcols = maximum $ map length rs -- depends on non-null ts+ zeros = replicate numcols nullmixedamt+ rs' = [take numcols $ as ++ repeat nullmixedamt | as <- rs]+ coltotals = foldl' (zipWith (+)) zeros rs'+ grandtotal = sum coltotals+ grandavg | null coltotals = nullmixedamt+ | otherwise = grandtotal `divideMixedAmount` fromIntegral (length coltotals)+ in + (coltotals, grandtotal, grandavg)+ cbr =+ (title+ ,namedsubreports+ ,overalltotals + )+ -- render appropriately+ writeOutput opts $+ case format of+ "csv" -> printCSV (compoundBalanceReportAsCsv ropts cbr) ++ "\n"+ _ -> compoundBalanceReportAsText ropts' cbr --- | Run one subreport for a single-column compound balance command.--- Currently this returns the plain text rendering of the subreport,--- and its total.+-- | Run one subreport for a compound balance command in single-column mode.+-- Currently this returns the plain text rendering of the subreport, and its total.+-- The latter is wrapped in a Sum for easy monoidal combining. compoundBalanceCommandSingleColumnReport :: ReportOpts -> Query -> Journal -> String -> (Journal -> Query)+ -> Maybe NormalBalance -> ([String], Sum MixedAmount)-compoundBalanceCommandSingleColumnReport ropts q0 j t q = ([view], Sum amt)- where- q' = And [q0, q j]- rep@(_ , amt)- -- For --historical/--cumulative, we must use multiBalanceReport.- -- (This forces --no-elide.)- -- See Balance.hs's implementation of 'balance' for more information- | balancetype_ ropts `elem` [HistoricalBalance, CumulativeChange]- = singleBalanceReport ropts q' j- | otherwise- = balanceReport ropts q' j- view = intercalate "\n" [t <> ":", balanceReportAsText ropts rep]+compoundBalanceCommandSingleColumnReport ropts userq j subreporttitle subreportqfn subreportnormalsign = + ([subreportstr], Sum total)+ where+ q = And [subreportqfn j, userq]+ ropts' = ropts{normalbalance_=subreportnormalsign}+ r@(_,total)+ -- XXX For --historical/--cumulative, we must use singleBalanceReport;+ -- otherwise we use balanceReport -- because it supports eliding boring parents. + -- See also compoundBalanceCommand, Balance.hs -> balance.+ | balancetype_ ropts `elem` [CumulativeChange, HistoricalBalance] = singleBalanceReport ropts' q j+ | otherwise = balanceReport ropts' q j+ subreportstr = intercalate "\n" [subreporttitle <> ":", balanceReportAsText ropts r] --- | Run all the subreports for a multi-column compound balance command.--- Currently this returns a table of rendered balance amounts for each --- subreport (including a totals row), the totals row for each subreport --- (again, as mixedamounts), and the grand total.-compoundBalanceCommandMultiColumnReports- :: ReportOpts- -> Query- -> Journal- -> String- -> (Journal -> Query)- -> ([Table String String MixedAmount], [[MixedAmount]], Sum MixedAmount)-compoundBalanceCommandMultiColumnReports ropts q0 j t q = ([tabl], [coltotals], Sum tot)- where- singlesection = "Cash" `isPrefixOf` t -- TODO temp- ropts' = ropts { no_total_ = singlesection && no_total_ ropts, empty_ = True }- q' = And [q0, q j]- MultiBalanceReport (dates, rows, (coltotals,tot,avg)) =- multiBalanceReport ropts' q' j- rows' | empty_ ropts = rows- | otherwise = filter (not . emptyRow) rows- where- emptyRow (_,_,_,amts,_,_) = all isZeroMixedAmount amts- r = MultiBalanceReport (dates, rows', (coltotals, tot, avg))- Table hLeft hTop dat = balanceReportAsTable ropts' r- tabl = Table (T.Group SingleLine [Header t, hLeft]) hTop ([]:dat)+-- | A compound balance report has:+--+-- * an overall title+--+-- * one or more named multi balance reports, with the same column headings+--+-- * a list of overall totals for each column, and their grand total and average+--+-- It is used in compound balance report commands like balancesheet, +-- cashflow and incomestatement.+type CompoundBalanceReport = + ( String+ , [(String, MultiBalanceReport)]+ , ([MixedAmount], MixedAmount, MixedAmount)+ ) +-- | Run one subreport for a compound balance command in multi-column mode.+-- This returns a MultiBalanceReport.+compoundBalanceSubreport :: ReportOpts -> Query -> Journal -> (Journal -> Query) -> Maybe NormalBalance -> MultiBalanceReport+compoundBalanceSubreport ropts userq j subreportqfn subreportnormalsign = r'+ where+ -- force --empty to ensure same columns in all sections+ ropts' = ropts { empty_=True, normalbalance_=subreportnormalsign }+ -- run the report+ q = And [subreportqfn j, userq]+ r@(MultiBalanceReport (dates, rows, totals)) = multiBalanceReport ropts' q j+ -- if user didn't specify --empty, now remove the all-zero rows+ r' | empty_ ropts = r+ | otherwise = MultiBalanceReport (dates, rows', totals) + where+ rows' = filter (not . emptyRow) rows+ where+ emptyRow (_,_,_,amts,_,_) = all isZeroMixedAmount amts++-- | Render a compound balance report as plain text suitable for console output.+{- Eg:+Balance Sheet++ || 2017/12/31 Total Average +=============++===============================+ Assets || +-------------++-------------------------------+ assets:b || 1 1 1 +-------------++-------------------------------+ || 1 1 1 +=============++===============================+ Liabilities || +-------------++-------------------------------+-------------++-------------------------------+ || +=============++===============================+ Total || 1 1 1 ++-}+compoundBalanceReportAsText :: ReportOpts -> CompoundBalanceReport -> String+compoundBalanceReportAsText ropts (title, subreports, (coltotals, grandtotal, grandavg)) =+ title ++ "\n\n" ++ + renderBalanceReportTable ropts bigtable'+ where+ singlesubreport = length subreports == 1+ bigtable = + case map (subreportAsTable ropts singlesubreport) subreports of+ [] -> T.empty+ r:rs -> foldl' concatTables r rs+ bigtable'+ | no_total_ ropts || singlesubreport = + bigtable+ | otherwise =+ bigtable+ +====++ row "Total" (+ coltotals+ ++ (if row_total_ ropts then [grandtotal] else [])+ ++ (if average_ ropts then [grandavg] else [])+ )++ -- | Convert a named multi balance report to a table suitable for+ -- concatenating with others to make a compound balance report table.+ subreportAsTable ropts singlesubreport (title, r) = t+ where+ -- unless there's only one section, always show the subtotal row+ ropts' | singlesubreport = ropts+ | otherwise = ropts{ no_total_=False }+ -- convert to table+ Table lefthdrs tophdrs cells = balanceReportAsTable ropts' r+ -- tweak the layout+ t = Table (T.Group SingleLine [Header title, lefthdrs]) tophdrs ([]:cells)++-- | Add the second table below the first, discarding its column headings.+concatTables (Table hLeft hTop dat) (Table hLeft' _ dat') =+ Table (T.Group DoubleLine [hLeft, hLeft']) hTop (dat ++ dat')++-- | Render a compound balance report as CSV.+{- Eg: +ghci> :main -f examples/sample.journal bs -Y -O csv -AT+"Balance Sheet","","","","",""+"Assets","","","","",""+"account","short account","indent","2008","total","average"+"assets:bank:saving","saving","3","$1","$1","$1"+"assets:cash","cash","2","$-2","$-2","$-2"+"totals","","","$-1","$-1","$-1"+"Liabilities","","","","",""+"account","short account","indent","2008","total","average"+"liabilities:debts","debts","2","$1","$1","$1"+"totals","","","$1","$1","$1"+"Total","0","0","0"+-}+compoundBalanceReportAsCsv :: ReportOpts -> CompoundBalanceReport -> CSV+compoundBalanceReportAsCsv ropts (title, subreports, (coltotals, grandtotal, grandavg)) =+ addtotals $+ padRow title :+ concatMap (subreportAsCsv ropts singlesubreport) subreports+ where+ singlesubreport = length subreports == 1+ subreportAsCsv ropts singlesubreport (subreporttitle, multibalreport) =+ padRow subreporttitle :+ multiBalanceReportAsCsv ropts' multibalreport+ where+ -- unless there's only one section, always show the subtotal row+ ropts' | singlesubreport = ropts+ | otherwise = ropts{ no_total_=False }+ padRow s = take numcols $ s : repeat ""+ where+ numcols+ | null subreports = 1+ | otherwise =+ (3 +) $ -- account name & indent columns+ (if row_total_ ropts then (1+) else id) $+ (if average_ ropts then (1+) else id) $+ maximum $ -- depends on non-null subreports+ map (\(MultiBalanceReport (amtcolheadings, _, _)) -> length amtcolheadings) $ + map snd subreports+ addtotals+ | no_total_ ropts || length subreports == 1 = id+ | otherwise = (++ + ["Total" :+ map showMixedAmountOneLineWithoutPrice (+ coltotals+ ++ (if row_total_ ropts then [grandtotal] else [])+ ++ (if average_ ropts then [grandavg] else [])+ )+ ])
Hledger/Cli/DocFiles.hs view
@@ -16,6 +16,7 @@ ,printHelpForTopic ,runManForTopic ,runInfoForTopic+ ,runPagerForTopic ) where @@ -33,22 +34,22 @@ docFiles :: IsString a => [(Topic, (a, a, a))] docFiles = [- ("cli",+ ("hledger", ($(makeRelativeToProject "doc/hledger.1" >>= embedStringFile) ,$(makeRelativeToProject "doc/hledger.1.txt" >>= embedStringFile) ,$(makeRelativeToProject "doc/hledger.1.info" >>= embedStringFile) ))- ,("ui",+ ,("hledger-ui", ($(makeRelativeToProject "doc/other/hledger-ui.1" >>= embedStringFile) ,$(makeRelativeToProject "doc/other/hledger-ui.1.txt" >>= embedStringFile) ,$(makeRelativeToProject "doc/other/hledger-ui.1.info" >>= embedStringFile) ))- ,("web",+ ,("hledger-web", ($(makeRelativeToProject "doc/other/hledger-web.1" >>= embedStringFile) ,$(makeRelativeToProject "doc/other/hledger-web.1.txt" >>= embedStringFile) ,$(makeRelativeToProject "doc/other/hledger-web.1.info" >>= embedStringFile) ))- ,("api",+ ,("hledger-api", ($(makeRelativeToProject "doc/other/hledger-api.1" >>= embedStringFile) ,$(makeRelativeToProject "doc/other/hledger-api.1.txt" >>= embedStringFile) ,$(makeRelativeToProject "doc/other/hledger-api.1.info" >>= embedStringFile)@@ -93,6 +94,15 @@ printHelpForTopic :: Topic -> IO () printHelpForTopic t = putStrLn $ lookupDocTxt t++runPagerForTopic :: FilePath -> Topic -> IO ()+runPagerForTopic exe t = do+ (Just inp, _, _, ph) <- createProcess (proc exe []){+ std_in=CreatePipe+ }+ hPutStrLn inp (lookupDocTxt t)+ _ <- waitForProcess ph+ return () runManForTopic :: Topic -> IO () runManForTopic t =
− Hledger/Cli/Help.hs
@@ -1,42 +0,0 @@-{-|--The help command.--|-}--module Hledger.Cli.Help (-- helpmode- ,help'-- ) where--import Prelude ()-import Prelude.Compat-import Data.List-import System.Console.CmdArgs.Explicit--import Hledger.Data.RawOptions-import Hledger.Cli.CliOptions-import Hledger.Cli.DocFiles--helpmode = (defCommandMode $ ["help"] ++ aliases) {- modeHelp = "show any of the hledger manuals" `withAliases` aliases- ,modeGroupFlags = Group {- groupUnnamed = []- ,groupHidden = []- ,groupNamed = []- }- }- where aliases = []---- | Print detailed help on various topics.-help' :: CliOpts -> IO ()-help' opts = do- let args = listofstringopt "args" $ rawopts_ opts- case args of- [] -> putStrLn $- "Choose a topic, eg: hledger help cli\n" ++- intercalate ", " docTopics- topic:_ -> printHelpForTopic topic-
− Hledger/Cli/Histogram.hs
@@ -1,59 +0,0 @@-{-|--Print a histogram report. (The "activity" command).---}--module Hledger.Cli.Histogram-where--import Data.List-import Data.Maybe-import Data.Ord-import System.Console.CmdArgs.Explicit-import Text.Printf--import Hledger-import Hledger.Cli.CliOptions-import Prelude hiding (putStr)-import Hledger.Utils.UTF8IOCompat (putStr)--activitymode :: Mode RawOpts-activitymode = (defCommandMode $ ["activity"] ++ aliases) {- modeHelp = "show an ascii barchart of posting counts per interval (default: daily)" `withAliases` aliases- ,modeHelpSuffix = []- ,modeGroupFlags = Group {- groupUnnamed = []- ,groupHidden = []- ,groupNamed = [generalflagsgroup1]- }- }- where aliases = []--barchar :: Char-barchar = '*'---- | Print a histogram of some statistic per report interval, such as--- number of postings per day.-histogram :: CliOpts -> Journal -> IO ()-histogram CliOpts{reportopts_=ropts} j = do- d <- getCurrentDay- putStr $ showHistogram ropts (queryFromOpts d ropts) j--showHistogram :: ReportOpts -> Query -> Journal -> String-showHistogram opts q j = concatMap (printDayWith countBar) spanps- where- i = interval_ opts- interval | i == NoInterval = Days 1- | otherwise = i- span' = queryDateSpan (date2_ opts) q `spanDefaultsFrom` journalDateSpan (date2_ opts) j- spans = filter (DateSpan Nothing Nothing /=) $ splitSpan interval span'- spanps = [(s, filter (isPostingInDateSpan s) ps) | s <- spans]- -- same as Register- -- should count transactions, not postings ?- -- ps = sortBy (comparing postingDate) $ filterempties $ filter matchapats $ filterdepth $ journalPostings j- ps = sortBy (comparing postingDate) $ filter (q `matchesPosting`) $ journalPostings j--printDayWith f (DateSpan b _, ps) = printf "%s %s\n" (show $ fromJust b) (f ps)--countBar ps = replicate (length ps) barchar
− Hledger/Cli/Incomestatement.hs
@@ -1,47 +0,0 @@-{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, NoCPP #-}-{-|--The @incomestatement@ command prints a simple income statement (profit & loss report).---}--module Hledger.Cli.Incomestatement (- incomestatementmode- ,incomestatement- ,tests_Hledger_Cli_Incomestatement-) where--import Data.String.Here-import System.Console.CmdArgs.Explicit-import Test.HUnit--import Hledger-import Hledger.Cli.CliOptions-import Hledger.Cli.CompoundBalanceCommand--incomestatementSpec = CompoundBalanceCommandSpec {- cbcname = "incomestatement",- cbcaliases = ["is"],- cbchelp = [here|-This command displays a simple income statement, showing revenues-and expenses during a period. It assumes that these accounts are under a -top-level `revenue` or `income` or `expense` account (case insensitive,-plural forms also allowed).- |],- cbctitle = "Income Statement",- cbcqueries = [ ("Revenues", journalIncomeAccountQuery),- ("Expenses", journalExpenseAccountQuery)- ],- cbctype = PeriodChange-}--incomestatementmode :: Mode RawOpts-incomestatementmode = compoundBalanceCommandMode incomestatementSpec--incomestatement :: CliOpts -> Journal -> IO ()-incomestatement = compoundBalanceCommand incomestatementSpec--tests_Hledger_Cli_Incomestatement :: Test-tests_Hledger_Cli_Incomestatement = TestList- [- ]
− Hledger/Cli/Info.hs
@@ -1,41 +0,0 @@-{-|--The info command.--|-}--module Hledger.Cli.Info (-- infomode- ,info'-- ) where--import Prelude ()-import Prelude.Compat-import Data.List-import System.Console.CmdArgs.Explicit--import Hledger.Data.RawOptions-import Hledger.Cli.CliOptions-import Hledger.Cli.DocFiles--infomode = (defCommandMode $ ["info"] ++ aliases) {- modeHelp = "show any of the hledger manuals with info" `withAliases` aliases- ,modeGroupFlags = Group {- groupUnnamed = []- ,groupHidden = []- ,groupNamed = []- }- }- where aliases = []---- | Try to use info to view the selected manual.-info' :: CliOpts -> IO ()-info' opts = do- let args = listofstringopt "args" $ rawopts_ opts- case args of- [] -> putStrLn $- "Choose a topic, eg: hledger info cli\n" ++- intercalate ", " docTopics- topic:_ -> runInfoForTopic topic
Hledger/Cli/Main.hs view
@@ -40,11 +40,9 @@ module Hledger.Cli.Main where --- import Control.Monad import Data.Char (isDigit)-import Data.String.Here import Data.List-import Data.List.Split (splitOn)+import Data.String.Here import Safe import System.Console.CmdArgs.Explicit as C import System.Environment@@ -53,36 +51,14 @@ import System.Process import Text.Printf -import Hledger (ensureJournalFileExists)-import Hledger.Cli.Add-import Hledger.Cli.Accounts-import Hledger.Cli.Balance-import Hledger.Cli.Balancesheet-import Hledger.Cli.Cashflow-import Hledger.Cli.DocFiles-import Hledger.Cli.Help-import Hledger.Cli.Histogram-import Hledger.Cli.Incomestatement-import Hledger.Cli.Info-import Hledger.Cli.Man-import Hledger.Cli.Print-import Hledger.Cli.Register-import Hledger.Cli.Stats-import Hledger.Cli.CliOptions-import Hledger.Cli.Tests-import Hledger.Cli.Utils-import Hledger.Cli.Version-import Hledger.Data.Dates (getCurrentDay)-import Hledger.Data.RawOptions (RawOpts)-import Hledger.Reports.ReportOptions (period_, interval_, queryFromOpts)-import Hledger.Utils+import Hledger.Cli --- | The overall cmdargs mode describing command-line options for hledger.+-- | The overall cmdargs mode describing hledger's command-line options and subcommands. mainmode addons = defMode { modeNames = [progname ++ " [CMD]"] ,modeArgs = ([], Just $ argsFlag "[ARGS]")- ,modeHelp = unlines ["hledger's command line interface"]+ ,modeHelp = unlines ["hledger's main command line interface. Runs builtin commands and other hledger executables. Type \"hledger\" to list available commands."] ,modeGroupModes = Group { -- subcommands in the unnamed group, shown first: groupUnnamed = [@@ -91,23 +67,7 @@ ,groupNamed = [ ] -- subcommands handled but not shown in the help:- ,groupHidden = [- oldconvertmode- ,accountsmode- ,activitymode- ,addmode- ,balancemode- ,balancesheetmode- ,cashflowmode- ,helpmode- ,incomestatementmode- ,infomode- ,manmode- ,printmode- ,registermode- ,statsmode- ,testmode- ] ++ map quickAddonCommandMode addons+ ,groupHidden = map fst builtinCommands ++ map quickAddonCommandMode addons } ,modeGroupFlags = Group { -- flags in named groups:@@ -127,178 +87,13 @@ PROGNAME list commands PROGNAME CMD [--] [OPTS] [ARGS] run a command (use -- with addon commands) PROGNAME-CMD [OPTS] [ARGS] or run addon commands directly-PROGNAME -h general usage-PROGNAME CMD -h command usage-PROGNAME --help PROGNAME manual-PROGNAME --man PROGNAME manual as man page-PROGNAME --info PROGNAME manual as info manual-PROGNAME help list help topics-PROGNAME help TOPIC TOPIC manual-PROGNAME man TOPIC TOPIC manual as man page-PROGNAME info TOPIC TOPIC manual as info manual+PROGNAME -h show general usage+PROGNAME CMD -h show command usage+PROGNAME help [MANUAL] show any of the hledger manuals in various formats |] } -oldconvertmode = (defCommandMode ["convert"]) {- modeValue = [("command","convert")]- ,modeHelp = "convert is no longer needed, just use -f FILE.csv"- ,modeArgs = ([], Just $ argsFlag "[CSVFILE]")- ,modeGroupFlags = Group {- groupUnnamed = []- ,groupHidden = helpflags- ,groupNamed = []- }- }--builtinCommands :: [Mode RawOpts]-builtinCommands =- let gs = modeGroupModes $ mainmode []- in concatMap snd (groupNamed gs) ++ groupUnnamed gs ++ groupHidden gs--builtinCommandNames :: [String]-builtinCommandNames = concatMap modeNames builtinCommands---- | Parse hledger CLI options from these command line arguments and--- add-on command names, or raise any error.-argsToCliOpts :: [String] -> [String] -> IO CliOpts-argsToCliOpts args addons = do- let- args' = moveFlagsAfterCommand args- cmdargsopts = either usageError id $ process (mainmode addons) args'- cmdargsopts' = decodeRawOpts cmdargsopts- rawOptsToCliOpts cmdargsopts'---- | A hacky workaround for cmdargs not accepting flags before the--- subcommand name: try to detect and move such flags after the--- command. This allows the user to put them in either position.--- The order of options is not preserved, but this should be ok.------ Since we're not parsing flags as precisely as cmdargs here, this is--- imperfect. We make a decent effort to:--- - move all no-argument help/input/report flags--- - move all required-argument help/input/report flags along with their values, space-separated or not--- - not confuse things further or cause misleading errors.-moveFlagsAfterCommand :: [String] -> [String]-moveFlagsAfterCommand args = moveArgs $ ensureDebugHasArg args- where- -- quickly! make sure --debug has a numeric argument, or this all goes to hell- ensureDebugHasArg as =- case break (=="--debug") as of- (bs,"--debug":c:cs) | null c || not (all isDigit c) -> bs++"--debug=1":c:cs- (bs,"--debug":[]) -> bs++"--debug=1":[]- _ -> as-- -- -h ..., --version ...- moveArgs (f:a:as) | isMovableNoArgFlag f = (moveArgs $ a:as) ++ [f]- -- -f FILE ..., --alias ALIAS ...- moveArgs (f:v:a:as) | isMovableReqArgFlag f, isValue v = (moveArgs $ a:as) ++ [f,v]- -- -fFILE ..., --alias=ALIAS ...- moveArgs (fv:a:as) | isMovableReqArgFlagAndValue fv = (moveArgs $ a:as) ++ [fv]- -- -f(missing arg)- moveArgs (f:a:as) | isMovableReqArgFlag f, not (isValue a) = (moveArgs $ a:as) ++ [f]- -- anything else- moveArgs as = as--isMovableNoArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` noargflagstomove--isMovableReqArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` reqargflagstomove--isMovableReqArgFlagAndValue ('-':'-':a:as) = case break (== '=') (a:as) of (f:fs,_:_) -> (f:fs) `elem` reqargflagstomove- _ -> False-isMovableReqArgFlagAndValue ('-':shortflag:_:_) = [shortflag] `elem` reqargflagstomove-isMovableReqArgFlagAndValue _ = False--isValue "-" = True-isValue ('-':_) = False-isValue _ = True--flagstomove = inputflags ++ reportflags ++ helpflags-noargflagstomove = concatMap flagNames $ filter ((==FlagNone).flagInfo) flagstomove-reqargflagstomove = -- filter (/= "debug") $- concatMap flagNames $ filter ((==FlagReq ).flagInfo) flagstomove---- | Template for the commands list. Includes an entry for known (or--- hypothetical) builtin and addon commands; these will be filtered--- based on the commands found at runtime. COUNT is replaced with the--- number of commands found. OTHERCMDS is replaced with an entry for--- each unknown addon command found. The command descriptions here--- should be synced with the commands' builtin help and the command--- list in the hledger manual.-commandsListTemplate :: String-commandsListTemplate = [here|Commands available (COUNT):--Standard reports:- accounts show chart of accounts- balancesheet (bs) show a balance sheet- cashflow (cf) show a cashflow statement- incomestatement (is) show an income statement- transactions (txns) show transactions in some account--General reporting:- activity show a bar chart of posting counts per interval- balance (bal) show accounts and balances- budget add automated postings/txns/bucket accts (experimental)- chart generate simple balance pie charts (experimental)- check check more powerful balance assertions- check-dates check transactions are ordered by date- check-dupes check for accounts with the same leaf name- irr calculate internal rate of return of an investment- prices show market price records- print show transaction journal entries- print-unique show only transactions with unique descriptions- register (reg) show postings and running total- register-match show best matching transaction for a description- stats show some journal statistics--Interfaces:- add console ui for adding transactions- api web api server- iadd curses ui for adding transactions- ui curses ui- web web ui--Misc:- autosync download/deduplicate/convert OFX data- equity generate transactions to zero & restore account balances- interest generate interest transactions- rewrite add automated postings to certain transactions- test run some self tests-OTHERCMDS-Help: (see also -h, CMD -h, --help|--man|--info)- help|man|info show any of the hledger manuals in text/man/info format-|]--knownCommands :: [String]-knownCommands = sort $ commandsFromCommandsList commandsListTemplate---- | Extract the command names from a commands list like the above:--- the first word (or words separated by |) of lines beginning with a space.-commandsFromCommandsList :: String -> [String]-commandsFromCommandsList s = concatMap (splitOn "|") [w | ' ':l <- lines s, let w:_ = words l]---- | Print the commands list, modifying the template above based on--- the currently available addons. Missing addons will be removed, and--- extra addons will be added under Misc.-printCommandsList :: [String] -> IO ()-printCommandsList addonsFound = putStr commandsList- where- commandsFound = builtinCommandNames ++ addonsFound- unknownCommandsFound = addonsFound \\ knownCommands-- adjustline (' ':l) | not $ w `elem` commandsFound = []- where w = takeWhile (not . (`elem` "| ")) l- adjustline l = [l]-- commandsList1 =- regexReplace "OTHERCMDS" (unlines [' ':w | w <- unknownCommandsFound]) $- unlines $ concatMap adjustline $ lines commandsListTemplate-- commandsList =- regexReplace "COUNT" (show $ length $ commandsFromCommandsList commandsList1)- commandsList1----- | Let's go.+-- | Let's go! main :: IO () main = do @@ -310,9 +105,9 @@ -- command-line.test. -- some preliminary (imperfect) argument parsing to supplement cmdargs- args <- getArgs+ args <- getArgs >>= expandArgsAt let- args' = moveFlagsAfterCommand args+ args' = moveFlagsAfterCommand $ replaceNumericFlags args isFlag = ("-" `isPrefixOf`) isNonEmptyNonFlag s = not (isFlag s) && not (null s) rawcmd = headDef "" $ takeWhile isNonEmptyNonFlag args'@@ -346,18 +141,10 @@ hasDetailedVersion = ("--version+" `elem`) printUsage = putStr $ showModeUsage $ mainmode addons badCommandError = error' ("command "++rawcmd++" is not recognized, run with no command to see a list") >> exitFailure- hasShortHelpFlag args = any (`elem` args) ["-h"]- hasLongHelpFlag args = any (`elem` args) ["--help"]- hasManFlag args = any (`elem` args) ["--man"]- hasInfoFlag args = any (`elem` args) ["--info"]- hasSomeHelpFlag args = hasShortHelpFlag args || hasLongHelpFlag args || hasManFlag args || hasInfoFlag args+ hasHelpFlag args = any (`elem` args) ["-h","--help"] f `orShowHelp` mode- | hasShortHelpFlag args = putStr $ showModeUsage mode- | hasLongHelpFlag args = printHelpForTopic t- | hasManFlag args = runManForTopic t- | hasInfoFlag args = runInfoForTopic t- | otherwise = f- where t = topicForMode mode+ | hasHelpFlag args = putStr $ showModeUsage mode+ | otherwise = f dbgIO "processed opts" opts dbgIO "command matched" cmd dbgIO "isNullCommand" isNullCommand@@ -371,36 +158,27 @@ let runHledgerCommand -- high priority flags and situations. -h, then --help, then --info are highest priority.- | hasShortHelpFlag argsbeforecmd = dbgIO "" "-h before command, showing general usage" >> printUsage- | hasLongHelpFlag argsbeforecmd = dbgIO "" "--help before command, showing general manual" >> printHelpForTopic (topicForMode $ mainmode addons)- | hasManFlag argsbeforecmd = dbgIO "" "--man before command, showing general manual with man" >> runManForTopic (topicForMode $ mainmode addons)- | hasInfoFlag argsbeforecmd = dbgIO "" "--info before command, showing general manual with info" >> runInfoForTopic (topicForMode $ mainmode addons)- | not (hasSomeHelpFlag argsaftercmd) && (hasVersion argsbeforecmd || (hasVersion argsaftercmd && isInternalCommand))+ | hasHelpFlag argsbeforecmd = dbgIO "" "-h before command, showing general usage" >> printUsage+ | not (hasHelpFlag argsaftercmd) && (hasVersion argsbeforecmd || (hasVersion argsaftercmd && isInternalCommand)) = putStrLn prognameandversion- | not (hasSomeHelpFlag argsaftercmd) && (hasDetailedVersion argsbeforecmd || (hasDetailedVersion argsaftercmd && isInternalCommand))+ | not (hasHelpFlag argsaftercmd) && (hasDetailedVersion argsbeforecmd || (hasDetailedVersion argsaftercmd && isInternalCommand)) = putStrLn prognameanddetailedversion -- \| (null externalcmd) && "binary-filename" `inRawOpts` rawopts = putStrLn $ binaryfilename progname -- \| "--browse-args" `elem` args = System.Console.CmdArgs.Helper.execute "cmdargs-browser" mainmode' args >>= (putStr . show) | isNullCommand = dbgIO "" "no command, showing commands list" >> printCommandsList addons | isBadCommand = badCommandError - -- internal commands- | cmd == "activity" = withJournalDo opts histogram `orShowHelp` activitymode- | cmd == "add" = (journalFilePathFromOpts opts >>= (ensureJournalFileExists . head) >> withJournalDo opts add) `orShowHelp` addmode- | cmd == "accounts" = withJournalDo opts accounts `orShowHelp` accountsmode- | cmd == "balance" = withJournalDo opts balance `orShowHelp` balancemode- | cmd == "balancesheet" = withJournalDo opts balancesheet `orShowHelp` balancesheetmode- | cmd == "cashflow" = withJournalDo opts cashflow `orShowHelp` cashflowmode- | cmd == "incomestatement" = withJournalDo opts incomestatement `orShowHelp` incomestatementmode- | cmd == "print" = withJournalDo opts print' `orShowHelp` printmode- | cmd == "register" = withJournalDo opts register `orShowHelp` registermode- | cmd == "stats" = withJournalDo opts stats `orShowHelp` statsmode- | cmd == "test" = test' opts `orShowHelp` testmode- | cmd == "help" = help' opts `orShowHelp` helpmode- | cmd == "man" = man opts `orShowHelp` manmode- | cmd == "info" = info' opts `orShowHelp` infomode+ -- builtin commands+ | Just (cmdmode, cmdaction) <- findCommand cmd = do + if cmd=="add" -- add command does extra work before reading journal+ then (do+ journalFilePathFromOpts opts >>= (ensureJournalFileExists . head) + withJournalDo opts cmdaction)+ `orShowHelp` cmdmode+ else+ withJournalDo opts cmdaction `orShowHelp` cmdmode - -- an external command+ -- addon commands | isExternalCommand = do let externalargs = argsbeforecmd ++ filter (not.(=="--")) argsaftercmd let shellcmd = printf "%s-%s %s" progname cmd (unwords' externalargs) :: String@@ -410,20 +188,69 @@ system shellcmd >>= exitWith -- deprecated commands- | cmd == "convert" = error' (modeHelp oldconvertmode) >> exitFailure+ -- cmd == "convert" = error' (modeHelp oldconvertmode) >> exitFailure -- shouldn't reach here | otherwise = usageError ("could not understand the arguments "++show args) >> exitFailure runHledgerCommand +-- | Parse hledger CLI options from these command line arguments and+-- add-on command names, or raise any error.+argsToCliOpts :: [String] -> [String] -> IO CliOpts+argsToCliOpts args addons = do+ let+ args' = moveFlagsAfterCommand $ replaceNumericFlags args+ cmdargsopts = either usageError id $ process (mainmode addons) args'+ cmdargsopts' = decodeRawOpts cmdargsopts+ rawOptsToCliOpts cmdargsopts' --- tests_runHledgerCommand = [--- -- "runHledgerCommand" ~: do--- -- let opts = defreportopts{query_="expenses"}--- -- d <- getCurrentDay--- -- runHledgerCommand addons opts@CliOpts{command_=cmd} args+-- | A hacky workaround for cmdargs not accepting flags before the+-- subcommand name: try to detect and move such flags after the+-- command. This allows the user to put them in either position.+-- The order of options is not preserved, but this should be ok.+--+-- Since we're not parsing flags as precisely as cmdargs here, this is+-- imperfect. We make a decent effort to:+-- - move all no-argument help/input/report flags+-- - move all required-argument help/input/report flags along with their values, space-separated or not+-- - not confuse things further or cause misleading errors.+moveFlagsAfterCommand :: [String] -> [String]+moveFlagsAfterCommand args = moveArgs $ ensureDebugHasArg args+ where+ -- quickly! make sure --debug has a numeric argument, or this all goes to hell+ ensureDebugHasArg as =+ case break (=="--debug") as of+ (bs,"--debug":c:cs) | null c || not (all isDigit c) -> bs++"--debug=1":c:cs+ (bs,"--debug":[]) -> bs++"--debug=1":[]+ _ -> as --- ]+ -- -h ..., --version ...+ moveArgs (f:a:as) | isMovableNoArgFlag f = (moveArgs $ a:as) ++ [f]+ -- -f FILE ..., --alias ALIAS ...+ moveArgs (f:v:a:as) | isMovableReqArgFlag f, isValue v = (moveArgs $ a:as) ++ [f,v]+ -- -fFILE ..., --alias=ALIAS ...+ moveArgs (fv:a:as) | isMovableReqArgFlagAndValue fv = (moveArgs $ a:as) ++ [fv]+ -- -f(missing arg)+ moveArgs (f:a:as) | isMovableReqArgFlag f, not (isValue a) = (moveArgs $ a:as) ++ [f]+ -- anything else+ moveArgs as = as +isMovableNoArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` noargflagstomove++isMovableReqArgFlag a = "-" `isPrefixOf` a && dropWhile (=='-') a `elem` reqargflagstomove++isMovableReqArgFlagAndValue ('-':'-':a:as) = case break (== '=') (a:as) of (f:fs,_:_) -> (f:fs) `elem` reqargflagstomove+ _ -> False+isMovableReqArgFlagAndValue ('-':shortflag:_:_) = [shortflag] `elem` reqargflagstomove+isMovableReqArgFlagAndValue _ = False++isValue "-" = True+isValue ('-':_) = False+isValue _ = True++flagstomove = inputflags ++ reportflags ++ helpflags+noargflagstomove = concatMap flagNames $ filter ((==FlagNone).flagInfo) flagstomove+reqargflagstomove = -- filter (/= "debug") $+ concatMap flagNames $ filter ((==FlagReq ).flagInfo) flagstomove
− Hledger/Cli/Man.hs
@@ -1,41 +0,0 @@-{-|--The man command.--|-}--module Hledger.Cli.Man (-- manmode- ,man-- ) where--import Prelude ()-import Prelude.Compat-import Data.List-import System.Console.CmdArgs.Explicit--import Hledger.Data.RawOptions-import Hledger.Cli.CliOptions-import Hledger.Cli.DocFiles--manmode = (defCommandMode $ ["man"] ++ aliases) {- modeHelp = "show any of the hledger manuals with man" `withAliases` aliases- ,modeGroupFlags = Group {- groupUnnamed = []- ,groupHidden = []- ,groupNamed = []- }- }- where aliases = []---- | Try to use man to view the selected manual.-man :: CliOpts -> IO ()-man opts = do- let args = listofstringopt "args" $ rawopts_ opts- case args of- [] -> putStrLn $- "Choose a topic, eg: hledger man cli\n" ++- intercalate ", " docTopics- topic:_ -> runManForTopic topic
− Hledger/Cli/Print.hs
@@ -1,180 +0,0 @@-{-|--A ledger-compatible @print@ command.---}--{-# LANGUAGE OverloadedStrings #-}--module Hledger.Cli.Print (- printmode- ,print'- -- ,entriesReportAsText- ,originalTransaction- ,tests_Hledger_Cli_Print-)-where--import Data.Text (Text)-import qualified Data.Text as T-import System.Console.CmdArgs.Explicit-import Test.HUnit-import Text.CSV--import Hledger-import Hledger.Cli.CliOptions-import Hledger.Cli.Utils-import Hledger.Cli.Add ( transactionsSimilarTo )---printmode = (defCommandMode $ ["print"] ++ aliases) {- modeHelp = "show transaction journal entries" `withAliases` aliases- ,modeGroupFlags = Group {- groupUnnamed = [- let matcharg = "STR"- in- flagReq ["match","m"] (\s opts -> Right $ setopt "match" s opts) matcharg- ("show the transaction whose description is most similar to "++matcharg- ++ ", and is most recent"),- flagNone ["explicit","x"] (setboolopt "explicit")- "show all amounts explicitly"- ]- ++ outputflags- ,groupHidden = []- ,groupNamed = [generalflagsgroup1]- }- }- where aliases = []---- | Print journal transactions in standard format.-print' :: CliOpts -> Journal -> IO ()-print' opts j = do- case maybestringopt "match" $ rawopts_ opts of- Nothing -> printEntries opts j- Just desc -> printMatch opts j $ T.pack desc--printEntries :: CliOpts -> Journal -> IO ()-printEntries opts@CliOpts{reportopts_=ropts} j = do- d <- getCurrentDay- let q = queryFromOpts d ropts- fmt = outputFormatFromOpts opts- (render, ropts') = case fmt of- "csv" -> ((++"\n") . printCSV . entriesReportAsCsv, ropts{accountlistmode_=ALFlat})- _ -> (entriesReportAsText opts, ropts)- writeOutput opts $ render $ entriesReport ropts' q j--entriesReportAsText :: CliOpts -> EntriesReport -> String-entriesReportAsText opts = concatMap (showTransactionUnelided . gettxn) - where- gettxn | boolopt "explicit" $ rawopts_ opts = id -- use the fully inferred/explicit txn- | otherwise = originalTransaction -- use the original txn (more or less)---- Replace this transaction's postings with the original postings if any, but keep the--- current possibly rewritten account names.-originalTransaction t = t { tpostings = map originalPostingPreservingAccount $ tpostings t }---- Get the original posting if any, but keep the current possibly rewritten account name.-originalPostingPreservingAccount p = (originalPosting p) { paccount = paccount p }---- XXX--- tests_showTransactions = [--- "showTransactions" ~: do---- -- "print expenses" ~:--- do--- let opts = defreportopts{query_="expenses"}--- d <- getCurrentDay--- showTransactions opts (queryFromOpts d opts) samplejournal `is` unlines--- ["2008/06/03 * eat & shop"--- ," expenses:food $1"--- ," expenses:supplies $1"--- ," assets:cash $-2"--- ,""--- ]---- -- , "print report with depth arg" ~:--- do--- let opts = defreportopts{depth_=Just 2}--- d <- getCurrentDay--- showTransactions opts (queryFromOpts d opts) samplejournal `is` unlines--- ["2008/01/01 income"--- ," assets:bank:checking $1"--- ," income:salary $-1"--- ,""--- ,"2008/06/01 gift"--- ," assets:bank:checking $1"--- ," income:gifts $-1"--- ,""--- ,"2008/06/03 * eat & shop"--- ," expenses:food $1"--- ," expenses:supplies $1"--- ," assets:cash $-2"--- ,""--- ,"2008/12/31 * pay off"--- ," liabilities:debts $1"--- ," assets:bank:checking $-1"--- ,""--- ]--- ]--entriesReportAsCsv :: EntriesReport -> CSV-entriesReportAsCsv txns =- ["txnidx","date","date2","status","code","description","comment","account","amount","commodity","credit","debit","posting-status","posting-comment"] :- concatMap transactionToCSV txns---- | Generate one CSV record per posting, duplicating the common transaction fields.--- The txnidx field (transaction index) allows postings to be grouped back into transactions.-transactionToCSV :: Transaction -> CSV-transactionToCSV t =- map (\p -> show idx:date:date2:status:code:description:comment:p)- (concatMap postingToCSV $ tpostings t)- where- idx = tindex t- description = T.unpack $ tdescription t- date = showDate (tdate t)- date2 = maybe "" showDate (tdate2 t)- status = show $ tstatus t- code = T.unpack $ tcode t- comment = chomp $ strip $ T.unpack $ tcomment t--postingToCSV :: Posting -> CSV-postingToCSV p =- map (\(a@(Amount {aquantity=q,acommodity=c})) ->- let a_ = a{acommodity=""} in- let amount = showAmount a_ in- let commodity = T.unpack c in- let credit = if q < 0 then showAmount $ negate a_ else "" in- let debit = if q >= 0 then showAmount a_ else "" in- account:amount:commodity:credit:debit:status:comment:[])- amounts- where- Mixed amounts = pamount p- status = show $ pstatus p- account = showAccountName Nothing (ptype p) (paccount p)- comment = chomp $ strip $ T.unpack $ pcomment p---- --match---- | Print the transaction most closely and recently matching a description--- (and the query, if any).-printMatch :: CliOpts -> Journal -> Text -> IO ()-printMatch CliOpts{reportopts_=ropts} j desc = do- d <- getCurrentDay- let q = queryFromOpts d ropts- case similarTransaction' j q desc of- Nothing -> putStrLn "no matches found."- Just t -> putStr $ showTransactionUnelided t-- where- -- Identify the closest recent match for this description in past transactions.- similarTransaction' :: Journal -> Query -> Text -> Maybe Transaction- similarTransaction' j q desc- | null historymatches = Nothing- | otherwise = Just $ snd $ head historymatches- where- historymatches = transactionsSimilarTo j q desc---- tests--tests_Hledger_Cli_Print = TestList []- -- tests_showTransactions
− Hledger/Cli/Register.hs
@@ -1,204 +0,0 @@-{-|--A ledger-compatible @register@ command.---}--{-# LANGUAGE CPP, OverloadedStrings #-}--module Hledger.Cli.Register (- registermode- ,register- ,postingsReportAsText- ,postingsReportItemAsText- -- ,showPostingWithBalanceForVty- ,tests_Hledger_Cli_Register-) where--import Data.List-import Data.Maybe--- import Data.Text (Text)-import qualified Data.Text as T-import System.Console.CmdArgs.Explicit-import Text.CSV-import Test.HUnit--import Hledger-import Hledger.Cli.CliOptions-import Hledger.Cli.Utils---registermode = (defCommandMode $ ["register"] ++ aliases) {- modeHelp = "show postings and running total" `withAliases` aliases- ,modeGroupFlags = Group {- groupUnnamed = [- flagNone ["cumulative"] (\opts -> setboolopt "change" opts)- "show running total from report start date (default)"- ,flagNone ["historical","H"] (\opts -> setboolopt "historical" opts)- "show historical running total/balance (includes postings before report start date)\n "- ,flagNone ["average","A"] (\opts -> setboolopt "average" opts)- "show running average of posting amounts instead of total (implies --empty)"- ,flagNone ["related","r"] (\opts -> setboolopt "related" opts) "show postings' siblings instead"- ,flagReq ["width","w"] (\s opts -> Right $ setopt "width" s opts) "N"- ("set output width (default: " ++-#ifdef mingw32_HOST_OS- show defaultWidth-#else- "terminal width"-#endif- ++ " or $COLUMNS). -wN,M sets description width as well."- )- ]- ++ outputflags- ,groupHidden = []- ,groupNamed = [generalflagsgroup1]- }- }- where aliases = ["reg"]---- | Print a (posting) register report.-register :: CliOpts -> Journal -> IO ()-register opts@CliOpts{reportopts_=ropts} j = do- d <- getCurrentDay- let fmt = outputFormatFromOpts opts- render | fmt=="csv" = const ((++"\n") . printCSV . postingsReportAsCsv)- | otherwise = postingsReportAsText- writeOutput opts $ render opts $ postingsReport ropts (queryFromOpts d ropts) j--postingsReportAsCsv :: PostingsReport -> CSV-postingsReportAsCsv (_,is) =- ["txnidx","date","description","account","amount","total"]- :- map postingsReportItemAsCsvRecord is--postingsReportItemAsCsvRecord :: PostingsReportItem -> Record-postingsReportItemAsCsvRecord (_, _, _, p, b) = [idx,date,desc,acct,amt,bal]- where- idx = show $ maybe 0 tindex $ ptransaction p- date = showDate $ postingDate p -- XXX csv should show date2 with --date2- desc = T.unpack $ maybe "" tdescription $ ptransaction p- acct = bracket $ T.unpack $ paccount p- where- bracket = case ptype p of- BalancedVirtualPosting -> (\s -> "["++s++"]")- VirtualPosting -> (\s -> "("++s++")")- _ -> id- amt = showMixedAmountOneLineWithoutPrice $ pamount p- bal = showMixedAmountOneLineWithoutPrice b---- | Render a register report as plain text suitable for console output.-postingsReportAsText :: CliOpts -> PostingsReport -> String-postingsReportAsText opts (_,items) = unlines $ map (postingsReportItemAsText opts amtwidth balwidth) items- where- amtwidth = maximumStrict $ 12 : map (strWidth . showMixedAmount . itemamt) items- balwidth = maximumStrict $ 12 : map (strWidth . showMixedAmount . itembal) items- itemamt (_,_,_,Posting{pamount=a},_) = a- itembal (_,_,_,_,a) = a--tests_postingsReportAsText = [- "postingsReportAsText" ~: do- -- "unicode in register layout" ~: do- j <- readJournal'- "2009/01/01 * медвежья шкура\n расходы:покупки 100\n актив:наличные\n"- let opts = defreportopts- (postingsReportAsText defcliopts $ postingsReport opts (queryFromOpts (parsedate "2008/11/26") opts) j) `is` unlines- ["2009/01/01 медвежья шкура расходы:покупки 100 100"- ," актив:наличные -100 0"]- ]---- | Render one register report line item as plain text. Layout is like so:--- @--- <---------------- width (specified, terminal width, or 80) -------------------->--- date (10) description account amount (12) balance (12)--- DDDDDDDDDD dddddddddddddddddddd aaaaaaaaaaaaaaaaaaa AAAAAAAAAAAA AAAAAAAAAAAA--- @--- If description's width is specified, account will use the remaining space.--- Otherwise, description and account divide up the space equally.------ With a report interval, the layout is like so:--- @--- <---------------- width (specified, terminal width, or 80) -------------------->--- date (21) account amount (12) balance (12)--- DDDDDDDDDDDDDDDDDDDDD aaaaaaaaaaaaaaaaaaaaaaaaaaaaa AAAAAAAAAAAA AAAAAAAAAAAA--- @------ date and description are shown for the first posting of a transaction only.------ Returns a string which can be multi-line, eg if the running balance--- has multiple commodities. Does not yet support formatting control--- like balance reports.----postingsReportItemAsText :: CliOpts -> Int -> Int -> PostingsReportItem -> String-postingsReportItemAsText opts preferredamtwidth preferredbalwidth (mdate, menddate, mdesc, p, b) =- -- use elide*Width to be wide-char-aware- -- trace (show (totalwidth, datewidth, descwidth, acctwidth, amtwidth, balwidth)) $- intercalate "\n" $- [concat [fitString (Just datewidth) (Just datewidth) True True date- ," "- ,fitString (Just descwidth) (Just descwidth) True True desc- ," "- ,fitString (Just acctwidth) (Just acctwidth) True True acct- ," "- ,fitString (Just amtwidth) (Just amtwidth) True False amtfirstline- ," "- ,fitString (Just balwidth) (Just balwidth) True False balfirstline- ]]- ++- [concat [spacer- ,fitString (Just amtwidth) (Just amtwidth) True False a- ," "- ,fitString (Just balwidth) (Just balwidth) True False b- ]- | (a,b) <- zip amtrest balrest- ]- where- -- calculate widths- (totalwidth,mdescwidth) = registerWidthsFromOpts opts- (datewidth, date) = case (mdate,menddate) of- (Just _, Just _) -> (21, showDateSpan (DateSpan mdate menddate))- (Nothing, Just _) -> (21, "")- (Just d, Nothing) -> (10, showDate d)- _ -> (10, "")- (amtwidth, balwidth)- | shortfall <= 0 = (preferredamtwidth, preferredbalwidth)- | otherwise = (adjustedamtwidth, adjustedbalwidth)- where- mincolwidth = 2 -- columns always show at least an ellipsis- maxamtswidth = max 0 (totalwidth - (datewidth + 1 + mincolwidth + 2 + mincolwidth + 2 + 2))- shortfall = (preferredamtwidth + preferredbalwidth) - maxamtswidth- amtwidthproportion = fromIntegral preferredamtwidth / fromIntegral (preferredamtwidth + preferredbalwidth)- adjustedamtwidth = round $ amtwidthproportion * fromIntegral maxamtswidth- adjustedbalwidth = maxamtswidth - adjustedamtwidth-- remaining = totalwidth - (datewidth + 1 + 2 + amtwidth + 2 + balwidth)- (descwidth, acctwidth)- | hasinterval = (0, remaining - 2)- | otherwise = (w, remaining - 2 - w)- where- hasinterval = isJust menddate- w = fromMaybe ((remaining - 2) `div` 2) mdescwidth-- -- gather content- desc = fromMaybe "" mdesc- acct = parenthesise $ T.unpack $ elideAccountName awidth $ paccount p- where- (parenthesise, awidth) =- case ptype p of- BalancedVirtualPosting -> (\s -> "["++s++"]", acctwidth-2)- VirtualPosting -> (\s -> "("++s++")", acctwidth-2)- _ -> (id,acctwidth)- amt = showMixedAmountWithoutPrice $ pamount p- bal = showMixedAmountWithoutPrice b- -- alternate behaviour, show null amounts as 0 instead of blank- -- amt = if null amt' then "0" else amt'- -- bal = if null bal' then "0" else bal'- (amtlines, ballines) = (lines amt, lines bal)- (amtlen, ballen) = (length amtlines, length ballines)- numlines = max 1 (max amtlen ballen)- (amtfirstline:amtrest) = take numlines $ amtlines ++ repeat "" -- posting amount is top-aligned- (balfirstline:balrest) = take numlines $ replicate (numlines - ballen) "" ++ ballines -- balance amount is bottom-aligned- spacer = replicate (totalwidth - (amtwidth + 2 + balwidth)) ' '--tests_Hledger_Cli_Register :: Test-tests_Hledger_Cli_Register = TestList- tests_postingsReportAsText
− Hledger/Cli/Stats.hs
@@ -1,112 +0,0 @@-{-|--Print some statistics for the journal.---}--{-# LANGUAGE OverloadedStrings #-}--module Hledger.Cli.Stats (- statsmode- ,stats-)-where--import Data.List-import Data.Maybe-import Data.Ord-import Data.HashSet (size, fromList)--- import Data.Text (Text)-import qualified Data.Text as T-import Data.Time.Calendar-import System.Console.CmdArgs.Explicit-import Text.Printf-import qualified Data.Map as Map--import Hledger-import Hledger.Cli.CliOptions-import Prelude hiding (putStr)-import Hledger.Cli.Utils (writeOutput)---statsmode = (defCommandMode $ ["stats"] ++ aliases) {- modeHelp = "show some journal statistics" `withAliases` aliases- ,modeGroupFlags = Group {- groupUnnamed = [- flagReq ["output-file","o"] (\s opts -> Right $ setopt "output-file" s opts) "FILE" "write output to FILE. A file extension matching one of the above formats selects that format."- ]- ,groupHidden = []- ,groupNamed = [generalflagsgroup1]- }- }- where aliases = []---- like Register.summarisePostings--- | Print various statistics for the journal.-stats :: CliOpts -> Journal -> IO ()-stats opts@CliOpts{reportopts_=reportopts_} j = do- d <- getCurrentDay- let q = queryFromOpts d reportopts_- l = ledgerFromJournal q j- reportspan = (ledgerDateSpan l) `spanDefaultsFrom` (queryDateSpan False q)- intervalspans = splitSpan (interval_ reportopts_) reportspan- showstats = showLedgerStats l d- s = intercalate "\n" $ map showstats intervalspans- writeOutput opts s--showLedgerStats :: Ledger -> Day -> DateSpan -> String-showLedgerStats l today span =- unlines $ map (\(label,value) -> concatBottomPadded [printf fmt1 label, value]) stats- where- fmt1 = "%-" ++ show w1 ++ "s: "- -- fmt2 = "%-" ++ show w2 ++ "s"- w1 = maximum $ map (length . fst) stats- -- w2 = maximum $ map (length . show . snd) stats- stats = [- ("Main file" :: String, path) -- ++ " (from " ++ source ++ ")")- ,("Included files", unlines $ drop 1 $ journalFilePaths j)- ,("Transactions span", printf "%s to %s (%d days)" (start span) (end span) days)- ,("Last transaction", maybe "none" show lastdate ++ showelapsed lastelapsed)- ,("Transactions", printf "%d (%0.1f per day)" tnum txnrate)- ,("Transactions last 30 days", printf "%d (%0.1f per day)" tnum30 txnrate30)- ,("Transactions last 7 days", printf "%d (%0.1f per day)" tnum7 txnrate7)- ,("Payees/descriptions", show $ size $ fromList $ map (tdescription) ts)- ,("Accounts", printf "%d (depth %d)" acctnum acctdepth)- ,("Commodities", printf "%s (%s)" (show $ length cs) (T.intercalate ", " cs))- -- Transactions this month : %(monthtxns)s (last month in the same period: %(lastmonthtxns)s)- -- Unmarked transactions : %(unmarked)s- -- Days since reconciliation : %(reconcileelapsed)s- -- Days since last transaction : %(recentelapsed)s- ]- where- j = ljournal l- path = journalFilePath j- ts = sortBy (comparing tdate) $ filter (spanContainsDate span . tdate) $ jtxns j- as = nub $ map paccount $ concatMap tpostings ts- cs = Map.keys $ commodityStylesFromAmounts $ concatMap amounts $ map pamount $ concatMap tpostings ts- lastdate | null ts = Nothing- | otherwise = Just $ tdate $ last ts- lastelapsed = maybe Nothing (Just . diffDays today) lastdate- showelapsed Nothing = ""- showelapsed (Just days) = printf " (%d %s)" days' direction- where days' = abs days- direction | days >= 0 = "days ago" :: String- | otherwise = "days from now"- tnum = length ts- start (DateSpan (Just d) _) = show d- start _ = ""- end (DateSpan _ (Just d)) = show d- end _ = ""- days = fromMaybe 0 $ daysInSpan span- txnrate | days==0 = 0- | otherwise = fromIntegral tnum / fromIntegral days :: Double- tnum30 = length $ filter withinlast30 ts- withinlast30 t = d >= addDays (-30) today && (d<=today) where d = tdate t- txnrate30 = fromIntegral tnum30 / 30 :: Double- tnum7 = length $ filter withinlast7 ts- withinlast7 t = d >= addDays (-7) today && (d<=today) where d = tdate t- txnrate7 = fromIntegral tnum7 / 7 :: Double- acctnum = length as- acctdepth | null as = 0- | otherwise = maximum $ map accountNameLevel as-
− Hledger/Cli/Tests.hs
@@ -1,71 +0,0 @@--- {-# OPTIONS_GHC -F -pgmF htfpp #-}-{-# LANGUAGE CPP #-}-{- |--A simple test runner for hledger's built-in unit tests.---}--module Hledger.Cli.Tests (- testmode- ,test'-)-where--import Control.Monad--- import Data.Text (Text)-import qualified Data.Text as T-import System.Exit-import Test.HUnit--import Hledger-import Hledger.Cli--#ifdef TESTS--import Test.Framework-import {-@ HTF_TESTS @-} Hledger.Read.JournalReader---- | Run HTF unit tests and exit with success or failure.-test' :: CliOpts -> IO ()-test' _opts = htfMain htf_importedTests--#else---- | Run HUnit unit tests and exit with success or failure.-test' :: CliOpts -> IO ()-test' opts = do- results <- runTests opts- if errors results > 0 || failures results > 0- then exitFailure- else exitWith ExitSuccess--testmode = (defCommandMode ["test"]) {- modeHelp = "run built-in self-tests"- ,modeArgs = ([], Just $ argsFlag "[REGEXPS]")- ,modeGroupFlags = Group {- groupUnnamed = []- ,groupHidden = []- ,groupNamed = [generalflagsgroup3]- }- }---- | Run all or just the matched unit tests and return their HUnit result counts.-runTests :: CliOpts -> IO Counts-runTests = liftM (fst . flip (,) 0) . runTestTT . flatTests---- -- | Run all or just the matched unit tests until the first failure or--- -- error, returning the name of the problem test if any.--- runTestsTillFailure :: CliOpts -> IO (Maybe String)--- runTestsTillFailure _ = undefined -- do--- -- let ts = flatTests opts--- -- results = liftM (fst . flip (,) 0) $ runTestTT $--- -- firstproblem = find (\counts -> )---- | All or pattern-matched tests, as a flat list to show simple names.-flatTests opts = TestList $ filter (matchesAccount (queryFromOpts nulldate $ reportopts_ opts) . T.pack . testName) $ flattenTests tests_Hledger_Cli---- -- | All or pattern-matched tests, in the original suites to show hierarchical names.--- hierarchicalTests opts = filterTests (matchesAccount (queryFromOpts nulldate $ reportopts_ opts) . testName) tests_Hledger_Cli--#endif
Hledger/Cli/Utils.hs view
@@ -19,6 +19,8 @@ writeFileWithBackup, writeFileWithBackupIfChanged, readFileStrictly,+ pivotByOpts,+ anonymiseByOpts, Test(TestList), ) where@@ -27,7 +29,6 @@ import Data.Hashable (hash) import Data.List import Data.Maybe-import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.IO as T import Data.Time (Day)@@ -45,21 +46,8 @@ import Text.Printf import Text.Regex.TDFA ((=~)) ---- kludge - adapt to whichever directory version is installed, or when--- cabal macros aren't available, assume the new directory-#ifdef MIN_VERSION_directory-#if MIN_VERSION_directory(1,2,0)-#define directory_1_2-#endif-#else-#define directory_1_2-#endif--#ifdef directory_1_2 import System.Time (ClockTime(TOD)) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)-#endif import Hledger.Cli.CliOptions import Hledger.Data@@ -75,9 +63,8 @@ -- We kludgily read the file before parsing to grab the full text, unless -- it's stdin, or it doesn't exist and we are adding. We read it strictly -- to let the add command work.- rulespath <- rulesFilePathFromOpts opts journalpaths <- journalFilePathFromOpts opts- ej <- readJournalFiles Nothing rulespath (not $ ignore_assertions_ opts) journalpaths+ ej <- readJournalFilesWithOpts (inputopts_ opts) journalpaths let f = cmd opts . pivotByOpts opts . anonymiseByOpts opts@@ -89,19 +76,9 @@ pivotByOpts :: CliOpts -> Journal -> Journal pivotByOpts opts = case maybestringopt "pivot" . rawopts_ $ opts of- Just tag -> pivot $ T.pack tag+ Just tag -> journalPivot $ T.pack tag Nothing -> id --- | Apply the pivot transformation by given tag on a journal.-pivot :: Text -> Journal -> Journal-pivot tag j = j{jtxns = map pivotTrans . jtxns $ j}- where- pivotTrans t = t{tpostings = map pivotPosting . tpostings $ t}- pivotPosting p- | Just (_ , value) <- tagTuple = p{paccount = value, porigin = Just $ originalPosting p}- | _ <- tagTuple = p{paccount = T.pack "", porigin = Just $ originalPosting p}- where tagTuple = find ((tag ==) . fst) . postingAllImplicitTags $ p- -- | Apply the anonymisation transformation on a journal, if option is present anonymiseByOpts :: CliOpts -> Journal -> Journal anonymiseByOpts opts =@@ -126,6 +103,10 @@ where anon = T.pack . flip showHex "" . (fromIntegral :: Int -> Word32) . hash +-- XXX as of since 2017/4 this is used instead of +-- balanceReportValue/multiBalanceReportValue (mostly; not yet hledger-ui)+-- | If -V/--value was requested, convert all journal amounts to their market value+-- as of the report end date. Cf http://hledger.org/manual.html#market-value journalApplyValue :: ReportOpts -> Journal -> IO Journal journalApplyValue ropts j = do mvaluedate <- reportEndDate j ropts@@ -137,6 +118,7 @@ return $ convert j -- | Write some output to stdout or to a file selected by --output-file.+-- If the file exists it will be overwritten. writeOutput :: CliOpts -> String -> IO () writeOutput opts s = do f <- outputFileFromOpts opts@@ -151,10 +133,9 @@ -- Reads the full journal, without filtering. journalReload :: CliOpts -> IO (Either String Journal) journalReload opts = do- rulespath <- rulesFilePathFromOpts opts journalpaths <- journalFilePathFromOpts opts ((pivotByOpts opts . journalApplyAliases (aliasesFromOpts opts)) <$>) <$>- readJournalFiles Nothing rulespath (not $ ignore_assertions_ opts) journalpaths+ readJournalFilesWithOpts (inputopts_ opts) journalpaths -- | Re-read the option-specified journal file(s), but only if any of -- them has changed since last read. (If the file is standard input,@@ -195,13 +176,9 @@ fileModificationTime f | null f = getClockTime | otherwise = (do-#ifdef directory_1_2 utc <- getModificationTime f let nom = utcTimeToPOSIXSeconds utc let clo = TOD (read $ takeWhile (`elem` "0123456789") $ show nom) 0 -- XXX read-#else- clo <- getModificationTime f-#endif return clo ) `C.catch` \(_::C.IOException) -> getClockTime
README.md view
@@ -115,7 +115,8 @@ --> <!-- []() --> -[](https://travis-ci.org/simonmichael/hledger)+[](https://travis.hledger.org)+[](https://appveyor.hledger.org) [](http://packdeps.haskellers.com/feed?needle=hledger-lib) [](http://packdeps.haskellers.com/feed?needle=hledger) [](http://packdeps.haskellers.com/feed?needle=hledger-ui)
Text/Tabular/AsciiWide.hs view
@@ -70,7 +70,7 @@ coreLine = concatMap helper $ flattenHeader $ zipHeader 0 is h helper = either hsep (uncurry padLeftWide) hsep :: Properties -> String- hsep NoLine = " "+ hsep NoLine = " " hsep SingleLine = midBar pretty hsep DoubleLine = doubleMidBar pretty @@ -98,7 +98,7 @@ coreLine = concatMap helper $ flattenHeader $ zipHeader 0 is h helper = either vsep dashes dashes (i,_) = replicate i sep- vsep NoLine = [sep]+ vsep NoLine = replicate 2 sep -- match the double space sep in renderColumns vsep SingleLine = sep : cross pretty : [sep] vsep DoubleLine = sep : cross' ++ [sep] cross' = case prop of
doc/hledger.1 view
@@ -1,6 +1,6 @@ .\"t -.TH "hledger" "1" "August 2017" "hledger 1.3.1" "hledger User Manuals"+.TH "hledger" "1" "September 2017" "hledger 1.4" "hledger User Manuals" @@ -156,31 +156,14 @@ .PP To see general usage help, including general options which are supported by most hledger commands, run \f[C]hledger\ \-h\f[].-(Note \-h and \-\-help are different, like git.) .PP General help options: .TP-.B \f[C]\-h\f[]+.B \f[C]\-h\ \-\-help\f[] show general usage (or after COMMAND, command usage) .RS .RE .TP-.B \f[C]\-\-help\f[]-show this program\[aq]s manual as plain text (or after an add\-on-COMMAND, the add\-on\[aq]s manual)-.RS-.RE-.TP-.B \f[C]\-\-man\f[]-show this program\[aq]s manual with man-.RS-.RE-.TP-.B \f[C]\-\-info\f[]-show this program\[aq]s manual with info-.RS-.RE-.TP .B \f[C]\-\-version\f[] show version .RS@@ -215,8 +198,8 @@ .RS .RE .TP-.B \f[C]\-\-pivot\ TAGNAME\f[]-use some other field/tag for account names+.B \f[C]\-\-pivot\ FIELDNAME\f[]+use some other field or tag for the account name .RS .RE .TP@@ -269,7 +252,7 @@ .RE .TP .B \f[C]\-\-date2\f[]-show, and match with \-b/\-e/\-p/date:, secondary dates instead+match the secondary date instead (see command help for other effects) .RS .RE .TP@@ -293,8 +276,8 @@ .RS .RE .TP-.B \f[C]\-\-depth=N\f[]-hide accounts/postings deeper than N+.B \f[C]\-NUM\ \-\-depth=NUM\f[]+hide/aggregate accounts or postings more than NUM levels deep .RS .RE .TP@@ -315,11 +298,10 @@ .RS .RE .PP-Note when multiple similar reporting options are provided, the last one-takes precedence.-Eg \f[C]\-p\ feb\ \-p\ mar\f[] is equivalent to \f[C]\-p\ mar\f[].+When a reporting option appears more than once in the command line, the+last one takes precedence. .PP-Some of these can also be written as queries.+Some reporting options can also be written as query arguments. .SS Command options .PP To see options for a particular command, including command\-specific@@ -337,6 +319,12 @@ .PP Most hledger commands accept arguments after the command name, which are often a query, filtering the data in some way.+.SS Argument expansion+.PP+You can save a set of command line options/arguments in a file, one per+line, and then reuse them by writing \f[C]\@FILE\f[] in a command line.+(To prevent this expansion of \f[C]\@\f[]\-arguments, precede them with+a \f[C]\-\-\f[] argument.) .SS Special characters .PP Option and argument values which contain problematic characters should@@ -353,13 +341,19 @@ \f[C]hledger\ balance\ cur:\[aq]\\$\[aq]\f[] or \f[C]hledger\ balance\ cur:\\\\$\f[]. .PP-There\[aq]s more..-options and arguments get de\-escaped when hledger is passing them to an-addon executable.-In this case you might need \f[I]triple\f[]\-escaping.+When hledger is invoking an addon executable (like hledger\-ui), options+and arguments get de\-escaped once more, so you might need+\f[I]triple\f[]\-escaping. Eg: \f[C]hledger\ ui\ cur:\[aq]\\\\$\[aq]\f[] or-\f[C]hledger\ ui\ cur:\\\\\\\\$\f[].+\f[C]hledger\ ui\ cur:\\\\\\\\$\f[] in bash.+(The number of backslashes in fish shell is left as an exercise for the+reader.) .PP+Inside a file used for argument expansion, one less level of escaping is+enough.+(And in this case, backslashes seem to work better than quotes.+Eg: \f[C]cur:\\$\f[]).+.PP If in doubt, keep things simple: .IP \[bu] 2 run add\-on executables directly@@ -758,29 +752,28 @@ \f[C]hledger\ register\ checking\ \-p\ "every\ 3rd\ day\ of\ week"\f[] .SS Depth limiting .PP-With the \f[C]\-\-depth\ N\f[] option, commands like account, balance-and register will show only the uppermost accounts in the account tree,-down to level N.+With the \f[C]\-\-depth\ N\f[] option (short form: \f[C]\-N\f[]),+commands like account, balance and register will show only the uppermost+accounts in the account tree, down to level N. Use this when you want a summary with less detail.+This flag has the same effect as a \f[C]depth:\f[] query argument (so+\f[C]\-2\f[], \f[C]\-\-depth=2\f[] or \f[C]depth:2\f[] are basically+equivalent). .SS Pivoting .PP Normally hledger sums amounts, and organizes them in a hierarchy, based on account name.-The \f[C]\-\-pivot\ TAGNAME\f[] option causes it to sum and organize-hierarchy based on some other field instead.-.PP-TAGNAME is the full, case\-insensitive name of a tag you have defined,-or one of the built\-in implicit tags (like \f[C]code\f[] or-\f[C]payee\f[]).-As with account names, when tag values have-\f[C]multiple:colon\-separated:parts\f[] hledger will build hierarchy,-displayed in tree\-mode reports, summarisable with a depth limit, and so-on.+The \f[C]\-\-pivot\ FIELD\f[] option causes it to sum and organize+hierarchy based on the value of some other field instead.+FIELD can be: \f[C]code\f[], \f[C]description\f[], \f[C]payee\f[],+\f[C]note\f[], or the full name (case insensitive) of any tag.+As with account names, values containing \f[C]colon:separated:parts\f[]+will be displayed hierarchically in reports. .PP \f[C]\-\-pivot\f[] is a general option affecting all reports; you can think of hledger transforming the journal before any other processing, replacing every posting\[aq]s account name with the value of the-specified tag on that posting, inheriting it from the transaction or+specified field on that posting, inheriting it from the transaction or using a blank value if it\[aq]s not present. .PP An example:@@ -957,12 +950,12 @@ after the command name, to filter the data by date, account name or other criteria. The syntax is similar to a web search: one or more space\-separated-search terms, quotes to enclose whitespace, optional prefixes to match-specific fields.-Multiple search terms are combined as follows:+search terms, quotes to enclose whitespace, prefixes to match specific+fields, a not: prefix to negate the match. .PP-All commands except print: show transactions/postings/accounts which-match (or negatively match)+We do not yet support arbitrary boolean combinations of search terms;+instead most commands show transactions/postings/accounts which match+(or negatively match): .IP \[bu] 2 any of the description terms AND .IP \[bu] 2@@ -972,7 +965,7 @@ .IP \[bu] 2 all the other terms. .PP-The print command: show transactions which+The print command instead shows transactions which: .IP \[bu] 2 match any of the description terms AND .IP \[bu] 2@@ -982,10 +975,13 @@ .IP \[bu] 2 match all the other terms. .PP-The following kinds of search terms can be used:+The following kinds of search terms can be used.+Remember these can also be prefixed with \f[B]\f[C]not:\f[]\f[], eg to+exclude a particular subaccount. .TP .B \f[B]\f[C]REGEX\f[]\f[]-match account names by this regular expression+match account names by this regular expression.+(No prefix is equivalent to \f[C]acct:\f[]). .RS .RE .TP@@ -1023,7 +1019,7 @@ .RE .TP .B \f[B]\f[C]desc:REGEX\f[]\f[]-match transaction descriptions+match transaction descriptions. .RS .RE .TP@@ -1047,6 +1043,18 @@ .RS .RE .TP+.B \f[B]\f[C]note:REGEX\f[]\f[]+match transaction notes (part of description right of \f[C]|\f[], or+whole description when there\[aq]s no \f[C]|\f[])+.RS+.RE+.TP+.B \f[B]\f[C]payee:REGEX\f[]\f[]+match transaction payee/payer names (part of description left of+\f[C]|\f[], or whole description when there\[aq]s no \f[C]|\f[])+.RS+.RE+.TP .B \f[B]\f[C]real:,\ real:0\f[]\f[] match real or virtual postings respectively .RS@@ -1065,18 +1073,13 @@ transaction. .RS .RE-.TP-.B \f[B]\f[C]not:\f[]\f[]-before any of the above negates the match.-.RS-.RE+.PP+The following special search term is used automatically in hledger\-web,+only: .TP .B \f[B]\f[C]inacct:ACCTNAME\f[]\f[]-a special term used automatically when you click an account name in-hledger\-web, specifying the account register we are currently in-(selects the transactions of that account and how to show them, can be-filtered further with \f[C]acct\f[] etc).-Not supported elsewhere in hledger.+tells hledger\-web to show the transaction register for this account.+Can be filtered further with \f[C]acct\f[] etc. .RS .RE .PP@@ -1096,12 +1099,17 @@ .PP Run a subcommand by writing its name as first argument (eg \f[C]hledger\ incomestatement\f[]).-You can also write any unambiguous prefix of a command name-(\f[C]hledger\ inc\f[]), or one of the standard short aliases displayed-in the command list (\f[C]hledger\ is\f[]).+You can also write one of the standard short aliases displayed in+parentheses in the command list (\f[C]hledger\ b\f[]), or any any+unambiguous prefix of a command name (\f[C]hledger\ inc\f[]).+.PP+Here are all the builtin commands in alphabetical order.+See also \f[C]hledger\f[] for a more organised command list, and+\f[C]hledger\ CMD\ \-h\f[] for detailed command help. .SS accounts .PP Show account names.+Alias: a. .TP .B \f[C]\-\-tree\f[] show short account names, as a tree@@ -1278,7 +1286,7 @@ .SS balance .PP Show accounts and their balances.-Alias: bal.+Aliases: b, bal. .TP .B \f[C]\-\-change\f[] show balance change in each period (default)@@ -1354,6 +1362,12 @@ Use unicode to display prettier tables. .RS .RE+.TP+.B \f[C]\-\-sort\-amount\f[]+Sort by amount (total row amount, or by average if that is displayed),+instead of account name (in flat mode)+.RS+.RE .PP The balance command displays accounts and balances. It is hledger\[aq]s most featureful and versatile command.@@ -1732,6 +1746,11 @@ in single\-column balance reports: use this custom line format .RS .RE+.TP+.B \f[C]\-\-sort\-amount\f[]+sort by amount instead of account name+.RS+.RE .PP This command displays a simple balance sheet. It currently assumes that you have top\-level accounts named@@ -1767,6 +1786,47 @@ Normally balancesheet shows historical ending balances, which is what you need for a balance sheet; note this means it ignores report begin dates.+.SS balancesheetequity+.PP+Show a balance sheet including equity.+Alias: bse.+.PP+Other than showing the equity accounts, this command is exactly the same+as the command balancesheet.+Please refer to it for the available options.+.PP+This command displays a balancesheet.+It currently assumes that you have top\-level accounts named+\f[C]asset\f[], \f[C]liability\f[] and \f[C]equity\f[] (plural forms+also allowed.)+.IP+.nf+\f[C]+$\ hledger\ balancesheetequity+Balance\ Sheet\ With\ Equity++Assets:+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $\-2\ \ assets+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $1\ \ \ \ bank:saving+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $\-3\ \ \ \ cash+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $\-2++Liabilities:+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $1\ \ liabilities:debts+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ $1++Equity:+\ \ \ \ \ \ \ \ \ \ $1\ \ equity:owner+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\ \ \ \ \ \ \ \ \ \ $1++Total:+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 0+\f[]+.fi .SS cashflow .PP Show a cashflow statement.@@ -1830,12 +1890,17 @@ in single\-column balance reports: use this custom line format .RS .RE+.TP+.B \f[C]\-\-sort\-amount\f[]+sort by amount instead of account name+.RS+.RE .PP This command displays a simple cashflow statement It shows the change in all "cash" (ie, liquid assets) accounts for the period. It currently assumes that cash accounts are under a top\-level account-named \f[C]asset\f[] and do not contain \f[C]receivable\f[] or-\f[C]A/R\f[] (plural forms also allowed.)+named \f[C]asset\f[] and do not contain \f[C]receivable\f[],+\f[C]:A/R\f[] or \f[C]:fixed\f[]. .IP .nf \f[C]@@ -1860,44 +1925,77 @@ Normally cashflow shows changes in assets per period, though as with multicolumn balance reports you can alter the report mode with \f[C]\-\-change\f[]/\f[C]\-\-cumulative\f[]/\f[C]\-\-historical\f[].+.SS check\-dates+.PP+Check that transactions are sorted by increasing date.+With a query, only matched transactions\[aq] dates are checked.+.SS check\-dupes+.PP+Report account names having the same leaf but different prefixes.+An example: http://stefanorodighiero.net/software/hledger\-dupes.html+.SS equity+.PP+Print closing/opening transactions that bring some or all account+balances to zero and back.+Can be useful for bringing account balances across file boundaries. .SS help .PP Show any of the hledger manuals. .PP-The \f[C]help\f[] command displays any of the main hledger man pages.-(Unlike \f[C]hledger\ \-\-help\f[], which displays only the hledger man-page.) Run it with no arguments to list available topics (their names-are shortened for easier typing), and run \f[C]hledger\ help\ TOPIC\f[]-to select one.-The output is similar to a man page, but fixed width.-It may be long, so you may wish to pipe it into a pager.-See also info and man.+The \f[C]help\f[] command displays any of the main hledger manuals, in+one of several ways.+Run it with no argument to list the manuals, or provide a full or+partial manual name to select one.+.PP+hledger manuals are available in several formats.+hledger help will use the first of these display methods that it finds:+info, man, $PAGER, less, stdout (or when non\-interactive, just stdout).+You can force a particular viewer with the \f[C]\-\-info\f[],+\f[C]\-\-man\f[], \f[C]\-\-pager\f[], \f[C]\-\-cat\f[] flags. .IP .nf \f[C] $\ hledger\ help-Choose\ a\ topic,\ eg:\ hledger\ help\ cli-cli,\ ui,\ web,\ api,\ journal,\ csv,\ timeclock,\ timedot+Please\ choose\ a\ manual\ by\ typing\ "hledger\ help\ MANUAL"\ (a\ substring\ is\ ok).+Manuals:\ hledger\ hledger\-ui\ hledger\-web\ hledger\-api\ journal\ csv\ timeclock\ timedot \f[] .fi .IP .nf \f[C]-$\ hledger\ help\ cli\ |\ less--hledger(1)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ hledger\ User\ Manuals\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ hledger(1)-+$\ hledger\ help\ h\ \-\-man +hledger(1)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ hledger\ User\ Manuals\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ hledger(1) NAME \ \ \ \ \ \ \ hledger\ \-\ a\ command\-line\ accounting\ tool SYNOPSIS-\ \ \ \ \ \ \ hledger\ [\-f\ FILE]\ COMMAND\ [OPTIONS]\ [CMDARGS]-\ \ \ \ \ \ \ hledger\ [\-f\ FILE]\ ADDONCMD\ \-\-\ [OPTIONS]\ [CMDARGS]-:+\ \ \ \ \ \ \ hledger\ [\-f\ FILE]\ COMMAND\ [OPTIONS]\ [ARGS]+\ \ \ \ \ \ \ hledger\ [\-f\ FILE]\ ADDONCMD\ \-\-\ [OPTIONS]\ [ARGS]+\ \ \ \ \ \ \ hledger++DESCRIPTION+\ \ \ \ \ \ \ hledger\ \ is\ \ a\ \ cross\-platform\ \ program\ \ for\ tracking\ money,\ time,\ or\ any+\&... \f[] .fi+.SS import+.PP+Read new transactions added to each FILE since last run, and add them to+the main journal file.+.TP+.B \f[C]\-\-dry\-run\f[]+just show the transactions to be imported+.RS+.RE+.PP+Input files are provided as arguments, or glob patterns.+So eg to add new transactions from all CSV files to the main journal:+hledger import *.csv+.PP+New transactions are detected like print \-\-new (using .latest.FILE+state files). .SS incomestatement .PP Show an income statement.@@ -1961,6 +2059,11 @@ in single\-column balance reports: use this custom line format .RS .RE+.TP+.B \f[C]\-\-sort\-amount\f[]+sort by amount instead of account name+.RS+.RE .PP This command displays a simple income statement. It currently assumes that you have top\-level accounts named@@ -1997,37 +2100,13 @@ Normally incomestatement shows revenues/expenses per period, though as with multicolumn balance reports you can alter the report mode with \f[C]\-\-change\f[]/\f[C]\-\-cumulative\f[]/\f[C]\-\-historical\f[].-.SS info-.PP-Show any of the hledger manuals using info.-.PP-The \f[C]info\f[] command displays any of the hledger reference manuals-using the info hypertextual documentation viewer.-This can be a very efficient way to browse large manuals.-It requires the "info" program to be available in your PATH.-.PP-As with help, run it with no arguments to list available topics-(manuals).-.SS man-.PP-Show any of the hledger manuals using man.-.PP-The \f[C]man\f[] command displays any of the hledger reference manuals-using man, the standard documentation viewer on unix systems.-This will fit the text to your terminal width, and probably invoke a-pager automatically.-It requires the "man" program to be available in your PATH.+.SS prices .PP-As with help, run it with no arguments to list available topics-(manuals).+Print all market prices from the journal. .SS print .PP Show transactions from the journal.-.TP-.B \f[C]\-x\ \ \ \ \ \-\-explicit\f[]-show all amounts explicitly-.RS-.RE+Aliases: p, txns. .TP .B \f[C]\-m\ STR\ \-\-match=STR\f[] show the transaction whose description is most similar to STR, and is@@ -2035,6 +2114,16 @@ .RS .RE .TP+.B \f[C]\-\-new\f[]+show only newer\-dated transactions added in each file since last run+.RS+.RE+.TP+.B \f[C]\-x\ \ \ \ \ \-\-explicit\f[]+show all amounts explicitly+.RS+.RE+.TP .B \f[C]\-O\ FMT\ \-\-output\-format=FMT\f[] select the output format. Supported formats: txt, csv.@@ -2074,25 +2163,54 @@ .fi .PP The print command displays full journal entries (transactions) from the-journal file, tidily formatted.-.PP-As of hledger 1.2, print\[aq]s output is always a valid hledger journal.-However it may not preserve all original content, eg it does not print-directives or inter\-transaction comments.+journal file in date order, tidily formatted.+print\[aq]s output is always a valid hledger journal.+It preserves all transaction information, but it does not preserve+directives or inter\-transaction comments .PP-Normally, transactions\[aq] implicit/explicit amount style is preserved:-when an amount is omitted in the journal, it will be omitted in the+Normally, the journal entry\[aq]s explicit or implicit amount style is+preserved.+Ie when an amount is omitted in the journal, it will be omitted in the output.-You can use the \f[C]\-x/\-\-explicit\f[] flag to make all amounts-explicit, which can be useful for troubleshooting or for making your-journal more readable and robust against data entry errors.-Note, in this mode postings with a multi\-commodity amount (possible-with an implicit amount in a multi\-commodity transaction) will be split-into multiple single\-commodity postings, for valid journal output.+You can use the \f[C]\-x\f[]/\f[C]\-\-explicit\f[] flag to make all+amounts explicit, which can be useful for troubleshooting or for making+your journal more readable and robust against data entry errors.+Note, \f[C]\-x\f[] will cause postings with a multi\-commodity amount+(these can arise when a multi\-commodity transaction has an implicit+amount) will be split into multiple single\-commodity postings, for+valid journal output. .PP-With \-B/\-\-cost, amounts with transaction prices are converted to cost-(using the transaction price).+With \f[C]\-B\f[]/\f[C]\-\-cost\f[], amounts with transaction prices are+converted to cost using that price. .PP+With \f[C]\-m\f[]/\f[C]\-\-match\f[] and a STR argument, print will show+at most one transaction: the one one whose description is most similar+to STR, and is most recent.+STR should contain at least two characters.+If there is no similar\-enough match, no transaction will be shown.+.PP+With \f[C]\-\-new\f[], for each FILE being read, hledger reads (and+writes) a special state file (\f[C]\&.latest.FILE\f[] in the same+directory), containing the latest transaction date(s) that were seen+last time FILE was read.+When this file is found, only transactions with newer dates (and new+transactions on the latest date) are printed.+This is useful for ignoring already\-seen entries in import data, such+as downloaded CSV files.+Eg:+.IP+.nf+\f[C]+$\ hledger\ \-f\ bank1.csv\ print\ \-\-new+#\ shows\ transactions\ added\ since\ last\ print\ \-\-new\ on\ this\ file+\f[]+.fi+.PP+This assumes that transactions added to FILE always have same or+increasing dates, and that transactions on the same day do not get+reordered.+See also the import command.+.PP The print command also supports output destination and CSV output. Here\[aq]s an example of print\[aq]s CSV output: .IP@@ -2129,10 +2247,13 @@ for convenience. (Those names are not accurate in the accounting sense; it just puts negative amounts under credit and zero or greater amounts under debit.)+.SS print\-unique+.PP+Print transactions which do not reuse an already\-seen description. .SS register .PP Show postings and their running total.-Alias: reg.+Aliases: r, reg. .TP .B \f[C]\-\-cumulative\f[] show running total from report start date (default)@@ -2305,6 +2426,14 @@ The register command also supports the \f[C]\-o/\-\-output\-file\f[] and \f[C]\-O/\-\-output\-format\f[] options for controlling output destination and CSV output.+.SS register\-match+.PP+Print the one posting whose transaction description is closest to DESC,+in the style of the register command.+Helps ledger\-autosync detect already\-seen transactions when importing.+.SS rewrite+.PP+Print all transactions, adding custom postings to the matched ones. .SS stats .PP Show some journal statistics.@@ -2337,6 +2466,9 @@ .PP The stats command also supports \f[C]\-o/\-\-output\-file\f[] for controlling output destination.+.SS tags+.PP+List all the tag names in use. .SS test .PP Run built\-in unit tests.@@ -2440,33 +2572,6 @@ .SS check .PP hledger\-check.hs checks more powerful account balance assertions.-.SS check\-dates-.PP-hledger\-check\-dates.hs checks that journal entries are ordered by-date.-.SS check\-dupes-.PP-hledger\-check\-dupes.hs checks for account names sharing the same leaf-name.-.SS equity-.PP-hledger\-equity.hs prints balance\-resetting transactions, useful for-bringing account balances across file boundaries.-.SS prices-.PP-hledger\-prices.hs prints all prices from the journal.-.SS print\-unique-.PP-hledger\-print\-unique.hs prints transactions which do not reuse an-already\-seen description.-.SS register\-match-.PP-hledger\-register\-match.hs helps ledger\-autosync detect already\-seen-transactions when importing.-.SS rewrite-.PP-hledger\-rewrite.hs Adds one or more custom postings to matched-transactions. .SH ENVIRONMENT .PP \f[B]COLUMNS\f[] The screen width used by the register command.
doc/hledger.1.info view
@@ -3,8 +3,8 @@ File: hledger.1.info, Node: Top, Next: EXAMPLES, Up: (dir) -hledger(1) hledger 1.3.1-************************+hledger(1) hledger 1.4+********************** This is hledger's command-line interface (there are also curses and web interfaces). Its basic function is to read a plain text file describing@@ -118,6 +118,7 @@ * General options:: * Command options:: * Command arguments::+* Argument expansion:: * Special characters:: * Input files:: * Smart dates::@@ -137,24 +138,13 @@ =================== To see general usage help, including general options which are supported-by most hledger commands, run 'hledger -h'. (Note -h and -help are-different, like git.)+by most hledger commands, run 'hledger -h'. General help options: -'-h'+'-h --help' show general usage (or after COMMAND, command usage)-'--help'-- show this program's manual as plain text (or after an add-on- COMMAND, the add-on's manual)-'--man'-- show this program's manual with man-'--info'-- show this program's manual with info '--version' show version@@ -177,9 +167,9 @@ '--anon' anonymize accounts and payees-'--pivot TAGNAME'+'--pivot FIELDNAME' - use some other field/tag for account names+ use some other field or tag for the account name '-I --ignore-assertions' ignore any failing balance assertions@@ -213,7 +203,8 @@ (overrides the flags above) '--date2' - show, and match with -b/-e/-p/date:, secondary dates instead+ match the secondary date instead (see command help for other+ effects) '-U --unmarked' include only unmarked postings/txns (can combine with -P or -C)@@ -226,9 +217,9 @@ '-R --real' include only non-virtual postings-'--depth=N'+'-NUM --depth=NUM' - hide accounts/postings deeper than N+ hide/aggregate accounts or postings more than NUM levels deep '-E --empty' show items with zero amount, normally hidden@@ -241,10 +232,10 @@ convert amounts to their market value on the report end date (using the most recent applicable market price, if any) - Note when multiple similar reporting options are provided, the last-one takes precedence. Eg '-p feb -p mar' is equivalent to '-p mar'.+ When a reporting option appears more than once in the command line,+the last one takes precedence. - Some of these can also be written as queries.+ Some reporting options can also be written as query arguments. File: hledger.1.info, Node: Command options, Next: Command arguments, Prev: General options, Up: OPTIONS@@ -263,7 +254,7 @@ run the addon executable directly: 'hledger-ui --watch'. -File: hledger.1.info, Node: Command arguments, Next: Special characters, Prev: Command options, Up: OPTIONS+File: hledger.1.info, Node: Command arguments, Next: Argument expansion, Prev: Command options, Up: OPTIONS 2.3 Command arguments =====================@@ -272,11 +263,22 @@ often a query, filtering the data in some way. -File: hledger.1.info, Node: Special characters, Next: Input files, Prev: Command arguments, Up: OPTIONS+File: hledger.1.info, Node: Argument expansion, Next: Special characters, Prev: Command arguments, Up: OPTIONS -2.4 Special characters+2.4 Argument expansion ====================== +You can save a set of command line options/arguments in a file, one per+line, and then reuse them by writing '@FILE' in a command line. (To+prevent this expansion of '@'-arguments, precede them with a '--'+argument.)+++File: hledger.1.info, Node: Special characters, Next: Input files, Prev: Argument expansion, Up: OPTIONS++2.5 Special characters+======================+ Option and argument values which contain problematic characters should be escaped with double quotes, backslashes, or (best) single quotes. Problematic characters means spaces, and also characters which are@@ -290,11 +292,16 @@ dollar symbol, bash users should do: 'hledger balance cur:'\$'' or 'hledger balance cur:\\$'. - There's more.. options and arguments get de-escaped when hledger is-passing them to an addon executable. In this case you might need-_triple_-escaping. Eg: 'hledger ui cur:'\\$'' or 'hledger ui-cur:\\\\$'.+ When hledger is invoking an addon executable (like hledger-ui),+options and arguments get de-escaped once more, so you might need+_triple_-escaping. Eg: 'hledger ui cur:'\\$'' or 'hledger ui cur:\\\\$'+in bash. (The number of backslashes in fish shell is left as an+exercise for the reader.) + Inside a file used for argument expansion, one less level of escaping+is enough. (And in this case, backslashes seem to work better than+quotes. Eg: 'cur:\$').+ If in doubt, keep things simple: * run add-on executables directly@@ -307,7 +314,7 @@ File: hledger.1.info, Node: Input files, Next: Smart dates, Prev: Special characters, Up: OPTIONS -2.5 Input files+2.6 Input files =============== hledger reads transactions from a data file (and the add command writes@@ -362,7 +369,7 @@ File: hledger.1.info, Node: Smart dates, Next: Report start & end date, Prev: Input files, Up: OPTIONS -2.6 Smart dates+2.7 Smart dates =============== hledger's user interfaces accept a flexible "smart date" syntax (unlike@@ -385,7 +392,7 @@ File: hledger.1.info, Node: Report start & end date, Next: Report intervals, Prev: Smart dates, Up: OPTIONS -2.7 Report start & end date+2.8 Report start & end date =========================== Most hledger reports show the full span of time represented by the@@ -414,7 +421,7 @@ File: hledger.1.info, Node: Report intervals, Next: Period expressions, Prev: Report start & end date, Up: OPTIONS -2.8 Report intervals+2.9 Report intervals ==================== A report interval can be specified so that commands like register,@@ -427,8 +434,8 @@ File: hledger.1.info, Node: Period expressions, Next: Depth limiting, Prev: Report intervals, Up: OPTIONS -2.9 Period expressions-======================+2.10 Period expressions+======================= The '-p/--period' option accepts period expressions, a shorthand way of expressing a start date, end date, and/or report interval all at once.@@ -502,34 +509,33 @@ File: hledger.1.info, Node: Depth limiting, Next: Pivoting, Prev: Period expressions, Up: OPTIONS -2.10 Depth limiting+2.11 Depth limiting =================== -With the '--depth N' option, commands like account, balance and register-will show only the uppermost accounts in the account tree, down to level-N. Use this when you want a summary with less detail.+With the '--depth N' option (short form: '-N'), commands like account,+balance and register will show only the uppermost accounts in the+account tree, down to level N. Use this when you want a summary with+less detail. This flag has the same effect as a 'depth:' query argument+(so '-2', '--depth=2' or 'depth:2' are basically equivalent). File: hledger.1.info, Node: Pivoting, Next: Cost, Prev: Depth limiting, Up: OPTIONS -2.11 Pivoting+2.12 Pivoting ============= Normally hledger sums amounts, and organizes them in a hierarchy, based-on account name. The '--pivot TAGNAME' option causes it to sum and-organize hierarchy based on some other field instead.-- TAGNAME is the full, case-insensitive name of a tag you have defined,-or one of the built-in implicit tags (like 'code' or 'payee'). As with-account names, when tag values have 'multiple:colon-separated:parts'-hledger will build hierarchy, displayed in tree-mode reports,-summarisable with a depth limit, and so on.+on account name. The '--pivot FIELD' option causes it to sum and+organize hierarchy based on the value of some other field instead.+FIELD can be: 'code', 'description', 'payee', 'note', or the full name+(case insensitive) of any tag. As with account names, values containing+'colon:separated:parts' will be displayed hierarchically in reports. '--pivot' is a general option affecting all reports; you can think of hledger transforming the journal before any other processing, replacing-every posting's account name with the value of the specified tag on that-posting, inheriting it from the transaction or using a blank value if-it's not present.+every posting's account name with the value of the specified field on+that posting, inheriting it from the transaction or using a blank value+if it's not present. An example: @@ -572,7 +578,7 @@ File: hledger.1.info, Node: Cost, Next: Market value, Prev: Pivoting, Up: OPTIONS -2.12 Cost+2.13 Cost ========= The '-B/--cost' flag converts amounts to their cost at transaction time,@@ -581,7 +587,7 @@ File: hledger.1.info, Node: Market value, Next: Regular expressions, Prev: Cost, Up: OPTIONS -2.13 Market value+2.14 Market value ================= The '-V/--value' flag converts the reported amounts to their market@@ -630,7 +636,7 @@ File: hledger.1.info, Node: Regular expressions, Prev: Market value, Up: OPTIONS -2.14 Regular expressions+2.15 Regular expressions ======================== hledger uses regular expressions in a number of places:@@ -678,29 +684,32 @@ expression, written as arguments after the command name, to filter the data by date, account name or other criteria. The syntax is similar to a web search: one or more space-separated search terms, quotes to-enclose whitespace, optional prefixes to match specific fields.-Multiple search terms are combined as follows:+enclose whitespace, prefixes to match specific fields, a not: prefix to+negate the match. - All commands except print: show transactions/postings/accounts which-match (or negatively match)+ We do not yet support arbitrary boolean combinations of search terms;+instead most commands show transactions/postings/accounts which match+(or negatively match): * any of the description terms AND * any of the account terms AND * any of the status terms AND * all the other terms. - The print command: show transactions which+ The print command instead shows transactions which: * match any of the description terms AND * have any postings matching any of the positive account terms AND * have no postings matching any of the negative account terms AND * match all the other terms. - The following kinds of search terms can be used:+ The following kinds of search terms can be used. Remember these can+also be prefixed with *'not:'*, eg to exclude a particular subaccount. *'REGEX'* - match account names by this regular expression+ match account names by this regular expression. (No prefix is+ equivalent to 'acct:'). *'acct:REGEX'* same as above@@ -726,7 +735,7 @@ print cur:'\$'' or 'hledger print cur:\\$'. *'desc:REGEX'* - match transaction descriptions+ match transaction descriptions. *'date:PERIODEXPR'* match dates within the specified period. PERIODEXPR is a period@@ -741,6 +750,14 @@ match (or display, depending on command) accounts at or above this depth+*'note:REGEX'*++ match transaction notes (part of description right of '|', or whole+ description when there's no '|')+*'payee:REGEX'*++ match transaction payee/payer names (part of description left of+ '|', or whole description when there's no '|') *'real:, real:0'* match real or virtual postings respectively@@ -753,16 +770,14 @@ query is considered to match a transaction if it matches any of the postings. Also remember that postings inherit the tags of their parent transaction.-*'not:'* - before any of the above negates the match.+ The following special search term is used automatically in+hledger-web, only:+ *'inacct:ACCTNAME'* - a special term used automatically when you click an account name in- hledger-web, specifying the account register we are currently in- (selects the transactions of that account and how to show them, can- be filtered further with 'acct' etc). Not supported elsewhere in- hledger.+ tells hledger-web to show the transaction register for this+ account. Can be filtered further with 'acct' etc. Some of these can also be expressed as command-line options (eg 'depth:2' is equivalent to '--depth 2'). Generally you can mix options@@ -783,9 +798,13 @@ be listed as subcommands. Run a subcommand by writing its name as first argument (eg 'hledger-incomestatement'). You can also write any unambiguous prefix of a-command name ('hledger inc'), or one of the standard short aliases-displayed in the command list ('hledger is').+incomestatement'). You can also write one of the standard short aliases+displayed in parentheses in the command list ('hledger b'), or any any+unambiguous prefix of a command name ('hledger inc').++ Here are all the builtin commands in alphabetical order. See also+'hledger' for a more organised command list, and 'hledger CMD -h' for+detailed command help. * Menu: * accounts::@@ -793,14 +812,22 @@ * add:: * balance:: * balancesheet::+* balancesheetequity:: * cashflow::+* check-dates::+* check-dupes::+* equity:: * help::+* import:: * incomestatement::-* info::-* man::+* prices:: * print::+* print-unique:: * register::+* register-match::+* rewrite:: * stats::+* tags:: * test:: @@ -809,7 +836,7 @@ 4.1 accounts ============ -Show account names.+Show account names. Alias: a. '--tree' @@ -962,7 +989,7 @@ 4.4 balance =========== -Show accounts and their balances. Alias: bal.+Show accounts and their balances. Aliases: b, bal. '--change' @@ -1011,7 +1038,11 @@ '--pretty-tables' Use unicode to display prettier tables.+'--sort-amount' + Sort by amount (total row amount, or by average if that is+ displayed), instead of account name (in flat mode)+ The balance command displays accounts and balances. It is hledger's most featureful and versatile command. @@ -1298,7 +1329,7 @@ $ hledger balance -o FILE.csv # write CSV to FILE.csv -File: hledger.1.info, Node: balancesheet, Next: cashflow, Prev: balance, Up: COMMANDS+File: hledger.1.info, Node: balancesheet, Next: balancesheetequity, Prev: balance, Up: COMMANDS 4.5 balancesheet ================@@ -1343,7 +1374,10 @@ '--format=LINEFORMAT' in single-column balance reports: use this custom line format+'--sort-amount' + sort by amount instead of account name+ This command displays a simple balance sheet. It currently assumes that you have top-level accounts named 'asset' and 'liability' (plural forms also allowed.)@@ -1374,9 +1408,49 @@ for a balance sheet; note this means it ignores report begin dates. -File: hledger.1.info, Node: cashflow, Next: help, Prev: balancesheet, Up: COMMANDS+File: hledger.1.info, Node: balancesheetequity, Next: cashflow, Prev: balancesheet, Up: COMMANDS -4.6 cashflow+4.6 balancesheetequity+======================++Show a balance sheet including equity. Alias: bse.++ Other than showing the equity accounts, this command is exactly the+same as the command balancesheet. Please refer to it for the available+options.++ This command displays a balancesheet. It currently assumes that you+have top-level accounts named 'asset', 'liability' and 'equity' (plural+forms also allowed.)++$ hledger balancesheetequity+Balance Sheet With Equity++Assets:+ $-2 assets+ $1 bank:saving+ $-3 cash+--------------------+ $-2++Liabilities:+ $1 liabilities:debts+--------------------+ $1++Equity:+ $1 equity:owner+--------------------+ $1++Total:+--------------------+ 0+++File: hledger.1.info, Node: cashflow, Next: check-dates, Prev: balancesheetequity, Up: COMMANDS++4.7 cashflow ============ Show a cashflow statement. Alias: cf.@@ -1418,11 +1492,14 @@ '--format=LINEFORMAT' in single-column balance reports: use this custom line format+'--sort-amount' + sort by amount instead of account name+ This command displays a simple cashflow statement It shows the change in all "cash" (ie, liquid assets) accounts for the period. It currently assumes that cash accounts are under a top-level account named 'asset'-and do not contain 'receivable' or 'A/R' (plural forms also allowed.)+and do not contain 'receivable', ':A/R' or ':fixed'. $ hledger cashflow Cashflow Statement@@ -1444,44 +1521,96 @@ report mode with '--change'/'--cumulative'/'--historical'. -File: hledger.1.info, Node: help, Next: incomestatement, Prev: cashflow, Up: COMMANDS+File: hledger.1.info, Node: check-dates, Next: check-dupes, Prev: cashflow, Up: COMMANDS -4.7 help-========+4.8 check-dates+=============== -Show any of the hledger manuals.+Check that transactions are sorted by increasing date. With a query,+only matched transactions' dates are checked. - The 'help' command displays any of the main hledger man pages.-(Unlike 'hledger --help', which displays only the hledger man page.)-Run it with no arguments to list available topics (their names are-shortened for easier typing), and run 'hledger help TOPIC' to select-one. The output is similar to a man page, but fixed width. It may be-long, so you may wish to pipe it into a pager. See also info and man.++File: hledger.1.info, Node: check-dupes, Next: equity, Prev: check-dates, Up: COMMANDS -$ hledger help-Choose a topic, eg: hledger help cli-cli, ui, web, api, journal, csv, timeclock, timedot+4.9 check-dupes+=============== -$ hledger help cli | less+Report account names having the same leaf but different prefixes. An+example: http://stefanorodighiero.net/software/hledger-dupes.html -hledger(1) hledger User Manuals hledger(1)++File: hledger.1.info, Node: equity, Next: help, Prev: check-dupes, Up: COMMANDS +4.10 equity+=========== +Print closing/opening transactions that bring some or all account+balances to zero and back. Can be useful for bringing account balances+across file boundaries. ++File: hledger.1.info, Node: help, Next: import, Prev: equity, Up: COMMANDS++4.11 help+=========++Show any of the hledger manuals.++ The 'help' command displays any of the main hledger manuals, in one+of several ways. Run it with no argument to list the manuals, or+provide a full or partial manual name to select one.++ hledger manuals are available in several formats. hledger help will+use the first of these display methods that it finds: info, man, $PAGER,+less, stdout (or when non-interactive, just stdout). You can force a+particular viewer with the '--info', '--man', '--pager', '--cat' flags.++$ hledger help+Please choose a manual by typing "hledger help MANUAL" (a substring is ok).+Manuals: hledger hledger-ui hledger-web hledger-api journal csv timeclock timedot++$ hledger help h --man++hledger(1) hledger User Manuals hledger(1)+ NAME hledger - a command-line accounting tool SYNOPSIS- hledger [-f FILE] COMMAND [OPTIONS] [CMDARGS]- hledger [-f FILE] ADDONCMD -- [OPTIONS] [CMDARGS]-:+ hledger [-f FILE] COMMAND [OPTIONS] [ARGS]+ hledger [-f FILE] ADDONCMD -- [OPTIONS] [ARGS]+ hledger +DESCRIPTION+ hledger is a cross-platform program for tracking money, time, or any+...+ -File: hledger.1.info, Node: incomestatement, Next: info, Prev: help, Up: COMMANDS+File: hledger.1.info, Node: import, Next: incomestatement, Prev: help, Up: COMMANDS -4.8 incomestatement-===================+4.12 import+=========== +Read new transactions added to each FILE since last run, and add them to+the main journal file.++'--dry-run'++ just show the transactions to be imported++ Input files are provided as arguments, or glob patterns. So eg to+add new transactions from all CSV files to the main journal: hledger+import *.csv++ New transactions are detected like print -new (using .latest.FILE+state files).+++File: hledger.1.info, Node: incomestatement, Next: prices, Prev: import, Up: COMMANDS++4.13 incomestatement+====================+ Show an income statement. Alias: is. '--change'@@ -1521,7 +1650,10 @@ '--format=LINEFORMAT' in single-column balance reports: use this custom line format+'--sort-amount' + sort by amount instead of account name+ This command displays a simple income statement. It currently assumes that you have top-level accounts named 'income' (or 'revenue') and 'expense' (plural forms also allowed.)@@ -1553,53 +1685,32 @@ report mode with '--change'/'--cumulative'/'--historical'. -File: hledger.1.info, Node: info, Next: man, Prev: incomestatement, Up: COMMANDS--4.9 info-========--Show any of the hledger manuals using info.-- The 'info' command displays any of the hledger reference manuals-using the info hypertextual documentation viewer. This can be a very-efficient way to browse large manuals. It requires the "info" program-to be available in your PATH.-- As with help, run it with no arguments to list available topics-(manuals).---File: hledger.1.info, Node: man, Next: print, Prev: info, Up: COMMANDS--4.10 man-========--Show any of the hledger manuals using man.+File: hledger.1.info, Node: prices, Next: print, Prev: incomestatement, Up: COMMANDS - The 'man' command displays any of the hledger reference manuals using-man, the standard documentation viewer on unix systems. This will fit-the text to your terminal width, and probably invoke a pager-automatically. It requires the "man" program to be available in your-PATH.+4.14 prices+=========== - As with help, run it with no arguments to list available topics-(manuals).+Print all market prices from the journal. -File: hledger.1.info, Node: print, Next: register, Prev: man, Up: COMMANDS+File: hledger.1.info, Node: print, Next: print-unique, Prev: prices, Up: COMMANDS -4.11 print+4.15 print ========== -Show transactions from the journal.--'-x --explicit'+Show transactions from the journal. Aliases: p, txns. - show all amounts explicitly '-m STR --match=STR' show the transaction whose description is most similar to STR, and is most recent+'--new'++ show only newer-dated transactions added in each file since last+ run+'-x --explicit'++ show all amounts explicitly '-O FMT --output-format=FMT' select the output format. Supported formats: txt, csv.@@ -1631,24 +1742,43 @@ assets:bank:checking $-1 The print command displays full journal entries (transactions) from-the journal file, tidily formatted.+the journal file in date order, tidily formatted. print's output is+always a valid hledger journal. It preserves all transaction+information, but it does not preserve directives or inter-transaction+comments - As of hledger 1.2, print's output is always a valid hledger journal.-However it may not preserve all original content, eg it does not print-directives or inter-transaction comments.+ Normally, the journal entry's explicit or implicit amount style is+preserved. Ie when an amount is omitted in the journal, it will be+omitted in the output. You can use the '-x'/'--explicit' flag to make+all amounts explicit, which can be useful for troubleshooting or for+making your journal more readable and robust against data entry errors.+Note, '-x' will cause postings with a multi-commodity amount (these can+arise when a multi-commodity transaction has an implicit amount) will be+split into multiple single-commodity postings, for valid journal output. - Normally, transactions' implicit/explicit amount style is preserved:-when an amount is omitted in the journal, it will be omitted in the-output. You can use the '-x/--explicit' flag to make all amounts-explicit, which can be useful for troubleshooting or for making your-journal more readable and robust against data entry errors. Note, in-this mode postings with a multi-commodity amount (possible with an-implicit amount in a multi-commodity transaction) will be split into-multiple single-commodity postings, for valid journal output.+ With '-B'/'--cost', amounts with transaction prices are converted to+cost using that price. - With -B/-cost, amounts with transaction prices are converted to cost-(using the transaction price).+ With '-m'/'--match' and a STR argument, print will show at most one+transaction: the one one whose description is most similar to STR, and+is most recent. STR should contain at least two characters. If there+is no similar-enough match, no transaction will be shown. + With '--new', for each FILE being read, hledger reads (and writes) a+special state file ('.latest.FILE' in the same directory), containing+the latest transaction date(s) that were seen last time FILE was read.+When this file is found, only transactions with newer dates (and new+transactions on the latest date) are printed. This is useful for+ignoring already-seen entries in import data, such as downloaded CSV+files. Eg:++$ hledger -f bank1.csv print --new+# shows transactions added since last print --new on this file++ This assumes that transactions added to FILE always have same or+increasing dates, and that transactions on the same day do not get+reordered. See also the import command.+ The print command also supports output destination and CSV output. Here's an example of print's CSV output: @@ -1680,12 +1810,20 @@ zero or greater amounts under debit.) -File: hledger.1.info, Node: register, Next: stats, Prev: print, Up: COMMANDS+File: hledger.1.info, Node: print-unique, Next: register, Prev: print, Up: COMMANDS -4.12 register+4.16 print-unique+=================++Print transactions which do not reuse an already-seen description.+++File: hledger.1.info, Node: register, Next: register-match, Prev: print-unique, Up: COMMANDS++4.17 register ============= -Show postings and their running total. Alias: reg.+Show postings and their running total. Aliases: r, reg. '--cumulative' @@ -1787,7 +1925,7 @@ File: hledger.1.info, Node: Custom register output, Up: register -4.12.1 Custom register output+4.17.1 Custom register output ----------------------------- register uses the full terminal width by default, except on windows.@@ -1817,9 +1955,27 @@ output. -File: hledger.1.info, Node: stats, Next: test, Prev: register, Up: COMMANDS+File: hledger.1.info, Node: register-match, Next: rewrite, Prev: register, Up: COMMANDS -4.13 stats+4.18 register-match+===================++Print the one posting whose transaction description is closest to DESC,+in the style of the register command. Helps ledger-autosync detect+already-seen transactions when importing.+++File: hledger.1.info, Node: rewrite, Next: stats, Prev: register-match, Up: COMMANDS++4.19 rewrite+============++Print all transactions, adding custom postings to the matched ones.+++File: hledger.1.info, Node: stats, Next: tags, Prev: rewrite, Up: COMMANDS++4.20 stats ========== Show some journal statistics.@@ -1849,11 +2005,19 @@ output destination. -File: hledger.1.info, Node: test, Prev: stats, Up: COMMANDS+File: hledger.1.info, Node: tags, Next: test, Prev: stats, Up: COMMANDS -4.14 test+4.21 tags ========= +List all the tag names in use.+++File: hledger.1.info, Node: test, Prev: tags, Up: COMMANDS++4.22 test+=========+ Run built-in unit tests. $ hledger test@@ -2006,13 +2170,6 @@ * budget:: * chart:: * check::-* check-dates::-* check-dupes::-* equity::-* prices::-* print-unique::-* register-match::-* rewrite:: File: hledger.1.info, Node: autosync, Next: budget, Up: Experimental add-ons@@ -2042,7 +2199,7 @@ hledger-chart.hs is an old pie chart generator, in need of some love. -File: hledger.1.info, Node: check, Next: check-dates, Prev: chart, Up: Experimental add-ons+File: hledger.1.info, Node: check, Prev: chart, Up: Experimental add-ons 5.3.4 check -----------@@ -2050,192 +2207,135 @@ hledger-check.hs checks more powerful account balance assertions. -File: hledger.1.info, Node: check-dates, Next: check-dupes, Prev: check, Up: Experimental add-ons--5.3.5 check-dates--------------------hledger-check-dates.hs checks that journal entries are ordered by date.---File: hledger.1.info, Node: check-dupes, Next: equity, Prev: check-dates, Up: Experimental add-ons--5.3.6 check-dupes--------------------hledger-check-dupes.hs checks for account names sharing the same leaf-name.---File: hledger.1.info, Node: equity, Next: prices, Prev: check-dupes, Up: Experimental add-ons--5.3.7 equity---------------hledger-equity.hs prints balance-resetting transactions, useful for-bringing account balances across file boundaries.---File: hledger.1.info, Node: prices, Next: print-unique, Prev: equity, Up: Experimental add-ons--5.3.8 prices---------------hledger-prices.hs prints all prices from the journal.---File: hledger.1.info, Node: print-unique, Next: register-match, Prev: prices, Up: Experimental add-ons--5.3.9 print-unique---------------------hledger-print-unique.hs prints transactions which do not reuse an-already-seen description.---File: hledger.1.info, Node: register-match, Next: rewrite, Prev: print-unique, Up: Experimental add-ons--5.3.10 register-match------------------------hledger-register-match.hs helps ledger-autosync detect already-seen-transactions when importing.---File: hledger.1.info, Node: rewrite, Prev: register-match, Up: Experimental add-ons--5.3.11 rewrite-----------------hledger-rewrite.hs Adds one or more custom postings to matched-transactions.-- Tag Table: Node: Top70-Node: EXAMPLES1890-Ref: #examples1992-Node: OPTIONS3638-Ref: #options3742-Node: General options4023-Ref: #general-options4150-Node: Command options6688-Ref: #command-options6841-Node: Command arguments7239-Ref: #command-arguments7399-Node: Special characters7520-Ref: #special-characters7678-Node: Input files8846-Ref: #input-files8984-Node: Smart dates10947-Ref: #smart-dates11090-Node: Report start & end date12069-Ref: #report-start-end-date12241-Node: Report intervals13307-Ref: #report-intervals13472-Node: Period expressions13873-Ref: #period-expressions14033-Node: Depth limiting16373-Ref: #depth-limiting16519-Node: Pivoting16720-Ref: #pivoting16840-Node: Cost18611-Ref: #cost18721-Node: Market value18839-Ref: #market-value18976-Node: Regular expressions20276-Ref: #regular-expressions20414-Node: QUERIES21775-Ref: #queries21879-Node: COMMANDS25544-Ref: #commands25658-Node: accounts26331-Ref: #accounts26431-Node: activity27413-Ref: #activity27525-Node: add27884-Ref: #add27985-Node: balance30643-Ref: #balance30756-Node: Flat mode33771-Ref: #flat-mode33898-Node: Depth limited balance reports34318-Ref: #depth-limited-balance-reports34521-Node: Multicolumn balance reports34941-Ref: #multicolumn-balance-reports35152-Node: Custom balance output39800-Ref: #custom-balance-output39984-Node: Colour support42077-Ref: #colour-support42238-Node: Output destination42411-Ref: #output-destination42569-Node: CSV output42839-Ref: #csv-output42958-Node: balancesheet43355-Ref: #balancesheet43483-Node: cashflow45390-Ref: #cashflow45507-Node: help47375-Ref: #help47487-Node: incomestatement48325-Ref: #incomestatement48455-Node: info50347-Ref: #info50454-Node: man50818-Ref: #man50915-Node: print51320-Ref: #print51425-Node: register55181-Ref: #register55294-Node: Custom register output59790-Ref: #custom-register-output59921-Node: stats61218-Ref: #stats61324-Node: test62205-Ref: #test62292-Node: ADD-ON COMMANDS62660-Ref: #add-on-commands62772-Node: Official add-ons64059-Ref: #official-add-ons64201-Node: api64288-Ref: #api64379-Node: ui64431-Ref: #ui64532-Node: web64590-Ref: #web64681-Node: Third party add-ons64727-Ref: #third-party-add-ons64904-Node: diff65039-Ref: #diff65138-Node: iadd65237-Ref: #iadd65353-Node: interest65436-Ref: #interest65559-Node: irr65654-Ref: #irr65754-Node: Experimental add-ons65832-Ref: #experimental-add-ons65986-Node: autosync66379-Ref: #autosync66493-Node: budget66732-Ref: #budget66856-Node: chart66922-Ref: #chart67041-Node: check67112-Ref: #check67236-Node: check-dates67303-Ref: #check-dates67445-Node: check-dupes67518-Ref: #check-dupes67661-Node: equity67738-Ref: #equity67866-Node: prices67985-Ref: #prices68114-Node: print-unique68169-Ref: #print-unique68318-Node: register-match68411-Ref: #register-match68567-Node: rewrite68665-Ref: #rewrite68786+Node: EXAMPLES1886+Ref: #examples1988+Node: OPTIONS3634+Ref: #options3738+Node: General options4042+Ref: #general-options4169+Node: Command options6488+Ref: #command-options6641+Node: Command arguments7039+Ref: #command-arguments7199+Node: Argument expansion7320+Ref: #argument-expansion7485+Node: Special characters7704+Ref: #special-characters7863+Node: Input files9282+Ref: #input-files9420+Node: Smart dates11383+Ref: #smart-dates11526+Node: Report start & end date12505+Ref: #report-start-end-date12677+Node: Report intervals13743+Ref: #report-intervals13908+Node: Period expressions14309+Ref: #period-expressions14471+Node: Depth limiting16811+Ref: #depth-limiting16957+Node: Pivoting17299+Ref: #pivoting17419+Node: Cost19095+Ref: #cost19205+Node: Market value19323+Ref: #market-value19460+Node: Regular expressions20760+Ref: #regular-expressions20898+Node: QUERIES22259+Ref: #queries22363+Node: COMMANDS26330+Ref: #commands26444+Node: accounts27427+Ref: #accounts27527+Node: activity28520+Ref: #activity28632+Node: add28991+Ref: #add29092+Node: balance31750+Ref: #balance31863+Node: Flat mode35020+Ref: #flat-mode35147+Node: Depth limited balance reports35567+Ref: #depth-limited-balance-reports35770+Node: Multicolumn balance reports36190+Ref: #multicolumn-balance-reports36401+Node: Custom balance output41049+Ref: #custom-balance-output41233+Node: Colour support43326+Ref: #colour-support43487+Node: Output destination43660+Ref: #output-destination43818+Node: CSV output44088+Ref: #csv-output44207+Node: balancesheet44604+Ref: #balancesheet44742+Node: balancesheetequity46710+Ref: #balancesheetequity46861+Node: cashflow47650+Ref: #cashflow47780+Node: check-dates49692+Ref: #check-dates49821+Node: check-dupes49938+Ref: #check-dupes50065+Node: equity50202+Ref: #equity50314+Node: help50477+Ref: #help50580+Node: import51654+Ref: #import51770+Node: incomestatement52165+Ref: #incomestatement52301+Node: prices54254+Ref: #prices54371+Node: print54414+Ref: #print54526+Node: print-unique59372+Ref: #print-unique59500+Node: register59568+Ref: #register59697+Node: Custom register output64198+Ref: #custom-register-output64329+Node: register-match65626+Ref: #register-match65762+Node: rewrite65945+Ref: #rewrite66064+Node: stats66133+Ref: #stats66238+Node: tags67119+Ref: #tags67219+Node: test67251+Ref: #test67337+Node: ADD-ON COMMANDS67705+Ref: #add-on-commands67817+Node: Official add-ons69104+Ref: #official-add-ons69246+Node: api69333+Ref: #api69424+Node: ui69476+Ref: #ui69577+Node: web69635+Ref: #web69726+Node: Third party add-ons69772+Ref: #third-party-add-ons69949+Node: diff70084+Ref: #diff70183+Node: iadd70282+Ref: #iadd70398+Node: interest70481+Ref: #interest70604+Node: irr70699+Ref: #irr70799+Node: Experimental add-ons70877+Ref: #experimental-add-ons71031+Node: autosync71322+Ref: #autosync71436+Node: budget71675+Ref: #budget71799+Node: chart71865+Ref: #chart71984+Node: check72055+Ref: #check72159 End Tag Table
doc/hledger.1.txt view
@@ -111,19 +111,12 @@ OPTIONS General options To see general usage help, including general options which are sup-- ported by most hledger commands, run hledger -h. (Note -h and --help- are different, like git.)+ ported by most hledger commands, run hledger -h. General help options: - -h show general usage (or after COMMAND, command usage)-- --help show this program's manual as plain text (or after an add-on- COMMAND, the add-on's manual)-- --man show this program's manual with man-- --info show this program's manual with info+ -h --help+ show general usage (or after COMMAND, command usage) --version show version@@ -146,8 +139,8 @@ --anon anonymize accounts and payees - --pivot TAGNAME- use some other field/tag for account names+ --pivot FIELDNAME+ use some other field or tag for the account name -I --ignore-assertions ignore any failing balance assertions@@ -180,7 +173,8 @@ (overrides the flags above) --date2- show, and match with -b/-e/-p/date:, secondary dates instead+ match the secondary date instead (see command help for other+ effects) -U --unmarked include only unmarked postings/txns (can combine with -P or -C)@@ -194,58 +188,69 @@ -R --real include only non-virtual postings - --depth=N- hide accounts/postings deeper than N+ -NUM --depth=NUM+ hide/aggregate accounts or postings more than NUM levels deep -E --empty show items with zero amount, normally hidden -B --cost- convert amounts to their cost at transaction time (using the+ convert amounts to their cost at transaction time (using the transaction price, if any) -V --value- convert amounts to their market value on the report end date+ convert amounts to their market value on the report end date (using the most recent applicable market price, if any) - Note when multiple similar reporting options are provided, the last one- takes precedence. Eg -p feb -p mar is equivalent to -p mar.+ When a reporting option appears more than once in the command line, the+ last one takes precedence. - Some of these can also be written as queries.+ Some reporting options can also be written as query arguments. Command options- To see options for a particular command, including command-specific+ To see options for a particular command, including command-specific options, run: hledger COMMAND -h. - Command-specific options must be written after the command name, eg:+ Command-specific options must be written after the command name, eg: hledger print -x. - Additionally, if the command is an addon, you may need to put its- options after a double-hyphen, eg: hledger ui -- --watch. Or, you can+ Additionally, if the command is an addon, you may need to put its+ options after a double-hyphen, eg: hledger ui -- --watch. Or, you can run the addon executable directly: hledger-ui --watch. Command arguments- Most hledger commands accept arguments after the command name, which+ Most hledger commands accept arguments after the command name, which are often a query, filtering the data in some way. + Argument expansion+ You can save a set of command line options/arguments in a file, one per+ line, and then reuse them by writing @FILE in a command line. (To pre-+ vent this expansion of @-arguments, precede them with a -- argument.)+ Special characters- Option and argument values which contain problematic characters should- be escaped with double quotes, backslashes, or (best) single quotes.+ Option and argument values which contain problematic characters should+ be escaped with double quotes, backslashes, or (best) single quotes. Problematic characters means spaces, and also characters which are sig-- nificant to your command shell, such as less-than/greater-than. Eg:+ nificant to your command shell, such as less-than/greater-than. Eg: hledger register -p 'last year' "accounts receivable (receiv- able|payable)" amt:\>100. - Characters which are significant both to the shell and in regular- expressions sometimes need to be double-escaped. These include paren-- theses, the pipe symbol and the dollar sign. Eg, to match the dollar- symbol, bash users should do: hledger balance cur:'\$' or hledger bal-+ Characters which are significant both to the shell and in regular+ expressions sometimes need to be double-escaped. These include paren-+ theses, the pipe symbol and the dollar sign. Eg, to match the dollar+ symbol, bash users should do: hledger balance cur:'\$' or hledger bal- ance cur:\\$. - There's more.. options and arguments get de-escaped when hledger is- passing them to an addon executable. In this case you might need- triple-escaping. Eg: hledger ui cur:'\\$' or hledger ui cur:\\\\$.+ When hledger is invoking an addon executable (like hledger-ui), options+ and arguments get de-escaped once more, so you might need triple-escap-+ ing. Eg: hledger ui cur:'\\$' or hledger ui cur:\\\\$ in bash. (The+ number of backslashes in fish shell is left as an exercise for the+ reader.) + Inside a file used for argument expansion, one less level of escaping+ is enough. (And in this case, backslashes seem to work better than+ quotes. Eg: cur:\$).+ If in doubt, keep things simple: o run add-on executables directly@@ -261,7 +266,7 @@ Input files hledger reads transactions from a data file (and the add command writes to it). By default this file is $HOME/.hledger.journal (or on Windows,- something like C:/Users/USER/.hledger.journal). You can override this+ something like C:/Users/USER/.hledger.journal). You can override this with the $LEDGER_FILE environment variable: $ setenv LEDGER_FILE ~/finance/2016.journal@@ -275,9 +280,9 @@ $ cat some.journal | hledger -f- - Usually the data file is in hledger's journal format, but it can also- be one of several other formats, listed below. hledger detects the- format automatically based on the file extension, or if that is not+ Usually the data file is in hledger's journal format, but it can also+ be one of several other formats, listed below. hledger detects the+ format automatically based on the file extension, or if that is not recognised, by trying each built-in "reader" in turn: @@ -285,15 +290,15 @@ ----------------------------------------------------------------------------- journal hledger's journal format, also .journal .j .hledger some Ledger journals .ledger- timeclock timeclock files (precise time .timeclock+ timeclock timeclock files (precise time .timeclock logging)- timedot timedot files (approximate time .timedot+ timedot timedot files (approximate time .timedot logging)- csv comma-separated values (data .csv+ csv comma-separated values (data .csv interchange) - If needed (eg to ensure correct error messages when a file has the- "wrong" extension), you can force a specific reader/format by prepend-+ If needed (eg to ensure correct error messages when a file has the+ "wrong" extension), you can force a specific reader/format by prepend- ing it to the file path with a colon. Examples: $ hledger -f csv:/some/csv-file.dat stats@@ -304,7 +309,7 @@ o directives in one file will not affect the other files - o balance assertions will not see any account balances from previous+ o balance assertions will not see any account balances from previous files If you need those, either use the include directive, or concatenate the@@ -312,8 +317,8 @@ Smart dates hledger's user interfaces accept a flexible "smart date" syntax (unlike- dates in the journal file). Smart dates allow some english words, can- be relative to today's date, and can have less-significant date parts+ dates in the journal file). Smart dates allow some english words, can+ be relative to today's date, and can have less-significant date parts omitted (defaulting to 1). Examples:@@ -321,14 +326,15 @@ 2009/1/1, 2009/01/01, simple dates, several sep- 2009-1-1, 2009.1.1 arators allowed- 2009/1, 2009 same as above - a missing+ 2009/1, 2009 same as above - a missing day or month defaults to 1- 1/1, january, jan, relative dates, meaning- this year january 1 of the current+ 1/1, january, jan, relative dates, meaning+ this year january 1 of the current year next year january 1 of next year this month the 1st of the current month+ this week the most recent monday last week the monday of the week before this one@@ -336,16 +342,16 @@ today, yesterday, tomorrow Report start & end date- Most hledger reports show the full span of time represented by the+ Most hledger reports show the full span of time represented by the journal data, by default. So, the effective report start and end dates- will be the earliest and latest transaction or posting dates found in+ will be the earliest and latest transaction or posting dates found in the journal. - Often you will want to see a shorter time span, such as the current- month. You can specify a start and/or end date using -b/--begin,+ Often you will want to see a shorter time span, such as the current+ month. You can specify a start and/or end date using -b/--begin, -e/--end, -p/--period or a date: query (described below). All of these- accept the smart date syntax. One important thing to be aware of when- specifying end dates: as in Ledger, end dates are exclusive, so you+ accept the smart date syntax. One important thing to be aware of when+ specifying end dates: as in Ledger, end dates are exclusive, so you need to write the date after the last day you want to include. Examples:@@ -355,10 +361,10 @@ day 2016 -e 12/1 end at the start of decem- ber 1st of the current- year (11/30 will be the+ year (11/30 will be the last date included)- -b thismonth all transactions on or- after the 1st of the cur-+ -b thismonth all transactions on or+ after the 1st of the cur- rent month -p thismonth all transactions in the current month@@ -370,24 +376,24 @@ Report intervals A report interval can be specified so that commands like register, bal-- ance and activity will divide their reports into multiple subperiods.- The basic intervals can be selected with one of -D/--daily,- -W/--weekly, -M/--monthly, -Q/--quarterly, or -Y/--yearly. More com-- plex intervals may be specified with a period expression. Report+ ance and activity will divide their reports into multiple subperiods.+ The basic intervals can be selected with one of -D/--daily,+ -W/--weekly, -M/--monthly, -Q/--quarterly, or -Y/--yearly. More com-+ plex intervals may be specified with a period expression. Report intervals can not be specified with a query, currently. Period expressions- The -p/--period option accepts period expressions, a shorthand way of- expressing a start date, end date, and/or report interval all at once.+ The -p/--period option accepts period expressions, a shorthand way of+ expressing a start date, end date, and/or report interval all at once. - Here's a basic period expression specifying the first quarter of 2009.- Note, hledger always treats start dates as inclusive and end dates as+ Here's a basic period expression specifying the first quarter of 2009.+ Note, hledger always treats start dates as inclusive and end dates as exclusive: -p "from 2009/1/1 to 2009/4/1" - Keywords like "from" and "to" are optional, and so are the spaces, as- long as you don't run two dates together. "to" can also be written as+ Keywords like "from" and "to" are optional, and so are the spaces, as+ long as you don't run two dates together. "to" can also be written as "-". These are equivalent to the above: @@ -395,7 +401,7 @@ -p2009/1/1to2009/4/1 -p2009/1/1-2009/4/1 - Dates are smart dates, so if the current year is 2009, the above can+ Dates are smart dates, so if the current year is 2009, the above can also be written as: @@ -411,25 +417,25 @@ 1, 2009 -p "from 2009/1" the same -p "from 2009" the same- -p "to 2009" everything before january+ -p "to 2009" everything before january 1, 2009 - A single date with no "from" or "to" defines both the start and end+ A single date with no "from" or "to" defines both the start and end date like so: - -p "2009" the year 2009; equivalent+ -p "2009" the year 2009; equivalent to "2009/1/1 to 2010/1/1"- -p "2009/1" the month of jan; equiva-+ -p "2009/1" the month of jan; equiva- lent to "2009/1/1 to 2009/2/1"- -p "2009/1/1" just that day; equivalent+ -p "2009/1/1" just that day; equivalent to "2009/1/1 to 2009/1/2" - The argument of -p can also begin with, or be, a report interval- expression. The basic report intervals are daily, weekly, monthly,+ The argument of -p can also begin with, or be, a report interval+ expression. The basic report intervals are daily, weekly, monthly, quarterly, or yearly, which have the same effect as the -D,-W,-M,-Q, or- -Y flags. Between report interval and start/end dates (if any), the+ -Y flags. Between report interval and start/end dates (if any), the word in is optional. Examples: @@ -438,7 +444,7 @@ -p "quarterly" The following more complex report intervals are also supported:- biweekly, bimonthly, every N days|weeks|months|quarters|years,+ biweekly, bimonthly, every N days|weeks|months|quarters|years, every Nth day [of month], every Nth day of week. Examples:@@ -448,35 +454,34 @@ -p "every 2 weeks" -p "every 5 days from 1/3" - Show historical balances at end of 15th each month (N is exclusive end+ Show historical balances at end of 15th each month (N is exclusive end date): hledger balance -H -p "every 16th day" - Group postings from start of wednesday to end of next tuesday (N is+ Group postings from start of wednesday to end of next tuesday (N is start date and exclusive end date): hledger register checking -p "every 3rd day of week" Depth limiting- With the --depth N option, commands like account, balance and register- will show only the uppermost accounts in the account tree, down to- level N. Use this when you want a summary with less detail.+ With the --depth N option (short form: -N), commands like account, bal-+ ance and register will show only the uppermost accounts in the account+ tree, down to level N. Use this when you want a summary with less+ detail. This flag has the same effect as a depth: query argument (so+ -2, --depth=2 or depth:2 are basically equivalent). Pivoting Normally hledger sums amounts, and organizes them in a hierarchy, based- on account name. The --pivot TAGNAME option causes it to sum and orga-- nize hierarchy based on some other field instead.-- TAGNAME is the full, case-insensitive name of a tag you have defined,- or one of the built-in implicit tags (like code or payee). As with- account names, when tag values have multiple:colon-separated:parts- hledger will build hierarchy, displayed in tree-mode reports, summaris-- able with a depth limit, and so on.+ on account name. The --pivot FIELD option causes it to sum and orga-+ nize hierarchy based on the value of some other field instead. FIELD+ can be: code, description, payee, note, or the full name (case insensi-+ tive) of any tag. As with account names, values containing colon:sepa-+ rated:parts will be displayed hierarchically in reports. --pivot is a general option affecting all reports; you can think of hledger transforming the journal before any other processing, replacing- every posting's account name with the value of the specified tag on+ every posting's account name with the value of the specified field on that posting, inheriting it from the transaction or using a blank value if it's not present. @@ -613,11 +618,12 @@ sion, written as arguments after the command name, to filter the data by date, account name or other criteria. The syntax is similar to a web search: one or more space-separated search terms, quotes to enclose- whitespace, optional prefixes to match specific fields. Multiple- search terms are combined as follows:+ whitespace, prefixes to match specific fields, a not: prefix to negate+ the match. - All commands except print: show transactions/postings/accounts which- match (or negatively match)+ We do not yet support arbitrary boolean combinations of search terms;+ instead most commands show transactions/postings/accounts which match+ (or negatively match): o any of the description terms AND @@ -627,7 +633,7 @@ o all the other terms. - The print command: show transactions which+ The print command instead shows transactions which: o match any of the description terms AND @@ -637,50 +643,60 @@ o match all the other terms. - The following kinds of search terms can be used:+ The following kinds of search terms can be used. Remember these can+ also be prefixed with not:, eg to exclude a particular subaccount. - REGEX match account names by this regular expression+ REGEX match account names by this regular expression. (No prefix is+ equivalent to acct:). acct:REGEX same as above amt:N, amt:<N, amt:<=N, amt:>N, amt:>=N- match postings with a single-commodity amount that is equal to,- less than, or greater than N. (Multi-commodity amounts are not+ match postings with a single-commodity amount that is equal to,+ less than, or greater than N. (Multi-commodity amounts are not tested, and will always match.) The comparison has two modes: if N is preceded by a + or - sign (or is 0), the two signed numbers- are compared. Otherwise, the absolute magnitudes are compared,+ are compared. Otherwise, the absolute magnitudes are compared, ignoring sign. code:REGEX match by transaction code (eg check number) cur:REGEX- match postings or transactions including any amounts whose cur-- rency/commodity symbol is fully matched by REGEX. (For a par-+ match postings or transactions including any amounts whose cur-+ rency/commodity symbol is fully matched by REGEX. (For a par- tial match, use .*REGEX.*). Note, to match characters which are regex-significant, like the dollar sign ($), you need to prepend- \. And when using the command line you need to add one more+ \. And when using the command line you need to add one more level of quoting to hide it from the shell, so eg do: hledger print cur:'\$' or hledger print cur:\\$. desc:REGEX- match transaction descriptions+ match transaction descriptions. date:PERIODEXPR match dates within the specified period. PERIODEXPR is a period- expression (with no report interval). Examples: date:2016,- date:thismonth, date:2000/2/1-2/15, date:lastweek-. If the- --date2 command line flag is present, this matches secondary+ expression (with no report interval). Examples: date:2016,+ date:thismonth, date:2000/2/1-2/15, date:lastweek-. If the+ --date2 command line flag is present, this matches secondary dates instead. date2:PERIODEXPR match secondary dates within the specified period. depth:N- match (or display, depending on command) accounts at or above+ match (or display, depending on command) accounts at or above this depth + note:REGEX+ match transaction notes (part of description right of |, or+ whole description when there's no |)++ payee:REGEX+ match transaction payee/payer names (part of description left of+ |, or whole description when there's no |)+ real:, real:0 match real or virtual postings respectively @@ -688,40 +704,42 @@ match unmarked, pending, or cleared transactions respectively tag:REGEX[=REGEX]- match by tag name, and optionally also by tag value. Note a- tag: query is considered to match a transaction if it matches- any of the postings. Also remember that postings inherit the+ match by tag name, and optionally also by tag value. Note a+ tag: query is considered to match a transaction if it matches+ any of the postings. Also remember that postings inherit the tags of their parent transaction. - not: before any of the above negates the match.+ The following special search term is used automatically in hledger-web,+ only: inacct:ACCTNAME- a special term used automatically when you click an account name- in hledger-web, specifying the account register we are currently- in (selects the transactions of that account and how to show- them, can be filtered further with acct etc). Not supported- elsewhere in hledger.+ tells hledger-web to show the transaction register for this+ account. Can be filtered further with acct etc. Some of these can also be expressed as command-line options (eg depth:2- is equivalent to --depth 2). Generally you can mix options and query- arguments, and the resulting query will be their intersection (perhaps+ is equivalent to --depth 2). Generally you can mix options and query+ arguments, and the resulting query will be their intersection (perhaps excluding the -p/--period option). COMMANDS- hledger provides a number of subcommands; hledger with no arguments+ hledger provides a number of subcommands; hledger with no arguments shows a list. If you install additional hledger-* packages, or if you put programs or- scripts named hledger-NAME in your PATH, these will also be listed as+ scripts named hledger-NAME in your PATH, these will also be listed as subcommands. - Run a subcommand by writing its name as first argument (eg- hledger incomestatement). You can also write any unambiguous prefix of- a command name (hledger inc), or one of the standard short aliases dis-- played in the command list (hledger is).+ Run a subcommand by writing its name as first argument (eg+ hledger incomestatement). You can also write one of the standard short+ aliases displayed in parentheses in the command list (hledger b), or+ any any unambiguous prefix of a command name (hledger inc). + Here are all the builtin commands in alphabetical order. See also+ hledger for a more organised command list, and hledger CMD -h for+ detailed command help.+ accounts- Show account names.+ Show account names. Alias: a. --tree show short account names, as a tree @@ -730,14 +748,14 @@ --drop=N in flat mode: omit N leading account name parts - This command lists all account names that are in use (ie, all the- accounts which have at least one transaction posting to them). With+ This command lists all account names that are in use (ie, all the+ accounts which have at least one transaction posting to them). With query arguments, only matched account names are shown. - It shows a flat list by default. With --tree, it uses indentation to+ It shows a flat list by default. With --tree, it uses indentation to show the account hierarchy. - In flat mode you can add --drop N to omit the first few account name+ In flat mode you can add --drop N to omit the first few account name components. Examples:@@ -780,8 +798,8 @@ activity Show an ascii barchart of posting counts per interval. - The activity command displays an ascii histogram showing transaction- counts by day, week, month or other reporting interval (by day is the+ The activity command displays an ascii histogram showing transaction+ counts by day, week, month or other reporting interval (by day is the default). With query arguments, it counts only matched transactions. $ hledger activity --quarterly@@ -794,24 +812,24 @@ Prompt for transactions and add them to the journal. --no-new-accounts- don't allow creating new accounts; helps prevent typos when+ don't allow creating new accounts; helps prevent typos when entering account names - Many hledger users edit their journals directly with a text editor, or- generate them from CSV. For more interactive data entry, there is the- add command, which prompts interactively on the console for new trans-- actions, and appends them to the journal file (if there are multiple+ Many hledger users edit their journals directly with a text editor, or+ generate them from CSV. For more interactive data entry, there is the+ add command, which prompts interactively on the console for new trans-+ actions, and appends them to the journal file (if there are multiple -f FILE options, the first file is used.) Existing transactions are not- changed. This is the only hledger command that writes to the journal+ changed. This is the only hledger command that writes to the journal file. To use it, just run hledger add and follow the prompts. You can add as- many transactions as you like; when you are finished, enter . or press+ many transactions as you like; when you are finished, enter . or press control-d or control-c to exit. Features: - o add tries to provide useful defaults, using the most similar recent+ o add tries to provide useful defaults, using the most similar recent transaction (by description) as a template. o You can also set the initial defaults with command line arguments.@@ -819,20 +837,20 @@ o Readline-style edit keys can be used during data entry. o The tab key will auto-complete whenever possible - accounts, descrip-- tions, dates (yesterday, today, tomorrow). If the input area is+ tions, dates (yesterday, today, tomorrow). If the input area is empty, it will insert the default value. - o If the journal defines a default commodity, it will be added to any+ o If the journal defines a default commodity, it will be added to any bare numbers entered. o A parenthesised transaction code may be entered following a date. o Comments and tags may be entered following a description or amount. - o If you make a mistake, enter < at any prompt to restart the transac-+ o If you make a mistake, enter < at any prompt to restart the transac- tion. - o Input prompts are displayed in a different colour when the terminal+ o Input prompts are displayed in a different colour when the terminal supports it. Example (see the tutorial for a detailed explanation):@@ -863,13 +881,13 @@ Date [2015/05/22]: <CTRL-D> $ balance- Show accounts and their balances. Alias: bal.+ Show accounts and their balances. Aliases: b, bal. --change show balance change in each period (default) --cumulative- show balance change accumulated across periods (in multicolumn+ show balance change accumulated across periods (in multicolumn reports) -H --historical@@ -904,12 +922,16 @@ select the output format. Supported formats: txt, csv. -o FILE --output-file=FILE- write output to FILE. A file extension matching one of the+ write output to FILE. A file extension matching one of the above formats selects that format. --pretty-tables Use unicode to display prettier tables. + --sort-amount+ Sort by amount (total row amount, or by average if that is dis-+ played), instead of account name (in flat mode)+ The balance command displays accounts and balances. It is hledger's most featureful and versatile command. @@ -1198,6 +1220,9 @@ --format=LINEFORMAT in single-column balance reports: use this custom line format + --sort-amount+ sort by amount instead of account name+ This command displays a simple balance sheet. It currently assumes that you have top-level accounts named asset and liability (plural forms also allowed.)@@ -1227,6 +1252,41 @@ ancesheet shows historical ending balances, which is what you need for a balance sheet; note this means it ignores report begin dates. + balancesheetequity+ Show a balance sheet including equity. Alias: bse.++ Other than showing the equity accounts, this command is exactly the+ same as the command balancesheet. Please refer to it for the available+ options.++ This command displays a balancesheet. It currently assumes that you+ have top-level accounts named asset, liability and equity (plural forms+ also allowed.)++ $ hledger balancesheetequity+ Balance Sheet With Equity++ Assets:+ $-2 assets+ $1 bank:saving+ $-3 cash+ --------------------+ $-2++ Liabilities:+ $1 liabilities:debts+ --------------------+ $1++ Equity:+ $1 equity:owner+ --------------------+ $1++ Total:+ --------------------+ 0+ cashflow Show a cashflow statement. Alias: cf. @@ -1265,10 +1325,13 @@ --format=LINEFORMAT in single-column balance reports: use this custom line format + --sort-amount+ sort by amount instead of account name+ This command displays a simple cashflow statement It shows the change in all "cash" (ie, liquid assets) accounts for the period. It cur- rently assumes that cash accounts are under a top-level account named- asset and do not contain receivable or A/R (plural forms also allowed.)+ asset and do not contain receivable, :A/R or :fixed. $ hledger cashflow Cashflow Statement@@ -1285,38 +1348,69 @@ $-1 With a reporting interval, multiple columns will be shown, one for each- report period. Normally cashflow shows changes in assets per period,- though as with multicolumn balance reports you can alter the report+ report period. Normally cashflow shows changes in assets per period,+ though as with multicolumn balance reports you can alter the report mode with --change/--cumulative/--historical. + check-dates+ Check that transactions are sorted by increasing date. With a query,+ only matched transactions' dates are checked.++ check-dupes+ Report account names having the same leaf but different prefixes. An+ example: http://stefanorodighiero.net/software/hledger-dupes.html++ equity+ Print closing/opening transactions that bring some or all account bal-+ ances to zero and back. Can be useful for bringing account balances+ across file boundaries.+ help Show any of the hledger manuals. - The help command displays any of the main hledger man pages. (Unlike- hledger --help, which displays only the hledger man page.) Run it with- no arguments to list available topics (their names are shortened for- easier typing), and run hledger help TOPIC to select one. The output- is similar to a man page, but fixed width. It may be long, so you may- wish to pipe it into a pager. See also info and man.-- $ hledger help- Choose a topic, eg: hledger help cli- cli, ui, web, api, journal, csv, timeclock, timedot+ The help command displays any of the main hledger manuals, in one of+ several ways. Run it with no argument to list the manuals, or provide+ a full or partial manual name to select one. - $ hledger help cli | less+ hledger manuals are available in several formats. hledger help will+ use the first of these display methods that it finds: info, man,+ $PAGER, less, stdout (or when non-interactive, just stdout). You can+ force a particular viewer with the --info, --man, --pager, --cat flags. - hledger(1) hledger User Manuals hledger(1)+ $ hledger help+ Please choose a manual by typing "hledger help MANUAL" (a substring is ok).+ Manuals: hledger hledger-ui hledger-web hledger-api journal csv timeclock timedot + $ hledger help h --man + hledger(1) hledger User Manuals hledger(1) NAME hledger - a command-line accounting tool SYNOPSIS- hledger [-f FILE] COMMAND [OPTIONS] [CMDARGS]- hledger [-f FILE] ADDONCMD -- [OPTIONS] [CMDARGS]- :+ hledger [-f FILE] COMMAND [OPTIONS] [ARGS]+ hledger [-f FILE] ADDONCMD -- [OPTIONS] [ARGS]+ hledger + DESCRIPTION+ hledger is a cross-platform program for tracking money, time, or any+ ...++ import+ Read new transactions added to each FILE since last run, and add them+ to the main journal file.++ --dry-run+ just show the transactions to be imported++ Input files are provided as arguments, or glob patterns. So eg to add+ new transactions from all CSV files to the main journal: hledger import+ *.csv++ New transactions are detected like print --new (using .latest.FILE+ state files).+ incomestatement Show an income statement. Alias: is. @@ -1355,6 +1449,9 @@ --format=LINEFORMAT in single-column balance reports: use this custom line format + --sort-amount+ sort by amount instead of account name+ This command displays a simple income statement. It currently assumes that you have top-level accounts named income (or revenue) and expense (plural forms also allowed.)@@ -1385,43 +1482,27 @@ period, though as with multicolumn balance reports you can alter the report mode with --change/--cumulative/--historical. - info- Show any of the hledger manuals using info.-- The info command displays any of the hledger reference manuals using- the info hypertextual documentation viewer. This can be a very effi-- cient way to browse large manuals. It requires the "info" program to- be available in your PATH.-- As with help, run it with no arguments to list available topics (manu-- als).-- man- Show any of the hledger manuals using man.-- The man command displays any of the hledger reference manuals using- man, the standard documentation viewer on unix systems. This will fit- the text to your terminal width, and probably invoke a pager automati-- cally. It requires the "man" program to be available in your PATH.-- As with help, run it with no arguments to list available topics (manu-- als).+ prices+ Print all market prices from the journal. print- Show transactions from the journal.-- -x --explicit- show all amounts explicitly+ Show transactions from the journal. Aliases: p, txns. -m STR --match=STR show the transaction whose description is most similar to STR, and is most recent + --new show only newer-dated transactions added in each file since last+ run++ -x --explicit+ show all amounts explicitly+ -O FMT --output-format=FMT select the output format. Supported formats: txt, csv. -o FILE --output-file=FILE- write output to FILE. A file extension matching one of the+ write output to FILE. A file extension matching one of the above formats selects that format. $ hledger print@@ -1447,25 +1528,44 @@ assets:bank:checking $-1 The print command displays full journal entries (transactions) from the- journal file, tidily formatted.-- As of hledger 1.2, print's output is always a valid hledger journal.- However it may not preserve all original content, eg it does not print- directives or inter-transaction comments.+ journal file in date order, tidily formatted. print's output is always+ a valid hledger journal. It preserves all transaction information, but+ it does not preserve directives or inter-transaction comments - Normally, transactions' implicit/explicit amount style is preserved:- when an amount is omitted in the journal, it will be omitted in the- output. You can use the -x/--explicit flag to make all amounts- explicit, which can be useful for troubleshooting or for making your- journal more readable and robust against data entry errors. Note, in- this mode postings with a multi-commodity amount (possible with an- implicit amount in a multi-commodity transaction) will be split into- multiple single-commodity postings, for valid journal output.+ Normally, the journal entry's explicit or implicit amount style is pre-+ served. Ie when an amount is omitted in the journal, it will be omit-+ ted in the output. You can use the -x/--explicit flag to make all+ amounts explicit, which can be useful for troubleshooting or for making+ your journal more readable and robust against data entry errors. Note,+ -x will cause postings with a multi-commodity amount (these can arise+ when a multi-commodity transaction has an implicit amount) will be+ split into multiple single-commodity postings, for valid journal out-+ put. With -B/--cost, amounts with transaction prices are converted to cost- (using the transaction price).+ using that price. - The print command also supports output destination and CSV output.+ With -m/--match and a STR argument, print will show at most one trans-+ action: the one one whose description is most similar to STR, and is+ most recent. STR should contain at least two characters. If there is+ no similar-enough match, no transaction will be shown.++ With --new, for each FILE being read, hledger reads (and writes) a spe-+ cial state file (.latest.FILE in the same directory), containing the+ latest transaction date(s) that were seen last time FILE was read.+ When this file is found, only transactions with newer dates (and new+ transactions on the latest date) are printed. This is useful for+ ignoring already-seen entries in import data, such as downloaded CSV+ files. Eg:++ $ hledger -f bank1.csv print --new+ # shows transactions added since last print --new on this file++ This assumes that transactions added to FILE always have same or+ increasing dates, and that transactions on the same day do not get+ reordered. See also the import command.++ The print command also supports output destination and CSV output. Here's an example of print's CSV output: $ hledger print -Ocsv@@ -1482,30 +1582,33 @@ "5","2008/12/31","","*","","pay off","","liabilities:debts","1","$","","1","","" "5","2008/12/31","","*","","pay off","","assets:bank:checking","-1","$","1","","","" - o There is one CSV record per posting, with the parent transaction's+ o There is one CSV record per posting, with the parent transaction's fields repeated. o The "txnidx" (transaction index) field shows which postings belong to- the same transaction. (This number might change if transactions are- reordered within the file, files are parsed/included in a different+ the same transaction. (This number might change if transactions are+ reordered within the file, files are parsed/included in a different order, etc.) - o The amount is separated into "commodity" (the symbol) and "amount"+ o The amount is separated into "commodity" (the symbol) and "amount" (numeric quantity) fields. o The numeric amount is repeated in either the "credit" or "debit" col-- umn, for convenience. (Those names are not accurate in the account-- ing sense; it just puts negative amounts under credit and zero or+ umn, for convenience. (Those names are not accurate in the account-+ ing sense; it just puts negative amounts under credit and zero or greater amounts under debit.) + print-unique+ Print transactions which do not reuse an already-seen description.+ register- Show postings and their running total. Alias: reg.+ Show postings and their running total. Aliases: r, reg. --cumulative show running total from report start date (default) -H --historical- show historical running total/balance (includes postings before+ show historical running total/balance (includes postings before report start date) -A --average@@ -1516,18 +1619,18 @@ show postings' siblings instead -w N --width=N- set output width (default: terminal width or COLUMNS. -wN,M+ set output width (default: terminal width or COLUMNS. -wN,M sets description width as well) -O FMT --output-format=FMT select the output format. Supported formats: txt, csv. -o FILE --output-file=FILE- write output to FILE. A file extension matching one of the+ write output to FILE. A file extension matching one of the above formats selects that format. The register command displays postings, one per line, and their running- total. This is typically used with a query selecting a particular+ total. This is typically used with a query selecting a particular account, to see that account's activity: $ hledger register checking@@ -1536,8 +1639,8 @@ 2008/06/02 save assets:bank:checking $-1 $1 2008/12/31 pay off assets:bank:checking $-1 0 - The --historical/-H flag adds the balance from any undisplayed prior- postings to the running total. This is useful when you want to see+ The --historical/-H flag adds the balance from any undisplayed prior+ postings to the running total. This is useful when you want to see only recent activity, with a historically accurate running balance: $ hledger register checking -b 2008/6 --historical@@ -1547,23 +1650,23 @@ The --depth option limits the amount of sub-account detail displayed. - The --average/-A flag shows the running average posting amount instead+ The --average/-A flag shows the running average posting amount instead of the running total (so, the final number displayed is the average for- the whole report period). This flag implies --empty (see below). It- is affected by --historical. It works best when showing just one+ the whole report period). This flag implies --empty (see below). It+ is affected by --historical. It works best when showing just one account and one commodity. - The --related/-r flag shows the other postings in the transactions of+ The --related/-r flag shows the other postings in the transactions of the postings which would normally be shown. - With a reporting interval, register shows summary postings, one per+ With a reporting interval, register shows summary postings, one per interval, aggregating the postings to each account: $ hledger register --monthly income 2008/01 income:salary $-1 $-1 2008/06 income:gifts $-1 $-2 - Periods with no activity, and summary postings with a zero amount, are+ Periods with no activity, and summary postings with a zero amount, are not shown by default; use the --empty/-E flag to see them: $ hledger register --monthly income -E@@ -1580,7 +1683,7 @@ 2008/11 0 $-2 2008/12 0 $-2 - Often, you'll want to see just one line per interval. The --depth+ Often, you'll want to see just one line per interval. The --depth option helps with this, causing subaccounts to be aggregated: $ hledger register --monthly assets --depth 1h@@ -1588,19 +1691,19 @@ 2008/06 assets $-1 0 2008/12 assets $-1 $-1 - Note when using report intervals, if you specify start/end dates these- will be adjusted outward if necessary to contain a whole number of- intervals. This ensures that the first and last intervals are full+ Note when using report intervals, if you specify start/end dates these+ will be adjusted outward if necessary to contain a whole number of+ intervals. This ensures that the first and last intervals are full length and comparable to the others in the report. Custom register output- register uses the full terminal width by default, except on windows.- You can override this by setting the COLUMNS environment variable (not+ register uses the full terminal width by default, except on windows.+ You can override this by setting the COLUMNS environment variable (not a bash shell variable) or by using the --width/-w option. - The description and account columns normally share the space equally- (about half of (width - 40) each). You can adjust this by adding a- description width as part of --width's argument, comma-separated:+ The description and account columns normally share the space equally+ (about half of (width - 40) each). You can adjust this by adding a+ description width as part of --width's argument, comma-separated: --width W,D . Here's a diagram: <--------------------------------- width (W) ---------------------------------->@@ -1616,14 +1719,22 @@ $ hledger reg -w 100,40 # set overall width 100, description width 40 $ hledger reg -w $COLUMNS,40 # use terminal width, and set description width - The register command also supports the -o/--output-file and -O/--out-+ The register command also supports the -o/--output-file and -O/--out- put-format options for controlling output destination and CSV output. + register-match+ Print the one posting whose transaction description is closest to DESC,+ in the style of the register command. Helps ledger-autosync detect+ already-seen transactions when importing.++ rewrite+ Print all transactions, adding custom postings to the matched ones.+ stats Show some journal statistics. -o FILE --output-file=FILE- write output to FILE. A file extension matching one of the+ write output to FILE. A file extension matching one of the above formats selects that format. $ hledger stats@@ -1638,47 +1749,50 @@ Accounts : 8 (depth 3) Commodities : 1 ($) - The stats command displays summary information for the whole journal,- or a matched part of it. With a reporting interval, it shows a report+ The stats command displays summary information for the whole journal,+ or a matched part of it. With a reporting interval, it shows a report for each report period. The stats command also supports -o/--output-file for controlling output destination. + tags+ List all the tag names in use.+ test Run built-in unit tests. $ hledger test Cases: 74 Tried: 74 Errors: 0 Failures: 0 - This command runs hledger's built-in unit tests and displays a quick+ This command runs hledger's built-in unit tests and displays a quick report. With a regular expression argument, it selects only tests with matching names. It's mainly used in development, but it's also nice to be able to check your hledger executable for smoke at any time. ADD-ON COMMANDS- hledger also searches for external add-on commands, and will include+ hledger also searches for external add-on commands, and will include these in the commands list. These are programs or scripts in your PATH- whose name starts with hledger- and ends with a recognised file exten-+ whose name starts with hledger- and ends with a recognised file exten- sion (currently: no extension, bat,com,exe, hs,lhs,pl,py,rb,rkt,sh). - Add-ons can be invoked like any hledger command, but there are a few+ Add-ons can be invoked like any hledger command, but there are a few things to be aware of. Eg if the hledger-web add-on is installed, o hledger -h web shows hledger's help, while hledger web -h shows hledger-web's help. - o Flags specific to the add-on must have a preceding -- to hide them- from hledger. So hledger web --serve --port 9000 will be rejected;+ o Flags specific to the add-on must have a preceding -- to hide them+ from hledger. So hledger web --serve --port 9000 will be rejected; you must use hledger web -- --serve --port 9000. - o You can always run add-ons directly if preferred:+ o You can always run add-ons directly if preferred: hledger-web --serve --port 9000. - Add-ons are a relatively easy way to add local features or experiment- with new ideas. They can be written in any language, but haskell- scripts have a big advantage: they can use the same hledger (and- haskell) library functions that built-in commands do, for command-line+ Add-ons are a relatively easy way to add local features or experiment+ with new ideas. They can be written in any language, but haskell+ scripts have a big advantage: they can use the same hledger (and+ haskell) library functions that built-in commands do, for command-line options, journal parsing, reporting, etc. Here are some hledger add-ons available:@@ -1696,7 +1810,7 @@ hledger-web provides a simple web interface. Third party add-ons- These are maintained separately, and usually updated shortly after a+ These are maintained separately, and usually updated shortly after a hledger release. diff@@ -1704,7 +1818,7 @@ journal file and another. iadd- hledger-iadd is a curses-style, more interactive replacement for the+ hledger-iadd is a curses-style, more interactive replacement for the add command. interest@@ -1712,19 +1826,19 @@ ing to various schemes. irr- hledger-irr calculates the internal rate of return of an investment+ hledger-irr calculates the internal rate of return of an investment account. Experimental add-ons- These are available in source form in the hledger repo's bin/ direc-+ These are available in source form in the hledger repo's bin/ direc- tory; installing them is pretty easy. They may be less mature and doc-- umented than built-in commands. Reading and tweaking these is a good+ umented than built-in commands. Reading and tweaking these is a good way to start making your own! autosync hledger-autosync is a symbolic link for easily running ledger-autosync,- if installed. ledger-autosync does deduplicating conversion of OFX- data and some CSV formats, and can also download the data if your bank+ if installed. ledger-autosync does deduplicating conversion of OFX+ data and some CSV formats, and can also download the data if your bank offers OFX Direct Connect. budget@@ -1736,48 +1850,22 @@ check hledger-check.hs checks more powerful account balance assertions. - check-dates- hledger-check-dates.hs checks that journal entries are ordered by date.-- check-dupes- hledger-check-dupes.hs checks for account names sharing the same leaf- name.-- equity- hledger-equity.hs prints balance-resetting transactions, useful for- bringing account balances across file boundaries.-- prices- hledger-prices.hs prints all prices from the journal.-- print-unique- hledger-print-unique.hs prints transactions which do not reuse an- already-seen description.-- register-match- hledger-register-match.hs helps ledger-autosync detect already-seen- transactions when importing.-- rewrite- hledger-rewrite.hs Adds one or more custom postings to matched transac-- tions.- ENVIRONMENT- COLUMNS The screen width used by the register command. Default: the+ COLUMNS The screen width used by the register command. Default: the full terminal width. LEDGER_FILE The journal file path when not specified with -f. Default:- ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour-+ ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour- nal). FILES- Reads data from one or more files in hledger journal, timeclock, time-- dot, or CSV format specified with -f, or $LEDGER_FILE, or- $HOME/.hledger.journal (on windows, perhaps+ Reads data from one or more files in hledger journal, timeclock, time-+ dot, or CSV format specified with -f, or $LEDGER_FILE, or+ $HOME/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.journal). BUGS- The need to precede addon command options with -- when invoked from+ The need to precede addon command options with -- when invoked from hledger is awkward. When input data contains non-ascii characters, a suitable system locale@@ -1790,33 +1878,33 @@ In a Cygwin/MSYS/Mintty window, the tab key is not supported in hledger add. - Not all of Ledger's journal file syntax is supported. See file format+ Not all of Ledger's journal file syntax is supported. See file format differences. - On large data files, hledger is slower and uses more memory than+ On large data files, hledger is slower and uses more memory than Ledger. TROUBLESHOOTING- Here are some issues you might encounter when you run hledger (and- remember you can also seek help from the IRC channel, mail list or bug+ Here are some issues you might encounter when you run hledger (and+ remember you can also seek help from the IRC channel, mail list or bug tracker): Successfully installed, but "No command 'hledger' found" stack and cabal install binaries into a special directory, which should- be added to your PATH environment variable. Eg on unix-like systems,+ be added to your PATH environment variable. Eg on unix-like systems, that is ~/.local/bin and ~/.cabal/bin respectively. I set a custom LEDGER_FILE, but hledger is still using the default file- LEDGER_FILE should be a real environment variable, not just a shell- variable. The command env | grep LEDGER_FILE should show it. You may+ LEDGER_FILE should be a real environment variable, not just a shell+ variable. The command env | grep LEDGER_FILE should show it. You may need to use export. Here's an explanation. - "Illegal byte sequence" or "Invalid or incomplete multibyte or wide+ "Illegal byte sequence" or "Invalid or incomplete multibyte or wide character" errors In order to handle non-ascii letters and symbols (like ), hledger needs an appropriate locale. This is usually configured system-wide; you can also configure it temporarily. The locale may need to be one that sup-- ports UTF-8, if you built hledger with GHC < 7.2 (or possibly always,+ ports UTF-8, if you built hledger with GHC < 7.2 (or possibly always, I'm not sure yet). Here's an example of setting the locale temporarily, on ubuntu@@ -1835,7 +1923,7 @@ $ echo "export LANG=en_US.UTF-8" >>~/.bash_profile $ bash --login - If we preferred to use eg fr_FR.utf8, we might have to install that+ If we preferred to use eg fr_FR.utf8, we might have to install that first: $ apt-get install language-pack-fr@@ -1856,7 +1944,7 @@ REPORTING BUGS- Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel+ Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel or hledger mail list) @@ -1870,7 +1958,7 @@ SEE ALSO- hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1),+ hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1), hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time- dot(5), ledger(1) @@ -1878,4 +1966,4 @@ -hledger 1.3.1 August 2017 hledger(1)+hledger 1.4 September 2017 hledger(1)
doc/other/hledger-api.1 view
@@ -1,5 +1,5 @@ -.TH "hledger\-api" "1" "August 2017" "hledger\-api 1.3.1" "hledger User Manuals"+.TH "hledger\-api" "1" "September 2017" "hledger\-api 1.4" "hledger User Manuals" @@ -79,23 +79,8 @@ .RS .RE .TP-.B \f[C]\-h\f[]+.B \f[C]\-h\ \-\-help\f[] show usage-.RS-.RE-.TP-.B \f[C]\-\-help\f[]-show manual as plain text-.RS-.RE-.TP-.B \f[C]\-\-man\f[]-show manual with man-.RS-.RE-.TP-.B \f[C]\-\-info\f[]-show manual with info .RS .RE .SH ENVIRONMENT
doc/other/hledger-api.1.info view
@@ -3,8 +3,8 @@ File: hledger-api.1.info, Node: Top, Next: OPTIONS, Up: (dir) -hledger-api(1) hledger-api 1.3.1-********************************+hledger-api(1) hledger-api 1.4+****************************** hledger-api is a simple web API server, intended to support client-side web apps operating on hledger data. It comes with a series of simple@@ -57,23 +57,14 @@ '--version' show version-'-h'+'-h --help' show usage-'--help' - show manual as plain text-'--man'-- show manual with man-'--info'-- show manual with info- Tag Table: Node: Top74-Node: OPTIONS1224-Ref: #options1311+Node: OPTIONS1220+Ref: #options1307 End Tag Table
doc/other/hledger-api.1.txt view
@@ -59,13 +59,8 @@ --version show version - -h show usage-- --help show manual as plain text-- --man show manual with man-- --info show manual with info+ -h --help+ show usage ENVIRONMENT LEDGER_FILE The journal file path when not specified with -f. Default:@@ -107,4 +102,4 @@ -hledger-api 1.3.1 August 2017 hledger-api(1)+hledger-api 1.4 September 2017 hledger-api(1)
doc/other/hledger-ui.1 view
@@ -1,5 +1,5 @@ -.TH "hledger\-ui" "1" "August 2017" "hledger\-ui 1.3.1" "hledger User Manuals"+.TH "hledger\-ui" "1" "September 2017" "hledger\-ui 1.4" "hledger User Manuals" @@ -88,8 +88,8 @@ .RS .RE .TP-.B \f[C]\-\-pivot\ TAGNAME\f[]-use some other field/tag for account names+.B \f[C]\-\-pivot\ FIELDNAME\f[]+use some other field or tag for the account name .RS .RE .TP@@ -142,7 +142,7 @@ .RE .TP .B \f[C]\-\-date2\f[]-show, and match with \-b/\-e/\-p/date:, secondary dates instead+match the secondary date instead (see command help for other effects) .RS .RE .TP@@ -166,8 +166,8 @@ .RS .RE .TP-.B \f[C]\-\-depth=N\f[]-hide accounts/postings deeper than N+.B \f[C]\-NUM\ \-\-depth=NUM\f[]+hide/aggregate accounts or postings more than NUM levels deep .RS .RE .TP@@ -188,29 +188,18 @@ .RS .RE .PP+When a reporting option appears more than once in the command line, the+last one takes precedence.+.PP+Some reporting options can also be written as query arguments.+.PP hledger help options: .TP-.B \f[C]\-h\f[]+.B \f[C]\-h\ \-\-help\f[] show general usage (or after COMMAND, command usage) .RS .RE .TP-.B \f[C]\-\-help\f[]-show this program\[aq]s manual as plain text (or after an add\-on-COMMAND, the add\-on\[aq]s manual)-.RS-.RE-.TP-.B \f[C]\-\-man\f[]-show this program\[aq]s manual with man-.RS-.RE-.TP-.B \f[C]\-\-info\f[]-show this program\[aq]s manual with info-.RS-.RE-.TP .B \f[C]\-\-version\f[] show version .RS@@ -220,6 +209,10 @@ show debug output (levels 1\-9, default: 1) .RS .RE+.PP+A \@FILE argument will be expanded to the contents of FILE, which should+contain one command line option/argument per line.+(To prevent this, insert a \f[C]\-\-\f[] argument before.) .SH KEYS .PP \f[C]?\f[] shows a help dialog listing all keys.
doc/other/hledger-ui.1.info view
@@ -3,8 +3,8 @@ File: hledger-ui.1.info, Node: Top, Next: OPTIONS, Up: (dir) -hledger-ui(1) hledger-ui 1.3.1-******************************+hledger-ui(1) hledger-ui 1.4+**************************** hledger-ui is hledger's curses-style interface, providing an efficient full-window text UI for viewing accounts and transactions, and some@@ -67,9 +67,9 @@ '--anon' anonymize accounts and payees-'--pivot TAGNAME'+'--pivot FIELDNAME' - use some other field/tag for account names+ use some other field or tag for the account name '-I --ignore-assertions' ignore any failing balance assertions@@ -103,7 +103,8 @@ (overrides the flags above) '--date2' - show, and match with -b/-e/-p/date:, secondary dates instead+ match the secondary date instead (see command help for other+ effects) '-U --unmarked' include only unmarked postings/txns (can combine with -P or -C)@@ -116,9 +117,9 @@ '-R --real' include only non-virtual postings-'--depth=N'+'-NUM --depth=NUM' - hide accounts/postings deeper than N+ hide/aggregate accounts or postings more than NUM levels deep '-E --empty' show items with zero amount, normally hidden@@ -131,21 +132,16 @@ convert amounts to their market value on the report end date (using the most recent applicable market price, if any) - hledger help options:--'-h'+ When a reporting option appears more than once in the command line,+the last one takes precedence. - show general usage (or after COMMAND, command usage)-'--help'+ Some reporting options can also be written as query arguments. - show this program's manual as plain text (or after an add-on- COMMAND, the add-on's manual)-'--man'+ hledger help options: - show this program's manual with man-'--info'+'-h --help' - show this program's manual with info+ show general usage (or after COMMAND, command usage) '--version' show version@@ -153,6 +149,10 @@ show debug output (levels 1-9, default: 1) + A @FILE argument will be expanded to the contents of FILE, which+should contain one command line option/argument per line. (To prevent+this, insert a '--' argument before.)+ File: hledger-ui.1.info, Node: KEYS, Next: SCREENS, Prev: OPTIONS, Up: Top @@ -365,19 +365,19 @@ Tag Table: Node: Top73-Node: OPTIONS829-Ref: #options928-Node: KEYS3669-Ref: #keys3766-Node: SCREENS6562-Ref: #screens6649-Node: Accounts screen6739-Ref: #accounts-screen6869-Node: Register screen9099-Ref: #register-screen9256-Node: Transaction screen11330-Ref: #transaction-screen11490-Node: Error screen12360-Ref: #error-screen12484+Node: OPTIONS825+Ref: #options924+Node: KEYS3861+Ref: #keys3958+Node: SCREENS6754+Ref: #screens6841+Node: Accounts screen6931+Ref: #accounts-screen7061+Node: Register screen9291+Ref: #register-screen9448+Node: Transaction screen11522+Ref: #transaction-screen11682+Node: Error screen12552+Ref: #error-screen12676 End Tag Table
doc/other/hledger-ui.1.txt view
@@ -65,8 +65,8 @@ --anon anonymize accounts and payees - --pivot TAGNAME- use some other field/tag for account names+ --pivot FIELDNAME+ use some other field or tag for the account name -I --ignore-assertions ignore any failing balance assertions@@ -99,7 +99,8 @@ (overrides the flags above) --date2- show, and match with -b/-e/-p/date:, secondary dates instead+ match the secondary date instead (see command help for other+ effects) -U --unmarked include only unmarked postings/txns (can combine with -P or -C)@@ -113,30 +114,29 @@ -R --real include only non-virtual postings - --depth=N- hide accounts/postings deeper than N+ -NUM --depth=NUM+ hide/aggregate accounts or postings more than NUM levels deep -E --empty show items with zero amount, normally hidden -B --cost- convert amounts to their cost at transaction time (using the+ convert amounts to their cost at transaction time (using the transaction price, if any) -V --value- convert amounts to their market value on the report end date+ convert amounts to their market value on the report end date (using the most recent applicable market price, if any) - hledger help options:-- -h show general usage (or after COMMAND, command usage)+ When a reporting option appears more than once in the command line, the+ last one takes precedence. - --help show this program's manual as plain text (or after an add-on- COMMAND, the add-on's manual)+ Some reporting options can also be written as query arguments. - --man show this program's manual with man+ hledger help options: - --info show this program's manual with info+ -h --help+ show general usage (or after COMMAND, command usage) --version show version@@ -144,57 +144,61 @@ --debug[=N] show debug output (levels 1-9, default: 1) + A @FILE argument will be expanded to the contents of FILE, which should+ contain one command line option/argument per line. (To prevent this,+ insert a -- argument before.)+ KEYS- ? shows a help dialog listing all keys. (Some of these also appear in+ ? shows a help dialog listing all keys. (Some of these also appear in the quick help at the bottom of each screen.) Press ? again (or ESCAPE, or LEFT) to close it. The following keys work on most screens: The cursor keys navigate: right (or enter) goes deeper, left returns to- the previous screen, up/down/page up/page down/home/end move up and- down through lists. Vi-style (h/j/k/l) and Emacs-style+ the previous screen, up/down/page up/page down/home/end move up and+ down through lists. Vi-style (h/j/k/l) and Emacs-style (CTRL-p/CTRL-n/CTRL-f/CTRL-b) movement keys are also supported. A tip:- movement speed is limited by your keyboard repeat rate, to move faster- you may want to adjust it. (If you're on a mac, the Karabiner app is+ movement speed is limited by your keyboard repeat rate, to move faster+ you may want to adjust it. (If you're on a mac, the Karabiner app is one way to do that.) - With shift pressed, the cursor keys adjust the report period, limiting- the transactions to be shown (by default, all are shown).- shift-down/up steps downward and upward through these standard report+ With shift pressed, the cursor keys adjust the report period, limiting+ the transactions to be shown (by default, all are shown).+ shift-down/up steps downward and upward through these standard report period durations: year, quarter, month, week, day. Then,- shift-left/right moves to the previous/next period. t sets the report- period to today. With the --watch option, when viewing a "current"- period (the current day, week, month, quarter, or year), the period- will move automatically to track the current date. To set a non-stan-+ shift-left/right moves to the previous/next period. t sets the report+ period to today. With the --watch option, when viewing a "current"+ period (the current day, week, month, quarter, or year), the period+ will move automatically to track the current date. To set a non-stan- dard period, you can use / and a date: query. - / lets you set a general filter query limiting the data shown, using- the same query terms as in hledger and hledger-web. While editing the- query, you can use CTRL-a/e/d/k, BS, cursor keys; press ENTER to set+ / lets you set a general filter query limiting the data shown, using+ the same query terms as in hledger and hledger-web. While editing the+ query, you can use CTRL-a/e/d/k, BS, cursor keys; press ENTER to set it, or ESCAPEto cancel. There are also keys for quickly adjusting some- common filters like account depth and transaction status (see below).+ common filters like account depth and transaction status (see below). BACKSPACE or DELETE removes all filters, showing all transactions. - ESCAPE removes all filters and jumps back to the top screen. Or, it+ ESCAPE removes all filters and jumps back to the top screen. Or, it cancels a minibuffer edit or help dialog in progress. CTRL-l redraws the screen and centers the selection if possible (selec-- tions near the top won't be centered, since we don't scroll above the+ tions near the top won't be centered, since we don't scroll above the top). - g reloads from the data file(s) and updates the current screen and any- previous screens. (With large files, this could cause a noticeable+ g reloads from the data file(s) and updates the current screen and any+ previous screens. (With large files, this could cause a noticeable pause.) - I toggles balance assertion checking. Disabling balance assertions+ I toggles balance assertion checking. Disabling balance assertions temporarily can be useful for troubleshooting. - a runs command-line hledger's add command, and reloads the updated+ a runs command-line hledger's add command, and reloads the updated file. This allows some basic data entry. - E runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default (emac-+ E runs $HLEDGER_UI_EDITOR, or $EDITOR, or a default (emac- sclient -a "" -nw) on the journal file. With some editors (emacs, vi),- the cursor will be positioned at the current transaction when invoked- from the register and transaction screens, and at the error location+ the cursor will be positioned at the current transaction when invoked+ from the register and transaction screens, and at the error location (if possible) when invoked from the error screen. q quits the application.@@ -203,44 +207,44 @@ SCREENS Accounts screen- This is normally the first screen displayed. It lists accounts and- their balances, like hledger's balance command. By default, it shows- all accounts and their latest ending balances (including the balances- of subaccounts). if you specify a query on the command line, it shows+ This is normally the first screen displayed. It lists accounts and+ their balances, like hledger's balance command. By default, it shows+ all accounts and their latest ending balances (including the balances+ of subaccounts). if you specify a query on the command line, it shows just the matched accounts and the balances from matched transactions. - Account names are normally indented to show the hierarchy (tree mode).+ Account names are normally indented to show the hierarchy (tree mode). To see less detail, set a depth limit by pressing a number key, 1 to 9. 0 shows even less detail, collapsing all accounts to a single total. -- and + (or =) decrease and increase the depth limit. To remove the- depth limit, set it higher than the maximum account depth, or press+ and + (or =) decrease and increase the depth limit. To remove the+ depth limit, set it higher than the maximum account depth, or press ESCAPE. - F toggles flat mode, in which accounts are shown as a flat list, with- their full names. In this mode, account balances exclude subaccounts,- except for accounts at the depth limit (as with hledger's balance com-+ F toggles flat mode, in which accounts are shown as a flat list, with+ their full names. In this mode, account balances exclude subaccounts,+ except for accounts at the depth limit (as with hledger's balance com- mand). H toggles between showing historical balances or period balances. His-- torical balances (the default) are ending balances at the end of the- report period, taking into account all transactions before that date- (filtered by the filter query if any), including transactions before- the start of the report period. In other words, historical balances- are what you would see on a bank statement for that account (unless- disturbed by a filter query). Period balances ignore transactions+ torical balances (the default) are ending balances at the end of the+ report period, taking into account all transactions before that date+ (filtered by the filter query if any), including transactions before+ the start of the report period. In other words, historical balances+ are what you would see on a bank statement for that account (unless+ disturbed by a filter query). Period balances ignore transactions before the report start date, so they show the change in balance during the report period. They are more useful eg when viewing a time log. U toggles filtering by unmarked status, including or excluding unmarked postings in the balances. Similarly, P toggles pending postings, and C- toggles cleared postings. (By default, balances include all postings;- if you activate one or two status filters, only those postings are+ toggles cleared postings. (By default, balances include all postings;+ if you activate one or two status filters, only those postings are included; and if you activate all three, the filter is removed.) R toggles real mode, in which virtual postings are ignored. - Z toggles nonzero mode, in which only accounts with nonzero balances- are shown (hledger-ui shows zero items by default, unlike command-line+ Z toggles nonzero mode, in which only accounts with nonzero balances+ are shown (hledger-ui shows zero items by default, unlike command-line hledger). Press right or enter to view an account's transactions register.@@ -249,65 +253,65 @@ This screen shows the transactions affecting a particular account, like a check register. Each line represents one transaction and shows: - o the other account(s) involved, in abbreviated form. (If there are- both real and virtual postings, it shows only the accounts affected+ o the other account(s) involved, in abbreviated form. (If there are+ both real and virtual postings, it shows only the accounts affected by real postings.) - o the overall change to the current account's balance; positive for an+ o the overall change to the current account's balance; positive for an inflow to this account, negative for an outflow. o the running historical total or period total for the current account,- after the transaction. This can be toggled with H. Similar to the- accounts screen, the historical total is affected by transactions- (filtered by the filter query) before the report start date, while+ after the transaction. This can be toggled with H. Similar to the+ accounts screen, the historical total is affected by transactions+ (filtered by the filter query) before the report start date, while the period total is not. If the historical total is not disturbed by- a filter query, it will be the running historical balance you would+ a filter query, it will be the running historical balance you would see on a bank register for the current account. - If the accounts screen was in tree mode, the register screen will+ If the accounts screen was in tree mode, the register screen will include transactions from both the current account and its subaccounts.- If the accounts screen was in flat mode, and a non-depth-clipped- account was selected, the register screen will exclude transactions+ If the accounts screen was in flat mode, and a non-depth-clipped+ account was selected, the register screen will exclude transactions from subaccounts. In other words, the register always shows the trans-- actions responsible for the period balance shown on the accounts+ actions responsible for the period balance shown on the accounts screen. As on the accounts screen, this can be toggled with F. - U toggles filtering by unmarked status, showing or hiding unmarked+ U toggles filtering by unmarked status, showing or hiding unmarked transactions. Similarly, P toggles pending transactions, and C toggles- cleared transactions. (By default, transactions with all statuses are- shown; if you activate one or two status filters, only those transac-- tions are shown; and if you activate all three, the filter is+ cleared transactions. (By default, transactions with all statuses are+ shown; if you activate one or two status filters, only those transac-+ tions are shown; and if you activate all three, the filter is removed.)q R toggles real mode, in which virtual postings are ignored. - Z toggles nonzero mode, in which only transactions posting a nonzero- change are shown (hledger-ui shows zero items by default, unlike com-+ Z toggles nonzero mode, in which only transactions posting a nonzero+ change are shown (hledger-ui shows zero items by default, unlike com- mand-line hledger). Press right (or enter) to view the selected transaction in detail. Transaction screen- This screen shows a single transaction, as a general journal entry,- similar to hledger's print command and journal format (hledger_jour-+ This screen shows a single transaction, as a general journal entry,+ similar to hledger's print command and journal format (hledger_jour- nal(5)). - The transaction's date(s) and any cleared flag, transaction code,- description, comments, along with all of its account postings are- shown. Simple transactions have two postings, but there can be more+ The transaction's date(s) and any cleared flag, transaction code,+ description, comments, along with all of its account postings are+ shown. Simple transactions have two postings, but there can be more (or in certain cases, fewer). - up and down will step through all transactions listed in the previous- account register screen. In the title bar, the numbers in parentheses- show your position within that account register. They will vary+ up and down will step through all transactions listed in the previous+ account register screen. In the title bar, the numbers in parentheses+ show your position within that account register. They will vary depending on which account register you came from (remember most trans- actions appear in multiple account registers). The #N number preceding them is the transaction's position within the complete unfiltered jour- nal, which is a more stable id (at least until the next reload). Error screen- This screen will appear if there is a problem, such as a parse error,- when you press g to reload. Once you have fixed the problem, press g+ This screen will appear if there is a problem, such as a parse error,+ when you press g to reload. Once you have fixed the problem, press g again to reload and resume normal operation. (Or, you can press escape to cancel the reload attempt.) @@ -315,17 +319,17 @@ COLUMNS The screen width to use. Default: the full terminal width. LEDGER_FILE The journal file path when not specified with -f. Default:- ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour-+ ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour- nal). FILES- Reads data from one or more files in hledger journal, timeclock, time-- dot, or CSV format specified with -f, or $LEDGER_FILE, or- $HOME/.hledger.journal (on windows, perhaps+ Reads data from one or more files in hledger journal, timeclock, time-+ dot, or CSV format specified with -f, or $LEDGER_FILE, or+ $HOME/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.journal). BUGS- The need to precede options with -- when invoked from hledger is awk-+ The need to precede options with -- when invoked from hledger is awk- ward. -f- doesn't work (hledger-ui can't read from stdin).@@ -333,13 +337,13 @@ -V affects only the accounts screen. When you press g, the current and all previous screens are regenerated,- which may cause a noticeable pause with large files. Also there is no+ which may cause a noticeable pause with large files. Also there is no visual indication that this is in progress. - --watch is not yet fully robust. It works well for normal usage, but- many file changes in a short time (eg saving the file thousands of- times with an editor macro) can cause problems at least on OSX. Symp-- toms include: unresponsive UI, periodic resetting of the cursor posi-+ --watch is not yet fully robust. It works well for normal usage, but+ many file changes in a short time (eg saving the file thousands of+ times with an editor macro) can cause problems at least on OSX. Symp-+ toms include: unresponsive UI, periodic resetting of the cursor posi- tion, momentary display of parse errors, high CPU usage eventually sub- siding, and possibly a small but persistent build-up of CPU usage until the program is restarted.@@ -347,7 +351,7 @@ REPORTING BUGS- Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel+ Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel or hledger mail list) @@ -361,7 +365,7 @@ SEE ALSO- hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1),+ hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1), hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time- dot(5), ledger(1) @@ -369,4 +373,4 @@ -hledger-ui 1.3.1 August 2017 hledger-ui(1)+hledger-ui 1.4 September 2017 hledger-ui(1)
doc/other/hledger-web.1 view
@@ -1,5 +1,5 @@ -.TH "hledger\-web" "1" "August 2017" "hledger\-web 1.3.1" "hledger User Manuals"+.TH "hledger\-web" "1" "September 2017" "hledger\-web 1.4" "hledger User Manuals" @@ -144,8 +144,8 @@ .RS .RE .TP-.B \f[C]\-\-pivot\ TAGNAME\f[]-use some other field/tag for account names+.B \f[C]\-\-pivot\ FIELDNAME\f[]+use some other field or tag for the account name .RS .RE .TP@@ -198,7 +198,7 @@ .RE .TP .B \f[C]\-\-date2\f[]-show, and match with \-b/\-e/\-p/date:, secondary dates instead+match the secondary date instead (see command help for other effects) .RS .RE .TP@@ -222,8 +222,8 @@ .RS .RE .TP-.B \f[C]\-\-depth=N\f[]-hide accounts/postings deeper than N+.B \f[C]\-NUM\ \-\-depth=NUM\f[]+hide/aggregate accounts or postings more than NUM levels deep .RS .RE .TP@@ -244,29 +244,18 @@ .RS .RE .PP+When a reporting option appears more than once in the command line, the+last one takes precedence.+.PP+Some reporting options can also be written as query arguments.+.PP hledger help options: .TP-.B \f[C]\-h\f[]+.B \f[C]\-h\ \-\-help\f[] show general usage (or after COMMAND, command usage) .RS .RE .TP-.B \f[C]\-\-help\f[]-show this program\[aq]s manual as plain text (or after an add\-on-COMMAND, the add\-on\[aq]s manual)-.RS-.RE-.TP-.B \f[C]\-\-man\f[]-show this program\[aq]s manual with man-.RS-.RE-.TP-.B \f[C]\-\-info\f[]-show this program\[aq]s manual with info-.RS-.RE-.TP .B \f[C]\-\-version\f[] show version .RS@@ -276,6 +265,10 @@ show debug output (levels 1\-9, default: 1) .RS .RE+.PP+A \@FILE argument will be expanded to the contents of FILE, which should+contain one command line option/argument per line.+(To prevent this, insert a \f[C]\-\-\f[] argument before.) .SH ENVIRONMENT .PP \f[B]LEDGER_FILE\f[] The journal file path when not specified with
doc/other/hledger-web.1.info view
@@ -3,8 +3,8 @@ File: hledger-web.1.info, Node: Top, Next: OPTIONS, Up: (dir) -hledger-web(1) hledger-web 1.3.1-********************************+hledger-web(1) hledger-web 1.4+****************************** hledger-web is hledger's web interface. It starts a simple web application for browsing and adding transactions, and optionally opens@@ -112,9 +112,9 @@ '--anon' anonymize accounts and payees-'--pivot TAGNAME'+'--pivot FIELDNAME' - use some other field/tag for account names+ use some other field or tag for the account name '-I --ignore-assertions' ignore any failing balance assertions@@ -148,7 +148,8 @@ (overrides the flags above) '--date2' - show, and match with -b/-e/-p/date:, secondary dates instead+ match the secondary date instead (see command help for other+ effects) '-U --unmarked' include only unmarked postings/txns (can combine with -P or -C)@@ -161,9 +162,9 @@ '-R --real' include only non-virtual postings-'--depth=N'+'-NUM --depth=NUM' - hide accounts/postings deeper than N+ hide/aggregate accounts or postings more than NUM levels deep '-E --empty' show items with zero amount, normally hidden@@ -176,21 +177,16 @@ convert amounts to their market value on the report end date (using the most recent applicable market price, if any) - hledger help options:--'-h'+ When a reporting option appears more than once in the command line,+the last one takes precedence. - show general usage (or after COMMAND, command usage)-'--help'+ Some reporting options can also be written as query arguments. - show this program's manual as plain text (or after an add-on- COMMAND, the add-on's manual)-'--man'+ hledger help options: - show this program's manual with man-'--info'+'-h --help' - show this program's manual with info+ show general usage (or after COMMAND, command usage) '--version' show version@@ -198,10 +194,14 @@ show debug output (levels 1-9, default: 1) + A @FILE argument will be expanded to the contents of FILE, which+should contain one command line option/argument per line. (To prevent+this, insert a '--' argument before.)+ Tag Table: Node: Top74-Node: OPTIONS3160-Ref: #options3247+Node: OPTIONS3156+Ref: #options3243 End Tag Table
doc/other/hledger-web.1.txt view
@@ -110,8 +110,8 @@ --anon anonymize accounts and payees - --pivot TAGNAME- use some other field/tag for account names+ --pivot FIELDNAME+ use some other field or tag for the account name -I --ignore-assertions ignore any failing balance assertions@@ -144,7 +144,8 @@ (overrides the flags above) --date2- show, and match with -b/-e/-p/date:, secondary dates instead+ match the secondary date instead (see command help for other+ effects) -U --unmarked include only unmarked postings/txns (can combine with -P or -C)@@ -158,30 +159,29 @@ -R --real include only non-virtual postings - --depth=N- hide accounts/postings deeper than N+ -NUM --depth=NUM+ hide/aggregate accounts or postings more than NUM levels deep -E --empty show items with zero amount, normally hidden -B --cost- convert amounts to their cost at transaction time (using the+ convert amounts to their cost at transaction time (using the transaction price, if any) -V --value- convert amounts to their market value on the report end date+ convert amounts to their market value on the report end date (using the most recent applicable market price, if any) - hledger help options:-- -h show general usage (or after COMMAND, command usage)+ When a reporting option appears more than once in the command line, the+ last one takes precedence. - --help show this program's manual as plain text (or after an add-on- COMMAND, the add-on's manual)+ Some reporting options can also be written as query arguments. - --man show this program's manual with man+ hledger help options: - --info show this program's manual with info+ -h --help+ show general usage (or after COMMAND, command usage) --version show version@@ -189,19 +189,23 @@ --debug[=N] show debug output (levels 1-9, default: 1) + A @FILE argument will be expanded to the contents of FILE, which should+ contain one command line option/argument per line. (To prevent this,+ insert a -- argument before.)+ ENVIRONMENT LEDGER_FILE The journal file path when not specified with -f. Default:- ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour-+ ~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour- nal). FILES- Reads data from one or more files in hledger journal, timeclock, time-- dot, or CSV format specified with -f, or $LEDGER_FILE, or- $HOME/.hledger.journal (on windows, perhaps+ Reads data from one or more files in hledger journal, timeclock, time-+ dot, or CSV format specified with -f, or $LEDGER_FILE, or+ $HOME/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.journal). BUGS- The need to precede options with -- when invoked from hledger is awk-+ The need to precede options with -- when invoked from hledger is awk- ward. -f- doesn't work (hledger-web can't read from stdin).@@ -215,7 +219,7 @@ REPORTING BUGS- Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel+ Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel or hledger mail list) @@ -229,7 +233,7 @@ SEE ALSO- hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1),+ hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1), hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time- dot(5), ledger(1) @@ -237,4 +241,4 @@ -hledger-web 1.3.1 August 2017 hledger-web(1)+hledger-web 1.4 September 2017 hledger-web(1)
doc/other/hledger_csv.5 view
@@ -1,5 +1,5 @@ -.TH "hledger_csv" "5" "August 2017" "hledger 1.3.1" "hledger User Manuals"+.TH "hledger_csv" "5" "September 2017" "hledger 1.4" "hledger User Manuals" @@ -23,7 +23,7 @@ To learn about \f[I]exporting\f[] CSV, see CSV output. .SH CSV RULES .PP-The following six kinds of rule can appear in the rules file, in any+The following seven kinds of rule can appear in the rules file, in any order. Blank lines and lines beginning with \f[C]#\f[] or \f[C];\f[] are ignored.@@ -195,32 +195,65 @@ include\ common.rules \f[] .fi+.SS newest\-first+.PP+\f[C]newest\-first\f[]+.PP+Consider adding this rule if all of the following are true: you might be+processing just one day of data, your CSV records are in reverse+chronological order (newest first), and you care about preserving the+order of same\-day transactions.+It usually isn\[aq]t needed, because hledger autodetects the CSV order,+but when all CSV records have the same date it will assume they are+oldest first. .SH CSV TIPS+.SS CSV ordering .PP-Each generated journal entry will have two postings, to-\f[C]account1\f[] and \f[C]account2\f[] respectively.-Currently it\[aq]s not possible to generate entries with more than two+The generated journal entries will be sorted by date.+The order of same\-day entries will be preserved (except in the special+case where you might need \f[C]newest\-first\f[], see above).+.SS CSV accounts+.PP+Each journal entry will have two postings, to \f[C]account1\f[] and+\f[C]account2\f[] respectively.+It\[aq]s not yet possible to generate entries with more than two postings.+It\[aq]s conventional and recommended to use \f[C]account1\f[] for the+account whose CSV we are reading.+.SS CSV amounts .PP+The \f[C]amount\f[] field sets the amount of the \f[C]account1\f[]+posting.+.PP If the CSV has debit/credit amounts in separate fields, assign to the-\f[C]amount\-in\f[] and \f[C]amount\-out\f[] pseudo fields instead of-\f[C]amount\f[].+\f[C]amount\-in\f[] and \f[C]amount\-out\f[] pseudo fields instead.+(Whichever one has a value will be used, with appropriate sign.+If both contain a value, it may not work so well.) .PP-If the CSV has the currency in a separate field, assign that to the-\f[C]currency\f[] pseudo field which will be automatically prepended to-the amount.-(Or you can do the same thing with a field assignment.)+If an amount value is parenthesised, it will be de\-parenthesised and+sign\-flipped. .PP-If the CSV includes a running balance, you can assign that to the-\f[C]balance\f[] pseudo field to generate a balance assertion on-\f[C]account1\f[] whenever the balance field is non\-empty.-(Eg to double\-check your bank\[aq]s balance calculation.)+If an amount value begins with a double minus sign, those will cancel+out and be removed. .PP-If an amount value is parenthesised, it will be de\-parenthesised and-sign\-flipped automatically.+If the CSV has the currency symbol in a separate field, assign that to+the \f[C]currency\f[] pseudo field to have it prepended to the amount.+Or, you can use a field assignment to \f[C]amount\f[] that interpolates+both CSV fields (giving more control, eg to put the currency symbol on+the right).+.SS CSV balance assertions .PP-The generated journal entries will be sorted by date.-The original order of same\-day entries will be preserved, usually.+If the CSV includes a running balance, you can assign that to the+\f[C]balance\f[] pseudo field; whenever the running balance value is+non\-empty, it will be asserted as the balance after the+\f[C]account1\f[] posting.+.SS Reading multiple CSV files+.PP+You can read multiple CSV files at once using multiple \f[C]\-f\f[]+arguments on the command line, and hledger will look for a+correspondingly\-named rules file for each.+Note if you use the \f[C]\-\-rules\-file\f[] option, this one rules file+will be used for all the CSV files being read. .SH "REPORTING BUGS"
doc/other/hledger_csv.5.info view
@@ -3,8 +3,8 @@ File: hledger_csv.5.info, Node: Top, Next: CSV RULES, Up: (dir) -hledger_csv(5) hledger 1.3.1-****************************+hledger_csv(5) hledger 1.4+************************** hledger can read CSV files, converting each CSV record into a journal entry (transaction), if you provide some conversion hints in a "rules@@ -27,7 +27,7 @@ 1 CSV RULES *********** -The following six kinds of rule can appear in the rules file, in any+The following seven kinds of rule can appear in the rules file, in any order. Blank lines and lines beginning with '#' or ';' are ignored. * Menu: @@ -37,6 +37,7 @@ * field assignment:: * conditional block:: * include::+* newest-first:: File: hledger_csv.5.info, Node: skip, Next: date-format, Up: CSV RULES@@ -156,7 +157,7 @@ comment XXX deductible ? check it -File: hledger_csv.5.info, Node: include, Prev: conditional block, Up: CSV RULES+File: hledger_csv.5.info, Node: include, Next: newest-first, Prev: conditional block, Up: CSV RULES 1.6 include ===========@@ -171,51 +172,131 @@ include common.rules +File: hledger_csv.5.info, Node: newest-first, Prev: include, Up: CSV RULES++1.7 newest-first+================++'newest-first'++ Consider adding this rule if all of the following are true: you might+be processing just one day of data, your CSV records are in reverse+chronological order (newest first), and you care about preserving the+order of same-day transactions. It usually isn't needed, because+hledger autodetects the CSV order, but when all CSV records have the+same date it will assume they are oldest first.++ File: hledger_csv.5.info, Node: CSV TIPS, Prev: CSV RULES, Up: Top 2 CSV TIPS ********** -Each generated journal entry will have two postings, to 'account1' and-'account2' respectively. Currently it's not possible to generate-entries with more than two postings.+* Menu: - If the CSV has debit/credit amounts in separate fields, assign to the-'amount-in' and 'amount-out' pseudo fields instead of 'amount'.+* CSV ordering::+* CSV accounts::+* CSV amounts::+* CSV balance assertions::+* Reading multiple CSV files:: - If the CSV has the currency in a separate field, assign that to the-'currency' pseudo field which will be automatically prepended to the-amount. (Or you can do the same thing with a field assignment.)++File: hledger_csv.5.info, Node: CSV ordering, Next: CSV accounts, Up: CSV TIPS - If the CSV includes a running balance, you can assign that to the-'balance' pseudo field to generate a balance assertion on 'account1'-whenever the balance field is non-empty. (Eg to double-check your-bank's balance calculation.)+2.1 CSV ordering+================ +The generated journal entries will be sorted by date. The order of+same-day entries will be preserved (except in the special case where you+might need 'newest-first', see above).+++File: hledger_csv.5.info, Node: CSV accounts, Next: CSV amounts, Prev: CSV ordering, Up: CSV TIPS++2.2 CSV accounts+================++Each journal entry will have two postings, to 'account1' and 'account2'+respectively. It's not yet possible to generate entries with more than+two postings. It's conventional and recommended to use 'account1' for+the account whose CSV we are reading.+++File: hledger_csv.5.info, Node: CSV amounts, Next: CSV balance assertions, Prev: CSV accounts, Up: CSV TIPS++2.3 CSV amounts+===============++The 'amount' field sets the amount of the 'account1' posting.++ If the CSV has debit/credit amounts in separate fields, assign to the+'amount-in' and 'amount-out' pseudo fields instead. (Whichever one has+a value will be used, with appropriate sign. If both contain a value,+it may not work so well.)+ If an amount value is parenthesised, it will be de-parenthesised and-sign-flipped automatically.+sign-flipped. - The generated journal entries will be sorted by date. The original-order of same-day entries will be preserved, usually.+ If an amount value begins with a double minus sign, those will cancel+out and be removed. + If the CSV has the currency symbol in a separate field, assign that+to the 'currency' pseudo field to have it prepended to the amount. Or,+you can use a field assignment to 'amount' that interpolates both CSV+fields (giving more control, eg to put the currency symbol on the+right).+ +File: hledger_csv.5.info, Node: CSV balance assertions, Next: Reading multiple CSV files, Prev: CSV amounts, Up: CSV TIPS++2.4 CSV balance assertions+==========================++If the CSV includes a running balance, you can assign that to the+'balance' pseudo field; whenever the running balance value is non-empty,+it will be asserted as the balance after the 'account1' posting.+++File: hledger_csv.5.info, Node: Reading multiple CSV files, Prev: CSV balance assertions, Up: CSV TIPS++2.5 Reading multiple CSV files+==============================++You can read multiple CSV files at once using multiple '-f' arguments on+the command line, and hledger will look for a correspondingly-named+rules file for each. Note if you use the '--rules-file' option, this+one rules file will be used for all the CSV files being read.++ Tag Table: Node: Top74-Node: CSV RULES814-Ref: #csv-rules924-Node: skip1167-Ref: #skip1263-Node: date-format1435-Ref: #date-format1564-Node: field list2070-Ref: #field-list2209-Node: field assignment2914-Ref: #field-assignment3071-Node: conditional block3575-Ref: #conditional-block3731-Node: include4627-Ref: #include4738-Node: CSV TIPS4969-Ref: #csv-tips5065+Node: CSV RULES810+Ref: #csv-rules920+Node: skip1182+Ref: #skip1278+Node: date-format1450+Ref: #date-format1579+Node: field list2085+Ref: #field-list2224+Node: field assignment2929+Ref: #field-assignment3086+Node: conditional block3590+Ref: #conditional-block3746+Node: include4642+Ref: #include4774+Node: newest-first5005+Ref: #newest-first5121+Node: CSV TIPS5532+Ref: #csv-tips5628+Node: CSV ordering5746+Ref: #csv-ordering5866+Node: CSV accounts6047+Ref: #csv-accounts6187+Node: CSV amounts6441+Ref: #csv-amounts6589+Node: CSV balance assertions7364+Ref: #csv-balance-assertions7548+Node: Reading multiple CSV files7753+Ref: #reading-multiple-csv-files7925 End Tag Table
doc/other/hledger_csv.5.txt view
@@ -19,7 +19,7 @@ To learn about exporting CSV, see CSV output. CSV RULES- The following six kinds of rule can appear in the rules file, in any+ The following seven kinds of rule can appear in the rules file, in any order. Blank lines and lines beginning with # or ; are ignored. skip@@ -123,33 +123,62 @@ # rules reused with several CSV files include common.rules + newest-first+ newest-first++ Consider adding this rule if all of the following are true: you might+ be processing just one day of data, your CSV records are in reverse+ chronological order (newest first), and you care about preserving the+ order of same-day transactions. It usually isn't needed, because+ hledger autodetects the CSV order, but when all CSV records have the+ same date it will assume they are oldest first.+ CSV TIPS- Each generated journal entry will have two postings, to account1 and- account2 respectively. Currently it's not possible to generate entries- with more than two postings.+ CSV ordering+ The generated journal entries will be sorted by date. The order of+ same-day entries will be preserved (except in the special case where+ you might need newest-first, see above). + CSV accounts+ Each journal entry will have two postings, to account1 and account2+ respectively. It's not yet possible to generate entries with more than+ two postings. It's conventional and recommended to use account1 for+ the account whose CSV we are reading.++ CSV amounts+ The amount field sets the amount of the account1 posting.+ If the CSV has debit/credit amounts in separate fields, assign to the- amount-in and amount-out pseudo fields instead of amount.+ amount-in and amount-out pseudo fields instead. (Whichever one has a+ value will be used, with appropriate sign. If both contain a value, it+ may not work so well.) - If the CSV has the currency in a separate field, assign that to the- currency pseudo field which will be automatically prepended to the- amount. (Or you can do the same thing with a field assignment.)+ If an amount value is parenthesised, it will be de-parenthesised and+ sign-flipped. - If the CSV includes a running balance, you can assign that to the bal-- ance pseudo field to generate a balance assertion on account1 whenever- the balance field is non-empty. (Eg to double-check your bank's bal-- ance calculation.)+ If an amount value begins with a double minus sign, those will cancel+ out and be removed. - If an amount value is parenthesised, it will be de-parenthesised and- sign-flipped automatically.+ If the CSV has the currency symbol in a separate field, assign that to+ the currency pseudo field to have it prepended to the amount. Or, you+ can use a field assignment to amount that interpolates both CSV fields+ (giving more control, eg to put the currency symbol on the right). - The generated journal entries will be sorted by date. The original- order of same-day entries will be preserved, usually.+ CSV balance assertions+ If the CSV includes a running balance, you can assign that to the bal-+ ance pseudo field; whenever the running balance value is non-empty, it+ will be asserted as the balance after the account1 posting. + Reading multiple CSV files+ You can read multiple CSV files at once using multiple -f arguments on+ the command line, and hledger will look for a correspondingly-named+ rules file for each. Note if you use the --rules-file option, this one+ rules file will be used for all the CSV files being read. + REPORTING BUGS- Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel+ Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel or hledger mail list) @@ -163,7 +192,7 @@ SEE ALSO- hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1),+ hledger(1), hledger-ui(1), hledger-web(1), hledger-api(1), hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time- dot(5), ledger(1) @@ -171,4 +200,4 @@ -hledger 1.3.1 August 2017 hledger_csv(5)+hledger 1.4 September 2017 hledger_csv(5)
doc/other/hledger_journal.5 view
@@ -1,6 +1,6 @@ .\"t -.TH "hledger_journal" "5" "August 2017" "hledger 1.3.1" "hledger User Manuals"+.TH "hledger_journal" "5" "September 2017" "hledger 1.4" "hledger User Manuals" @@ -53,6 +53,10 @@ \ \ \ \ expenses:supplies\ \ \ \ \ $1\ \ \ \ ;\ <\-\ this\ transaction\ debits\ two\ expense\ accounts \ \ \ \ assets:cash\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ;\ <\-\ $\-2\ inferred +2008/10/01\ take\ a\ loan+\ \ \ \ assets:bank:checking\ \ $1+\ \ \ \ liabilities:debts\ \ \ \ $\-1+ 2008/12/31\ *\ pay\ off\ \ \ \ \ \ \ \ \ \ \ \ ;\ <\-\ an\ optional\ *\ or\ !\ after\ the\ date\ means\ "cleared"\ (or\ anything\ you\ want) \ \ \ \ liabilities:debts\ \ \ \ \ $1 \ \ \ \ assets:bank:checking@@ -73,7 +77,10 @@ parentheses) .IP \[bu] 2 (optional) a transaction description (any remaining text until end of-line)+line or a semicolon)+.IP \[bu] 2+(optional) a transaction comment (any remaining text following a+semicolon until end of line) .PP Then comes zero or more (but usually at least 2) indented lines representing...@@ -296,6 +303,20 @@ at your bank, \f[C]\-U\f[] to see things which will probably hit your bank soon (like uncashed checks), and no flags to see the most up\-to\-date state of your finances.+.SS Description+.PP+A transaction\[aq]s description is the rest of the line following the+date and status mark (or until a comment begins).+Sometimes called the "narration" in traditional bookkeeping, it can be+used for whatever you wish, or left blank.+Transaction descriptions can be queried, unlike comments.+.SS Payee and note+.PP+You can optionally include a \f[C]|\f[] (pipe) character in a+description to subdivide it into a payee/payer name on the left and+additional notes on the right.+This may be worthwhile if you need to do more precise querying and+pivoting by payee. .SS Account names .PP Account names typically have several parts separated by a full colon,@@ -793,24 +814,6 @@ .PP Tags are like Ledger\[aq]s metadata feature, except hledger\[aq]s tag values are simple strings.-.SS Implicit tags-.PP-Some predefined "implicit" tags are also provided:-.IP \[bu] 2-\f[C]code\f[] \- the transaction\[aq]s code field-.IP \[bu] 2-\f[C]description\f[] \- the transaction\[aq]s description-.IP \[bu] 2-\f[C]payee\f[] \- the part of description before \f[C]|\f[], or all of-it-.IP \[bu] 2-\f[C]note\f[] \- the part of description after \f[C]|\f[], or all of it-.PP-\f[C]payee\f[] and \f[C]note\f[] support descriptions written in a-special \f[C]PAYEE\ |\ NOTE\f[] format, accessing the parts before and-after the pipe character respectively.-For descriptions not containing a pipe character they are the same as-\f[C]description\f[]. .SS Directives .SS Account aliases .PP@@ -878,9 +881,7 @@ replaced by REPLACEMENT. If REGEX contains parenthesised match groups, these can be referenced by the usual numeric backreferences in REPLACEMENT.-Note, currently regular expression aliases may cause noticeable-slow\-downs.-(And if you use Ledger on your hledger file, they will be ignored.) Eg:+Eg: .IP .nf \f[C]@@ -888,6 +889,9 @@ #\ rewrites\ "assets:bank:wells\ fargo:checking"\ to\ \ "assets:wells\ fargo\ checking" \f[] .fi+.PP+Also note that REPLACEMENT continues to the end of line (or on command+line, to end of option argument), so it can contain trailing whitespace. .SS Multiple aliases .PP You can define as many aliases as you like using directives or@@ -1119,6 +1123,11 @@ Text Wrangler \ T}@T{ https://github.com/ledger/ledger/wiki/Editing\-Ledger\-files\-with\-TextWrangler+T}+T{+Visual Studio Code+T}@T{+https://marketplace.visualstudio.com/items?itemName=mark\-hansen.hledger\-vscode T} .TE
doc/other/hledger_journal.5.info view
@@ -4,8 +4,8 @@ File: hledger_journal.5.info, Node: Top, Next: FILE FORMAT, Up: (dir) -hledger_journal(5) hledger 1.3.1-********************************+hledger_journal(5) hledger 1.4+****************************** hledger's usual data source is a plain text file containing journal entries in hledger journal format. This file represents a standard@@ -46,6 +46,10 @@ expenses:supplies $1 ; <- this transaction debits two expense accounts assets:cash ; <- $-2 inferred +2008/10/01 take a loan+ assets:bank:checking $1+ liabilities:debts $-1+ 2008/12/31 * pay off ; <- an optional * or ! after the date means "cleared" (or anything you want) liabilities:debts $1 assets:bank:checking@@ -67,6 +71,7 @@ * Postings:: * Dates:: * Status::+* Description:: * Account names:: * Amounts:: * Virtual Postings::@@ -92,7 +97,9 @@ * (optional) a transaction code (any short number or text, enclosed in parentheses) * (optional) a transaction description (any remaining text until end- of line)+ of line or a semicolon)+ * (optional) a transaction comment (any remaining text following a+ semicolon until end of line) Then comes zero or more (but usually at least 2) indented lines representing...@@ -227,7 +234,7 @@ transaction and DATE2 infers its year from DATE. -File: hledger_journal.5.info, Node: Status, Next: Account names, Prev: Dates, Up: FILE FORMAT+File: hledger_journal.5.info, Node: Status, Next: Description, Prev: Dates, Up: FILE FORMAT 1.4 Status ==========@@ -277,9 +284,35 @@ your finances. -File: hledger_journal.5.info, Node: Account names, Next: Amounts, Prev: Status, Up: FILE FORMAT+File: hledger_journal.5.info, Node: Description, Next: Account names, Prev: Status, Up: FILE FORMAT -1.5 Account names+1.5 Description+===============++A transaction's description is the rest of the line following the date+and status mark (or until a comment begins). Sometimes called the+"narration" in traditional bookkeeping, it can be used for whatever you+wish, or left blank. Transaction descriptions can be queried, unlike+comments.+* Menu:++* Payee and note::+++File: hledger_journal.5.info, Node: Payee and note, Up: Description++1.5.1 Payee and note+--------------------++You can optionally include a '|' (pipe) character in a description to+subdivide it into a payee/payer name on the left and additional notes on+the right. This may be worthwhile if you need to do more precise+querying and pivoting by payee.+++File: hledger_journal.5.info, Node: Account names, Next: Amounts, Prev: Description, Up: FILE FORMAT++1.6 Account names ================= Account names typically have several parts separated by a full colon,@@ -297,7 +330,7 @@ File: hledger_journal.5.info, Node: Amounts, Next: Virtual Postings, Prev: Account names, Up: FILE FORMAT -1.6 Amounts+1.7 Amounts =========== After the account name, there is usually an amount. Important: between@@ -352,7 +385,7 @@ File: hledger_journal.5.info, Node: Virtual Postings, Next: Balance Assertions, Prev: Amounts, Up: FILE FORMAT -1.7 Virtual Postings+1.8 Virtual Postings ==================== When you parenthesise the account name in a posting, we call that a@@ -387,7 +420,7 @@ File: hledger_journal.5.info, Node: Balance Assertions, Next: Balance Assignments, Prev: Virtual Postings, Up: FILE FORMAT -1.8 Balance Assertions+1.9 Balance Assertions ====================== hledger supports Ledger-style balance assertions in journal files.@@ -421,7 +454,7 @@ File: hledger_journal.5.info, Node: Assertions and ordering, Next: Assertions and included files, Up: Balance Assertions -1.8.1 Assertions and ordering+1.9.1 Assertions and ordering ----------------------------- hledger sorts an account's postings and assertions first by date and@@ -440,7 +473,7 @@ File: hledger_journal.5.info, Node: Assertions and included files, Next: Assertions and multiple -f options, Prev: Assertions and ordering, Up: Balance Assertions -1.8.2 Assertions and included files+1.9.2 Assertions and included files ----------------------------------- With included files, things are a little more complicated. Including@@ -452,7 +485,7 @@ File: hledger_journal.5.info, Node: Assertions and multiple -f options, Next: Assertions and commodities, Prev: Assertions and included files, Up: Balance Assertions -1.8.3 Assertions and multiple -f options+1.9.3 Assertions and multiple -f options ---------------------------------------- Balance assertions don't work well across files specified with multiple@@ -461,7 +494,7 @@ File: hledger_journal.5.info, Node: Assertions and commodities, Next: Assertions and subaccounts, Prev: Assertions and multiple -f options, Up: Balance Assertions -1.8.4 Assertions and commodities+1.9.4 Assertions and commodities -------------------------------- The asserted balance must be a simple single-commodity amount, and in@@ -480,7 +513,7 @@ File: hledger_journal.5.info, Node: Assertions and subaccounts, Next: Assertions and virtual postings, Prev: Assertions and commodities, Up: Balance Assertions -1.8.5 Assertions and subaccounts+1.9.5 Assertions and subaccounts -------------------------------- Balance assertions do not count the balance from subaccounts; they check@@ -503,7 +536,7 @@ File: hledger_journal.5.info, Node: Assertions and virtual postings, Prev: Assertions and subaccounts, Up: Balance Assertions -1.8.6 Assertions and virtual postings+1.9.6 Assertions and virtual postings ------------------------------------- Balance assertions are checked against all postings, both real and@@ -513,8 +546,8 @@ File: hledger_journal.5.info, Node: Balance Assignments, Next: Prices, Prev: Balance Assertions, Up: FILE FORMAT -1.9 Balance Assignments-=======================+1.10 Balance Assignments+======================== Ledger-style balance assignments are also supported. These are like balance assertions, but with no posting amount on the left side of the@@ -546,7 +579,7 @@ File: hledger_journal.5.info, Node: Prices, Next: Comments, Prev: Balance Assignments, Up: FILE FORMAT -1.10 Prices+1.11 Prices =========== * Menu:@@ -557,7 +590,7 @@ File: hledger_journal.5.info, Node: Transaction prices, Next: Market prices, Up: Prices -1.10.1 Transaction prices+1.11.1 Transaction prices ------------------------- Within a transaction, you can note an amount's price in another@@ -618,7 +651,7 @@ File: hledger_journal.5.info, Node: Market prices, Prev: Transaction prices, Up: Prices -1.10.2 Market prices+1.11.2 Market prices -------------------- Market prices are not tied to a particular transaction; they represent@@ -647,7 +680,7 @@ File: hledger_journal.5.info, Node: Comments, Next: Tags, Prev: Prices, Up: FILE FORMAT -1.11 Comments+1.12 Comments ============= Lines in the journal beginning with a semicolon (';') or hash ('#') or@@ -687,7 +720,7 @@ File: hledger_journal.5.info, Node: Tags, Next: Directives, Prev: Comments, Up: FILE FORMAT -1.12 Tags+1.13 Tags ========= Tags are a way to add extra labels or labelled data to postings and@@ -726,32 +759,11 @@ Tags are like Ledger's metadata feature, except hledger's tag values are simple strings.-* Menu: -* Implicit tags::- -File: hledger_journal.5.info, Node: Implicit tags, Up: Tags--1.12.1 Implicit tags-----------------------Some predefined "implicit" tags are also provided:-- * 'code' - the transaction's code field- * 'description' - the transaction's description- * 'payee' - the part of description before '|', or all of it- * 'note' - the part of description after '|', or all of it-- 'payee' and 'note' support descriptions written in a special 'PAYEE |-NOTE' format, accessing the parts before and after the pipe character-respectively. For descriptions not containing a pipe character they are-the same as 'description'.-- File: hledger_journal.5.info, Node: Directives, Prev: Tags, Up: FILE FORMAT -1.13 Directives+1.14 Directives =============== * Menu:@@ -768,7 +780,7 @@ File: hledger_journal.5.info, Node: Account aliases, Next: account directive, Up: Directives -1.13.1 Account aliases+1.14.1 Account aliases ---------------------- You can define aliases which rewrite your account names (after reading@@ -793,7 +805,7 @@ File: hledger_journal.5.info, Node: Basic aliases, Next: Regex aliases, Up: Account aliases -1.13.1.1 Basic aliases+1.14.1.1 Basic aliases ...................... To set an account alias, use the 'alias' directive in your journal file.@@ -816,7 +828,7 @@ File: hledger_journal.5.info, Node: Regex aliases, Next: Multiple aliases, Prev: Basic aliases, Up: Account aliases -1.13.1.2 Regex aliases+1.14.1.2 Regex aliases ...................... There is also a more powerful variant that uses a regular expression,@@ -829,17 +841,19 @@ REGEX is a case-insensitive regular expression. Anywhere it matches inside an account name, the matched part will be replaced by REPLACEMENT. If REGEX contains parenthesised match groups, these can be-referenced by the usual numeric backreferences in REPLACEMENT. Note,-currently regular expression aliases may cause noticeable slow-downs.-(And if you use Ledger on your hledger file, they will be ignored.) Eg:+referenced by the usual numeric backreferences in REPLACEMENT. Eg: alias /^(.+):bank:([^:]+)(.*)/ = \1:\2 \3 # rewrites "assets:bank:wells fargo:checking" to "assets:wells fargo checking" + Also note that REPLACEMENT continues to the end of line (or on+command line, to end of option argument), so it can contain trailing+whitespace.+ File: hledger_journal.5.info, Node: Multiple aliases, Next: end aliases, Prev: Regex aliases, Up: Account aliases -1.13.1.3 Multiple aliases+1.14.1.3 Multiple aliases ......................... You can define as many aliases as you like using directives or@@ -855,7 +869,7 @@ File: hledger_journal.5.info, Node: end aliases, Prev: Multiple aliases, Up: Account aliases -1.13.1.4 end aliases+1.14.1.4 end aliases .................... You can clear (forget) all currently defined aliases with the 'end@@ -866,7 +880,7 @@ File: hledger_journal.5.info, Node: account directive, Next: apply account directive, Prev: Account aliases, Up: Directives -1.13.2 account directive+1.14.2 account directive ------------------------ The 'account' directive predefines account names, as in Ledger and@@ -887,7 +901,7 @@ File: hledger_journal.5.info, Node: apply account directive, Next: Multi-line comments, Prev: account directive, Up: Directives -1.13.3 apply account directive+1.14.3 apply account directive ------------------------------ You can specify a parent account which will be prepended to all accounts@@ -923,7 +937,7 @@ File: hledger_journal.5.info, Node: Multi-line comments, Next: commodity directive, Prev: apply account directive, Up: Directives -1.13.4 Multi-line comments+1.14.4 Multi-line comments -------------------------- A line containing just 'comment' starts a multi-line comment, and a line@@ -932,7 +946,7 @@ File: hledger_journal.5.info, Node: commodity directive, Next: Default commodity, Prev: Multi-line comments, Up: Directives -1.13.5 commodity directive+1.14.5 commodity directive -------------------------- The 'commodity' directive predefines commodities (currently this is just@@ -964,7 +978,7 @@ File: hledger_journal.5.info, Node: Default commodity, Next: Default year, Prev: commodity directive, Up: Directives -1.13.6 Default commodity+1.14.6 Default commodity ------------------------ The D directive sets a default commodity (and display format), to be@@ -984,7 +998,7 @@ File: hledger_journal.5.info, Node: Default year, Next: Including other files, Prev: Default commodity, Up: Directives -1.13.7 Default year+1.14.7 Default year ------------------- You can set a default year to be used for subsequent dates which don't@@ -1010,7 +1024,7 @@ File: hledger_journal.5.info, Node: Including other files, Prev: Default year, Up: Directives -1.13.8 Including other files+1.14.8 Including other files ---------------------------- You can pull in the content of additional journal files by writing an@@ -1043,87 +1057,91 @@ Sublime Text https://github.com/ledger/ledger/wiki/Using-Sublime-Text Textmate https://github.com/ledger/ledger/wiki/Using-TextMate-2 Text Wrangler https://github.com/ledger/ledger/wiki/Editing-Ledger-files-with-TextWrangler+Visual Studio https://marketplace.visualstudio.com/items?itemName=mark-hansen.hledger-vscode+Code Tag Table: Node: Top78-Node: FILE FORMAT2296-Ref: #file-format2422-Node: Transactions2629-Ref: #transactions2752-Node: Postings3317-Ref: #postings3446-Node: Dates4441-Ref: #dates4558-Node: Simple dates4623-Ref: #simple-dates4751-Node: Secondary dates5117-Ref: #secondary-dates5273-Node: Posting dates6836-Ref: #posting-dates6967-Node: Status8341-Ref: #status8465-Node: Account names10179-Ref: #account-names10319-Node: Amounts10806-Ref: #amounts10944-Node: Virtual Postings13045-Ref: #virtual-postings13206-Node: Balance Assertions14426-Ref: #balance-assertions14603-Node: Assertions and ordering15499-Ref: #assertions-and-ordering15687-Node: Assertions and included files16387-Ref: #assertions-and-included-files16630-Node: Assertions and multiple -f options16963-Ref: #assertions-and-multiple--f-options17219-Node: Assertions and commodities17351-Ref: #assertions-and-commodities17588-Node: Assertions and subaccounts18284-Ref: #assertions-and-subaccounts18518-Node: Assertions and virtual postings19039-Ref: #assertions-and-virtual-postings19248-Node: Balance Assignments19390-Ref: #balance-assignments19559-Node: Prices20678-Ref: #prices20813-Node: Transaction prices20864-Ref: #transaction-prices21011-Node: Market prices23167-Ref: #market-prices23304-Node: Comments24264-Ref: #comments24388-Node: Tags25501-Ref: #tags25621-Node: Implicit tags27050-Ref: #implicit-tags27158-Node: Directives27675-Ref: #directives27790-Node: Account aliases27983-Ref: #account-aliases28129-Node: Basic aliases28733-Ref: #basic-aliases28878-Node: Regex aliases29568-Ref: #regex-aliases29738-Node: Multiple aliases30453-Ref: #multiple-aliases30627-Node: end aliases31125-Ref: #end-aliases31267-Node: account directive31368-Ref: #account-directive31550-Node: apply account directive31846-Ref: #apply-account-directive32044-Node: Multi-line comments32703-Ref: #multi-line-comments32895-Node: commodity directive33023-Ref: #commodity-directive33209-Node: Default commodity34081-Ref: #default-commodity34256-Node: Default year34793-Ref: #default-year34960-Node: Including other files35383-Ref: #including-other-files35542-Node: EDITOR SUPPORT35939-Ref: #editor-support36059+Node: FILE FORMAT2374+Ref: #file-format2500+Node: Transactions2723+Ref: #transactions2846+Node: Postings3530+Ref: #postings3659+Node: Dates4654+Ref: #dates4771+Node: Simple dates4836+Ref: #simple-dates4964+Node: Secondary dates5330+Ref: #secondary-dates5486+Node: Posting dates7049+Ref: #posting-dates7180+Node: Status8554+Ref: #status8676+Node: Description10390+Ref: #description10530+Node: Payee and note10849+Ref: #payee-and-note10965+Node: Account names11207+Ref: #account-names11352+Node: Amounts11839+Ref: #amounts11977+Node: Virtual Postings14078+Ref: #virtual-postings14239+Node: Balance Assertions15459+Ref: #balance-assertions15636+Node: Assertions and ordering16532+Ref: #assertions-and-ordering16720+Node: Assertions and included files17420+Ref: #assertions-and-included-files17663+Node: Assertions and multiple -f options17996+Ref: #assertions-and-multiple--f-options18252+Node: Assertions and commodities18384+Ref: #assertions-and-commodities18621+Node: Assertions and subaccounts19317+Ref: #assertions-and-subaccounts19551+Node: Assertions and virtual postings20072+Ref: #assertions-and-virtual-postings20281+Node: Balance Assignments20423+Ref: #balance-assignments20594+Node: Prices21713+Ref: #prices21848+Node: Transaction prices21899+Ref: #transaction-prices22046+Node: Market prices24202+Ref: #market-prices24339+Node: Comments25299+Ref: #comments25423+Node: Tags26536+Ref: #tags26656+Node: Directives28058+Ref: #directives28173+Node: Account aliases28366+Ref: #account-aliases28512+Node: Basic aliases29116+Ref: #basic-aliases29261+Node: Regex aliases29951+Ref: #regex-aliases30121+Node: Multiple aliases30839+Ref: #multiple-aliases31013+Node: end aliases31511+Ref: #end-aliases31653+Node: account directive31754+Ref: #account-directive31936+Node: apply account directive32232+Ref: #apply-account-directive32430+Node: Multi-line comments33089+Ref: #multi-line-comments33281+Node: commodity directive33409+Ref: #commodity-directive33595+Node: Default commodity34467+Ref: #default-commodity34642+Node: Default year35179+Ref: #default-year35346+Node: Including other files35769+Ref: #including-other-files35928+Node: EDITOR SUPPORT36325+Ref: #editor-support36445 End Tag Table
doc/other/hledger_journal.5.txt view
@@ -47,6 +47,10 @@ expenses:supplies $1 ; <- this transaction debits two expense accounts assets:cash ; <- $-2 inferred + 2008/10/01 take a loan+ assets:bank:checking $1+ liabilities:debts $-1+ 2008/12/31 * pay off ; <- an optional * or ! after the date means "cleared" (or anything you want) liabilities:debts $1 assets:bank:checking@@ -64,59 +68,62 @@ parentheses) o (optional) a transaction description (any remaining text until end of- line)+ line or a semicolon) - Then comes zero or more (but usually at least 2) indented lines repre-+ o (optional) a transaction comment (any remaining text following a+ semicolon until end of line)++ Then comes zero or more (but usually at least 2) indented lines repre- senting... Postings- A posting is an addition of some amount to, or removal of some amount- from, an account. Each posting line begins with at least one space or+ A posting is an addition of some amount to, or removal of some amount+ from, an account. Each posting line begins with at least one space or tab (2 or 4 spaces is common), followed by: o (optional) a status character (empty, !, or *), followed by a space - o (required) an account name (any text, optionally containing single+ o (required) an account name (any text, optionally containing single spaces, until end of line or a double space) o (optional) two or more spaces or tabs followed by an amount. - Positive amounts are being added to the account, negative amounts are+ Positive amounts are being added to the account, negative amounts are being removed. The amounts within a transaction must always sum up to zero. As a con-- venience, one amount may be left blank; it will be inferred so as to+ venience, one amount may be left blank; it will be inferred so as to balance the transaction. - Be sure to note the unusual two-space delimiter between account name- and amount. This makes it easy to write account names containing spa-- ces. But if you accidentally leave only one space (or tab) before the+ Be sure to note the unusual two-space delimiter between account name+ and amount. This makes it easy to write account names containing spa-+ ces. But if you accidentally leave only one space (or tab) before the amount, the amount will be considered part of the account name. Dates Simple dates- Within a journal file, transaction dates use Y/M/D (or Y-M-D or Y.M.D)- Leading zeros are optional. The year may be omitted, in which case it- will be inferred from the context - the current transaction, the- default year set with a default year directive, or the current date- when the command is run. Some examples: 2010/01/31, 1/31, 2010-01-31,+ Within a journal file, transaction dates use Y/M/D (or Y-M-D or Y.M.D)+ Leading zeros are optional. The year may be omitted, in which case it+ will be inferred from the context - the current transaction, the+ default year set with a default year directive, or the current date+ when the command is run. Some examples: 2010/01/31, 1/31, 2010-01-31, 2010.1.31. Secondary dates- Real-life transactions sometimes involve more than one date - eg the+ Real-life transactions sometimes involve more than one date - eg the date you write a cheque, and the date it clears in your bank. When you- want to model this, eg for more accurate balances, you can specify- individual posting dates, which I recommend. Or, you can use the sec-- ondary dates (aka auxiliary/effective dates) feature, supported for+ want to model this, eg for more accurate balances, you can specify+ individual posting dates, which I recommend. Or, you can use the sec-+ ondary dates (aka auxiliary/effective dates) feature, supported for compatibility with Ledger. A secondary date can be written after the primary date, separated by an- equals sign. The primary date, on the left, is used by default; the- secondary date, on the right, is used when the --date2 flag is speci-+ equals sign. The primary date, on the left, is used by default; the+ secondary date, on the right, is used when the --date2 flag is speci- fied (--aux-date or --effective also work). - The meaning of secondary dates is up to you, but it's best to follow a- consistent rule. Eg write the bank's clearing date as primary, and+ The meaning of secondary dates is up to you, but it's best to follow a+ consistent rule. Eg write the bank's clearing date as primary, and when needed, the date the transaction was initiated as secondary. Here's an example. Note that a secondary date will use the year of the@@ -132,18 +139,18 @@ $ hledger register checking --date2 2010/02/19 movie ticket assets:checking $-10 $-10 - Secondary dates require some effort; you must use them consistently in+ Secondary dates require some effort; you must use them consistently in your journal entries and remember whether to use or not use the --date2 flag for your reports. They are included in hledger for Ledger compat-- ibility, but posting dates are a more powerful and less confusing+ ibility, but posting dates are a more powerful and less confusing alternative. Posting dates- You can give individual postings a different date from their parent- transaction, by adding a posting comment containing a tag (see below)+ You can give individual postings a different date from their parent+ transaction, by adding a posting comment containing a tag (see below) like date:DATE. This is probably the best way to control posting dates- precisely. Eg in this example the expense should appear in May- reports, and the deduction from checking should be reported on 6/1 for+ precisely. Eg in this example the expense should appear in May+ reports, and the deduction from checking should be reported on 6/1 for easy bank reconciliation: 2015/5/30@@ -156,22 +163,22 @@ $ hledger -f t.j register checking 2015/06/01 assets:checking $-10 $-10 - DATE should be a simple date; if the year is not specified it will use- the year of the transaction's date. You can set the secondary date- similarly, with date2:DATE2. The date: or date2: tags must have a- valid simple date value if they are present, eg a date: tag with no+ DATE should be a simple date; if the year is not specified it will use+ the year of the transaction's date. You can set the secondary date+ similarly, with date2:DATE2. The date: or date2: tags must have a+ valid simple date value if they are present, eg a date: tag with no value is not allowed. Ledger's earlier, more compact bracketed date syntax is also supported:- [DATE], [DATE=DATE2] or [=DATE2]. hledger will attempt to parse any+ [DATE], [DATE=DATE2] or [=DATE2]. hledger will attempt to parse any square-bracketed sequence of the 0123456789/-.= characters in this way.- With this syntax, DATE infers its year from the transaction and DATE2+ With this syntax, DATE infers its year from the transaction and DATE2 infers its year from DATE. Status- Transactions, or individual postings within a transaction, can have a- status mark, which is a single character before the transaction- description or posting account name, separated from it by a space,+ Transactions, or individual postings within a transaction, can have a+ status mark, which is a single character before the transaction+ description or posting account name, separated from it by a space, indicating one of three statuses: @@ -181,39 +188,52 @@ ! pending * cleared - When reporting, you can filter by status with the -U/--unmarked,- -P/--pending, and -C/--cleared flags; or the status:, status:!, and+ When reporting, you can filter by status with the -U/--unmarked,+ -P/--pending, and -C/--cleared flags; or the status:, status:!, and status:* queries; or the U, P, C keys in hledger-ui. - Note, in Ledger and in older versions of hledger, the "unmarked" state- is called "uncleared". As of hledger 1.3 we have renamed it to+ Note, in Ledger and in older versions of hledger, the "unmarked" state+ is called "uncleared". As of hledger 1.3 we have renamed it to unmarked for clarity. - To replicate Ledger and old hledger's behaviour of also matching pend-+ To replicate Ledger and old hledger's behaviour of also matching pend- ing, combine -U and -P. - Status marks are optional, but can be helpful eg for reconciling with+ Status marks are optional, but can be helpful eg for reconciling with real-world accounts. Some editor modes provide highlighting and short-- cuts for working with status. Eg in Emacs ledger-mode, you can toggle+ cuts for working with status. Eg in Emacs ledger-mode, you can toggle transaction status with C-c C-e, or posting status with C-c C-c. - What "uncleared", "pending", and "cleared" actually mean is up to you.+ What "uncleared", "pending", and "cleared" actually mean is up to you. Here's one suggestion: status meaning -------------------------------------------------------------------------- uncleared recorded but not yet reconciled; needs review- pending tentatively reconciled (if needed, eg during a big recon-+ pending tentatively reconciled (if needed, eg during a big recon- ciliation)- cleared complete, reconciled as far as possible, and considered+ cleared complete, reconciled as far as possible, and considered correct - With this scheme, you would use -PC to see the current balance at your- bank, -U to see things which will probably hit your bank soon (like+ With this scheme, you would use -PC to see the current balance at your+ bank, -U to see things which will probably hit your bank soon (like uncashed checks), and no flags to see the most up-to-date state of your finances. + Description+ A transaction's description is the rest of the line following the date+ and status mark (or until a comment begins). Sometimes called the+ "narration" in traditional bookkeeping, it can be used for whatever you+ wish, or left blank. Transaction descriptions can be queried, unlike+ comments.++ Payee and note+ You can optionally include a | (pipe) character in a description to+ subdivide it into a payee/payer name on the left and additional notes+ on the right. This may be worthwhile if you need to do more precise+ querying and pivoting by payee.+ Account names Account names typically have several parts separated by a full colon, from which hledger derives a hierarchical chart of accounts. They can@@ -577,25 +597,9 @@ Tags are like Ledger's metadata feature, except hledger's tag values are simple strings. - Implicit tags- Some predefined "implicit" tags are also provided:-- o code - the transaction's code field-- o description - the transaction's description-- o payee - the part of description before |, or all of it-- o note - the part of description after |, or all of it-- payee and note support descriptions written in a special PAYEE | NOTE- format, accessing the parts before and after the pipe character respec-- tively. For descriptions not containing a pipe character they are the- same as description.- Directives Account aliases- You can define aliases which rewrite your account names (after reading+ You can define aliases which rewrite your account names (after reading the journal, before generating reports). hledger's account aliases can be useful for: @@ -612,8 +616,8 @@ See also Cookbook: rewrite account names. Basic aliases- To set an account alias, use the alias directive in your journal file.- This affects all subsequent journal entries in the current file or its+ To set an account alias, use the alias directive in your journal file.+ This affects all subsequent journal entries in the current file or its included files. The spaces around the = are optional: alias OLD = NEW@@ -621,31 +625,33 @@ Or, you can use the --alias 'OLD=NEW' option on the command line. This affects all entries. It's useful for trying out aliases interactively. - OLD and NEW are full account names. hledger will replace any occur-- rence of the old account name with the new one. Subaccounts are also+ OLD and NEW are full account names. hledger will replace any occur-+ rence of the old account name with the new one. Subaccounts are also affected. Eg: alias checking = assets:bank:wells fargo:checking # rewrites "checking" to "assets:bank:wells fargo:checking", or "checking:a" to "assets:bank:wells fargo:checking:a" Regex aliases- There is also a more powerful variant that uses a regular expression,+ There is also a more powerful variant that uses a regular expression, indicated by the forward slashes: alias /REGEX/ = REPLACEMENT or --alias '/REGEX/=REPLACEMENT'. - REGEX is a case-insensitive regular expression. Anywhere it matches- inside an account name, the matched part will be replaced by REPLACE-- MENT. If REGEX contains parenthesised match groups, these can be ref-- erenced by the usual numeric backreferences in REPLACEMENT. Note, cur-- rently regular expression aliases may cause noticeable slow-downs.- (And if you use Ledger on your hledger file, they will be ignored.) Eg:+ REGEX is a case-insensitive regular expression. Anywhere it matches+ inside an account name, the matched part will be replaced by REPLACE-+ MENT. If REGEX contains parenthesised match groups, these can be ref-+ erenced by the usual numeric backreferences in REPLACEMENT. Eg: alias /^(.+):bank:([^:]+)(.*)/ = \1:\2 \3 # rewrites "assets:bank:wells fargo:checking" to "assets:wells fargo checking" + Also note that REPLACEMENT continues to the end of line (or on command+ line, to end of option argument), so it can contain trailing white-+ space.+ Multiple aliases You can define as many aliases as you like using directives or com- mand-line options. Aliases are recursive - each alias sees the result@@ -811,7 +817,11 @@ ing-Ledger-files-with-TextWrangler + Visual Studio https://marketplace.visualstudio.com/items?item-+ Code Name=mark-hansen.hledger-vscode ++ REPORTING BUGS Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel or hledger mail list)@@ -835,4 +845,4 @@ -hledger 1.3.1 August 2017 hledger_journal(5)+hledger 1.4 September 2017 hledger_journal(5)
doc/other/hledger_timeclock.5 view
@@ -1,5 +1,5 @@ -.TH "hledger_timeclock" "5" "August 2017" "hledger 1.3.1" "hledger User Manuals"+.TH "hledger_timeclock" "5" "September 2017" "hledger 1.4" "hledger User Manuals"
doc/other/hledger_timeclock.5.info view
@@ -4,8 +4,8 @@ File: hledger_timeclock.5.info, Node: Top, Up: (dir) -hledger_timeclock(5) hledger 1.3.1-**********************************+hledger_timeclock(5) hledger 1.4+******************************** hledger can read timeclock files. As with Ledger, these are (a subset of) timeclock.el's format, containing clock-in and clock-out entries as
doc/other/hledger_timeclock.5.txt view
@@ -79,4 +79,4 @@ -hledger 1.3.1 August 2017 hledger_timeclock(5)+hledger 1.4 September 2017 hledger_timeclock(5)
doc/other/hledger_timedot.5 view
@@ -1,5 +1,5 @@ -.TH "hledger_timedot" "5" "August 2017" "hledger 1.3.1" "hledger User Manuals"+.TH "hledger_timedot" "5" "September 2017" "hledger 1.4" "hledger User Manuals" @@ -9,17 +9,17 @@ .SH DESCRIPTION .PP Timedot is a plain text format for logging dated, categorised quantities-(eg time), supported by hledger.+(of time, usually), supported by hledger. It is convenient for approximate and retroactive time logging, eg when the real\-time clock\-in/out required with a timeclock file is too precise or too interruptive. It can be formatted like a bar chart, making clear at a glance where time was spent. .PP-Though called "timedot", the format does not specify the commodity being-logged, so could represent other dated, quantifiable things.-Eg you could record a single\-entry journal of financial transactions,-perhaps slightly more conveniently than with hledger_journal(5) format.+Though called "timedot", this format is read by hledger as commodityless+quantities, so it could be used to represent dated quantities other than+time.+In the docs below we\[aq]ll assume it\[aq]s time. .SH FILE FORMAT .PP A timedot file contains a series of day entries.@@ -27,16 +27,26 @@ pairs, one per line. Dates are hledger\-style simple dates (see hledger_journal(5)). Categories are hledger\-style account names, optionally indented.-There must be at least two spaces between the category and the quantity.-Quantities can be written in two ways:-.IP "1." 3-a series of dots (period characters).-Each dot represents "a quarter" \- eg, a quarter hour.-Spaces can be used to group dots into hours, for easier counting.-.IP "2." 3-a number (integer or decimal), representing "units" \- eg, hours.-A good alternative when dots are cumbersome.-(A number also can record negative quantities.)+As in a hledger journal, there must be at least two spaces between the+category (account name) and the quantity.+.PP+Quantities can be written as:+.IP \[bu] 2+a sequence of dots (.) representing quarter hours.+Spaces may optionally be used for grouping and readability.+Eg: ....+\&..+.IP \[bu] 2+an integral or decimal number, representing hours.+Eg: 1.5+.IP \[bu] 2+an integral or decimal number immediately followed by a unit symbol+\f[C]s\f[], \f[C]m\f[], \f[C]h\f[], \f[C]d\f[], \f[C]w\f[], \f[C]mo\f[],+or \f[C]y\f[], representing seconds, minutes, hours, days weeks, months+or years respectively.+Eg: 90m.+The following equivalencies are assumed, currently: 1m = 60s, 1h = 60m,+1d = 24h, 1w = 7d, 1mo = 30d, 1y=365d. .PP Blank lines and lines beginning with #, ; or * are ignored. An example:
doc/other/hledger_timedot.5.info view
@@ -4,20 +4,19 @@ File: hledger_timedot.5.info, Node: Top, Next: FILE FORMAT, Up: (dir) -hledger_timedot(5) hledger 1.3.1-********************************+hledger_timedot(5) hledger 1.4+****************************** Timedot is a plain text format for logging dated, categorised quantities-(eg time), supported by hledger. It is convenient for approximate and-retroactive time logging, eg when the real-time clock-in/out required-with a timeclock file is too precise or too interruptive. It can be-formatted like a bar chart, making clear at a glance where time was-spent.+(of time, usually), supported by hledger. It is convenient for+approximate and retroactive time logging, eg when the real-time+clock-in/out required with a timeclock file is too precise or too+interruptive. It can be formatted like a bar chart, making clear at a+glance where time was spent. - Though called "timedot", the format does not specify the commodity-being logged, so could represent other dated, quantifiable things. Eg-you could record a single-entry journal of financial transactions,-perhaps slightly more conveniently than with hledger_journal(5) format.+ Though called "timedot", this format is read by hledger as+commodityless quantities, so it could be used to represent dated+quantities other than time. In the docs below we'll assume it's time. * Menu: * FILE FORMAT::@@ -31,18 +30,23 @@ A timedot file contains a series of day entries. A day entry begins with a date, and is followed by category/quantity pairs, one per line. Dates are hledger-style simple dates (see hledger_journal(5)).-Categories are hledger-style account names, optionally indented. There-must be at least two spaces between the category and the quantity.-Quantities can be written in two ways:+Categories are hledger-style account names, optionally indented. As in+a hledger journal, there must be at least two spaces between the+category (account name) and the quantity. - 1. a series of dots (period characters). Each dot represents "a- quarter" - eg, a quarter hour. Spaces can be used to group dots- into hours, for easier counting.+ Quantities can be written as: - 2. a number (integer or decimal), representing "units" - eg, hours. A- good alternative when dots are cumbersome. (A number also can- record negative quantities.)+ * a sequence of dots (.) representing quarter hours. Spaces may+ optionally be used for grouping and readability. Eg: .... .. + * an integral or decimal number, representing hours. Eg: 1.5++ * an integral or decimal number immediately followed by a unit symbol+ 's', 'm', 'h', 'd', 'w', 'mo', or 'y', representing seconds,+ minutes, hours, days weeks, months or years respectively. Eg: 90m.+ The following equivalencies are assumed, currently: 1m = 60s, 1h =+ 60m, 1d = 24h, 1w = 7d, 1mo = 30d, 1y=365d.+ Blank lines and lines beginning with #, ; or * are ignored. An example: @@ -106,7 +110,7 @@ Tag Table: Node: Top78-Node: FILE FORMAT886-Ref: #file-format989+Node: FILE FORMAT809+Ref: #file-format912 End Tag Table
doc/other/hledger_timedot.5.txt view
@@ -8,33 +8,37 @@ DESCRIPTION Timedot is a plain text format for logging dated, categorised quanti-- ties (eg time), supported by hledger. It is convenient for approximate- and retroactive time logging, eg when the real-time clock-in/out- required with a timeclock file is too precise or too interruptive. It- can be formatted like a bar chart, making clear at a glance where time- was spent.+ ties (of time, usually), supported by hledger. It is convenient for+ approximate and retroactive time logging, eg when the real-time+ clock-in/out required with a timeclock file is too precise or too+ interruptive. It can be formatted like a bar chart, making clear at a+ glance where time was spent. - Though called "timedot", the format does not specify the commodity- being logged, so could represent other dated, quantifiable things. Eg- you could record a single-entry journal of financial transactions, per-- haps slightly more conveniently than with hledger_journal(5) format.+ Though called "timedot", this format is read by hledger as commodity-+ less quantities, so it could be used to represent dated quantities+ other than time. In the docs below we'll assume it's time. FILE FORMAT- A timedot file contains a series of day entries. A day entry begins- with a date, and is followed by category/quantity pairs, one per line.- Dates are hledger-style simple dates (see hledger_journal(5)). Cate-- gories are hledger-style account names, optionally indented. There- must be at least two spaces between the category and the quantity.- Quantities can be written in two ways:+ A timedot file contains a series of day entries. A day entry begins+ with a date, and is followed by category/quantity pairs, one per line.+ Dates are hledger-style simple dates (see hledger_journal(5)). Cate-+ gories are hledger-style account names, optionally indented. As in a+ hledger journal, there must be at least two spaces between the category+ (account name) and the quantity. - 1. a series of dots (period characters). Each dot represents "a quar-- ter" - eg, a quarter hour. Spaces can be used to group dots into- hours, for easier counting.+ Quantities can be written as: - 2. a number (integer or decimal), representing "units" - eg, hours. A- good alternative when dots are cumbersome. (A number also can- record negative quantities.)+ o a sequence of dots (.) representing quarter hours. Spaces may+ optionally be used for grouping and readability. Eg: .... .. + o an integral or decimal number, representing hours. Eg: 1.5++ o an integral or decimal number immediately followed by a unit symbol+ s, m, h, d, w, mo, or y, representing seconds, minutes, hours, days+ weeks, months or years respectively. Eg: 90m. The following equiva-+ lencies are assumed, currently: 1m = 60s, 1h = 60m, 1d = 24h, 1w =+ 7d, 1mo = 30d, 1y=365d.+ Blank lines and lines beginning with #, ; or * are ignored. An exam- ple: @@ -120,4 +124,4 @@ -hledger 1.3.1 August 2017 hledger_timedot(5)+hledger 1.4 September 2017 hledger_timedot(5)
hledger.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack name: hledger-version: 1.3.1+version: 1.4 synopsis: Command-line interface for the hledger accounting tool description: This is hledger's command-line interface. Its basic function is to read a plain text file describing@@ -23,7 +23,7 @@ maintainer: Simon Michael <simon@joyful.com> license: GPL-3 license-file: LICENSE-tested-with: GHC==7.10.3, GHC==8.0+tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.1 build-type: Simple cabal-version: >= 1.10 @@ -63,11 +63,6 @@ type: git location: https://github.com/simonmichael/hledger -flag oldtime- description: If building with time < 1.5, also depend on old-locale. Set automatically by cabal.- manual: False- default: False- flag terminfo description: On POSIX systems, build with the terminfo lib for detecting terminal width. manual: False@@ -80,26 +75,30 @@ library ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans- cpp-options: -DVERSION="1.3.1"+ cpp-options: -DVERSION="1.4" build-depends: base >=4.8 && <5 , base-compat >=0.8.1- , ansi-terminal >= 0.6.2.3 && < 0.7+ , ansi-terminal >= 0.6.2.3 && < 0.8 , directory , file-embed >=0.0.10 && <0.1 , filepath , here , pretty-show >=1.6.4 , process+ , shakespeare >=2.0.2.2 && <2.1 , temporary , tabular >=0.2 && <0.3- , hledger-lib >= 1.3.1 && < 1.4+ , time >=1.5+ , utility-ht >= 0.0.13+ , hledger-lib >= 1.4 && < 1.5 , bytestring , containers , unordered-containers , cmdargs >=0.10 && <0.11 , csv , data-default >=0.5+ , Diff , hashable >=1.2.4 , haskeline >=0.6 && <=0.8 , HUnit@@ -114,23 +113,6 @@ , text >=0.11 , utf8-string >=0.3.5 && <1.1 , wizards ==1.0.*- if impl(ghc <7.6)- build-depends:- ghc-prim- if impl(ghc >=7.10)- build-depends:- shakespeare >=2.0.2.2 && <2.1- else- build-depends:- shakespeare >=1.0 && <2.1- , shakespeare-text >=1.0 && <1.2- if flag(oldtime)- build-depends:- time <1.5- , old-locale- else- build-depends:- time >=1.5 if (!(os(windows))) && (flag(terminfo)) build-depends: terminfo@@ -139,23 +121,31 @@ Hledger.Cli.Main Hledger.Cli.CliOptions Hledger.Cli.DocFiles- Hledger.Cli.Tests Hledger.Cli.Utils Hledger.Cli.Version- Hledger.Cli.Add- Hledger.Cli.Accounts- Hledger.Cli.Balance- Hledger.Cli.Balancesheet+ Hledger.Cli.Commands+ Hledger.Cli.Commands.Accounts+ Hledger.Cli.Commands.Activity+ Hledger.Cli.Commands.Add+ Hledger.Cli.Commands.Balance+ Hledger.Cli.Commands.Balancesheet+ Hledger.Cli.Commands.Balancesheetequity+ Hledger.Cli.Commands.Cashflow+ Hledger.Cli.Commands.Checkdates+ Hledger.Cli.Commands.Checkdupes+ Hledger.Cli.Commands.Equity+ Hledger.Cli.Commands.Help+ Hledger.Cli.Commands.Import+ Hledger.Cli.Commands.Incomestatement+ Hledger.Cli.Commands.Prices+ Hledger.Cli.Commands.Print+ Hledger.Cli.Commands.Printunique+ Hledger.Cli.Commands.Register+ Hledger.Cli.Commands.Registermatch+ Hledger.Cli.Commands.Rewrite+ Hledger.Cli.Commands.Stats+ Hledger.Cli.Commands.Tags Hledger.Cli.CompoundBalanceCommand- Hledger.Cli.Cashflow- Hledger.Cli.Help- Hledger.Cli.Histogram- Hledger.Cli.Incomestatement- Hledger.Cli.Info- Hledger.Cli.Man- Hledger.Cli.Print- Hledger.Cli.Register- Hledger.Cli.Stats Text.Tabular.AsciiWide other-modules: Paths_hledger@@ -166,20 +156,23 @@ hs-source-dirs: app ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans- cpp-options: -DVERSION="1.3.1"+ cpp-options: -DVERSION="1.4" build-depends: base >=4.8 && <5 , base-compat >=0.8.1- , ansi-terminal >= 0.6.2.3 && < 0.7+ , ansi-terminal >= 0.6.2.3 && < 0.8 , directory , file-embed >=0.0.10 && <0.1 , filepath , here , pretty-show >=1.6.4 , process+ , shakespeare >=2.0.2.2 && <2.1 , temporary , tabular >=0.2 && <0.3- , hledger-lib >= 1.3.1+ , time >=1.5+ , utility-ht >= 0.0.13+ , hledger-lib >= 1.4 && < 1.5 , hledger , bytestring , containers@@ -199,28 +192,11 @@ , text >=0.11 , utf8-string >=0.3.5 && <1.1 , wizards ==1.0.*- if flag(threaded)- ghc-options: -threaded- if impl(ghc <7.6)- build-depends:- ghc-prim- if impl(ghc >=7.10)- build-depends:- shakespeare >=2.0.2.2 && <2.1- else- build-depends:- shakespeare >=1.0 && <2.1- , shakespeare-text >=1.0 && <1.2- if flag(oldtime)- build-depends:- time <1.5- , old-locale- else- build-depends:- time >=1.5 if (!(os(windows))) && (flag(terminfo)) build-depends: terminfo+ if flag(threaded)+ ghc-options: -threaded default-language: Haskell2010 test-suite test@@ -229,20 +205,23 @@ hs-source-dirs: test ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans- cpp-options: -DVERSION="1.3.1"+ cpp-options: -DVERSION="1.4" build-depends: base >=4.8 && <5 , base-compat >=0.8.1- , ansi-terminal >= 0.6.2.3 && < 0.7+ , ansi-terminal >= 0.6.2.3 && < 0.8 , directory , file-embed >=0.0.10 && <0.1 , filepath , here , pretty-show >=1.6.4 , process+ , shakespeare >=2.0.2.2 && <2.1 , temporary , tabular >=0.2 && <0.3- , hledger-lib >= 1.3.1+ , time >=1.5+ , utility-ht >= 0.0.13+ , hledger-lib >= 1.4 && < 1.5 , hledger , bytestring , containers@@ -264,23 +243,6 @@ , wizards ==1.0.* , test-framework , test-framework-hunit- if impl(ghc <7.6)- build-depends:- ghc-prim- if impl(ghc >=7.10)- build-depends:- shakespeare >=2.0.2.2 && <2.1- else- build-depends:- shakespeare >=1.0 && <2.1- , shakespeare-text >=1.0 && <1.2- if flag(oldtime)- build-depends:- time <1.5- , old-locale- else- build-depends:- time >=1.5 if (!(os(windows))) && (flag(terminfo)) build-depends: terminfo@@ -295,25 +257,24 @@ build-depends: base >=4.8 && <5 , base-compat >=0.8.1- , ansi-terminal >= 0.6.2.3 && < 0.7+ , ansi-terminal >= 0.6.2.3 && < 0.8 , directory , file-embed >=0.0.10 && <0.1 , filepath , here , pretty-show >=1.6.4 , process+ , shakespeare >=2.0.2.2 && <2.1 , temporary , tabular >=0.2 && <0.3- , hledger-lib >= 1.3.1+ , time >=1.5+ , utility-ht >= 0.0.13+ , hledger-lib >= 1.4 && < 1.5 , hledger , criterion , html , timeit- if flag(oldtime)- build-depends:- time <1.5- , old-locale- else+ if (!(os(windows))) && (flag(terminfo)) build-depends:- time >=1.5+ terminfo default-language: Haskell2010