import datetime from django.core.exceptions import ValidationError from django.db import models from django.db.models import Q from core.models.base import BaseModel from core.models.account import AccountAddress class Schedule(BaseModel): """ Service schedules for accounts. """ name = models.CharField(max_length=255, blank=True, null=True, verbose_name="Name") account_address = models.ForeignKey(AccountAddress, on_delete=models.PROTECT, related_name='schedules', verbose_name="Account Service Address", null=True) monday_service = models.BooleanField(default=False, verbose_name="Monday Service") tuesday_service = models.BooleanField(default=False, verbose_name="Tuesday Service") wednesday_service = models.BooleanField(default=False, verbose_name="Wednesday Service") thursday_service = models.BooleanField(default=False, verbose_name="Thursday Service") friday_service = models.BooleanField(default=False, verbose_name="Friday Service") saturday_service = models.BooleanField(default=False, verbose_name="Saturday Service") sunday_service = models.BooleanField(default=False, verbose_name="Sunday Service") weekend_service = models.BooleanField(default=False, verbose_name="Weekend Service", help_text=( "When enabled, represents a single service visit on Friday that can be performed " "any time between Friday-Sunday and verified by Monday morning. " "Individual Fri/Sat/Sun service flags must be disabled when this is enabled.")) schedule_exception = models.TextField(blank=True, null=True, verbose_name="Schedule Exceptions", help_text=( "Notes about any exceptions or special requirements for this schedule")) start_date = models.DateField(verbose_name="Start Date", help_text="Date when this schedule becomes active") end_date = models.DateField(blank=True, null=True, verbose_name="End Date", help_text="Optional date when this schedule expires") class Meta: ordering = ['-start_date'] verbose_name = "Schedule" verbose_name_plural = "Schedules" indexes = [ models.Index(fields=['account_address', 'start_date']), models.Index(fields=['weekend_service']), ] def __str__(self): return f"Schedule for {self.account_address.account.name} - {self.account_address.name if self.account_address.name else 'Primary Service Address'}" def clean(self): """Validate schedule configuration""" super().clean() if self.end_date and self.start_date > self.end_date: raise ValidationError({ 'end_date': "End date must be after start date" }) start = self.start_date end = self.end_date or datetime.date.max qs = Schedule.objects.filter(account_address=self.account_address) if self.pk: qs = qs.exclude(pk=self.pk) overlaps = qs.filter( Q(end_date__isnull=True, start_date__lte=end) | Q(end_date__isnull=False, start_date__lte=end, end_date__gte=start) ) if overlaps.exists(): raise ValidationError("Schedule dates overlap with an existing schedule for this address.") # Validate weekend service configuration if self.weekend_service: weekend_days = [ self.friday_service, self.saturday_service, self.sunday_service ] if any(weekend_days): raise ValidationError({ 'weekend_service': "When weekend service is enabled, Friday, Saturday, " "and Sunday service flags must be disabled" }) has_regular_service = any([ self.monday_service, self.tuesday_service, self.wednesday_service, self.thursday_service, self.friday_service, self.saturday_service, self.sunday_service ]) if not has_regular_service and not self.weekend_service: raise ValidationError( "At least one service day or weekend service must be selected" )