Changeset - fdd9f2847b78
[Not reviewed]
0 10 0
Brett Smith - 3 years ago 2021-01-29 14:38:37
brettcsmith@brettcsmith.org
plugin: Skip enum value checks with a flag+FIXME.

We've long supported skipping documentation checks by flagging the
transaction. We haven't done the same for enumerated metadata because we
need it less often, and bad values tend to do more damage to reports.

However, occasionally when something very off-process happens, we do need it
as a matter of expediency. So support it.

In order to skip validation of these fields, the plugin requires that the
value start with the string "FIXME". This helps ensure that reports have a
consistent way to detect and warn about unfilled values in flagged
transactions.
10 files changed with 71 insertions and 10 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/plugin/core.py
Show inline comments
...
 
@@ -162,2 +162,9 @@ class _PostingHook(TransactionHook, metaclass=abc.ABCMeta):
 

	
 
    def _is_flagged_fixme(self, post: data.Posting, value: MetaValue) -> bool:
 
        return (
 
            post.meta.txn.flag == '!'
 
            and isinstance(value, str)
 
            and value.startswith('FIXME')
 
        )
 

	
 
    def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
...
 
@@ -207,5 +214,6 @@ class _NormalizePostingMetadataHook(_PostingHook):
 
            except KeyError:
 
                error = errormod.InvalidMetadataError(
 
                    txn, self.METADATA_KEY, source_value, post,
 
                )
 
                if not self._is_flagged_fixme(post, source_value):
 
                    error = errormod.InvalidMetadataError(
 
                        txn, self.METADATA_KEY, source_value, post,
 
                    )
 
        if error is None:
conservancy_beancount/plugin/meta_paypal_id.py
Show inline comments
...
 
@@ -41,3 +41,3 @@ class MetaPayPalID(core._PostingHook):
 
            match = None
 
        if match is None:
 
        if match is None and not self._is_flagged_fixme(post, value):
 
            yield errormod.InvalidMetadataError(txn, self.METADATA_KEY, value, post)
conservancy_beancount/plugin/meta_tax_implication.py
Show inline comments
...
 
@@ -61,6 +61,2 @@ class MetaTaxImplication(core._NormalizePostingMetadataHook):
 

	
 
    # Sometimes we accrue a payment before we have determined the recipient's
 
    # tax status.
 
    SKIP_FLAGS = '!'
 

	
 
    def __init__(self, config: configmod.Config) -> None:
setup.py
Show inline comments
...
 
@@ -7,3 +7,3 @@ setup(
 
    description="Plugin, library, and reports for reading Conservancy's books",
 
    version='1.16.1',
 
    version='1.16.2',
 
    author='Software Freedom Conservancy',
tests/test_meta_expense_type.py
Show inline comments
...
 
@@ -159 +159,11 @@ def test_flagged_txn_checked(hook, src_value):
 
    testutil.check_post_meta(txn, None, {TEST_KEY: src_value})
 

	
 
@pytest.mark.parametrize('src_value', testutil.FIXME_VALUES)
 
def test_flagged_fixme_ok(hook, src_value):
 
    txn = testutil.Transaction(flag='!', postings=[
 
        ('Assets:Cash', -25),
 
        ('Expenses:General', 25, {TEST_KEY: src_value}),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert not errors
 
    testutil.check_post_meta(txn, None, {TEST_KEY: src_value})
tests/test_meta_income_type.py
Show inline comments
...
 
@@ -153 +153,11 @@ def test_flagged_txn_checked(hook, src_value):
 
    testutil.check_post_meta(txn, None, {TEST_KEY: src_value})
 

	
 
@pytest.mark.parametrize('src_value', testutil.FIXME_VALUES)
 
def test_flagged_fixme_ok(hook, src_value):
 
    txn = testutil.Transaction(flag='!', postings=[
 
        ('Assets:Cash', 25),
 
        ('Income:Other', -25, {TEST_KEY: src_value}),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert not errors
 
    testutil.check_post_meta(txn, None, {TEST_KEY: src_value})
tests/test_meta_paypal_id.py
Show inline comments
...
 
@@ -194 +194,13 @@ def test_still_required_on_flagged_txn(hook):
 
    assert list(hook.run(txn))
 

	
 
@pytest.mark.parametrize('account,src_value', testutil.combine_values(
 
    ACCOUNTS,
 
    testutil.FIXME_VALUES,
 
))
 
def test_flagged_fixme_ok(hook, account, src_value):
 
    txn = testutil.Transaction(flag='!', postings=[
 
        (account, 200, {TEST_KEY: src_value}),
 
        ('Income:Donations', -200),
 
    ])
 
    assert not list(hook.run(txn))
 
    testutil.check_post_meta(txn, {TEST_KEY: src_value}, None)
tests/test_meta_project.py
Show inline comments
...
 
@@ -203 +203,11 @@ def test_still_required_on_flagged_txn(hook, src_value):
 
    testutil.check_post_meta(txn, None, None)
 

	
 
@pytest.mark.parametrize('src_value', testutil.FIXME_VALUES)
 
def test_flagged_fixme_ok(hook, src_value):
 
    txn = testutil.Transaction(flag='!', postings=[
 
        ('Assets:Cash', -25),
 
        ('Expenses:General', 25, {TEST_KEY: src_value}),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert not errors
 
    testutil.check_post_meta(txn, None, {TEST_KEY: src_value})
tests/test_meta_tax_implication.py
Show inline comments
...
 
@@ -139,3 +139,3 @@ def test_validation_only_in_date_range(hook, date, need_value):
 
@pytest.mark.parametrize('src_value', INVALID_VALUES)
 
def test_flagged_txn_skipped(hook, src_value):
 
def test_flagged_txn_checked(hook, src_value):
 
    txn = testutil.Transaction(flag='!', **{TEST_KEY: src_value}, postings=[
...
 
@@ -144,2 +144,11 @@ def test_flagged_txn_skipped(hook, src_value):
 
    ])
 
    assert list(hook.run(txn))
 

	
 
@pytest.mark.parametrize('src_value', testutil.FIXME_VALUES)
 
def test_flagged_fixme_ok(hook, src_value):
 
    txn = testutil.Transaction(flag='!', postings=[
 
        ('Liabilities:Payable:Accounts', 25),
 
        ('Assets:Cash', -25, {TEST_KEY: src_value}),
 
    ])
 
    assert not list(hook.run(txn))
 
    testutil.check_post_meta(txn, None, {TEST_KEY: src_value})
tests/testutil.py
Show inline comments
...
 
@@ -196,2 +196,8 @@ NON_STRING_METADATA_VALUES = [
 

	
 
FIXME_VALUES = [
 
    'FIXME',
 
    'FIXME loose comment',
 
    'FIXME: comment with punctuation',
 
]
 

	
 
OPENING_EQUITY_ACCOUNTS = itertools.cycle([
0 comments (0 inline, 0 general)