meta_entity: Use payee as entity when metadata not available. RT#10529.
This commit is contained in:
		
							parent
							
								
									bff3eec952
								
							
						
					
					
						commit
						0413fed8b9
					
				
					 4 changed files with 65 additions and 2 deletions
				
			
		| 
						 | 
					@ -185,6 +185,15 @@ class PostingMeta(Metadata):
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.meta = collections.ChainMap(post.meta, txn.meta)
 | 
					            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:
 | 
					    def __setitem__(self, key: MetaKey, value: MetaValue) -> None:
 | 
				
			||||||
        if self.post.meta is None:
 | 
					        if self.post.meta is None:
 | 
				
			||||||
            self.post = self.post._replace(meta={key: value})
 | 
					            self.post = self.post._replace(meta={key: value})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@ class MetaEntity(core.TransactionHook):
 | 
				
			||||||
    del alnum
 | 
					    del alnum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self, txn: Transaction) -> errormod.Iter:
 | 
					    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:
 | 
					        if txn_entity is None:
 | 
				
			||||||
            txn_entity_ok = None
 | 
					            txn_entity_ok = None
 | 
				
			||||||
        elif isinstance(txn_entity, str):
 | 
					        elif isinstance(txn_entity, str):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,13 +21,21 @@ from . import testutil
 | 
				
			||||||
from conservancy_beancount import data
 | 
					from conservancy_beancount import data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
def simple_txn(index=None, key=None):
 | 
					def simple_txn():
 | 
				
			||||||
    return testutil.Transaction(note='txn note', postings=[
 | 
					    return testutil.Transaction(note='txn note', postings=[
 | 
				
			||||||
        ('Assets:Cash', 5),
 | 
					        ('Assets:Cash', 5),
 | 
				
			||||||
        ('Income:Donations', -5, {'note': 'donation love', 'extra': 'Extra'}),
 | 
					        ('Income:Donations', -5, {'note': 'donation love', 'extra': 'Extra'}),
 | 
				
			||||||
    ])
 | 
					    ])
 | 
				
			||||||
SIMPLE_TXN_METAKEYS = frozenset(['filename', 'lineno', 'note'])
 | 
					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):
 | 
					def test_getitem_transaction(simple_txn):
 | 
				
			||||||
    assert data.PostingMeta(simple_txn, 0)['note'] == 'txn note'
 | 
					    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)
 | 
					    meta = data.PostingMeta(simple_txn, 1)
 | 
				
			||||||
    assert list(meta.get_links('note')) == ['donation', 'love']
 | 
					    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
 | 
					# The .get() tests are arguably testing the stdlib, but they're short and
 | 
				
			||||||
# they confirm that we're using the stdlib as we intend.
 | 
					# they confirm that we're using the stdlib as we intend.
 | 
				
			||||||
def test_get_with_meta_value(simple_txn):
 | 
					def test_get_with_meta_value(simple_txn):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,6 +110,36 @@ def test_invalid_values_on_transactions(hook, src_value):
 | 
				
			||||||
    assert all(error.message == "transaction has invalid entity: {}".format(src_value)
 | 
					    assert all(error.message == "transaction has invalid entity: {}".format(src_value)
 | 
				
			||||||
               for error in hook.run(txn))
 | 
					               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', [
 | 
					@pytest.mark.parametrize('account,required', [
 | 
				
			||||||
    ('Assets:Bank:Checking', False),
 | 
					    ('Assets:Bank:Checking', False),
 | 
				
			||||||
    ('Assets:Cash', False),
 | 
					    ('Assets:Cash', False),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue