begin refactoring balanceTransaction
This commit is contained in:
parent
4c6979c3fc
commit
ba18f4a25a
@ -277,34 +277,30 @@ isTransactionBalanced styles t =
|
||||
-- non-explicitly-priced amounts in different commodities (likewise
|
||||
-- for balanced virtual postings).
|
||||
balanceTransaction :: Maybe (Map.Map Commodity AmountStyle) -> Transaction -> Either String Transaction
|
||||
balanceTransaction styles t@Transaction{tpostings=ps}
|
||||
| length rwithoutamounts > 1 || length bvwithoutamounts > 1
|
||||
= Left $ printerr "could not balance this transaction (can't have more than one missing amount; remember to put 2 or more spaces before amounts)"
|
||||
| not $ isTransactionBalanced styles t''' = Left $ printerr $ nonzerobalanceerror t'''
|
||||
| otherwise = Right t''''
|
||||
where
|
||||
-- maybe infer missing amounts
|
||||
(rwithamounts, rwithoutamounts) = partition hasAmount $ realPostings t
|
||||
(bvwithamounts, bvwithoutamounts) = partition hasAmount $ balancedVirtualPostings t
|
||||
ramounts = map pamount rwithamounts
|
||||
bvamounts = map pamount bvwithamounts
|
||||
t' = t{tpostings=map inferamount ps}
|
||||
where
|
||||
inferamount p | not (hasAmount p) && isReal p = p{pamount = costOfMixedAmount (- sum ramounts)}
|
||||
| not (hasAmount p) && isBalancedVirtual p = p{pamount = costOfMixedAmount (- sum bvamounts)}
|
||||
| otherwise = p
|
||||
balanceTransaction styles t@Transaction{tpostings=ps} =
|
||||
case inferAmounts t of
|
||||
Left err -> Left err
|
||||
Right tWithAmounts ->
|
||||
case isTransactionBalanced styles tWithAmountsAndPrices of
|
||||
False -> Left $ printerr $ nonzerobalanceerror tWithAmountsAndPrices
|
||||
True -> Right $ txnTieKnot tWithAmountsAndPrices
|
||||
|
||||
-- maybe infer conversion prices, for real postings
|
||||
rmixedamountsinorder = map pamount $ realPostings t'
|
||||
where
|
||||
printerr s = intercalate "\n" [s, showTransactionUnelided t]
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- infer conversion prices for real postings if needed
|
||||
rmixedamountsinorder = map pamount $ realPostings tWithAmounts
|
||||
ramountsinorder = concatMap amounts rmixedamountsinorder
|
||||
rcommoditiesinorder = map acommodity ramountsinorder
|
||||
rsumamounts = amounts $ sum rmixedamountsinorder
|
||||
-- as it says above, we can infer a conversion price when
|
||||
t'' = if t'==t -- all real amounts were explicit (we didn't have to infer any)
|
||||
&& length rsumamounts == 2 -- and the sum of real amounts has exactly two commodities (assumption: summing mixed amounts normalises to one simple amount per commodity)
|
||||
&& all ((==NoPrice).aprice) rsumamounts -- and none of the amounts had explicit prices
|
||||
then t'{tpostings=map inferprice ps}
|
||||
else t'
|
||||
tWithAmountsAndRealPrices =
|
||||
if tWithAmounts == t -- all real amounts were explicit (we didn't have to infer any)
|
||||
&& length rsumamounts == 2 -- and the sum of real amounts has exactly two commodities (assumption: summing mixed amounts normalises to one simple amount per commodity)
|
||||
&& all ((==NoPrice).aprice) rsumamounts -- and none of the amounts had explicit prices
|
||||
then tWithAmounts{tpostings=map inferprice ps}
|
||||
else tWithAmounts
|
||||
where
|
||||
inferprice p@Posting{pamount=Mixed [a@Amount{acommodity=c,aprice=NoPrice}], ptype=RegularPosting} -- assumption: a posting's mixed amount contains one simple amount
|
||||
= p{pamount=Mixed [a{aprice=conversionprice c}]}
|
||||
@ -337,14 +333,16 @@ balanceTransaction styles t@Transaction{tpostings=ps}
|
||||
sumofprecisions = (asprecision $ astyle $ targetcommodityamount) + (asprecision $ astyle $ unpricedamount)
|
||||
inferprice p = p
|
||||
|
||||
-- maybe infer prices for balanced virtual postings. Duplicates the above. XXX
|
||||
bvmixedamountsinorder = map pamount $ balancedVirtualPostings t''
|
||||
-------------------------------------------------------------------------
|
||||
-- infer conversion prices for balanced virtual postings if needed. XXX duplicates the above
|
||||
bvmixedamountsinorder = map pamount $ balancedVirtualPostings tWithAmountsAndRealPrices
|
||||
bvamountsinorder = concatMap amounts bvmixedamountsinorder
|
||||
bvcommoditiesinorder = map acommodity bvamountsinorder
|
||||
bvsumamounts = amounts $ sum bvmixedamountsinorder
|
||||
t''' = if length bvsumamounts == 2 && all ((==NoPrice).aprice) bvsumamounts && t'==t -- XXX could check specifically for bv amount inferring
|
||||
then t''{tpostings=map inferprice ps}
|
||||
else t''
|
||||
tWithAmountsAndPrices =
|
||||
if length bvsumamounts == 2 && all ((==NoPrice).aprice) bvsumamounts && tWithAmounts==t -- XXX could check specifically for bv amount inferring
|
||||
then tWithAmountsAndRealPrices{tpostings=map inferprice ps}
|
||||
else tWithAmountsAndRealPrices
|
||||
where
|
||||
inferprice p@Posting{pamount=Mixed [a@Amount{acommodity=c,aprice=NoPrice}], ptype=BalancedVirtualPosting}
|
||||
= p{pamount=Mixed [a{aprice=conversionprice c}]}
|
||||
@ -363,10 +361,24 @@ balanceTransaction styles t@Transaction{tpostings=ps}
|
||||
sumofprecisions = (asprecision $ astyle $ targetcommodityamount) + (asprecision $ astyle $ unpricedamount)
|
||||
inferprice p = p
|
||||
|
||||
-- tie the knot so eg relatedPostings works right
|
||||
t'''' = txnTieKnot t'''
|
||||
|
||||
printerr s = intercalate "\n" [s, showTransactionUnelided t]
|
||||
-- | Infer up to one missing amount each for this transactions's real
|
||||
-- and balanced virtual postings, if needed, or return an error
|
||||
-- message if we can't.
|
||||
inferAmounts :: Transaction -> Either String Transaction
|
||||
inferAmounts t@Transaction{tpostings=ps}
|
||||
| length amountlessrealps > 1
|
||||
= Left $ printerr "could not balance this transaction - can't have more than one real posting with no amount (remember to put 2 or more spaces before amounts)"
|
||||
| length amountlessbvps > 1
|
||||
= Left $ printerr "could not balance this transaction - can't have more than one balanced virtual posting with no amount (remember to put 2 or more spaces before amounts)"
|
||||
| otherwise
|
||||
= Right t{tpostings=map inferamount ps}
|
||||
where
|
||||
printerr s = intercalate "\n" [s, showTransactionUnelided t]
|
||||
((amountfulrealps, amountlessrealps), realsum) = (partition hasAmount (realPostings t), sum $ map pamount amountfulrealps)
|
||||
((amountfulbvps, amountlessbvps), bvsum) = (partition hasAmount (balancedVirtualPostings t), sum $ map pamount amountfulbvps)
|
||||
inferamount p@Posting{ptype=RegularPosting} | not (hasAmount p) = p{pamount=costOfMixedAmount (-realsum)}
|
||||
inferamount p@Posting{ptype=BalancedVirtualPosting} | not (hasAmount p) = p{pamount=costOfMixedAmount (-bvsum)}
|
||||
inferamount p = p
|
||||
|
||||
nonzerobalanceerror :: Transaction -> String
|
||||
nonzerobalanceerror t = printf "could not balance this transaction (%s%s%s)" rmsg sep bvmsg
|
||||
@ -382,7 +394,8 @@ nonzerobalanceerror t = printf "could not balance this transaction (%s%s%s)" rms
|
||||
transactionDate2 :: Transaction -> Day
|
||||
transactionDate2 t = fromMaybe (tdate t) $ tdate2 t
|
||||
|
||||
-- | Ensure a transaction's postings refer back to it.
|
||||
-- | Ensure a transaction's postings refer back to it, so that eg
|
||||
-- relatedPostings works right.
|
||||
txnTieKnot :: Transaction -> Transaction
|
||||
txnTieKnot t@Transaction{tpostings=ps} = t{tpostings=map (settxn t) ps}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user