lib: Make sure fromamount and toamount have opposite signs when inferring prices.
Also change priceInferrerFor so that it would give the correct (negative) prices when fromamount and toamount have the same sign.
This commit is contained in:
		
							parent
							
								
									0078f1a520
								
							
						
					
					
						commit
						686a0871a9
					
				| @ -552,41 +552,42 @@ inferBalancingPrices t@Transaction{tpostings=ps} = t{tpostings=ps'} | |||||||
| -- posting type (real or balanced virtual). If we cannot or should not infer | -- posting type (real or balanced virtual). If we cannot or should not infer | ||||||
| -- prices, just act as the identity on postings. | -- prices, just act as the identity on postings. | ||||||
| priceInferrerFor :: Transaction -> PostingType -> (Posting -> Posting) | priceInferrerFor :: Transaction -> PostingType -> (Posting -> Posting) | ||||||
| priceInferrerFor t pt = maybe id inferprice $ inferFromAndTo sumamounts | priceInferrerFor t pt = maybe id inferprice inferFromAndTo | ||||||
|   where |   where | ||||||
|     postings     = filter ((==pt).ptype) $ tpostings t |     postings     = filter ((==pt).ptype) $ tpostings t | ||||||
|     pcommodities = map acommodity $ concatMap (amounts . pamount) postings |     pcommodities = map acommodity $ concatMap (amounts . pamount) postings | ||||||
|     sumamounts   = amounts $ sumPostings postings  -- amounts normalises to one amount per commodity & price |     sumamounts   = amounts $ sumPostings postings  -- amounts normalises to one amount per commodity & price | ||||||
|     noprices     = all (isNothing . aprice) sumamounts |  | ||||||
| 
 | 
 | ||||||
|     -- We can infer prices if there are no prices given, and exactly two commodities in the |     -- We can infer prices if there are no prices given, exactly two commodities in the normalised | ||||||
|     -- normalised sum of postings in this transaction. The amount we are converting from is |     -- sum of postings in this transaction, and these two have opposite signs. The amount we are | ||||||
|     -- the first commodity to appear in the ordered list of postings, and the commodity we |     -- converting from is the first commodity to appear in the ordered list of postings, and the | ||||||
|     -- are converting to is the other. If we cannot infer prices, return Nothing. |     -- commodity we are converting to is the other. If we cannot infer prices, return Nothing. | ||||||
|     inferFromAndTo [a,b] | noprices = asum $ map orderIfMatches pcommodities |     inferFromAndTo = case sumamounts of | ||||||
|       where orderIfMatches x | x == acommodity a = Just (a,b) |       [a,b] | noprices, oppositesigns -> asum $ map orderIfMatches pcommodities | ||||||
|                              | x == acommodity b = Just (b,a) |         where | ||||||
|                              | otherwise         = Nothing |           noprices      = all (isNothing . aprice) sumamounts | ||||||
|     inferFromAndTo _ = Nothing |           oppositesigns = signum (aquantity a) /= signum (aquantity b) | ||||||
|  |           orderIfMatches x | x == acommodity a = Just (a,b) | ||||||
|  |                            | x == acommodity b = Just (b,a) | ||||||
|  |                            | otherwise         = Nothing | ||||||
|  |       _ -> Nothing | ||||||
| 
 | 
 | ||||||
|     -- For each posting, if the posting type matches, there is only a single amount in the posting, |     -- For each posting, if the posting type matches, there is only a single amount in the posting, | ||||||
|     -- and the commodity of the amount matches the amount we're converting from, |     -- and the commodity of the amount matches the amount we're converting from, | ||||||
|     -- then set its price based on the ratio between fromamount and toamount. |     -- then set its price based on the ratio between fromamount and toamount. | ||||||
|     inferprice (fromamount, toamount) posting |     inferprice (fromamount, toamount) posting | ||||||
|         | [a] <- amounts (pamount posting), ptype posting == pt, acommodity a == acommodity fromamount |         | [a] <- amounts (pamount posting), ptype posting == pt, acommodity a == acommodity fromamount | ||||||
|         , let totalpricesign = if aquantity a < 0 then negate else id |             = posting{ pamount   = mixedAmount a{aprice=Just conversionprice} | ||||||
|             = posting{ pamount   = mixedAmount a{aprice=Just $ conversionprice totalpricesign} |  | ||||||
|                      , poriginal = Just $ originalPosting posting } |                      , poriginal = Just $ originalPosting posting } | ||||||
|         | otherwise = posting |         | otherwise = posting | ||||||
|       where |       where | ||||||
|         -- If only one Amount in the posting list matches fromamount we can use TotalPrice, |         -- If only one Amount in the posting list matches fromamount we can use TotalPrice. | ||||||
|         -- but we need to know the sign. Otherwise divide the conversion equally among the |         -- Otherwise divide the conversion equally among the Amounts by using a unit price. | ||||||
|         -- Amounts by using a unit price. |         conversionprice = case filter (== acommodity fromamount) pcommodities of | ||||||
|         conversionprice sign = case filter (== acommodity fromamount) pcommodities of |             [_] -> TotalPrice $ negate toamount  `withPrecision` NaturalPrecision | ||||||
|             [_] -> TotalPrice $ sign (abs toamount) `withPrecision` NaturalPrecision |             _   -> UnitPrice  $ negate unitprice `withPrecision` unitprecision | ||||||
|             _   -> UnitPrice  $ abs unitprice       `withPrecision` unitprecision |  | ||||||
| 
 | 
 | ||||||
|         unitprice     = (aquantity fromamount) `divideAmount` toamount |         unitprice     = aquantity fromamount `divideAmount` toamount | ||||||
|         unitprecision = case (asprecision $ astyle fromamount, asprecision $ astyle toamount) of |         unitprecision = case (asprecision $ astyle fromamount, asprecision $ astyle toamount) of | ||||||
|             (Precision a, Precision b) -> Precision . max 2 $ saturatedAdd a b |             (Precision a, Precision b) -> Precision . max 2 $ saturatedAdd a b | ||||||
|             _                          -> NaturalPrecision |             _                          -> NaturalPrecision | ||||||
|  | |||||||
| @ -127,7 +127,7 @@ hledger: "-" (lines 1-3) | |||||||
| could not balance this transaction: | could not balance this transaction: | ||||||
| real postings all have the same sign | real postings all have the same sign | ||||||
| 2020-01-01 | 2020-01-01 | ||||||
|     a        1A @@ 1B |     a              1A | ||||||
|     b              1B |     b              1B | ||||||
| 
 | 
 | ||||||
| >=1 | >=1 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user