from django.db import models from django_choices_field import TextChoicesField from core.models.account import Account, AccountAddress from core.models.base import BaseModel from core.models.enums import TaskFrequencyChoices from core.models.profile import TeamProfile from core.models.service import Service class Scope(BaseModel): """Scope of work definition for an account address""" name = models.CharField(max_length=255) account = models.ForeignKey(Account, on_delete=models.PROTECT, related_name='scopes') account_address = models.ForeignKey(AccountAddress, on_delete=models.PROTECT, related_name='scopes', null=True, blank=True) description = models.TextField(blank=True) is_active = models.BooleanField(default=True) class Meta: ordering = ['name'] verbose_name = "Scope" verbose_name_plural = "Scopes" indexes = [ models.Index(fields=['account', 'is_active']), models.Index(fields=['account_address', 'is_active']), ] constraints = [ models.UniqueConstraint( fields=['account_address'], condition=models.Q(is_active=True, account_address__isnull=False), name='unique_active_scope_per_address' ) ] def __str__(self): return f"{self.name} (account_id={self.account_id})" class Area(BaseModel): """Area within a scope (e.g., Kitchen, Restrooms, Lobby)""" name = models.CharField(max_length=100) scope = models.ForeignKey(Scope, on_delete=models.CASCADE, related_name='areas') order = models.PositiveIntegerField(default=0) class Meta: ordering = ['scope', 'order', 'name'] verbose_name = "Area" verbose_name_plural = "Areas" indexes = [ models.Index(fields=['scope', 'order']), ] def __str__(self): return f"{self.name} (scope_id={self.scope_id})" class Task(BaseModel): """Individual task template within an area""" area = models.ForeignKey(Area, on_delete=models.CASCADE, related_name='tasks') description = models.TextField() checklist_description = models.TextField(blank=True) frequency = TextChoicesField( choices_enum=TaskFrequencyChoices, default=TaskFrequencyChoices.AS_NEEDED, help_text="How often the task should be performed" ) order = models.PositiveIntegerField(default=0) is_conditional = models.BooleanField(default=False, help_text="Task marked 'where applicable'") estimated_minutes = models.PositiveIntegerField(null=True, blank=True) class Meta: ordering = ['area', 'order'] verbose_name = "Task" verbose_name_plural = "Tasks" indexes = [ models.Index(fields=['area', 'order']), models.Index(fields=['frequency']), ] def __str__(self): return f"{self.description[:50]}... (area_id={self.area_id})" class TaskCompletion(BaseModel): """Record of a task template being completed during a service visit""" task = models.ForeignKey(Task, on_delete=models.PROTECT, related_name='completions') service = models.ForeignKey(Service, on_delete=models.PROTECT, related_name='task_completions') account_address = models.ForeignKey(AccountAddress, on_delete=models.PROTECT, null=True, related_name='task_completions') completed_by = models.ForeignKey(TeamProfile, on_delete=models.PROTECT) completed_at = models.DateTimeField() notes = models.TextField(blank=True) # Autopopulated for efficient monthly/quarterly queries year = models.PositiveIntegerField(editable=False) month = models.PositiveIntegerField(editable=False) class Meta: ordering = ['-completed_at'] verbose_name = "Task Completion" verbose_name_plural = "Task Completions" indexes = [ models.Index(fields=['service']), models.Index(fields=['task', 'year', 'month']), models.Index(fields=['completed_by', 'completed_at']), models.Index(fields=['account_address']), ] constraints = [ # Prevent the same task being completed multiple times in the same service models.UniqueConstraint( fields=['service', 'task'], name='unique_task_per_service' ) ] def save(self, *args, **kwargs): """Autopopulate year/month from service date""" # Backfill account_address from service if missing if self.account_address_id is None and self.service_id and hasattr(self.service, 'account_address_id'): self.account_address_id = getattr(self.service, 'account_address_id', None) if self.service_id and hasattr(self.service, 'date'): self.year = self.service.date.year self.month = self.service.date.month super().save(*args, **kwargs) def __str__(self): return f"TaskCompletion (task_id={self.task_id}, service_id={self.service_id})"