From 015492553e6381ca22e36f40d447fc7e6db623b2 Mon Sep 17 00:00:00 2001 From: Stephen Morgan Date: Wed, 24 Jun 2020 23:38:17 +1000 Subject: [PATCH] lib: Move unifyMixedAmount to Hledger.Data.Amount, make it return Maybe Amount, export it. --- hledger-lib/Hledger/Data/Amount.hs | 15 +++++++ .../Hledger/Reports/MultiBalanceReport.hs | 42 ++++++------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/hledger-lib/Hledger/Data/Amount.hs b/hledger-lib/Hledger/Data/Amount.hs index 6850ae558..244fd6a79 100644 --- a/hledger-lib/Hledger/Data/Amount.hs +++ b/hledger-lib/Hledger/Data/Amount.hs @@ -100,6 +100,7 @@ module Hledger.Data.Amount ( mapMixedAmount, normaliseMixedAmountSquashPricesForDisplay, normaliseMixedAmount, + unifyMixedAmount, mixedAmountStripPrices, -- ** arithmetic mixedAmountCost, @@ -131,6 +132,7 @@ module Hledger.Data.Amount ( tests_Amount ) where +import Control.Monad (foldM) import Data.Char (isDigit) import Data.Decimal (roundTo, decimalPlaces, normalizeDecimal) import Data.Function (on) @@ -537,6 +539,19 @@ normaliseHelper squashprices (Mixed as) normaliseMixedAmountSquashPricesForDisplay :: MixedAmount -> MixedAmount normaliseMixedAmountSquashPricesForDisplay = normaliseHelper True +-- | Unify a MixedAmount to a single commodity value if possible. +-- Like normaliseMixedAmount, this consolidates amounts of the same commodity +-- and discards zero amounts; but this one insists on simplifying to +-- a single commodity, and will return Nothing if this is not possible. +unifyMixedAmount :: MixedAmount -> Maybe Amount +unifyMixedAmount = foldM combine 0 . amounts + where + combine amount result + | amountIsZero amount = Just result + | amountIsZero result = Just amount + | acommodity amount == acommodity result = Just $ amount + result + | otherwise = Nothing + -- | Sum same-commodity amounts in a lossy way, applying the first -- price to the result and discarding any other prices. Only used as a -- rendering helper. diff --git a/hledger-lib/Hledger/Reports/MultiBalanceReport.hs b/hledger-lib/Hledger/Reports/MultiBalanceReport.hs index 72bf2752d..4acba4e20 100644 --- a/hledger-lib/Hledger/Reports/MultiBalanceReport.hs +++ b/hledger-lib/Hledger/Reports/MultiBalanceReport.hs @@ -25,20 +25,21 @@ module Hledger.Reports.MultiBalanceReport ( ) where -import Data.List +import Control.Monad (guard) +import Data.List (sortBy, transpose) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM import Data.Map (Map) import qualified Data.Map as M -import Data.Maybe -import Data.Ord +import Data.Maybe (fromMaybe, mapMaybe) +import Data.Ord (comparing) #if !(MIN_VERSION_base(4,11,0)) import Data.Semigroup ((<>)) #endif -import Data.Time.Calendar -import Safe +import Data.Time.Calendar (Day, addDays, fromGregorian) +import Safe (headDef, headMay, lastMay) import Text.Tabular as T -import Text.Tabular.AsciiWide +import Text.Tabular.AsciiWide (render) import Hledger.Data import Hledger.Query @@ -511,32 +512,15 @@ subaccountTallies as = foldr incrementParent mempty allaccts allaccts = expandAccountNames as incrementParent a = HM.insertWith (+) (parentAccountName a) 1 --- | Helper to unify a MixedAmount to a single commodity value. --- Like normaliseMixedAmount, this consolidates amounts of the same commodity --- and discards zero amounts; but this one insists on simplifying to --- a single commodity, and will throw a program-terminating error if --- this is not possible. -unifyMixedAmount :: MixedAmount -> Amount -unifyMixedAmount mixedAmount = foldl combine (num 0) (amounts mixedAmount) - where - combine amount result = - if amountIsZero amount - then result - else if amountIsZero result - then amount - else if acommodity amount == acommodity result - then amount + result - else error' "Cannot calculate percentages for accounts with multiple commodities. (Hint: Try --cost, -V or similar flags.)" - -- | Helper to calculate the percentage from two mixed. Keeps the sign of the first argument. -- Uses unifyMixedAmount to unify each argument and then divides them. perdivide :: MixedAmount -> MixedAmount -> MixedAmount -perdivide a b = - let a' = unifyMixedAmount a - b' = unifyMixedAmount b - in if amountIsZero a' || amountIsZero b' || acommodity a' == acommodity b' - then mixed [per $ if aquantity b' == 0 then 0 else (aquantity a' / abs (aquantity b') * 100)] - else error' "Cannot calculate percentages if accounts have different commodities. (Hint: Try --cost, -V or similar flags.)" +perdivide a b = fromMaybe (error' errmsg) $ do + a' <- unifyMixedAmount a + b' <- unifyMixedAmount b + guard $ amountIsZero a' || amountIsZero b' || acommodity a' == acommodity b' + return $ mixed [per $ if aquantity b' == 0 then 0 else aquantity a' / abs (aquantity b') * 100] + where errmsg = "Cannot calculate percentages if accounts have different commodities (Hint: Try --cost, -V or similar flags.)" -- Local debug helper -- add a prefix to this function's debug output