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:
		
							parent
							
								
									e120e261bd
								
							
						
					
					
						commit
						70b11ed0a4
					
				| @ -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) $ | ||||
|         -- traceWith (("asserted:"++).showAmountDebug) | ||||
|         assertedamt - | ||||
|         -- traceWith (("actual:"++).showAmountDebug) | ||||
|         actualbalincommodity | ||||
|       assertedcomm = acommodity assertedamt | ||||
|       actualbalincommodity = fromMaybe nullamt $ find ((== assertedcomm) . acommodity) (amounts actualbal) | ||||
|       diffplus | isNegativeAmount diff == False = "+" | ||||
|                | otherwise = "" | ||||
|       pass = | ||||
|         aquantity | ||||
|         -- traceWith (("asserted:"++).showAmountDebug) | ||||
|         assertedamt == | ||||
|         aquantity | ||||
|         -- traceWith (("actual:"++).showAmountDebug) | ||||
|         actualbalincommodity | ||||
|       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 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user