Changeset - 0413fed8b957
[Not reviewed]
0 4 0
Brett Smith - 4 years ago 2020-04-06 20:03:56
brettcsmith@brettcsmith.org
meta_entity: Use payee as entity when metadata not available. RT#10529.
4 files changed with 65 insertions and 2 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/data.py
Show inline comments
...
 
@@ -185,6 +185,15 @@ class PostingMeta(Metadata):
 
        else:
 
            self.meta = collections.ChainMap(post.meta, txn.meta)
 

	
 
    def __getitem__(self, key: MetaKey) -> MetaValue:
 
        try:
 
            return super().__getitem__(key)
 
        except KeyError:
 
            if key == 'entity' and self.txn.payee is not None:
 
                return self.txn.payee
 
            else:
 
                raise
 

	
 
    def __setitem__(self, key: MetaKey, value: MetaValue) -> None:
 
        if self.post.meta is None:
 
            self.post = self.post._replace(meta={key: value})
conservancy_beancount/plugin/meta_entity.py
Show inline comments
...
 
@@ -49,7 +49,7 @@ class MetaEntity(core.TransactionHook):
 
    del alnum
 

	
 
    def run(self, txn: Transaction) -> errormod.Iter:
 
        txn_entity = txn.meta.get(self.METADATA_KEY)
 
        txn_entity = txn.meta.get(self.METADATA_KEY, txn.payee)
 
        if txn_entity is None:
 
            txn_entity_ok = None
 
        elif isinstance(txn_entity, str):
tests/test_data_posting_meta.py
Show inline comments
...
 
@@ -21,13 +21,21 @@ from . import testutil
 
from conservancy_beancount import data
 

	
 
@pytest.fixture
 
def simple_txn(index=None, key=None):
 
def simple_txn():
 
    return testutil.Transaction(note='txn note', postings=[
 
        ('Assets:Cash', 5),
 
        ('Income:Donations', -5, {'note': 'donation love', 'extra': 'Extra'}),
 
    ])
 
SIMPLE_TXN_METAKEYS = frozenset(['filename', 'lineno', 'note'])
 

	
 
@pytest.fixture
 
def payee_txn():
 
    return testutil.Transaction(payee='SampleCo', postings=[
 
        ('Assets:Receivable:Accounts', -100),
 
        ('Assets:Checking', 95),
 
        ('Expenses:BankingFees', 5, {'entity': 'MyBank'}),
 
    ])
 

	
 
def test_getitem_transaction(simple_txn):
 
    assert data.PostingMeta(simple_txn, 0)['note'] == 'txn note'
 

	
...
 
@@ -88,6 +96,22 @@ def test_get_links_from_post_override(simple_txn):
 
    meta = data.PostingMeta(simple_txn, 1)
 
    assert list(meta.get_links('note')) == ['donation', 'love']
 

	
 
def test_payee_used_as_entity(payee_txn):
 
    actual = [data.PostingMeta(payee_txn, n, p)['entity']
 
              for n, p in enumerate(payee_txn.postings)]
 
    assert actual == ['SampleCo', 'SampleCo', 'MyBank']
 

	
 
def test_entity_metadata_has_precedence_over_payee(payee_txn):
 
    payee_txn.meta['entity'] = 'ExampleCo'
 
    actual = [data.PostingMeta(payee_txn, n, p)['entity']
 
              for n, p in enumerate(payee_txn.postings)]
 
    assert actual == ['ExampleCo', 'ExampleCo', 'MyBank']
 

	
 
def test_keyerror_when_no_entity_or_payee(simple_txn):
 
    meta = data.PostingMeta(simple_txn, 1)
 
    with pytest.raises(KeyError):
 
        meta['entity']
 

	
 
# The .get() tests are arguably testing the stdlib, but they're short and
 
# they confirm that we're using the stdlib as we intend.
 
def test_get_with_meta_value(simple_txn):
tests/test_meta_entity.py
Show inline comments
...
 
@@ -110,6 +110,36 @@ def test_invalid_values_on_transactions(hook, src_value):
 
    assert all(error.message == "transaction has invalid entity: {}".format(src_value)
 
               for error in hook.run(txn))
 

	
 
@pytest.mark.parametrize('src_value', VALID_VALUES)
 
def test_valid_values_on_payee(hook, src_value):
 
    txn = testutil.Transaction(payee=src_value, postings=[
 
        ('Assets:Cash', -25),
 
        ('Expenses:General', 25),
 
    ])
 
    assert not any(hook.run(txn))
 

	
 
@pytest.mark.parametrize('src_value', INVALID_VALUES)
 
def test_invalid_values_on_payee(hook, src_value):
 
    txn = testutil.Transaction(payee=src_value, postings=[
 
        ('Assets:Cash', -25),
 
        ('Expenses:General', 25),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert 1 <= len(errors) <= 2
 
    assert all(error.message == "transaction has invalid entity: {}".format(src_value)
 
               for error in hook.run(txn))
 

	
 
@pytest.mark.parametrize('payee,src_value', testutil.combine_values(
 
    INVALID_VALUES,
 
    VALID_VALUES,
 
))
 
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),
0 comments (0 inline, 0 general)