115 lines
4.8 KiB
Python
115 lines
4.8 KiB
Python
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 Client(models.Model):
|
|
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='clients')
|
|
name = models.CharField(max_length=255)
|
|
client_job_ref_prefix = models.CharField(max_length=50, blank=True, null=True)
|
|
|
|
class Meta:
|
|
unique_together = [['company', 'name']]
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Job(models.Model):
|
|
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='jobs')
|
|
client = models.ForeignKey(Client, on_delete=models.PROTECT, related_name='jobs', null=True, blank=True)
|
|
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}" |