accrual: Aging report filters out too-recent accruals. RT#11600.

This commit is contained in:
Brett Smith 2020-06-09 17:05:36 -04:00
parent 948d3a2d14
commit 6e9a612bb7
3 changed files with 50 additions and 6 deletions

View file

@ -507,11 +507,29 @@ class AgingReport(BaseReport):
self.ods = AgingODS(rt_client, date, self.logger) self.ods = AgingODS(rt_client, date, self.logger)
def run(self, groups: PostGroups) -> None: def run(self, groups: PostGroups) -> None:
rows = list( rows: List[AccrualPostings] = []
group.since_last_nonzero() for group in groups.values():
for group in groups.values() if group.is_zero():
if not group.is_zero() # Cheap optimization: don't slice and dice groups we're not
) # going to report anyway.
continue
elif group.accrual_type is None:
group = group.since_last_nonzero()
else:
# Filter out new accruals after the report date.
# e.g., cover the case that the same invoices has multiple
# postings over time, and we don't want to report too-recent
# ones.
cutoff_date = self.ods.date - datetime.timedelta(
days=group.accrual_type.value.aging_thresholds[-1],
)
group = AccrualPostings(
post for post in group.since_last_nonzero()
if post.meta.date <= cutoff_date
or group.accrual_type.normalize_amount(post.units.number) < 0
)
if group and not group.is_zero():
rows.append(group)
rows.sort(key=lambda related: ( rows.sort(key=lambda related: (
related.account, related.account,
related[0].meta.date, related[0].meta.date,

View file

@ -151,3 +151,10 @@
project: "Development Grant" project: "Development Grant"
Assets:Receivable:Accounts 5500 USD Assets:Receivable:Accounts 5500 USD
Income:Donations -5500 USD Income:Donations -5500 USD
2010-09-15 * "GrantCo" "2010Q3 grant"
rt-id: "rt:470"
invoice: "rt:470/4700"
project: "Development Grant"
Assets:Receivable:Accounts 6000 USD
Income:Donations -6000 USD

View file

@ -121,7 +121,7 @@ AGING_AR = [
AgingRow.make_simple('2010-03-05', 'EarlyBird', -500, 'rt:40/400'), AgingRow.make_simple('2010-03-05', 'EarlyBird', -500, 'rt:40/400'),
AgingRow.make_simple('2010-05-15', 'MatchingProgram', 1500, AgingRow.make_simple('2010-05-15', 'MatchingProgram', 1500,
'rt://ticket/515/attachments/5150'), 'rt://ticket/515/attachments/5150'),
AgingRow.make_simple('2010-06-15', 'GrantCo', 5500, 'rt:470/4700', AgingRow.make_simple('2010-06-15', 'GrantCo', 11500, 'rt:470/4700',
project='Development Grant'), project='Development Grant'),
] ]
@ -630,6 +630,11 @@ def test_aging_report(accrual_postings):
def test_aging_report_date_cutoffs(accrual_postings, date, recv_end, pay_end): def test_aging_report_date_cutoffs(accrual_postings, date, recv_end, pay_end):
expect_recv = AGING_AR[:recv_end] expect_recv = AGING_AR[:recv_end]
expect_pay = AGING_AP[:pay_end] expect_pay = AGING_AP[:pay_end]
if 10 <= date.day < 12:
# Take the 60 USD posting out of the invoice 510/6100 payable.
expect_pay[-1] = expect_pay[-1]._replace(
at_cost=testutil.Amount(expect_pay[-1].at_cost.number - 60),
)
output = run_aging_report(accrual_postings, date) output = run_aging_report(accrual_postings, date)
check_aging_ods(output, date, expect_recv, expect_pay) check_aging_ods(output, date, expect_recv, expect_pay)
@ -644,6 +649,20 @@ def test_aging_report_entity_consistency(accrual_postings):
AgingRow.make_simple('2010-04-15', 'MultiPartyB', 125, 'rt:480/4800'), AgingRow.make_simple('2010-04-15', 'MultiPartyB', 125, 'rt:480/4800'),
]) ])
def test_aging_report_does_not_include_too_recent_postings(accrual_postings):
# This date is after the Q3 posting, but too soon after for that to be
# included in the aging report.
date = datetime.date(2010, 10, 1)
output = run_aging_report((
post for post in accrual_postings
if post.meta.get('rt-id') == 'rt:470'
), date)
# Date+amount are both from the Q2 posting only.
check_aging_ods(output, date, [
AgingRow.make_simple('2010-06-15', 'GrantCo', 5500, 'rt:470/4700',
project='Development Grant'),
], [])
def run_main(arglist, config=None): def run_main(arglist, config=None):
if config is None: if config is None:
config = testutil.TestConfig( config = testutil.TestConfig(