dev: Amount: aprice -> acost

Acked-by: Simon Michael <simon@joyful.com>
This commit is contained in:
Simon Michael 2024-01-23 07:50:11 -10:00
parent 8102bd9c2b
commit 323f87b3e9
12 changed files with 54 additions and 54 deletions

View File

@ -206,7 +206,7 @@ main = do
-- the final balance portion to move -- the final balance portion to move
finalamt = dbgamt "final amt to move" $ finalamt = dbgamt "final amt to move" $
(balincommsinglecost + stilltomovenext) (balincommsinglecost + stilltomovenext)
{aprice=aprice balincommsinglecost} -- + discards cost, need to restore it {acost=acost balincommsinglecost} -- + discards cost, need to restore it
in (0, reverse $ (acct, mixed [finalamt]) : balscollected) in (0, reverse $ (acct, mixed [finalamt]) : balscollected)
where where
-- how much of the requested commodity is in this account -- how much of the requested commodity is in this account
@ -228,7 +228,7 @@ main = do
fromps = [ fromps = [
posting{paccount = a posting{paccount = a
,pamount = mixedAmount $ negate b ,pamount = mixedAmount $ negate b
-- ,pbalanceassertion = Just nullassertion{baamount=precise b{aquantity=0, aprice=Nothing}} -- ,pbalanceassertion = Just nullassertion{baamount=precise b{aquantity=0, acost=Nothing}}
} }
| -- get the balances for each commodity and transaction price | -- get the balances for each commodity and transaction price

View File

@ -279,7 +279,7 @@ instance Num Amount where
-- | The empty simple amount - a zero with no commodity symbol or cost -- | The empty simple amount - a zero with no commodity symbol or cost
-- and the default amount display style. -- and the default amount display style.
nullamt :: Amount nullamt :: Amount
nullamt = Amount{acommodity="", aquantity=0, aprice=Nothing, astyle=amountstyle} nullamt = Amount{acommodity="", aquantity=0, acost=Nothing, astyle=amountstyle}
-- | A special amount used as a marker, meaning -- | A special amount used as a marker, meaning
-- "no explicit amount provided here, infer it when needed". -- "no explicit amount provided here, infer it when needed".
@ -296,8 +296,8 @@ usd n = nullamt{acommodity="$", aquantity=roundTo 2 n, astyle=amountstyle{asprec
eur n = nullamt{acommodity="", aquantity=roundTo 2 n, astyle=amountstyle{asprecision=Precision 2}} eur n = nullamt{acommodity="", aquantity=roundTo 2 n, astyle=amountstyle{asprecision=Precision 2}}
gbp n = nullamt{acommodity="£", aquantity=roundTo 2 n, astyle=amountstyle{asprecision=Precision 2}} gbp n = nullamt{acommodity="£", aquantity=roundTo 2 n, astyle=amountstyle{asprecision=Precision 2}}
per n = nullamt{acommodity="%", aquantity=n, astyle=amountstyle{asprecision=Precision 1, ascommodityside=R, ascommodityspaced=True}} per n = nullamt{acommodity="%", aquantity=n, astyle=amountstyle{asprecision=Precision 1, ascommodityside=R, ascommodityspaced=True}}
amt `at` costamt = amt{aprice=Just $ UnitCost costamt} amt `at` costamt = amt{acost=Just $ UnitCost costamt}
amt @@ costamt = amt{aprice=Just $ TotalCost costamt} amt @@ costamt = amt{acost=Just $ TotalCost costamt}
-- | Apply a binary arithmetic operator to two amounts, which should -- | Apply a binary arithmetic operator to two amounts, which should
-- be in the same commodity if non-zero (warning, this is not checked). -- be in the same commodity if non-zero (warning, this is not checked).
@ -317,7 +317,7 @@ similarAmountsOp op Amount{acommodity=_, aquantity=q1, astyle=AmountStyle{aspre
-- | Convert an amount to the specified commodity, ignoring and discarding -- | Convert an amount to the specified commodity, ignoring and discarding
-- any costs and assuming an exchange rate of 1. -- any costs and assuming an exchange rate of 1.
amountWithCommodity :: CommoditySymbol -> Amount -> Amount amountWithCommodity :: CommoditySymbol -> Amount -> Amount
amountWithCommodity c a = a{acommodity=c, aprice=Nothing} amountWithCommodity c a = a{acommodity=c, acost=Nothing}
-- | Convert a amount to its "cost" or "selling price" in another commodity, -- | Convert a amount to its "cost" or "selling price" in another commodity,
-- using its attached cost if it has one. Notes: -- using its attached cost if it has one. Notes:
@ -328,7 +328,7 @@ amountWithCommodity c a = a{acommodity=c, aprice=Nothing}
-- - cost amounts should be positive in the Journal -- - cost amounts should be positive in the Journal
-- (though this is currently not enforced) -- (though this is currently not enforced)
amountCost :: Amount -> Amount amountCost :: Amount -> Amount
amountCost a@Amount{aquantity=q, aprice=mp} = amountCost a@Amount{aquantity=q, acost=mp} =
case mp of case mp of
Nothing -> a Nothing -> a
Just (UnitCost p@Amount{aquantity=pq}) -> p{aquantity=pq * q} Just (UnitCost p@Amount{aquantity=pq}) -> p{aquantity=pq * q}
@ -336,11 +336,11 @@ amountCost a@Amount{aquantity=q, aprice=mp} =
-- | Strip all costs from an Amount -- | Strip all costs from an Amount
amountStripCost :: Amount -> Amount amountStripCost :: Amount -> Amount
amountStripCost a = a{aprice=Nothing} amountStripCost a = a{acost=Nothing}
-- | Apply a function to an amount's quantity (and its total cost, if it has one). -- | Apply a function to an amount's quantity (and its total cost, if it has one).
transformAmount :: (Quantity -> Quantity) -> Amount -> Amount transformAmount :: (Quantity -> Quantity) -> Amount -> Amount
transformAmount f a@Amount{aquantity=q,aprice=p} = a{aquantity=f q, aprice=f' <$> p} transformAmount f a@Amount{aquantity=q,acost=p} = a{aquantity=f q, acost=f' <$> p}
where where
f' (TotalCost a1@Amount{aquantity=pq}) = TotalCost a1{aquantity = f pq} f' (TotalCost a1@Amount{aquantity=pq}) = TotalCost a1{aquantity = f pq}
f' p' = p' f' p' = p'
@ -371,7 +371,7 @@ amountRoundedQuantity Amount{aquantity=q, astyle=AmountStyle{asprecision=mp}} =
-- | Apply a test to both an Amount and its total cost, if it has one. -- | Apply a test to both an Amount and its total cost, if it has one.
testAmountAndTotalCost :: (Amount -> Bool) -> Amount -> Bool testAmountAndTotalCost :: (Amount -> Bool) -> Amount -> Bool
testAmountAndTotalCost f amt = case aprice amt of testAmountAndTotalCost f amt = case acost amt of
Just (TotalCost cost) -> f amt && f cost Just (TotalCost cost) -> f amt && f cost
_ -> f amt _ -> f amt
@ -514,8 +514,8 @@ instance HasAmounts Amount where
-- except that costs' precision is never changed (costs are often recorded inexactly, -- except that costs' precision is never changed (costs are often recorded inexactly,
-- so we don't want to imply greater precision than they were recorded with). -- so we don't want to imply greater precision than they were recorded with).
-- If no style is found for an amount, it is left unchanged. -- If no style is found for an amount, it is left unchanged.
styleAmounts styles a@Amount{aquantity=qty, acommodity=comm, astyle=oldstyle, aprice=mcost0} = styleAmounts styles a@Amount{aquantity=qty, acommodity=comm, astyle=oldstyle, acost=mcost0} =
a{astyle=newstyle, aprice=mcost1} a{astyle=newstyle, acost=mcost1}
where where
newstyle = mknewstyle False qty oldstyle comm newstyle = mknewstyle False qty oldstyle comm
@ -617,7 +617,7 @@ withDecimalPoint = flip setAmountDecimalPoint
-- Amount rendering -- Amount rendering
showAmountCostB :: Amount -> WideBuilder showAmountCostB :: Amount -> WideBuilder
showAmountCostB amt = case aprice amt of showAmountCostB amt = case acost amt of
Nothing -> mempty Nothing -> mempty
Just (UnitCost pa) -> WideBuilder (TB.fromString " @ ") 3 <> showAmountB noColour{displayZeroCommodity=True} pa Just (UnitCost pa) -> WideBuilder (TB.fromString " @ ") 3 <> showAmountB noColour{displayZeroCommodity=True} pa
Just (TotalCost pa) -> WideBuilder (TB.fromString " @@ ") 4 <> showAmountB noColour{displayZeroCommodity=True} (sign pa) Just (TotalCost pa) -> WideBuilder (TB.fromString " @@ ") 4 <> showAmountB noColour{displayZeroCommodity=True} (sign pa)
@ -693,7 +693,7 @@ showAmountDebug :: Amount -> String
showAmountDebug Amount{acommodity="AUTO"} = "(missing)" showAmountDebug Amount{acommodity="AUTO"} = "(missing)"
showAmountDebug Amount{..} = showAmountDebug Amount{..} =
"Amount {acommodity=" ++ show acommodity ++ ", aquantity=" ++ show aquantity "Amount {acommodity=" ++ show acommodity ++ ", aquantity=" ++ show aquantity
++ ", aprice=" ++ showAmountCostDebug aprice ++ ", astyle=" ++ show astyle ++ "}" ++ ", acost=" ++ showAmountCostDebug acost ++ ", astyle=" ++ show astyle ++ "}"
-- | Get a Text Builder for the string representation of the number part of of an amount, -- | Get a Text Builder for the string representation of the number part of of an amount,
-- using the display settings from its commodity. Also returns the width of the number. -- using the display settings from its commodity. Also returns the width of the number.
@ -758,7 +758,7 @@ instance Num MixedAmount where
-- | Calculate the key used to store an Amount within a MixedAmount. -- | Calculate the key used to store an Amount within a MixedAmount.
amountKey :: Amount -> MixedAmountKey amountKey :: Amount -> MixedAmountKey
amountKey amt@Amount{acommodity=c} = case aprice amt of amountKey amt@Amount{acommodity=c} = case acost amt of
Nothing -> MixedAmountKeyNoCost c Nothing -> MixedAmountKeyNoCost c
Just (TotalCost p) -> MixedAmountKeyTotalCost c (acommodity p) Just (TotalCost p) -> MixedAmountKeyTotalCost c (acommodity p)
Just (UnitCost p) -> MixedAmountKeyUnitCost c (acommodity p) (aquantity p) Just (UnitCost p) -> MixedAmountKeyUnitCost c (acommodity p) (aquantity p)
@ -942,12 +942,12 @@ unifyMixedAmount = foldM combine 0 . amounts
-- cost to the result and discarding any other costs. Only used as a -- cost to the result and discarding any other costs. Only used as a
-- rendering helper. -- rendering helper.
sumSimilarAmountsUsingFirstPrice :: Amount -> Amount -> Amount sumSimilarAmountsUsingFirstPrice :: Amount -> Amount -> Amount
sumSimilarAmountsUsingFirstPrice a b = (a + b){aprice=p} sumSimilarAmountsUsingFirstPrice a b = (a + b){acost=p}
where where
p = case (aprice a, aprice b) of p = case (acost a, acost b) of
(Just (TotalCost ap), Just (TotalCost bp)) (Just (TotalCost ap), Just (TotalCost bp))
-> Just . TotalCost $ ap{aquantity = aquantity ap + aquantity bp } -> Just . TotalCost $ ap{aquantity = aquantity ap + aquantity bp }
_ -> aprice a _ -> acost a
-- -- | Sum same-commodity amounts. If there were different costs, set -- -- | Sum same-commodity amounts. If there were different costs, set
-- -- the cost to a special marker indicating "various". Only used as a -- -- the cost to a special marker indicating "various". Only used as a
@ -985,7 +985,7 @@ mapMixedAmountUnsafe f (Mixed ma) = Mixed $ M.map f ma -- Use M.map instead of
mixedAmountCost :: MixedAmount -> MixedAmount mixedAmountCost :: MixedAmount -> MixedAmount
mixedAmountCost (Mixed ma) = mixedAmountCost (Mixed ma) =
foldl' (\m a -> maAddAmount m (amountCost a)) (Mixed noCosts) withCosts foldl' (\m a -> maAddAmount m (amountCost a)) (Mixed noCosts) withCosts
where (noCosts, withCosts) = M.partition (isNothing . aprice) ma where (noCosts, withCosts) = M.partition (isNothing . acost) ma
-- -- | MixedAmount derived Eq instance in Types.hs doesn't know that we -- -- | MixedAmount derived Eq instance in Types.hs doesn't know that we
-- -- want $0 = EUR0 = 0. Yet we don't want to drag all this code over there. -- -- want $0 = EUR0 = 0. Yet we don't want to drag all this code over there.
@ -1227,8 +1227,8 @@ mixedAmountSetPrecisionMax p = mapMixedAmountUnsafe (amountSetPrecisionMax p)
-- | Remove all costs from a MixedAmount. -- | Remove all costs from a MixedAmount.
mixedAmountStripPrices :: MixedAmount -> MixedAmount mixedAmountStripPrices :: MixedAmount -> MixedAmount
mixedAmountStripPrices (Mixed ma) = mixedAmountStripPrices (Mixed ma) =
foldl' (\m a -> maAddAmount m a{aprice=Nothing}) (Mixed noPrices) withPrices foldl' (\m a -> maAddAmount m a{acost=Nothing}) (Mixed noPrices) withPrices
where (noPrices, withPrices) = M.partition (isNothing . aprice) ma where (noPrices, withPrices) = M.partition (isNothing . acost) ma
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -1239,9 +1239,9 @@ tests_Amount = testGroup "Amount" [
testCase "amountCost" $ do testCase "amountCost" $ do
amountCost (eur 1) @?= eur 1 amountCost (eur 1) @?= eur 1
amountCost (eur 2){aprice=Just $ UnitCost $ usd 2} @?= usd 4 amountCost (eur 2){acost=Just $ UnitCost $ usd 2} @?= usd 4
amountCost (eur 1){aprice=Just $ TotalCost $ usd 2} @?= usd 2 amountCost (eur 1){acost=Just $ TotalCost $ usd 2} @?= usd 2
amountCost (eur (-1)){aprice=Just $ TotalCost $ usd (-2)} @?= usd (-2) amountCost (eur (-1)){acost=Just $ TotalCost $ usd (-2)} @?= usd (-2)
,testCase "amountLooksZero" $ do ,testCase "amountLooksZero" $ do
assertBool "" $ amountLooksZero nullamt assertBool "" $ amountLooksZero nullamt
@ -1249,7 +1249,7 @@ tests_Amount = testGroup "Amount" [
,testCase "negating amounts" $ do ,testCase "negating amounts" $ do
negate (usd 1) @?= (usd 1){aquantity= -1} negate (usd 1) @?= (usd 1){aquantity= -1}
let b = (usd 1){aprice=Just $ UnitCost $ eur 2} in negate b @?= b{aquantity= -1} let b = (usd 1){acost=Just $ UnitCost $ eur 2} in negate b @?= b{aquantity= -1}
,testCase "adding amounts without costs" $ do ,testCase "adding amounts without costs" $ do
(usd 1.23 + usd (-1.23)) @?= usd 0 (usd 1.23 + usd (-1.23)) @?= usd 0

View File

@ -340,7 +340,7 @@ costInferrerFor t pt = maybe id infercost inferFromAndTo
inferFromAndTo = case sumamounts of inferFromAndTo = case sumamounts of
[a,b] | noprices, oppositesigns -> asum $ map orderIfMatches pcommodities [a,b] | noprices, oppositesigns -> asum $ map orderIfMatches pcommodities
where where
noprices = all (isNothing . aprice) sumamounts noprices = all (isNothing . acost) sumamounts
oppositesigns = signum (aquantity a) /= signum (aquantity b) oppositesigns = signum (aquantity a) /= signum (aquantity b)
orderIfMatches x | x == acommodity a = Just (a,b) orderIfMatches x | x == acommodity a = Just (a,b)
| x == acommodity b = Just (b,a) | x == acommodity b = Just (b,a)
@ -352,7 +352,7 @@ costInferrerFor t pt = maybe id infercost inferFromAndTo
-- then set its cost based on the ratio between fromamount and toamount. -- then set its cost based on the ratio between fromamount and toamount.
infercost (fromamount, toamount) p infercost (fromamount, toamount) p
| [a] <- amounts (pamount p), ptype p == pt, acommodity a == acommodity fromamount | [a] <- amounts (pamount p), ptype p == pt, acommodity a == acommodity fromamount
= p{ pamount = mixedAmount a{aprice=Just conversionprice} = p{ pamount = mixedAmount a{acost=Just conversionprice}
& dbg9With (lbl "inferred cost".showMixedAmountOneLine) & dbg9With (lbl "inferred cost".showMixedAmountOneLine)
, poriginal = Just $ originalPosting p } , poriginal = Just $ originalPosting p }
| otherwise = p | otherwise = p

View File

@ -970,7 +970,7 @@ journalMarkRedundantCosts j = do
-- -- | Get this amount's commodity and any commodities referenced in its price. -- -- | Get this amount's commodity and any commodities referenced in its price.
-- amountCommodities :: Amount -> [CommoditySymbol] -- amountCommodities :: Amount -> [CommoditySymbol]
-- amountCommodities Amount{acommodity=c,aprice=p} = -- amountCommodities Amount{acommodity=c,acost=p} =
-- case p of Nothing -> [c] -- case p of Nothing -> [c]
-- Just (UnitCost ma) -> c:(concatMap amountCommodities $ amounts ma) -- Just (UnitCost ma) -> c:(concatMap amountCommodities $ amounts ma)
-- Just (TotalCost ma) -> c:(concatMap amountCommodities $ amounts ma) -- Just (TotalCost ma) -> c:(concatMap amountCommodities $ amounts ma)
@ -983,7 +983,7 @@ journalMarkRedundantCosts j = do
-- * posting amounts in transactions (in parse order) -- * posting amounts in transactions (in parse order)
-- * the amount in the final default commodity (D) directive -- * the amount in the final default commodity (D) directive
-- --
-- Transaction price amounts (posting amounts' aprice field) are not included. -- Transaction price amounts (posting amounts' acost field) are not included.
-- --
journalStyleInfluencingAmounts :: Journal -> [Amount] journalStyleInfluencingAmounts :: Journal -> [Amount]
journalStyleInfluencingAmounts j = journalStyleInfluencingAmounts j =
@ -1023,7 +1023,7 @@ journalStyleInfluencingAmounts j =
-- * posting amounts in transactions (in parse order) -- * posting amounts in transactions (in parse order)
-- --
-- Transaction price amounts, which may be embedded in posting amounts -- Transaction price amounts, which may be embedded in posting amounts
-- (the aprice field), are left intact but not traversed/processed. -- (the acost field), are left intact but not traversed/processed.
-- --
-- traverseJournalAmounts :: Applicative f => (Amount -> f Amount) -> Journal -> f Journal -- traverseJournalAmounts :: Applicative f => (Amount -> f Amount) -> Journal -> f Journal
-- traverseJournalAmounts f j = -- traverseJournalAmounts f j =

View File

@ -386,7 +386,7 @@ type BeancountAmount = Amount
-- in a way that Beancount can read: forces the commodity symbol to the right, -- in a way that Beancount can read: forces the commodity symbol to the right,
-- converts a few currency symbols to names, capitalises all letters. -- converts a few currency symbols to names, capitalises all letters.
amountToBeancount :: Amount -> BeancountAmount amountToBeancount :: Amount -> BeancountAmount
amountToBeancount a@Amount{acommodity=c,astyle=s,aprice=mp} = a{acommodity=c', astyle=s', aprice=mp'} amountToBeancount a@Amount{acommodity=c,astyle=s,acost=mp} = a{acommodity=c', astyle=s', acost=mp'}
-- https://beancount.github.io/docs/beancount_language_syntax.html#commodities-currencies -- https://beancount.github.io/docs/beancount_language_syntax.html#commodities-currencies
where where
c' = T.toUpper $ c' = T.toUpper $
@ -543,7 +543,7 @@ postingToCost ToCost p
| "_conversion-matched" `elem` map fst (ptags p) && nocosts = Nothing | "_conversion-matched" `elem` map fst (ptags p) && nocosts = Nothing
| otherwise = Just $ postingTransformAmount mixedAmountCost p | otherwise = Just $ postingTransformAmount mixedAmountCost p
where where
nocosts = (not . any (isJust . aprice) . amountsRaw) $ pamount p nocosts = (not . any (isJust . acost) . amountsRaw) $ pamount p
-- | Generate inferred equity postings from a 'Posting' using transaction prices. -- | Generate inferred equity postings from a 'Posting' using transaction prices.
-- Make sure not to generate equity postings when there are already matched -- Make sure not to generate equity postings when there are already matched
@ -556,7 +556,7 @@ postingAddInferredEquityPostings verbosetags equityAcct p
taggedPosting taggedPosting
| null priceAmounts = p | null priceAmounts = p
| otherwise = p{ ptags = ("_price-matched","") : ptags p } | otherwise = p{ ptags = ("_price-matched","") : ptags p }
conversionPostings amt = case aprice amt of conversionPostings amt = case acost amt of
Nothing -> [] Nothing -> []
Just _ -> [ cp{ paccount = accountPrefix <> amtCommodity Just _ -> [ cp{ paccount = accountPrefix <> amtCommodity
, pamount = mixedAmount . negate $ amountStripCost amt , pamount = mixedAmount . negate $ amountStripCost amt
@ -581,7 +581,7 @@ postingAddInferredEquityPostings verbosetags equityAcct p
-- Take the commodity of an amount and collapse consecutive spaces to a single space -- Take the commodity of an amount and collapse consecutive spaces to a single space
commodity = T.unwords . filter (not . T.null) . T.words . acommodity commodity = T.unwords . filter (not . T.null) . T.words . acommodity
priceAmounts = filter (isJust . aprice) . amountsRaw $ pamount p priceAmounts = filter (isJust . acost) . amountsRaw $ pamount p
-- | Make a market price equivalent to this posting's amount's unit -- | Make a market price equivalent to this posting's amount's unit
-- price, if any. -- price, if any.

View File

@ -386,7 +386,7 @@ transactionInferCostsFromEquity dryrun acctTypes t = first (annotateErrorWithTra
-- with the matching amount which must be present in another non-conversion posting. -- with the matching amount which must be present in another non-conversion posting.
costfulPostingIfMatchesBothAmounts :: Amount -> Amount -> Posting -> Maybe Posting costfulPostingIfMatchesBothAmounts :: Amount -> Amount -> Posting -> Maybe Posting
costfulPostingIfMatchesBothAmounts a1 a2 costfulp = do costfulPostingIfMatchesBothAmounts a1 a2 costfulp = do
a@Amount{aprice=Just _} <- postingSingleAmount costfulp a@Amount{acost=Just _} <- postingSingleAmount costfulp
if if
| dbgamtmatch 1 a1 a (amountsMatch (-a1) a) && dbgcostmatch 2 a2 a (amountsMatch a2 (amountCost a)) -> Just costfulp | dbgamtmatch 1 a1 a (amountsMatch (-a1) a) && dbgcostmatch 2 a2 a (amountsMatch a2 (amountCost a)) -> Just costfulp
| dbgamtmatch 2 a2 a (amountsMatch (-a2) a) && dbgcostmatch 1 a1 a (amountsMatch a1 (amountCost a)) -> Just costfulp | dbgamtmatch 2 a2 a (amountsMatch (-a2) a) && dbgcostmatch 1 a1 a (amountsMatch a1 (amountCost a)) -> Just costfulp
@ -400,7 +400,7 @@ transactionInferCostsFromEquity dryrun acctTypes t = first (annotateErrorWithTra
addCostIfMatchesOneAmount :: Amount -> Amount -> Posting -> Maybe (Posting, Amount) addCostIfMatchesOneAmount :: Amount -> Amount -> Posting -> Maybe (Posting, Amount)
addCostIfMatchesOneAmount a1 a2 p = do addCostIfMatchesOneAmount a1 a2 p = do
a <- postingSingleAmount p a <- postingSingleAmount p
let newp cost = p{pamount = mixedAmount a{aprice = Just $ TotalCost cost}} let newp cost = p{pamount = mixedAmount a{acost = Just $ TotalCost cost}}
if if
| amountsMatch (-a1) a -> Just (newp a2, a2) | amountsMatch (-a1) a -> Just (newp a2, a2)
| amountsMatch (-a2) a -> Just (newp a1, a1) | amountsMatch (-a2) a -> Just (newp a1, a1)
@ -408,8 +408,8 @@ transactionInferCostsFromEquity dryrun acctTypes t = first (annotateErrorWithTra
-- Get the single-commodity costless amount from a conversion posting, or raise an error. -- Get the single-commodity costless amount from a conversion posting, or raise an error.
conversionPostingAmountNoCost p = case postingSingleAmount p of conversionPostingAmountNoCost p = case postingSingleAmount p of
Just a@Amount{aprice=Nothing} -> Right a Just a@Amount{acost=Nothing} -> Right a
Just Amount{aprice=Just _} -> Left $ annotateWithPostings [p] "Conversion postings must not have a cost:" Just Amount{acost=Just _} -> Left $ annotateWithPostings [p] "Conversion postings must not have a cost:"
Nothing -> Left $ annotateWithPostings [p] "Conversion postings must have a single-commodity amount:" Nothing -> Left $ annotateWithPostings [p] "Conversion postings must have a single-commodity amount:"
-- Do these amounts look the same when compared at the first's display precision ? -- Do these amounts look the same when compared at the first's display precision ?
@ -456,7 +456,7 @@ partitionAndCheckConversionPostings check acctTypes =
| check = Left "Conversion postings must occur in adjacent pairs" | check = Left "Conversion postings must occur in adjacent pairs"
| otherwise = Right ((cs, (ps, np:os)), Nothing) | otherwise = Right ((cs, (ps, np:os)), Nothing)
isConversion p = M.lookup (paccount p) acctTypes == Just Conversion isConversion p = M.lookup (paccount p) acctTypes == Just Conversion
hasCost p = isJust $ aprice =<< postingSingleAmount p hasCost p = isJust $ acost =<< postingSingleAmount p
-- | Get a posting's amount if it is single-commodity. -- | Get a posting's amount if it is single-commodity.
postingSingleAmount :: Posting -> Maybe Amount postingSingleAmount :: Posting -> Maybe Amount

View File

@ -140,7 +140,7 @@ tmPostingRuleToFunction verbosetags styles query querytxt tmpr =
-- TODO multipliers with commodity symbols are not yet a documented feature. -- TODO multipliers with commodity symbols are not yet a documented feature.
-- For now: in addition to multiplying the quantity, it also replaces the -- For now: in addition to multiplying the quantity, it also replaces the
-- matched amount's commodity, display style, and price with those of the posting rule. -- matched amount's commodity, display style, and price with those of the posting rule.
c -> mapMixedAmount (\a -> a{acommodity = c, astyle = astyle pramount, aprice = aprice pramount}) as c -> mapMixedAmount (\a -> a{acommodity = c, astyle = astyle pramount, acost = acost pramount}) as
postingRuleMultiplier :: TMPostingRule -> Maybe Quantity postingRuleMultiplier :: TMPostingRule -> Maybe Quantity
postingRuleMultiplier tmpr = case amountsRaw . pamount $ tmprPosting tmpr of postingRuleMultiplier tmpr = case amountsRaw . pamount $ tmprPosting tmpr of

View File

@ -312,7 +312,7 @@ data Amount = Amount {
acommodity :: !CommoditySymbol, -- commodity symbol, or special value "AUTO" acommodity :: !CommoditySymbol, -- commodity symbol, or special value "AUTO"
aquantity :: !Quantity, -- numeric quantity, or zero in case of "AUTO" aquantity :: !Quantity, -- numeric quantity, or zero in case of "AUTO"
astyle :: !AmountStyle, astyle :: !AmountStyle,
aprice :: !(Maybe AmountCost) -- ^ the (fixed, transaction-specific) cost in another commodity of this amount, if any acost :: !(Maybe AmountCost) -- ^ the (fixed, transaction-specific) cost in another commodity of this amount, if any
} deriving (Eq,Ord,Generic,Show) } deriving (Eq,Ord,Generic,Show)
-- | Types with this class have one or more amounts, -- | Types with this class have one or more amounts,
@ -351,7 +351,7 @@ maCompare (Mixed a) (Mixed b) = go (M.toList a) (M.toList b)
go [] ((_,y):ys) = compareQuantities Nothing (Just y) <> go [] ys go [] ((_,y):ys) = compareQuantities Nothing (Just y) <> go [] ys
go [] [] = EQ go [] [] = EQ
compareQuantities = comparing (maybe 0 aquantity) <> comparing (maybe 0 totalcost) compareQuantities = comparing (maybe 0 aquantity) <> comparing (maybe 0 totalcost)
totalcost x = case aprice x of totalcost x = case acost x of
Just (TotalCost p) -> aquantity p Just (TotalCost p) -> aquantity p
_ -> 0 _ -> 0

View File

@ -112,7 +112,7 @@ priceDirectiveToMarketPrice PriceDirective{..} =
-- The price's display precision will be set to show all significant -- The price's display precision will be set to show all significant
-- decimal digits; or if they seem to be infinite, defaultPrecisionLimit. -- decimal digits; or if they seem to be infinite, defaultPrecisionLimit.
amountPriceDirectiveFromCost :: Day -> Amount -> Maybe PriceDirective amountPriceDirectiveFromCost :: Day -> Amount -> Maybe PriceDirective
amountPriceDirectiveFromCost d amt@Amount{acommodity=fromcomm, aquantity=n} = case aprice amt of amountPriceDirectiveFromCost d amt@Amount{acommodity=fromcomm, aquantity=n} = case acost amt of
Just (UnitCost u) -> Just $ pd{pdamount=u} Just (UnitCost u) -> Just $ pd{pdamount=u}
Just (TotalCost t) | n /= 0 -> Just $ pd{pdamount=u} Just (TotalCost t) | n /= 0 -> Just $ pd{pdamount=u}
where u = amountSetFullPrecisionOr Nothing $ divideAmount n t where u = amountSetFullPrecisionOr Nothing $ divideAmount n t

View File

@ -774,7 +774,7 @@ amountp' mult =
<*> toPermutationWithDefault Nothing (Just <$> lotcostp <* spaces) <*> toPermutationWithDefault Nothing (Just <$> lotcostp <* spaces)
<*> toPermutationWithDefault Nothing (Just <$> lotdatep <* spaces) <*> toPermutationWithDefault Nothing (Just <$> lotdatep <* spaces)
<*> toPermutationWithDefault Nothing (Just <$> lotnotep <* spaces) <*> toPermutationWithDefault Nothing (Just <$> lotnotep <* spaces)
pure $ amt { aprice = mcost } pure $ amt { acost = mcost }
-- An amount with optional cost, but no cost basis. -- An amount with optional cost, but no cost basis.
amountnobasisp :: JournalParser m Amount amountnobasisp :: JournalParser m Amount
@ -785,7 +785,7 @@ amountnobasisp =
amt <- simpleamountp False amt <- simpleamountp False
spaces spaces
mprice <- optional $ costp amt <* spaces mprice <- optional $ costp amt <* spaces
pure $ amt { aprice = mprice } pure $ amt { acost = mprice }
-- An amount with no cost or cost basis. -- An amount with no cost or cost basis.
-- A flag indicates whether we are parsing a multiplier amount; -- A flag indicates whether we are parsing a multiplier amount;
@ -815,7 +815,7 @@ simpleamountp mult =
let numRegion = (offBeforeNum, offAfterNum) let numRegion = (offBeforeNum, offAfterNum)
(q,prec,mdec,mgrps) <- lift $ interpretNumber numRegion suggestedStyle ambiguousRawNum mExponent (q,prec,mdec,mgrps) <- lift $ interpretNumber numRegion suggestedStyle ambiguousRawNum mExponent
let s = amountstyle{ascommodityside=L, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalmark=mdec, asdigitgroups=mgrps} let s = amountstyle{ascommodityside=L, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalmark=mdec, asdigitgroups=mgrps}
return nullamt{acommodity=c, aquantity=sign (sign2 q), astyle=s, aprice=Nothing} return nullamt{acommodity=c, aquantity=sign (sign2 q), astyle=s, acost=Nothing}
-- An amount with commodity symbol on the right or no commodity symbol. -- An amount with commodity symbol on the right or no commodity symbol.
-- A no-symbol amount will have the default commodity applied to it -- A no-symbol amount will have the default commodity applied to it
@ -837,7 +837,7 @@ simpleamountp mult =
let msuggestedStyle = mdecmarkStyle <|> mcommodityStyle let msuggestedStyle = mdecmarkStyle <|> mcommodityStyle
(q,prec,mdec,mgrps) <- lift $ interpretNumber numRegion msuggestedStyle ambiguousRawNum mExponent (q,prec,mdec,mgrps) <- lift $ interpretNumber numRegion msuggestedStyle ambiguousRawNum mExponent
let s = amountstyle{ascommodityside=R, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalmark=mdec, asdigitgroups=mgrps} let s = amountstyle{ascommodityside=R, ascommodityspaced=commodityspaced, asprecision=prec, asdecimalmark=mdec, asdigitgroups=mgrps}
return nullamt{acommodity=c, aquantity=sign q, astyle=s, aprice=Nothing} return nullamt{acommodity=c, aquantity=sign q, astyle=s, acost=Nothing}
-- no symbol amount -- no symbol amount
Nothing -> do Nothing -> do
-- look for a number style to use when parsing, based on -- look for a number style to use when parsing, based on
@ -854,7 +854,7 @@ simpleamountp mult =
let (c,s) = case (mult, defcs) of let (c,s) = case (mult, defcs) of
(False, Just (defc,defs)) -> (defc, defs{asprecision=max (asprecision defs) prec}) (False, Just (defc,defs)) -> (defc, defs{asprecision=max (asprecision defs) prec})
_ -> ("", amountstyle{asprecision=prec, asdecimalmark=mdec, asdigitgroups=mgrps}) _ -> ("", amountstyle{asprecision=prec, asdecimalmark=mdec, asdigitgroups=mgrps})
return nullamt{acommodity=c, aquantity=sign q, astyle=s, aprice=Nothing} return nullamt{acommodity=c, aquantity=sign q, astyle=s, acost=Nothing}
-- For reducing code duplication. Doesn't parse anything. Has the type -- For reducing code duplication. Doesn't parse anything. Has the type
-- of a parser only in order to throw parse errors (for convenience). -- of a parser only in order to throw parse errors (for convenience).
@ -1586,7 +1586,7 @@ tests_Common = testGroup "Common" [
acommodity="$" acommodity="$"
,aquantity=10 -- need to test internal precision with roundTo ? I think not ,aquantity=10 -- need to test internal precision with roundTo ? I think not
,astyle=amountstyle{asprecision=Precision 0, asdecimalmark=Nothing} ,astyle=amountstyle{asprecision=Precision 0, asdecimalmark=Nothing}
,aprice=Just $ UnitCost $ ,acost=Just $ UnitCost $
nullamt{ nullamt{
acommodity="" acommodity=""
,aquantity=0.5 ,aquantity=0.5
@ -1598,7 +1598,7 @@ tests_Common = testGroup "Common" [
acommodity="$" acommodity="$"
,aquantity=10 ,aquantity=10
,astyle=amountstyle{asprecision=Precision 0, asdecimalmark=Nothing} ,astyle=amountstyle{asprecision=Precision 0, asdecimalmark=Nothing}
,aprice=Just $ TotalCost $ ,acost=Just $ TotalCost $
nullamt{ nullamt{
acommodity="" acommodity=""
,aquantity=5 ,aquantity=5

View File

@ -617,7 +617,7 @@ balanceReportTableAsText ReportOpts{..} =
tests_MultiBalanceReport = testGroup "MultiBalanceReport" [ tests_MultiBalanceReport = testGroup "MultiBalanceReport" [
let let
amt0 = Amount {acommodity="$", aquantity=0, aprice=Nothing, amt0 = Amount {acommodity="$", aquantity=0, acost=Nothing,
astyle=AmountStyle {ascommodityside = L, ascommodityspaced = False, asdigitgroups = Nothing, astyle=AmountStyle {ascommodityside = L, ascommodityspaced = False, asdigitgroups = Nothing,
asdecimalmark = Just '.', asprecision = Precision 2, asrounding = NoRounding}} asdecimalmark = Just '.', asprecision = Precision 2, asrounding = NoRounding}}
(rspec,journal) `gives` r = do (rspec,journal) `gives` r = do

View File

@ -154,7 +154,7 @@ close copts@CliOpts{rawopts_=rawopts, reportspec_=rspec0} j = do
| mode_ == Assert = | mode_ == Assert =
[ posting{ [ posting{
paccount = a paccount = a
,pamount = mixedAmount $ precise b{aquantity=0, aprice=Nothing} ,pamount = mixedAmount $ precise b{aquantity=0, acost=Nothing}
-- after each commodity's last posting, assert 0 balance (#1035) -- after each commodity's last posting, assert 0 balance (#1035)
-- balance assertion amounts are unpriced (#824) -- balance assertion amounts are unpriced (#824)
,pbalanceassertion = ,pbalanceassertion =
@ -179,7 +179,7 @@ close copts@CliOpts{rawopts_=rawopts, reportspec_=rspec0} j = do
-- balance assertion amounts are unpriced (#824) -- balance assertion amounts are unpriced (#824)
,pbalanceassertion = ,pbalanceassertion =
if islast if islast
then Just nullassertion{baamount=precise b{aquantity=0, aprice=Nothing}} then Just nullassertion{baamount=precise b{aquantity=0, acost=Nothing}}
else Nothing else Nothing
} }
@ -233,7 +233,7 @@ close copts@CliOpts{rawopts_=rawopts, reportspec_=rspec0} j = do
,pamount = mixedAmount $ precise b ,pamount = mixedAmount $ precise b
,pbalanceassertion = ,pbalanceassertion =
case mcommoditysum of case mcommoditysum of
Just s -> Just nullassertion{baamount=precise s{aprice=Nothing}} Just s -> Just nullassertion{baamount=precise s{acost=Nothing}}
Nothing -> Nothing Nothing -> Nothing
} }
: [posting{paccount=openacct, pamount=mixedAmount . precise $ negate b} | interleaved] : [posting{paccount=openacct, pamount=mixedAmount . precise $ negate b} | interleaved]