diff --git a/hledger-lib/Hledger/Read/Common.hs b/hledger-lib/Hledger/Read/Common.hs index 6974fb613..96cb55742 100644 --- a/hledger-lib/Hledger/Read/Common.hs +++ b/hledger-lib/Hledger/Read/Common.hs @@ -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) diff --git a/hledger/Hledger/Cli/Commands/Print.hs b/hledger/Hledger/Cli/Commands/Print.hs index 49ea8d942..744c43d45 100644 --- a/hledger/Hledger/Cli/Commands/Print.hs +++ b/hledger/Hledger/Cli/Commands/Print.hs @@ -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 diff --git a/hledger/Hledger/Cli/Commands/Print.md b/hledger/Hledger/Cli/Commands/Print.md index c1fdc3fb0..cf7f170b2 100644 --- a/hledger/Hledger/Cli/Commands/Print.md +++ b/hledger/Hledger/Cli/Commands/Print.md @@ -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 diff --git a/hledger/test/journal/include.test b/hledger/test/journal/include.test index 3711bf9b9..a8cb6b556 100644 --- a/hledger/test/journal/include.test +++ b/hledger/test/journal/include.test @@ -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 diff --git a/hledger/test/journal/valuation.test b/hledger/test/journal/valuation.test index 37aa33ea6..d097f9934 100644 --- a/hledger/test/journal/valuation.test +++ b/hledger/test/journal/valuation.test @@ -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 diff --git a/hledger/test/json.test b/hledger/test/json.test index 9632b289e..fc19a5891 100644 --- a/hledger/test/json.test +++ b/hledger/test/json.test @@ -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" } } ] diff --git a/hledger/test/print/print-style.test b/hledger/test/print/print-style.test index d76dcb114..2af46780b 100644 --- a/hledger/test/print/print-style.test +++ b/hledger/test/print/print-style.test @@ -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 + +>= \ No newline at end of file