From b7e5c05da2ff8e5a08ad353c1ec5dfb88a265f70 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Tue, 7 May 2024 15:44:06 -1000 Subject: [PATCH] imp: -I can now be overridden by -s or the check command This enables a "relaxed" workflow where you delay balance assertions checking until strict mode is turned on: always run hledger -I, and add -s when you're ready. --- hledger-lib/Hledger/Read/Common.hs | 29 ++++++++++---------- hledger/Hledger/Cli/CliOptions.hs | 4 +-- hledger/Hledger/Cli/Commands/Check.md | 9 +++--- hledger/test/journal/balance-assertions.test | 18 ++++++++++++ 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/hledger-lib/Hledger/Read/Common.hs b/hledger-lib/Hledger/Read/Common.hs index 719962dea..7d476edcb 100644 --- a/hledger-lib/Hledger/Read/Common.hs +++ b/hledger-lib/Hledger/Read/Common.hs @@ -320,19 +320,28 @@ initialiseAndParseJournal parser iopts f txt = -- Others (commodities, accounts) are done later by journalStrictChecks. -- journalFinalise :: InputOpts -> FilePath -> Text -> ParsedJournal -> ExceptT String IO Journal -journalFinalise iopts@InputOpts{..} f txt pj = do - t <- liftIO getPOSIXTime +journalFinalise iopts@InputOpts{auto_,balancingopts_,infer_costs_,infer_equity_,strict_,verbose_tags_,_ioDay} f txt pj = do let + BalancingOpts{commodity_styles_, ignore_assertions_} = balancingopts_ fname = "journalFinalise " <> takeFileName f lbl = lbl_ fname + -- We want to know when certain checks have been explicitly requested with the check command, + -- but it does not run until later. For now, hackily inspect the command line with unsafePerformIO. + checking checkname = "check" `elem` args && checkname `elem` args where args = progArgs + -- We will check ordered dates when "check ordereddates" is used. + checkordereddates = checking "ordereddates" + -- We will check balance assertions by default, unless -I is used, but always if -s or "check assertions" are used. + checkassertions = not ignore_assertions_ || strict_ || checking "assertions" + + t <- liftIO getPOSIXTime liftEither $ - pj{jglobalcommoditystyles=fromMaybe mempty $ commodity_styles_ balancingopts_} + pj{jglobalcommoditystyles=fromMaybe mempty commodity_styles_} & journalSetLastReadTime t -- save the last read time & 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 (but don't round) - should be done early - <&> journalAddForecast (verbose_tags_) (forecastPeriod iopts pj) -- Add forecast transactions if enabled + <&> 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. >>= journalMarkRedundantCosts -- Mark redundant costs, to help journalBalanceTransactions ignore them. -- (Later, journalInferEquityFromCosts will do a similar pass, adding missing equity postings.) @@ -344,16 +353,8 @@ journalFinalise iopts@InputOpts{..} f txt pj = do -- >>= Right . dbg0With (concatMap (T.unpack.showTransaction).jtxns) -- >>= \j -> deepseq (concatMap (T.unpack.showTransaction).jtxns $ j) (return j) <&> dbg9With (lbl "amounts after styling, forecasting, auto-posting".showJournalAmountsDebug) - - -- Ensure ordereddates is checked before balance assertions. - -- Currently ordereddates is not part of strict mode and can only be enabled by the check command, - -- and that will not run for a while yet. So for now, this dirty hack (uses unsafePerformIO): - >>= (\j -> let args = progArgs in - if "check" `elem` args && "ordereddates" `elem` args - then journalCheckOrdereddates j <&> const j - else Right j) -- the outer parentheses are needed - - >>= journalBalanceTransactions balancingopts_ -- infer balance assignments and missing amounts and (unless disabled) check balance assertions. + >>= (\j -> if checkordereddates then journalCheckOrdereddates j <&> const j else Right j) -- check ordereddates before assertions. The outer parentheses are needed. + >>= journalBalanceTransactions balancingopts_{ignore_assertions_=not checkassertions} -- infer balance assignments and missing amounts, and maybe check balance assertions. <&> dbg9With (lbl "amounts after transaction-balancing".showJournalAmountsDebug) -- <&> dbg9With (("journalFinalise amounts after styling, forecasting, auto postings, transaction balancing"<>).showJournalAmountsDebug) >>= journalInferCommodityStyles -- infer commodity styles once more now that all posting amounts are present diff --git a/hledger/Hledger/Cli/CliOptions.hs b/hledger/Hledger/Cli/CliOptions.hs index 4c54cfb39..80a0d5d06 100644 --- a/hledger/Hledger/Cli/CliOptions.hs +++ b/hledger/Hledger/Cli/CliOptions.hs @@ -157,8 +157,8 @@ inputflags = [ ,flagReq ["rules-file"] (\s opts -> Right $ setopt "rules-file" s opts) "RFILE" "CSV conversion rules file (default: FILE.rules)" ,flagReq ["alias"] (\s opts -> Right $ setopt "alias" s opts) "OLD=NEW" "rename accounts named OLD to NEW" ,flagReq ["pivot"] (\s opts -> Right $ setopt "pivot" s opts) "TAGNAME" "use some other field/tag for account names" - ,flagNone ["ignore-assertions","I"] (setboolopt "ignore-assertions") "ignore any balance assertions" - ,flagNone ["strict","s"] (setboolopt "strict") "do extra error checking (check that all posted accounts are declared)" + ,flagNone ["ignore-assertions","I"] (setboolopt "ignore-assertions") "don't check balance assertions by default" + ,flagNone ["strict","s"] (setboolopt "strict") "do extra error checks, incl. balance assertions" ] -- | Common report-related flags: --period, --cost, etc. diff --git a/hledger/Hledger/Cli/Commands/Check.md b/hledger/Hledger/Cli/Commands/Check.md index 73609b72d..6d4af57c2 100644 --- a/hledger/Hledger/Cli/Commands/Check.md +++ b/hledger/Hledger/Cli/Commands/Check.md @@ -6,7 +6,7 @@ _FLAGS hledger provides a number of built-in correctness checks to help validate your data and prevent errors. Some are run automatically, some when you enable `--strict` mode; -or you can run any of them on demand with this `check` command. +or you can run any of them on demand by providing them as arguments to the `check` command. `check` produces no output and a zero exit code if all is well. Eg: @@ -37,13 +37,14 @@ These important checks are performed by default, by almost all hledger commands: - **assertions** - all [balance assertions] in the journal are passing. Balance assertions are like canaries in your journal, they catch many problems. - This check sometimes gets in the way, eg when troubleshooting bookkeeping errors, - or parsing a journal fragment; you can disable it temporarily with `-I`/`--ignore-assertions`. + They can get in the way sometimes; you can disable them temporarily with `-I`/`--ignore-assertions` + (unless overridden with `-s`/`--strict` or `hledger check assertions`). ### Strict checks These additional checks are performed by any command when the `-s`/`--strict` flag is used ([strict mode]). -They provide extra error-catching power when you are serious about keeping your data clean and free of typos: +Strict mode always enables the balance assertions check, also. +These provide extra error-catching power when you are serious about keeping your data clean and free of typos: - **balanced** - like `autobalanced`, but in [conversion transactions](#recording-costs), costs must be written explicitly. This ensures some redundancy in the entry, which helps prevent typos. diff --git a/hledger/test/journal/balance-assertions.test b/hledger/test/journal/balance-assertions.test index c746e75da..66a164498 100755 --- a/hledger/test/journal/balance-assertions.test +++ b/hledger/test/journal/balance-assertions.test @@ -475,3 +475,21 @@ $ hledger -f- print -x date:2022-01-02 e $ hledger -f - check + +# ** 30. -s overrides -I +< +2024-01-01 + (a) 1 = 2 + +$ hledger -f - check -I -s +>2 /Balance assertion failed/ +>=1 + +# ** 31. hledger check assertions overrides -I +< +2024-01-01 + (a) 1 = 2 + +$ hledger -f - check assertions -I +>2 /Balance assertion failed/ +>=1