journal: account directives can declare account types

Previously you had to use one of the standard english account names
(assets, liabilities..) for top-level accounts, if you wanted to use
the bs/bse/cf/is commands.
Now, account directives can specify which of the big five categories
an account belongs to - asset, liability, equity, revenue or expense -
by writing one of the letters A, L, E, R or X two or more spaces after
the account name (where the numeric account code used to be).

This might change. Some thoughts influencing the current syntax:
- easy to type and read
- does not require multiple lines
- does not depend on any particular account numbering scheme
- allows more types later if needed
- still anglocentric, but only a little
- could be treated as syntactic sugar for account tags later
- seems to be compatible with (ignored by) current Ledger

The current design permits unlimited account type declarations anywhere
in the account tree. So you could declare a liability account somewhere
under assets, and maybe a revenue account under that, and another asset
account even further down. In such cases you start to see oddities like
accounts appearing in multiple places in a tree-mode report. In theory
the reports will still behave reasonably, but this has not been tested
too hard. In any case this is clearly too much freedom. I have left it
this way, for now, in case it helps with:

- modelling contra accounts ?
- multiple files. I suspect the extra expressiveness may come in handy
  when combining multiple files with account type declarations,
  rewriting account names, apply parent accounts etc.
  If we only allowed type declarations on top-level accounts, or
  only allowed a single account of each type, complications seem likely.
This commit is contained in:
Simon Michael 2018-09-26 15:34:48 -10:00
parent 678e8c28e4
commit c1236fa6e9
4 changed files with 55 additions and 8 deletions

View File

@ -165,13 +165,14 @@ instance Sem.Semigroup Journal where
,jparsetimeclockentries = jparsetimeclockentries j1 <> jparsetimeclockentries j2
,jincludefilestack = jincludefilestack j2
,jdeclaredaccounts = jdeclaredaccounts j1 <> jdeclaredaccounts j2
,jdeclaredaccounttypes = jdeclaredaccounttypes j1 <> jdeclaredaccounttypes j2
,jcommodities = jcommodities j1 <> jcommodities j2
,jinferredcommodities = jinferredcommodities j1 <> jinferredcommodities j2
,jmarketprices = jmarketprices j1 <> jmarketprices j2
,jtxnmodifiers = jtxnmodifiers j1 <> jtxnmodifiers j2
,jperiodictxns = jperiodictxns j1 <> jperiodictxns j2
,jtxns = jtxns j1 <> jtxns j2
,jfinalcommentlines = jfinalcommentlines j2
,jfinalcommentlines = jfinalcommentlines j2 -- XXX discards j1's ?
,jfiles = jfiles j1 <> jfiles j2
,jlastreadtime = max (jlastreadtime j1) (jlastreadtime j2)
}
@ -193,8 +194,9 @@ nulljournal = Journal {
,jparsetimeclockentries = []
,jincludefilestack = []
,jdeclaredaccounts = []
,jcommodities = M.fromList []
,jinferredcommodities = M.fromList []
,jdeclaredaccounttypes = M.empty
,jcommodities = M.empty
,jinferredcommodities = M.empty
,jmarketprices = []
,jtxnmodifiers = []
,jperiodictxns = []

View File

@ -113,7 +113,26 @@ instance Default Interval where def = NoInterval
instance NFData Interval
type AccountName = Text
type AccountCode = Int
data AccountType =
Asset
| Liability
| Equity
| Revenue
| Expense
deriving (Show,Eq,Ord,Data,Generic)
instance NFData AccountType
-- not worth the trouble, letters defined in accountdirectivep for now
--instance Read AccountType
-- where
-- readsPrec _ ('A' : xs) = [(Asset, xs)]
-- readsPrec _ ('L' : xs) = [(Liability, xs)]
-- readsPrec _ ('E' : xs) = [(Equity, xs)]
-- readsPrec _ ('R' : xs) = [(Revenue, xs)]
-- readsPrec _ ('X' : xs) = [(Expense, xs)]
-- readsPrec _ _ = []
data AccountAlias = BasicAlias AccountName AccountName
| RegexAlias Regexp Replacement
@ -369,6 +388,7 @@ data Journal = Journal {
,jincludefilestack :: [FilePath]
-- principal data
,jdeclaredaccounts :: [AccountName] -- ^ Accounts declared by account directives, in parse order (after journal finalisation)
,jdeclaredaccounttypes :: M.Map AccountType [AccountName] -- ^ Accounts whose type has been declared in account directives (usually 5 top-level accounts)
,jcommodities :: M.Map CommoditySymbol Commodity -- ^ commodities and formats declared by commodity directives
,jinferredcommodities :: M.Map CommoditySymbol AmountStyle -- ^ commodities and formats inferred from journal amounts TODO misnamed - jusedstyles
,jmarketprices :: [MarketPrice]

View File

@ -43,6 +43,7 @@ module Hledger.Read.Common (
getDefaultAmountStyle,
getAmountStyle,
pushDeclaredAccount,
addDeclaredAccountType,
pushParentAccount,
popParentAccount,
getParentAccount,
@ -311,6 +312,10 @@ getAmountStyle commodity = do
pushDeclaredAccount :: AccountName -> JournalParser m ()
pushDeclaredAccount acct = modify' (\j -> j{jdeclaredaccounts = acct : jdeclaredaccounts j})
addDeclaredAccountType :: AccountName -> AccountType -> JournalParser m ()
addDeclaredAccountType acct atype =
modify' (\j -> j{jdeclaredaccounttypes = M.insertWith (++) atype [acct] (jdeclaredaccounttypes j)})
pushParentAccount :: AccountName -> JournalParser m ()
pushParentAccount acct = modify' (\j -> j{jparseparentaccounts = acct : jparseparentaccounts j})

View File

@ -69,6 +69,7 @@ import Control.Monad
import Control.Monad.Except (ExceptT(..))
import Control.Monad.State.Strict
import Data.Bifunctor (first)
import Data.Maybe
import qualified Data.Map.Strict as M
import Data.Text (Text)
import Data.String
@ -257,10 +258,29 @@ accountdirectivep :: JournalParser m ()
accountdirectivep = do
string "account"
lift (skipSome spacenonewline)
acct <- modifiedaccountnamep -- account directives can be modified by alias/apply account
_ :: Maybe String <- (optional $ lift $ skipSome spacenonewline >> some digitChar) -- compatibility: ignore account codes supported in 1.9/1.10
-- the account name, possibly modified by preceding alias or apply account directives
acct <- modifiedaccountnamep
-- and maybe something else after two or more spaces ?
matype :: Maybe AccountType <- lift $ fmap (fromMaybe Nothing) $ optional $ do
skipSome spacenonewline -- at least one more space in addition to the one consumed by modifiedaccountp
choice [
-- a numeric account code, as supported in 1.9-1.10 ? currently ignored
some digitChar >> return Nothing
-- a letter account type code (ALERX), as added in 1.11 ?
,char 'A' >> return (Just Asset)
,char 'L' >> return (Just Liability)
,char 'E' >> return (Just Equity)
,char 'R' >> return (Just Revenue)
,char 'X' >> return (Just Expense)
]
newline
-- Ledger-style indented subdirectives on following lines ? ignore
skipMany indentedlinep
-- update the journal
case matype of
Nothing -> return ()
Just atype -> addDeclaredAccountType acct atype
pushDeclaredAccount acct
indentedlinep :: JournalParser m String