"""Test Posting class""" # Copyright © 2020 Brett Smith # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import pytest from . import testutil from decimal import Decimal import beancount.core.amount as bc_amount from conservancy_beancount import data PAYMENT_ACCOUNTS = { 'Assets:Cash', 'Assets:Bank:Checking', } NON_PAYMENT_ACCOUNTS = { 'Assets:Prepaid:Expenses', 'Assets:Prepaid:Vacation', 'Assets:Receivable:Accounts', 'Equity:OpeningBalance', 'Expenses:Other', 'Income:Other', 'Liabilities:CreditCard', } AMOUNTS = [ None, '-25.50', 0, '25.75', ] def Posting(account, number, currency='USD', cost=None, price=None, flag=None, **meta): if not meta: meta = None if number is not None: number = Decimal(number) return data.Posting( data.Account(account), bc_amount.Amount(number, currency), cost, price, flag, meta, ) def check_all_thresholds(expected, method, threshold, *args): assert method(threshold, *args) is expected assert method(Decimal(threshold), *args) is expected @pytest.mark.parametrize('amount', AMOUNTS) def test_is_credit(amount): expected = None if amount is None else float(amount) > 0 assert Posting('Assets:Cash', amount).is_credit() is expected def test_is_credit_threshold(): post = Posting('Assets:Cash', 25) check_all_thresholds(True, post.is_credit, 0) check_all_thresholds(True, post.is_credit, 20) check_all_thresholds(False, post.is_credit, 40) def test_is_credit_default(): post = Posting('Assets:Cash', None) assert post.is_credit(default=True) is True assert post.is_credit(default=False) is False @pytest.mark.parametrize('amount', AMOUNTS) def test_is_debit(amount): expected = None if amount is None else float(amount) < 0 assert Posting('Assets:Cash', amount).is_debit() is expected def test_is_debit_threshold(): post = Posting('Assets:Cash', -25) check_all_thresholds(True, post.is_debit, 0) check_all_thresholds(True, post.is_debit, 20) check_all_thresholds(False, post.is_debit, 40) def test_is_debit_default(): post = Posting('Assets:Cash', None) assert post.is_debit(default=True) is True assert post.is_debit(default=False) is False @pytest.mark.parametrize('acct', PAYMENT_ACCOUNTS) def test_is_payment(acct): assert Posting(acct, -500).is_payment() @pytest.mark.parametrize('acct,amount,threshold', testutil.combine_values( NON_PAYMENT_ACCOUNTS, range(5, 20, 5), range(0, 30, 10), )) def test_is_not_payment_account(acct, amount, threshold): post = Posting(acct, -amount) assert not post.is_payment() check_all_thresholds(False, post.is_payment, threshold) @pytest.mark.parametrize('acct', PAYMENT_ACCOUNTS) def test_is_payment_with_threshold(acct): threshold = len(acct) * 10 post = Posting(acct, -500) check_all_thresholds(True, post.is_payment, threshold) @pytest.mark.parametrize('acct', PAYMENT_ACCOUNTS) def test_is_not_payment_by_threshold(acct): threshold = len(acct) * 10 post = Posting(acct, -9) check_all_thresholds(False, post.is_payment, threshold) @pytest.mark.parametrize('acct', PAYMENT_ACCOUNTS) def test_is_not_payment_but_credit(acct): post = Posting(acct, 9) assert not post.is_payment() check_all_thresholds(False, post.is_payment, 0) check_all_thresholds(False, post.is_payment, 5) check_all_thresholds(False, post.is_payment, 10)