packages feed

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 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