imp:journal: balance transactions with local precisions [#2402]

This commit is contained in:
Simon Michael 2025-06-07 15:23:31 -10:00
parent 82ba831822
commit a9408b8cd5
3 changed files with 33 additions and 7 deletions

View File

@ -34,6 +34,7 @@ import Control.Monad.Reader as R (ReaderT, reader, runReaderT, ask, asks)
import Control.Monad.ST (ST, runST) import Control.Monad.ST (ST, runST)
import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Class (lift)
import Data.Array.ST (STArray, getElems, newListArray, writeArray) import Data.Array.ST (STArray, getElems, newListArray, writeArray)
import Data.Bifunctor (second)
import Data.Foldable (asum) import Data.Foldable (asum)
import Data.Function ((&)) import Data.Function ((&))
import Data.Functor ((<&>), void) import Data.Functor ((<&>), void)
@ -49,7 +50,6 @@ import qualified Data.Map as M
import Safe (headErr) import Safe (headErr)
import Text.Printf (printf) import Text.Printf (printf)
import Hledger.Utils
import Hledger.Data.Types import Hledger.Data.Types
import Hledger.Data.AccountName (isAccountNamePrefixOf) import Hledger.Data.AccountName (isAccountNamePrefixOf)
import Hledger.Data.Amount import Hledger.Data.Amount
@ -57,7 +57,7 @@ import Hledger.Data.Journal
import Hledger.Data.Posting import Hledger.Data.Posting
import Hledger.Data.Transaction import Hledger.Data.Transaction
import Hledger.Data.Errors import Hledger.Data.Errors
import Data.Bifunctor (second) import Hledger.Utils
data BalancingOpts = BalancingOpts data BalancingOpts = BalancingOpts
@ -92,7 +92,8 @@ defbalancingopts = BalancingOpts
-- (using the given display styles if provided) -- (using the given display styles if provided)
-- --
transactionCheckBalanced :: BalancingOpts -> Transaction -> [String] transactionCheckBalanced :: BalancingOpts -> Transaction -> [String]
transactionCheckBalanced BalancingOpts{commodity_styles_} t = errs -- transactionCheckBalanced BalancingOpts{commodity_styles_=_mstyles} t = errs
transactionCheckBalanced _ t = errs
where where
-- get real and balanced virtual postings, to be checked separately -- get real and balanced virtual postings, to be checked separately
(rps, bvps) = foldr partitionPosting ([], []) $ tpostings t (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 | costPostingTagName `elem` map fst (ptags p) = mixedAmountStripCosts $ pamount p
| otherwise = mixedAmountCost $ pamount p | otherwise = mixedAmountCost $ pamount p
-- transaction balancedness is checked at each commodity's display precision lookszero = mixedAmountLooksZero .
lookszero = mixedAmountLooksZero . atdisplayprecision -- maybe id styleAmounts _mstyles -- when rounded with global/journal precisions
where styleAmounts (transactionCommodityStylesWith HardRounding t) -- when rounded with transaction's precisions
atdisplayprecision = maybe id styleAmounts $ commodity_styles_
-- when there's multiple non-zeros, check they do not all have the same sign -- when there's multiple non-zeros, check they do not all have the same sign
(rsignsok, bvsignsok) = (signsOk rps, signsOk bvps) (rsignsok, bvsignsok) = (signsOk rps, signsOk bvps)

View File

@ -33,6 +33,8 @@ module Hledger.Data.Transaction
, transactionMapPostings , transactionMapPostings
, transactionMapPostingAmounts , transactionMapPostingAmounts
, transactionAmounts , transactionAmounts
, transactionCommodityStyles
, transactionCommodityStylesWith
, transactionNegate , transactionNegate
, partitionAndCheckConversionPostings , partitionAndCheckConversionPostings
, transactionAddTags , transactionAddTags
@ -483,6 +485,17 @@ transactionMapPostingAmounts f = transactionMapPostings (postingTransformAmount
transactionAmounts :: Transaction -> [MixedAmount] transactionAmounts :: Transaction -> [MixedAmount]
transactionAmounts = map pamount . tpostings 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). -- | Flip the sign of this transaction's posting amounts (and balance assertion amounts).
transactionNegate :: Transaction -> Transaction transactionNegate :: Transaction -> Transaction
transactionNegate = transactionMapPostings postingNegate transactionNegate = transactionMapPostings postingNegate

View File

@ -188,3 +188,16 @@ $ hledger -f - bal -O csv
"b","1 JPY" "b","1 JPY"
"c","1000 JPY" "c","1000 JPY"
"Total:","1002 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