Files @ 5a8da108b983
Branch filter:

Location: NPO-Accounting/conservancy_beancount/tests/test_reports_balances.py - annotation

bsturmfels
statement_reconciler: Add initial Chase bank CSV statement matching

We currently don't have many examples to work with, so haven't done any
significant testing of the matching accuracy between statement and books.
ffc20b68996e
ffc20b68996e
1b7fdf4f3b00
ffc20b68996e
1b7fdf4f3b00
1b7fdf4f3b00
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
5e147dc0b557
ffc20b68996e
ffc20b68996e
ffc20b68996e
5e147dc0b557
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
5e147dc0b557
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
ffc20b68996e
5e147dc0b557
ffc20b68996e
ffc20b68996e
5e147dc0b557
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
ffc20b68996e
ffc20b68996e
ffc20b68996e
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
5e147dc0b557
ffc20b68996e
ffc20b68996e
ffc20b68996e
ffc20b68996e
"""test_reports_balances.py - Unit tests for Balances class"""
# Copyright © 2020  Brett Smith
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
#
# Full copyright and licensing details can be found at toplevel file
# LICENSE.txt in the repository.

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)}