Skip to content

Commit

Permalink
fix: non-print-like reports no longer add trailing decimal marks (fix #…
Browse files Browse the repository at this point in the history
…2115)

That 1.31 change was advertised as being for the print command only,
but it affected all commands. Now it affects only print and other
"print-like" commands (ie all commands that show whole journal entries
that we might want to re-parse).

Also three classes of hledger output, and how they modify the
commodity display styles' digit group marks and decimal marks
to suit different consumers, have been identified and documented
(under REPORTING CONCEPTS).
  • Loading branch information
simonmichael committed Nov 23, 2023
1 parent ef2f3e1 commit 1744021
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 18 deletions.
40 changes: 23 additions & 17 deletions hledger-lib/Hledger/Data/Amount.hs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ quoteCommoditySymbolIfNeeded s
data AmountDisplayOpts = AmountDisplayOpts
{ displayPrice :: Bool -- ^ Whether to display the Price of an Amount.
, displayZeroCommodity :: Bool -- ^ If the Amount rounds to 0, whether to display its commodity string.
, displayThousandsSep :: Bool -- ^ Whether to display thousands separators.
, displayThousandsSep :: Bool -- ^ Whether to display digit group marks (eg thousands separators)
, displayAddDecimalMark :: Bool -- ^ Whether to add a trailing decimal mark when there are no decimal digits
-- and there are digit group marks, to disambiguate
, displayColour :: Bool -- ^ Whether to colourise negative Amounts.
, displayOneLine :: Bool -- ^ Whether to display on one line.
, displayMinWidth :: Maybe Int -- ^ Minimum width to pad to
Expand All @@ -240,6 +242,7 @@ noColour = AmountDisplayOpts { displayPrice = True
, displayColour = False
, displayZeroCommodity = False
, displayThousandsSep = True
, displayAddDecimalMark = False
, displayOneLine = False
, displayMinWidth = Just 0
, displayMaxWidth = Nothing
Expand Down Expand Up @@ -645,22 +648,25 @@ showAmount = wbUnpack . showAmountB noColour
--
showAmountB :: AmountDisplayOpts -> Amount -> WideBuilder
showAmountB _ Amount{acommodity="AUTO"} = mempty
showAmountB opts a@Amount{astyle=style} =
showAmountB
AmountDisplayOpts{displayPrice, displayColour, displayZeroCommodity,
displayThousandsSep, displayAddDecimalMark, displayOrder}
a@Amount{astyle=style} =
color $ case ascommodityside style of
L -> showC (wbFromText comm) space <> quantity' <> price
R -> quantity' <> showC space (wbFromText comm) <> price
where
color = if displayColour opts && isNegativeAmount a then colorB Dull Red else id
quantity = showamountquantity $
if displayThousandsSep opts then a else a{astyle=(astyle a){asdigitgroups=Nothing}}
color = if displayColour && isNegativeAmount a then colorB Dull Red else id
quantity = showAmountQuantity displayAddDecimalMark $
if displayThousandsSep then a else a{astyle=(astyle a){asdigitgroups=Nothing}}
(quantity', comm)
| amountLooksZero a && not (displayZeroCommodity opts) = (WideBuilder (TB.singleton '0') 1, "")
| amountLooksZero a && not displayZeroCommodity = (WideBuilder (TB.singleton '0') 1, "")
| otherwise = (quantity, quoteCommoditySymbolIfNeeded $ acommodity a)
space = if not (T.null comm) && ascommodityspaced style then WideBuilder (TB.singleton ' ') 1 else mempty
-- concatenate these texts,
-- or return the empty text if there's a commodity display order. XXX why ?
showC l r = if isJust (displayOrder opts) then mempty else l <> r
price = if displayPrice opts then showAmountPrice a else mempty
showC l r = if isJust displayOrder then mempty else l <> r
price = if displayPrice then showAmountPrice a else mempty

-- | Colour version. For a negative amount, adds ANSI codes to change the colour,
-- currently to hard-coded red.
Expand Down Expand Up @@ -691,10 +697,10 @@ showAmountDebug Amount{..} =

-- | Get a Text Builder for the string representation of the number part of of an amount,
-- using the display settings from its commodity. Also returns the width of the number.
-- A special case: if it is showing digit group separators but no decimal places,
-- show a decimal mark (with nothing after it) to make it easier to parse correctly.
showamountquantity :: Amount -> WideBuilder
showamountquantity amt@Amount{astyle=AmountStyle{asdecimalmark=mdec, asdigitgroups=mgrps}} =
-- With a true first argument, if there are no decimal digits but there are digit group separators,
-- it shows the amount with a trailing decimal mark to help disambiguate it for parsing.
showAmountQuantity :: Bool -> Amount -> WideBuilder
showAmountQuantity disambiguate amt@Amount{astyle=AmountStyle{asdecimalmark=mdec, asdigitgroups=mgrps}} =
signB <> intB <> fracB
where
Decimal decplaces mantissa = amountRoundedQuantity amt
Expand All @@ -706,13 +712,13 @@ showamountquantity amt@Amount{astyle=AmountStyle{asdecimalmark=mdec, asdigitgrou
(intPart, fracPart) = T.splitAt intLen numtxtwithzero
intB = applyDigitGroupStyle mgrps intLen $ if decplaces == 0 then numtxt else intPart
signB = if mantissa < 0 then WideBuilder (TB.singleton '-') 1 else mempty
fracB = if decplaces > 0 || isshowingdigitgroupseparator
fracB = if decplaces > 0 || (isshowingdigitgroupseparator && disambiguate)
then WideBuilder (TB.singleton dec <> TB.fromText fracPart) (1 + fromIntegral decplaces)
else mempty

isshowingdigitgroupseparator = case mgrps of
Just (DigitGroups _ (rightmostgrplen:_)) -> intLen > fromIntegral rightmostgrplen
_ -> False
where
isshowingdigitgroupseparator = case mgrps of
Just (DigitGroups _ (rightmostgrplen:_)) -> intLen > fromIntegral rightmostgrplen
_ -> False

-- | Given an integer as text, and its length, apply the given DigitGroupStyle,
-- inserting digit group separators between digit groups where appropriate.
Expand Down
5 changes: 4 additions & 1 deletion hledger-lib/Hledger/Data/Posting.hs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,10 @@ postingAsLines elideamount onelineamounts acctwidth amtwidth p =
-- amtwidth at all.
shownAmounts
| elideamount = [mempty]
| otherwise = showMixedAmountLinesB noColour{displayZeroCommodity=True, displayOneLine=onelineamounts} $ pamount p
| otherwise = showMixedAmountLinesB displayopts $ pamount p
where displayopts = noColour{
displayZeroCommodity=True, displayAddDecimalMark=True, displayOneLine=onelineamounts
}
thisamtwidth = maximumBound 0 $ map wbWidth shownAmounts

-- when there is a balance assertion, show it only on the last posting line
Expand Down
26 changes: 26 additions & 0 deletions hledger/hledger.m4.md
Original file line number Diff line number Diff line change
Expand Up @@ -4340,6 +4340,32 @@ file.

# PART 3: REPORTING CONCEPTS


# Amount formatting

When displaying amounts, digit group marks and decimal marks are
handled a little differently depending on the report and output format
and intended consumer. hledger output falls into three rough categories:

**1. "hledger-readable output" should be readable by hledger and by humans**
- produced by reports that show full journal entries: `print`, `import`, `close`, `rewrite`..
- shows amounts with their original journal precisions, which may not be consistent
- adds a trailing decimal mark when needed to disambiguate [ambiguous amounts](decimal-marks-digit-group-marks)
(amounts with one digit group mark and no decimal digits)
- can be parsed reliably

**2. "human-readable output" - usually for humans**
- produced by all other reports
- shows amounts with standard display precisions, which will be consistent within each commodity
- can show ambiguous amounts
- can be parsed reliably in the context of a known report (because of consistent style)

**3. "machine-readable output" - usually for other software**
- produced by all reports when an output format like `csv`/`tsv`/`json`/`sql` is selected
- shows no digit group marks
- shows a period decimal mark (.) when there are decimal digits
- can be parsed reliably

# Time periods

<a name="report-period"></a>
Expand Down
41 changes: 41 additions & 0 deletions hledger/test/journal/precision.test
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,44 @@ $ hledger -f- print --explicit
$ hledger -f- reg
2023-01-01 (a) 1.0 A 1.0 A
2023-01-02 (a) 1.2 A 2.2 A

# ** 10. print-like reports add a trailing decimal mark, when amounts have digit group marks but no decimal digits.
<
commodity 1.000, JPY

2023-01-01
(a) 1 JPY

2023-01-02
(b) 1,2 JPY

2023-01-03
(c) 1.000 JPY

$ hledger -f - print
2023-01-01
(a) 1 JPY

2023-01-02
(b) 1,2 JPY

2023-01-03
(c) 1.000, JPY

>=

# ** 11. Non-print-like reports show all amounts with consistent display precision
$ hledger -f - bal
1 JPY a
1 JPY b
1.000 JPY c
--------------------
1.002 JPY

# ** 12. csv, json and other machine-readable formats show all amounts without digit groups and with period decimal marks.
$ hledger -f - bal -O csv
"account","balance"
"a","1 JPY"
"b","1 JPY"
"c","1000 JPY"
"total","1002 JPY"

0 comments on commit 1744021

Please sign in to comment.