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.
This commit is contained in:
parent
c230f91ab7
commit
fdd9f2847b
10 changed files with 71 additions and 10 deletions
|
@ -160,6 +160,13 @@ class _PostingHook(TransactionHook, metaclass=abc.ABCMeta):
|
|||
def __init_subclass__(cls) -> None:
|
||||
cls.HOOK_GROUPS = cls.HOOK_GROUPS.union(['posting'])
|
||||
|
||||
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:
|
||||
return True
|
||||
|
||||
|
@ -205,6 +212,7 @@ class _NormalizePostingMetadataHook(_PostingHook):
|
|||
try:
|
||||
set_value = self.VALUES_ENUM[source_value]
|
||||
except KeyError:
|
||||
if not self._is_flagged_fixme(post, source_value):
|
||||
error = errormod.InvalidMetadataError(
|
||||
txn, self.METADATA_KEY, source_value, post,
|
||||
)
|
||||
|
|
|
@ -39,5 +39,5 @@ class MetaPayPalID(core._PostingHook):
|
|||
match = regexp.match(value) # type:ignore[arg-type]
|
||||
except TypeError:
|
||||
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)
|
||||
|
|
|
@ -59,10 +59,6 @@ class MetaTaxImplication(core._NormalizePostingMetadataHook):
|
|||
VALUES_ENUM = core.MetadataEnum('tax-implication', _STDNAMES, _ALIASES)
|
||||
del _STDNAMES, _ALIASES
|
||||
|
||||
# Sometimes we accrue a payment before we have determined the recipient's
|
||||
# tax status.
|
||||
SKIP_FLAGS = '!'
|
||||
|
||||
def __init__(self, config: configmod.Config) -> None:
|
||||
self.payment_threshold = -config.payment_threshold()
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -5,7 +5,7 @@ from setuptools import setup
|
|||
setup(
|
||||
name='conservancy_beancount',
|
||||
description="Plugin, library, and reports for reading Conservancy's books",
|
||||
version='1.16.1',
|
||||
version='1.16.2',
|
||||
author='Software Freedom Conservancy',
|
||||
author_email='info@sfconservancy.org',
|
||||
license='GNU AGPLv3+',
|
||||
|
|
|
@ -157,3 +157,13 @@ def test_flagged_txn_checked(hook, src_value):
|
|||
errors = list(hook.run(txn))
|
||||
assert errors
|
||||
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})
|
||||
|
|
|
@ -151,3 +151,13 @@ def test_flagged_txn_checked(hook, src_value):
|
|||
errors = list(hook.run(txn))
|
||||
assert errors
|
||||
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})
|
||||
|
|
|
@ -192,3 +192,15 @@ def test_still_required_on_flagged_txn(hook):
|
|||
('Income:Donations', -1000),
|
||||
])
|
||||
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)
|
||||
|
|
|
@ -201,3 +201,13 @@ def test_still_required_on_flagged_txn(hook, src_value):
|
|||
errors = list(hook.run(txn))
|
||||
assert errors
|
||||
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})
|
||||
|
|
|
@ -137,9 +137,18 @@ def test_validation_only_in_date_range(hook, date, need_value):
|
|||
testutil.check_post_meta(txn, None, None)
|
||||
|
||||
@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=[
|
||||
('Liabilities:Payable:Accounts', 25),
|
||||
('Assets:Cash', -25),
|
||||
])
|
||||
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})
|
||||
|
|
|
@ -194,6 +194,12 @@ NON_STRING_METADATA_VALUES = [
|
|||
Amount(500, None),
|
||||
]
|
||||
|
||||
FIXME_VALUES = [
|
||||
'FIXME',
|
||||
'FIXME loose comment',
|
||||
'FIXME: comment with punctuation',
|
||||
]
|
||||
|
||||
OPENING_EQUITY_ACCOUNTS = itertools.cycle([
|
||||
'Equity:Funds:Unrestricted',
|
||||
'Equity:Funds:Restricted',
|
||||
|
|
Loading…
Reference in a new issue