From c712105bed3c5255cd9cb1162a971ed36964bd01 2020-04-03 14:34:10 From: Brett Smith Date: 2020-04-03 14:34:10 Subject: [PATCH] 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. --- diff --git a/conservancy_beancount/data.py b/conservancy_beancount/data.py index 3af7eff9bab6d6a3fa85c86a33cc08f0088b09f0..1af27ac65b64cf649492e8d1a9ed246b2de6327a 100644 --- a/conservancy_beancount/data.py +++ b/conservancy_beancount/data.py @@ -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,17 +66,17 @@ class Account(str): SEP = bc_account.sep - def is_checking(self) -> bool: - return self.is_real_asset() and ':Check' in self + 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_income(self) -> bool: - return self.is_under('Income:', 'UnearnedIncome:') is not None + def is_checking(self) -> bool: + return self.is_cash_equivalent() and ':Check' in self - def is_real_asset(self) -> bool: - return bool( - self.is_under('Assets:') - and not self.is_under('Assets:PrepaidExpenses', 'Assets:PrepaidVacation') - ) + 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]: diff --git a/conservancy_beancount/plugin/meta_entity.py b/conservancy_beancount/plugin/meta_entity.py index ee655f41111fc60745f0a2241291bc752f47a26f..78d562f356e7d2f6fd4889e0ef0c1a408035faa3 100644 --- a/conservancy_beancount/plugin/meta_entity.py +++ b/conservancy_beancount/plugin/meta_entity.py @@ -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: diff --git a/conservancy_beancount/plugin/meta_income_type.py b/conservancy_beancount/plugin/meta_income_type.py index 73038b63d754b7643785b71ea307349c972a563e..3834347b5d56434cc252ae7db9dc9eb99c3fa7de 100644 --- a/conservancy_beancount/plugin/meta_income_type.py +++ b/conservancy_beancount/plugin/meta_income_type.py @@ -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: diff --git a/conservancy_beancount/plugin/meta_invoice.py b/conservancy_beancount/plugin/meta_invoice.py index 5ab7f4342ef788948ab546304602b1d8d02efe83..a9e25194f23725fdd49e741c486bfae92400018d 100644 --- a/conservancy_beancount/plugin/meta_invoice.py +++ b/conservancy_beancount/plugin/meta_invoice.py @@ -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 diff --git a/conservancy_beancount/plugin/meta_project.py b/conservancy_beancount/plugin/meta_project.py index ccde207243496385c7cf6021cbab0c324c483d83..b47d9fe6ab20aaf16afb90bedc7ccdab513cf90f 100644 --- a/conservancy_beancount/plugin/meta_project.py +++ b/conservancy_beancount/plugin/meta_project.py @@ -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: diff --git a/conservancy_beancount/plugin/meta_receipt.py b/conservancy_beancount/plugin/meta_receipt.py index 9166409860ff40c62ded18240b8d1d7f1a224664..a524656e8c85b7cc10a30b6d8f493b9a308dcaab 100644 --- a/conservancy_beancount/plugin/meta_receipt.py +++ b/conservancy_beancount/plugin/meta_receipt.py @@ -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' diff --git a/conservancy_beancount/plugin/meta_receivable_documentation.py b/conservancy_beancount/plugin/meta_receivable_documentation.py index 9c79eb54e7e1ac45a42240aabf30d5ab3db54cd2..beb322b1599eb6c82d4c65f5720f51dcaf93a1ca 100644 --- a/conservancy_beancount/plugin/meta_receivable_documentation.py +++ b/conservancy_beancount/plugin/meta_receivable_documentation.py @@ -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. diff --git a/tests/test_data_account.py b/tests/test_data_account.py index b2901d4b76cce2eacda52bac4a04ae6f2d83f530..978030489c8b33aed95e3fe0bca50a57fa15a1d5 100644 --- a/tests/test_data_account.py +++ b/tests/test_data_account.py @@ -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 diff --git a/tests/test_data_posting.py b/tests/test_data_posting.py index 5f6dc05b18547dc793e86020bf4c889aca37a74f..708c940fc8b4e88fb8271d1bf32746fbe17c385d 100644 --- a/tests/test_data_posting.py +++ b/tests/test_data_posting.py @@ -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 = [ diff --git a/tests/test_meta_approval.py b/tests/test_meta_approval.py index 1d49825f3a64e164b19d6bf3faddbec7605c182c..90f01ec84f109d2446202918e2980ca9fd81e2f9 100644 --- a/tests/test_meta_approval.py +++ b/tests/test_meta_approval.py @@ -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' diff --git a/tests/test_meta_entity.py b/tests/test_meta_entity.py index 35f6ffa3f2cdfa5f3baa76a585ad25ccb420b99f..4542fa4769b22f9acb0296e98a58b92ef02a684d 100644 --- a/tests/test_meta_entity.py +++ b/tests/test_meta_entity.py @@ -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)) diff --git a/tests/test_meta_expense_allocation.py b/tests/test_meta_expense_allocation.py index c96f88e4dc6c390358b4237a9bb106d95938c884..af081d9e389cbe5a8e06110adfef7046edcbad16 100644 --- a/tests/test_meta_expense_allocation.py +++ b/tests/test_meta_expense_allocation.py @@ -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'} diff --git a/tests/test_meta_income_type.py b/tests/test_meta_income_type.py index 9adf4255305be9ec95e9d2584d4d13fd531f0489..0260bf071c852a7af5d1ab069ecb998b231b2550 100644 --- a/tests/test_meta_income_type.py +++ b/tests/test_meta_income_type.py @@ -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=[ diff --git a/tests/test_meta_invoice.py b/tests/test_meta_invoice.py index fcaa6ce8845844e2c4051f5dff0fb8f0ca5f0e0b..b4c86d49e9a4ed711247daf112a175715fe90488 100644 --- a/tests/test_meta_invoice.py +++ b/tests/test_meta_invoice.py @@ -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' diff --git a/tests/test_meta_project.py b/tests/test_meta_project.py index ae5d3f0286cfbd6166dcbfb4fde33781bb81d059..5c372dcccf5756ebac80640d709f158bccbdfc54 100644 --- a/tests/test_meta_project.py +++ b/tests/test_meta_project.py @@ -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 diff --git a/tests/test_meta_receipt.py b/tests/test_meta_receipt.py index 6f24302cab3f1ed3532a424f2d03c59708d1483e..82a1b021fc9caaf64de76d8135532d02610fca63 100644 --- a/tests/test_meta_receipt.py +++ b/tests/test_meta_receipt.py @@ -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, *, diff --git a/tests/test_meta_receivable_documentation.py b/tests/test_meta_receivable_documentation.py index 9dbf6de1cd8a069c1048ae8b0bc0b3ce08ff498d..853a9cabda4f683a7d32d330d675d2e90a1ccb41 100644 --- a/tests/test_meta_receivable_documentation.py +++ b/tests/test_meta_receivable_documentation.py @@ -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() diff --git a/tests/test_meta_tax_implication.py b/tests/test_meta_tax_implication.py index 2e98dcb623074b19c381b12c5e79140e2b87f242..62d80f79662d206a4cca46d0f99ab242bf613361 100644 --- a/tests/test_meta_tax_implication.py +++ b/tests/test_meta_tax_implication.py @@ -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),