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.
This commit is contained in:
Simon Michael 2024-05-07 15:44:06 -10:00
parent 0fedc35a95
commit b7e5c05da2
4 changed files with 40 additions and 20 deletions

View File

@ -320,19 +320,28 @@ initialiseAndParseJournal parser iopts f txt =
-- Others (commodities, accounts) are done later by journalStrictChecks. -- Others (commodities, accounts) are done later by journalStrictChecks.
-- --
journalFinalise :: InputOpts -> FilePath -> Text -> ParsedJournal -> ExceptT String IO Journal journalFinalise :: InputOpts -> FilePath -> Text -> ParsedJournal -> ExceptT String IO Journal
journalFinalise iopts@InputOpts{..} f txt pj = do journalFinalise iopts@InputOpts{auto_,balancingopts_,infer_costs_,infer_equity_,strict_,verbose_tags_,_ioDay} f txt pj = do
t <- liftIO getPOSIXTime
let let
BalancingOpts{commodity_styles_, ignore_assertions_} = balancingopts_
fname = "journalFinalise " <> takeFileName f fname = "journalFinalise " <> takeFileName f
lbl = lbl_ fname 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 $ liftEither $
pj{jglobalcommoditystyles=fromMaybe mempty $ commodity_styles_ balancingopts_} pj{jglobalcommoditystyles=fromMaybe mempty commodity_styles_}
& journalSetLastReadTime t -- save the last read time & journalSetLastReadTime t -- save the last read time
& journalAddFile (f, txt) -- save the main file's info & journalAddFile (f, txt) -- save the main file's info
& journalReverse -- convert all lists to the order they were parsed & journalReverse -- convert all lists to the order they were parsed
& journalAddAccountTypes -- build a map of all known account types & journalAddAccountTypes -- build a map of all known account types
& journalStyleAmounts -- Infer and apply commodity styles (but don't round) - 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 <&> 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. <&> journalPostingsAddAccountTags -- Add account tags to postings, so they can be matched by auto postings.
>>= journalMarkRedundantCosts -- Mark redundant costs, to help journalBalanceTransactions ignore them. >>= journalMarkRedundantCosts -- Mark redundant costs, to help journalBalanceTransactions ignore them.
-- (Later, journalInferEquityFromCosts will do a similar pass, adding missing equity postings.) -- (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) -- >>= Right . dbg0With (concatMap (T.unpack.showTransaction).jtxns)
-- >>= \j -> deepseq (concatMap (T.unpack.showTransaction).jtxns $ j) (return j) -- >>= \j -> deepseq (concatMap (T.unpack.showTransaction).jtxns $ j) (return j)
<&> dbg9With (lbl "amounts after styling, forecasting, auto-posting".showJournalAmountsDebug) <&> dbg9With (lbl "amounts after styling, forecasting, auto-posting".showJournalAmountsDebug)
>>= (\j -> if checkordereddates then journalCheckOrdereddates j <&> const j else Right j) -- check ordereddates before assertions. The outer parentheses are needed.
-- Ensure ordereddates is checked before balance assertions. >>= journalBalanceTransactions balancingopts_{ignore_assertions_=not checkassertions} -- infer balance assignments and missing amounts, and maybe check 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.
<&> dbg9With (lbl "amounts after transaction-balancing".showJournalAmountsDebug) <&> dbg9With (lbl "amounts after transaction-balancing".showJournalAmountsDebug)
-- <&> dbg9With (("journalFinalise amounts after styling, forecasting, auto postings, 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 >>= journalInferCommodityStyles -- infer commodity styles once more now that all posting amounts are present

View File

@ -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 ["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 ["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" ,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 ["ignore-assertions","I"] (setboolopt "ignore-assertions") "don't check balance assertions by default"
,flagNone ["strict","s"] (setboolopt "strict") "do extra error checking (check that all posted accounts are declared)" ,flagNone ["strict","s"] (setboolopt "strict") "do extra error checks, incl. balance assertions"
] ]
-- | Common report-related flags: --period, --cost, etc. -- | Common report-related flags: --period, --cost, etc.

View File

@ -6,7 +6,7 @@ _FLAGS
hledger provides a number of built-in correctness checks to help validate your data and prevent errors. 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; 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. `check` produces no output and a zero exit code if all is well.
Eg: 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. - **assertions** - all [balance assertions] in the journal are passing.
Balance assertions are like canaries in your journal, they catch many problems. Balance assertions are like canaries in your journal, they catch many problems.
This check sometimes gets in the way, eg when troubleshooting bookkeeping errors, They can get in the way sometimes; you can disable them temporarily with `-I`/`--ignore-assertions`
or parsing a journal fragment; you can disable it temporarily with `-I`/`--ignore-assertions`. (unless overridden with `-s`/`--strict` or `hledger check assertions`).
### Strict checks ### Strict checks
These additional checks are performed by any command when the `-s`/`--strict` flag is used ([strict mode]). 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), - **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. costs must be written explicitly. This ensures some redundancy in the entry, which helps prevent typos.

View File

@ -475,3 +475,21 @@ $ hledger -f- print -x date:2022-01-02
e e
$ hledger -f - check $ 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