plugin: Don't validate transactions flagged with !. RT#10591.
This commit is contained in:
parent
56b644f1db
commit
536b50b478
10 changed files with 77 additions and 17 deletions
|
@ -71,10 +71,6 @@ class Hook(Generic[Entry], metaclass=abc.ABCMeta):
|
|||
def run(self, entry: Entry) -> errormod.Iter: ...
|
||||
|
||||
|
||||
class TransactionHook(Hook[Transaction]):
|
||||
DIRECTIVE = Transaction
|
||||
|
||||
|
||||
### HELPER CLASSES
|
||||
|
||||
class LessComparable(metaclass=abc.ABCMeta):
|
||||
|
@ -178,18 +174,28 @@ class MetadataEnum:
|
|||
|
||||
### HOOK SUBCLASSES
|
||||
|
||||
class _PostingHook(TransactionHook, metaclass=abc.ABCMeta):
|
||||
class TransactionHook(Hook[Transaction]):
|
||||
DIRECTIVE = Transaction
|
||||
TXN_DATE_RANGE: _GenericRange = _GenericRange(DEFAULT_START_DATE, DEFAULT_STOP_DATE)
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
cls.HOOK_GROUPS = cls.HOOK_GROUPS.union(['posting'])
|
||||
|
||||
def _run_on_txn(self, txn: Transaction) -> bool:
|
||||
"""Check whether we should run on a given transaction
|
||||
|
||||
This method implements our usual checks for whether or not a hook
|
||||
should run on a given transaction. It's here for subclasses to use in
|
||||
their own implementations. See _PostingHook below for an example.
|
||||
"""
|
||||
return (
|
||||
txn.date in self.TXN_DATE_RANGE
|
||||
txn.flag != '!'
|
||||
and txn.date in self.TXN_DATE_RANGE
|
||||
and not data.is_opening_balance_txn(txn)
|
||||
)
|
||||
|
||||
|
||||
class _PostingHook(TransactionHook, metaclass=abc.ABCMeta):
|
||||
def __init_subclass__(cls) -> None:
|
||||
cls.HOOK_GROUPS = cls.HOOK_GROUPS.union(['posting'])
|
||||
|
||||
def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
return True
|
||||
|
||||
|
|
|
@ -63,7 +63,8 @@ class MetaRepoLinks(core.TransactionHook):
|
|||
yield errormod.BrokenLinkError(txn, key, link)
|
||||
|
||||
def run(self, txn: Transaction) -> errormod.Iter:
|
||||
yield from self._check_links(txn.meta, txn)
|
||||
for post in txn.postings:
|
||||
if post.meta is not None:
|
||||
yield from self._check_links(post.meta, txn, post)
|
||||
if self._run_on_txn(txn):
|
||||
yield from self._check_links(txn.meta, txn)
|
||||
for post in txn.postings:
|
||||
if post.meta is not None:
|
||||
yield from self._check_links(post.meta, txn, post)
|
||||
|
|
|
@ -59,7 +59,8 @@ class MetaRTLinks(core.TransactionHook):
|
|||
yield errormod.BrokenRTLinkError(txn, key, link, parsed)
|
||||
|
||||
def run(self, txn: Transaction) -> errormod.Iter:
|
||||
yield from self._check_links(txn.meta, txn)
|
||||
for post in txn.postings:
|
||||
if post.meta is not None:
|
||||
yield from self._check_links(post.meta, txn, post)
|
||||
if self._run_on_txn(txn):
|
||||
yield from self._check_links(txn.meta, txn)
|
||||
for post in txn.postings:
|
||||
if post.meta is not None:
|
||||
yield from self._check_links(post.meta, txn, post)
|
||||
|
|
|
@ -180,3 +180,10 @@ def test_approval_required_for_partial_transfer(hook):
|
|||
])
|
||||
actual = {error.message for error in hook.run(txn)}
|
||||
assert actual == {"Assets:Checking missing {}".format(TEST_KEY)}
|
||||
|
||||
def test_not_required_on_flagged(hook):
|
||||
txn = testutil.Transaction(flag='!', postings=[
|
||||
('Assets:Checking', -25),
|
||||
('Liabilities:Payable:Accounts', 25),
|
||||
])
|
||||
assert not list(hook.run(txn))
|
||||
|
|
|
@ -144,3 +144,14 @@ def test_missing_invoice(hook, acct1, acct2):
|
|||
def test_not_required_on_opening(hook):
|
||||
txn = testutil.OpeningBalance()
|
||||
assert not list(hook.run(txn))
|
||||
|
||||
@pytest.mark.parametrize('acct1,acct2', testutil.combine_values(
|
||||
REQUIRED_ACCOUNTS,
|
||||
NON_REQUIRED_ACCOUNTS,
|
||||
))
|
||||
def test_not_required_on_flagged(acct1, acct2, hook):
|
||||
txn = testutil.Transaction(flag='!', postings=[
|
||||
(acct1, 25),
|
||||
(acct2, -25),
|
||||
])
|
||||
assert not list(hook.run(txn))
|
||||
|
|
|
@ -171,3 +171,6 @@ def test_not_required_on_opening(hook):
|
|||
(next(testutil.OPENING_EQUITY_ACCOUNTS), 40),
|
||||
])
|
||||
assert not list(hook.run(txn))
|
||||
|
||||
def test_not_required_on_flagged(hook):
|
||||
check(hook, None, txn_meta={'flag': '!'})
|
||||
|
|
|
@ -349,3 +349,10 @@ def test_fallback_on_zero_amount_postings(hook, test_acct, other_acct, value):
|
|||
))
|
||||
def test_not_required_on_opening(hook, test_acct, equity_acct):
|
||||
check(hook, test_acct, equity_acct, None)
|
||||
|
||||
@pytest.mark.parametrize('test_acct,other_acct', testutil.combine_values(
|
||||
ACCOUNTS,
|
||||
NOT_REQUIRED_ACCOUNTS,
|
||||
))
|
||||
def test_not_required_on_flagged(hook, test_acct, other_acct):
|
||||
check(hook, test_acct, other_acct, None, txn_meta={'flag': '!'})
|
||||
|
|
|
@ -213,3 +213,7 @@ def test_not_required_on_opening(hook):
|
|||
(next(testutil.OPENING_EQUITY_ACCOUNTS), -300),
|
||||
])
|
||||
assert not list(hook.run(txn))
|
||||
|
||||
def test_not_required_on_flagged(hook):
|
||||
post_meta = seed_meta()
|
||||
check(hook, None, txn_meta={'flag': '!'}, post_meta=post_meta)
|
||||
|
|
|
@ -100,6 +100,16 @@ def test_bad_post_links(hook):
|
|||
actual = {error.message for error in hook.run(txn)}
|
||||
assert expected == actual
|
||||
|
||||
def test_flagged_txn_not_checked(hook):
|
||||
keys = iter(METADATA_KEYS)
|
||||
txn_meta = build_meta(keys, BAD_LINKS)
|
||||
txn_meta['flag'] = '!'
|
||||
txn = testutil.Transaction(**txn_meta, postings=[
|
||||
('Income:Donations', -5, build_meta(keys, BAD_LINKS)),
|
||||
('Assets:Checking', 5, build_meta(keys, BAD_LINKS)),
|
||||
])
|
||||
assert not list(hook.run(txn))
|
||||
|
||||
@pytest.mark.parametrize('value', testutil.NON_STRING_METADATA_VALUES)
|
||||
def test_bad_metadata_type(hook, value):
|
||||
txn = testutil.Transaction(**{'check': value}, postings=[
|
||||
|
|
|
@ -146,6 +146,16 @@ def test_docs_outside_rt_not_checked(hook, ext_doc):
|
|||
actual = {error.message for error in hook.run(txn)}
|
||||
assert expected == actual
|
||||
|
||||
def test_flagged_txn_not_checked(hook):
|
||||
txn_meta = build_meta(None, MALFORMED_LINKS)
|
||||
txn_meta['flag'] = '!'
|
||||
keys = iter(METADATA_KEYS)
|
||||
txn = testutil.Transaction(**txn_meta, postings=[
|
||||
('Income:Donations', -5, build_meta(keys, MALFORMED_LINKS)),
|
||||
('Assets:Checking', 5, build_meta(keys, NOT_FOUND_LINKS)),
|
||||
])
|
||||
assert not list(hook.run(txn))
|
||||
|
||||
def test_mixed_results(hook):
|
||||
txn = testutil.Transaction(
|
||||
approval='{} {}'.format(*GOOD_LINKS),
|
||||
|
|
Loading…
Reference in a new issue