Permalink
Browse files

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.
  • Loading branch information...
simonmichael committed Jan 8, 2019
1 parent e120e26 commit 70b11ed0a4b75f401cef34557c1c0011358128b4
@@ -583,50 +583,53 @@ checkBalanceAssertion p@Posting{pbalanceassertion=Just (BalanceAssertion{baamoun
| otherwise = []
checkBalanceAssertion _ _ = Right ()

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

-- | Fill in any missing amounts and check that all journal transactions
-- balance and all balance assertions pass, or return an error message.
@@ -35,7 +35,7 @@ module Hledger.Data.Transaction (
showTransaction,
showTransactionUnelided,
showTransactionUnelidedOneLineAmounts,
showPostingLine,
-- showPostingLine,
showPostingLines,
-- * GenericSourcePos
sourceFilePath,
@@ -246,14 +246,17 @@ postingAsLines elideamount onelineamounts pstoalignwith p = concat [
case renderCommentLines (pcomment p) of [] -> ("",[])
c:cs -> (c,cs)

-- | Show a posting's status, account name and amount on one line.
-- Used in balance assertion errors.
showPostingLine p =
indent $
if pstatus p == Cleared then "* " else "" ++
showAccountName Nothing (ptype p) (paccount p) ++
" " ++
showMixedAmountOneLine (pamount p)
-- | Render a posting, simply. Used in balance assertion errors.
-- showPostingLine p =
-- indent $
-- if pstatus p == Cleared then "* " else "" ++ -- XXX show !
-- showAccountName Nothing (ptype p) (paccount 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
-- its siblings if any. Used by the rewrite command.
@@ -468,71 +468,11 @@ flag or `real:` query.

### Assertions and precision

A [commodity directive](http://hledger.org/journal.html#declaring-commodities)
which limits the display precision, can affect assertions.

In general, hledger balance assertions should pass or fail as you would
expect from doing visual inspection and manual arithmetic with the 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 assertions compare the exactly calculated amounts,
which are not always what is shown by reports.
Eg a [commodity directive](http://hledger.org/journal.html#declaring-commodities)
may limit the display precision, but this will not affect balance assertions.
Balance assertion failure messages show exact amounts.

## Balance Assignments

@@ -57,7 +57,7 @@ hledger -f - stats
b $-1 = $-3

>>>
>>>2 /balance assertion error.*line 11, column 12/
>>>2 /balance assertion.*line 11, column 12/
>>>=1

# 4. should also work without commodity symbols
@@ -335,7 +335,7 @@ hledger -f - stats

2016/1/3
a 0 == $1
>>>2 /balance assertion error.*line 10, column 15/
>>>2 /balance assertion.*line 10, column 15/
>>>=1

# 19. Mix different commodities and exact assignments
@@ -366,17 +366,7 @@ hledger -f- stats
>>>2 /unexpected '@'/
>>>=1

# 21. With a commodity directive limiting the display precision.
# 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
# 21. The exact amounts are compared; display precision does not affect assertions.
hledger -f- stats
<<<
commodity $1000.00
@@ -391,13 +381,7 @@ commodity $1000.00
>>>2
>>>=0

# 22. A rounded assertion amount can also pass. Here,
# Actual balance: 1.006
# Asserted balence: 1.01
# Difference: 0.004
# Standard & asserted precisions: 2, 2
# Difference rendered: 0.00
# Result: pass
# 22. This fails
hledger -f- stats
<<<
commodity $1000.00
@@ -408,17 +392,10 @@ commodity $1000.00
2019/01/02
(a) $1.00 = $1.01

>>> /Transactions/
>>>2
>>>=0
>>>2 /difference: 0\.004/
>>>=1

# 23. A more precise assertion amount can fail. Here,
# Actual balance: 1.006
# Asserted balence: 1.0061
# Difference: 0.0001
# Standard & asserted precisions: 2, 4
# Difference rendered: 0.0001
# Result: fail
# 23. This fails
hledger -f- stats
<<<
commodity $1000.00
@@ -429,6 +406,5 @@ commodity $1000.00
2019/01/02
(a) $1.00 = $1.0061

>>>
>>>2 /difference: \+\$0\.0001/
>>>2 /difference: 0\.0001/
>>>=1

0 comments on commit 70b11ed

Please sign in to comment.