journal: strict mode also requires commodity declarations

This commit is contained in:
Simon Michael 2020-11-27 17:54:55 -08:00
parent d9b4446837
commit 8c23a16bf8
4 changed files with 76 additions and 39 deletions

View File

@ -52,6 +52,7 @@ module Hledger.Data.Journal (
-- overJournalAmounts,
-- traverseJournalAmounts,
-- journalCanonicalCommodities,
journalCommoditiesDeclared,
journalDateSpan,
journalStartDate,
journalEndDate,
@ -268,6 +269,10 @@ journalDescriptions = nubSort . map tdescription . jtxns
journalPostings :: Journal -> [Posting]
journalPostings = concatMap tpostings . jtxns
-- | Sorted unique commodity symbols declared by commodity directives in this journal.
journalCommoditiesDeclared :: Journal -> [AccountName]
journalCommoditiesDeclared = nubSort . M.keys . jcommodities
-- | Sorted unique account names posted to by this journal's transactions.
journalAccountNamesUsed :: Journal -> [AccountName]
journalAccountNamesUsed = accountNamesFromPostings . journalPostings

View File

@ -148,6 +148,7 @@ import Text.Megaparsec.Custom
import Hledger.Data
import Hledger.Utils
import Safe (headMay)
--- ** doctest setup
-- $setup
@ -334,33 +335,38 @@ journalFinalise InputOpts{auto_,ignore_assertions_,commoditystyles_,strict_} f t
Left e -> throwError e
Right () ->
-- Infer and apply canonical styles for each commodity (or throw an error).
-- This affects transaction balancing/assertions/assignments, so needs to be done early.
case journalApplyCommodityStyles pj' of
Left e -> throwError e
Right pj'' -> either throwError return $
pj''
& (if not auto_ || null (jtxnmodifiers pj'')
then
-- Auto postings are not active.
-- Balance all transactions and maybe check balance assertions.
journalBalanceTransactions (not ignore_assertions_)
else \j -> do -- Either monad
-- Auto postings are active.
-- Balance all transactions without checking balance assertions,
j' <- journalBalanceTransactions False j
-- then add the auto postings
-- (Note adding auto postings after balancing means #893b fails;
-- adding them before balancing probably means #893a, #928, #938 fail.)
case journalModifyTransactions d j' of
Left e -> throwError e
Right j'' -> do
-- then apply commodity styles once more, to style the auto posting amounts. (XXX inefficient ?)
j''' <- journalApplyCommodityStyles j''
-- then check balance assertions.
journalBalanceTransactions (not ignore_assertions_) j'''
)
& fmap journalInferMarketPricesFromTransactions -- infer market prices from commodity-exchanging transactions
-- and using declared commodities
case if strict_ then journalCheckCommoditiesDeclared pj' else Right () of
Left e -> throwError e
Right () ->
-- Infer and apply canonical styles for each commodity (or throw an error).
-- This affects transaction balancing/assertions/assignments, so needs to be done early.
case journalApplyCommodityStyles pj' of
Left e -> throwError e
Right pj'' -> either throwError return $
pj''
& (if not auto_ || null (jtxnmodifiers pj'')
then
-- Auto postings are not active.
-- Balance all transactions and maybe check balance assertions.
journalBalanceTransactions (not ignore_assertions_)
else \j -> do -- Either monad
-- Auto postings are active.
-- Balance all transactions without checking balance assertions,
j' <- journalBalanceTransactions False j
-- then add the auto postings
-- (Note adding auto postings after balancing means #893b fails;
-- adding them before balancing probably means #893a, #928, #938 fail.)
case journalModifyTransactions d j' of
Left e -> throwError e
Right j'' -> do
-- then apply commodity styles once more, to style the auto posting amounts. (XXX inefficient ?)
j''' <- journalApplyCommodityStyles j''
-- then check balance assertions.
journalBalanceTransactions (not ignore_assertions_) j'''
)
& fmap journalInferMarketPricesFromTransactions -- infer market prices from commodity-exchanging transactions
-- | Check that all the journal's postings are to accounts declared with
-- account directives, returning an error message otherwise.
@ -370,13 +376,34 @@ journalCheckAccountsDeclared j = sequence_ $ map checkacct $ journalPostings j
checkacct Posting{paccount,ptransaction}
| paccount `elem` as = Right ()
| otherwise =
Left $ "\nstrict mode: undeclared account \""++T.unpack paccount++"\" is posted to"
Left $ "\nstrict mode: undeclared account \""++T.unpack paccount++"\""
++ case ptransaction of
Just Transaction{tsourcepos} -> "\n at: "++showGenericSourcePos tsourcepos
Just Transaction{tsourcepos} -> "\nin transaction at: "++showGenericSourcePos tsourcepos
Nothing -> ""
where
as = journalAccountNamesDeclared j
-- | Check that all the commodities used in this journal's postings have been declared
-- by commodity directives, returning an error message otherwise.
journalCheckCommoditiesDeclared :: Journal -> Either String ()
journalCheckCommoditiesDeclared j =
sequence_ $ map checkcommodities $ journalPostings j
where
checkcommodities Posting{..} =
case mfirstundeclaredcomm of
Nothing -> Right ()
Just c -> Left $
"\nstrict mode: undeclared commodity \""++T.unpack c++"\""
++ case ptransaction of
Just Transaction{tsourcepos} -> "\nin transaction at: "++showGenericSourcePos tsourcepos
Nothing -> ""
where
mfirstundeclaredcomm =
headMay $ filter (not . (`elem` cs)) $ catMaybes $
(acommodity . baamount <$> pbalanceassertion) :
(map (Just . acommodity) $ amounts pamount)
cs = journalCommoditiesDeclared j
setYear :: Year -> JournalParser m ()
setYear y = modify' (\j -> j{jparsedefaultyear=Just y})

View File

@ -992,6 +992,11 @@ Note hledger normally uses
so 0.5 displayed with zero decimal digits is "0".
(More at [Commodity display style](#commodity-display-style).)
#### Commodity error checking
In [strict mode], enabled with the `-s`/`--strict` flag, hledger will report an error if a
commodity symbol is used that has not been declared by a [`commodity` directive](#declaring-commodities). This works similarly to [account error checking](#account-error-checking), see the notes there for more details.
### Default commodity
The `D` directive sets a default commodity, to be used for amounts without a commodity symbol (ie, plain numbers).
@ -1070,16 +1075,14 @@ The simplest form is just the word `account` followed by a hledger-style
account assets:bank:checking
```
#### Account existence
#### Account error checking
By default, accounts come into existence when a transaction references them.
This is convenient, but when you mis-spell an account name in a transaction,
hledger won't be able to detect it. Usually this isn't a big problem, as you'll
notice the error in balance reports, or when reconciling account balances.
By default, accounts come into existence when a transaction references them by name.
This is convenient, but it means hledger can't warn you when you mis-spell an account name in the journal.
Usually you'll find the error later, as an extra account in balance reports,
or an incorrect balance when reconciling.
When you want more error checking, you can enable [strict mode] with the `-s`/`--strict` flag. Then hledger will will report an error if any transaction references
an account that has not been declared by an [account directive](#declaring-accounts).
Some things to note:
In [strict mode], enabled with the `-s`/`--strict` flag, hledger will report an error if any transaction uses an account name that has not been declared by an [account directive](#declaring-accounts). Some notes:
- The declaration is case-sensitive; transactions must use the correct account name capitalisation.
- The account directive's scope is "whole file and below" (see [directives](#directives)). This means it affects all of the current file, and any files it includes, but not parent or sibling files. The position of account directives within the file does not matter, though it's usual to put them at the top.

View File

@ -773,8 +773,10 @@ easy journal files without a lot of declarations:
With the `-s`/`--strict` flag, additional checks are performed:
- Are all accounts referenced by transactions declared with an account directive ?
([Account existence](journal.html#account-existence))
- Are all accounts posted to, declared with an `account` directive ?
([Account error checking](journal.html#account-error-checking))
- Are all commodities declared with a `commodity` directive ?
([Commodity error checking](journal.html#commodity-error-checking))
See also: <https://hledger.org/checking-for-errors.html>