feat:print: add --round option for more control of precisions (#2085)

This commit is contained in:
Simon Michael 2023-09-13 06:28:01 +01:00
parent c7bcdfcdcf
commit 5a72b9e9ea
7 changed files with 218 additions and 106 deletions

View File

@ -324,7 +324,7 @@ journalFinalise iopts@InputOpts{..} f txt pj = do
& journalAddFile (f, txt) -- save the main file's info
& journalReverse -- convert all lists to the order they were parsed
& journalAddAccountTypes -- build a map of all known account types
& journalStyleAmounts -- Infer and apply commodity styles - should be done early
& journalStyleAmounts -- Infer and apply commodity styles (but don't round) - should be done early
<&> journalAddForecast (verbose_tags_) (forecastPeriod iopts pj) -- Add forecast transactions if enabled
<&> journalPostingsAddAccountTags -- Add account tags to postings, so they can be matched by auto postings.
>>= (if auto_ && not (null $ jtxnmodifiers pj)

View File

@ -16,7 +16,7 @@ module Hledger.Cli.Commands.Print (
where
import Data.List (intersperse)
import Data.List (intersperse, intercalate)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
@ -28,20 +28,32 @@ import Hledger.Read.CsvUtils (CSV, printCSV)
import Hledger.Cli.CliOptions
import Hledger.Cli.Utils
import System.Exit (exitFailure)
import qualified Data.Map as M (map)
import Safe (lastMay)
printmode = hledgerCommandMode
$(embedFileRelative "Hledger/Cli/Commands/Print.txt")
([let arg = "DESC" in
flagReq ["match","m"] (\s opts -> Right $ setopt "match" s opts) arg
("fuzzy search for one recent transaction with description closest to "++arg)
,flagNone ["explicit","x"] (setboolopt "explicit")
([flagNone ["explicit","x"] (setboolopt "explicit")
"show all amounts explicitly"
,flagNone ["show-costs"] (setboolopt "show-costs")
"show transaction prices even with conversion postings"
,flagReq ["round"] (\s opts -> Right $ setopt "round" s opts) "TYPE" $
intercalate "\n"
["how much rounding or padding should be done when displaying amounts ?"
,"none - show original decimal digits,"
," as in journal"
,"soft - just add or remove decimal zeros"
," to match precision (default)"
,"hard - round posting amounts to precision"
," (can unbalance transactions)"
,"all - also round cost amounts to precision"
," (can unbalance transactions)"
]
,flagNone ["new"] (setboolopt "new")
"show only newer-dated transactions added in each file since last run"
,let arg = "DESC" in
flagReq ["match","m"] (\s opts -> Right $ setopt "match" s opts) arg
("fuzzy search for one recent transaction with description closest to "++arg)
,outputFormatFlag ["txt","csv","json","sql"]
,outputFileFlag
])
@ -49,6 +61,18 @@ printmode = hledgerCommandMode
hiddenflags
([], Just $ argsFlag "[QUERY]")
-- | Get the --round option's value, if any. Can fail with a parse error.
roundFromRawOpts :: RawOpts -> Maybe Rounding
roundFromRawOpts = lastMay . collectopts roundfromrawopt
where
roundfromrawopt (n,v)
| n=="round", v=="none" = Just NoRounding
| n=="round", v=="soft" = Just SoftRounding
| n=="round", v=="hard" = Just HardRounding
| n=="round", v=="all" = Just AllRounding
| n=="round" = error' $ "--round's value should be none, soft, hard or all; got: "++v
| otherwise = Nothing
-- | Print journal transactions in standard format.
print' :: CliOpts -> Journal -> IO ()
print' opts j = do
@ -69,16 +93,22 @@ print' opts j = do
Nothing -> putStrLn "no matches found." >> exitFailure
printEntries :: CliOpts -> Journal -> IO ()
printEntries opts@CliOpts{reportspec_=rspec} j =
printEntries opts@CliOpts{rawopts_=rawopts, reportspec_=rspec} j =
writeOutputLazyText opts $ render $ entriesReport rspec j
where
stylesnorounding = M.map (amountStyleSetRounding NoRounding) $ journalCommodityStyles j
stylessoftrounding = M.map (amountStyleSetRounding SoftRounding) $ journalCommodityStyles j
-- print does user-specified rounding or (by default) no rounding, in all output formats
styles =
case roundFromRawOpts rawopts of
Nothing -> styles0
Just NoRounding -> styles0
Just r -> amountStylesSetRounding r styles0
where styles0 = journalCommodityStyles j
fmt = outputFormatFromOpts opts
render | fmt=="txt" = entriesReportAsText opts . styleAmounts stylesnorounding
| fmt=="csv" = printCSV . entriesReportAsCsv . styleAmounts stylessoftrounding
| fmt=="json" = toJsonText . styleAmounts stylessoftrounding
| fmt=="sql" = entriesReportAsSql . styleAmounts stylessoftrounding
render | fmt=="txt" = entriesReportAsText opts . styleAmounts styles
| fmt=="csv" = printCSV . entriesReportAsCsv . styleAmounts styles
| fmt=="json" = toJsonText . styleAmounts styles
| fmt=="sql" = entriesReportAsSql . styleAmounts styles
| otherwise = error' $ unsupportedOutputFormatError fmt -- PARTIAL:
entriesReportAsText :: CliOpts -> EntriesReport -> TL.Text

View File

@ -4,31 +4,19 @@ Show transaction journal entries, sorted by date.
_FLAGS
The print command displays full journal entries (transactions) from
the journal file, sorted by date
The print command displays full journal entries (transactions)
from the journal file, sorted by date
(or with `--date2`, by [secondary date](#secondary-dates)).
Amounts are shown mostly normalised to
[commodity display style](#commodity-display-styles),
eg the placement of commodity symbols will be consistent.
All of their decimal places are shown, as in the original journal entry
(with one alteration: in some cases trailing zeroes are added.)
Amounts are shown right-aligned within each transaction (but not across all transactions).
Directives and inter-transaction comments are not shown, currently.
This means the print command is somewhat lossy, and if you are using it to
reformat your journal you should take care to also copy over the directives
and file-level comments.
reformat/regenerate your journal you should take care to also copy over
the directives and inter-transaction comments.
Eg:
```shell
$ hledger print
2008/01/01 income
assets:bank:checking $1
income:salary $-1
$ hledger print -f examples/sample.journal date:200806
2008/06/01 gift
assets:bank:checking $1
income:gifts $-1
@ -42,12 +30,55 @@ $ hledger print
expenses:supplies $1
assets:cash $-2
2008/12/31 * pay off
liabilities:debts $1
assets:bank:checking $-1
```
print's output is usually a valid [hledger journal](https://hledger.org/hledger.html), and you can process it again with a second hledger command. This can be useful for certain kinds of search, eg:
### print explicitness
Normally, whether posting amounts are implicit or explicit is preserved.
For example, when an amount is omitted in the journal, it will not appear in the output.
Similarly, if a conversion cost is implied but not written, it will not appear in the output.
You can use the `-x`/`--explicit` flag to force explicit display of all amounts and costs.
This can be useful for troubleshooting or for making your journal more readable and
robust against data entry errors.
`-x` is also implied by using any of `-B`,`-V`,`-X`,`--value`.
The `-x`/`--explicit` flag will cause any postings with a multi-commodity amount
(which can arise when a multi-commodity transaction has an implicit amount)
to be split into multiple single-commodity postings,
keeping the output parseable.
### print amount style
Amounts are shown right-aligned within each transaction
(but not aligned across all transactions; you can do that with ledger-mode in Emacs).
Amounts will be (mostly) normalised to their [commodity display style](#commodity-display-styles):
their symbol placement, decimal mark, and digit group marks will be made consistent.
By default, decimal digits are shown as they are written in the journal.
With the `--round` option, `print` will try increasingly hard to
display decimal digits according to the [commodity display styles](#commodity-display-style):
- `--round=none` show amounts with original precisions (default)
- `--round=soft` add/remove decimal zeros in amounts (except costs)
- `--round=hard` round amounts (except costs), possibly hiding significant digits
- `--round=all` round all amounts and costs
`soft` is good for non-lossy cleanup, formatting amounts more
consistently where it's safe to do so.
`hard` and `all` can cause `print` to show invalid unbalanced journal entries;
they may be useful eg for journal cleanup, with manual fixups where needed.
### print parseability
print's output is usually a valid [hledger journal](#journal),
and you can process it again with a second hledger command.
This can be useful for certain kinds of search
(though the same can be achieved with `expr:` queries now):
```shell
# Show running total of food expenses paid from cash.
@ -61,31 +92,24 @@ There are some situations where print's output can become unparseable:
- [Auto postings](#auto-postings) can generate postings with [too many missing amounts](https://github.com/simonmichael/hledger/issues/1276).
- [Account aliases can generate bad account names](#aliases-can-generate-bad-account-names).
Normally, the journal entry's explicit or implicit amount style is preserved.
For example, when an amount is omitted in the journal, it will not appear in the output.
Similarly, when a cost is implied but not written, it will not appear in the output.
You can use the `-x`/`--explicit` flag to make all amounts and costs explicit,
which can be useful for troubleshooting or for making your journal more readable and
robust against data entry errors.
`-x` is also implied by using any of `-B`,`-V`,`-X`,`--value`.
Note, `-x`/`--explicit` will cause postings with a multi-commodity amount
(these can arise when a multi-commodity transaction has an implicit amount)
to be split into multiple single-commodity postings,
keeping the output parseable.
### print, other features
With `-B`/`--cost`, amounts with [costs](https://hledger.org/hledger.html#costs)
are converted to cost using that price. This can be used for troubleshooting.
are shown converted to cost.
With `-m DESC`/`--match=DESC`, print does a fuzzy search for one recent transaction
With `--new`, print shows only transactions it has not seen on a previous run.
This uses the same deduplication system as the [`import`](#import) command.
(See import's docs for details.)
With `-m DESC`/`--match=DESC`, print shows one recent transaction
whose description is most similar to DESC.
DESC should contain at least two characters.
If there is no similar-enough match,
no transaction will be shown and the program exit code will be non-zero.
With `--new`, hledger prints only transactions it has not seen on a previous run.
This uses the same deduplication system as the [`import`](#import) command.
(See import's docs for details.)
### print output format
This command also supports the
[output destination](hledger.html#output-destination) and

View File

@ -14,7 +14,7 @@ $ mkdir -p b/c/d ; printf '2010/1/1\n (D) 1\n' >b/c/d/d.journal ; printf '2010
>= 0
# 2. including other formats
# 2. Including other formats.
<
2016/1/1
(x) 1

View File

@ -111,7 +111,7 @@ P 2000/1/1 $ €1.20
$ hledger -f- print -V
2000-01-01
(a) €120.00 = $100
(a) €120 = $100
>=0

View File

@ -25,7 +25,8 @@ $ hledger -f- reg --output-format=json
"ascommodityspaced": true,
"asdecimalmark": ".",
"asdigitgroups": null,
"asprecision": 1
"asprecision": 1,
"asrounding": "HardRounding"
}
}
],
@ -53,7 +54,8 @@ $ hledger -f- reg --output-format=json
"ascommodityspaced": true,
"asdecimalmark": ".",
"asdigitgroups": null,
"asprecision": 1
"asprecision": 1,
"asrounding": "HardRounding"
}
}
]
@ -82,7 +84,8 @@ $ hledger -f- bal --output-format=json
"ascommodityspaced": true,
"asdecimalmark": ".",
"asdigitgroups": null,
"asprecision": 1
"asprecision": 1,
"asrounding": "HardRounding"
}
}
]
@ -102,7 +105,8 @@ $ hledger -f- bal --output-format=json
"ascommodityspaced": true,
"asdecimalmark": ".",
"asdigitgroups": null,
"asprecision": 1
"asprecision": 1,
"asrounding": "HardRounding"
}
}
]

View File

@ -1,41 +1,95 @@
# print amount styling tests
#
# The amounts are:
# amt - the posting amount
# cost - the posting amount's cost
# bal - the balance assertion amount
# balcost - the balance assertion amount's cost
# Here's an overview of historical behaviour.
# See the tests below for examples.
#
# Styling includes:
# basic - everything except precision
# prec - precision (number of decimal places)
# print shows four kinds of amount:
# amt - posting amount
# cost - posting amount's cost
# bal - balance assertion/assignment amount
# balcost - balance assertion/assignment amount's cost
#
# Historical behaviour:
# | hledger | amt basic | cost basic | bal basic | balcost basic | amt prec | cost prec | bal prec | balcost prec |
# | 1.14 | Y | N | N | N | Y | N | N | N |
# | 1.22 | Y | N | Y | N | N | N | N | N |
# | 1.30 | Y | Y | Y | N | N | N | N | N |
# | 1.31 | Y | Y | Y | Y | N | N | N | N |
#
# In the following,
# basic styling will move the commodity symbol to the left
# precision styling will hide the decimal place
# Which amounts does print do basic styling (eg symbol placement) on ?
#
# | hledger | amt | cost | bal | balcost |
# |-----------|-----|------|-----|---------|
# | 1.1-1.14 | Y | N | N | N |
# | 1.15-1.22 | Y | N | Y | N |
# | 1.23-1.30 | Y | Y | Y | N |
# | | | | | |
# | 1.31- | Y | Y | Y | Y |
#
# Which kind of rounding does print do on each amount ?
#
# | hledger | amt | cost | bal | balcost |
# |---------------------|------|------|------|---------|
# | 1.0-1.20 | hard | none | none | none |
# | 1.21-1.30 | soft | none | none | none |
# | 1.31 | none | none | none | none |
# | | | | | |
# | 1.31.1 | none | none | none | none |
# | 1.31.1 --round=soft | soft | none | soft | none |
# | 1.31.1 --round=hard | hard | none | hard | none |
# | 1.31.1 --round=all | hard | hard | hard | hard |
# 1. print styles all amounts, but leaves all precisions unchanged, even with -c.
# Four print style tests. In these, basic styling is applied
# to all amounts (the commodity symbol moves to the left),
# and precision styling is applied as described below.
<
commodity A 1000.
commodity B 1000.
; A and B styles
commodity A1000.00
commodity B1000.00
; a amounts have 1 significant digit
; b amounts have 1 significant digit and 2 zeros
; c amounts have 3 significant digits
2023-01-01
(a) 1.2 A @ 3.4 B = 1.2 A @ 3.4 B
$ hledger -f- print -c A1000.00 -c B1000.00
(a) 0.1 A @ 0.1 B = 0.1 A @ 0.1 B
(b) 0.100 A @ 0.100 B = 0.100 A @ 0.100 B
(c) 0.123 A @ 0.123 B = 0.123 A @ 0.123 B
# 1. By default, print shows all amounts with original precisions
# (like 1.31)
$ hledger -f- print
2023-01-01
(a) A1.2 @ B3.4 = A1.2 @ B3.4
(a) A0.1 @ B0.1 = A0.1 @ B0.1
(b) A0.100 @ B0.100 = A0.100 @ B0.100
(c) A0.123 @ B0.123 = A0.123 @ B0.123
>=
# 2. Precisions are also preserved when there's an implicit conversion (#2079).
# 2. With --round=soft, print adds/removes zeros in non-cost amounts
# (like 1.30 but more thorough, also affects balance assertion amounts,
# also does basic styling of balance assertion costs)
$ hledger -f- print --round=soft
2023-01-01
(a) A0.10 @ B0.1 = A0.10 @ B0.1
(b) A0.10 @ B0.100 = A0.10 @ B0.100
(c) A0.123 @ B0.123 = A0.123 @ B0.123
>=
# 3. With --round=hard, print rounds non-cost amounts.
$ hledger -f- print --round=hard
2023-01-01
(a) A0.10 @ B0.1 = A0.10 @ B0.1
(b) A0.10 @ B0.100 = A0.10 @ B0.100
(c) A0.12 @ B0.123 = A0.12 @ B0.123
>=
# 4. with --round=all, print rounds all amounts.
$ hledger -f- print --round=all
2023-01-01
(a) A0.10 @ B0.10 = A0.10 @ B0.10
(b) A0.10 @ B0.10 = A0.10 @ B0.10
(c) A0.12 @ B0.12 = A0.12 @ B0.12
>=
# 5. print also preserves precisions when there's an implicit conversion
# (unlike 1.30, #2079).
<
commodity A 1000.
@ -52,29 +106,7 @@ $ hledger -f- print
>=
# Maybe later:
# # 0. With print, -c has extra power: it can increase all amount precisions.
# $ hledger -f- print -c A1000.00 -c B1000.00
# 2023-01-01
# (a) A1.20 @ B3.40 = A1.20 @ B3.40
#
# >=
#
# # 0. And -c can decrease trailing decimal zeros. But not significant decimal digits
# (because that would change transactions).
# <
# 2023-01-01
# (a) 1.2340 A @ 3.4560 B = 1.234 A @ 3.456 B
#
# $ hledger -f- print -c A1000. -c B1000.
# 2023-01-01
# (a) A1.234 @ B3.456 = A1.234 @ B3.456
#
# >=
# 3. When showing digit group marks, print always shows a decimal mark as well,
# 6. When showing digit group marks, print always shows a decimal mark as well,
# even when no decimal digits are shown.
<
decimal-mark .
@ -87,3 +119,25 @@ $ hledger -f- print
>=
# 7. print shows zeros with a commodity symbol and decimal digits when possible.
# This also means that "multi-commodity zeros" are shown more verbosely.
<
2023-01-01
a A 0.00
b B 0.00
c
2023-01-02
d
$ hledger -f- print -x
2023-01-01
a A 0.00
b B 0.00
c A 0.00
c B 0.00
2023-01-02
d 0
>=