strict mode: -s/--strict requires posted accounts to be declared
This commit is contained in:
		
							parent
							
								
									ea0d4901ab
								
							
						
					
					
						commit
						ec3ad14ae5
					
				| @ -90,6 +90,9 @@ m4_define({{_inputoptions_}}, {{ | |||||||
| `-I --ignore-assertions` | `-I --ignore-assertions` | ||||||
| : disable balance assertion checks (note: does not disable balance assignments) | : disable balance assertion checks (note: does not disable balance assignments) | ||||||
| 
 | 
 | ||||||
|  | `-s --strict` | ||||||
|  | : do extra error checking (check that all posted accounts are declared) | ||||||
|  | 
 | ||||||
| }} )m4_dnl | }} )m4_dnl | ||||||
| m4_dnl | m4_dnl | ||||||
| m4_define({{_reportingoptions_}}, {{ | m4_define({{_reportingoptions_}}, {{ | ||||||
|  | |||||||
| @ -198,6 +198,7 @@ data InputOpts = InputOpts { | |||||||
|     ,pivot_             :: String               -- ^ use the given field's value as the account name |     ,pivot_             :: String               -- ^ use the given field's value as the account name | ||||||
|     ,auto_              :: Bool                 -- ^ generate automatic postings when journal is parsed |     ,auto_              :: Bool                 -- ^ generate automatic postings when journal is parsed | ||||||
|     ,commoditystyles_   :: Maybe (M.Map CommoditySymbol AmountStyle) -- ^ optional commodity display styles affecting all files |     ,commoditystyles_   :: Maybe (M.Map CommoditySymbol AmountStyle) -- ^ optional commodity display styles affecting all files | ||||||
|  |     ,strict_            :: Bool                 -- ^ do extra error checking (eg, all posted accounts are declared) | ||||||
|  } deriving (Show) |  } deriving (Show) | ||||||
| 
 | 
 | ||||||
| instance Default InputOpts where def = definputopts | instance Default InputOpts where def = definputopts | ||||||
| @ -214,6 +215,7 @@ definputopts = InputOpts | |||||||
|     , pivot_             = "" |     , pivot_             = "" | ||||||
|     , auto_              = False |     , auto_              = False | ||||||
|     , commoditystyles_   = Nothing |     , commoditystyles_   = Nothing | ||||||
|  |     , strict_            = False | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| rawOptsToInputOpts :: RawOpts -> InputOpts | rawOptsToInputOpts :: RawOpts -> InputOpts | ||||||
| @ -229,6 +231,7 @@ rawOptsToInputOpts rawopts = InputOpts{ | |||||||
|   ,pivot_             = stringopt "pivot" rawopts |   ,pivot_             = stringopt "pivot" rawopts | ||||||
|   ,auto_              = boolopt "auto" rawopts |   ,auto_              = boolopt "auto" rawopts | ||||||
|   ,commoditystyles_   = Nothing |   ,commoditystyles_   = Nothing | ||||||
|  |   ,strict_            = boolopt "strict" rawopts | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| --- ** parsing utilities | --- ** parsing utilities | ||||||
| @ -317,7 +320,7 @@ parseAndFinaliseJournal' parser iopts f txt = do | |||||||
| -- - infer transaction-implied market prices from transaction prices | -- - infer transaction-implied market prices from transaction prices | ||||||
| -- | -- | ||||||
| journalFinalise :: InputOpts -> FilePath -> Text -> ParsedJournal -> ExceptT String IO Journal | journalFinalise :: InputOpts -> FilePath -> Text -> ParsedJournal -> ExceptT String IO Journal | ||||||
| journalFinalise InputOpts{auto_,ignore_assertions_,commoditystyles_} f txt pj = do | journalFinalise InputOpts{auto_,ignore_assertions_,commoditystyles_,strict_} f txt pj = do | ||||||
|   t <- liftIO getClockTime |   t <- liftIO getClockTime | ||||||
|   d <- liftIO getCurrentDay |   d <- liftIO getCurrentDay | ||||||
|   let pj' = |   let pj' = | ||||||
| @ -326,33 +329,53 @@ journalFinalise InputOpts{auto_,ignore_assertions_,commoditystyles_} f txt pj = | |||||||
|         & journalSetLastReadTime t -- save the last read time |         & journalSetLastReadTime t -- save the last read time | ||||||
|         & journalReverse -- convert all lists to the order they were parsed |         & journalReverse -- convert all lists to the order they were parsed | ||||||
| 
 | 
 | ||||||
|   -- Infer and apply canonical styles for each commodity (or throw an error). |   -- If in strict mode, check all postings are to declared accounts | ||||||
|   -- This affects transaction balancing/assertions/assignments, so needs to be done early. |   case if strict_ then journalCheckAccountsDeclared pj' else Right () of | ||||||
|   case journalApplyCommodityStyles pj' of |     Left e   -> throwError e | ||||||
|     Left e     -> throwError e |     Right () -> | ||||||
|     Right pj'' -> either throwError return $ | 
 | ||||||
|       pj'' |       -- Infer and apply canonical styles for each commodity (or throw an error). | ||||||
|       & (if not auto_ || null (jtxnmodifiers pj') |       -- This affects transaction balancing/assertions/assignments, so needs to be done early. | ||||||
|          then |       case journalApplyCommodityStyles pj' of | ||||||
|            -- Auto postings are not active. |         Left e     -> throwError e | ||||||
|            -- Balance all transactions and maybe check balance assertions. |         Right pj'' -> either throwError return $ | ||||||
|            journalBalanceTransactions (not ignore_assertions_) |           pj'' | ||||||
|          else \j -> do  -- Either monad |           & (if not auto_ || null (jtxnmodifiers pj'') | ||||||
|            -- Auto postings are active. |             then | ||||||
|            -- Balance all transactions without checking balance assertions, |               -- Auto postings are not active. | ||||||
|            j' <- journalBalanceTransactions False j |               -- Balance all transactions and maybe check balance assertions. | ||||||
|            -- then add the auto postings |               journalBalanceTransactions (not ignore_assertions_) | ||||||
|            -- (Note adding auto postings after balancing means #893b fails; |             else \j -> do  -- Either monad | ||||||
|            -- adding them before balancing probably means #893a, #928, #938 fail.) |               -- Auto postings are active. | ||||||
|            case journalModifyTransactions d j' of |               -- Balance all transactions without checking balance assertions, | ||||||
|              Left e -> throwError e |               j' <- journalBalanceTransactions False j | ||||||
|              Right j'' -> do |               -- then add the auto postings | ||||||
|                -- then apply commodity styles once more, to style the auto posting amounts. (XXX inefficient ?) |               -- (Note adding auto postings after balancing means #893b fails; | ||||||
|                j''' <- journalApplyCommodityStyles j'' |               -- adding them before balancing probably means #893a, #928, #938 fail.) | ||||||
|                -- then check balance assertions. |               case journalModifyTransactions d j' of | ||||||
|                journalBalanceTransactions (not ignore_assertions_) j''' |                 Left e -> throwError e | ||||||
|         ) |                 Right j'' -> do | ||||||
|      & fmap journalInferMarketPricesFromTransactions  -- infer market prices from commodity-exchanging transactions |                   -- 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. | ||||||
|  | journalCheckAccountsDeclared :: Journal -> Either String () | ||||||
|  | journalCheckAccountsDeclared j = sequence_ $ map checkacct $ journalPostings j | ||||||
|  |   where | ||||||
|  |     checkacct Posting{paccount,ptransaction} | ||||||
|  |       | paccount `elem` as = Right () | ||||||
|  |       | otherwise          =  | ||||||
|  |           Left $ "\nstrict mode: undeclared account \""++T.unpack paccount++"\" is posted to" | ||||||
|  |             ++ case ptransaction of | ||||||
|  |                 Just Transaction{tsourcepos} -> "\n at: "++showGenericSourcePos tsourcepos | ||||||
|  |                 Nothing -> "" | ||||||
|  |       where | ||||||
|  |         as = journalAccountNamesDeclared 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}) | ||||||
|  | |||||||
| @ -1046,24 +1046,45 @@ in another commodity. See [Valuation](hledger.html#valuation). | |||||||
| 
 | 
 | ||||||
| ### Declaring accounts | ### Declaring accounts | ||||||
| 
 | 
 | ||||||
| `account` directives can be used to pre-declare accounts. | `account` directives can be used to declare accounts  | ||||||
| Though not required, they can provide several benefits: | (ie, the places that amounts are transferred from and to). | ||||||
|  | Though not required, these declarations can provide several benefits: | ||||||
| 
 | 
 | ||||||
| - They can document your intended chart of accounts, providing a reference. | - They can document your intended chart of accounts, providing a reference. | ||||||
| - They can store extra information about accounts (account numbers, notes, etc.) |  | ||||||
| - They can help hledger know your accounts' types (asset, liability, equity, revenue, expense), | - They can help hledger know your accounts' types (asset, liability, equity, revenue, expense), | ||||||
|   useful for reports like balancesheet and incomestatement. |   useful for reports like balancesheet and incomestatement. | ||||||
| - They control account display order in reports, allowing non-alphabetic sorting | - They control account display order in reports, allowing non-alphabetic sorting | ||||||
|   (eg Revenues to appear above Expenses). |   (eg Revenues to appear above Expenses). | ||||||
|  | - They can store extra information about accounts (account numbers, notes, etc.) | ||||||
| - They help with account name completion | - They help with account name completion | ||||||
|   in the add command, hledger-iadd, hledger-web, ledger-mode etc. |   in the add command, hledger-iadd, hledger-web, ledger-mode etc. | ||||||
|  | - In [strict mode], they restrict which accounts may be posted to by transactions, | ||||||
|  |   which helps detect typos. | ||||||
|  | 
 | ||||||
|  | [strict mode]: hledger.html#strict-mode | ||||||
| 
 | 
 | ||||||
| The simplest form is just the word `account` followed by a hledger-style | The simplest form is just the word `account` followed by a hledger-style | ||||||
| [account name](journal.html#account-names), eg: | [account name](journal.html#account-names), eg this account directive declares the `assets:bank:checking` account:  | ||||||
|  | 
 | ||||||
| ```journal | ```journal | ||||||
| account assets:bank:checking | account assets:bank:checking | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | #### Account existence | ||||||
|  | 
 | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | 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. Some things to note: | ||||||
|  | 
 | ||||||
|  | - 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. | ||||||
|  | - Accounts can only be declared in `journal` files (but will affect included files in other formats). | ||||||
|  | - It's currently not possible to declare "all possible subaccounts" with a wildcard; every account posted to must be declared. | ||||||
|  | 
 | ||||||
| #### Account comments | #### Account comments | ||||||
| 
 | 
 | ||||||
| [Comments](#comments), beginning with a semicolon, can be added: | [Comments](#comments), beginning with a semicolon, can be added: | ||||||
|  | |||||||
| @ -125,6 +125,7 @@ inputflags = [ | |||||||
|  ,flagNone ["anon"]          (setboolopt "anon") "anonymize accounts and payees" |  ,flagNone ["anon"]          (setboolopt "anon") "anonymize accounts and payees" | ||||||
|  ,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") "ignore any balance assertions" | ||||||
|  |  ,flagNone ["strict","s"]    (setboolopt "strict") "do extra error checking (check that all posted accounts are declared)" | ||||||
|  ] |  ] | ||||||
| 
 | 
 | ||||||
| -- | Common report-related flags: --period, --cost, etc. | -- | Common report-related flags: --period, --cost, etc. | ||||||
|  | |||||||
| @ -761,6 +761,22 @@ If you need either of those things, you can | |||||||
| - use a single parent file which [includes](journal.html#including-other-files) the others | - use a single parent file which [includes](journal.html#including-other-files) the others | ||||||
| - or concatenate the files into one before reading, eg: `cat a.journal b.journal | hledger -f- CMD`. | - or concatenate the files into one before reading, eg: `cat a.journal b.journal | hledger -f- CMD`. | ||||||
| 
 | 
 | ||||||
|  | ## Strict mode | ||||||
|  | 
 | ||||||
|  | hledger checks input files for valid data. | ||||||
|  | By default, the most important errors are detected, while still accepting | ||||||
|  | easy journal files without a lot of declarations: | ||||||
|  | 
 | ||||||
|  | - Are the input files parseable, with valid syntax ? | ||||||
|  | - Are all transactions balanced ? | ||||||
|  | - Do all balance assertions pass ? | ||||||
|  | 
 | ||||||
|  | With the `-s`/`--strict` flag, additional checks are performed: | ||||||
|  | 
 | ||||||
|  | - Are all accounts referenced by transactions declared with an account directive ? | ||||||
|  | 
 | ||||||
|  | *experimental.* | ||||||
|  | 
 | ||||||
| ## Output destination | ## Output destination | ||||||
| 
 | 
 | ||||||
| hledger commands send their output to the terminal by default. | hledger commands send their output to the terminal by default. | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user