Complete the Season model: - name property renders the start/end years as a "YY-YY" label (e.g. "25-26") via strftime %y, and __str__ now returns it. - get_current(date) returns the active club's season covering the given date (today by default), inclusive of both boundaries, scoped through the tenant queryset so it never crosses clubs. Rename the tenant queryset's current() to current_club() for clarity and update callers/tests. Cover the new behaviour; tenancy modules stay at 100%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
89 lines
2.7 KiB
Python
89 lines
2.7 KiB
Python
import datetime
|
|
|
|
from django.db import models
|
|
from django.utils import timezone
|
|
from django.utils.text import slugify
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from clubmanager.base import ClubScopedModel, UUIDModel
|
|
from members.models import Member
|
|
|
|
|
|
class ClubManager(models.Manager):
|
|
def current(self):
|
|
"""Return the club for the active tenant context, if any."""
|
|
from .tenancy import get_current_club
|
|
|
|
return get_current_club()
|
|
|
|
|
|
class Club(UUIDModel):
|
|
name = models.CharField(_("name"), max_length=255)
|
|
slug = models.SlugField(_("slug"), max_length=255, unique=True, blank=True, help_text=_("Drives subdomain / path resolution (e.g. ajax-united.clubmanager.app)."))
|
|
|
|
objects = ClubManager()
|
|
|
|
class Meta:
|
|
verbose_name = _("club")
|
|
verbose_name_plural = _("clubs")
|
|
ordering = ["name"]
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def save(self, *args, **kwargs):
|
|
if not self.slug:
|
|
self.slug = self._unique_slug()
|
|
super().save(*args, **kwargs)
|
|
|
|
def _unique_slug(self):
|
|
base = slugify(self.name) or "club"
|
|
slug = base
|
|
suffix = 2
|
|
existing = Club.objects.exclude(pk=self.pk)
|
|
while existing.filter(slug=slug).exists():
|
|
slug = f"{base}-{suffix}"
|
|
suffix += 1
|
|
return slug
|
|
|
|
|
|
class ClubMembership(UUIDModel):
|
|
member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="member_of", verbose_name=_("member"))
|
|
club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name="members", verbose_name=_("club"))
|
|
|
|
license = models.CharField(_("license"), max_length=250, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _("club membership")
|
|
verbose_name_plural = _("club memberships")
|
|
ordering = ["club", "member__last_name", "member__first_name"]
|
|
unique_together = ("club", "member")
|
|
|
|
def __str__(self):
|
|
return f"{self.club} - {self.member}"
|
|
|
|
|
|
class Season(ClubScopedModel):
|
|
start_date = models.DateField(_("start date"))
|
|
end_date = models.DateField(_("end date"))
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Meta:
|
|
verbose_name = _("season")
|
|
verbose_name_plural = _("seasons")
|
|
|
|
@property
|
|
def name(self):
|
|
"""Short label built from the start/end years, e.g. "25-26"."""
|
|
return f"{self.start_date:%y}-{self.end_date:%y}"
|
|
|
|
@classmethod
|
|
def get_current(cls, date: datetime.date | None = None):
|
|
"""Return the current club's season covering ``date`` (today by default)."""
|
|
if date is None:
|
|
date = timezone.now().date()
|
|
|
|
return cls.objects.current_club().filter(start_date__lte=date, end_date__gte=date).first()
|