meta_project: Force the default project on Equity accounts.
See rationale in comments.
This commit is contained in:
parent
8b8bdc0225
commit
d7e2ab34b9
3 changed files with 56 additions and 2 deletions
|
@ -80,7 +80,7 @@ class MetaProject(core._NormalizePostingMetadataHook):
|
|||
)
|
||||
|
||||
def _run_on_opening_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
return post.account.is_under(self.RESTRICTED_FUNDS_ACCT) is not None
|
||||
return post.account.is_under('Equity') is not None
|
||||
|
||||
def _run_on_other_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
if post.account.is_under('Liabilities'):
|
||||
|
@ -88,6 +88,7 @@ class MetaProject(core._NormalizePostingMetadataHook):
|
|||
else:
|
||||
return post.account.is_under(
|
||||
'Assets:Receivable',
|
||||
'Equity',
|
||||
'Expenses',
|
||||
'Income',
|
||||
self.RESTRICTED_FUNDS_ACCT,
|
||||
|
@ -105,6 +106,25 @@ class MetaProject(core._NormalizePostingMetadataHook):
|
|||
def _run_on_txn(self, txn: Transaction) -> bool:
|
||||
return txn.date in self.TXN_DATE_RANGE
|
||||
|
||||
def post_run(self, txn: Transaction, post: data.Posting) -> errormod.Iter:
|
||||
if (post.account.is_under('Equity')
|
||||
and not post.account.is_under(self.RESTRICTED_FUNDS_ACCT)):
|
||||
# Force all unrestricted Equity accounts to have the default
|
||||
# project. This is what our fiscal controls policy says, and
|
||||
# setting it here simplifies higher-level queries and reporting.
|
||||
post_value = post.meta.get(self.METADATA_KEY)
|
||||
txn_value = txn.meta.get(self.METADATA_KEY)
|
||||
# Only report an error if the posting specifically had a different
|
||||
# value, not if it just inherited it from the transaction.
|
||||
if (post_value is not txn_value
|
||||
and post_value != self.DEFAULT_PROJECT):
|
||||
yield errormod.InvalidMetadataError(
|
||||
txn, self.METADATA_KEY, post_value, post,
|
||||
)
|
||||
post.meta[self.METADATA_KEY] = self.DEFAULT_PROJECT
|
||||
else:
|
||||
yield from super().post_run(txn, post)
|
||||
|
||||
def run(self, txn: Transaction) -> errormod.Iter:
|
||||
# mypy says we can't assign over a method.
|
||||
# I understand why it wants to enforce thas as a blanket rule, but
|
||||
|
|
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.2.4',
|
||||
version='1.2.5',
|
||||
author='Software Freedom Conservancy',
|
||||
author_email='info@sfconservancy.org',
|
||||
license='GNU AGPLv3+',
|
||||
|
|
|
@ -109,6 +109,8 @@ def test_which_accounts_required_on(hook, account, required):
|
|||
assert required == any(errors)
|
||||
|
||||
@pytest.mark.parametrize('account', [
|
||||
'Equity:Funds:Unrestricted',
|
||||
'Equity:Realized:CurrencyConversion',
|
||||
'Expenses:Payroll:Salary',
|
||||
'Expenses:Payroll:Tax',
|
||||
'Liabilities:Payable:Vacation',
|
||||
|
@ -122,6 +124,38 @@ def test_default_values(hook, account):
|
|||
assert not errors
|
||||
testutil.check_post_meta(txn, None, {TEST_KEY: DEFAULT_VALUE})
|
||||
|
||||
@pytest.mark.parametrize('equity,other_acct,value', testutil.combine_values(
|
||||
['Equity:Funds:Unrestricted', 'Equity:Realized:CurrencyConversion'],
|
||||
['Assets:Checking', 'Liabilities:CreditCard'],
|
||||
VALID_VALUES,
|
||||
))
|
||||
def test_equity_override_txn_meta(hook, equity, other_acct, value):
|
||||
if value == DEFAULT_VALUE:
|
||||
value = f'Not{value}'
|
||||
txn = testutil.Transaction(**{TEST_KEY: value}, postings=[
|
||||
(other_acct, 100),
|
||||
(equity, -100),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
assert not errors
|
||||
testutil.check_post_meta(txn, None, {TEST_KEY: DEFAULT_VALUE})
|
||||
|
||||
@pytest.mark.parametrize('equity,other_acct,value', testutil.combine_values(
|
||||
['Equity:Funds:Unrestricted', 'Equity:Realized:CurrencyConversion'],
|
||||
['Assets:Checking', 'Liabilities:CreditCard'],
|
||||
VALID_VALUES,
|
||||
))
|
||||
def test_equity_override_post_meta(hook, equity, other_acct, value):
|
||||
if value == DEFAULT_VALUE:
|
||||
value = f'Not{value}'
|
||||
txn = testutil.Transaction(postings=[
|
||||
(other_acct, 100),
|
||||
(equity, -100, {TEST_KEY: value}),
|
||||
])
|
||||
actual = {error.message for error in hook.run(txn)}
|
||||
assert actual == {f"{equity} has invalid {TEST_KEY}: {value}"}
|
||||
testutil.check_post_meta(txn, None, {TEST_KEY: DEFAULT_VALUE})
|
||||
|
||||
@pytest.mark.parametrize('date,required', [
|
||||
(testutil.EXTREME_FUTURE_DATE, False),
|
||||
(testutil.FUTURE_DATE, True),
|
||||
|
|
Loading…
Reference in a new issue