diff --git a/hledger-lib/Hledger/Data/Journal.hs b/hledger-lib/Hledger/Data/Journal.hs index 3f34c10fcf0..8c9d64f5f2a 100644 --- a/hledger-lib/Hledger/Data/Journal.hs +++ b/hledger-lib/Hledger/Data/Journal.hs @@ -225,6 +225,7 @@ journalConcat j1 j2 = ,jparsedecimalmark = jparsedecimalmark j2 ,jparseparentaccounts = jparseparentaccounts j2 ,jparsealiases = jparsealiases j2 + ,jparsedefaultaccount = jparsedefaultaccount j2 -- ,jparsetransactioncount = jparsetransactioncount j1 + jparsetransactioncount j2 ,jparsetimeclockentries = jparsetimeclockentries j1 <> jparsetimeclockentries j2 ,jincludefilestack = jincludefilestack j2 @@ -284,6 +285,7 @@ nulljournal = Journal { ,jparsedecimalmark = Nothing ,jparseparentaccounts = [] ,jparsealiases = [] + ,jparsedefaultaccount = Nothing -- ,jparsetransactioncount = 0 ,jparsetimeclockentries = [] ,jincludefilestack = [] diff --git a/hledger-lib/Hledger/Data/Types.hs b/hledger-lib/Hledger/Data/Types.hs index f4952a9a82b..e7d5bba4b15 100644 --- a/hledger-lib/Hledger/Data/Types.hs +++ b/hledger-lib/Hledger/Data/Types.hs @@ -581,6 +581,7 @@ data Journal = Journal { ,jparsedecimalmark :: Maybe DecimalMark -- ^ the character to always parse as decimal point, if set by CsvReader's decimal-mark (or a future journal directive) ,jparseparentaccounts :: [AccountName] -- ^ the current stack of parent account names, specified by apply account directives ,jparsealiases :: [AccountAlias] -- ^ the current account name aliases in effect, specified by alias directives (& options ?) + ,jparsedefaultaccount :: Maybe AccountName -- ,jparsetransactioncount :: Integer -- ^ the current count of transactions parsed so far (only journal format txns, currently) ,jparsetimeclockentries :: [TimeclockEntry] -- ^ timeclock sessions which have not been clocked out ,jincludefilestack :: [FilePath] diff --git a/hledger-lib/Hledger/Read/Common.hs b/hledger-lib/Hledger/Read/Common.hs index 18e576a3618..c0f21dcfa54 100644 --- a/hledger-lib/Hledger/Read/Common.hs +++ b/hledger-lib/Hledger/Read/Common.hs @@ -43,6 +43,8 @@ module Hledger.Read.Common ( journalAddAutoPostings, setYear, getYear, + setDefaultAccount, + getDefaultAccount, setDefaultCommodityAndStyle, getDefaultCommodityAndStyle, getDefaultAmountStyle, @@ -442,6 +444,12 @@ getParentAccount = fmap (concatAccountNames . reverse . jparseparentaccounts) ge addAccountAlias :: MonadState Journal m => AccountAlias -> m () addAccountAlias a = modify' (\(j@Journal{..}) -> j{jparsealiases=a:jparsealiases}) +setDefaultAccount :: MonadState Journal m => Maybe AccountName -> m () +setDefaultAccount a = modify' (\j -> j{jparsedefaultaccount=a}) + +getDefaultAccount :: JournalParser m (Maybe AccountName) +getDefaultAccount = jparsedefaultaccount `fmap` get + getAccountAliases :: MonadState Journal m => m [AccountAlias] getAccountAliases = fmap jparsealiases get diff --git a/hledger-lib/Hledger/Read/JournalReader.hs b/hledger-lib/Hledger/Read/JournalReader.hs index 369b6457f17..ea06c62a558 100644 --- a/hledger-lib/Hledger/Read/JournalReader.hs +++ b/hledger-lib/Hledger/Read/JournalReader.hs @@ -542,7 +542,7 @@ formatdirectivep expectedsym = do -- More Ledger directives, ignore for now: -- apply fixed, apply tag, assert, bucket, A, capture, check, define, expr applyfixeddirectivep, endapplyfixeddirectivep, applytagdirectivep, endapplytagdirectivep, - assertdirectivep, bucketdirectivep, capturedirectivep, checkdirectivep, + assertdirectivep, capturedirectivep, checkdirectivep, endapplyyeardirectivep, definedirectivep, exprdirectivep, valuedirectivep, evaldirectivep, pythondirectivep, commandlineflagdirectivep :: JournalParser m () @@ -552,7 +552,6 @@ applytagdirectivep = do string "apply tag" >> lift restofline >> return () endapplytagdirectivep = do string "end apply tag" >> lift restofline >> return () endapplyyeardirectivep = do string "end apply year" >> lift restofline >> return () assertdirectivep = do string "assert" >> lift restofline >> return () -bucketdirectivep = do string "A " <|> string "bucket " >> lift restofline >> return () capturedirectivep = do string "capture" >> lift restofline >> return () checkdirectivep = do string "check" >> lift restofline >> return () definedirectivep = do string "define" >> lift restofline >> return () @@ -696,6 +695,13 @@ decimalmarkdirectivep = do lift restofline return () +bucketdirectivep :: JournalParser m () +bucketdirectivep = do + keywordp "bucket" "bucket directive" + lift (skipMany spacenonewline) + acct <- modifiedaccountnamep + setDefaultAccount (Just acct) + --- *** transactions -- | Parse a transaction modifier (auto postings) rule. @@ -788,8 +794,20 @@ transactionp = do let year = first3 $ toGregorian date postings <- postingsp (Just year) endpos <- getSourcePos + acc <- getDefaultAccount + let postingsBucketed = ( + postings ++ [posting { pdate=Nothing + , pdate2=Nothing + , pstatus=Unmarked + , paccount=fromJust acc + , pamount=missingmixedamt + , pcomment="" + , ptype=RegularPosting + , ptags=[] + , pbalanceassertion=Nothing + } | all hasAmount postings && isJust acc]) let sourcepos = (startpos, endpos) - return $ txnTieKnot $ Transaction 0 "" sourcepos date edate status code description comment tags postings + return $ txnTieKnot $ Transaction 0 "" sourcepos date edate status code description comment tags postingsBucketed --- *** postings @@ -1080,6 +1098,39 @@ tests_JournalReader = testGroup "JournalReader" [ (length . tpostings) 2 + ,testCase "unbalanced simple transaction within bucketed context" $ do + ep <- parseWithState nulljournal{jparsedefaultaccount=Just "b"} transactionp + (T.unlines + ["2009/1/1 x" + ," a -1" + ]) + assertRight ep + let (Right x) = ep + length (tpostings x) @?= 2 + (tpostings x !! 1) @?= nullposting{paccount="b", pamount=missingmixedamt} + + ,testCase "multi-leg transaction within bucketed context" $ do + ep <- parseWithState nulljournal{jparsedefaultaccount=Just "b"} transactionp + (T.unlines + ["2009/1/1 x" + ," a -2" + ," c 1" + ]) + assertRight ep + let (Right x) = ep + length (tpostings x) @?= 3 + (tpostings x !! 2) @?= nullposting{paccount="b", pamount=missingmixedamt} + + ,testCase "self-balancing transaction within bucketed context" $ do + ep <- parseWithState nulljournal{jparsedefaultaccount=Just "b"} transactionp + (T.unlines + ["2009/1/1 x" + ," a -2" + ," c" + ]) + assertRight ep + let (Right x) = ep + length (tpostings x) @?= 2 ] -- directives @@ -1151,6 +1202,12 @@ tests_JournalReader = testGroup "JournalReader" [ testCase "empty file" $ assertParseEqE journalp "" nulljournal ] + ,testGroup "bucketdirectivep" [ + testCase "affects state" $ assertParseStateOn bucketdirectivep "bucket a:b" + jparsedefaultaccount + (Just "a:b") + ] + -- these are defined here rather than in Common so they can use journalp ,testCase "parseAndFinaliseJournal" $ do ej <- runExceptT $ parseAndFinaliseJournal journalp definputopts "" "2019-1-1\n"