hledger-lib 1.17.0.1 → 1.17.1
raw patch · 25 files changed
+998/−774 lines, 25 filesdep ~DecimalPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: Decimal
API changes (from Hackage documentation)
- Hledger.Data.Journal: instance GHC.Base.Monoid Hledger.Data.Types.Journal
- Hledger.Utils: firstJust :: Eq a => [Maybe a] -> Maybe a
- Hledger.Utils: isLeft :: Either a b -> Bool
- Hledger.Utils: isRight :: Either a b -> Bool
+ Hledger.Data.Journal: instance Data.Default.Class.Default Hledger.Data.Types.Journal
+ Hledger.Utils.Debug: traceAt :: Int -> String -> a -> a
+ Hledger.Utils.Debug: traceAtWith :: (a -> String) -> a -> a
- Hledger.Utils.Debug: traceWith :: (a -> String) -> a -> a
+ Hledger.Utils.Debug: traceWith :: Show a => (a -> String) -> a -> a
- Hledger.Utils.Test: assertParse :: (HasCallStack, Eq a, Show a, Monoid st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> Assertion
+ Hledger.Utils.Test: assertParse :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> Assertion
- Hledger.Utils.Test: assertParseE :: (HasCallStack, Eq a, Show a, Monoid st) => StateT st (ParsecT CustomErr Text (ExceptT FinalParseError IO)) a -> Text -> Assertion
+ Hledger.Utils.Test: assertParseE :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr Text (ExceptT FinalParseError IO)) a -> Text -> Assertion
- Hledger.Utils.Test: assertParseEq :: (HasCallStack, Eq a, Show a, Monoid st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> a -> Assertion
+ Hledger.Utils.Test: assertParseEq :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> a -> Assertion
- Hledger.Utils.Test: assertParseEqE :: (Monoid st, Eq a, Show a, HasCallStack) => StateT st (ParsecT CustomErr Text (ExceptT FinalParseError IO)) a -> Text -> a -> Assertion
+ Hledger.Utils.Test: assertParseEqE :: (Default st, Eq a, Show a, HasCallStack) => StateT st (ParsecT CustomErr Text (ExceptT FinalParseError IO)) a -> Text -> a -> Assertion
- Hledger.Utils.Test: assertParseEqOn :: (HasCallStack, Eq b, Show b, Monoid st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> (a -> b) -> b -> Assertion
+ Hledger.Utils.Test: assertParseEqOn :: (HasCallStack, Eq b, Show b, Default st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> (a -> b) -> b -> Assertion
- Hledger.Utils.Test: assertParseError :: (HasCallStack, Eq a, Show a, Monoid st) => StateT st (ParsecT CustomErr Text IO) a -> String -> String -> Assertion
+ Hledger.Utils.Test: assertParseError :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr Text IO) a -> String -> String -> Assertion
- Hledger.Utils.Test: assertParseErrorE :: (Monoid st, Eq a, Show a, HasCallStack) => StateT st (ParsecT CustomErr Text (ExceptT FinalParseError IO)) a -> Text -> String -> Assertion
+ Hledger.Utils.Test: assertParseErrorE :: (Default st, Eq a, Show a, HasCallStack) => StateT st (ParsecT CustomErr Text (ExceptT FinalParseError IO)) a -> Text -> String -> Assertion
- Hledger.Utils.Test: assertParseStateOn :: (HasCallStack, Eq b, Show b, Monoid st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> (st -> b) -> b -> Assertion
+ Hledger.Utils.Test: assertParseStateOn :: (HasCallStack, Eq b, Show b, Default st) => StateT st (ParsecT CustomErr Text IO) a -> Text -> (st -> b) -> b -> Assertion
Files
- CHANGES.md +11/−0
- Hledger/Data/Dates.hs +4/−3
- Hledger/Data/Journal.hs +85/−63
- Hledger/Read.hs +13/−14
- Hledger/Read/Common.hs +5/−5
- Hledger/Read/CsvReader.hs +129/−196
- Hledger/Read/JournalReader.hs +14/−5
- Hledger/Utils.hs +0/−11
- Hledger/Utils/Debug.hs +17/−2
- Hledger/Utils/Test.hs +18/−17
- hledger-lib.cabal +7/−8
- hledger_csv.5 +34/−20
- hledger_csv.info +138/−80
- hledger_csv.txt +35/−20
- hledger_journal.5 +88/−31
- hledger_journal.info +235/−175
- hledger_journal.txt +150/−114
- hledger_timeclock.5 +1/−1
- hledger_timeclock.info +2/−2
- hledger_timeclock.txt +1/−1
- hledger_timedot.5 +1/−1
- hledger_timedot.info +2/−2
- hledger_timedot.txt +1/−1
- test/doctests.hs +1/−0
- test/unittest.hs +6/−2
CHANGES.md view
@@ -1,6 +1,17 @@ Internal/api/developer-ish changes in the hledger-lib (and hledger) packages. For user-visible changes, see the hledger package changelog. +# 1.17.1 2020-03-19++- require newer Decimal, math-functions libs to ensure consistent+ rounding behaviour, even when built with old GHCs/snapshots. + hledger uses banker's rounding (rounds to nearest even number, eg+ 0.5 with with zero decimal places is "0").++- added: debug helpers traceAt, traceAtWith++- Journal is now a Semigroup, not a Monoid (since <> is right-biased). (Stephen Morgan)+ # 1.17.0.1 2020-03-01 - fix org heading comments and doctest setup comment that were
Hledger/Data/Dates.hs view
@@ -84,6 +84,7 @@ import Control.Monad (guard, unless) import "base-compat-batteries" Data.List.Compat import Data.Default+import Data.Foldable (asum) import Data.Maybe import qualified Data.Set as Set import Data.Text (Text)@@ -654,7 +655,7 @@ -- -- | Parse a couple of date-time string formats to a time type. -- parsedatetimeM :: String -> Maybe LocalTime--- parsedatetimeM s = firstJust [+-- parsedatetimeM s = asum [ -- parseTime defaultTimeLocale "%Y/%m/%d %H:%M:%S" s, -- parseTime defaultTimeLocale "%Y-%m-%d %H:%M:%S" s -- ]@@ -672,9 +673,9 @@ -- `YYYY-MM-DD`, `YYYY/MM/DD` or `YYYY.MM.DD`, with leading zeros required. -- For internal use, not quite the same as the journal's "simple dates". parsedateM :: String -> Maybe Day-parsedateM s = firstJust [- parsetime defaultTimeLocale "%Y/%m/%d" s,+parsedateM s = asum [ parsetime defaultTimeLocale "%Y-%m-%d" s,+ parsetime defaultTimeLocale "%Y/%m/%d" s, parsetime defaultTimeLocale "%Y.%m.%d" s ]
Hledger/Data/Journal.hs view
@@ -90,16 +90,17 @@ import Control.Monad.Reader as R import Control.Monad.ST import Data.Array.ST+import Data.Default (Default(..)) import Data.Function ((&))+import qualified Data.HashTable.Class as H (toList) import qualified Data.HashTable.ST.Cuckoo as H import Data.List import Data.List.Extra (groupSort, nubSort) import qualified Data.Map as M import Data.Maybe #if !(MIN_VERSION_base(4,11,0))-import Data.Monoid+import Data.Semigroup (Semigroup(..)) #endif-import qualified Data.Semigroup as Sem import qualified Data.Set as S import Data.Text (Text) import qualified Data.Text as T@@ -157,7 +158,7 @@ -- ,show $ map fst $ jfiles j -- ] --- The monoid instance for Journal is useful for two situations.+-- The semigroup instance for Journal is useful for two situations. -- -- 1. concatenating finalised journals, eg with multiple -f options: -- FIRST <> SECOND. The second's list fields are appended to the@@ -168,7 +169,9 @@ -- CHILD <> PARENT. A parsed journal's data is in reverse order, so -- this gives what we want. ---instance Sem.Semigroup Journal where+-- Note that (<>) is right-biased, so nulljournal is only a left identity.+-- In particular, this prevents Journal from being a monoid.+instance Semigroup Journal where j1 <> j2 = Journal { jparsedefaultyear = jparsedefaultyear j2 ,jparsedefaultcommodity = jparsedefaultcommodity j2@@ -190,12 +193,8 @@ ,jlastreadtime = max (jlastreadtime j1) (jlastreadtime j2) } -instance Monoid Journal where- mempty = nulljournal-#if !(MIN_VERSION_base(4,11,0))- -- This is redundant starting with base-4.11 / GHC 8.4.- mappend = (Sem.<>)-#endif+instance Default Journal where+ def = nulljournal nulljournal :: Journal nulljournal = Journal {@@ -587,7 +586,29 @@ journalCheckBalanceAssertions :: Journal -> Maybe String journalCheckBalanceAssertions = either Just (const Nothing) . journalBalanceTransactions True --- "Transaction balancing" - inferring missing amounts and checking transaction balancedness and balance assertions+-- "Transaction balancing", including: inferring missing amounts,+-- applying balance assignments, checking transaction balancedness,+-- checking balance assertions, respecting posting dates. These things+-- are all interdependent.+-- WARN tricky algorithm and code ahead. +--+-- Code overview as of 20190219, this could/should be simplified/documented more:+-- parseAndFinaliseJournal['] (Cli/Utils.hs), journalAddForecast (Common.hs), budgetJournal (BudgetReport.hs), tests (BalanceReport.hs)+-- journalBalanceTransactions+-- runST+-- runExceptT+-- balanceTransaction (Transaction.hs)+-- balanceTransactionHelper+-- runReaderT+-- balanceTransactionAndCheckAssertionsB+-- addAmountAndCheckAssertionB+-- addOrAssignAmountAndCheckAssertionB+-- balanceTransactionHelper (Transaction.hs)+-- uiCheckBalanceAssertions d ui@UIState{aopts=UIOpts{cliopts_=copts}, ajournal=j} (ErrorScreen.hs)+-- journalCheckBalanceAssertions+-- journalBalanceTransactions+-- transactionWizard, postingsBalanced (Add.hs), tests (Transaction.hs)+-- balanceTransaction (Transaction.hs) XXX hledger add won't allow balance assignments + missing amount ? -- | Monad used for statefully balancing/amount-inferring/assertion-checking -- a sequence of transactions.@@ -603,37 +624,54 @@ ,bsAssrt :: Bool -- ^ whether to check balance assertions -- mutable ,bsBalances :: H.HashTable s AccountName MixedAmount -- ^ running account balances, initially empty- ,bsTransactions :: STArray s Integer Transaction -- ^ the transactions being balanced+ ,bsTransactions :: STArray s Integer Transaction -- ^ a mutable array of the transactions being balanced+ -- (for efficiency ? journalBalanceTransactions says: not strictly necessary but avoids a sort at the end I think) } -- | Access the current balancing state, and possibly modify the mutable bits, -- lifting through the Except and Reader layers into the Balancing monad.-withB :: (BalancingState s -> ST s a) -> Balancing s a-withB f = ask >>= lift . lift . f+withRunningBalance :: (BalancingState s -> ST s a) -> Balancing s a+withRunningBalance f = ask >>= lift . lift . f --- | Get an account's running balance so far.-getAmountB :: AccountName -> Balancing s MixedAmount-getAmountB acc = withB $ \BalancingState{bsBalances} -> do+-- | Get this account's current exclusive running balance.+getRunningBalanceB :: AccountName -> Balancing s MixedAmount+getRunningBalanceB acc = withRunningBalance $ \BalancingState{bsBalances} -> do fromMaybe 0 <$> H.lookup bsBalances acc --- | Add an amount to an account's running balance, and return the new running balance.-addAmountB :: AccountName -> MixedAmount -> Balancing s MixedAmount-addAmountB acc amt = withB $ \BalancingState{bsBalances} -> do+-- | Add this amount to this account's exclusive running balance.+-- Returns the new running balance.+addToRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount+addToRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do old <- fromMaybe 0 <$> H.lookup bsBalances acc let new = old + amt H.insert bsBalances acc new return new --- | Set an account's running balance to this amount, and return the difference from the old.-setAmountB :: AccountName -> MixedAmount -> Balancing s MixedAmount-setAmountB acc amt = withB $ \BalancingState{bsBalances} -> do+-- | Set this account's exclusive running balance to this amount.+-- Returns the change in exclusive running balance.+setRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount+setRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do old <- fromMaybe 0 <$> H.lookup bsBalances acc H.insert bsBalances acc amt return $ amt - old --- | Update (overwrite) this transaction with a new one.-storeTransactionB :: Transaction -> Balancing s ()-storeTransactionB t = withB $ \BalancingState{bsTransactions} ->+-- | Set this account's exclusive running balance to whatever amount+-- makes its *inclusive* running balance (the sum of exclusive running+-- balances of this account and any subaccounts) be the given amount.+-- Returns the change in exclusive running balance.+setInclusiveRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount+setInclusiveRunningBalanceB acc newibal = withRunningBalance $ \BalancingState{bsBalances} -> do+ oldebal <- fromMaybe 0 <$> H.lookup bsBalances acc+ allebals <- H.toList bsBalances+ let subsibal = -- sum of any subaccounts' running balances+ sum $ map snd $ filter ((acc `isAccountNamePrefixOf`).fst) allebals+ let newebal = newibal - subsibal+ H.insert bsBalances acc newebal+ return $ newebal - oldebal++-- | Update (overwrite) this transaction in the balancing state.+updateTransactionB :: Transaction -> Balancing s ()+updateTransactionB t = withRunningBalance $ \BalancingState{bsTransactions} -> void $ writeArray bsTransactions (tindex t) t -- | Infer any missing amounts (to satisfy balance assignments and@@ -641,30 +679,11 @@ -- and (optional) all balance assertions pass. Or return an error message -- (just the first error encountered). ----- Assumes journalInferCommodityStyles has been called, since those affect transaction balancing.------ This does multiple things because amount inferring, balance assignments,--- balance assertions and posting dates are interdependent.+-- Assumes journalInferCommodityStyles has been called, since those+-- affect transaction balancing. ----- This can be simplified further. Overview as of 20190219:--- @--- ****** parseAndFinaliseJournal['] (Cli/Utils.hs), journalAddForecast (Common.hs), budgetJournal (BudgetReport.hs), tests (BalanceReport.hs)--- ******* journalBalanceTransactions--- ******** runST--- ********* runExceptT--- ********** balanceTransaction (Transaction.hs)--- *********** balanceTransactionHelper--- ********** runReaderT--- *********** balanceTransactionAndCheckAssertionsB--- ************ addAmountAndCheckAssertionB--- ************ addOrAssignAmountAndCheckAssertionB--- ************ balanceTransactionHelper (Transaction.hs)--- ****** uiCheckBalanceAssertions d ui@UIState{aopts=UIOpts{cliopts_=copts}, ajournal=j} (ErrorScreen.hs)--- ******* journalCheckBalanceAssertions--- ******** journalBalanceTransactions--- ****** transactionWizard, postingsBalanced (Add.hs), tests (Transaction.hs)--- ******* balanceTransaction (Transaction.hs) XXX hledger add won't allow balance assignments + missing amount ?--- @+-- This does multiple things at once because amount inferring, balance+-- assignments, balance assertions and posting dates are interdependent. journalBalanceTransactions :: Bool -> Journal -> Either String Journal journalBalanceTransactions assrt j' = let@@ -716,11 +735,9 @@ -- Transaction prices are removed, which helps eg balance-assertions.test: 15. Mix different commodities and assignments. -- This stores the balanced transactions in case 2 but not in case 1. balanceTransactionAndCheckAssertionsB :: Either Posting Transaction -> Balancing s ()- balanceTransactionAndCheckAssertionsB (Left p@Posting{}) = -- update the account's running balance and check the balance assertion if any void $ addAmountAndCheckAssertionB $ removePrices p- balanceTransactionAndCheckAssertionsB (Right t@Transaction{tpostings=ps}) = do -- make sure we can handle the balance assignments mapM_ checkIllegalBalanceAssignmentB ps@@ -733,9 +750,9 @@ Left err -> throwError err Right (t', inferredacctsandamts) -> do -- for each amount just inferred, update the running balance- mapM_ (uncurry addAmountB) inferredacctsandamts+ mapM_ (uncurry addToRunningBalanceB) inferredacctsandamts -- and save the balanced transaction.- storeTransactionB t'+ updateTransactionB t' -- | If this posting has an explicit amount, add it to the account's running balance. -- If it has a missing amount and a balance assignment, infer the amount from, and@@ -744,28 +761,33 @@ -- Then test the balance assertion if any. addOrAssignAmountAndCheckAssertionB :: Posting -> Balancing s Posting addOrAssignAmountAndCheckAssertionB p@Posting{paccount=acc, pamount=amt, pbalanceassertion=mba}+ -- an explicit posting amount | hasAmount p = do- newbal <- addAmountB acc amt+ newbal <- addToRunningBalanceB acc amt whenM (R.reader bsAssrt) $ checkBalanceAssertionB p newbal return p- | Just BalanceAssertion{baamount,batotal} <- mba = do++ -- no explicit posting amount, but there is a balance assignment+ -- TODO this doesn't yet handle inclusive assignments right, #1207+ | Just BalanceAssertion{baamount,batotal,bainclusive} <- mba = do (diff,newbal) <- case batotal of+ -- a total balance assignment (==, all commodities) True -> do- -- a total balance assignment let newbal = Mixed [baamount]- diff <- setAmountB acc newbal+ diff <- (if bainclusive then setInclusiveRunningBalanceB else setRunningBalanceB) acc newbal return (diff,newbal)+ -- a partial balance assignment (=, one commodity) False -> do- -- a partial balance assignment- oldbalothercommodities <- filterMixedAmount ((acommodity baamount /=) . acommodity) <$> getAmountB acc+ oldbalothercommodities <- filterMixedAmount ((acommodity baamount /=) . acommodity) <$> getRunningBalanceB acc let assignedbalthiscommodity = Mixed [baamount] newbal = oldbalothercommodities + assignedbalthiscommodity- diff <- setAmountB acc newbal+ diff <- (if bainclusive then setInclusiveRunningBalanceB else setRunningBalanceB) acc newbal return (diff,newbal) let p' = p{pamount=diff, poriginal=Just $ originalPosting p} whenM (R.reader bsAssrt) $ checkBalanceAssertionB p' newbal return p'- -- no amount, no balance assertion (GHC 7 doesn't like Nothing <- mba here)++ -- no explicit posting amount, no balance assignment | otherwise = return p -- | Add the posting's amount to its account's running balance, and@@ -775,7 +797,7 @@ -- need to see the balance as it stands after each individual posting. addAmountAndCheckAssertionB :: Posting -> Balancing s Posting addAmountAndCheckAssertionB p | hasAmount p = do- newbal <- addAmountB (paccount p) (pamount p)+ newbal <- addToRunningBalanceB (paccount p) (pamount p) whenM (R.reader bsAssrt) $ checkBalanceAssertionB p newbal return p addAmountAndCheckAssertionB p = return p@@ -808,7 +830,7 @@ if isinclusive then -- sum the running balances of this account and any of its subaccounts seen so far- withB $ \BalancingState{bsBalances} ->+ withRunningBalance $ \BalancingState{bsBalances} -> H.foldM (\ibal (acc, amt) -> return $ ibal + if assertedacct==acc || assertedacct `isAccountNamePrefixOf` acc then amt else 0)
Hledger/Read.hs view
@@ -48,21 +48,24 @@ import qualified Control.Exception as C import Control.Monad (when) import "mtl" Control.Monad.Except (runExceptT)-import Data.Default-import Data.List-import Data.Maybe-import Data.Ord+import Data.Default (def)+import Data.Foldable (asum)+import Data.List (group, sort, sortBy)+import Data.List.NonEmpty (nonEmpty)+import Data.Maybe (fromMaybe)+import Data.Ord (comparing)+import Data.Semigroup (sconcat) import Data.Text (Text) import qualified Data.Text as T import Data.Time (Day)-import Safe+import Safe (headDef) import System.Directory (doesFileExist, getHomeDirectory) import System.Environment (getEnv) import System.Exit (exitFailure)-import System.FilePath+import System.FilePath ((<.>), (</>), splitDirectories, splitFileName) import System.Info (os)-import System.IO-import Text.Printf+import System.IO (stderr, writeFile)+import Text.Printf (hPrintf, printf) import Hledger.Data.Dates (getCurrentDay, parsedate, showDate) import Hledger.Data.Types@@ -149,11 +152,7 @@ -- Also the final parse state saved in the Journal does span all files. readJournalFiles :: InputOpts -> [PrefixedFilePath] -> IO (Either String Journal) readJournalFiles iopts =- (right mconcat1 . sequence <$>) . mapM (readJournalFile iopts)- where- mconcat1 :: Monoid t => [t] -> t- mconcat1 [] = mempty- mconcat1 x = foldr1 mappend x+ fmap (right (maybe def sconcat . nonEmpty) . sequence) . mapM (readJournalFile iopts) -- | Read a Journal from this file, or from stdin if the file path is -, -- or return an error message. The file path can have a READER: prefix.@@ -170,7 +169,7 @@ readJournalFile iopts prefixedfile = do let (mfmt, f) = splitReaderPrefix prefixedfile- iopts' = iopts{mformat_=firstJust [mfmt, mformat_ iopts]}+ iopts' = iopts{mformat_=asum [mfmt, mformat_ iopts]} requireJournalFileExists f t <- readFileOrStdinPortably f -- <- T.readFile f -- or without line ending translation, for testing
Hledger/Read/Common.hs view
@@ -222,7 +222,7 @@ runJournalParser, rjp :: Monad m => JournalParser m a -> Text -> m (Either (ParseErrorBundle Text CustomErr) a)-runJournalParser p t = runParserT (evalStateT p mempty) "" t+runJournalParser p t = runParserT (evalStateT p nulljournal) "" t rjp = runJournalParser -- | Run an erroring journal parser in some monad. See also: parseWithState.@@ -232,7 +232,7 @@ -> Text -> m (Either FinalParseError (Either (ParseErrorBundle Text CustomErr) a)) runErroringJournalParser p t =- runExceptT $ runParserT (evalStateT p mempty) "" t+ runExceptT $ runParserT (evalStateT p nulljournal) "" t rejp = runErroringJournalParser genericSourcePos :: SourcePos -> GenericSourcePos@@ -680,7 +680,7 @@ -- | Parse an amount from a string, or get an error. amountp' :: String -> Amount amountp' s =- case runParser (evalStateT (amountp <* eof) mempty) "" (T.pack s) of+ case runParser (evalStateT (amountp <* eof) nulljournal) "" (T.pack s) of Right amt -> amt Left err -> error' $ show err -- XXX should throwError @@ -766,12 +766,12 @@ -- attributes. -- -- Some international number formats are accepted, eg either period or comma--- may be used for the decimal point, and the other of these may be used for+-- may be used for the decimal mark, and the other of these may be used for -- separating digit groups in the integer part. See -- http://en.wikipedia.org/wiki/Decimal_separator for more examples. -- -- This returns: the parsed numeric value, the precision (number of digits--- seen following the decimal point), the decimal point character used if any,+-- seen following the decimal mark), the decimal mark character used if any, -- and the digit group style if any. -- numberp :: Maybe AmountStyle -> TextParser m (Quantity, Int, Maybe Char, Maybe DigitGroupStyle)
Hledger/Read/CsvReader.hs view
@@ -457,6 +457,8 @@ lift (dbgparse 2 "trying journalfieldnamep") T.unpack <$> choiceInState (map (lift . string . T.pack) journalfieldnames) +maxpostings = 9+ -- Transaction fields and pseudo fields for CSV conversion. -- Names must precede any other name they contain, for the parser -- (amount-in before amount; date2 before date). TODO: fix@@ -468,7 +470,7 @@ ,"balance" ++ i ,"comment" ++ i ,"currency" ++ i- ] | x <- [1..9], let i = show x]+ ] | x <- [1..maxpostings], let i = show x] ++ ["amount-in" ,"amount-out"@@ -757,13 +759,6 @@ csvRule :: CsvRules -> DirectiveName -> Maybe FieldTemplate csvRule rules = (`getDirective` rules) --- | Look up the final value assigned to a csv rule by rule keyword, taking--- into account the current record and conditional rules.--- Generally rules with keywords ("directives") don't have interpolated--- values, but for now it's possible.-csvRuleValue :: CsvRules -> CsvRecord -> DirectiveName -> Maybe String-csvRuleValue rules record = fmap (renderTemplate rules record) . csvRule rules- -- | Look up the value template assigned to a hledger field by field -- list/field assignment rules, taking into account the current record and -- conditional rules.@@ -775,8 +770,6 @@ hledgerFieldValue :: CsvRules -> CsvRecord -> HledgerFieldName -> Maybe String hledgerFieldValue rules record = fmap (renderTemplate rules record) . hledgerField rules record -s `withDefault` def = if null s then def else s- transactionFromCsvRecord :: SourcePos -> CsvRules -> CsvRecord -> Transaction transactionFromCsvRecord sourcepos rules record = t where@@ -828,64 +821,27 @@ precomment = maybe "" singleline $ fieldval "precomment" ----------------------------------------------------------------------- -- 3. Generate the postings-- -- Make posting 1 if possible, with special support for old syntax to- -- support pre-1.16 rules.- posting1 = mkPosting rules record "1"- ("account1" `withAlias` "account")- ("amount1" `withAlias` "amount")- ("amount1-in" `withAlias` "amount-in")- ("amount1-out" `withAlias` "amount-out")- ("balance1" `withAlias` "balance")- "comment1" -- comment1 does not have legacy alias- t- where- withAlias fld alias =- case (field fld, field alias) of- (Just fld, Just alias) -> error' $ unlines- [ "error: both \"" ++ fld ++ "\" and \"" ++ alias ++ "\" have values."- , showRecord record- , showRules rules record- ]- (Nothing, Just _) -> alias- (_, Nothing) -> fld-- -- Make other postings where possible, and gather all that were generated.- postings = catMaybes $ posting1 : otherpostings- where- otherpostings = [mkPostingN i | x<-[2..9], let i = show x]- where- mkPostingN n = mkPosting rules record n- ("account"++n) ("amount"++n) ("amount"++n++"-in")- ("amount"++n++"-out") ("balance"++n) ("comment"++n) t- - -- Auto-generate a second posting or second posting amount,- -- for compatibility with pre-1.16 rules.- postings' =- case postings of- -- when rules generate just one posting, of a kind that needs to be- -- balanced, generate the second posting to balance it.- [p1@(p1',_)] ->- if ptype p1' == VirtualPosting then [p1] else [p1, p2]- where- p2 = (nullposting{paccount=unknownExpenseAccount- ,pamount=costOfMixedAmount (-pamount p1')- ,ptransaction=Just t}, False)- -- when rules generate exactly two postings, and only the second has- -- no amount, give it the balancing amount.- [p1@(p1',_), p2@(p2',final2)] ->- if hasAmount p1' && not (hasAmount p2')- then [p1, (p2'{pamount=costOfMixedAmount(-(pamount p1'))}, final2)]- else [p1, p2]- --- ps -> ps+ -- 3. Generate the postings for which an account has been assigned+ -- (possibly indirectly due to an amount or balance assignment) - -- Finally, wherever default "unknown" accounts were used, refine them- -- based on the sign of the posting amount if it's now known.- postings'' = map maybeImprove postings'- where- maybeImprove (p,final) = if final then p else improveUnknownAccountName p+ p1IsVirtual = (accountNamePostingType . T.pack <$> fieldval "account1") == Just VirtualPosting+ ps = [p | n <- [1..maxpostings]+ ,let comment = T.pack $ fromMaybe "" $ fieldval ("comment"++show n)+ ,let currency = fromMaybe "" (fieldval ("currency"++show n) <|> fieldval "currency")+ ,let mamount = getAmount rules record currency p1IsVirtual n+ ,let mbalance = getBalance rules record currency n+ ,Just (acct,isfinal) <- [getAccount rules record mamount mbalance n] -- skips Nothings+ ,let acct' | not isfinal && acct==unknownExpenseAccount &&+ fromMaybe False (mamount >>= isNegativeMixedAmount) = unknownIncomeAccount+ | otherwise = acct+ ,let p = nullposting{paccount = accountNameWithoutPostingType acct'+ ,pamount = fromMaybe missingmixedamt mamount+ ,ptransaction = Just t+ ,pbalanceassertion = mkBalanceAssertion rules record <$> mbalance+ ,pcomment = comment+ ,ptype = accountNamePostingType acct+ }+ ] ---------------------------------------------------------------------- -- 4. Build the transaction (and name it, so the postings can reference it).@@ -899,99 +855,91 @@ ,tdescription = T.pack description ,tcomment = T.pack comment ,tprecedingcomment = T.pack precomment- ,tpostings = postings''+ ,tpostings = ps } --- | Given CSV rules and a CSV record, generate the corresponding transaction's--- Nth posting, if sufficient fields have been assigned for it.--- N is provided as a string.--- The names of the required fields are provided, allowing more flexibility.--- The transaction which will contain this posting is also provided,--- so we can build the usual transaction<->posting cyclic reference.-mkPosting ::- CsvRules -> CsvRecord -> String ->- HledgerFieldName -> HledgerFieldName -> HledgerFieldName ->- HledgerFieldName -> HledgerFieldName -> HledgerFieldName ->- Transaction ->- Maybe (Posting, Bool)-mkPosting rules record number accountFld amountFld amountInFld amountOutFld balanceFld commentFld t =- -- if we have figured out an account N, make a posting N- case maccountAndIsFinal of- Nothing -> Nothing- Just (acct, final) ->- Just (posting{paccount = accountNameWithoutPostingType acct- ,pamount = fromMaybe missingmixedamt mamount- ,ptransaction = Just t- ,pbalanceassertion = mkBalanceAssertion rules record <$> mbalance- ,pcomment = comment- ,ptype = accountNamePostingType acct}- ,final)+-- | Figure out the amount specified for posting N, if any.+-- Looks for a non-zero amount assigned to one of "amountN", "amountN-in", "amountN-out".+-- Postings 1 or 2 also look at "amount", "amount-in", "amount-out".+-- Throws an error if more than one of these has a non-zero amount assigned.+-- A currency symbol to prepend to the amount, if any, is provided,+-- and whether posting 1 requires balancing or not.+getAmount :: CsvRules -> CsvRecord -> String -> Bool -> Int -> Maybe MixedAmount+getAmount rules record currency p1IsVirtual n =+ let+ unnumberedfieldnames = ["amount","amount-in","amount-out"]+ fieldnames = map (("amount"++show n)++) ["","-in","-out"]+ -- For posting 1, also recognise the old amount/amount-in/amount-out names.+ -- For posting 2, the same but only if posting 1 needs balancing.+ ++ if n==1 || n==2 && not p1IsVirtual then unnumberedfieldnames else []+ nonzeroamounts = [(f,a') | f <- fieldnames+ , Just v@(_:_) <- [strip . renderTemplate rules record <$> hledgerField rules record f]+ , let a = parseAmount rules record currency v+ , not $ isZeroMixedAmount a+ -- With amount/amount-in/amount-out, in posting 2,+ -- flip the sign and convert to cost, as they did before 1.17+ , let a' = if f `elem` unnumberedfieldnames && n==2 then costOfMixedAmount (-a) else a+ ]+ in case nonzeroamounts of+ [] -> Nothing+ [(f,a)] | "-out" `isSuffixOf` f -> Just (-a) -- for -out fields, flip the sign+ [(_,a)] -> Just a+ fs -> error' $+ "more than one non-zero amount for this record, please ensure just one\n"+ ++ unlines [" " ++ padright 11 f ++ ": " ++ showMixedAmount a+ ++ " from rule: " ++ fromMaybe "" (hledgerField rules record f)+ | (f,a) <- fs]+ ++ " " ++ showRecord record ++ "\n" where- -- the account name to use for this posting, if any, and whether it is the- -- default unknown account, which may be improved later, or an explicitly- -- set account, which may not.- maccountAndIsFinal :: Maybe (AccountName, Bool) =- case maccount of- -- accountN is set to the empty string - no posting will be generated- Just "" -> Nothing- -- accountN is set (possibly to "expenses:unknown"! #1192) - mark it final- Just a -> Just (a, True)- -- accountN is unset- Nothing ->- case (mamount, mbalance) of- -- amountN is set, or implied by balanceN - set accountN to- -- the default unknown account ("expenses:unknown") and- -- allow it to be improved later- (Just _, _) -> Just (unknownExpenseAccount, False)- (_, Just _) -> Just (unknownExpenseAccount, False)- -- amountN is also unset - no posting will be generated- (Nothing, Nothing) -> Nothing+ -- | Given a non-empty amount string to parse, along with a possibly+ -- non-empty currency symbol to prepend, parse as a hledger amount (as+ -- in journal format), or raise an error.+ -- The CSV rules and record are provided for the error message.+ parseAmount :: CsvRules -> CsvRecord -> String -> String -> MixedAmount+ parseAmount rules record currency amountstr =+ either mkerror (Mixed . (:[])) $+ runParser (evalStateT (amountp <* eof) nulljournal) "" $+ T.pack $ (currency++) $ simplifySign amountstr where- maccount = T.pack <$> (fieldval accountFld- -- XXX what's this needed for ? Test & document, or drop.- -- Also, this the only place we interpolate in a keyword rule, I think.- `withDefault` ruleval ("default-account" ++ number))- -- XXX what's this needed for ? Test & document, or drop.- mdefaultcurrency = rule "default-currency"- currency = fromMaybe (fromMaybe "" mdefaultcurrency) $- fieldval ("currency"++number) `withDefault` fieldval "currency"- mamount = chooseAmount rules record currency amountFld amountInFld amountOutFld- mbalance :: Maybe (Amount, GenericSourcePos) =- fieldval balanceFld >>= parsebalance currency number+ mkerror e = error' $ unlines+ ["error: could not parse \""++amountstr++"\" as an amount"+ ,showRecord record+ ,showRules rules record+ -- ,"the default-currency is: "++fromMaybe "unspecified" (getDirective "default-currency" rules)+ ,"the parse error is: "++customErrorBundlePretty e+ ,"you may need to "+ ++"change your amount*, balance*, or currency* rules, "+ ++"or add or change your skip rule"+ ]++-- | Figure out the expected balance (assertion or assignment) specified for posting N,+-- if any (and its parse position).+getBalance :: CsvRules -> CsvRecord -> String -> Int -> Maybe (Amount, GenericSourcePos)+getBalance rules record currency n =+ (fieldval ("balance"++show n)+ -- for posting 1, also recognise the old field name+ <|> if n==1 then fieldval "balance" else Nothing)+ >>= parsebalance currency n . strip+ where+ parsebalance currency n s+ | null s = Nothing+ | otherwise = Just+ (either (mkerror n s) id $+ runParser (evalStateT (amountp <* eof) nulljournal) "" $+ T.pack $ (currency++) $ simplifySign s+ ,nullsourcepos) -- XXX parse position to show when assertion fails,+ -- the csv record's line number would be good where- parsebalance currency n str- | all isSpace str = Nothing- | otherwise = Just- (either (balanceerror n str) id $- runParser (evalStateT (amountp <* eof) mempty) "" $- T.pack $ (currency++) $ simplifySign str- ,nullsourcepos) -- XXX parse position to show when assertion fails,- -- the csv record's line number would be good- where- balanceerror n str err = error' $ unlines- ["error: could not parse \""++str++"\" as balance"++n++" amount"- ,showRecord record- ,showRules rules record- ,"the default-currency is: "++fromMaybe "unspecified" mdefaultcurrency- ,"the parse error is: "++customErrorBundlePretty err- ]- comment = T.pack $ fromMaybe "" $ fieldval commentFld- rule = csvRule rules :: DirectiveName -> Maybe FieldTemplate- ruleval = csvRuleValue rules record :: DirectiveName -> Maybe String- -- field = hledgerField rules record :: HledgerFieldName -> Maybe FieldTemplate+ mkerror n s e = error' $ unlines+ ["error: could not parse \""++s++"\" as balance"++show n++" amount"+ ,showRecord record+ ,showRules rules record+ -- ,"the default-currency is: "++fromMaybe "unspecified" mdefaultcurrency+ ,"the parse error is: "++customErrorBundlePretty e+ ]+ -- mdefaultcurrency = rule "default-currency" fieldval = hledgerFieldValue rules record :: HledgerFieldName -> Maybe String--- | Default account names to use when needed.-unknownExpenseAccount = "expenses:unknown"-unknownIncomeAccount = "income:unknown" --- | If this posting has the "expenses:unknown" account name,--- replace that with "income:unknown" if the amount is negative.--- The posting's amount should be explicit.-improveUnknownAccountName p@Posting{..}- | paccount == unknownExpenseAccount- && fromMaybe False (isNegativeMixedAmount pamount) = p{paccount=unknownIncomeAccount}- | otherwise = p- -- | Make a balance assertion for the given amount, with the given parse -- position (to be shown in assertion failures), with the assertion type -- possibly set by a balance-type rule.@@ -1013,48 +961,33 @@ , showRules rules record ] -chooseAmount :: CsvRules -> CsvRecord -> String -> String -> String -> String -> Maybe MixedAmount-chooseAmount rules record currency amountFld amountInFld amountOutFld =- let- mamount = getEffectiveAssignment rules record amountFld- mamountin = getEffectiveAssignment rules record amountInFld- mamountout = getEffectiveAssignment rules record amountOutFld- parse amt = notZero =<< (parseAmount currency <$> notEmpty =<< (strip . renderTemplate rules record) <$> amt)- in- case (parse mamount, parse mamountin, parse mamountout) of- (Nothing, Nothing, Nothing) -> Nothing- (Just a, Nothing, Nothing) -> Just a- (Nothing, Just i, Nothing) -> Just i- (Nothing, Nothing, Just o) -> Just $ negate o- (Nothing, Just i, Just o) -> error' $ "both "++amountInFld++" and "++amountOutFld++" have a value\n"- ++ " "++amountInFld++": " ++ show i ++ "\n"- ++ " "++amountOutFld++": " ++ show o ++ "\n"- ++ " record: " ++ showRecord record- _ -> error' $ "found values for "++amountFld++" and for "++amountInFld++"/"++amountOutFld++"\n"- ++ "please use either "++amountFld++" or "++amountInFld++"/"++amountOutFld++"\n"- ++ " record: " ++ showRecord record- where- notZero amt = if isZeroMixedAmount amt then Nothing else Just amt- notEmpty str = if str=="" then Nothing else Just str-- parseAmount currency amountstr =- either (amounterror amountstr) (Mixed . (:[]))- <$> runParser (evalStateT (amountp <* eof) mempty) ""- <$> T.pack- <$> (currency++)- <$> simplifySign- <$> amountstr+-- | Figure out the account name specified for posting N, if any.+-- And whether it is the default unknown account (which may be+-- improved later) or an explicitly set account (which may not).+getAccount :: CsvRules -> CsvRecord -> Maybe MixedAmount -> Maybe (Amount, GenericSourcePos) -> Int -> Maybe (AccountName, Bool)+getAccount rules record mamount mbalance n =+ let+ fieldval = hledgerFieldValue rules record :: HledgerFieldName -> Maybe String+ maccount = T.pack <$> fieldval ("account"++show n)+ in case maccount of+ -- accountN is set to the empty string - no posting will be generated+ Just "" -> Nothing+ -- accountN is set (possibly to "expenses:unknown"! #1192) - mark it final+ Just a -> Just (a, True)+ -- accountN is unset+ Nothing ->+ case (mamount, mbalance) of+ -- amountN is set, or implied by balanceN - set accountN to+ -- the default unknown account ("expenses:unknown") and+ -- allow it to be improved later+ (Just _, _) -> Just (unknownExpenseAccount, False)+ (_, Just _) -> Just (unknownExpenseAccount, False)+ -- amountN is also unset - no posting will be generated+ (Nothing, Nothing) -> Nothing - amounterror amountstr err = error' $ unlines- ["error: could not parse \""++fromJust amountstr++"\" as an amount"- ,showRecord record- ,showRules rules record- ,"the default-currency is: "++fromMaybe "unspecified" (getDirective "default-currency" rules)- ,"the parse error is: "++customErrorBundlePretty err- ,"you may need to "- ++"change your amount or currency rules, "- ++"or add or change your skip rule"- ]+-- | Default account names to use when needed.+unknownExpenseAccount = "expenses:unknown"+unknownIncomeAccount = "income:unknown" type CsvAmountString = String @@ -1088,7 +1021,7 @@ -- | Show a (approximate) recreation of the original CSV record. showRecord :: CsvRecord -> String-showRecord r = "the CSV record is: "++intercalate "," (map show r)+showRecord r = "record values: "++intercalate "," (map show r) -- | Given the conversion rules, a CSV record and a hledger field name, find -- the value template ultimately assigned to this field, if any, by a field@@ -1152,7 +1085,7 @@ -- the "simple date" formats (YYYY/MM/DD, YYYY-MM-DD, YYYY.MM.DD, leading -- zeroes optional). parseDateWithCustomOrDefaultFormats :: Maybe DateFormat -> String -> Maybe Day-parseDateWithCustomOrDefaultFormats mformat s = firstJust $ map parsewith formats+parseDateWithCustomOrDefaultFormats mformat s = asum $ map parsewith formats where parsetime = #if MIN_VERSION_time(1,5,0)
Hledger/Read/JournalReader.hs view
@@ -82,9 +82,10 @@ import Control.Monad.Except (ExceptT(..), runExceptT) import Control.Monad.State.Strict (get,modify',put) import Control.Monad.Trans.Class (lift)+import Data.Either (isRight) import qualified Data.Map.Strict as M #if !(MIN_VERSION_base(4,11,0))-import Data.Monoid ((<>))+import Data.Semigroup ((<>)) #endif import Data.Text (Text) import Data.String@@ -297,7 +298,7 @@ put $ updatedChildj <> parentj newJournalWithParseStateFrom :: FilePath -> Journal -> Journal- newJournalWithParseStateFrom filepath j = mempty{+ newJournalWithParseStateFrom filepath j = nulljournal{ jparsedefaultyear = jparsedefaultyear j ,jparsedefaultcommodity = jparsedefaultcommodity j ,jparseparentaccounts = jparseparentaccounts j@@ -424,7 +425,15 @@ else modify' (\j -> j{jcommodities=M.insert acommodity comm $ jcommodities j}) pleaseincludedecimalpoint :: String-pleaseincludedecimalpoint = "to avoid ambiguity, please include a decimal separator in commodity directives"+pleaseincludedecimalpoint = chomp $ unlines [+ "Please include a decimal point or decimal comma in commodity directives,"+ ,"to help us parse correctly. It may be followed by zero or more decimal digits."+ ,"Examples:"+ ,"commodity $1000. ; no thousands mark, decimal period, no decimals"+ ,"commodity 1.234,00 ARS ; period at thousands, decimal comma, 2 decimals"+ ,"commodity EUR 1 000,000 ; space at thousands, decimal comma, 3 decimals"+ ,"commodity INR1,23,45,678.0 ; comma at thousands/lakhs/crores, decimal period, 1 decimal"+ ] -- | Parse a multi-line commodity directive, containing 0 or more format subdirectives. --@@ -746,7 +755,7 @@ ,test "yearless date with no default year" $ assertParseError datep "1/1" "current year is unknown" ,test "yearless date with default year" $ do let s = "1/1"- ep <- parseWithState mempty{jparsedefaultyear=Just 2018} datep s+ ep <- parseWithState nulljournal{jparsedefaultyear=Just 2018} datep s either (assertFailure . ("parse error at "++) . customErrorBundlePretty) (const $ return ()) ep ,test "no leading zero" $ assertParse datep "2018/1/1" ]@@ -968,7 +977,7 @@ ,test "defaultcommoditydirectivep" $ do assertParse defaultcommoditydirectivep "D $1,000.0\n"- assertParseError defaultcommoditydirectivep "D $1000\n" "please include a decimal separator"+ assertParseError defaultcommoditydirectivep "D $1000\n" "Please include a decimal point or decimal comma" ,tests "defaultyeardirectivep" [ test "1000" $ assertParse defaultyeardirectivep "Y 1000" -- XXX no \n like the others
Hledger/Utils.hs view
@@ -146,13 +146,6 @@ instance Default Bool where def = False -isLeft :: Either a b -> Bool-isLeft (Left _) = True-isLeft _ = False--isRight :: Either a b -> Bool-isRight = not . isLeft- -- | Apply a function the specified number of times, -- which should be > 0 (otherwise does nothing). -- Possibly uses O(n) stack ?@@ -177,10 +170,6 @@ ('~':'\\':p) -> (</> p) <$> getHomeDirectory ('~':_) -> ioError $ userError "~USERNAME in paths is not supported" p -> return p--firstJust ms = case dropWhile (==Nothing) ms of- [] -> Nothing- (md:_) -> md -- | Read text from a file, -- converting any \r\n line endings to \n,,
Hledger/Utils/Debug.hs view
@@ -12,6 +12,8 @@ ,pshow ,ptrace ,traceWith+ ,traceAt+ ,traceAtWith ,debugLevel ,ptraceAt ,ptraceAtWith@@ -81,8 +83,9 @@ ptrace :: Show a => a -> a ptrace = traceWith pshow --- | Trace (print to stderr) a showable value using a custom show function.-traceWith :: (a -> String) -> a -> a+-- | Like traceShowId, but uses a custom show function to render the value.+-- traceShowIdWith was too much of a mouthful.+traceWith :: Show a => (a -> String) -> a -> a traceWith f a = trace (f a) a -- | Global debug level, which controls the verbosity of debug output@@ -107,6 +110,18 @@ where args = unsafePerformIO getArgs++-- | Trace (print to stderr) a string if the global debug level is at+-- or above the specified level. At level 0, always prints. Otherwise,+-- uses unsafePerformIO.+traceAt :: Int -> String -> a -> a+traceAt level+ | level > 0 && debugLevel < level = flip const+ | otherwise = trace++-- | Trace (print to stderr) a showable value using a custom show function.+traceAtWith :: (a -> String) -> a -> a+traceAtWith f a = trace (f a) a -- | Pretty-print a label and a showable value to the console -- if the global debug level is at or above the specified level.
Hledger/Utils/Test.hs view
@@ -25,8 +25,9 @@ import Control.Monad.Except (ExceptT, runExceptT) import Control.Monad.State.Strict (StateT, evalStateT, execStateT)+import Data.Default (Default(..)) #if !(MIN_VERSION_base(4,11,0))-import Data.Monoid ((<>))+import Data.Semigroup ((<>)) #endif -- import Data.CallStack import Data.List (isInfixOf)@@ -73,35 +74,35 @@ -- | Assert that this stateful parser runnable in IO successfully parses -- all of the given input text, showing the parse error if it fails. -- Suitable for hledger's JournalParser parsers.-assertParse :: (HasCallStack, Eq a, Show a, Monoid st) =>+assertParse :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> Assertion assertParse parser input = do- ep <- runParserT (evalStateT (parser <* eof) mempty) "" input+ ep <- runParserT (evalStateT (parser <* eof) def) "" input either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty) (const $ return ()) ep -- | Assert a parser produces an expected value.-assertParseEq :: (HasCallStack, Eq a, Show a, Monoid st) =>+assertParseEq :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> a -> Assertion assertParseEq parser input expected = assertParseEqOn parser input id expected -- | Like assertParseEq, but transform the parse result with the given function -- before comparing it.-assertParseEqOn :: (HasCallStack, Eq b, Show b, Monoid st) =>+assertParseEqOn :: (HasCallStack, Eq b, Show b, Default st) => StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> Assertion assertParseEqOn parser input f expected = do- ep <- runParserT (evalStateT (parser <* eof) mempty) "" input+ ep <- runParserT (evalStateT (parser <* eof) def) "" input either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty) (assertEqual "" expected . f) ep -- | Assert that this stateful parser runnable in IO fails to parse -- the given input text, with a parse error containing the given string.-assertParseError :: (HasCallStack, Eq a, Show a, Monoid st) =>+assertParseError :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr T.Text IO) a -> String -> String -> Assertion assertParseError parser input errstr = do- ep <- runParserT (evalStateT parser mempty) "" (T.pack input)+ ep <- runParserT (evalStateT parser def) "" (T.pack input) case ep of Right v -> assertFailure $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n" Left e -> do@@ -113,28 +114,28 @@ -- | Run a stateful parser in IO like assertParse, then assert that the -- final state (the wrapped state, not megaparsec's internal state), -- transformed by the given function, matches the given expected value.-assertParseStateOn :: (HasCallStack, Eq b, Show b, Monoid st) =>+assertParseStateOn :: (HasCallStack, Eq b, Show b, Default st) => StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (st -> b) -> b -> Assertion assertParseStateOn parser input f expected = do- es <- runParserT (execStateT (parser <* eof) mempty) "" input+ es <- runParserT (execStateT (parser <* eof) def) "" input case es of Left err -> assertFailure $ (++"\n") $ ("\nparse error at "++) $ customErrorBundlePretty err Right s -> assertEqual "" expected $ f s -- | These "E" variants of the above are suitable for hledger's ErroringJournalParser parsers. assertParseE- :: (HasCallStack, Eq a, Show a, Monoid st)+ :: (HasCallStack, Eq a, Show a, Default st) => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a -> T.Text -> Assertion assertParseE parser input = do let filepath = "" eep <- runExceptT $- runParserT (evalStateT (parser <* eof) mempty) filepath input+ runParserT (evalStateT (parser <* eof) def) filepath input case eep of Left finalErr -> let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr@@ -145,7 +146,7 @@ ep assertParseEqE- :: (Monoid st, Eq a, Show a, HasCallStack)+ :: (Default st, Eq a, Show a, HasCallStack) => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a -> T.Text -> a@@ -153,7 +154,7 @@ assertParseEqE parser input expected = assertParseEqOnE parser input id expected assertParseEqOnE- :: (HasCallStack, Eq b, Show b, Monoid st)+ :: (HasCallStack, Eq b, Show b, Default st) => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a -> T.Text -> (a -> b)@@ -161,7 +162,7 @@ -> Assertion assertParseEqOnE parser input f expected = do let filepath = ""- eep <- runExceptT $ runParserT (evalStateT (parser <* eof) mempty) filepath input+ eep <- runExceptT $ runParserT (evalStateT (parser <* eof) def) filepath input case eep of Left finalErr -> let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr@@ -172,14 +173,14 @@ ep assertParseErrorE- :: (Monoid st, Eq a, Show a, HasCallStack)+ :: (Default st, Eq a, Show a, HasCallStack) => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a -> T.Text -> String -> Assertion assertParseErrorE parser input errstr = do let filepath = ""- eep <- runExceptT $ runParserT (evalStateT parser mempty) filepath input+ eep <- runExceptT $ runParserT (evalStateT parser def) filepath input case eep of Left finalErr -> do let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
hledger-lib.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 4f9a789b279703a75cecaf4e5cca835f20a5ddb9cfeb9e98e62db665a573d016+-- hash: 2280d91d2a756a6b17f33e15453b491d8945496a3876f4df74683110a0cc5d08 name: hledger-lib-version: 1.17.0.1+version: 1.17.1 synopsis: Core data types, parsers and functionality for the hledger accounting tools description: This is a reusable library containing hledger's core functionality. .@@ -106,7 +106,7 @@ ./. ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans build-depends:- Decimal+ Decimal >=0.5.1 , Glob >=0.9 , aeson , ansi-terminal >=0.6.2.3@@ -157,7 +157,7 @@ test ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans build-depends:- Decimal+ Decimal >=0.5.1 , Glob >=0.7 , aeson , ansi-terminal >=0.6.2.3@@ -199,8 +199,8 @@ , transformers >=0.2 , uglymemo , utf8-string >=0.3.5- buildable: False- if (impl(ghc < 8.2))+ buildable: True+ if (impl(ghc < 8.2) || impl(ghc >= 8.10)) buildable: False default-language: Haskell2010 @@ -212,7 +212,7 @@ test ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans build-depends:- Decimal+ Decimal >=0.5.1 , Glob >=0.9 , aeson , ansi-terminal >=0.6.2.3@@ -254,5 +254,4 @@ , transformers >=0.2 , uglymemo , utf8-string >=0.3.5- buildable: True default-language: Haskell2010
hledger_csv.5 view
@@ -1,6 +1,6 @@ .\"t -.TH "hledger_csv" "5" "March 2020" "hledger 1.17" "hledger User Manuals"+.TH "hledger_csv" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals" @@ -487,34 +487,48 @@ \f[C]description\f[R], \f[C]comment\f[R] can be used to form the transaction\[aq]s first line. .SS Posting field names+.SS account .PP-\f[C]accountN\f[R], where N is 1 to 9, generates a posting, with that-account name.+\f[C]accountN\f[R], where N is 1 to 9, causes a posting to be generated,+with that account name.+.PP Most often there are two postings, so you\[aq]ll want to set \f[C]account1\f[R] and \f[C]account2\f[R].-If a posting\[aq]s account name is left unset but its amount is set, a-default account name will be chosen (like expenses:unknown or-income:unknown).+Typically \f[C]account1\f[R] is associated with the CSV file, and is set+once with a top-level assignment, while \f[C]account2\f[R] is set based+on each transaction\[aq]s description, and in conditional blocks. .PP+If a posting\[aq]s account name is left unset but its amount is set (see+below), a default account name will be chosen (like+\[dq]expenses:unknown\[dq] or \[dq]income:unknown\[dq]).+.SS amount+.PP \f[C]amountN\f[R] sets posting N\[aq]s amount.-Or, \f[C]amount\f[R] with no N sets posting 1\[aq]s.-If the CSV has debits and credits in separate fields, use-\f[C]amountN-in\f[R] and \f[C]amountN-out\f[R] instead.-Or \f[C]amount-in\f[R] and \f[C]amount-out\f[R] with no N for posting 1.+If the CSV uses separate fields for debit and credit amounts, you can+use \f[C]amountN-in\f[R] and \f[C]amountN-out\f[R] instead. .PP-For convenience and backwards compatibility, if you set the amount of-posting 1 only, a second posting with the negative amount will be-generated automatically.-(Unless the account name is parenthesised indicating an unbalanced-posting.)+Also, for compatibility with hledger <1.17: \f[C]amount\f[R] or+\f[C]amount-in\f[R]/\f[C]amount-out\f[R] with no number sets the amount+for postings 1 and 2.+For posting 2 the amount is negated, and converted to cost if+there\[aq]s a transaction price.+.SS currency .PP-If the CSV has the currency symbol in a separate field, you can use-\f[C]currencyN\f[R] to prepend it to posting N\[aq]s amount.-\f[C]currency\f[R] with no N affects ALL postings.+If the CSV has the currency symbol in a separate field (ie, not part of+the amount field), you can use \f[C]currencyN\f[R] to prepend it to+posting N\[aq]s amount.+Or, \f[C]currency\f[R] with no number affects all postings.+.SS balance .PP \f[C]balanceN\f[R] sets a balance assertion amount (or if the posting-amount is left empty, a balance assignment).-You may need to adjust this with the \f[C]balance-type\f[R] rule.+amount is left empty, a balance assignment) on posting N.+.PP+Also, for compatibility with hledger <1.17: \f[C]balance\f[R] with no+number is equivalent to \f[C]balance1\f[R].+.PP+You can adjust the type of assertion/assignment with the+\f[C]balance-type\f[R] rule (see below).+.SS comment .PP Finally, \f[C]commentN\f[R] sets a comment on the Nth posting. Comments can also contain tags, as usual.
hledger_csv.info view
@@ -3,8 +3,8 @@ File: hledger_csv.info, Node: Top, Next: EXAMPLES, Up: (dir) -hledger_csv(5) hledger 1.17-***************************+hledger_csv(5) hledger 1.17.1+***************************** CSV - how hledger reads CSV data, and the CSV rules file format @@ -451,31 +451,79 @@ 2.2.2 Posting field names ------------------------- -'accountN', where N is 1 to 9, generates a posting, with that account-name. Most often there are two postings, so you'll want to set-'account1' and 'account2'. If a posting's account name is left unset-but its amount is set, a default account name will be chosen (like-expenses:unknown or income:unknown).+* Menu: - 'amountN' sets posting N's amount. Or, 'amount' with no N sets-posting 1's. If the CSV has debits and credits in separate fields, use-'amountN-in' and 'amountN-out' instead. Or 'amount-in' and 'amount-out'-with no N for posting 1.+* account::+* amount::+* currency::+* balance::+* comment:: - For convenience and backwards compatibility, if you set the amount of-posting 1 only, a second posting with the negative amount will be-generated automatically. (Unless the account name is parenthesised-indicating an unbalanced posting.)++File: hledger_csv.info, Node: account, Next: amount, Up: Posting field names - If the CSV has the currency symbol in a separate field, you can use-'currencyN' to prepend it to posting N's amount. 'currency' with no N-affects ALL postings.+2.2.2.1 account+............... - 'balanceN' sets a balance assertion amount (or if the posting amount-is left empty, a balance assignment). You may need to adjust this with-the 'balance-type' rule.+'accountN', where N is 1 to 9, causes a posting to be generated, with+that account name. - Finally, 'commentN' sets a comment on the Nth posting. Comments can+ Most often there are two postings, so you'll want to set 'account1'+and 'account2'. Typically 'account1' is associated with the CSV file,+and is set once with a top-level assignment, while 'account2' is set+based on each transaction's description, and in conditional blocks.++ If a posting's account name is left unset but its amount is set (see+below), a default account name will be chosen (like "expenses:unknown"+or "income:unknown").+++File: hledger_csv.info, Node: amount, Next: currency, Prev: account, Up: Posting field names++2.2.2.2 amount+..............++'amountN' sets posting N's amount. If the CSV uses separate fields for+debit and credit amounts, you can use 'amountN-in' and 'amountN-out'+instead.++ Also, for compatibility with hledger <1.17: 'amount' or+'amount-in'/'amount-out' with no number sets the amount for postings 1+and 2. For posting 2 the amount is negated, and converted to cost if+there's a transaction price.+++File: hledger_csv.info, Node: currency, Next: balance, Prev: amount, Up: Posting field names++2.2.2.3 currency+................++If the CSV has the currency symbol in a separate field (ie, not part of+the amount field), you can use 'currencyN' to prepend it to posting N's+amount. Or, 'currency' with no number affects all postings.+++File: hledger_csv.info, Node: balance, Next: comment, Prev: currency, Up: Posting field names++2.2.2.4 balance+...............++'balanceN' sets a balance assertion amount (or if the posting amount is+left empty, a balance assignment) on posting N.++ Also, for compatibility with hledger <1.17: 'balance' with no number+is equivalent to 'balance1'.++ You can adjust the type of assertion/assignment with the+'balance-type' rule (see below).+++File: hledger_csv.info, Node: comment, Prev: balance, Up: Posting field names++2.2.2.5 comment+...............++Finally, 'commentN' sets a comment on the Nth posting. Comments can also contain tags, as usual. See TIPS below for more about setting amounts and currency.@@ -971,64 +1019,74 @@ Tag Table: Node: Top72-Node: EXAMPLES2093-Ref: #examples2199-Node: Basic2407-Ref: #basic2507-Node: Bank of Ireland3049-Ref: #bank-of-ireland3184-Node: Amazon4646-Ref: #amazon4764-Node: Paypal6483-Ref: #paypal6577-Node: CSV RULES14221-Ref: #csv-rules14330-Node: skip14606-Ref: #skip14699-Node: fields15074-Ref: #fields15196-Node: Transaction field names16361-Ref: #transaction-field-names16521-Node: Posting field names16632-Ref: #posting-field-names16784-Node: field assignment18075-Ref: #field-assignment18218-Node: separator19036-Ref: #separator19165-Node: if19576-Ref: #if19678-Node: end21597-Ref: #end21703-Node: date-format21927-Ref: #date-format22059-Node: newest-first22808-Ref: #newest-first22946-Node: include23629-Ref: #include23758-Node: balance-type24202-Ref: #balance-type24322-Node: TIPS25022-Ref: #tips25104-Node: Rapid feedback25360-Ref: #rapid-feedback25477-Node: Valid CSV25937-Ref: #valid-csv26067-Node: File Extension26259-Ref: #file-extension26411-Node: Reading multiple CSV files26821-Ref: #reading-multiple-csv-files27006-Node: Valid transactions27247-Ref: #valid-transactions27425-Node: Deduplicating importing28053-Ref: #deduplicating-importing28232-Node: Setting amounts29265-Ref: #setting-amounts29434-Node: Setting currency/commodity30420-Ref: #setting-currencycommodity30612-Node: Referencing other fields31415-Ref: #referencing-other-fields31615-Node: How CSV rules are evaluated32512-Ref: #how-csv-rules-are-evaluated32685+Node: EXAMPLES2097+Ref: #examples2203+Node: Basic2411+Ref: #basic2511+Node: Bank of Ireland3053+Ref: #bank-of-ireland3188+Node: Amazon4650+Ref: #amazon4768+Node: Paypal6487+Ref: #paypal6581+Node: CSV RULES14225+Ref: #csv-rules14334+Node: skip14610+Ref: #skip14703+Node: fields15078+Ref: #fields15200+Node: Transaction field names16365+Ref: #transaction-field-names16525+Node: Posting field names16636+Ref: #posting-field-names16788+Node: account16858+Ref: #account16974+Node: amount17510+Ref: #amount17641+Node: currency18022+Ref: #currency18157+Node: balance18363+Ref: #balance18497+Node: comment18814+Ref: #comment18931+Node: field assignment19094+Ref: #field-assignment19237+Node: separator20055+Ref: #separator20184+Node: if20595+Ref: #if20697+Node: end22616+Ref: #end22722+Node: date-format22946+Ref: #date-format23078+Node: newest-first23827+Ref: #newest-first23965+Node: include24648+Ref: #include24777+Node: balance-type25221+Ref: #balance-type25341+Node: TIPS26041+Ref: #tips26123+Node: Rapid feedback26379+Ref: #rapid-feedback26496+Node: Valid CSV26956+Ref: #valid-csv27086+Node: File Extension27278+Ref: #file-extension27430+Node: Reading multiple CSV files27840+Ref: #reading-multiple-csv-files28025+Node: Valid transactions28266+Ref: #valid-transactions28444+Node: Deduplicating importing29072+Ref: #deduplicating-importing29251+Node: Setting amounts30284+Ref: #setting-amounts30453+Node: Setting currency/commodity31439+Ref: #setting-currencycommodity31631+Node: Referencing other fields32434+Ref: #referencing-other-fields32634+Node: How CSV rules are evaluated33531+Ref: #how-csv-rules-are-evaluated33704 End Tag Table
hledger_csv.txt view
@@ -376,30 +376,45 @@ transaction's first line. Posting field names- accountN, where N is 1 to 9, generates a posting, with that account- name. Most often there are two postings, so you'll want to set ac-- count1 and account2. If a posting's account name is left unset but its- amount is set, a default account name will be chosen (like expenses:un-- known or income:unknown).+ account+ accountN, where N is 1 to 9, causes a posting to be generated, with+ that account name. - amountN sets posting N's amount. Or, amount with no N sets posting- 1's. If the CSV has debits and credits in separate fields, use- amountN-in and amountN-out instead. Or amount-in and amount-out with- no N for posting 1.+ Most often there are two postings, so you'll want to set account1 and+ account2. Typically account1 is associated with the CSV file, and is+ set once with a top-level assignment, while account2 is set based on+ each transaction's description, and in conditional blocks. - For convenience and backwards compatibility, if you set the amount of- posting 1 only, a second posting with the negative amount will be gen-- erated automatically. (Unless the account name is parenthesised indi-- cating an unbalanced posting.)+ If a posting's account name is left unset but its amount is set (see+ below), a default account name will be chosen (like "expenses:unknown"+ or "income:unknown"). - If the CSV has the currency symbol in a separate field, you can use- currencyN to prepend it to posting N's amount. currency with no N af-- fects ALL postings.+ amount+ amountN sets posting N's amount. If the CSV uses separate fields for+ debit and credit amounts, you can use amountN-in and amountN-out in-+ stead. - balanceN sets a balance assertion amount (or if the posting amount is- left empty, a balance assignment). You may need to adjust this with- the balance-type rule.+ Also, for compatibility with hledger <1.17: amount or amount-in/amount-+ out with no number sets the amount for postings 1 and 2. For posting 2+ the amount is negated, and converted to cost if there's a transaction+ price. + currency+ If the CSV has the currency symbol in a separate field (ie, not part of+ the amount field), you can use currencyN to prepend it to posting N's+ amount. Or, currency with no number affects all postings.++ balance+ balanceN sets a balance assertion amount (or if the posting amount is+ left empty, a balance assignment) on posting N.++ Also, for compatibility with hledger <1.17: balance with no number is+ equivalent to balance1.++ You can adjust the type of assertion/assignment with the balance-type+ rule (see below).++ comment Finally, commentN sets a comment on the Nth posting. Comments can also contain tags, as usual. @@ -819,4 +834,4 @@ -hledger 1.17 March 2020 hledger_csv(5)+hledger 1.17.1 March 2020 hledger_csv(5)
hledger_journal.5 view
@@ -1,6 +1,6 @@ .\"t -.TH "hledger_journal" "5" "March 2020" "hledger 1.17" "hledger User Manuals"+.TH "hledger_journal" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals" @@ -524,7 +524,7 @@ \f[R] .fi .PP-A decimal mark (decimal point) can be written with a period or a comma:+A decimal mark can be written as a period or a comma: .IP .nf \f[C]@@ -1027,6 +1027,16 @@ T}@T{ following inline/included entries until end of current file T}+T{+\f[C]=\f[R]+T}@T{+T}@T{+T}@T{+declare an auto posting rule, adding postings to other transactions+T}@T{+all entries in parent/current/child files (but not sibling files, see+#1212)+T} .TE .PP And some definitions:@@ -1066,13 +1076,21 @@ affect, and whether they are focussed on input (parsing) or output (reports). Some directives have multiple effects.+.SS Directives and multiple files .PP-If you have a journal made up of multiple files, or pass multiple -f-options on the command line, note that directives which affect input-typically last only until the end of their defining file.-This provides more simplicity and predictability, eg reports are not-changed by writing file options in a different order.-It can be surprising at times though.+If you use multiple \f[C]-f\f[R]/\f[C]--file\f[R] options, or the+\f[C]include\f[R] directive, hledger will process multiple input files.+But note that directives which affect input (see above) typically last+only until the end of the file in which they occur.+.PP+This may seem inconvenient, but it\[aq]s intentional; it makes reports+stable and deterministic, independent of the order of input.+Otherwise you could see different numbers if you happened to write -f+options in a different order, or if you moved includes around while+cleaning up your files.+.PP+It can be surprising though; for example, it means that \f[C]alias\f[R]+directives do not affect parent or sibling files (see below). .SS Comment blocks .PP A line containing just \f[C]comment\f[R] starts a commented region of@@ -1543,6 +1561,46 @@ .PP In case of trouble, adding \f[C]--debug=6\f[R] to the command line will show which aliases are being applied when.+.SS Aliases and multiple files+.PP+As explained at Directives and multiple files, \f[C]alias\f[R]+directives do not affect parent or sibling files.+Eg in this command,+.IP+.nf+\f[C]+hledger -f a.aliases -f b.journal+\f[R]+.fi+.PP+account aliases defined in a.aliases will not affect b.journal.+Including the aliases doesn\[aq]t work either:+.IP+.nf+\f[C]+include a.aliases++2020-01-01 ; not affected by a.aliases+ foo 1+ bar+\f[R]+.fi+.PP+This means that account aliases should usually be declared at the start+of your top-most file, like this:+.IP+.nf+\f[C]+alias foo=Foo+alias bar=Bar++2020-01-01 ; affected by aliases above+ foo 1+ bar++include c.journal ; also affected+\f[R]+.fi .SS \f[C]end aliases\f[R] .PP You can clear (forget) all currently defined aliases with the@@ -1759,31 +1817,28 @@ For more details, see: balance: Budget report and Budgeting and Forecasting. .PP-.SS Auto postings / transaction modifiers+.SS Auto postings .PP-Transaction modifier rules, AKA auto posting rules, describe changes to-be applied automatically to certain matched transactions.-Currently just one kind of change is possible - adding extra postings,-which we call \[dq]automated postings\[dq] or just \[dq]auto-postings\[dq].-These rules become active when you use the \f[C]--auto\f[R] flag.+\[dq]Automated postings\[dq] or \[dq]auto postings\[dq] are extra+postings which get added automatically to transactions which match+certain queries, defined by \[dq]auto posting rules\[dq], when you use+the \f[C]--auto\f[R] flag. .PP-A transaction modifier rule looks much like a normal transaction except-the first line is an equals sign followed by a query that matches-certain postings (mnemonic: \f[C]=\f[R] suggests matching).-And each \[dq]posting\[dq] is actually a posting-generating rule:+An auto posting rule looks a bit like a transaction: .IP .nf \f[C] = QUERY ACCOUNT AMOUNT- ACCOUNT [AMOUNT] ...+ ACCOUNT [AMOUNT] \f[R] .fi .PP-These posting-generating rules look like normal postings, except the-amount can be:+except the first line is an equals sign (mnemonic: \f[C]=\f[R] suggests+matching), followed by a query (which matches existing postings), and+each \[dq]posting\[dq] line describes a posting to be generated, and the+posting amounts can be: .IP \[bu] 2 a normal amount with a commodity symbol, eg \f[C]$2\f[R]. This will be used as-is.@@ -1801,7 +1856,7 @@ The matched posting\[aq]s amount will be multiplied by N, and its commodity symbol will be replaced with S. .PP-A query term containing spaces must be enclosed in single or double+Any query term containing spaces must be enclosed in single or double quotes, as on the command line. Eg, note the quotes around the second query term below: .IP@@ -1812,10 +1867,6 @@ \f[R] .fi .PP-These rules have global effect - a rule appearing anywhere in your data-can potentially affect any transaction, including transactions recorded-above it or in another file.-.PP Some examples: .IP .nf@@ -1854,6 +1905,12 @@ assets:checking $20 \f[R] .fi+.SS Auto postings and multiple files+.PP+An auto posting rule can affect any transaction in the current file, or+in any parent file or child file.+Note, currently it will not affect sibling files (when multiple+\f[C]-f\f[R]/\f[C]--file\f[R] are used - see #1212). .SS Auto postings and dates .PP A posting date (or secondary date) in the matched posting, or (taking@@ -1861,7 +1918,7 @@ used in the generated posting. .SS Auto postings and transaction balancing / inferred amounts / balance assertions .PP-Currently, transaction modifiers are applied / auto postings are added:+Currently, auto postings are added: .IP \[bu] 2 after missing amounts are inferred, and transactions are checked for balancedness,@@ -1873,7 +1930,7 @@ This changed in hledger 1.12+; see #893 for background. .SS Auto posting tags .PP-Postings added by transaction modifiers will have some extra tags:+Automated postings will have some extra tags: .IP \[bu] 2 \f[C]generated-posting:= QUERY\f[R] - shows this was generated by an auto posting rule, and the query@@ -1883,8 +1940,8 @@ This can be used to match postings generated \[dq]just now\[dq], rather than generated in the past and saved to the journal. .PP-Also, any transaction that has been changed by transaction modifier-rules will have these tags added:+Also, any transaction that has been changed by auto posting rules will+have these tags added: .IP \[bu] 2 \f[C]modified:\f[R] - this transaction was modified .IP \[bu] 2
hledger_journal.info view
@@ -4,8 +4,8 @@ File: hledger_journal.info, Node: Top, Up: (dir) -hledger_journal(5) hledger 1.17-*******************************+hledger_journal(5) hledger 1.17.1+********************************* Journal - hledger's default file format, representing a General Journal @@ -85,7 +85,7 @@ * Balance Assignments:: * Directives:: * Periodic transactions::-* Auto postings / transaction modifiers::+* Auto postings:: File: hledger_journal.info, Node: Dates, Next: Status, Up: Transactions@@ -470,8 +470,7 @@ 1E-6 EUR 1E3 - A decimal mark (decimal point) can be written with a period or a-comma:+ A decimal mark can be written as a period or a comma: 1.23 1,23456780000009@@ -912,6 +911,11 @@ dates inline/included entries until end of current file+'=' declare an auto posting all entries in+ rule, adding postings to parent/current/child+ other transactions files (but not+ sibling files,+ see #1212) And some definitions: @@ -929,15 +933,9 @@ they affect, and whether they are focussed on input (parsing) or output (reports). Some directives have multiple effects. - If you have a journal made up of multiple files, or pass multiple -f-options on the command line, note that directives which affect input-typically last only until the end of their defining file. This provides-more simplicity and predictability, eg reports are not changed by-writing file options in a different order. It can be surprising at-times though.- * Menu: +* Directives and multiple files:: * Comment blocks:: * Including other files:: * Default year::@@ -949,9 +947,29 @@ * Default parent account:: -File: hledger_journal.info, Node: Comment blocks, Next: Including other files, Up: Directives+File: hledger_journal.info, Node: Directives and multiple files, Next: Comment blocks, Up: Directives -1.12.1 Comment blocks+1.12.1 Directives and multiple files+------------------------------------++If you use multiple '-f'/'--file' options, or the 'include' directive,+hledger will process multiple input files. But note that directives+which affect input (see above) typically last only until the end of the+file in which they occur.++ This may seem inconvenient, but it's intentional; it makes reports+stable and deterministic, independent of the order of input. Otherwise+you could see different numbers if you happened to write -f options in a+different order, or if you moved includes around while cleaning up your+files.++ It can be surprising though; for example, it means that 'alias'+directives do not affect parent or sibling files (see below).+++File: hledger_journal.info, Node: Comment blocks, Next: Including other files, Prev: Directives and multiple files, Up: Directives++1.12.2 Comment blocks --------------------- A line containing just 'comment' starts a commented region of the file,@@ -961,7 +979,7 @@ File: hledger_journal.info, Node: Including other files, Next: Default year, Prev: Comment blocks, Up: Directives -1.12.2 Including other files+1.12.3 Including other files ---------------------------- You can pull in the content of additional files by writing an include@@ -979,7 +997,7 @@ File: hledger_journal.info, Node: Default year, Next: Declaring commodities, Prev: Including other files, Up: Directives -1.12.3 Default year+1.12.4 Default year ------------------- You can set a default year to be used for subsequent dates which don't@@ -1005,7 +1023,7 @@ File: hledger_journal.info, Node: Declaring commodities, Next: Default commodity, Prev: Default year, Up: Directives -1.12.4 Declaring commodities+1.12.5 Declaring commodities ---------------------------- The 'commodity' directive has several functions:@@ -1055,7 +1073,7 @@ File: hledger_journal.info, Node: Default commodity, Next: Market prices, Prev: Declaring commodities, Up: Directives -1.12.5 Default commodity+1.12.6 Default commodity ------------------------ The 'D' directive sets a default commodity, to be used for amounts@@ -1082,7 +1100,7 @@ File: hledger_journal.info, Node: Market prices, Next: Declaring accounts, Prev: Default commodity, Up: Directives -1.12.6 Market prices+1.12.7 Market prices -------------------- The 'P' directive declares a market price, which is an exchange rate@@ -1112,7 +1130,7 @@ File: hledger_journal.info, Node: Declaring accounts, Next: Rewriting accounts, Prev: Market prices, Up: Directives -1.12.7 Declaring accounts+1.12.8 Declaring accounts ------------------------- 'account' directives can be used to pre-declare accounts. Though not@@ -1145,7 +1163,7 @@ File: hledger_journal.info, Node: Account comments, Next: Account subdirectives, Up: Declaring accounts -1.12.7.1 Account comments+1.12.8.1 Account comments ......................... Comments, beginning with a semicolon, can be added:@@ -1165,7 +1183,7 @@ File: hledger_journal.info, Node: Account subdirectives, Next: Account types, Prev: Account comments, Up: Declaring accounts -1.12.7.2 Account subdirectives+1.12.8.2 Account subdirectives .............................. We also allow (and ignore) Ledger-style indented subdirectives, just for@@ -1183,7 +1201,7 @@ File: hledger_journal.info, Node: Account types, Next: Account display order, Prev: Account subdirectives, Up: Declaring accounts -1.12.7.3 Account types+1.12.8.3 Account types ...................... hledger recognises five types (or classes) of account: Asset, Liability,@@ -1228,7 +1246,7 @@ File: hledger_journal.info, Node: Account display order, Prev: Account types, Up: Declaring accounts -1.12.7.4 Account display order+1.12.8.4 Account display order .............................. Account directives also set the order in which accounts are displayed,@@ -1274,7 +1292,7 @@ File: hledger_journal.info, Node: Rewriting accounts, Next: Default parent account, Prev: Declaring accounts, Up: Directives -1.12.8 Rewriting accounts+1.12.9 Rewriting accounts ------------------------- You can define account alias rules which rewrite your account names, or@@ -1298,12 +1316,13 @@ * Basic aliases:: * Regex aliases:: * Combining aliases::+* Aliases and multiple files:: * end aliases:: File: hledger_journal.info, Node: Basic aliases, Next: Regex aliases, Up: Rewriting accounts -1.12.8.1 Basic aliases+1.12.9.1 Basic aliases ...................... To set an account alias, use the 'alias' directive in your journal file.@@ -1326,7 +1345,7 @@ File: hledger_journal.info, Node: Regex aliases, Next: Combining aliases, Prev: Basic aliases, Up: Rewriting accounts -1.12.8.2 Regex aliases+1.12.9.2 Regex aliases ...................... There is also a more powerful variant that uses a regular expression,@@ -1349,9 +1368,9 @@ whitespace. -File: hledger_journal.info, Node: Combining aliases, Next: end aliases, Prev: Regex aliases, Up: Rewriting accounts+File: hledger_journal.info, Node: Combining aliases, Next: Aliases and multiple files, Prev: Regex aliases, Up: Rewriting accounts -1.12.8.3 Combining aliases+1.12.9.3 Combining aliases .......................... You can define as many aliases as you like, using journal directives@@ -1386,9 +1405,41 @@ which aliases are being applied when. -File: hledger_journal.info, Node: end aliases, Prev: Combining aliases, Up: Rewriting accounts+File: hledger_journal.info, Node: Aliases and multiple files, Next: end aliases, Prev: Combining aliases, Up: Rewriting accounts -1.12.8.4 'end aliases'+1.12.9.4 Aliases and multiple files+...................................++As explained at Directives and multiple files, 'alias' directives do not+affect parent or sibling files. Eg in this command,++hledger -f a.aliases -f b.journal++ account aliases defined in a.aliases will not affect b.journal.+Including the aliases doesn't work either:++include a.aliases++2020-01-01 ; not affected by a.aliases+ foo 1+ bar++ This means that account aliases should usually be declared at the+start of your top-most file, like this:++alias foo=Foo+alias bar=Bar++2020-01-01 ; affected by aliases above+ foo 1+ bar++include c.journal ; also affected+++File: hledger_journal.info, Node: end aliases, Prev: Aliases and multiple files, Up: Rewriting accounts++1.12.9.5 'end aliases' ...................... You can clear (forget) all currently defined aliases with the 'end@@ -1399,8 +1450,8 @@ File: hledger_journal.info, Node: Default parent account, Prev: Rewriting accounts, Up: Directives -1.12.9 Default parent account------------------------------+1.12.10 Default parent account+------------------------------ You can specify a parent account which will be prepended to all accounts within a section of the journal. Use the 'apply account' and 'end apply@@ -1438,7 +1489,7 @@ parent account. -File: hledger_journal.info, Node: Periodic transactions, Next: Auto postings / transaction modifiers, Prev: Directives, Up: Transactions+File: hledger_journal.info, Node: Periodic transactions, Next: Auto postings, Prev: Directives, Up: Transactions 1.13 Periodic transactions ==========================@@ -1600,29 +1651,26 @@ Forecasting. -File: hledger_journal.info, Node: Auto postings / transaction modifiers, Prev: Periodic transactions, Up: Transactions+File: hledger_journal.info, Node: Auto postings, Prev: Periodic transactions, Up: Transactions -1.14 Auto postings / transaction modifiers-==========================================+1.14 Auto postings+================== -Transaction modifier rules, AKA auto posting rules, describe changes to-be applied automatically to certain matched transactions. Currently-just one kind of change is possible - adding extra postings, which we-call "automated postings" or just "auto postings". These rules become-active when you use the '--auto' flag.+"Automated postings" or "auto postings" are extra postings which get+added automatically to transactions which match certain queries, defined+by "auto posting rules", when you use the '--auto' flag. - A transaction modifier rule looks much like a normal transaction-except the first line is an equals sign followed by a query that matches-certain postings (mnemonic: '=' suggests matching). And each "posting"-is actually a posting-generating rule:+ An auto posting rule looks a bit like a transaction: = QUERY ACCOUNT AMOUNT- ACCOUNT [AMOUNT] ...+ ACCOUNT [AMOUNT] - These posting-generating rules look like normal postings, except the-amount can be:+ except the first line is an equals sign (mnemonic: '=' suggests+matching), followed by a query (which matches existing postings), and+each "posting" line describes a posting to be generated, and the posting+amounts can be: * a normal amount with a commodity symbol, eg '$2'. This will be used as-is.@@ -1635,17 +1683,13 @@ and symbol S). The matched posting's amount will be multiplied by N, and its commodity symbol will be replaced with S. - A query term containing spaces must be enclosed in single or double+ Any query term containing spaces must be enclosed in single or double quotes, as on the command line. Eg, note the quotes around the second query term below: = expenses:groceries 'expenses:dining out' (budget:funds:dining out) *-1 - These rules have global effect - a rule appearing anywhere in your-data can potentially affect any transaction, including transactions-recorded above it or in another file.- Some examples: ; every time I buy food, schedule a dollar donation@@ -1679,14 +1723,25 @@ * Menu: +* Auto postings and multiple files:: * Auto postings and dates:: * Auto postings and transaction balancing / inferred amounts / balance assertions:: * Auto posting tags:: -File: hledger_journal.info, Node: Auto postings and dates, Next: Auto postings and transaction balancing / inferred amounts / balance assertions, Up: Auto postings / transaction modifiers+File: hledger_journal.info, Node: Auto postings and multiple files, Next: Auto postings and dates, Up: Auto postings -1.14.1 Auto postings and dates+1.14.1 Auto postings and multiple files+---------------------------------------++An auto posting rule can affect any transaction in the current file, or+in any parent file or child file. Note, currently it will not affect+sibling files (when multiple '-f'/'--file' are used - see #1212).+++File: hledger_journal.info, Node: Auto postings and dates, Next: Auto postings and transaction balancing / inferred amounts / balance assertions, Prev: Auto postings and multiple files, Up: Auto postings++1.14.2 Auto postings and dates ------------------------------ A posting date (or secondary date) in the matched posting, or (taking@@ -1694,13 +1749,12 @@ used in the generated posting. -File: hledger_journal.info, Node: Auto postings and transaction balancing / inferred amounts / balance assertions, Next: Auto posting tags, Prev: Auto postings and dates, Up: Auto postings / transaction modifiers+File: hledger_journal.info, Node: Auto postings and transaction balancing / inferred amounts / balance assertions, Next: Auto posting tags, Prev: Auto postings and dates, Up: Auto postings -1.14.2 Auto postings and transaction balancing / inferred amounts /+1.14.3 Auto postings and transaction balancing / inferred amounts / ------------------------------------------------------------------- -balance assertions Currently, transaction modifiers are applied / auto-postings are added:+balance assertions Currently, auto postings are added: * after missing amounts are inferred, and transactions are checked for balancedness,@@ -1711,12 +1765,12 @@ for background. -File: hledger_journal.info, Node: Auto posting tags, Prev: Auto postings and transaction balancing / inferred amounts / balance assertions, Up: Auto postings / transaction modifiers+File: hledger_journal.info, Node: Auto posting tags, Prev: Auto postings and transaction balancing / inferred amounts / balance assertions, Up: Auto postings -1.14.3 Auto posting tags+1.14.4 Auto posting tags ------------------------ -Postings added by transaction modifiers will have some extra tags:+Automated postings will have some extra tags: * 'generated-posting:= QUERY' - shows this was generated by an auto posting rule, and the query@@ -1725,8 +1779,8 @@ "just now", rather than generated in the past and saved to the journal. - Also, any transaction that has been changed by transaction modifier-rules will have these tags added:+ Also, any transaction that has been changed by auto posting rules+will have these tags added: * 'modified:' - this transaction was modified * '_modified:' - a hidden tag not appearing in the comment; this@@ -1735,116 +1789,122 @@ Tag Table: Node: Top76-Node: Transactions1869-Ref: #transactions1961-Node: Dates3150-Ref: #dates3249-Node: Simple dates3314-Ref: #simple-dates3440-Node: Secondary dates3949-Ref: #secondary-dates4103-Node: Posting dates5439-Ref: #posting-dates5568-Node: Status6940-Ref: #status7061-Node: Description8769-Ref: #description8903-Node: Payee and note9223-Ref: #payee-and-note9337-Node: Comments9672-Ref: #comments9798-Node: Tags10992-Ref: #tags11107-Node: Postings12500-Ref: #postings12628-Node: Virtual Postings13654-Ref: #virtual-postings13771-Node: Account names15076-Ref: #account-names15217-Node: Amounts15704-Ref: #amounts15843-Node: Digit group marks16775-Ref: #digit-group-marks16923-Node: Amount display style17861-Ref: #amount-display-style18015-Node: Transaction prices19176-Ref: #transaction-prices19342-Node: Balance Assertions21608-Ref: #balance-assertions21788-Node: Assertions and ordering22821-Ref: #assertions-and-ordering23009-Node: Assertions and included files23709-Ref: #assertions-and-included-files23952-Node: Assertions and multiple -f options24285-Ref: #assertions-and-multiple--f-options24541-Node: Assertions and commodities24673-Ref: #assertions-and-commodities24905-Node: Assertions and prices26062-Ref: #assertions-and-prices26276-Node: Assertions and subaccounts26716-Ref: #assertions-and-subaccounts26945-Node: Assertions and virtual postings27269-Ref: #assertions-and-virtual-postings27511-Node: Assertions and precision27653-Ref: #assertions-and-precision27846-Node: Balance Assignments28113-Ref: #balance-assignments28287-Node: Balance assignments and prices29451-Ref: #balance-assignments-and-prices29623-Node: Directives29847-Ref: #directives30006-Node: Comment blocks35654-Ref: #comment-blocks35799-Node: Including other files35975-Ref: #including-other-files36155-Node: Default year36563-Ref: #default-year36732-Node: Declaring commodities37139-Ref: #declaring-commodities37322-Node: Default commodity38995-Ref: #default-commodity39171-Node: Market prices40060-Ref: #market-prices40225-Node: Declaring accounts41066-Ref: #declaring-accounts41242-Node: Account comments42167-Ref: #account-comments42330-Node: Account subdirectives42754-Ref: #account-subdirectives42949-Node: Account types43262-Ref: #account-types43446-Node: Account display order45085-Ref: #account-display-order45255-Node: Rewriting accounts46406-Ref: #rewriting-accounts46591-Node: Basic aliases47317-Ref: #basic-aliases47463-Node: Regex aliases48167-Ref: #regex-aliases48339-Node: Combining aliases49057-Ref: #combining-aliases49235-Node: end aliases50511-Ref: #end-aliases50659-Node: Default parent account50760-Ref: #default-parent-account50926-Node: Periodic transactions51810-Ref: #periodic-transactions52009-Node: Periodic rule syntax53881-Ref: #periodic-rule-syntax54087-Node: Two spaces between period expression and description!54791-Ref: #two-spaces-between-period-expression-and-description55110-Node: Forecasting with periodic transactions55794-Ref: #forecasting-with-periodic-transactions56099-Node: Budgeting with periodic transactions58125-Ref: #budgeting-with-periodic-transactions58364-Node: Auto postings / transaction modifiers58813-Ref: #auto-postings-transaction-modifiers59025-Node: Auto postings and dates61521-Ref: #auto-postings-and-dates61778-Node: Auto postings and transaction balancing / inferred amounts / balance assertions61953-Ref: #auto-postings-and-transaction-balancing-inferred-amounts-balance-assertions62328-Node: Auto posting tags62706-Ref: #auto-posting-tags62945+Node: Transactions1873+Ref: #transactions1965+Node: Dates3130+Ref: #dates3229+Node: Simple dates3294+Ref: #simple-dates3420+Node: Secondary dates3929+Ref: #secondary-dates4083+Node: Posting dates5419+Ref: #posting-dates5548+Node: Status6920+Ref: #status7041+Node: Description8749+Ref: #description8883+Node: Payee and note9203+Ref: #payee-and-note9317+Node: Comments9652+Ref: #comments9778+Node: Tags10972+Ref: #tags11087+Node: Postings12480+Ref: #postings12608+Node: Virtual Postings13634+Ref: #virtual-postings13751+Node: Account names15056+Ref: #account-names15197+Node: Amounts15684+Ref: #amounts15823+Node: Digit group marks16737+Ref: #digit-group-marks16885+Node: Amount display style17823+Ref: #amount-display-style17977+Node: Transaction prices19138+Ref: #transaction-prices19304+Node: Balance Assertions21570+Ref: #balance-assertions21750+Node: Assertions and ordering22783+Ref: #assertions-and-ordering22971+Node: Assertions and included files23671+Ref: #assertions-and-included-files23914+Node: Assertions and multiple -f options24247+Ref: #assertions-and-multiple--f-options24503+Node: Assertions and commodities24635+Ref: #assertions-and-commodities24867+Node: Assertions and prices26024+Ref: #assertions-and-prices26238+Node: Assertions and subaccounts26678+Ref: #assertions-and-subaccounts26907+Node: Assertions and virtual postings27231+Ref: #assertions-and-virtual-postings27473+Node: Assertions and precision27615+Ref: #assertions-and-precision27808+Node: Balance Assignments28075+Ref: #balance-assignments28249+Node: Balance assignments and prices29413+Ref: #balance-assignments-and-prices29585+Node: Directives29809+Ref: #directives29968+Node: Directives and multiple files35649+Ref: #directives-and-multiple-files35832+Node: Comment blocks36496+Ref: #comment-blocks36679+Node: Including other files36855+Ref: #including-other-files37035+Node: Default year37443+Ref: #default-year37612+Node: Declaring commodities38019+Ref: #declaring-commodities38202+Node: Default commodity39875+Ref: #default-commodity40051+Node: Market prices40940+Ref: #market-prices41105+Node: Declaring accounts41946+Ref: #declaring-accounts42122+Node: Account comments43047+Ref: #account-comments43210+Node: Account subdirectives43634+Ref: #account-subdirectives43829+Node: Account types44142+Ref: #account-types44326+Node: Account display order45965+Ref: #account-display-order46135+Node: Rewriting accounts47286+Ref: #rewriting-accounts47471+Node: Basic aliases48228+Ref: #basic-aliases48374+Node: Regex aliases49078+Ref: #regex-aliases49250+Node: Combining aliases49968+Ref: #combining-aliases50161+Node: Aliases and multiple files51437+Ref: #aliases-and-multiple-files51646+Node: end aliases52225+Ref: #end-aliases52382+Node: Default parent account52483+Ref: #default-parent-account52651+Node: Periodic transactions53535+Ref: #periodic-transactions53710+Node: Periodic rule syntax55582+Ref: #periodic-rule-syntax55788+Node: Two spaces between period expression and description!56492+Ref: #two-spaces-between-period-expression-and-description56811+Node: Forecasting with periodic transactions57495+Ref: #forecasting-with-periodic-transactions57800+Node: Budgeting with periodic transactions59826+Ref: #budgeting-with-periodic-transactions60065+Node: Auto postings60514+Ref: #auto-postings60654+Node: Auto postings and multiple files62833+Ref: #auto-postings-and-multiple-files63037+Node: Auto postings and dates63246+Ref: #auto-postings-and-dates63520+Node: Auto postings and transaction balancing / inferred amounts / balance assertions63695+Ref: #auto-postings-and-transaction-balancing-inferred-amounts-balance-assertions64046+Node: Auto posting tags64388+Ref: #auto-posting-tags64603 End Tag Table
hledger_journal.txt view
@@ -370,7 +370,7 @@ 1E-6 EUR 1E3 - A decimal mark (decimal point) can be written with a period or a comma:+ A decimal mark can be written as a period or a comma: 1.23 1,23456780000009@@ -709,6 +709,11 @@ dates line/included en- tries until end of current file+ = declare an auto posting all entries in par-+ rule, adding postings to ent/current/child+ other transactions files (but not sib-+ ling files, see+ #1212) And some definitions: @@ -718,12 +723,10 @@ number how to interpret numbers when parsing journal entries (the iden- nota- tity of the decimal separator character). (Currently each com- tion modity can have its own notation, even in the same file.)+ dis- how to display amounts of a commodity in reports (symbol side play and spacing, digit groups, decimal separator, decimal places) style--- direc- which entries and (when there are multiple files) which files tive are affected by a directive scope@@ -732,34 +735,42 @@ affect, and whether they are focussed on input (parsing) or output (re- ports). Some directives have multiple effects. - If you have a journal made up of multiple files, or pass multiple -f- options on the command line, note that directives which affect input- typically last only until the end of their defining file. This pro-- vides more simplicity and predictability, eg reports are not changed by- writing file options in a different order. It can be surprising at- times though.+ Directives and multiple files+ If you use multiple -f/--file options, or the include directive,+ hledger will process multiple input files. But note that directives+ which affect input (see above) typically last only until the end of the+ file in which they occur. + This may seem inconvenient, but it's intentional; it makes reports sta-+ ble and deterministic, independent of the order of input. Otherwise+ you could see different numbers if you happened to write -f options in+ a different order, or if you moved includes around while cleaning up+ your files.++ It can be surprising though; for example, it means that alias direc-+ tives do not affect parent or sibling files (see below).+ Comment blocks- A line containing just comment starts a commented region of the file,+ A line containing just comment starts a commented region of the file, and a line containing just end comment (or the end of the current file) ends it. See also comments. Including other files- You can pull in the content of additional files by writing an include+ You can pull in the content of additional files by writing an include directive, like this: include path/to/file.journal - If the path does not begin with a slash, it is relative to the current- file. The include file path may contain common glob patterns (e.g.+ If the path does not begin with a slash, it is relative to the current+ file. The include file path may contain common glob patterns (e.g. *). - The include directive can only be used in journal files. It can in-+ The include directive can only be used in journal files. It can in- clude journal, timeclock or timedot files, but not CSV files. Default year- You can set a default year to be used for subsequent dates which don't- specify a year. This is a line beginning with Y followed by the year.+ You can set a default year to be used for subsequent dates which don't+ specify a year. This is a line beginning with Y followed by the year. Eg: Y2009 ; set default year to 2009@@ -781,19 +792,19 @@ Declaring commodities The commodity directive has several functions: - 1. It declares commodities which may be used in the journal. This is+ 1. It declares commodities which may be used in the journal. This is currently not enforced, but can serve as documentation. - 2. It declares what decimal mark character (period or comma) to expect- when parsing input - useful to disambiguate international number- formats in your data. (Without this, hledger will parse both 1,000+ 2. It declares what decimal mark character (period or comma) to expect+ when parsing input - useful to disambiguate international number+ formats in your data. (Without this, hledger will parse both 1,000 and 1.000 as 1). - 3. It declares the amount display style to use in output - decimal and+ 3. It declares the amount display style to use in output - decimal and digit group marks, number of decimal places, symbol placement etc. - You are likely to run into one of the problems solved by commodity di-- rectives, sooner or later, so it's a good idea to just always use them+ You are likely to run into one of the problems solved by commodity di-+ rectives, sooner or later, so it's a good idea to just always use them to declare your commodities. A commodity directive is just the word commodity followed by an amount.@@ -806,8 +817,8 @@ ; separating thousands with comma. commodity 1,000.0000 AAAA - or on multiple lines, using the "format" subdirective. (In this case- the commodity symbol appears twice and should be the same in both+ or on multiple lines, using the "format" subdirective. (In this case+ the commodity symbol appears twice and should be the same in both places.): ; commodity SYMBOL@@ -820,19 +831,19 @@ format INR 1,00,00,000.00 The quantity of the amount does not matter; only the format is signifi-- cant. The number must include a decimal mark: either a period or a+ cant. The number must include a decimal mark: either a period or a comma, followed by 0 or more decimal digits. Default commodity- The D directive sets a default commodity, to be used for amounts with-+ The D directive sets a default commodity, to be used for amounts with- out a commodity symbol (ie, plain numbers). This commodity will be ap- plied to all subsequent commodity-less amounts, or until the next D di- rective. (Note, this is different from Ledger's D.) - For compatibility/historical reasons, D also acts like a commodity di-+ For compatibility/historical reasons, D also acts like a commodity di- rective, setting the commodity's display style (for output) and decimal mark (for parsing input). As with commodity, the amount must always be- written with a decimal mark (period or comma). If both directives are+ written with a decimal mark (period or comma). If both directives are used, commodity's style takes precedence. The syntax is D AMOUNT. Eg:@@ -846,9 +857,9 @@ b Market prices- The P directive declares a market price, which is an exchange rate be-- tween two commodities on a certain date. (In Ledger, they are called- "historical prices".) These are often obtained from a stock exchange,+ The P directive declares a market price, which is an exchange rate be-+ tween two commodities on a certain date. (In Ledger, they are called+ "historical prices".) These are often obtained from a stock exchange, cryptocurrency exchange, or the foreign exchange market. Here is the format:@@ -859,16 +870,16 @@ o COMMODITYA is the symbol of the commodity being priced - o COMMODITYBAMOUNT is an amount (symbol and quantity) in a second com-+ o COMMODITYBAMOUNT is an amount (symbol and quantity) in a second com- modity, giving the price in commodity B of one unit of commodity A. - These two market price directives say that one euro was worth 1.35 US+ These two market price directives say that one euro was worth 1.35 US dollars during 2009, and $1.40 from 2010 onward: P 2009/1/1 EUR $1.35 P 2010/1/1 EUR $1.40 - The -V/--value flag can be used to convert reported amounts to another+ The -V/--value flag can be used to convert reported amounts to another commodity using these prices. Declaring accounts@@ -878,20 +889,20 @@ o They can document your intended chart of accounts, providing a refer- ence. - o They can store extra information about accounts (account numbers,+ o They can store extra information about accounts (account numbers, notes, etc.) - o They can help hledger know your accounts' types (asset, liability,- equity, revenue, expense), useful for reports like balancesheet and+ o They can help hledger know your accounts' types (asset, liability,+ equity, revenue, expense), useful for reports like balancesheet and incomestatement. - o They control account display order in reports, allowing non-alpha-+ o They control account display order in reports, allowing non-alpha- betic sorting (eg Revenues to appear above Expenses). - o They help with account name completion in the add command, hledger-+ o They help with account name completion in the add command, hledger- iadd, hledger-web, ledger-mode etc. - The simplest form is just the word account followed by a hledger-style+ The simplest form is just the word account followed by a hledger-style account name, eg: account assets:bank:checking@@ -899,7 +910,7 @@ Account comments Comments, beginning with a semicolon, can be added: - o on the same line, after two or more spaces (because ; is allowed in+ o on the same line, after two or more spaces (because ; is allowed in account names) o on the next lines, indented@@ -913,7 +924,7 @@ Same-line comments are not supported by Ledger, or hledger <1.13. Account subdirectives- We also allow (and ignore) Ledger-style indented subdirectives, just+ We also allow (and ignore) Ledger-style indented subdirectives, just for compatibility.: account assets:bank:checking@@ -926,18 +937,18 @@ [LEDGER-STYLE SUBDIRECTIVES, IGNORED] Account types- hledger recognises five types (or classes) of account: Asset, Liabil-- ity, Equity, Revenue, Expense. This is used by a few accounting-aware+ hledger recognises five types (or classes) of account: Asset, Liabil-+ ity, Equity, Revenue, Expense. This is used by a few accounting-aware reports such as balancesheet, incomestatement and cashflow. Auto-detected account types If you name your top-level accounts with some variation of assets, lia-- bilities/debts, equity, revenues/income, or expenses, their types are+ bilities/debts, equity, revenues/income, or expenses, their types are detected automatically. Account types declared with tags- More generally, you can declare an account's type with an account di-- rective, by writing a type: tag in a comment, followed by one of the+ More generally, you can declare an account's type with an account di-+ rective, by writing a type: tag in a comment, followed by one of the words Asset, Liability, Equity, Revenue, Expense, or one of the letters ALERX (case insensitive): @@ -948,8 +959,8 @@ account expenses ; type:Expense Account types declared with account type codes- Or, you can write one of those letters separated from the account name- by two or more spaces, but this should probably be considered depre-+ Or, you can write one of those letters separated from the account name+ by two or more spaces, but this should probably be considered depre- cated as of hledger 1.13: account assets A@@ -959,7 +970,7 @@ account expenses X Overriding auto-detected types- If you ever override the types of those auto-detected english account+ If you ever override the types of those auto-detected english account names mentioned above, you might need to help the reports a bit. Eg: ; make "liabilities" not have the liability type - who knows why@@ -970,8 +981,8 @@ account - ; type:L Account display order- Account directives also set the order in which accounts are displayed,- eg in reports, the hledger-ui accounts screen, and the hledger-web+ Account directives also set the order in which accounts are displayed,+ eg in reports, the hledger-ui accounts screen, and the hledger-web sidebar. By default accounts are listed in alphabetical order. But if you have these account directives in the journal: @@ -993,20 +1004,20 @@ Undeclared accounts, if any, are displayed last, in alphabetical order. - Note that sorting is done at each level of the account tree (within- each group of sibling accounts under the same parent). And currently,+ Note that sorting is done at each level of the account tree (within+ each group of sibling accounts under the same parent). And currently, this directive: account other:zoo - would influence the position of zoo among other's subaccounts, but not+ would influence the position of zoo among other's subaccounts, but not the position of other among the top-level accounts. This means: - o you will sometimes declare parent accounts (eg account other above)+ o you will sometimes declare parent accounts (eg account other above) that you don't intend to post to, just to customize their display or- der - o sibling accounts stay together (you couldn't display x:y in between+ o sibling accounts stay together (you couldn't display x:y in between a:b and a:c). Rewriting accounts@@ -1024,14 +1035,14 @@ o customising reports Account aliases also rewrite account names in account directives. They- do not affect account names being entered via hledger add or hledger-+ do not affect account names being entered via hledger add or hledger- web. See also 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@@ -1039,49 +1050,49 @@ 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 case sensitive full account names. hledger will re-- place any occurrence of the old account name with the new one. Subac-+ OLD and NEW are case sensitive full account names. hledger will re-+ place any occurrence of the old account name with the new one. Subac- counts 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-+ 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-+ 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. Combining aliases- You can define as many aliases as you like, using journal directives+ You can define as many aliases as you like, using journal directives and/or command line options. - Recursive aliases - where an account name is rewritten by one alias,- then by another alias, and so on - are allowed. Each alias sees the+ Recursive aliases - where an account name is rewritten by one alias,+ then by another alias, and so on - are allowed. Each alias sees the effect of previously applied aliases. - In such cases it can be important to understand which aliases will be- applied and in which order. For (each account name in) each journal+ In such cases it can be important to understand which aliases will be+ applied and in which order. For (each account name in) each journal entry, we apply: - 1. alias directives preceding the journal entry, most recently parsed+ 1. alias directives preceding the journal entry, most recently parsed first (ie, reading upward from the journal entry, bottom to top) - 2. --alias options, in the order they appeared on the command line+ 2. --alias options, in the order they appeared on the command line (left to right). In other words, for (an account name in) a given journal entry:@@ -1092,13 +1103,40 @@ o aliases defined after/below the entry do not affect it. - This gives nearby aliases precedence over distant ones, and helps pro-- vide semantic stability - aliases will keep working the same way inde-+ This gives nearby aliases precedence over distant ones, and helps pro-+ vide semantic stability - aliases will keep working the same way inde- pendent of which files are being read and in which order. - In case of trouble, adding --debug=6 to the command line will show+ In case of trouble, adding --debug=6 to the command line will show which aliases are being applied when. + Aliases and multiple files+ As explained at Directives and multiple files, alias directives do not+ affect parent or sibling files. Eg in this command,++ hledger -f a.aliases -f b.journal++ account aliases defined in a.aliases will not affect b.journal. In-+ cluding the aliases doesn't work either:++ include a.aliases++ 2020-01-01 ; not affected by a.aliases+ foo 1+ bar++ This means that account aliases should usually be declared at the start+ of your top-most file, like this:++ alias foo=Foo+ alias bar=Bar++ 2020-01-01 ; affected by aliases above+ foo 1+ bar++ include c.journal ; also affected+ end aliases You can clear (forget) all currently defined aliases with the end aliases directive:@@ -1278,51 +1316,44 @@ For more details, see: balance: Budget report and Budgeting and Fore- casting. - Auto postings / transaction modifiers- Transaction modifier rules, AKA auto posting rules, describe changes to- be applied automatically to certain matched transactions. Currently- just one kind of change is possible - adding extra postings, which we- call "automated postings" or just "auto postings". These rules become- active when you use the --auto flag.+ Auto postings+ "Automated postings" or "auto postings" are extra postings which get+ added automatically to transactions which match certain queries, de-+ fined by "auto posting rules", when you use the --auto flag. - A transaction modifier rule looks much like a normal transaction except- the first line is an equals sign followed by a query that matches cer-- tain postings (mnemonic: = suggests matching). And each "posting" is- actually a posting-generating rule:+ An auto posting rule looks a bit like a transaction: = QUERY ACCOUNT AMOUNT- ACCOUNT [AMOUNT] ...+ ACCOUNT [AMOUNT] - These posting-generating rules look like normal postings, except the- amount can be:+ except the first line is an equals sign (mnemonic: = suggests match-+ ing), followed by a query (which matches existing postings), and each+ "posting" line describes a posting to be generated, and the posting+ amounts can be: - o a normal amount with a commodity symbol, eg $2. This will be used+ o a normal amount with a commodity symbol, eg $2. This will be used as-is. o a number, eg 2. The commodity symbol (if any) from the matched post- ing will be added to this. - o a numeric multiplier, eg *2 (a star followed by a number N). The+ o a numeric multiplier, eg *2 (a star followed by a number N). The matched posting's amount (and total price, if any) will be multiplied by N. - o a multiplier with a commodity symbol, eg *$2 (a star, number N, and+ o a multiplier with a commodity symbol, eg *$2 (a star, number N, and symbol S). The matched posting's amount will be multiplied by N, and its commodity symbol will be replaced with S. - A query term containing spaces must be enclosed in single or double- quotes, as on the command line. Eg, note the quotes around the second+ Any query term containing spaces must be enclosed in single or double+ quotes, as on the command line. Eg, note the quotes around the second query term below: = expenses:groceries 'expenses:dining out' (budget:funds:dining out) *-1 - These rules have global effect - a rule appearing anywhere in your data- can potentially affect any transaction, including transactions recorded- above it or in another file.- Some examples: ; every time I buy food, schedule a dollar donation@@ -1354,36 +1385,41 @@ assets:checking:gifts -$20 assets:checking $20 + Auto postings and multiple files+ An auto posting rule can affect any transaction in the current file, or+ in any parent file or child file. Note, currently it will not affect+ sibling files (when multiple -f/--file are used - see #1212).+ Auto postings and dates- A posting date (or secondary date) in the matched posting, or (taking- precedence) a posting date in the auto posting rule itself, will also+ A posting date (or secondary date) in the matched posting, or (taking+ precedence) a posting date in the auto posting rule itself, will also be used in the generated posting. Auto postings and transaction balancing / inferred amounts / balance asser- tions- Currently, transaction modifiers are applied / auto postings are added:+ Currently, auto postings are added: - o after missing amounts are inferred, and transactions are checked for+ o after missing amounts are inferred, and transactions are checked for balancedness, o but before balance assertions are checked. - Note this means that journal entries must be balanced both before and+ Note this means that journal entries must be balanced both before and after auto postings are added. This changed in hledger 1.12+; see #893 for background. Auto posting tags- Postings added by transaction modifiers will have some extra tags:+ Automated postings will have some extra tags: o generated-posting:= QUERY - shows this was generated by an auto post- ing rule, and the query - o _generated-posting:= QUERY - a hidden tag, which does not appear in+ o _generated-posting:= QUERY - a hidden tag, which does not appear in hledger's output. This can be used to match postings generated "just now", rather than generated in the past and saved to the journal. - Also, any transaction that has been changed by transaction modifier- rules will have these tags added:+ Also, any transaction that has been changed by auto posting rules will+ have these tags added: o modified: - this transaction was modified @@ -1393,7 +1429,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) @@ -1407,7 +1443,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) @@ -1415,4 +1451,4 @@ -hledger 1.17 March 2020 hledger_journal(5)+hledger 1.17.1 March 2020 hledger_journal(5)
hledger_timeclock.5 view
@@ -1,5 +1,5 @@ -.TH "hledger_timeclock" "5" "March 2020" "hledger 1.17" "hledger User Manuals"+.TH "hledger_timeclock" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals"
hledger_timeclock.info view
@@ -4,8 +4,8 @@ File: hledger_timeclock.info, Node: Top, Up: (dir) -hledger_timeclock(5) hledger 1.17-*********************************+hledger_timeclock(5) hledger 1.17.1+*********************************** Timeclock - the time logging format of timeclock.el, as read by hledger
hledger_timeclock.txt view
@@ -78,4 +78,4 @@ -hledger 1.17 March 2020 hledger_timeclock(5)+hledger 1.17.1 March 2020 hledger_timeclock(5)
hledger_timedot.5 view
@@ -1,5 +1,5 @@ -.TH "hledger_timedot" "5" "March 2020" "hledger 1.17" "hledger User Manuals"+.TH "hledger_timedot" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals"
hledger_timedot.info view
@@ -4,8 +4,8 @@ File: hledger_timedot.info, Node: Top, Up: (dir) -hledger_timedot(5) hledger 1.17-*******************************+hledger_timedot(5) hledger 1.17.1+********************************* Timedot - hledger's human-friendly time logging format
hledger_timedot.txt view
@@ -161,4 +161,4 @@ -hledger 1.17 March 2020 hledger_timedot(5)+hledger 1.17.1 March 2020 hledger_timedot(5)
test/doctests.hs view
@@ -27,6 +27,7 @@ import "Glob" System.FilePath.Glob import Test.DocTest +main :: IO () main = do args <- getArgs let
test/unittest.hs view
@@ -5,7 +5,11 @@ -- package-qualified import to avoid cabal missing-home-modules warning (and double-building ?) {-# LANGUAGE PackageImports #-} import "hledger-lib" Hledger (tests_Hledger)-+import System.Environment (setEnv) import Test.Tasty (defaultMain) -main = defaultMain tests_Hledger+main :: IO ()+main = do+ setEnv "TASTY_HIDE_SUCCESSES" "true"+ setEnv "TASTY_ANSI_TRICKS" "false" -- helps the above+ defaultMain tests_Hledger