Generate seasons per club instead of a hardcoded Aug-May window

Club now carries its own season_start and season_duration_months,
editable via controlpanel; generate_seasons chains each new season off
the day after the club's last one ends (or its configured start, for a
club with none yet) instead of assuming every club runs Aug 1 - May 31.

Adds --resync to the generate_seasons command to clean up seasons left
over from the old hardcoded rule -- removing any that don't match a
club's current settings and aren't still referenced by real data.
This commit is contained in:
2026-08-03 17:03:03 +02:00
parent 062da00bb9
commit 8598fd2b46
9 changed files with 447 additions and 4 deletions

View File

@@ -13,7 +13,7 @@ from .services.admins import find_member_by_email
class ClubForm(forms.ModelForm):
class Meta:
model = Club
fields = ["name", "slug", "logo", "primary_color", "secondary_color"]
fields = ["name", "slug", "logo", "primary_color", "secondary_color", "season_start", "season_duration_months"]
help_texts = {"slug": _("Drives the club's subdomain. Left blank, it is derived from the name.")}
# Deliberately a text input, not <input type="color">: a colour picker cannot
# express "no colour" -- it would submit #000000 for every club that never
@@ -22,6 +22,7 @@ class ClubForm(forms.ModelForm):
"primary_color": forms.TextInput(attrs={"placeholder": "#1e40af"}),
"secondary_color": forms.TextInput(attrs={"placeholder": "#be185d"}),
"logo": forms.ClearableFileInput(attrs={"accept": "image/png,image/jpeg,image/gif,image/webp,image/svg+xml"}),
"season_start": forms.DateInput(attrs={"type": "date"}),
}
def __init__(self, *args, **kwargs):

View File

@@ -117,14 +117,17 @@ class ClubManagementTests(ControlPanelTestBase):
self.assertContains(self.client.get(reverse("controlpanel:dashboard")), "Ajax United")
def test_create_club_derives_the_slug(self):
response = self.client.post(reverse("controlpanel:club_create"), {"name": "New Club", "slug": ""})
response = self.client.post(reverse("controlpanel:club_create"), {"name": "New Club", "slug": "", "season_start": "2000-08-01", "season_duration_months": "12"})
club = Club.objects.get(name="New Club")
self.assertEqual(club.slug, "new-club")
self.assertRedirects(response, reverse("controlpanel:club_detail", args=[club.pk]))
def test_update_club(self):
self.client.post(reverse("controlpanel:club_update", args=[self.club.pk]), {"name": "Renamed", "slug": self.club.slug})
self.client.post(
reverse("controlpanel:club_update", args=[self.club.pk]),
{"name": "Renamed", "slug": self.club.slug, "season_start": "2000-08-01", "season_duration_months": "12"},
)
self.club.refresh_from_db()
self.assertEqual(self.club.name, "Renamed")