feat: accounts: add Gain (G) account type as subtype of Revenue [#2522]
Add a new account type Gain with single-letter code G as a subtype of Revenue, similar to how Cash is a subtype of Asset and Conversion is a subtype of Equity. This enables tracking capital gains/losses separately while still including them in income statements and close --retain. Usage: account revenues:capital ; type: G - type:G matches only Gain accounts - type:R matches both Revenue and Gain (subtype matching) - Auto-detection from account names matching: ^(income|revenue)s?:(capital[- ]?)?(gains?|loss(es)?)(:|$) e.g. income:gains, revenue:capital-gains, income:losses
This commit is contained in:
parent
c7732c7600
commit
e678e09704
@ -30,6 +30,7 @@ module Hledger.Data.AccountName (
|
|||||||
,equityAccountRegex
|
,equityAccountRegex
|
||||||
,conversionAccountRegex
|
,conversionAccountRegex
|
||||||
,revenueAccountRegex
|
,revenueAccountRegex
|
||||||
|
,gainAccountRegex
|
||||||
,expenseAccountRegex
|
,expenseAccountRegex
|
||||||
,acctsep
|
,acctsep
|
||||||
,acctsepchar
|
,acctsepchar
|
||||||
@ -99,6 +100,7 @@ liabilityAccountRegex = toRegexCI' "^(debts?|liabilit(y|ies))(:|$)"
|
|||||||
equityAccountRegex = toRegexCI' "^equity(:|$)"
|
equityAccountRegex = toRegexCI' "^equity(:|$)"
|
||||||
conversionAccountRegex = toRegexCI' "^equity:(trade|trades|trading|conversion)(:|$)"
|
conversionAccountRegex = toRegexCI' "^equity:(trade|trades|trading|conversion)(:|$)"
|
||||||
revenueAccountRegex = toRegexCI' "^(income|revenue)s?(:|$)"
|
revenueAccountRegex = toRegexCI' "^(income|revenue)s?(:|$)"
|
||||||
|
gainAccountRegex = toRegexCI' "^(income|revenue)s?:(capital[- ]?)?(gains?|loss(es)?)(:|$)"
|
||||||
expenseAccountRegex = toRegexCI' "^expenses?(:|$)"
|
expenseAccountRegex = toRegexCI' "^expenses?(:|$)"
|
||||||
|
|
||||||
-- | Try to guess an account's type from its name,
|
-- | Try to guess an account's type from its name,
|
||||||
@ -110,6 +112,7 @@ accountNameInferType a
|
|||||||
| regexMatchText liabilityAccountRegex a = Just Liability
|
| regexMatchText liabilityAccountRegex a = Just Liability
|
||||||
| regexMatchText conversionAccountRegex a = Just Conversion
|
| regexMatchText conversionAccountRegex a = Just Conversion
|
||||||
| regexMatchText equityAccountRegex a = Just Equity
|
| regexMatchText equityAccountRegex a = Just Equity
|
||||||
|
| regexMatchText gainAccountRegex a = Just Gain
|
||||||
| regexMatchText revenueAccountRegex a = Just Revenue
|
| regexMatchText revenueAccountRegex a = Just Revenue
|
||||||
| regexMatchText expenseAccountRegex a = Just Expense
|
| regexMatchText expenseAccountRegex a = Just Expense
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
@ -447,6 +450,13 @@ tests_AccountName = testGroup "AccountName" [
|
|||||||
accountNameInferType "revenues" @?= Just Revenue
|
accountNameInferType "revenues" @?= Just Revenue
|
||||||
accountNameInferType "revenue" @?= Just Revenue
|
accountNameInferType "revenue" @?= Just Revenue
|
||||||
accountNameInferType "income" @?= Just Revenue
|
accountNameInferType "income" @?= Just Revenue
|
||||||
|
accountNameInferType "income:gains" @?= Just Gain
|
||||||
|
accountNameInferType "revenue:gain" @?= Just Gain
|
||||||
|
accountNameInferType "revenues:capital-gains" @?= Just Gain
|
||||||
|
accountNameInferType "income:capitalgain" @?= Just Gain
|
||||||
|
accountNameInferType "income:losses" @?= Just Gain
|
||||||
|
accountNameInferType "revenue:capital-loss" @?= Just Gain
|
||||||
|
accountNameInferType "income:gains:realized" @?= Just Gain
|
||||||
,testCase "joinAccountNames" $ do
|
,testCase "joinAccountNames" $ do
|
||||||
joinAccountNames "assets" "cash" @?= "assets:cash"
|
joinAccountNames "assets" "cash" @?= "assets:cash"
|
||||||
joinAccountNames "assets:cash" "a" @?= "assets:cash:a"
|
joinAccountNames "assets:cash" "a" @?= "assets:cash:a"
|
||||||
|
|||||||
@ -181,6 +181,7 @@ data AccountType =
|
|||||||
| Expense
|
| Expense
|
||||||
| Cash -- ^ a subtype of Asset - liquid assets to show in cashflow report
|
| Cash -- ^ a subtype of Asset - liquid assets to show in cashflow report
|
||||||
| Conversion -- ^ a subtype of Equity - account with which to balance commodity conversions
|
| Conversion -- ^ a subtype of Equity - account with which to balance commodity conversions
|
||||||
|
| Gain -- ^ a subtype of Revenue - capital gains/losses
|
||||||
deriving (Eq,Ord,Generic)
|
deriving (Eq,Ord,Generic)
|
||||||
|
|
||||||
instance Show AccountType where
|
instance Show AccountType where
|
||||||
@ -191,6 +192,7 @@ instance Show AccountType where
|
|||||||
show Expense = "X"
|
show Expense = "X"
|
||||||
show Cash = "C"
|
show Cash = "C"
|
||||||
show Conversion = "V"
|
show Conversion = "V"
|
||||||
|
show Gain = "G"
|
||||||
|
|
||||||
isBalanceSheetAccountType :: AccountType -> Bool
|
isBalanceSheetAccountType :: AccountType -> Bool
|
||||||
isBalanceSheetAccountType t = t `elem` [
|
isBalanceSheetAccountType t = t `elem` [
|
||||||
@ -204,7 +206,8 @@ isBalanceSheetAccountType t = t `elem` [
|
|||||||
isIncomeStatementAccountType :: AccountType -> Bool
|
isIncomeStatementAccountType :: AccountType -> Bool
|
||||||
isIncomeStatementAccountType t = t `elem` [
|
isIncomeStatementAccountType t = t `elem` [
|
||||||
Revenue,
|
Revenue,
|
||||||
Expense
|
Expense,
|
||||||
|
Gain
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | Check whether the first argument is a subtype of the second: either equal
|
-- | Check whether the first argument is a subtype of the second: either equal
|
||||||
@ -219,6 +222,8 @@ isAccountSubtypeOf Cash Cash = True
|
|||||||
isAccountSubtypeOf Cash Asset = True
|
isAccountSubtypeOf Cash Asset = True
|
||||||
isAccountSubtypeOf Conversion Conversion = True
|
isAccountSubtypeOf Conversion Conversion = True
|
||||||
isAccountSubtypeOf Conversion Equity = True
|
isAccountSubtypeOf Conversion Equity = True
|
||||||
|
isAccountSubtypeOf Gain Gain = True
|
||||||
|
isAccountSubtypeOf Gain Revenue = True
|
||||||
isAccountSubtypeOf _ _ = False
|
isAccountSubtypeOf _ _ = False
|
||||||
|
|
||||||
-- not worth the trouble, letters defined in accountdirectivep for now
|
-- not worth the trouble, letters defined in accountdirectivep for now
|
||||||
|
|||||||
@ -514,11 +514,11 @@ parseTypeCodes s =
|
|||||||
help = "type:'s argument should be one or more of " ++ accountTypeChoices False
|
help = "type:'s argument should be one or more of " ++ accountTypeChoices False
|
||||||
|
|
||||||
accountTypeChoices :: Bool -> String
|
accountTypeChoices :: Bool -> String
|
||||||
accountTypeChoices allowlongform =
|
accountTypeChoices allowlongform =
|
||||||
intercalate ", "
|
intercalate ", "
|
||||||
-- keep synced with parseAccountType
|
-- keep synced with parseAccountType
|
||||||
$ ["A","L","E","R","X","C","V"]
|
$ ["A","L","E","R","X","C","V","G"]
|
||||||
++ if allowlongform then ["Asset","Liability","Equity","Revenue","Expense","Cash","Conversion"] else []
|
++ if allowlongform then ["Asset","Liability","Equity","Revenue","Expense","Cash","Conversion","Gain"] else []
|
||||||
|
|
||||||
-- | Case-insensitively parse one single-letter code, or one long-form word if permitted, to an account type.
|
-- | Case-insensitively parse one single-letter code, or one long-form word if permitted, to an account type.
|
||||||
-- On failure, returns the unparseable text.
|
-- On failure, returns the unparseable text.
|
||||||
@ -533,6 +533,7 @@ parseAccountType allowlongform s =
|
|||||||
"x" -> Right Expense
|
"x" -> Right Expense
|
||||||
"c" -> Right Cash
|
"c" -> Right Cash
|
||||||
"v" -> Right Conversion
|
"v" -> Right Conversion
|
||||||
|
"g" -> Right Gain
|
||||||
"asset" | allowlongform -> Right Asset
|
"asset" | allowlongform -> Right Asset
|
||||||
"liability" | allowlongform -> Right Liability
|
"liability" | allowlongform -> Right Liability
|
||||||
"equity" | allowlongform -> Right Equity
|
"equity" | allowlongform -> Right Equity
|
||||||
@ -540,6 +541,7 @@ parseAccountType allowlongform s =
|
|||||||
"expense" | allowlongform -> Right Expense
|
"expense" | allowlongform -> Right Expense
|
||||||
"cash" | allowlongform -> Right Cash
|
"cash" | allowlongform -> Right Cash
|
||||||
"conversion" | allowlongform -> Right Conversion
|
"conversion" | allowlongform -> Right Conversion
|
||||||
|
"gains" | allowlongform -> Right Gain
|
||||||
_ -> Left $ T.unpack s
|
_ -> Left $ T.unpack s
|
||||||
|
|
||||||
-- | Parse the value part of a "status:" query, or return an error.
|
-- | Parse the value part of a "status:" query, or return an error.
|
||||||
|
|||||||
@ -563,10 +563,12 @@ parseAccountTypeCode s =
|
|||||||
"c" -> Right Cash
|
"c" -> Right Cash
|
||||||
"conversion" -> Right Conversion
|
"conversion" -> Right Conversion
|
||||||
"v" -> Right Conversion
|
"v" -> Right Conversion
|
||||||
|
"gains" -> Right Gain
|
||||||
|
"g" -> Right Gain
|
||||||
_ -> Left err
|
_ -> Left err
|
||||||
where
|
where
|
||||||
err = T.unpack $ "invalid account type code "<>s<>", should be one of " <>
|
err = T.unpack $ "invalid account type code "<>s<>", should be one of " <>
|
||||||
T.intercalate ", " ["A","L","E","R","X","C","V","Asset","Liability","Equity","Revenue","Expense","Cash","Conversion"]
|
T.intercalate ", " ["A","L","E","R","X","C","V","G","Asset","Liability","Equity","Revenue","Expense","Cash","Conversion","Gain"]
|
||||||
|
|
||||||
-- Add an account declaration to the journal, auto-numbering it.
|
-- Add an account declaration to the journal, auto-numbering it.
|
||||||
addAccountDeclaration :: (AccountName,Text,[Tag],SourcePos) -> JournalParser m ()
|
addAccountDeclaration :: (AccountName,Text,[Tag],SourcePos) -> JournalParser m ()
|
||||||
|
|||||||
@ -2212,16 +2212,17 @@ and two more representing changes in these:
|
|||||||
| `Revenue` | `R` | inflows (also known as `Income`) |
|
| `Revenue` | `R` | inflows (also known as `Income`) |
|
||||||
| `Expense` | `X` | outflows |
|
| `Expense` | `X` | outflows |
|
||||||
|
|
||||||
hledger also uses a couple of subtypes:
|
hledger also uses a few subtypes:
|
||||||
|
|
||||||
||||
|
||||
|
||||||
|-|-|-|
|
|-|-|-|
|
||||||
| `Cash` | `C` | liquid assets |
|
| `Cash` | `C` | liquid assets (subtype of Asset) |
|
||||||
| `Conversion` | `V` | commodity conversions equity |
|
| `Conversion` | `V` | commodity conversions equity (subtype of Equity) |
|
||||||
|
| `Gain` | `G` | capital gains/losses (subtype of Revenue) |
|
||||||
|
|
||||||
<!-- [liquid assets]: https://en.wikipedia.org/wiki/Cash_and_cash_equivalents -->
|
<!-- [liquid assets]: https://en.wikipedia.org/wiki/Cash_and_cash_equivalents -->
|
||||||
|
|
||||||
As a convenience, hledger will detect these types automatically from english account names.
|
As a convenience, hledger will detect most of these types automatically from english account names.
|
||||||
But it's better to declare them explicitly by adding a `type:` [tag](#tags) in the account directives.
|
But it's better to declare them explicitly by adding a `type:` [tag](#tags) in the account directives.
|
||||||
The tag's value can be any of the types or one-letter abbreviations above.
|
The tag's value can be any of the types or one-letter abbreviations above.
|
||||||
|
|
||||||
@ -2239,6 +2240,8 @@ account assets:bank ; type: C
|
|||||||
account assets:cash ; type: C
|
account assets:cash ; type: C
|
||||||
|
|
||||||
account equity:conversion ; type: V
|
account equity:conversion ; type: V
|
||||||
|
|
||||||
|
account revenues:capital ; type: G
|
||||||
```
|
```
|
||||||
|
|
||||||
This enables the easy [balancesheet], [balancesheetequity], [cashflow] and [incomestatement] reports, and querying by [type:](#queries).
|
This enables the easy [balancesheet], [balancesheetequity], [cashflow] and [incomestatement] reports, and querying by [type:](#queries).
|
||||||
@ -5682,8 +5685,9 @@ Match unmarked, pending, or cleared transactions respectively.
|
|||||||
**`type:TYPECODES`**\
|
**`type:TYPECODES`**\
|
||||||
Match by account type (see [Declaring accounts > Account types](#account-types)).
|
Match by account type (see [Declaring accounts > Account types](#account-types)).
|
||||||
`TYPECODES` is one or more of the single-letter account type codes
|
`TYPECODES` is one or more of the single-letter account type codes
|
||||||
`ALERXCV`, case insensitive.
|
`ALERXCVG`, case insensitive.
|
||||||
Note `type:A` and `type:E` will also match their respective subtypes `C` (Cash) and `V` (Conversion).
|
Note `type:A`, `type:E`, and `type:R` will also match their respective subtypes
|
||||||
|
`C` (Cash), `V` (Conversion), and `G` (Gain).
|
||||||
Certain kinds of account alias can disrupt account types, see
|
Certain kinds of account alias can disrupt account types, see
|
||||||
[Rewriting accounts > Aliases and account types](#aliases-and-account-types).
|
[Rewriting accounts > Aliases and account types](#aliases-and-account-types).
|
||||||
|
|
||||||
|
|||||||
@ -274,3 +274,23 @@ $ hledger -f- close --clopen -e 2001 --round=hard -c '$1.0'
|
|||||||
equity:opening/closing balances
|
equity:opening/closing balances
|
||||||
|
|
||||||
>=
|
>=
|
||||||
|
|
||||||
|
# ** 20. With --retain, Gain accounts (subtype of Revenue) are also closed.
|
||||||
|
<
|
||||||
|
account revenues ; type:R
|
||||||
|
account gains ; type:G
|
||||||
|
account expenses ; type:X
|
||||||
|
|
||||||
|
2016/1/1
|
||||||
|
revenues $-100
|
||||||
|
gains $-50
|
||||||
|
expenses $150
|
||||||
|
|
||||||
|
$ hledger close -f- -e 2017 --retain
|
||||||
|
2016-12-31 retain earnings ; retain:
|
||||||
|
revenues $100 = $0
|
||||||
|
gains $50 = $0
|
||||||
|
expenses $-150 = $0
|
||||||
|
equity:retained earnings
|
||||||
|
|
||||||
|
>=0
|
||||||
|
|||||||
@ -123,6 +123,7 @@ account a:aa:aaa ; type:L
|
|||||||
(a:aa) 1
|
(a:aa) 1
|
||||||
(a:aa:aaa) 1
|
(a:aa:aaa) 1
|
||||||
|
|
||||||
|
|
||||||
# ** 6. bs will detect proper accounts even with an intervening parent account (#1921)
|
# ** 6. bs will detect proper accounts even with an intervening parent account (#1921)
|
||||||
$ hledger -f- bs -N
|
$ hledger -f- bs -N
|
||||||
Balance Sheet 2021-01-01
|
Balance Sheet 2021-01-01
|
||||||
@ -136,3 +137,63 @@ Balance Sheet 2021-01-01
|
|||||||
-------------++------------
|
-------------++------------
|
||||||
a || -1
|
a || -1
|
||||||
a:aa:aaa || -1
|
a:aa:aaa || -1
|
||||||
|
|
||||||
|
# ** 7. Gains accounts appear in income statement as revenue subtype
|
||||||
|
<
|
||||||
|
account revenues ; type:R
|
||||||
|
account gains ; type:G
|
||||||
|
account expenses ; type:X
|
||||||
|
|
||||||
|
2020-01-01
|
||||||
|
revenues -100
|
||||||
|
gains -50
|
||||||
|
expenses 150
|
||||||
|
|
||||||
|
$ hledger -f- is
|
||||||
|
Income Statement 2020-01-01
|
||||||
|
|
||||||
|
|| 2020-01-01
|
||||||
|
==========++============
|
||||||
|
Revenues ||
|
||||||
|
----------++------------
|
||||||
|
revenues || 100
|
||||||
|
gains || 50
|
||||||
|
----------++------------
|
||||||
|
|| 150
|
||||||
|
==========++============
|
||||||
|
Expenses ||
|
||||||
|
----------++------------
|
||||||
|
expenses || 150
|
||||||
|
----------++------------
|
||||||
|
|| 150
|
||||||
|
==========++============
|
||||||
|
Net: || 0
|
||||||
|
|
||||||
|
# ** 8. Gain accounts are auto-detected from common naming patterns
|
||||||
|
<
|
||||||
|
2020-01-01
|
||||||
|
income:gains -50
|
||||||
|
revenue:capital-gains -30
|
||||||
|
income:losses -20
|
||||||
|
expenses 100
|
||||||
|
|
||||||
|
$ hledger -f- is
|
||||||
|
Income Statement 2020-01-01
|
||||||
|
|
||||||
|
|| 2020-01-01
|
||||||
|
=======================++============
|
||||||
|
Revenues ||
|
||||||
|
-----------------------++------------
|
||||||
|
income:gains || 50
|
||||||
|
income:losses || 20
|
||||||
|
revenue:capital-gains || 30
|
||||||
|
-----------------------++------------
|
||||||
|
|| 100
|
||||||
|
=======================++============
|
||||||
|
Expenses ||
|
||||||
|
-----------------------++------------
|
||||||
|
expenses || 100
|
||||||
|
-----------------------++------------
|
||||||
|
|| 100
|
||||||
|
=======================++============
|
||||||
|
Net: || 0
|
||||||
|
|||||||
@ -135,3 +135,38 @@ $ hledger -f- reg type:ae
|
|||||||
2022-02-02 Test (assets:cash) 1 1
|
2022-02-02 Test (assets:cash) 1 1
|
||||||
(equity:conversion) 2 3
|
(equity:conversion) 2 3
|
||||||
(equity:conversion) -2 1
|
(equity:conversion) -2 1
|
||||||
|
|
||||||
|
# ** 16. type:g matches gains accounts
|
||||||
|
<
|
||||||
|
account gains ; type:G
|
||||||
|
|
||||||
|
2022-02-02 Test
|
||||||
|
(gains) 1
|
||||||
|
|
||||||
|
$ hledger -f- accounts type:g
|
||||||
|
gains
|
||||||
|
|
||||||
|
# ** 17. type:r matches both revenue and gains (subtype matching)
|
||||||
|
<
|
||||||
|
account revenue ; type:R
|
||||||
|
account gains ; type:G
|
||||||
|
|
||||||
|
2022-02-02 Test
|
||||||
|
(revenue) 1
|
||||||
|
(gains) 1
|
||||||
|
|
||||||
|
$ hledger -f- accounts type:r
|
||||||
|
revenue
|
||||||
|
gains
|
||||||
|
|
||||||
|
# ** 18. type:g matches auto-detected gain accounts
|
||||||
|
<
|
||||||
|
2022-02-02 Test
|
||||||
|
(income:gains) 1
|
||||||
|
(revenue:capital-gains) 1
|
||||||
|
(income:losses) 1
|
||||||
|
|
||||||
|
$ hledger -f- accounts type:g
|
||||||
|
income:gains
|
||||||
|
income:losses
|
||||||
|
revenue:capital-gains
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user