@@ -36,7 +36,7 @@ import Safe (headDef, headMay, atMay)
36
36
import System.Console.CmdArgs.Explicit (flagNone )
37
37
import System.Console.Haskeline (runInputT , defaultSettings , setComplete )
38
38
import System.Console.Haskeline.Completion (CompletionFunc , completeWord , isFinished , noCompletion , simpleCompletion )
39
- import System.Console.Wizard (Wizard , defaultTo , line , output , retryMsg , linePrewritten , nonEmpty , parser , run )
39
+ import System.Console.Wizard (Wizard , defaultTo , line , output , outputLn , retryMsg , linePrewritten , nonEmpty , parser , run )
40
40
import System.Console.Wizard.Haskeline
41
41
import System.IO ( stderr , hPutStr , hPutStrLn )
42
42
import Text.Megaparsec
@@ -231,16 +231,33 @@ confirmedTransactionWizard prevInput es@EntryState{..} stack@(currentStage : _)
231
231
confirmedTransactionWizard prevInput es{esPostings= init esPostings} (dropWhile notPrevAmountAndNotEnterDesc stack)
232
232
233
233
EnterAmountAndComment txnParams account -> amountAndCommentWizard prevInput es >>= \ case
234
- Just (amt, comment) -> do
234
+ Just (amt, assertion, (comment, tags, pdate1, pdate2)) -> do
235
+ -- This check is necessary because we cons a ';' in the comment parser above,
236
+ -- and we don't want to add an empty comment here if it wasn't given.
237
+ let pcomment = if T. length comment == 1 then " " else comment
235
238
let p = nullposting{paccount= T. pack $ stripbrackets account
236
239
,pamount= mixedAmount amt
237
- ,pcomment= comment
240
+ ,pcomment= pcomment
238
241
,ptype= accountNamePostingType $ T. pack account
242
+ ,pbalanceassertion = assertion
243
+ ,pdate= pdate1
244
+ ,pdate2= pdate2
245
+ ,ptags= tags
239
246
}
240
247
amountAndCommentString = showAmount amt ++ T. unpack (if T. null comment then " " else " ;" <> comment)
241
248
prevAmountAndCmnt' = replaceNthOrAppend (length esPostings) amountAndCommentString (prevAmountAndCmnt prevInput)
242
249
es' = es{esPostings= esPostings++ [p], esArgs= drop 1 esArgs}
243
- confirmedTransactionWizard prevInput{prevAmountAndCmnt= prevAmountAndCmnt'} es' (EnterNewPosting txnParams (Just posting) : stack)
250
+ -- Include a dummy posting to balance the unfinished transation in assertion checking
251
+ dummytxn = nulltransaction{tpostings = esPostings ++ [p, post " " missingamt]
252
+ ,tdate = txnDate txnParams
253
+ ,tdescription = txnDesc txnParams }
254
+ validated = balanceTransaction defbalancingopts dummytxn >>= checkAssertions defbalancingopts esJournal
255
+ case validated of
256
+ Left err -> do
257
+ liftIO (hPutStrLn stderr err)
258
+ confirmedTransactionWizard prevInput es (EnterAmountAndComment txnParams account : stack)
259
+ Right _ ->
260
+ confirmedTransactionWizard prevInput{prevAmountAndCmnt= prevAmountAndCmnt'} es' (EnterNewPosting txnParams (Just posting) : stack)
244
261
Nothing -> confirmedTransactionWizard prevInput es (drop 1 stack)
245
262
246
263
EndStage t -> do
@@ -324,7 +341,10 @@ accountWizard PrevInput{..} EntryState{..} = do
324
341
| otherwise = Just t
325
342
dbg' = id -- strace
326
343
327
- amountAndCommentWizard PrevInput {.. } EntryState {.. } = do
344
+ type Comment = (Text , [Tag ], Maybe Day , Maybe Day )
345
+
346
+ amountAndCommentWizard :: PrevInput -> EntryState -> Wizard Haskeline (Maybe (Amount , Maybe BalanceAssertion , Comment ))
347
+ amountAndCommentWizard previnput@ PrevInput {.. } entrystate@ EntryState {.. } = do
328
348
let pnum = length esPostings + 1
329
349
(mhistoricalp,followedhistoricalsofar) =
330
350
case esSimilarTransaction of
@@ -339,26 +359,36 @@ amountAndCommentWizard PrevInput{..} EntryState{..} = do
339
359
| Just hp <- mhistoricalp, followedhistoricalsofar = showamt $ pamount hp
340
360
| pnum > 1 && not (mixedAmountLooksZero balancingamt) = showamt balancingamtfirstcommodity
341
361
| otherwise = " "
342
- retryMsg " A valid hledger amount is required. Eg: 1, $2, 3 EUR, \" 4 red apples\" ." $
343
- parser parseAmountAndComment $
362
+ retryMsg " A valid hledger amount is required. Eg: 1, $2, 3 EUR, \" 4 red apples\" ." $
363
+ parser' parseAmountAndComment $
344
364
withCompletion (amountCompleter def) $
345
365
defaultTo' def $
346
366
nonEmpty $
347
367
linePrewritten (green' $ printf " Amount %d%s: " pnum (showDefault def)) (fromMaybe " " $ prevAmountAndCmnt `atMay` length esPostings) " "
348
368
where
349
- parseAmountAndComment s = if s == " <" then return Nothing else either (const Nothing ) (return . Just ) $
350
- runParser
351
- (evalStateT (amountandcommentp <* eof) nodefcommodityj)
352
- " "
353
- (T. pack s)
369
+ -- Custom parser that combines with Wizard to use IO via outputLn
370
+ parser' f a = a >>= \ input ->
371
+ case f input of
372
+ Left err -> do
373
+ outputLn (customErrorBundlePretty err)
374
+ amountAndCommentWizard previnput entrystate
375
+ Right res -> pure res
376
+ parseAmountAndComment s =
377
+ if s == " <" then Right Nothing else
378
+ Just <$> runParser
379
+ (evalStateT (amountandcommentp <* eof) nodefcommodityj)
380
+ " "
381
+ (T. pack s)
354
382
nodefcommodityj = esJournal{jparsedefaultcommodity= Nothing }
355
- amountandcommentp :: JournalParser Identity (Amount , Text )
383
+ amountandcommentp :: JournalParser Identity (Amount , Maybe BalanceAssertion , Comment )
356
384
amountandcommentp = do
357
385
a <- amountp
358
386
lift skipNonNewlineSpaces
359
- c <- T. pack <$> fromMaybe " " `fmap` optional (char ' ;' >> many anySingle)
360
- -- eof
361
- return (a,c)
387
+ assertion <- optional balanceassertionp
388
+ com <- T. pack <$> fromMaybe " " `fmap` optional (char ' ;' >> many anySingle)
389
+ case rtp (postingcommentp Nothing ) (T. cons ' ;' com) of
390
+ Left err -> fail $ customErrorBundlePretty err
391
+ Right comment -> return $ (a, assertion, comment)
362
392
balancingamt = maNegate . sumPostings $ filter isReal esPostings
363
393
balancingamtfirstcommodity = mixed . take 1 $ amounts balancingamt
364
394
showamt = wbUnpack . showMixedAmountB defaultFmt . mixedAmountSetPrecision
0 commit comments