File diff dd949a48669a → b37d7a302407
tests/test_reports_accrual.py
Show inline comments
...
 
@@ -94,8 +94,8 @@ def check_link_regexp(regexp, match_s, first_link_only=False):
 
    else:
 
        assert end_match
 

	
 
def relate_accruals_by_meta(postings, value, key='invoice'):
 
    return core.RelatedPostings(
 
def accruals_by_meta(postings, value, key='invoice', wrap_type=iter):
 
    return wrap_type(
 
        post for post in postings
 
        if post.meta.get(key) == value
 
        and post.account.is_under('Assets:Receivable', 'Liabilities:Payable')
...
 
@@ -200,22 +200,107 @@ def test_report_type_by_unknown_name(arg):
 
    with pytest.raises(ValueError):
 
        accrual.ReportType.by_name(arg)
 

	
 
@pytest.mark.parametrize('acct_name', ACCOUNTS)
 
def test_accrual_postings_consistent_account(acct_name):
 
    meta = {'invoice': '{acct_name} invoice.pdf'}
 
    txn = testutil.Transaction(postings=[
 
        (acct_name, 50, meta),
 
        (acct_name, 25, meta),
 
    ])
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    assert related.account == acct_name
 
    assert related.accounts == {acct_name}
 

	
 
@pytest.mark.parametrize('cost', [
 
    testutil.Cost('1.2', 'USD'),
 
    None,
 
])
 
def test_accrual_postings_consistent_cost(cost):
 
    meta = {'invoice': 'FXinvoice.pdf'}
 
    txn = testutil.Transaction(postings=[
 
        (ACCOUNTS[0], 60, 'EUR', cost, meta),
 
        (ACCOUNTS[0], 30, 'EUR', cost, meta),
 
    ])
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    assert related.cost == cost
 
    assert related.costs == {cost}
 

	
 
@pytest.mark.parametrize('meta_key,acct_name', testutil.combine_values(
 
    CONSISTENT_METADATA,
 
    ACCOUNTS,
 
))
 
def test_accrual_postings_consistent_metadata(meta_key, acct_name):
 
    meta_value = f'{meta_key}.pdf'
 
    meta = {
 
        meta_key: meta_value,
 
        'invoice': f'invoice with {meta_key}.pdf',
 
    }
 
    txn = testutil.Transaction(postings=[
 
        (acct_name, 70, meta),
 
        (acct_name, 35, meta),
 
    ])
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    attr_name = meta_key.replace('-', '_')
 
    assert getattr(related, attr_name) == meta_value
 
    assert getattr(related, f'{attr_name}s') == {meta_value}
 

	
 
def test_accrual_postings_inconsistent_account():
 
    meta = {'invoice': 'invoice.pdf'}
 
    txn = testutil.Transaction(postings=[
 
        (acct_name, index, meta)
 
        for index, acct_name in enumerate(ACCOUNTS)
 
    ])
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    assert related.account is related.INCONSISTENT
 
    assert related.accounts == set(ACCOUNTS)
 

	
 
def test_accrual_postings_inconsistent_cost():
 
    meta = {'invoice': 'FXinvoice.pdf'}
 
    costs = {
 
        testutil.Cost('1.1', 'USD'),
 
        testutil.Cost('1.2', 'USD'),
 
    }
 
    txn = testutil.Transaction(postings=[
 
        (ACCOUNTS[0], 10, 'EUR', cost, meta)
 
        for cost in costs
 
    ])
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    assert related.cost is related.INCONSISTENT
 
    assert related.costs == costs
 

	
 
@pytest.mark.parametrize('meta_key,acct_name', testutil.combine_values(
 
    CONSISTENT_METADATA,
 
    ACCOUNTS,
 
))
 
def test_accrual_postings_inconsistent_metadata(meta_key, acct_name):
 
    invoice = 'invoice with {meta_key}.pdf'
 
    meta_value = f'{meta_key}.pdf'
 
    txn = testutil.Transaction(postings=[
 
        (acct_name, 20, {'invoice': invoice, meta_key: meta_value}),
 
        (acct_name, 35, {'invoice': invoice}),
 
    ])
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    attr_name = meta_key.replace('-', '_')
 
    assert getattr(related, attr_name) is related.INCONSISTENT
 
    assert getattr(related, f'{attr_name}s') == {meta_value, None}
 

	
 
@pytest.mark.parametrize('meta_key,account', testutil.combine_values(
 
    CONSISTENT_METADATA,
 
    ACCOUNTS,
 
))
 
def test_consistency_check_when_consistent(meta_key, account):
 
    invoice = f'test-{meta_key}-invoice'
 
    meta_value = f'test-{meta_key}-value'
 
    meta = {
 
        'invoice': invoice,
 
        meta_key: f'test-{meta_key}-value',
 
        meta_key: meta_value,
 
    }
 
    txn = testutil.Transaction(postings=[
 
        (account, 100, meta),
 
        (account, -100, meta),
 
    ])
 
    related = core.RelatedPostings(data.Posting.from_txn(txn))
 
    assert not list(accrual.consistency_check({invoice: related}))
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    assert not list(related.report_inconsistencies())
 

	
 
@pytest.mark.parametrize('meta_key,account', testutil.combine_values(
 
    ['approval', 'fx-rate', 'statement'],
...
 
@@ -227,8 +312,8 @@ def test_consistency_check_ignored_metadata(meta_key, account):
 
        (account, 100, {'invoice': invoice, meta_key: 'credit'}),
 
        (account, -100, {'invoice': invoice, meta_key: 'debit'}),
 
    ])
 
    related = core.RelatedPostings(data.Posting.from_txn(txn))
 
    assert not list(accrual.consistency_check({invoice: related}))
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    assert not list(related.report_inconsistencies())
 

	
 
@pytest.mark.parametrize('meta_key,account', testutil.combine_values(
 
    CONSISTENT_METADATA,
...
 
@@ -240,8 +325,8 @@ def test_consistency_check_when_inconsistent(meta_key, account):
 
        (account, 100, {'invoice': invoice, meta_key: 'credit', 'lineno': 1}),
 
        (account, -100, {'invoice': invoice, meta_key: 'debit', 'lineno': 2}),
 
    ])
 
    related = core.RelatedPostings(data.Posting.from_txn(txn))
 
    errors = list(accrual.consistency_check({invoice: related}))
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    errors = list(related.report_inconsistencies())
 
    for exp_lineno, (actual, exp_msg) in enumerate(itertools.zip_longest(errors, [
 
            f'inconsistent {meta_key} for invoice {invoice}: credit',
 
            f'inconsistent {meta_key} for invoice {invoice}: debit',
...
 
@@ -257,8 +342,8 @@ def test_consistency_check_cost():
 
        (account, 100, 'EUR', ('1.1251', 'USD'), {'invoice': invoice, 'lineno': 1}),
 
        (account, -100, 'EUR', ('1.125', 'USD'), {'invoice': invoice, 'lineno': 2}),
 
    ])
 
    related = core.RelatedPostings(data.Posting.from_txn(txn))
 
    errors = list(accrual.consistency_check({invoice: related}))
 
    related = accrual.AccrualPostings(data.Posting.from_txn(txn))
 
    errors = list(related.report_inconsistencies())
 
    for post, err in itertools.zip_longest(txn.postings, errors):
 
        assert err.message == f'inconsistent cost for invoice {invoice}: {post.cost}'
 
        assert err.entry is txn
...
 
@@ -272,7 +357,7 @@ def run_outgoing(invoice, postings, rt_client=None):
 
    if rt_client is None:
 
        rt_client = RTClient()
 
    if not isinstance(postings, core.RelatedPostings):
 
        postings = relate_accruals_by_meta(postings, invoice)
 
        postings = accruals_by_meta(postings, invoice, wrap_type=accrual.AccrualPostings)
 
    output = io.StringIO()
 
    report = accrual.OutgoingReport(rt_client, output)
 
    report.run({invoice: postings})
...
 
@@ -285,7 +370,7 @@ def run_outgoing(invoice, postings, rt_client=None):
 
    ('rt://ticket/515/attachments/5150', "1,500.00 USD outstanding since 2020-05-15",),
 
])
 
def test_balance_report(accrual_postings, invoice, expected, caplog):
 
    related = relate_accruals_by_meta(accrual_postings, invoice)
 
    related = accruals_by_meta(accrual_postings, invoice, wrap_type=accrual.AccrualPostings)
 
    output = io.StringIO()
 
    report = accrual.BalanceReport(output)
 
    report.run({invoice: related})