feat(teams): management positions and cross-club validation

Position gains management_position, distinguishing a coach/manager from other
staff (physio, kit manager). A CheckConstraint enforces that a management
position is always a staff position.

TeamMembership and StaffAssignment validate that their season and position
belong to the same club as the team.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 14:42:06 +02:00
parent c320931595
commit c0816a1add
4 changed files with 92 additions and 1 deletions

View File

@@ -1,8 +1,9 @@
from django.db import models
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from club.models import Season
from clubmanager.base import ClubScopedModel, UUIDModel
from clubmanager.base import ClubScopedModel, UUIDModel, validate_club_scope
from members.models import Member
@@ -27,12 +28,15 @@ class Position(ClubScopedModel):
ordering = models.PositiveSmallIntegerField(_("ordering"), default=0)
staff_position = models.BooleanField(_("staff position"), default=False)
management_position = models.BooleanField(_("management position"), default=False)
class Meta:
verbose_name = _("position")
verbose_name_plural = _("positions")
constraints = [
models.UniqueConstraint(fields=["club", "name"], name="unique_position_name_per_club"),
# A management position is always a staff position.
models.CheckConstraint(condition=Q(management_position=False) | Q(staff_position=True), name="management_position_implies_staff_position"),
]
ordering = ["ordering", "name"]
@@ -62,6 +66,10 @@ class TeamMembership(UUIDModel):
def __str__(self):
return f"{self.team} - {self.member}"
def clean(self):
club_id = self.team.club_id if self.team_id else None
validate_club_scope(self, club_id, same_club_fields=("season", "position"))
class StaffAssignment(UUIDModel):
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="staff_assignments", verbose_name=_("team"))
@@ -79,3 +87,7 @@ class StaffAssignment(UUIDModel):
def __str__(self):
return f"{self.team} - {self.member}"
def clean(self):
club_id = self.team.club_id if self.team_id else None
validate_club_scope(self, club_id, same_club_fields=("season", "position"))