from django.db import models from django.contrib.auth.models import User from django.core.validators import MinLengthValidator import uuid from datetime import timedelta from django.utils import timezone class Company(models.Model): name = models.CharField(max_length=255) is_uprn_required = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name class Profile(models.Model): ROLE_CHOICES = [ ('ADMIN', 'Admin'), ('STANDARD', 'Standard User'), ] user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='users', null=True, blank=True) role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='STANDARD') def __str__(self): return f"{self.user.username} - {self.company.name if self.company else 'No Company'}" class JobStatus(models.Model): company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='statuses') name = models.CharField(max_length=100) is_starting_status = models.BooleanField(default=False) order = models.PositiveIntegerField(default=0) class Meta: verbose_name_plural = "Job Statuses" ordering = ['order'] def __str__(self): return f"{self.name} ({self.company.name})" class RequiredFolder(models.Model): company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='required_folders') name = models.CharField(max_length=100) def __str__(self): return f"{self.name} ({self.company.name})" class Job(models.Model): company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='jobs') job_ref = models.CharField(max_length=100) uprn = models.CharField(max_length=100, null=True, blank=True) address_line_1 = models.CharField(max_length=255) address_line_2 = models.CharField(max_length=255, null=True, blank=True) address_line_3 = models.CharField(max_length=255, null=True, blank=True) postcode = models.CharField(max_length=20) description = models.TextField(null=True, blank=True) action = models.TextField(null=True, blank=True) notes = models.TextField(null=True, blank=True) status = models.ForeignKey(JobStatus, on_delete=models.PROTECT, related_name='jobs') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: unique_together = [['company', 'job_ref'], ['company', 'uprn']] # Add a custom constraint to allow null=True and blank=True for uprn, # but still enforce unique_together when it's not null. # This is handled by a custom validator or by overriding save method if needed. # For now, Django's unique_together with null=True will allow multiple nulls. def __str__(self): return f"{self.job_ref} - {self.address_line_1}" class JobFolderCompletion(models.Model): job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='folder_completions') folder = models.ForeignKey(RequiredFolder, on_delete=models.CASCADE) is_completed = models.BooleanField(default=False) class Meta: unique_together = ['job', 'folder'] class JobFile(models.Model): job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='files') folder = models.ForeignKey(RequiredFolder, on_delete=models.CASCADE, related_name='files') file = models.FileField(upload_to='job_files/') uploaded_at = models.DateTimeField(auto_now_add=True) uploaded_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) def __str__(self): return f"{self.file.name} in {self.folder.name}" class Invitation(models.Model): company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='invitations') invited_by = models.ForeignKey(User, on_delete=models.CASCADE) email = models.EmailField(unique=True) token = models.CharField(max_length=32, unique=True, default=uuid.uuid4().hex) created_at = models.DateTimeField(auto_now_add=True) expires_at = models.DateTimeField(default=timezone.now() + timedelta(days=7)) is_accepted = models.BooleanField(default=False) def __str__(self): return f"Invitation for {self.email} to {self.company.name}"