journal: make balance assertions exact again (#941)

Going with option 1b from the issue: calculated and asserted amounts
are compared exactly, disregarding display precision.
But now balance assertion failure messages show those exact amounts at
full precision, avoiding confusion.
This commit is contained in:
Simon Michael 2019-01-08 16:51:11 +00:00
parent e120e261bd
commit 70b11ed0a4
4 changed files with 58 additions and 136 deletions

View File

@ -583,50 +583,53 @@ checkBalanceAssertion p@Posting{pbalanceassertion=Just (BalanceAssertion{baamoun
| otherwise = [] | otherwise = []
checkBalanceAssertion _ _ = Right () checkBalanceAssertion _ _ = Right ()
-- | Does the difference between the asserted balance -- | Are the asserted balance and the actual balance
-- and (the corresponding part of) the actual balance -- exactly equal (disregarding display precision) ?
-- appear as zero, when rendered to the greater of -- The posting is used for creating an error message.
-- 1. the standard display precision for the commodity
-- 2. the full precision of the asserted amount ?
-- The posting is used when creating an error message.
checkBalanceAssertionCommodity :: Posting -> Amount -> MixedAmount -> Either String () checkBalanceAssertionCommodity :: Posting -> Amount -> MixedAmount -> Either String ()
checkBalanceAssertionCommodity p assertedamt actualbal checkBalanceAssertionCommodity p assertedamt actualbal
| isZeroAmount diff = Right () | pass = Right ()
| True = Left err | otherwise = Left err
where where
diff =
-- traceWith (("diff:"++).showAmountDebug) $
-- traceWith (("asserted:"++).showAmountDebug)
assertedamt -
-- traceWith (("actual:"++).showAmountDebug)
actualbalincommodity
assertedcomm = acommodity assertedamt assertedcomm = acommodity assertedamt
actualbalincommodity = fromMaybe nullamt $ find ((== assertedcomm) . acommodity) (amounts actualbal) actualbalincommodity = fromMaybe nullamt $ find ((== assertedcomm) . acommodity) (amounts actualbal)
diffplus | isNegativeAmount diff == False = "+" pass =
| otherwise = "" aquantity
-- traceWith (("asserted:"++).showAmountDebug)
assertedamt ==
aquantity
-- traceWith (("actual:"++).showAmountDebug)
actualbalincommodity
diff = aquantity assertedamt - aquantity actualbalincommodity
err = printf (unlines err = printf (unlines
[ "balance assertion error%s", [ "balance assertion: %s",
"after posting:", "\nassertion details:",
"%s",
"balance assertion details:",
"date: %s", "date: %s",
"account: %s", "account: %s",
"commodity: %s", "commodity: %s",
"calculated: %s", -- "display precision: %d",
"asserted: %s (difference: %s)" "calculated: %s", -- (at display precision: %s)",
"asserted: %s", -- (at display precision: %s)",
"difference: %s"
]) ])
(case ptransaction p of (case ptransaction p of
Nothing -> ":" -- shouldn't happen Nothing -> "?" -- shouldn't happen
Just t -> printf " in %s:\nin transaction:\n%s" Just t -> printf "%s\ntransaction:\n%s"
(showGenericSourcePos pos) (chomp $ showTransaction t) :: String (showGenericSourcePos pos)
where pos = baposition $ fromJust $ pbalanceassertion p) (chomp $ showTransaction t)
(showPostingLine p) :: String
where
pos = baposition $ fromJust $ pbalanceassertion p
)
(showDate $ postingDate p) (showDate $ postingDate p)
(T.unpack $ paccount p) -- XXX pack (T.unpack $ paccount p) -- XXX pack
assertedcomm assertedcomm
(showAmount actualbalincommodity) -- (asprecision $ astyle actualbalincommodity) -- should be the standard display precision I think
(showAmount assertedamt) (show $ aquantity actualbalincommodity)
(diffplus ++ showAmount diff) -- (showAmount actualbalincommodity)
(show $ aquantity assertedamt)
-- (showAmount assertedamt)
(show diff)
-- | Fill in any missing amounts and check that all journal transactions -- | Fill in any missing amounts and check that all journal transactions
-- balance and all balance assertions pass, or return an error message. -- balance and all balance assertions pass, or return an error message.

View File

@ -35,7 +35,7 @@ module Hledger.Data.Transaction (
showTransaction, showTransaction,
showTransactionUnelided, showTransactionUnelided,
showTransactionUnelidedOneLineAmounts, showTransactionUnelidedOneLineAmounts,
showPostingLine, -- showPostingLine,
showPostingLines, showPostingLines,
-- * GenericSourcePos -- * GenericSourcePos
sourceFilePath, sourceFilePath,
@ -246,14 +246,17 @@ postingAsLines elideamount onelineamounts pstoalignwith p = concat [
case renderCommentLines (pcomment p) of [] -> ("",[]) case renderCommentLines (pcomment p) of [] -> ("",[])
c:cs -> (c,cs) c:cs -> (c,cs)
-- | Show a posting's status, account name and amount on one line. -- | Render a posting, simply. Used in balance assertion errors.
-- Used in balance assertion errors. -- showPostingLine p =
showPostingLine p = -- indent $
indent $ -- if pstatus p == Cleared then "* " else "" ++ -- XXX show !
if pstatus p == Cleared then "* " else "" ++ -- showAccountName Nothing (ptype p) (paccount p) ++
showAccountName Nothing (ptype p) (paccount p) ++ -- " " ++
" " ++ -- showMixedAmountOneLine (pamount p) ++
showMixedAmountOneLine (pamount p) -- assertion
-- where
-- -- XXX extract, handle ==
-- assertion = maybe "" ((" = " ++) . showAmountWithZeroCommodity . baamount) $ pbalanceassertion p
-- | Render a posting, at the appropriate width for aligning with -- | Render a posting, at the appropriate width for aligning with
-- its siblings if any. Used by the rewrite command. -- its siblings if any. Used by the rewrite command.

View File

@ -468,71 +468,11 @@ flag or `real:` query.
### Assertions and precision ### Assertions and precision
A [commodity directive](http://hledger.org/journal.html#declaring-commodities) Balance assertions compare the exactly calculated amounts,
which limits the display precision, can affect assertions. which are not always what is shown by reports.
Eg a [commodity directive](http://hledger.org/journal.html#declaring-commodities)
In general, hledger balance assertions should pass or fail as you would may limit the display precision, but this will not affect balance assertions.
expect from doing visual inspection and manual arithmetic with the amounts Balance assertion failure messages show exact amounts.
shown in reports and error messages, ie at display precision.
More specifically, currently assertions pass if the difference between asserted
and actual amounts appears to be zero, when rendered to the greater of
the standard display precision and the asserted amount's precision.
Here are some examples of this in action.
Asserting the exact balance:
```journal
commodity $1000.00
2019/01/01
(a) $0.006
2019/01/02
(a) $1.00 = $1.006
; Actual balance: 1.006
; Asserted balence: 1.006
; Difference: 0.000
; Standard & asserted precisions: 2, 3
; Difference rendered: 0.000
; Result: pass
```
Asserting the balance rounded to fewer decimal places:
```journal
commodity $1000.00
2019/01/01
(a) $0.006
2019/01/02
(a) $1.00 = $1.01
; Actual balance: 1.006
; Asserted balence: 1.01
; Difference: 0.004
; Standard & asserted precisions: 2, 2
; Difference rendered: 0.00
; Result: pass
```
Asserting an inexact balance with too many decimal places (fails):
```journal
commodity $1000.00
2019/01/01
(a) $0.006
2019/01/02
(a) $1.00 = $1.0061
; Actual balance: 1.006
; Asserted balence: 1.0061
; Difference: 0.0001
; Standard & asserted precisions: 2, 4
; Difference rendered: 0.0001
; Result: fail
```
## Balance Assignments ## Balance Assignments

View File

@ -57,7 +57,7 @@ hledger -f - stats
b $-1 = $-3 b $-1 = $-3
>>> >>>
>>>2 /balance assertion error.*line 11, column 12/ >>>2 /balance assertion.*line 11, column 12/
>>>=1 >>>=1
# 4. should also work without commodity symbols # 4. should also work without commodity symbols
@ -335,7 +335,7 @@ hledger -f - stats
2016/1/3 2016/1/3
a 0 == $1 a 0 == $1
>>>2 /balance assertion error.*line 10, column 15/ >>>2 /balance assertion.*line 10, column 15/
>>>=1 >>>=1
# 19. Mix different commodities and exact assignments # 19. Mix different commodities and exact assignments
@ -366,17 +366,7 @@ hledger -f- stats
>>>2 /unexpected '@'/ >>>2 /unexpected '@'/
>>>=1 >>>=1
# 21. With a commodity directive limiting the display precision. # 21. The exact amounts are compared; display precision does not affect assertions.
# Assertions pass if the difference between asserted and actual amounts
# appears to be zero, when rendered to the greater of the standard
# display precision and the asserted amount's precision.
# Here,
# Actual balance: 1.006
# Asserted balence: 1.006
# Difference: 0.000
# Standard & asserted precisions: 2, 3
# Difference rendered: 0.000
# Result: pass
hledger -f- stats hledger -f- stats
<<< <<<
commodity $1000.00 commodity $1000.00
@ -391,13 +381,7 @@ commodity $1000.00
>>>2 >>>2
>>>=0 >>>=0
# 22. A rounded assertion amount can also pass. Here, # 22. This fails
# Actual balance: 1.006
# Asserted balence: 1.01
# Difference: 0.004
# Standard & asserted precisions: 2, 2
# Difference rendered: 0.00
# Result: pass
hledger -f- stats hledger -f- stats
<<< <<<
commodity $1000.00 commodity $1000.00
@ -408,17 +392,10 @@ commodity $1000.00
2019/01/02 2019/01/02
(a) $1.00 = $1.01 (a) $1.00 = $1.01
>>> /Transactions/ >>>2 /difference: 0\.004/
>>>2 >>>=1
>>>=0
# 23. A more precise assertion amount can fail. Here, # 23. This fails
# Actual balance: 1.006
# Asserted balence: 1.0061
# Difference: 0.0001
# Standard & asserted precisions: 2, 4
# Difference rendered: 0.0001
# Result: fail
hledger -f- stats hledger -f- stats
<<< <<<
commodity $1000.00 commodity $1000.00
@ -429,6 +406,5 @@ commodity $1000.00
2019/01/02 2019/01/02
(a) $1.00 = $1.0061 (a) $1.00 = $1.0061
>>> >>>2 /difference: 0\.0001/
>>>2 /difference: \+\$0\.0001/
>>>=1 >>>=1