Show the cover-end date for waived periods too, not just paid

The "until <date>" only appeared for a PAID due, because the annotation filtered
status=PAID. A WAIVED period is settled just the same — the club is covered for
that time, and its end is still when grace would start — so it belongs there too.

The annotation is now `covered_until` (furthest-out period end where status is PAID
or WAIVED) plus `covered_status`, read from the same ordered row so the table can
badge "paid" vs "waived" and still show the date for both. Rides the same single
query — assertNumQueries(1) still holds.

Full matrix now: no plan -> n/a; paid -> "paid, until X"; waived -> "waived, until
X"; unpaid/partial -> the amount owed (no date); a paid-then-owing club shows the
amount, not the stale cover date. A subscribed club with no covering period at all
(only cancelled dues) shows a dash rather than a bare "paid".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 18:16:56 +02:00
parent 10b113f244
commit 19108407c6
3 changed files with 44 additions and 12 deletions

View File

@@ -17,7 +17,7 @@ from waffle import get_waffle_flag_model, get_waffle_switch_model
from billing.models import GRACE_DAYS, Due, Tier, TierPrice
from billing.services import BillingError
from billing.services.dues import record_payment, subscribe
from billing.services.dues import record_payment, subscribe, waive
from club.models import Club, ClubMembership, ClubRole, Season
from events.models import Attendance, Event
from features.models import Maintenance
@@ -1139,12 +1139,27 @@ class PlatformDuesMetricTests(TestCase):
club = clubs_with_health().get(pk=self.club.pk)
self.assertEqual(club.paid_until, due.period_end)
self.assertEqual(club.covered_until, due.period_end)
self.assertEqual(club.covered_status, Due.Status.PAID)
def test_a_club_that_owes_has_no_paid_until(self):
def test_a_waived_period_also_shows_its_cover_end(self):
# Waived is settled too — the club is covered for that time, so its end date shows,
# badged "waived" rather than "paid".
subscribe(self.club, self.tier)
due = self.club.dues.first()
waive(due)
club = clubs_with_health().get(pk=self.club.pk)
self.assertEqual(club.covered_until, due.period_end)
self.assertEqual(club.covered_status, Due.Status.WAIVED)
def test_a_club_that_owes_has_no_cover(self):
subscribe(self.club, self.tier) # unpaid
self.assertIsNone(clubs_with_health().get(pk=self.club.pk).paid_until)
club = clubs_with_health().get(pk=self.club.pk)
self.assertIsNone(club.covered_until)
self.assertIsNone(club.covered_status)
def test_the_health_table_still_costs_one_query_with_billing_on_it(self):
subscribe(self.club, self.tier)