"""test_reports_balances.py - Unit tests for Balances 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 datetime import itertools import pytest from . import testutil from beancount.core.data import Open from conservancy_beancount import data from conservancy_beancount.reports import core Fund = core.Fund Period = core.Period clean_account_meta = pytest.fixture(scope='module')(testutil.clean_account_meta) @pytest.fixture(scope='module') def income_expense_entries(): txns = [] prior_date = datetime.date(2019, 2, 2) period_date = datetime.date(2019, 4, 4) for (acct, post_meta), fund in itertools.product([ ('Income:Donations', 'Donations'), ('Income:Sales', 'RBI'), ('Expenses:Postage', 'fundraising'), ('Expenses:Postage', 'management'), ('Expenses:Postage', 'program'), ('Expenses:Services', 'fundraising'), ('Expenses:Services', 'program'), ], ['Conservancy', 'Alpha']): root_acct, _, classification = acct.partition(':') try: data.Account(acct).meta except KeyError: data.Account.load_opening(Open( {'classification': classification}, datetime.date(2000, 1, 1), acct, None, None, )) meta = { 'project': fund, f'{root_acct.lower().rstrip("s")}-type': post_meta, } sign = '' if root_acct == 'Expenses' else '-' txns.append(testutil.Transaction(date=prior_date, postings=[ (acct, f'{sign}2.40', meta), ])) txns.append(testutil.Transaction(date=period_date, postings=[ (acct, f'{sign}2.60', meta), ])) return txns @pytest.fixture(scope='module') def expense_balances(income_expense_entries): return core.Balances( data.Posting.from_entries(income_expense_entries), datetime.date(2019, 3, 1), datetime.date(2020, 3, 1), 'expense-type', ) @pytest.fixture(scope='module') def income_balances(income_expense_entries): return core.Balances( data.Posting.from_entries(income_expense_entries), datetime.date(2019, 3, 1), datetime.date(2020, 3, 1), 'income-type', ) @pytest.mark.parametrize('kwargs,expected', [ ({'account': 'Income:Donations'}, -10), ({'account': 'Income'}, -20), ({'account': 'Income:Nonexistent'}, None), ({'classification': 'Postage'}, 30), ({'classification': 'Services'}, 20), ({'classification': 'Nonexistent'}, None), ({'period': Period.PRIOR, 'account': 'Income'}, '-9.60'), ({'period': Period.PERIOD, 'account': 'Expenses'}, 26), ({'fund': Fund.RESTRICTED, 'account': 'Income'}, -10), ({'fund': Fund.UNRESTRICTED, 'account': 'Expenses'}, 25), ({'post_meta': 'fundraising'}, 20), ({'post_meta': 'management'}, 10), ({'post_meta': 'Donations'}, None), ({'post_meta': 'RBI'}, None), ({'period': Period.PRIOR, 'post_meta': 'fundraising'}, '9.60'), ({'fund': Fund.RESTRICTED, 'post_meta': 'program'}, 10), ({'period': Period.PRIOR, 'fund': Fund.RESTRICTED, 'post_meta': 'program'}, '4.80'), ({'period': Period.PERIOD, 'fund': Fund.RESTRICTED, 'post_meta': 'ø'}, None), ({'account': ('Income', 'Expenses')}, 30), ({'account': ('Income', 'Expenses'), 'fund': Fund.UNRESTRICTED}, 15), ]) def test_expense_balance_total(expense_balances, kwargs, expected): actual = expense_balances.total(**kwargs) if expected is None: assert not actual else: assert actual == {'USD': testutil.Amount(expected)} @pytest.mark.parametrize('kwargs,expected', [ ({'post_meta': 'fundraising'}, None), ({'post_meta': 'management'}, None), ({'post_meta': 'Donations'}, -10), ({'post_meta': 'RBI'}, -10), ({'period': Period.PRIOR, 'post_meta': 'Donations'}, '-4.80'), ({'fund': Fund.RESTRICTED, 'post_meta': 'RBI'}, -5), ({'period': Period.PRIOR, 'fund': Fund.RESTRICTED, 'post_meta': 'Donations'}, '-2.40'), ({'period': Period.PERIOD, 'fund': Fund.RESTRICTED, 'post_meta': 'ø'}, None), ]) def test_income_balance_total(income_balances, kwargs, expected): actual = income_balances.total(**kwargs) if expected is None: assert not actual else: assert actual == {'USD': testutil.Amount(expected)}