smarter handling of priced amounts. Normalising and some basic arithmetic now preserve prices.

Currently, amounts with the same commodity but different prices are kept separate. Useful ?
This commit is contained in:
Simon Michael 2008-11-22 20:30:43 +00:00
parent 54ee68976b
commit 4c97ca5514
2 changed files with 23 additions and 9 deletions

View File

@ -55,14 +55,22 @@ instance Num Amount where
(-) = amountop (-) (-) = amountop (-)
(*) = amountop (*) (*) = amountop (*)
instance Ord Amount where
compare (Amount ac aq ap) (Amount bc bq bp) = compare (ac,aq,ap) (bc,bq,bp)
instance Num MixedAmount where instance Num MixedAmount where
fromInteger i = Mixed [Amount (comm "") (fromInteger i) Nothing] fromInteger i = Mixed [Amount (comm "") (fromInteger i) Nothing]
negate (Mixed as) = Mixed $ map negate as negate (Mixed as) = Mixed $ map negateAmountPreservingPrice as
(+) (Mixed as) (Mixed bs) = normaliseMixedAmount $ Mixed $ filter (not . isZeroAmount) $ as ++ bs (+) (Mixed as) (Mixed bs) = normaliseMixedAmount $ Mixed $ filter (not . isZeroAmount) $ as ++ bs
(*) = error "programming error, mixed amounts do not support multiplication" (*) = error "programming error, mixed amounts do not support multiplication"
abs = error "programming error, mixed amounts do not support abs" abs = error "programming error, mixed amounts do not support abs"
signum = error "programming error, mixed amounts do not support signum" signum = error "programming error, mixed amounts do not support signum"
instance Ord MixedAmount where
compare (Mixed as) (Mixed bs) = compare as bs
negateAmountPreservingPrice a = (-a){price=price a}
-- | Apply a binary arithmetic operator to two amounts - converting to the -- | Apply a binary arithmetic operator to two amounts - converting to the
-- second one's commodity, adopting the lowest precision, and discarding -- second one's commodity, adopting the lowest precision, and discarding
-- any price information. (Using the second commodity is best since sum -- any price information. (Using the second commodity is best since sum
@ -136,14 +144,20 @@ showMixedAmountOrZero a
| isZeroMixedAmount a = "0" | isZeroMixedAmount a = "0"
| otherwise = showMixedAmount a | otherwise = showMixedAmount a
-- | Simplify a mixed amount by combining any of its component amounts -- | Simplify a mixed amount by combining any component amounts which have
-- which have the same commodity. -- the same commodity and the same price.
normaliseMixedAmount :: MixedAmount -> MixedAmount normaliseMixedAmount :: MixedAmount -> MixedAmount
normaliseMixedAmount (Mixed as) = Mixed $ map sum $ grouped normaliseMixedAmount (Mixed as) = Mixed as'
where where
grouped = [filter (hassymbol s) as | s <- symbols] as' = map sumAmountsPreservingPrice $ group $ sort as
symbols = sort $ nub $ map (symbol . commodity) as sort = sortBy cmpsymbolandprice
hassymbol s a = s == (symbol $ commodity a) cmpsymbolandprice a1 a2 = compare (sym a1,price a1) (sym a2,price a2)
group = groupBy samesymbolandprice
samesymbolandprice a1 a2 = (sym a1 == sym a2) && (price a1 == price a2)
sym = symbol . commodity
sumAmountsPreservingPrice [] = nullamt
sumAmountsPreservingPrice as = (sum as){price=price $ head as}
-- | Convert a mixed amount's component amounts to the commodity of their -- | Convert a mixed amount's component amounts to the commodity of their
-- saved price, if any. -- saved price, if any.

View File

@ -13,7 +13,7 @@ import qualified Data.Map as Map
type AccountName = String type AccountName = String
data Side = L | R deriving (Eq,Show) data Side = L | R deriving (Eq,Show,Ord)
data Commodity = Commodity { data Commodity = Commodity {
symbol :: String, -- ^ the commodity's symbol symbol :: String, -- ^ the commodity's symbol
@ -23,7 +23,7 @@ data Commodity = Commodity {
spaced :: Bool, -- ^ should there be a space between symbol and quantity spaced :: Bool, -- ^ should there be a space between symbol and quantity
comma :: Bool, -- ^ should thousands be comma-separated comma :: Bool, -- ^ should thousands be comma-separated
precision :: Int -- ^ number of decimal places to display precision :: Int -- ^ number of decimal places to display
} deriving (Eq,Show) } deriving (Eq,Show,Ord)
data Amount = Amount { data Amount = Amount {
commodity :: Commodity, commodity :: Commodity,