File diff 385f5a20da20 → a87d4bfc6c0c
tests/test_reports_balance_sheet.py
Show inline comments
 
new file 100644
 
"""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 <https://www.gnu.org/licenses/>.
 

	
 
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.OPENING, 'account': 'Income'}, '-9.60'),
 
    ({'period': Period.PERIOD, 'account': 'Expenses'}, 26),
 
    ({'fund': Fund.RESTRICTED, 'account': 'Income'}, -10),
 
    ({'fund': Fund.UNRESTRICTED, 'account': 'Expenses'}, 25),
 
    ({'post_type': 'Donations'}, -10),
 
    ({'post_type': 'fundraising'}, 20),
 
    ({'post_type': 'management'}, 10),
 
    ({'post_type': 'Nonexistent'}, None),
 
    ({'period': Period.OPENING, 'post_type': 'RBI'}, '-4.80'),
 
    ({'fund': Fund.RESTRICTED, 'post_type': 'program'}, 10),
 
    ({'period': Period.PERIOD, 'fund': Fund.UNRESTRICTED, 'post_type': 'RBI'}, '-2.60'),
 
    ({'period': Period.OPENING, 'fund': Fund.RESTRICTED, 'post_type': 'program'}, '4.80'),
 
    ({'period': Period.PERIOD, 'fund': Fund.RESTRICTED, 'post_type': 'ø'}, None),
 
])
 
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