journal: account types: add Cash, tweak fallback regexp, rewrite docs

This commit is contained in:
Simon Michael 2020-07-15 17:04:37 -07:00
parent 30f02b0882
commit fd9171df07
4 changed files with 119 additions and 60 deletions

View File

@ -296,29 +296,6 @@ journalAccountNameTree = accountNameTreeFrom . journalAccountNames
-- queries for standard account types -- queries for standard account types
-- | Get a query for accounts of a certain type (Asset, Liability..) in this journal.
-- The query will match all accounts which were declared as that type by account directives,
-- plus all their subaccounts which have not been declared as a different type.
-- If no accounts were declared as this type, the query will instead match accounts
-- with names matched by the provided case-insensitive regular expression.
journalAccountTypeQuery :: AccountType -> Regexp -> Journal -> Query
journalAccountTypeQuery atype fallbackregex j =
case M.lookup atype (jdeclaredaccounttypes j) of
Nothing -> Acct fallbackregex
Just as ->
-- XXX Query isn't able to match account type since that requires extra info from the journal.
-- So we do a hacky search by name instead.
And [
Or $ map (Acct . accountNameToAccountRegex) as
,Not $ Or $ map (Acct . accountNameToAccountRegex) differentlytypedsubs
]
where
differentlytypedsubs = concat
[subs | (t,bs) <- M.toList (jdeclaredaccounttypes j)
, t /= atype
, let subs = [b | b <- bs, any (`isAccountNamePrefixOf` b) as]
]
-- | A query for accounts in this journal which have been -- | A query for accounts in this journal which have been
-- declared as Asset by account directives, or otherwise for -- declared as Asset by account directives, or otherwise for
-- accounts with names matched by the case-insensitive regular expression -- accounts with names matched by the case-insensitive regular expression
@ -369,12 +346,43 @@ journalProfitAndLossAccountQuery j = Or [journalRevenueAccountQuery j
,journalExpenseAccountQuery j ,journalExpenseAccountQuery j
] ]
-- | A query for Cash (-equivalent) accounts in this journal (ie, -- | A query for "Cash" (liquid asset) accounts in this journal (ie,
-- accounts which appear on the cashflow statement.) This is currently -- accounts which appear on the cashflow statement.) This is the
-- hard-coded to be all the Asset accounts except for those with names -- accounts declared to be Cash type, or if none of these are
-- containing the case-insensitive regular expression @(receivable|:A/R|:fixed)@. -- declared, the Asset accounts whose names do not contain the
-- case-insensitive regular expression @(investment|receivable|:A/R|:fixed)@.
journalCashAccountQuery :: Journal -> Query journalCashAccountQuery :: Journal -> Query
journalCashAccountQuery j = And [journalAssetAccountQuery j, Not $ Acct "(receivable|:A/R|:fixed)"] journalCashAccountQuery j =
case M.lookup Cash (jdeclaredaccounttypes j) of
Just _ -> journalAccountTypeQuery Cash notused j
where notused = error' "journalCashAccountQuery: this should not have happened!"
Nothing -> And
[journalAssetAccountQuery j
,Not $ Acct "(investment|receivable|:A/R|:fixed)"
]
-- | Get a query for accounts of a certain type (Asset, Liability..) in this journal.
-- The query will match all accounts which were declared as that type by account directives,
-- plus all their subaccounts which have not been declared as a different type.
-- If no accounts were declared as this type, the query will instead match accounts
-- with names matched by the provided case-insensitive regular expression.
journalAccountTypeQuery :: AccountType -> Regexp -> Journal -> Query
journalAccountTypeQuery atype fallbackregex j =
case M.lookup atype (jdeclaredaccounttypes j) of
Nothing -> Acct fallbackregex
Just as ->
-- XXX Query isn't able to match account type since that requires extra info from the journal.
-- So we do a hacky search by name instead.
And [
Or $ map (Acct . accountNameToAccountRegex) as
,Not $ Or $ map (Acct . accountNameToAccountRegex) differentlytypedsubs
]
where
differentlytypedsubs = concat
[subs | (t,bs) <- M.toList (jdeclaredaccounttypes j)
, t /= atype
, let subs = [b | b <- bs, any (`isAccountNamePrefixOf` b) as]
]
-- Various kinds of filtering on journals. We do it differently depending -- Various kinds of filtering on journals. We do it differently depending
-- on the command. -- on the command.

View File

@ -121,6 +121,7 @@ data AccountType =
| Equity | Equity
| Revenue | Revenue
| Expense | Expense
| Cash -- ^ a subtype of Asset - liquid assets to show in cashflow report
deriving (Show,Eq,Ord,Data,Generic) deriving (Show,Eq,Ord,Data,Generic)
instance NFData AccountType instance NFData AccountType

View File

@ -380,10 +380,12 @@ parseAccountTypeCode s =
"r" -> Right Revenue "r" -> Right Revenue
"expense" -> Right Expense "expense" -> Right Expense
"x" -> Right Expense "x" -> Right Expense
"cash" -> Right Cash
"c" -> Right Cash
_ -> Left err _ -> Left err
where where
err = "invalid account type code "++T.unpack s++", should be one of " ++ err = "invalid account type code "++T.unpack s++", should be one of " ++
(intercalate ", " $ ["A","L","E","R","X","ASSET","LIABILITY","EQUITY","REVENUE","EXPENSE"]) (intercalate ", " $ ["A","L","E","R","X","C","Asset","Liability","Equity","Revenue","Expense","Cash"])
-- Add an account declaration to the journal, auto-numbering it. -- Add an account declaration to the journal, auto-numbering it.
addAccountDeclaration :: (AccountName,Text,[Tag]) -> JournalParser m () addAccountDeclaration :: (AccountName,Text,[Tag]) -> JournalParser m ()

View File

@ -1087,37 +1087,88 @@ account ACCTNAME [ACCTTYPE] [;COMMENT]
#### Account types #### Account types
hledger recognises five types (or classes) of account: Asset, Liability, Equity, Revenue, Expense. hledger recognises five main types of account,
This is used by a few accounting-aware reports such as [balancesheet][], [incomestatement][] and [cashflow][]. corresponding to the account classes in the [accounting equation][]:
[balancesheet]: hledger.html#balancesheet `Asset`, `Liability`, `Equity`, `Revenue`, `Expense`.
[cashflow]: hledger.html#cashflow
[incomestatement]: hledger.html#incomestatement These account types are important for controlling which accounts
appear in the [balancesheet][], [balancesheetequity][],
[incomestatement][] reports (and probably for other things in future).
There is also the `Cash` type, which is a subtype of `Asset`,
and which causes accounts to appear in the [cashflow][] report.
("Cash" here means [liquid assets][CCE], eg typically bank balances
but not investments or receivables.)
##### Declaring account types
Generally, to make these reports work you should declare your
top-level accounts and their types,
using [account directives](#declaring-accounts)
with `type:` [tags](journal.html#tags).
The tag's value should be one of:
`Asset`, `Liability`, `Equity`, `Revenue`, `Expense`, `Cash`,
`A`, `L`, `E`, `R`, `X`, `C` (all case insensitive).
The type is inherited by all subaccounts except where they override it.
Here's a complete example:
```journal
account assets ; type: Asset
account assets:bank ; type: Cash
account assets:cash ; type: Cash
account liabilities ; type: Liability
account equity ; type: Equity
account revenues ; type: Revenue
account expenses ; type: Expense
```
##### Auto-detected account types ##### Auto-detected account types
If you name your top-level accounts with some variation of If you happen to use common english top-level account names, you may
`assets`, `liabilities`/`debts`, `equity`, `revenues`/`income`, or `expenses`, not need to declare account types, as they will be detected
their types are detected automatically. automatically using the following rules:
##### Account types declared with tags | If name matches [regular expression][]: | account type is:
|-----------------------------------------|-----------------
| `^assets?(:|$)` | `Asset`
| `^(debts?|liabilit(y|ies))(:|$)` | `Liability`
| `^equity(:|$)` | `Equity`
| `^(income|revenue)s?(:|$)` | `Revenue`
| `^expenses?(:|$)` | `Expense`
| If account type is `Asset` and name does not contain regular expression: | account type is:
|--------------------------------------------------------------------------|-----------------
| `(investment|receivable|:A/R|:fixed)` | `Cash`
Even so, explicit declarations may be a good idea, for clarity and
predictability.
##### Interference from auto-detected account types
If you assign any account type, it's a good idea to assign all of
them, to prevent any confusion from mixing declared and auto-detected
types. Although it's unlikely to happen in real life, here's an
example: with the following journal, `balancesheetequity` shows
"liabilities" in both Liabilities and Equity sections. Declaring another
account as `type:Liability` would fix it:
More generally, you can declare an account's type with an account directive,
by writing a `type:` [tag](journal.html#tags) in a comment, followed by one of the words
`Asset`, `Liability`, `Equity`, `Revenue`, `Expense`,
or one of the letters `ALERX` (case insensitive):
```journal ```journal
account assets ; type:Asset account liabilities ; type:Equity
account liabilities ; type:Liability
account equity ; type:Equity 2020-01-01
account revenues ; type:Revenue assets 1
account expenses ; type:Expense liabilities 1
equity -2
``` ```
##### Account types declared with account type codes ##### Old account type syntax
In some hledger journals you might instead see this old syntax (the
letters ALERX, separated from the account name by two or more spaces);
this is deprecated and may be removed soon:
Or, you can write one of those letters separated from the account name by two or more spaces,
but this should probably be considered deprecated as of hledger 1.13:
```journal ```journal
account assets A account assets A
account liabilities L account liabilities L
@ -1126,18 +1177,15 @@ account revenues R
account expenses X account expenses X
``` ```
##### Overriding auto-detected types
If you ever override the types of those auto-detected english account names mentioned above, [balancesheet]: hledger.html#balancesheet
you might need to help the reports a bit. Eg: [balancesheetequity]: hledger.html#balancesheetequity
```journal [cashflow]: hledger.html#cashflow
; make "liabilities" not have the liability type - who knows why [incomestatement]: hledger.html#incomestatement
account liabilities ; type:E [CCE]: https://en.wikipedia.org/wiki/Cash_and_cash_equivalents
[regular expression]: hledger.html#regular-expressions
[account equation]: https://en.wikipedia.org/wiki/Accounting_equation
; we need to ensure some other account has the liability type,
; otherwise balancesheet would still show "liabilities" under Liabilities
account - ; type:L
```
#### Account display order #### Account display order