refactor/fix balanced entry checking and test it properly

This commit is contained in:
Simon Michael 2009-04-01 08:55:46 +00:00
parent 0cacc2a7e4
commit 89abdfa456
5 changed files with 61 additions and 35 deletions

View File

@ -49,7 +49,13 @@ pcommentwidth = no limit -- 22
@ @
-} -}
showEntry :: Entry -> String showEntry :: Entry -> String
showEntry e = showEntry = showEntry' True
showEntryUnelided :: Entry -> String
showEntryUnelided = showEntry' False
showEntry' :: Bool -> Entry -> String
showEntry' elide e =
unlines $ [{-precedingcomment ++ -}description] ++ (showtxns $ etransactions e) ++ [""] unlines $ [{-precedingcomment ++ -}description] ++ (showtxns $ etransactions e) ++ [""]
where where
precedingcomment = epreceding_comment_lines e precedingcomment = epreceding_comment_lines e
@ -59,8 +65,9 @@ showEntry e =
code = if (length $ ecode e) > 0 then (printf " (%s)" $ ecode e) else "" code = if (length $ ecode e) > 0 then (printf " (%s)" $ ecode e) else ""
desc = " " ++ edescription e desc = " " ++ edescription e
comment = if (length $ ecomment e) > 0 then " ; "++(ecomment e) else "" comment = if (length $ ecomment e) > 0 then " ; "++(ecomment e) else ""
showtxns (t1:t2:[]) = [showtxn t1, showtxnnoamt t2] showtxns ts
showtxns ts = map showtxn ts | elide && length ts == 2 = [showtxn (ts !! 0), showtxnnoamt (ts !! 1)]
| otherwise = map showtxn ts
showtxn t = showacct t ++ " " ++ (showamount $ tamount t) ++ (showcomment $ tcomment t) showtxn t = showacct t ++ " " ++ (showamount $ tamount t) ++ (showcomment $ tcomment t)
showtxnnoamt t = showacct t ++ " " ++ (showcomment $ tcomment t) showtxnnoamt t = showacct t ++ " " ++ (showcomment $ tcomment t)
showacct t = " " ++ (showaccountname $ taccount t) showacct t = " " ++ (showaccountname $ taccount t)
@ -77,19 +84,19 @@ isEntryBalanced (Entry {etransactions=ts}) =
-- amount first. We can auto-fill if there is just one non-virtual -- amount first. We can auto-fill if there is just one non-virtual
-- transaction without an amount. The auto-filled balance will be -- transaction without an amount. The auto-filled balance will be
-- converted to cost basis if possible. If the entry can not be balanced, -- converted to cost basis if possible. If the entry can not be balanced,
-- raise an error. -- return an error message instead.
balanceEntry :: Entry -> Entry balanceEntry :: Entry -> Either String Entry
balanceEntry e@Entry{etransactions=ts} = (e{etransactions=ts'}) balanceEntry e@Entry{etransactions=ts}
| length missingamounts > 1 = Left $ showerr "could not balance this entry, too many missing amounts"
| not $ isEntryBalanced e' = Left $ showerr "could not balance this entry, amounts do not balance"
| otherwise = Right e'
where where
check e
| isEntryBalanced e = e
| otherwise = error $ "could not balance this entry:\n" ++ show e
(withamounts, missingamounts) = partition hasAmount $ filter isReal ts (withamounts, missingamounts) = partition hasAmount $ filter isReal ts
ts' = case (length missingamounts) of e' = e{etransactions=ts'}
0 -> ts ts' | length missingamounts == 1 = map balance ts
1 -> map balance ts | otherwise = ts
otherwise -> error $ "could not balance this entry, too many missing amounts:\n" ++ show e where
otherstotal = sum $ map tamount withamounts balance t | isReal t && not (hasAmount t) = t{tamount = costOfMixedAmount (-otherstotal)}
balance t | otherwise = t
| isReal t && not (hasAmount t) = t{tamount = costOfMixedAmount (-otherstotal)} where otherstotal = sum $ map tamount withamounts
| otherwise = t showerr s = printf "%s:\n%s" s (showEntryUnelided e)

View File

@ -301,6 +301,8 @@ ledgerDefaultYear = do
setYear y' setYear y'
return $ return id return $ return id
-- | Try to parse a ledger entry. If we successfully parse an entry, ensure it is balanced,
-- and if we cannot, raise an error.
ledgerEntry :: GenParser Char LedgerFileCtx Entry ledgerEntry :: GenParser Char LedgerFileCtx Entry
ledgerEntry = do ledgerEntry = do
date <- ledgerdate <?> "entry" date <- ledgerdate <?> "entry"
@ -313,7 +315,10 @@ ledgerEntry = do
comment <- ledgercomment comment <- ledgercomment
restofline restofline
transactions <- ledgertransactions transactions <- ledgertransactions
return $ balanceEntry $ Entry date status code description comment transactions "" let e = Entry date status code description comment transactions ""
case balanceEntry e of
Right e' -> return e'
Left err -> error err
ledgerdate :: GenParser Char LedgerFileCtx Day ledgerdate :: GenParser Char LedgerFileCtx Day
ledgerdate = try ledgerfulldate <|> ledgerpartialdate ledgerdate = try ledgerfulldate <|> ledgerpartialdate

View File

@ -239,3 +239,13 @@ spacenonewline = satisfy (\c -> c `elem` " \v\f\t")
restofline :: GenParser Char st String restofline :: GenParser Char st String
restofline = anyChar `manyTill` newline restofline = anyChar `manyTill` newline
isLeft :: Either a b -> Bool
isLeft (Left _) = True
isLeft _ = False
isRight :: Either a b -> Bool
isRight = not . isLeft

16
NOTES
View File

@ -10,24 +10,8 @@ clever tricks like the plague." --Edsger Dijkstra
* to do * to do
** errors ** errors
*** not catching some unbalanced entries, two ways:
**** 1
1/1 test1
a $-100
b -10h @ $10
; $ hledger -B reg -- test1
; 2009/01/01 test1 a $-100.00 $-100.00
**** 2
1/1 test2
a $-100
b $-100
; $ hledger -B reg -- test2
; 2009/01/01 test2 a $-100.00 $-100.00
; b $-100.00 $-200.00
*** --depth works with reg -W but not with reg *** --depth works with reg -W but not with reg
*** register report should sort by date *** register report should sort by date
*** too many dependencies, hard to install esp. without cabal install
** features ** features
*** more ledger features *** more ledger features
**** rename entry -> transaction, transaction -> posting **** rename entry -> transaction, transaction -> posting

View File

@ -259,7 +259,27 @@ tests = [
] ]
,"balanceEntry" ~: do ,"balanceEntry" ~: do
(tamount $ last $ etransactions $ balanceEntry entry1) `is` Mixed [dollars (-47.18)] let fromeither (Left err) = error err
fromeither (Right e) = e
(tamount $ last $ etransactions $ fromeither $ balanceEntry entry1) `is` Mixed [dollars (-47.18)]
assertBool "detect unbalanced entry, sign error"
(isLeft $ balanceEntry
(Entry (parsedate "2007/01/28") False "" "test" ""
[RawTransaction False "a" (Mixed [dollars 1]) "" RegularTransaction,
RawTransaction False "b" (Mixed [dollars 1]) "" RegularTransaction
] ""))
assertBool "detect unbalanced entry, multiple missing amounts"
(isLeft $ balanceEntry
(Entry (parsedate "2007/01/28") False "" "test" ""
[RawTransaction False "a" missingamt "" RegularTransaction,
RawTransaction False "b" missingamt "" RegularTransaction
] ""))
assertBool "one missing amount should be ok"
(isRight $ balanceEntry
(Entry (parsedate "2007/01/28") False "" "test" ""
[RawTransaction False "a" (Mixed [dollars 1]) "" RegularTransaction,
RawTransaction False "b" missingamt "" RegularTransaction
] ""))
,"balancereportacctnames" ~: ,"balancereportacctnames" ~:
let gives (opt,pats) e = do let gives (opt,pats) e = do