Files @ 5784068904e8
Branch filter:

Location: NPO-Accounting/conservancy_beancount/tests/test_meta_tax_implication.py - annotation

bkuhn
payroll-type — US:403b:Employee:Roth — needed separate since taxable

Since Roth contributions are taxable, there are some reports that
need to include these amounts in total salary (i.e., when running a
report that seeks to show total taxable income for an employee). As
such, we need a `payroll-type` specifically for Roth 403(b)
contributions.
c9ff4ab74617
dbe9362987c0
1b7fdf4f3b00
dbe9362987c0
1b7fdf4f3b00
1b7fdf4f3b00
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
680bb6e30528
680bb6e30528
680bb6e30528
328f59231c08
680bb6e30528
dbe9362987c0
be35f36d26ba
dbe9362987c0
5c6d9d6f69e3
31cee4e0ba15
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
5c6d9d6f69e3
31cee4e0ba15
31cee4e0ba15
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
c9ff4ab74617
c9ff4ab74617
501bd251cbf3
501bd251cbf3
0d370c445b9e
0d370c445b9e
501bd251cbf3
dbe9362987c0
501bd251cbf3
dbe9362987c0
c712105bed3c
c9ff4ab74617
dbe9362987c0
501bd251cbf3
dbe9362987c0
e7720b8fb866
dbe9362987c0
dbe9362987c0
501bd251cbf3
dbe9362987c0
c712105bed3c
c9ff4ab74617
dbe9362987c0
501bd251cbf3
dbe9362987c0
e7720b8fb866
dbe9362987c0
dbe9362987c0
501bd251cbf3
c9ff4ab74617
c712105bed3c
dbe9362987c0
dbe9362987c0
501bd251cbf3
dbe9362987c0
e7720b8fb866
dbe9362987c0
dbe9362987c0
501bd251cbf3
c9ff4ab74617
c712105bed3c
dbe9362987c0
dbe9362987c0
501bd251cbf3
dbe9362987c0
e7720b8fb866
dbe9362987c0
c712105bed3c
c712105bed3c
c712105bed3c
c712105bed3c
c712105bed3c
c712105bed3c
dbe9362987c0
c712105bed3c
c712105bed3c
c712105bed3c
c712105bed3c
c712105bed3c
e7720b8fb866
dbe9362987c0
c712105bed3c
c712105bed3c
dbe9362987c0
501bd251cbf3
dbe9362987c0
e7720b8fb866
dbe9362987c0
e3e782c0280f
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
501bd251cbf3
dbe9362987c0
e7720b8fb866
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
501bd251cbf3
dbe9362987c0
dbe9362987c0
dbe9362987c0
dbe9362987c0
501bd251cbf3
dbe9362987c0
e7720b8fb866
552ef45f47df
552ef45f47df
fdd9f2847b78
552ef45f47df
552ef45f47df
552ef45f47df
552ef45f47df
fdd9f2847b78
fdd9f2847b78
fdd9f2847b78
fdd9f2847b78
fdd9f2847b78
fdd9f2847b78
fdd9f2847b78
fdd9f2847b78
552ef45f47df
fdd9f2847b78
"""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',
    '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):
    txn = testutil.Transaction(postings=[
        ('Income:Donations', -25),
        ('Assets:Cash', 25),
    ])
    errors = list(hook.run(txn))
    assert not errors
    testutil.check_post_meta(txn, None, None)

@pytest.mark.parametrize('date,need_value', [
    (testutil.EXTREME_FUTURE_DATE, False),
    (testutil.FUTURE_DATE, True),
    (testutil.FY_START_DATE, True),
    (testutil.FY_MID_DATE, True),
    (testutil.PAST_DATE, False),
])
def test_validation_only_in_date_range(hook, date, need_value):
    txn = testutil.Transaction(date=date, postings=[
        ('Liabilites:CreditCard', 25),
        ('Assets:Cash', -25),
    ])
    errors = list(hook.run(txn))
    assert bool(errors) == bool(need_value)
    testutil.check_post_meta(txn, None, None)

@pytest.mark.parametrize('src_value', INVALID_VALUES)
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})