accrual: Generate an aging report in more cases.

Default to generating an aging report unless the user searched for a
specific RT ticket or invoice.
This commit is contained in:
Brett Smith 2020-06-17 15:17:59 -04:00
parent d7e2ab34b9
commit 0caf78436f
2 changed files with 26 additions and 24 deletions

View file

@ -6,8 +6,7 @@ Liabilities:Payable) for errors and metadata consistency, and reports any
problems on stderr. Then it writes a report about the status of those
accruals.
If you run it with no arguments, it will generate an aging report in ODS format
in the current directory.
If you run it with no arguments, it will generate an aging report in ODS format.
Otherwise, the typical way to run it is to pass an RT ticket number or
invoice link as an argument, to report about accruals that match those
@ -38,17 +37,19 @@ You can pass any number of search terms. For example::
accrual-report 1230 entity=Doe-Jane
accrual-report will automatically decide what kind of report to generate
from the search terms you provide and the results they return. If you pass
no search terms, it generates an aging report. If your search terms match a
single outstanding payable, it writes an outgoing approval report.
Otherwise, it writes a basic balance report. You can specify what report
from the search terms you provide and the results they return. If you
searched on an RT ticket or invoice that returned a single outstanding
payable, it writes an outgoing approval report. If you searched on RT ticket
or invoice that returned other results, it writes a balance
report. Otherwise, it writes an aging report. You can specify what report
type you want with the ``--report-type`` option::
# Write an outgoing approval report for all outstanding accruals for
# Write an outgoing approval report for all outstanding payables for
# Jane Doe, even if there's more than one
accrual-report --report-type outgoing entity=Doe-Jane
# Write an aging report for a specific project
accrual-report --report-type aging project=ProjectName
# Write an aging report for a single RT invoice (this can be helpful when
# one invoice covers multiple parties)
accrual-report --report-type aging 12/345
"""
# Copyright © 2020 Brett Smith
#
@ -627,7 +628,10 @@ metadata to match. A single ticket number is a shortcut for
`TIK/ATT` format, is a shortcut for `invoice=LINK`.
""")
args = parser.parse_args(arglist)
if args.report_type is None and not args.search_terms:
if args.report_type is None and not any(
term.meta_key == 'invoice' or term.meta_key == 'rt-id'
for term in args.search_terms
):
args.report_type = ReportType.AGING
return args

View file

@ -577,15 +577,19 @@ def test_aging_report_does_not_include_too_recent_postings(accrual_postings):
project='Development Grant'),
], [])
def run_main(arglist, config=None):
def run_main(arglist, config=None, out_type=io.StringIO):
if config is None:
config = testutil.TestConfig(
books_path=testutil.test_path('books/accruals.beancount'),
rt_client=RTClient(),
)
output = io.StringIO()
if out_type is io.BytesIO:
arglist.insert(0, '--output-file=-')
output = out_type()
errors = io.StringIO()
retcode = accrual.main(arglist, output, errors, config)
output.seek(0)
errors.seek(0)
return retcode, output, errors
def check_main_fails(arglist, config, error_flags):
@ -593,7 +597,6 @@ def check_main_fails(arglist, config, error_flags):
assert retcode > 16
assert (retcode - 16) & error_flags
assert not output.getvalue()
errors.seek(0)
return errors
@pytest.mark.parametrize('arglist', [
@ -624,7 +627,7 @@ def test_output_payments_when_only_match(arglist, expect_invoice):
@pytest.mark.parametrize('arglist,expect_amount', [
(['310'], 420),
(['310/3120'], 220),
(['entity=Vendor'], 420),
(['-t', 'out', 'entity=Vendor'], 420),
])
def test_main_outgoing_report(arglist, expect_amount):
retcode, output, errors = run_main(arglist)
@ -643,7 +646,6 @@ def test_main_outgoing_report(arglist, expect_amount):
@pytest.mark.parametrize('arglist', [
['-t', 'balance'],
['515/5150'],
['entity=MatchingProgram'],
])
def test_main_balance_report(arglist):
retcode, output, errors = run_main(arglist)
@ -666,23 +668,19 @@ def test_main_balance_report_because_no_rt_id():
@pytest.mark.parametrize('arglist', [
[],
['-t', 'aging', 'entity=Lawyer'],
['entity=Lawyer'],
])
def test_main_aging_report(tmp_path, arglist):
def test_main_aging_report(arglist):
if arglist:
recv_rows = [row for row in AGING_AR if 'Lawyer' in row.entity]
pay_rows = [row for row in AGING_AP if 'Lawyer' in row.entity]
else:
recv_rows = AGING_AR
pay_rows = AGING_AP
output_path = tmp_path / 'AgingReport.ods'
arglist.insert(0, f'--output-file={output_path}')
retcode, output, errors = run_main(arglist)
retcode, output, errors = run_main(arglist, out_type=io.BytesIO)
assert not errors.getvalue()
assert retcode == 0
assert not output.getvalue()
with output_path.open('rb') as ods_file:
check_aging_ods(ods_file, None, recv_rows, pay_rows)
check_aging_ods(output, None, recv_rows, pay_rows)
def test_main_no_books():
errors = check_main_fails([], testutil.TestConfig(), 1 | 8)
@ -693,7 +691,7 @@ def test_main_no_books():
@pytest.mark.parametrize('arglist', [
['499'],
['505/99999'],
['entity=NonExistent'],
['-t', 'balance', 'entity=NonExistent'],
])
def test_main_no_matches(arglist, caplog):
check_main_fails(arglist, None, 8)