"""test_reports_balance_sheet.py - Unit tests for balance sheet report""" # 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 io import itertools from decimal import Decimal import pytest from . import testutil import odf.opendocument from beancount.core.data import Open from conservancy_beancount import data from conservancy_beancount.reports import balance_sheet Fund = balance_sheet.Fund Period = balance_sheet.Period clean_account_meta = pytest.fixture(scope='module')(testutil.clean_account_meta) @pytest.fixture(scope='module') def income_expense_balances(): txns = [] prior_date = datetime.date(2019, 2, 2) period_date = datetime.date(2019, 4, 4) for (acct, post_type), 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_type, } 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 balance_sheet.Balances( data.Posting.from_entries(txns), datetime.date(2019, 3, 1), datetime.date(2020, 3, 1), ) @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_type': 'fundraising'}, 20), ({'post_type': 'management'}, 10), ({'post_type': 'Nonexistent'}, None), ({'period': Period.PRIOR, 'post_type': 'fundraising'}, '9.60'), ({'fund': Fund.RESTRICTED, 'post_type': 'program'}, 10), ({'period': Period.PRIOR, 'fund': Fund.RESTRICTED, 'post_type': 'program'}, '4.80'), ({'period': Period.PERIOD, 'fund': Fund.RESTRICTED, 'post_type': 'ø'}, None), ({'account': ('Income', 'Expenses')}, 30), ({'account': ('Income', 'Expenses'), 'fund': Fund.UNRESTRICTED}, 15), ]) def test_balance_total(income_expense_balances, kwargs, expected): actual = income_expense_balances.total(**kwargs) if expected is None: assert not actual else: assert actual == {'USD': testutil.Amount(expected)} def run_main(arglist=[], config=None): if config is None: config = testutil.TestConfig(books_path=testutil.test_path('books/fund.beancount')) stdout = io.BytesIO() stderr = io.StringIO() retcode = balance_sheet.main(['-O', '-'] + arglist, stdout, stderr, config) stdout.seek(0) stderr.seek(0) return retcode, stdout, stderr def test_main(): retcode, stdout, stderr = run_main() assert retcode == 0 assert not stderr.getvalue() report = odf.opendocument.load(stdout) assert report.spreadsheet.childNodes