Changeset - 328f59231c08
[Not reviewed]
0 2 0
Brett Smith - 4 years ago 2021-02-11 16:17:24
brettcsmith@brettcsmith.org
meta_tax_implication: Accept titlecased values.
2 files changed with 4 insertions and 1 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/plugin/meta_tax_implication.py
Show inline comments
 
"""meta_tax_implication - Validate tax-implication metadata"""
 
# Copyright © 2020  Brett Smith
 
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
 
#
 
# Full copyright and licensing details can be found at toplevel file
 
# LICENSE.txt in the repository.
 

	
 
import decimal
 

	
 
from . import core
 
from .. import config as configmod
 
from .. import data
 

	
 
from typing import (
 
    Iterator,
 
    Optional,
 
    Tuple,
 
)
 
from ..beancount_types import (
 
    Transaction,
 
)
 

	
 
def _make_aliases(s: str, stdname: Optional[str]=None) -> Iterator[Tuple[str, str]]:
 
    if stdname is None:
 
        stdname = s
 
    elif s != stdname:
 
        yield (s, stdname)
 
    yield (s.lower(), stdname)
 
    title_s = s.title()
 
    if s != title_s:
 
        yield (title_s, stdname)
 
    if s.startswith('1099-'):
 
        yield from _make_aliases(f'1099{s[5:]}', stdname)
 
    elif s.startswith('USA-'):
 
        yield from _make_aliases(f'US-{s[4:]}', stdname)
 
    if s.endswith('-Corporation'):
 
        yield from _make_aliases(f'{s[:-12]}-Corp', stdname)
 

	
 
class MetaTaxImplication(core._NormalizePostingMetadataHook):
 
    _STDNAMES = [
 
        '1099-MISC-Other',
 
        '1099-NEC',
 
        'Bank-Transfer',
 
        'Chargeback',
 
        'Foreign-Corporation',
 
        'Foreign-Grantee',
 
        'Foreign-Individual-Contractor',
 
        'Loan',
 
        'Refund',
 
        'Reimbursement',
 
        'Retirement-Pretax',
 
        'Tax-Payment',
 
        'USA-501c3',
 
        'USA-Corporation',
 
        'USA-Grantee',
 
        'W2',
 
    ]
 
    _ALIASES = dict(
 
        alias for value in _STDNAMES for alias in _make_aliases(value)
 
    )
 
    _ALIASES['1099'] = '1099-NEC'
 
    VALUES_ENUM = core.MetadataEnum('tax-implication', _STDNAMES, _ALIASES)
 
    del _STDNAMES, _ALIASES
 

	
 
    def __init__(self, config: configmod.Config) -> None:
 
        self.payment_threshold = -config.payment_threshold()
 

	
 
    def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
 
        return (
 
            post.account.is_cash_equivalent()
 
            and post.units.number < self.payment_threshold
 
        )
tests/test_meta_tax_implication.py
Show inline comments
 
"""Test handling of tax-implication metadata"""
 
# Copyright © 2020  Brett Smith
 
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
 
#
 
# Full copyright and licensing details can be found at toplevel file
 
# LICENSE.txt in the repository.
 

	
 
import pytest
 

	
 
from . import testutil
 

	
 
from conservancy_beancount.plugin import meta_tax_implication
 

	
 
VALID_VALUES = {
 
    '1099': '1099-NEC',
 
    '1099-NEC': '1099-NEC',
 
    '1099nec': '1099-NEC',
 
    '1099-MISC-Other': '1099-MISC-Other',
 
    '1099-Misc-Other': '1099-MISC-Other',
 
    '1099misc-other': '1099-MISC-Other',
 
    'Bank-Transfer': 'Bank-Transfer',
 
    'Chargeback': 'Chargeback',
 
    'Foreign-Corporation': 'Foreign-Corporation',
 
    'foreign-corp': 'Foreign-Corporation',
 
    'Foreign-Grantee': 'Foreign-Grantee',
 
    'Foreign-Individual-Contractor': 'Foreign-Individual-Contractor',
 
    'Loan': 'Loan',
 
    'Refund': 'Refund',
 
    'Reimbursement': 'Reimbursement',
 
    'Retirement-Pretax': 'Retirement-Pretax',
 
    'Tax-Payment': 'Tax-Payment',
 
    'USA-501c3': 'USA-501c3',
 
    'USA-Corporation': 'USA-Corporation',
 
    'us-corp': 'USA-Corporation',
 
    'USA-Grantee': 'USA-Grantee',
 
    'US-Grantee': 'USA-Grantee',
 
    'W2': 'W2',
 
}
 

	
 
INVALID_VALUES = {
 
    '199',
 
    'W3',
 
    'Payrol',
 
    '',
 
}
 

	
 
TEST_KEY = 'tax-implication'
 

	
 
@pytest.fixture(scope='module')
 
def hook():
 
    config = testutil.TestConfig()
 
    return meta_tax_implication.MetaTaxImplication(config)
 

	
 
@pytest.mark.parametrize('src_value,set_value', VALID_VALUES.items())
 
def test_valid_values_on_postings(hook, src_value, set_value):
 
    txn = testutil.Transaction(postings=[
 
        ('Liabilities:Payable:Accounts', 25),
 
        ('Assets:Cash', -25, {TEST_KEY: src_value}),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert not errors
 
    testutil.check_post_meta(txn, None, {TEST_KEY: set_value})
 

	
 
@pytest.mark.parametrize('src_value', INVALID_VALUES)
 
def test_invalid_values_on_postings(hook, src_value):
 
    txn = testutil.Transaction(postings=[
 
        ('Liabilities:Payable:Accounts', 25),
 
        ('Assets:Cash', -25, {TEST_KEY: src_value}),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert errors
 
    testutil.check_post_meta(txn, None, {TEST_KEY: src_value})
 

	
 
@pytest.mark.parametrize('src_value,set_value', VALID_VALUES.items())
 
def test_valid_values_on_transactions(hook, src_value, set_value):
 
    txn = testutil.Transaction(**{TEST_KEY: src_value}, postings=[
 
        ('Liabilities:Payable:Accounts', 25),
 
        ('Assets:Cash', -25),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert not errors
 
    testutil.check_post_meta(txn, None, {TEST_KEY: set_value})
 

	
 
@pytest.mark.parametrize('src_value', INVALID_VALUES)
 
def test_invalid_values_on_transactions(hook, src_value):
 
    txn = testutil.Transaction(**{TEST_KEY: src_value}, postings=[
 
        ('Liabilities:Payable:Accounts', 25),
 
        ('Assets:Cash', -25),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert errors
 
    testutil.check_post_meta(txn, None, None)
 

	
 
@pytest.mark.parametrize('count,account', enumerate([
 
    'Assets:Payable:Accounts',
 
    'Assets:Prepaid:Expenses',
 
    'Equity:OpeningBalance',
 
    'Expenses:Other',
 
    'Income:Other',
 
    'Liabilities:CreditCard',
 
    'Liabilities:Payable:Accounts',
 
    'Liabilities:UnearnedIncome:Donations',
 
], 1))
 
def test_non_payment_accounts_skipped(hook, account, count):
 
    amount = count * 100
 
    meta = {TEST_KEY: 'USA-Corporation'}
 
    txn = testutil.Transaction(postings=[
 
        (account, amount),
 
        ('Assets:Checking', -amount, meta.copy()),
 
    ])
 
    errors = list(hook.run(txn))
 
    assert not errors
 
    testutil.check_post_meta(txn, None, meta)
 

	
 
def test_asset_credits_skipped(hook):
0 comments (0 inline, 0 general)