diff --git a/hledger-lib/Hledger/Data/Balancing.hs b/hledger-lib/Hledger/Data/Balancing.hs index ec998ddc6..f5d8ff24f 100644 --- a/hledger-lib/Hledger/Data/Balancing.hs +++ b/hledger-lib/Hledger/Data/Balancing.hs @@ -34,6 +34,7 @@ import Control.Monad.Reader as R (ReaderT, reader, runReaderT, ask, asks) import Control.Monad.ST (ST, runST) import Control.Monad.Trans.Class (lift) import Data.Array.ST (STArray, getElems, newListArray, writeArray) +import Data.Bifunctor (second) import Data.Foldable (asum) import Data.Function ((&)) import Data.Functor ((<&>), void) @@ -49,7 +50,6 @@ import qualified Data.Map as M import Safe (headErr) import Text.Printf (printf) -import Hledger.Utils import Hledger.Data.Types import Hledger.Data.AccountName (isAccountNamePrefixOf) import Hledger.Data.Amount @@ -57,7 +57,7 @@ import Hledger.Data.Journal import Hledger.Data.Posting import Hledger.Data.Transaction import Hledger.Data.Errors -import Data.Bifunctor (second) +import Hledger.Utils data BalancingOpts = BalancingOpts @@ -92,7 +92,8 @@ defbalancingopts = BalancingOpts -- (using the given display styles if provided) -- transactionCheckBalanced :: BalancingOpts -> Transaction -> [String] -transactionCheckBalanced BalancingOpts{commodity_styles_} t = errs +-- transactionCheckBalanced BalancingOpts{commodity_styles_=_mstyles} t = errs +transactionCheckBalanced _ t = errs where -- get real and balanced virtual postings, to be checked separately (rps, bvps) = foldr partitionPosting ([], []) $ tpostings t @@ -109,10 +110,9 @@ transactionCheckBalanced BalancingOpts{commodity_styles_} t = errs | costPostingTagName `elem` map fst (ptags p) = mixedAmountStripCosts $ pamount p | otherwise = mixedAmountCost $ pamount p - -- transaction balancedness is checked at each commodity's display precision - lookszero = mixedAmountLooksZero . atdisplayprecision - where - atdisplayprecision = maybe id styleAmounts $ commodity_styles_ + lookszero = mixedAmountLooksZero . + -- maybe id styleAmounts _mstyles -- when rounded with global/journal precisions + styleAmounts (transactionCommodityStylesWith HardRounding t) -- when rounded with transaction's precisions -- when there's multiple non-zeros, check they do not all have the same sign (rsignsok, bvsignsok) = (signsOk rps, signsOk bvps) diff --git a/hledger-lib/Hledger/Data/Transaction.hs b/hledger-lib/Hledger/Data/Transaction.hs index a3991a871..000cba1b4 100644 --- a/hledger-lib/Hledger/Data/Transaction.hs +++ b/hledger-lib/Hledger/Data/Transaction.hs @@ -33,6 +33,8 @@ module Hledger.Data.Transaction , transactionMapPostings , transactionMapPostingAmounts , transactionAmounts +, transactionCommodityStyles +, transactionCommodityStylesWith , transactionNegate , partitionAndCheckConversionPostings , transactionAddTags @@ -483,6 +485,17 @@ transactionMapPostingAmounts f = transactionMapPostings (postingTransformAmount transactionAmounts :: Transaction -> [MixedAmount] transactionAmounts = map pamount . tpostings +-- | Get the canonical amount styles inferred from this transaction's amounts. +transactionCommodityStyles :: Transaction -> M.Map CommoditySymbol AmountStyle +transactionCommodityStyles = + either (const mempty) id . -- ignore style problems, commodityStylesFromAmounts doesn't report them currently + commodityStylesFromAmounts . concatMap (amountsRaw . pamount) . tpostings + +-- | Like transactionCommodityStyles, but attach a particular rounding strategy to the styles, +-- affecting how they will affect display precisions when applied. +transactionCommodityStylesWith :: Rounding -> Transaction -> M.Map CommoditySymbol AmountStyle +transactionCommodityStylesWith r = amountStylesSetRounding r . transactionCommodityStyles + -- | Flip the sign of this transaction's posting amounts (and balance assertion amounts). transactionNegate :: Transaction -> Transaction transactionNegate = transactionMapPostings postingNegate diff --git a/hledger/test/journal/precision.test b/hledger/test/journal/precision.test index f19e8b968..183fc7231 100644 --- a/hledger/test/journal/precision.test +++ b/hledger/test/journal/precision.test @@ -188,3 +188,16 @@ $ hledger -f - bal -O csv "b","1 JPY" "c","1000 JPY" "Total:","1002 JPY" + +# ** 13. An entry like this, generated by bean-report, did not balance in hledger <=1.43, +# because the P amount influenced the commodity's display (and balancing) precision. +# From 1.44 transaction balancing uses the transaction's local precisions, +# so CNY's precision is 2 and this does balance. +< +P 2025-01-01 USD 7.147224669603524229074889868 CNY + +2025-01-01 + a -113.50 USD @ 7.147224669603524229074889868 CNY + a 811.21 CNY + +$ hledger -f - check