Skip to content

Commit bf90b20

Browse files
committed
imp: --txn-balancing flag to select transaction balancing precision [#2402]
1 parent 442bd24 commit bf90b20

File tree

6 files changed

+66
-9
lines changed

6 files changed

+66
-9
lines changed

doc/common.m4

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ General input/data transformation flags:
8787
hledger-ui, also make future-dated transactions
8888
visible at startup.
8989
-I --ignore-assertions don't check balance assertions by default
90+
--txn-balancing=... how to check that transactions are balanced:
91+
'old': use global display precision
92+
'exact': use transaction precision (default)
9093
--infer-costs infer conversion equity postings from costs
9194
--infer-equity infer costs from conversion equity postings
9295
--infer-market-prices infer market prices from costs

hledger-lib/Hledger/Data/Balancing.hs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,15 @@ data BalancingOpts = BalancingOpts
6565
, infer_balancing_costs_ :: Bool -- ^ Are we permitted to infer missing costs to balance transactions ?
6666
-- Distinct from InputOpts{infer_costs_}.
6767
, commodity_styles_ :: Maybe (M.Map CommoditySymbol AmountStyle) -- ^ commodity display styles
68+
, txn_balancing_ :: TransactionBalancingPrecision
6869
} deriving (Eq, Ord, Show)
6970

7071
defbalancingopts :: BalancingOpts
7172
defbalancingopts = BalancingOpts
7273
{ ignore_assertions_ = False
7374
, infer_balancing_costs_ = True
7475
, commodity_styles_ = Nothing
76+
, txn_balancing_ = TBPExact
7577
}
7678

7779
-- | Check that this transaction would appear balanced to a human when displayed.
@@ -92,8 +94,7 @@ defbalancingopts = BalancingOpts
9294
-- (using the given display styles if provided)
9395
--
9496
transactionCheckBalanced :: BalancingOpts -> Transaction -> [String]
95-
-- transactionCheckBalanced BalancingOpts{commodity_styles_=_mstyles} t = errs
96-
transactionCheckBalanced _ t = errs
97+
transactionCheckBalanced BalancingOpts{commodity_styles_=_mglobalstyles, txn_balancing_} t = errs
9798
where
9899
-- get real and balanced virtual postings, to be checked separately
99100
(rps, bvps) = foldr partitionPosting ([], []) $ tpostings t
@@ -110,9 +111,16 @@ transactionCheckBalanced _ t = errs
110111
| costPostingTagName `elem` map fst (ptags p) = mixedAmountStripCosts $ pamount p
111112
| otherwise = mixedAmountCost $ pamount p
112113

113-
lookszero = mixedAmountLooksZero .
114-
-- maybe id styleAmounts _mstyles -- when rounded with global/journal precisions
115-
styleAmounts (transactionCommodityStylesWith HardRounding t) -- when rounded with transaction's precisions
114+
lookszero = mixedAmountLooksZero . roundforbalancecheck
115+
where
116+
roundforbalancecheck = case txn_balancing_ of
117+
TBPOld -> maybe id styleAmounts _mglobalstyles
118+
-- TBPCompat -> styleAmounts (transactionstyles `limitprecisionsto` commoditydirectivestyles)
119+
TBPExact -> styleAmounts transactionstyles
120+
where
121+
transactionstyles = transactionCommodityStylesWith HardRounding t
122+
-- limitprecisionsto = undefined
123+
-- commoditydirectivestyles = undefined
116124

117125
-- when there's multiple non-zeros, check they do not all have the same sign
118126
(rsignsok, bvsignsok) = (signsOk rps, signsOk bvps)
@@ -289,7 +297,7 @@ transactionInferBalancingAmount styles t@Transaction{tpostings=ps}
289297
& mixedAmountCost
290298
-- & dbg9With (lbl "balancing amount converted to cost".showMixedAmountOneLine)
291299
& styleAmounts (styles
292-
-- Needed until we switch to locally-inferred balancing precisions:
300+
-- Needed until we switch to locally-inferred balancing precisions: XXX #2402
293301
-- these had hard rounding set to help with balanced-checking;
294302
-- set no rounding now to avoid excessive display precision in output
295303
& amountStylesSetRounding NoRounding

hledger-lib/Hledger/Data/Transaction.hs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ module Hledger.Data.Transaction
4040
, transactionAddTags
4141
, transactionAddHiddenAndMaybeVisibleTag
4242
-- * helpers
43+
, TransactionBalancingPrecision(..)
4344
, payeeAndNoteFromDescription
4445
, payeeAndNoteFromDescription'
4546
-- nonzerobalanceerror
@@ -85,6 +86,27 @@ import Data.Function ((&))
8586
import Data.List (union)
8687

8788

89+
-- | How to determine the precision used for checking that transactions are balanced. See #2402.
90+
data TransactionBalancingPrecision
91+
= -- | Legacy behaviour, as in hledger <=1.43:
92+
-- use precision inferred from the whole journal, overridable by commodity directive or -c.
93+
-- Display precision is also transaction balancing precision; increasing it can break journal reading.
94+
-- Some journals from ledger or beancount are rejected until commodity directives are added.
95+
TBPOld
96+
-- | -- | Use precision inferred from the transaction, reducible by commodity directive (or -c ?)
97+
-- -- This is more robust when there is no commodity directive, because it's not affected by other transactions or P directives.
98+
-- -- Increasing display precision does not increase balancing precision, so it does not break journal reading.
99+
-- -- But reducing it does reduce balancing precision, so existing hledger journals which rely on this can still be read.
100+
-- -- Journals from ledger or beancount are accepted without needing commodity directives.
101+
-- TBPCompat
102+
| -- | Use precision inferred from the transaction.
103+
-- This is the most strict; transactions that worked with hledger <=1.43 may need to be adjusted.
104+
-- It's also the simplest, and most robust overall ?
105+
-- Display precision and transaction balancing precision are independent; display precision never affects journal reading.
106+
-- Journals from ledger or beancount are accepted without needing commodity directives.
107+
TBPExact
108+
deriving (Bounded, Enum, Eq, Ord, Read, Show)
109+
88110
instance HasAmounts Transaction where
89111
styleAmounts styles t = t{tpostings=styleAmounts styles $ tpostings t}
90112

hledger-lib/Hledger/Read/Common.hs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ import Hledger.Reports.ReportOptions (ReportOpts(..), queryFromFlags, rawOptsToR
160160
import Hledger.Utils
161161
import Hledger.Read.InputOptions
162162

163+
163164
--- ** doctest setup
164165
-- $setup
165166
-- >>> :set -XOverloadedStrings
@@ -214,8 +215,11 @@ rawOptsToInputOpts day usecoloronstdout postingaccttags rawopts =
214215
argsquery = map fst . rights . map (parseQueryTerm day) $ querystring_ ropts
215216
datequery = simplifyQuery . filterQuery queryIsDate . And $ queryFromFlags ropts : argsquery
216217

218+
txnbalancingprecision = either err id $ transactionBalancingPrecisionFromOpts rawopts
219+
where err e = error' $ "could not parse --txn-balancing: '" ++ e ++ "'" -- PARTIAL:
220+
217221
styles = either err id $ commodityStyleFromRawOpts rawopts
218-
where err e = error' $ "could not parse commodity-style: '" ++ e ++ "'" -- PARTIAL:
222+
where err e = error' $ "could not parse --commodity-style: '" ++ e ++ "'" -- PARTIAL:
219223

220224
in definputopts{
221225
-- files_ = listofstringopt "file" rawopts
@@ -236,6 +240,7 @@ rawOptsToInputOpts day usecoloronstdout postingaccttags rawopts =
236240
,balancingopts_ = defbalancingopts{
237241
ignore_assertions_ = boolopt "ignore-assertions" rawopts
238242
, infer_balancing_costs_ = not noinferbalancingcosts
243+
, txn_balancing_ = txnbalancingprecision
239244
, commodity_styles_ = Just styles
240245
}
241246
,strict_ = boolopt "strict" rawopts
@@ -274,6 +279,15 @@ commodityStyleFromRawOpts rawOpts =
274279
Left _ -> Left optStr
275280
Right (Amount acommodity _ astyle _) -> Right (acommodity, astyle)
276281

282+
transactionBalancingPrecisionFromOpts :: RawOpts -> Either String TransactionBalancingPrecision
283+
transactionBalancingPrecisionFromOpts rawopts =
284+
case maybestringopt "txn-balancing" rawopts of
285+
Nothing -> Right TBPExact
286+
Just "old" -> Right TBPOld
287+
-- Just "compat" -> Right TBPCompat
288+
Just "exact" -> Right TBPExact
289+
Just s -> Left $ s<>", should be one of: old, exact" -- compat
290+
277291
-- | Given a parser to ParsedJournal, input options, file path and
278292
-- content: run the parser on the content, and finalise the result to
279293
-- get a Journal; or throw an error.
@@ -285,7 +299,7 @@ parseAndFinaliseJournal parser iopts f txt =
285299
-- | Given a parser to ParsedJournal, input options, file path and
286300
-- content: run the parser on the content. This is all steps of
287301
-- 'parseAndFinaliseJournal' without the finalisation step, and is used when
288-
-- you need to perform other actions before finalisation, as in parsing
302+
-- you need to perform other actions before finalisatison, as in parsing
289303
-- Timeclock and Timedot files.
290304
initialiseAndParseJournal :: ErroringJournalParser IO ParsedJournal -> InputOpts
291305
-> FilePath -> Text -> ExceptT String IO Journal

hledger/Hledger/Cli/CliOptions.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ inputflags = [
164164
, "In hledger-ui, also make future-dated transactions visible at startup."
165165
])
166166
,flagNone ["ignore-assertions","I"] (setboolopt "ignore-assertions") "don't check balance assertions by default"
167+
,flagReq ["txn-balancing"] (\s opts -> Right $ setopt "txn-balancing" s opts) "..." (unlines [
168+
"how to check that transactions are balanced:"
169+
,"'old': - use global display precision"
170+
-- ,"'compat': - use transaction precision, reducible"
171+
,"'exact': - use transaction precision (default)"
172+
])
167173
,flagNone ["infer-costs"] (setboolopt "infer-costs") "infer conversion equity postings from costs"
168174
,flagNone ["infer-equity"] (setboolopt "infer-equity") "infer costs from conversion equity postings"
169175
-- history of this flag so far, lest we be confused:

hledger/test/journal/precision.test

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ $ hledger -f - bal -N
221221

222222
# ** 16. Before hledger 1.44, an inexactly balanced entry like this could be accepted
223223
# because of a commodity directive reducing the display/balance-checking precision.
224-
# From 1.44, transaction balancing uses the transaction's local precisions only,
224+
# From 1.44, transaction balancing uses the transaction's local precisions by default,
225225
# making the balance checking more strict in this case.
226226
<
227227
commodity $1.00
@@ -233,3 +233,7 @@ commodity $1.00
233233
$ hledger -f - check
234234
>2 /unbalanced/
235235
>= 1
236+
237+
# ** 17. --txn-balancing=old can be used to restore the pre-1.44 behaviour.
238+
$ hledger -f - check --txn-balancing=old
239+

0 commit comments

Comments
 (0)