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, -- overJournalAmounts,
-- traverseJournalAmounts, -- traverseJournalAmounts,
-- journalCanonicalCommodities, -- journalCanonicalCommodities,
journalCommoditiesDeclared,
journalDateSpan, journalDateSpan,
journalStartDate, journalStartDate,
journalEndDate, journalEndDate,
@ -268,6 +269,10 @@ journalDescriptions = nubSort . map tdescription . jtxns
journalPostings :: Journal -> [Posting] journalPostings :: Journal -> [Posting]
journalPostings = concatMap tpostings . jtxns 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. -- | Sorted unique account names posted to by this journal's transactions.
journalAccountNamesUsed :: Journal -> [AccountName] journalAccountNamesUsed :: Journal -> [AccountName]
journalAccountNamesUsed = accountNamesFromPostings . journalPostings journalAccountNamesUsed = accountNamesFromPostings . journalPostings

View File

@ -148,6 +148,7 @@ import Text.Megaparsec.Custom
import Hledger.Data import Hledger.Data
import Hledger.Utils import Hledger.Utils
import Safe (headMay)
--- ** doctest setup --- ** doctest setup
-- $setup -- $setup
@ -331,6 +332,11 @@ journalFinalise InputOpts{auto_,ignore_assertions_,commoditystyles_,strict_} f t
-- If in strict mode, check all postings are to declared accounts -- If in strict mode, check all postings are to declared accounts
case if strict_ then journalCheckAccountsDeclared pj' else Right () of case if strict_ then journalCheckAccountsDeclared pj' else Right () of
Left e -> throwError e
Right () ->
-- and using declared commodities
case if strict_ then journalCheckCommoditiesDeclared pj' else Right () of
Left e -> throwError e Left e -> throwError e
Right () -> Right () ->
@ -370,13 +376,34 @@ journalCheckAccountsDeclared j = sequence_ $ map checkacct $ journalPostings j
checkacct Posting{paccount,ptransaction} checkacct Posting{paccount,ptransaction}
| paccount `elem` as = Right () | paccount `elem` as = Right ()
| otherwise = | otherwise =
Left $ "\nstrict mode: undeclared account \""++T.unpack paccount++"\" is posted to" Left $ "\nstrict mode: undeclared account \""++T.unpack paccount++"\""
++ case ptransaction of ++ case ptransaction of
Just Transaction{tsourcepos} -> "\n at: "++showGenericSourcePos tsourcepos Just Transaction{tsourcepos} -> "\nin transaction at: "++showGenericSourcePos tsourcepos
Nothing -> "" Nothing -> ""
where where
as = journalAccountNamesDeclared j 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 :: Year -> JournalParser m ()
setYear y = modify' (\j -> j{jparsedefaultyear=Just y}) 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". so 0.5 displayed with zero decimal digits is "0".
(More at [Commodity display style](#commodity-display-style).) (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 ### Default commodity
The `D` directive sets a default commodity, to be used for amounts without a commodity symbol (ie, plain numbers). 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 assets:bank:checking
``` ```
#### Account existence #### Account error checking
By default, accounts come into existence when a transaction references them. By default, accounts come into existence when a transaction references them by name.
This is convenient, but when you mis-spell an account name in a transaction, This is convenient, but it means hledger can't warn you when you mis-spell an account name in the journal.
hledger won't be able to detect it. Usually this isn't a big problem, as you'll Usually you'll find the error later, as an extra account in balance reports,
notice the error in balance reports, or when reconciling account balances. 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 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:
an account that has not been declared by an [account directive](#declaring-accounts).
Some things to note:
- The declaration is case-sensitive; transactions must use the correct account name capitalisation. - 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. - 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: With the `-s`/`--strict` flag, additional checks are performed:
- Are all accounts referenced by transactions declared with an account directive ? - Are all accounts posted to, declared with an `account` directive ?
([Account existence](journal.html#account-existence)) ([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> See also: <https://hledger.org/checking-for-errors.html>