data: Add Account.is_account and Account.load_options_map.
These work in concert to distinguish account names from other colon-separated strings.
This commit is contained in:
parent
6a7815090c
commit
fff9e37bf8
3 changed files with 67 additions and 0 deletions
|
@ -30,6 +30,7 @@ from beancount.core import amount as bc_amount
|
|||
from beancount.core import convert as bc_convert
|
||||
from beancount.core import data as bc_data
|
||||
from beancount.core import position as bc_position
|
||||
from beancount.parser import options as bc_options
|
||||
|
||||
from typing import (
|
||||
cast,
|
||||
|
@ -40,6 +41,7 @@ from typing import (
|
|||
Iterator,
|
||||
MutableMapping,
|
||||
Optional,
|
||||
Pattern,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
Union,
|
||||
|
@ -53,6 +55,7 @@ from .beancount_types import (
|
|||
MetaKey,
|
||||
MetaValue,
|
||||
Open,
|
||||
OptionsMap,
|
||||
Posting as BasePosting,
|
||||
Transaction,
|
||||
)
|
||||
|
@ -153,8 +156,19 @@ class Account(str):
|
|||
"""
|
||||
__slots__ = ()
|
||||
|
||||
ACCOUNT_RE: Pattern
|
||||
SEP = bc_account.sep
|
||||
_meta_map: MutableMapping[str, AccountMeta] = {}
|
||||
_options_map: OptionsMap
|
||||
|
||||
@classmethod
|
||||
def load_options_map(cls, options_map: OptionsMap) -> None:
|
||||
cls._options_map = options_map
|
||||
roots: Sequence[str] = bc_options.get_account_types(options_map)
|
||||
cls.ACCOUNT_RE = re.compile(
|
||||
r'^(?:{})(?:{}[A-Z0-9][-A-Za-z0-9]*)+$'.format(
|
||||
'|'.join(roots), cls.SEP,
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def load_opening(cls, opening: Open) -> None:
|
||||
|
@ -178,6 +192,10 @@ class Account(str):
|
|||
elif isinstance(entry, bc_data.Close):
|
||||
cls.load_closing(entry) # type:ignore[arg-type]
|
||||
|
||||
@classmethod
|
||||
def is_account(cls, s: str) -> bool:
|
||||
return cls.ACCOUNT_RE.fullmatch(s) is not None
|
||||
|
||||
@property
|
||||
def meta(self) -> AccountMeta:
|
||||
return self._meta_map[self]
|
||||
|
@ -286,6 +304,7 @@ class Account(str):
|
|||
return self
|
||||
else:
|
||||
return self[:stop]
|
||||
Account.load_options_map(bc_options.OPTIONS_DEFAULTS)
|
||||
|
||||
|
||||
class Amount(bc_amount.Amount):
|
||||
|
|
|
@ -21,6 +21,7 @@ from . import testutil
|
|||
from datetime import date as Date
|
||||
|
||||
from beancount.core.data import Open, Close, Booking
|
||||
from beancount.parser import options as bc_options
|
||||
|
||||
from conservancy_beancount import data
|
||||
|
||||
|
@ -267,3 +268,48 @@ def test_load_openings_and_closings(clean_account_meta):
|
|||
check_account_meta('Income:Donations', entries[0])
|
||||
check_account_meta('Income:Other', entries[1])
|
||||
check_account_meta('Assets:Checking', entries[2], entries[-1])
|
||||
|
||||
@pytest.mark.parametrize('account_s', [
|
||||
'Assets:Bank:Checking',
|
||||
'Equity:Funds:Restricted',
|
||||
'Expenses:Other',
|
||||
'Income:Donations',
|
||||
'Liabilities:CreditCard:Visa',
|
||||
])
|
||||
def test_is_account(account_s):
|
||||
assert data.Account.is_account(account_s)
|
||||
|
||||
@pytest.mark.parametrize('account_s', [
|
||||
'Assets:Bank:12-345',
|
||||
'Equity:Funds:Restricted',
|
||||
'Expenses:Other',
|
||||
'Income:Donations',
|
||||
'Liabilities:CreditCard:Visa0123',
|
||||
])
|
||||
def test_is_account(clean_account_meta, account_s):
|
||||
assert data.Account.is_account(account_s)
|
||||
|
||||
@pytest.mark.parametrize('account_s', [
|
||||
'Assets:checking',
|
||||
'Assets::Cash',
|
||||
'Equity',
|
||||
'Liabilities:Credit Card',
|
||||
'income:Donations',
|
||||
'Expenses:Banking_Fees',
|
||||
'Revenue:Grants',
|
||||
])
|
||||
def test_is_not_account(clean_account_meta, account_s):
|
||||
assert not data.Account.is_account(account_s)
|
||||
|
||||
@pytest.mark.parametrize('account_s,expected', [
|
||||
('Revenue:Donations', True),
|
||||
('Costs:Other', True),
|
||||
('Income:Donations', False),
|
||||
('Expenses:Other', False),
|
||||
])
|
||||
def test_is_account_respects_configured_roots(clean_account_meta, account_s, expected):
|
||||
config = bc_options.OPTIONS_DEFAULTS.copy()
|
||||
config['name_expenses'] = 'Costs'
|
||||
config['name_income'] = 'Revenue'
|
||||
data.Account.load_options_map(config)
|
||||
assert data.Account.is_account(account_s) == expected
|
||||
|
|
|
@ -21,6 +21,7 @@ import re
|
|||
import beancount.core.amount as bc_amount
|
||||
import beancount.core.data as bc_data
|
||||
import beancount.loader as bc_loader
|
||||
import beancount.parser.options as bc_options
|
||||
|
||||
import odf.element
|
||||
import odf.opendocument
|
||||
|
@ -43,6 +44,7 @@ TESTS_DIR = Path(__file__).parent
|
|||
# it with different scopes. Typical usage looks like:
|
||||
# clean_account_meta = pytest.fixture([options])(testutil.clean_account_meta)
|
||||
def clean_account_meta():
|
||||
data.Account.load_options_map(bc_options.OPTIONS_DEFAULTS)
|
||||
data.Account._meta_map.clear()
|
||||
|
||||
def _ods_cell_value_type(cell):
|
||||
|
|
Loading…
Reference in a new issue