@@ -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'}
(ACCOUNTS[0], 60, 'EUR', cost, meta),
(ACCOUNTS[0], 30, 'EUR', cost, meta),
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',
}
(acct_name, 70, meta),
(acct_name, 35, meta),
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'}
(acct_name, index, meta)
for index, acct_name in enumerate(ACCOUNTS)
assert related.account is related.INCONSISTENT
assert related.accounts == set(ACCOUNTS)
def test_accrual_postings_inconsistent_cost():
costs = {
testutil.Cost('1.1', 'USD'),
(ACCOUNTS[0], 10, 'EUR', cost, meta)
for cost in costs
assert related.cost is related.INCONSISTENT
assert related.costs == costs
def test_accrual_postings_inconsistent_metadata(meta_key, acct_name):
invoice = 'invoice with {meta_key}.pdf'
(acct_name, 20, {'invoice': invoice, meta_key: meta_value}),
(acct_name, 35, {'invoice': invoice}),
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(
def test_consistency_check_when_consistent(meta_key, account):
invoice = f'test-{meta_key}-invoice'
meta_value = f'test-{meta_key}-value'
'invoice': invoice,
meta_key: f'test-{meta_key}-value',
(account, 100, meta),
(account, -100, meta),
related = core.RelatedPostings(data.Posting.from_txn(txn))
assert not list(accrual.consistency_check({invoice: related}))
assert not list(related.report_inconsistencies())
['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'}),
@@ -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}),
errors = list(accrual.consistency_check({invoice: related}))
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}),
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)
report = accrual.BalanceReport(output)
report.run({invoice: related})