Revise chart of accounts used throughout.
The main impetus of this change is to rename accounts that were outside Beancount's accepted five root accounts, to move them into that structure. This includes: Accrued:*Payable: → Liabilities:Payable:* Accrued:*Receivable: → Assets:Receivable:* UneanedIncome:* → Liabilities:UnearnedIncome:* Note the last change did inspire in a change to our validation rules. We no longer require income-type on unearned income, because it's no longer considered income at all. Once it's earned and converted to an Income account, that has an income-type of course. This did inspire another rename that was not required, but provided more consistency with the other account names above: Assets:Prepaid* → Assets:Prepaid:* Where applicable, I have generally extended tests to make sure one of each of the five account types is tested. (This mostly meant adding an Equity account to the tests.) I also added tests for key parts of the hierarchy, like Assets:Receivable and Liabilities:Payable, where applicable. As part of this change, Account.is_real_asset() got renamed to Account.is_cash_equivalent(), to better self-document its purpose.
This commit is contained in:
parent
21c7646b41
commit
c712105bed
18 changed files with 156 additions and 115 deletions
|
@ -58,7 +58,7 @@ LINK_METADATA = frozenset([
|
|||
class Account(str):
|
||||
"""Account name string
|
||||
|
||||
This is a string that names an account, like Accrued:AccountsPayable
|
||||
This is a string that names an account, like Assets:Bank:Checking
|
||||
or Income:Donations. This class provides additional methods for common
|
||||
account name parsing and queries.
|
||||
"""
|
||||
|
@ -66,18 +66,18 @@ class Account(str):
|
|||
|
||||
SEP = bc_account.sep
|
||||
|
||||
def is_checking(self) -> bool:
|
||||
return self.is_real_asset() and ':Check' in self
|
||||
|
||||
def is_income(self) -> bool:
|
||||
return self.is_under('Income:', 'UnearnedIncome:') is not None
|
||||
|
||||
def is_real_asset(self) -> bool:
|
||||
return bool(
|
||||
self.is_under('Assets:')
|
||||
and not self.is_under('Assets:PrepaidExpenses', 'Assets:PrepaidVacation')
|
||||
def is_cash_equivalent(self) -> bool:
|
||||
return (
|
||||
self.is_under('Assets:') is not None
|
||||
and self.is_under('Assets:Prepaid', 'Assets:Receivable') is None
|
||||
)
|
||||
|
||||
def is_checking(self) -> bool:
|
||||
return self.is_cash_equivalent() and ':Check' in self
|
||||
|
||||
def is_credit_card(self) -> bool:
|
||||
return self.is_under('Liabilities:CreditCard') is not None
|
||||
|
||||
def is_under(self, *acct_seq: str) -> Optional[str]:
|
||||
"""Return a match if this account is "under" a part of the hierarchy
|
||||
|
||||
|
@ -248,7 +248,7 @@ class Posting(BasePosting):
|
|||
threshold: DecimalCompat=0,
|
||||
default: Optional[bool]=None,
|
||||
) -> Optional[bool]:
|
||||
return self.account.is_real_asset() and self.is_debit(threshold, default)
|
||||
return self.account.is_cash_equivalent() and self.is_debit(threshold, default)
|
||||
|
||||
|
||||
def iter_postings(txn: Transaction) -> Iterator[Posting]:
|
||||
|
|
|
@ -59,7 +59,12 @@ class MetaEntity(core.TransactionHook):
|
|||
if txn_entity_ok is False:
|
||||
yield errormod.InvalidMetadataError(txn, self.METADATA_KEY, txn_entity)
|
||||
for post in data.iter_postings(txn):
|
||||
if post.account.is_under('Assets', 'Equity', 'Liabilities'):
|
||||
if not post.account.is_under(
|
||||
'Assets:Receivable',
|
||||
'Expenses',
|
||||
'Income',
|
||||
'Liabilities:Payable',
|
||||
):
|
||||
continue
|
||||
entity = post.meta.get(self.METADATA_KEY)
|
||||
if entity is None:
|
||||
|
|
|
@ -30,6 +30,8 @@ class MetaIncomeType(core._NormalizePostingMetadataHook):
|
|||
'UBTI',
|
||||
})
|
||||
DEFAULT_VALUES = {
|
||||
'Income:Conferences:Registrations': 'RBI',
|
||||
'Income:Conferences:Sponsorship': 'RBI',
|
||||
'Income:Donations': 'Donations',
|
||||
'Income:Honoraria': 'RBI',
|
||||
'Income:Interest': 'RBI',
|
||||
|
@ -38,12 +40,10 @@ class MetaIncomeType(core._NormalizePostingMetadataHook):
|
|||
'Income:Sales': 'RBI',
|
||||
'Income:SoftwareDevelopment': 'RBI',
|
||||
'Income:TrademarkLicensing': 'RBI',
|
||||
'UnearnedIncome:Conferences:Registrations': 'RBI',
|
||||
'UnearnedIncome:MatchPledges': 'Donations',
|
||||
}
|
||||
|
||||
def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
return post.account.is_income()
|
||||
return post.account.is_under('Income') is not None
|
||||
|
||||
def _default_value(self, txn: Transaction, post: data.Posting) -> MetaValueEnum:
|
||||
try:
|
||||
|
|
|
@ -26,4 +26,7 @@ class MetaInvoice(core._RequireLinksPostingMetadataHook):
|
|||
METADATA_KEY = 'invoice'
|
||||
|
||||
def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
return post.account.is_under('Accrued') is not None
|
||||
return post.account.is_under(
|
||||
'Assets:Receivable',
|
||||
'Liabilities:Payable',
|
||||
) is not None
|
||||
|
|
|
@ -79,12 +79,19 @@ class MetaProject(core._NormalizePostingMetadataHook):
|
|||
)
|
||||
|
||||
def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
return post.account.is_under('Assets', 'Equity', 'Liabilities') is None
|
||||
if post.account.is_under('Liabilities'):
|
||||
return not post.account.is_credit_card()
|
||||
else:
|
||||
return post.account.is_under(
|
||||
'Assets:Receivable',
|
||||
'Expenses',
|
||||
'Income',
|
||||
) is not None
|
||||
|
||||
def _default_value(self, txn: Transaction, post: data.Posting) -> MetaValueEnum:
|
||||
if post.account.is_under(
|
||||
'Accrued:VacationPayable',
|
||||
'Expenses:Payroll',
|
||||
'Liabilities:Payable:Vacation',
|
||||
):
|
||||
return self.DEFAULT_PROJECT
|
||||
else:
|
||||
|
|
|
@ -29,8 +29,8 @@ class MetaReceipt(core._RequireLinksPostingMetadataHook):
|
|||
self.payment_threshold = abs(config.payment_threshold())
|
||||
|
||||
def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
return bool(
|
||||
(post.account.is_real_asset() or post.account.is_under('Liabilities'))
|
||||
return (
|
||||
(post.account.is_cash_equivalent() or post.account.is_credit_card())
|
||||
and post.units.number is not None
|
||||
and abs(post.units.number) >= self.payment_threshold
|
||||
)
|
||||
|
@ -52,7 +52,7 @@ class MetaReceipt(core._RequireLinksPostingMetadataHook):
|
|||
|
||||
if post.account.is_checking():
|
||||
fallback_key = 'check'
|
||||
elif post.account.is_under('Liabilities:CreditCard') and post_amount == -1:
|
||||
elif post.account.is_credit_card() and post_amount == -1:
|
||||
fallback_key = 'invoice'
|
||||
elif post.account.is_under('Assets:PayPal') and post_amount == 1:
|
||||
fallback_key = 'paypal-id'
|
||||
|
|
|
@ -57,7 +57,7 @@ class MetaReceivableDocumentation(core._RequireLinksPostingMetadataHook):
|
|||
self.rt = rt_wrapper
|
||||
|
||||
def _run_on_post(self, txn: Transaction, post: data.Posting) -> bool:
|
||||
if not post.account.is_under('Accrued:AccountsReceivable'):
|
||||
if not post.account.is_under('Assets:Receivable'):
|
||||
return False
|
||||
|
||||
# Get the first invoice, or return False if it doesn't exist.
|
||||
|
|
|
@ -26,55 +26,46 @@ from conservancy_beancount import data
|
|||
('Expenses:Tax:Sales', 'Expenses:', True),
|
||||
('Expenses:Tax:Sales', 'Expenses', True),
|
||||
('Expenses:Tax:Sales', 'Expense', False),
|
||||
('Expenses:Tax:Sales', 'Accrued:', False),
|
||||
('Expenses:Tax:Sales', 'Accrued', False),
|
||||
('Expenses:Tax:Sales', 'Equity:', False),
|
||||
('Expenses:Tax:Sales', 'Equity', False),
|
||||
])
|
||||
def test_is_under_one_arg(acct_name, under_arg, expected):
|
||||
expected = under_arg if expected else None
|
||||
assert data.Account(acct_name).is_under(under_arg) == expected
|
||||
|
||||
@pytest.mark.parametrize('acct_name,expected', [
|
||||
('Income:Other', 'Income'),
|
||||
('UnearnedIncome:Other', 'UnearnedIncome'),
|
||||
('Accrued:AccountsPayable', None),
|
||||
('Expenses:General', None),
|
||||
('Assets:Cash', None),
|
||||
('Assets:Checking', None),
|
||||
('Assets:Prepaid:Expenses', 'Assets:Prepaid'),
|
||||
('Assets:Receivable:Accounts', 'Assets:Receivable'),
|
||||
])
|
||||
def test_is_under_multi_arg(acct_name, expected):
|
||||
assert data.Account(acct_name).is_under('Income', 'UnearnedIncome') == expected
|
||||
assert expected == data.Account(acct_name).is_under(
|
||||
'Assets:Prepaid', 'Assets:Receivable',
|
||||
)
|
||||
if expected:
|
||||
expected += ':'
|
||||
assert data.Account(acct_name).is_under('Income:', 'UnearnedIncome:') == expected
|
||||
assert expected == data.Account(acct_name).is_under(
|
||||
'Assets:Prepaid:', 'Assets:Receivable:',
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize('acct_name,expected', [
|
||||
('Accrued:AccountsReceivable', False),
|
||||
('Assets:Cash', False),
|
||||
('Expenses:General', False),
|
||||
('Income:Donations', True),
|
||||
('Income:Sales', True),
|
||||
('Income:Other', True),
|
||||
('Liabilities:CreditCard', False),
|
||||
('UnearnedIncome:MatchPledges', True),
|
||||
])
|
||||
def test_is_income(acct_name, expected):
|
||||
assert data.Account(acct_name).is_income() == expected
|
||||
|
||||
@pytest.mark.parametrize('acct_name,expected', [
|
||||
('Accrued:AccountsPayable', False),
|
||||
('Accrued:AccountsReceivable', False),
|
||||
('Assets:Bank:Checking', True),
|
||||
('Assets:Cash', True),
|
||||
('Assets:Cash:EUR', True),
|
||||
('Assets:PrepaidExpenses', False),
|
||||
('Assets:PrepaidVacation', False),
|
||||
('Assets:Bank:Checking', True),
|
||||
('Expenses:General', False),
|
||||
('Income:Donations', False),
|
||||
('Assets:Prepaid:Expenses', False),
|
||||
('Assets:Prepaid:Vacation', False),
|
||||
('Assets:Receivable:Accounts', False),
|
||||
('Assets:Receivable:Fraud', False),
|
||||
('Expenses:Other', False),
|
||||
('Equity:OpeningBalance', False),
|
||||
('Income:Other', False),
|
||||
('Liabilities:CreditCard', False),
|
||||
])
|
||||
def test_is_real_asset(acct_name, expected):
|
||||
assert data.Account(acct_name).is_real_asset() == expected
|
||||
def test_is_cash_equivalent(acct_name, expected):
|
||||
assert data.Account(acct_name).is_cash_equivalent() == expected
|
||||
|
||||
@pytest.mark.parametrize('acct_name,expected', [
|
||||
('Accrued:AccountsPayable', False),
|
||||
('Accrued:AccountsReceivable', False),
|
||||
('Assets:Bank:Check9999', True),
|
||||
('Assets:Bank:CheckCard', True),
|
||||
('Assets:Bank:Checking', True),
|
||||
|
@ -83,10 +74,27 @@ def test_is_real_asset(acct_name, expected):
|
|||
('Assets:Check9999', True),
|
||||
('Assets:CheckCard', True),
|
||||
('Assets:Checking', True),
|
||||
('Assets:PrepaidExpenses', False),
|
||||
('Assets:Savings', False),
|
||||
('Expenses:CheckingFees', False),
|
||||
('Income:Interest:Checking', False),
|
||||
('Assets:Prepaid:Expenses', False),
|
||||
('Assets:Receivable:Accounts', False),
|
||||
('Expenses:Other', False),
|
||||
('Equity:OpeningBalance', False),
|
||||
('Income:Other', False),
|
||||
('Liabilities:CreditCard', False),
|
||||
])
|
||||
def test_is_checking(acct_name, expected):
|
||||
assert data.Account(acct_name).is_checking() == expected
|
||||
|
||||
@pytest.mark.parametrize('acct_name,expected', [
|
||||
('Assets:Cash', False),
|
||||
('Assets:Prepaid:Expenses', False),
|
||||
('Assets:Receivable:Accounts', False),
|
||||
('Expenses:Other', False),
|
||||
('Equity:OpeningBalance', False),
|
||||
('Income:Other', False),
|
||||
('Liabilities:CreditCard', True),
|
||||
('Liabilities:CreditCard:Visa', True),
|
||||
('Liabilities:Payable:Accounts', False),
|
||||
('Liabilities:UnearnedIncome:Donations', False),
|
||||
])
|
||||
def test_is_credit_card(acct_name, expected):
|
||||
assert data.Account(acct_name).is_credit_card() == expected
|
||||
|
|
|
@ -26,17 +26,17 @@ from conservancy_beancount import data
|
|||
|
||||
PAYMENT_ACCOUNTS = {
|
||||
'Assets:Cash',
|
||||
'Assets:Checking',
|
||||
'Assets:Bank:Checking',
|
||||
}
|
||||
|
||||
NON_PAYMENT_ACCOUNTS = {
|
||||
'Accrued:AccountsReceivable',
|
||||
'Assets:PrepaidExpenses',
|
||||
'Assets:PrepaidVacation',
|
||||
'Assets:Prepaid:Expenses',
|
||||
'Assets:Prepaid:Vacation',
|
||||
'Assets:Receivable:Accounts',
|
||||
'Equity:OpeningBalance',
|
||||
'Expenses:Other',
|
||||
'Income:Other',
|
||||
'Liabilities:CreditCard',
|
||||
'UnearnedIncome:MatchPledges',
|
||||
}
|
||||
|
||||
AMOUNTS = [
|
||||
|
|
|
@ -21,18 +21,18 @@ from . import testutil
|
|||
from conservancy_beancount.plugin import meta_approval
|
||||
|
||||
REQUIRED_ACCOUNTS = {
|
||||
'Assets:Bank:Checking',
|
||||
'Assets:Cash',
|
||||
'Assets:Checking',
|
||||
'Assets:Savings',
|
||||
}
|
||||
|
||||
NON_REQUIRED_ACCOUNTS = {
|
||||
'Accrued:AccountsPayable',
|
||||
'Assets:PrepaidExpenses',
|
||||
'Assets:PrepaidVacation',
|
||||
'Assets:Prepaid:Expenses',
|
||||
'Assets:Receivable:Accounts',
|
||||
'Equity:QpeningBalance',
|
||||
'Expenses:Other',
|
||||
'Income:Other',
|
||||
'UnearnedIncome:Donations',
|
||||
'Liabilities:Payable:Accounts',
|
||||
}
|
||||
|
||||
CREDITCARD_ACCOUNT = 'Liabilities:CreditCard'
|
||||
|
|
|
@ -111,17 +111,21 @@ def test_invalid_values_on_transactions(hook, src_value):
|
|||
for error in hook.run(txn))
|
||||
|
||||
@pytest.mark.parametrize('account,required', [
|
||||
('Accrued:AccountsReceivable', True),
|
||||
('Assets:Bank:Checking', False),
|
||||
('Assets:Cash', False),
|
||||
('Assets:Receivable:Accounts', True),
|
||||
('Assets:Receivable:Loans', True),
|
||||
('Equity:OpeningBalances', False),
|
||||
('Expenses:General', True),
|
||||
('Income:Donations', True),
|
||||
('Liabilities:CreditCard', False),
|
||||
('UnearnedIncome:Donations', True),
|
||||
('Liabilities:Payable:Accounts', True),
|
||||
('Liabilities:Payable:Vacation', True),
|
||||
('Liabilities:UnearnedIncome:Donations', False),
|
||||
])
|
||||
def test_which_accounts_required_on(hook, account, required):
|
||||
txn = testutil.Transaction(postings=[
|
||||
('Assets:Checking', 25),
|
||||
('Assets:Checking', -25),
|
||||
(account, 25),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
|
|
|
@ -83,11 +83,12 @@ def test_invalid_values_on_transactions(hook, src_value):
|
|||
testutil.check_post_meta(txn, None, None)
|
||||
|
||||
@pytest.mark.parametrize('account', [
|
||||
'Accrued:AccountsReceivable',
|
||||
'Assets:Cash',
|
||||
'Income:Donations',
|
||||
'Assets:Receivable:Accounts',
|
||||
'Equity:OpeningBalance',
|
||||
'Income:Other',
|
||||
'Liabilities:CreditCard',
|
||||
'UnearnedIncome:Donations',
|
||||
'Liabilities:Payable:Vacation',
|
||||
])
|
||||
def test_non_expense_accounts_skipped(hook, account):
|
||||
meta = {TEST_KEY: 'program'}
|
||||
|
|
|
@ -83,10 +83,12 @@ def test_invalid_values_on_transactions(hook, src_value):
|
|||
testutil.check_post_meta(txn, None, None)
|
||||
|
||||
@pytest.mark.parametrize('account', [
|
||||
'Accrued:AccountsReceivable',
|
||||
'Assets:Cash',
|
||||
'Expenses:General',
|
||||
'Assets:Receivable:Accounts',
|
||||
'Equity:OpeningBalance',
|
||||
'Expenses:Other',
|
||||
'Liabilities:CreditCard',
|
||||
'Liabilities:Payable:Vacation',
|
||||
])
|
||||
def test_non_income_accounts_skipped(hook, account):
|
||||
meta = {TEST_KEY: 'RBI'}
|
||||
|
@ -99,6 +101,8 @@ def test_non_income_accounts_skipped(hook, account):
|
|||
testutil.check_post_meta(txn, None, meta)
|
||||
|
||||
@pytest.mark.parametrize('account,set_value', [
|
||||
('Income:Conferences:Registrations', 'RBI'),
|
||||
('Income:Conferences:Sponsorship', 'RBI'),
|
||||
('Income:Donations', 'Donations'),
|
||||
('Income:Honoraria', 'RBI'),
|
||||
('Income:Interest', 'RBI'),
|
||||
|
@ -107,8 +111,6 @@ def test_non_income_accounts_skipped(hook, account):
|
|||
('Income:Sales', 'RBI'),
|
||||
('Income:SoftwareDevelopment', 'RBI'),
|
||||
('Income:TrademarkLicensing', 'RBI'),
|
||||
('UnearnedIncome:Conferences:Registrations', 'RBI'),
|
||||
('UnearnedIncome:MatchPledges', 'Donations'),
|
||||
])
|
||||
def test_default_values(hook, account, set_value):
|
||||
txn = testutil.Transaction(postings=[
|
||||
|
|
|
@ -21,16 +21,18 @@ from . import testutil
|
|||
from conservancy_beancount.plugin import meta_invoice
|
||||
|
||||
REQUIRED_ACCOUNTS = {
|
||||
'Accrued:AccountsPayable',
|
||||
'Accrued:AccountsReceivable',
|
||||
'Assets:Receivable:Accounts',
|
||||
'Assets:Receivable:Loans',
|
||||
'Liabilities:Payable:Accounts',
|
||||
'Liabilities:Payable:Vacation',
|
||||
}
|
||||
|
||||
NON_REQUIRED_ACCOUNTS = {
|
||||
'Assets:Cash',
|
||||
'Equity:OpeningBalance',
|
||||
'Expenses:Other',
|
||||
'Income:Other',
|
||||
'Liabilities:CreditCard',
|
||||
'UnearnedIncome:Donations',
|
||||
}
|
||||
|
||||
TEST_KEY = 'invoice'
|
||||
|
|
|
@ -86,13 +86,17 @@ def test_invalid_values_on_transactions(hook, src_value):
|
|||
testutil.check_post_meta(txn, None, None)
|
||||
|
||||
@pytest.mark.parametrize('account,required', [
|
||||
('Accrued:AccountsReceivable', True),
|
||||
('Assets:Cash', False),
|
||||
('Equity:Opening-Balances', False),
|
||||
('Assets:Receivable:Accounts', True),
|
||||
('Assets:Receivable:Loans', True),
|
||||
('Equity:OpeningBalance', False),
|
||||
('Expenses:General', True),
|
||||
('Income:Donations', True),
|
||||
('Liabilities:CreditCard', False),
|
||||
('UnearnedIncome:Donations', True),
|
||||
('Liabilities:Payable:Accounts', True),
|
||||
# We do want a "project" for Lia:Pay:Vacation but it has a default value
|
||||
('Liabilities:Payable:Vacation', False),
|
||||
('Liabilities:UnearnedIncome:Donations', True),
|
||||
])
|
||||
def test_which_accounts_required_on(hook, account, required):
|
||||
txn = testutil.Transaction(postings=[
|
||||
|
@ -103,9 +107,9 @@ def test_which_accounts_required_on(hook, account, required):
|
|||
assert required == any(errors)
|
||||
|
||||
@pytest.mark.parametrize('account', [
|
||||
'Accrued:VacationPayable',
|
||||
'Expenses:Payroll:Salary',
|
||||
'Expenses:Payroll:Tax',
|
||||
'Liabilities:Payable:Vacation',
|
||||
])
|
||||
def test_default_values(hook, account):
|
||||
txn = testutil.Transaction(postings=[
|
||||
|
@ -126,7 +130,7 @@ def test_default_values(hook, account):
|
|||
def test_default_value_set_in_date_range(hook, date, required):
|
||||
txn = testutil.Transaction(date=date, postings=[
|
||||
('Expenses:Payroll:Benefits', 25),
|
||||
('Accrued:VacationPayable', -25),
|
||||
('Liabilities:Payable:Vacation', -25),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
assert not errors
|
||||
|
|
|
@ -77,13 +77,13 @@ KNOWN_FALLBACKS = {acct.fallback_meta for acct in ACCOUNTS_WITH_FALLBACKS}
|
|||
# doesn't require the decorated test to go over every value, which in turn
|
||||
# trims unnecessary test time.
|
||||
NOT_REQUIRED_ACCOUNTS = itertools.cycle([
|
||||
'Accrued:AccountsPayable',
|
||||
'Accrued:AccountsReceivable',
|
||||
'Assets:PrepaidExpenses',
|
||||
'Assets:PrepaidVacation',
|
||||
'Assets:Prepaid:Expenses',
|
||||
'Assets:Receivable:Accounts',
|
||||
'Equity:OpeningBalance',
|
||||
'Expenses:Other',
|
||||
'Income:Other',
|
||||
'UnearnedIncome:Donations',
|
||||
'Liabilities:Payable:Accounts',
|
||||
'Liabilities:UnearnedIncome:Donations',
|
||||
])
|
||||
|
||||
def check(hook, test_acct, other_acct, expected, *,
|
||||
|
|
|
@ -23,7 +23,7 @@ from . import testutil
|
|||
from conservancy_beancount import errors as errormod
|
||||
from conservancy_beancount.plugin import meta_receivable_documentation
|
||||
|
||||
TEST_ACCT = 'Accrued:AccountsReceivable'
|
||||
TEST_ACCT = 'Assets:Receivable:Accounts'
|
||||
OTHER_ACCT = 'Income:Donations'
|
||||
|
||||
SUPPORTING_METADATA = [
|
||||
|
@ -178,9 +178,17 @@ def test_type_errors_reported_with_valid_txn_docs(hook, invoice, support_key, su
|
|||
def test_received_invoices_not_checked(hook, invoice, meta_type):
|
||||
check(hook, None, **{meta_type: {'invoice': invoice}})
|
||||
|
||||
def test_does_not_apply_to_payables(hook):
|
||||
@pytest.mark.parametrize('account', [
|
||||
'Assets:Bank:Checking',
|
||||
'Assets:Cash',
|
||||
'Equity:OpeningBalance',
|
||||
'Expenses:BankingFees',
|
||||
'Liabilities:CreditCard',
|
||||
'Liabilities:Payable:Accounts',
|
||||
])
|
||||
def test_does_not_apply_to_other_accounts(hook, account):
|
||||
meta = seed_meta()
|
||||
check(hook, None, 'Accrued:AccountsPayable', 'Expenses:Other', post_meta=meta)
|
||||
check(hook, None, account, 'Expenses:Other', post_meta=meta)
|
||||
|
||||
def test_configuration_error_without_rt():
|
||||
config = testutil.TestConfig()
|
||||
|
|
|
@ -57,7 +57,7 @@ def hook():
|
|||
@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=[
|
||||
('Accrued:AccountsPayable', 25),
|
||||
('Liabilities:Payable:Accounts', 25),
|
||||
('Assets:Cash', -25, {TEST_KEY: src_value}),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
|
@ -67,7 +67,7 @@ def test_valid_values_on_postings(hook, src_value, set_value):
|
|||
@pytest.mark.parametrize('src_value', INVALID_VALUES)
|
||||
def test_invalid_values_on_postings(hook, src_value):
|
||||
txn = testutil.Transaction(postings=[
|
||||
('Accrued:AccountsPayable', 25),
|
||||
('Liabilities:Payable:Accounts', 25),
|
||||
('Assets:Cash', -25, {TEST_KEY: src_value}),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
|
@ -77,7 +77,7 @@ def test_invalid_values_on_postings(hook, 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=[
|
||||
('Accrued:AccountsPayable', 25),
|
||||
('Liabilities:Payable:Accounts', 25),
|
||||
('Assets:Cash', -25),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
|
@ -87,37 +87,34 @@ def test_valid_values_on_transactions(hook, src_value, 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=[
|
||||
('Accrued:AccountsPayable', 25),
|
||||
('Liabilities:Payable:Accounts', 25),
|
||||
('Assets:Cash', -25),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
assert errors
|
||||
testutil.check_post_meta(txn, None, None)
|
||||
|
||||
@pytest.mark.parametrize('account', [
|
||||
'Accrued:AccountsPayable',
|
||||
'Expenses:General',
|
||||
@pytest.mark.parametrize('count,account', enumerate([
|
||||
'Assets:Payable:Accounts',
|
||||
'Assets:Prepaid:Expenses',
|
||||
'Equity:OpeningBalance',
|
||||
'Expenses:Other',
|
||||
'Income:Other',
|
||||
'Liabilities:CreditCard',
|
||||
])
|
||||
def test_non_asset_accounts_skipped(hook, account):
|
||||
'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, 25),
|
||||
('Assets:Cash', -25, meta.copy()),
|
||||
(account, amount),
|
||||
('Assets:Checking', -amount, meta.copy()),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
assert not errors
|
||||
testutil.check_post_meta(txn, None, meta)
|
||||
|
||||
def test_prepaid_expenses_skipped(hook, ):
|
||||
txn = testutil.Transaction(postings=[
|
||||
('Expenses:General', 25),
|
||||
('Assets:PrepaidExpenses', -25),
|
||||
])
|
||||
errors = list(hook.run(txn))
|
||||
assert not errors
|
||||
testutil.check_post_meta(txn, None, None)
|
||||
|
||||
def test_asset_credits_skipped(hook, ):
|
||||
txn = testutil.Transaction(postings=[
|
||||
('Income:Donations', -25),
|
||||
|
|
Loading…
Reference in a new issue