Changeset - 51eee8ec8fd8
[Not reviewed]
0 3 0
Brett Smith - 4 years ago 2020-11-04 18:43:54
brettcsmith@brettcsmith.org
meta_entity: Don't set transaction metadata when payee is None. RT#12913
3 files changed with 12 insertions and 2 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/plugin/meta_entity.py
Show inline comments
...
 
@@ -43,47 +43,47 @@ class MetaEntity(core.TransactionHook):
 

	
 
    # chars is the set of characters we always accept in entity metadata:
 
    # letters, digits, and ASCII punctuation, except `-` and the Latin 1 supplement
 
    # (i.e., Roman letters with diacritics: áÁàÀâÂåÅäÄãà çÇ ðÐ ñÑ øØ ß etc.)
 
    # See the tests for specific cases.
 
    chars = r'\u0021-\u002c\u002e-\u007e\p{Letter}\p{Digit}--\p{Block=Latin_1_Supplement}'
 
    ENTITY_RE: Pattern[str] = regex.compile(f'^[{chars}][-{chars}]*$', regex.VERSION1)
 
    ANONYMOUS_RE: Pattern[str] = regex.compile(r'^[-_.?!\s]*$', regex.VERSION1)
 
    del chars
 

	
 
    def _check_entity(self,
 
                      meta: MutableMapping[MetaKey, MetaValue],
 
                      default: Optional[str]=None,
 
    ) -> Tuple[Optional[str], Optional[bool]]:
 
        entity = meta.get(self.METADATA_KEY, default)
 
        if entity is None:
 
            return None, None
 
        elif not isinstance(entity, str):
 
            return None, False
 
        elif self.ANONYMOUS_RE.match(entity):
 
            entity = 'Anonymous'
 
            meta[self.METADATA_KEY] = entity
 
            return entity, True
 
        else:
 
            return entity, self.ENTITY_RE.match(entity) is not None
 

	
 
    def run(self, txn: Transaction) -> errormod.Iter:
 
        if not self._run_on_txn(txn):
 
            return
 
        txn_entity, txn_entity_ok = self._check_entity(txn.meta, txn.payee)
 
        if txn_entity_ok is False:
 
            yield errormod.InvalidMetadataError(txn, self.METADATA_KEY, txn_entity)
 
        if txn_entity is txn.payee:
 
        if txn_entity is txn.payee and txn_entity is not None:
 
            txn.meta[self.METADATA_KEY] = txn.payee
 
        for post in data.Posting.from_txn(txn):
 
            if not post.account.is_under(
 
                    'Assets:Receivable',
 
                    'Expenses',
 
                    'Income',
 
                    'Liabilities:Payable',
 
            ):
 
                continue
 
            entity, entity_ok = self._check_entity(post.meta, txn_entity)
 
            if entity is txn_entity and entity is not None:
 
                pass
 
            elif not entity_ok:
 
                yield errormod.InvalidMetadataError(txn, self.METADATA_KEY, entity, post)
setup.py
Show inline comments
 
#!/usr/bin/env python3
 

	
 
from setuptools import setup
 

	
 
setup(
 
    name='conservancy_beancount',
 
    description="Plugin, library, and reports for reading Conservancy's books",
 
    version='1.13.0',
 
    version='1.13.1',
 
    author='Software Freedom Conservancy',
 
    author_email='info@sfconservancy.org',
 
    license='GNU AGPLv3+',
 

	
 
    install_requires=[
 
        'babel>=2.6',  # Debian:python3-babel
 
        'beancount>=2.2',  # Debian:beancount
 
        'GitPython>=2.0',  # Debian:python3-git
 
        # 1.4.1 crashes when trying to save some documents.
 
        'odfpy>=1.4.0,!=1.4.1',  # Debian:python3-odf
 
        'PyYAML>=3.0',  # Debian:python3-yaml
 
        'regex',  # Debian:python3-regex
 
        'rt>=2.0',
 
    ],
 
    setup_requires=[
 
        'pytest-mypy',
 
        'pytest-runner',  # Debian:python3-pytest-runner
 
    ],
 
    tests_require=[
 
        'mypy>=0.770',  # Debian:python3-mypy
 
        'pytest',  # Debian:python3-pytest
 
    ],
 

	
 
    packages=[
 
        'conservancy_beancount',
 
        'conservancy_beancount.plugin',
 
        'conservancy_beancount.reports',
 
        'conservancy_beancount.tools',
 
    ],
 
    entry_points={
 
        'console_scripts': [
 
            'accrual-report = conservancy_beancount.reports.accrual:entry_point',
tests/test_meta_entity.py
Show inline comments
...
 
@@ -185,56 +185,66 @@ def test_invalid_payee_but_valid_metadata(hook, payee, src_value):
 
    txn = testutil.Transaction(**{'payee': payee, TEST_KEY: src_value}, postings=[
 
        ('Assets:Cash', -25),
 
        ('Expenses:Other', 25),
 
    ])
 
    assert not any(hook.run(txn))
 

	
 
@pytest.mark.parametrize('account,required', [
 
    ('Assets:Bank:Checking', False),
 
    ('Assets:Cash', False),
 
    ('Assets:Receivable:Accounts', True),
 
    ('Assets:Receivable:Loans', True),
 
    ('Equity:OpeningBalances', False),
 
    ('Expenses:General', True),
 
    ('Income:Donations', True),
 
    ('Liabilities:CreditCard', False),
 
    ('Liabilities:Payable:Accounts', True),
 
    ('Liabilities:Payable:Vacation', True),
 
    ('Liabilities:UnearnedIncome:Donations', False),
 
])
 
def test_which_accounts_required_on(hook, account, required):
 
    txn = testutil.Transaction(postings=[
 
        ('Assets:Checking', -25),
 
        (account, 25),
 
    ])
 
    errors = list(hook.run(txn))
 
    if not required:
 
        assert not errors
 
    else:
 
        assert errors
 
        assert any(error.message == "{} missing entity".format(account)
 
                   for error in errors)
 

	
 
def test_dont_set_entity_none(hook):
 
    txn = testutil.Transaction(postings=[
 
        ('Expenses:Other', 5),
 
        ('Assets:Cash', -5),
 
    ])
 
    assert any(hook.run(txn))
 
    assert 'entity' not in txn.meta
 
    for post in txn.postings:
 
        assert post.meta is None or 'entity' not in post.meta
 

	
 
def test_not_required_on_opening(hook):
 
    txn = testutil.OpeningBalance()
 
    assert not list(hook.run(txn))
 

	
 
@pytest.mark.parametrize('date,need_value', [
 
    (testutil.EXTREME_FUTURE_DATE, False),
 
    (testutil.FUTURE_DATE, True),
 
    (testutil.FY_START_DATE, True),
 
    (testutil.FY_MID_DATE, True),
 
    (testutil.PAST_DATE, False),
 
])
 
def test_required_by_date(hook, date, need_value):
 
    txn = testutil.Transaction(date=date, postings=[
 
        ('Income:Donations', -10),
 
        ('Assets:Checking', 10),
 
    ])
 
    assert any(hook.run(txn)) == need_value
 

	
 
def test_still_required_on_flagged(hook):
 
    txn = testutil.Transaction(flag='!', postings=[
 
        ('Income:Donations', -10),
 
        ('Assets:Checking', 10),
 
    ])
 
    assert list(hook.run(txn))
0 comments (0 inline, 0 general)