import uuid from django.db import models from django.contrib.auth.models import User class Profile(models.Model): """Extension of the Django User model""" ROLE_CHOICES = ( ('admin', 'Admin'), ('team_leader', 'Team Leader'), ('team_member', 'Team Member'), ) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) primary_phone = models.CharField(max_length=20) secondary_phone = models.CharField(max_length=20, blank=True, null=True) email = models.EmailField() role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='team_member') class Meta: ordering = ['last_name', 'first_name'] verbose_name_plural = "Profiles" def __str__(self): return f"{self.first_name} {self.last_name}" class Customer(models.Model): """Customer model with contact information""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=200) primary_contact_first_name = models.CharField(max_length=100) primary_contact_last_name = models.CharField(max_length=100) primary_contact_phone = models.CharField(max_length=20) primary_contact_email = models.EmailField() secondary_contact_first_name = models.CharField(max_length=100, blank=True, null=True) secondary_contact_last_name = models.CharField(max_length=100, blank=True, null=True) secondary_contact_phone = models.CharField(max_length=20, blank=True, null=True) secondary_contact_email = models.EmailField(blank=True, null=True) billing_contact_first_name = models.CharField(max_length=100) billing_contact_last_name = models.CharField(max_length=100) billing_street_address = models.CharField(max_length=255) billing_city = models.CharField(max_length=100) billing_state = models.CharField(max_length=100) billing_zip_code = models.CharField(max_length=20) billing_email = models.EmailField() billing_terms = models.TextField() start_date = models.DateField() end_date = models.DateField(blank=True, null=True) class Meta: ordering = ['name'] verbose_name_plural = "Customers" def __str__(self): return self.name class Account(models.Model): """Account model belonging to a customer""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='accounts') name = models.CharField(max_length=200) street_address = models.CharField(max_length=255) city = models.CharField(max_length=100) state = models.CharField(max_length=100) zip_code = models.CharField(max_length=20) contact_first_name = models.CharField(max_length=100) contact_last_name = models.CharField(max_length=100) contact_phone = models.CharField(max_length=20) contact_email = models.EmailField() start_date = models.DateField() end_date = models.DateField(blank=True, null=True) class Meta: ordering = ['name'] verbose_name_plural = "Accounts" def __str__(self): return f"{self.name} ({self.customer.name})" class Revenue(models.Model): """Revenue records for accounts""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='revenues') amount = models.DecimalField(max_digits=10, decimal_places=2) start_date = models.DateField() end_date = models.DateField(blank=True, null=True) class Meta: ordering = ['-start_date'] verbose_name_plural = "Revenues" def __str__(self): return f"{self.account.name} - ${self.amount}" class Labor(models.Model): """Labor records for accounts""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='labors') amount = models.DecimalField(max_digits=10, decimal_places=2) start_date = models.DateField() end_date = models.DateField(blank=True, null=True) class Meta: ordering = ['-start_date'] verbose_name_plural = "Labors" def __str__(self): return f"{self.account.name} - ${self.amount}" class Schedule(models.Model): """Service schedules for accounts""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='schedules') monday_service = models.BooleanField(default=False) tuesday_service = models.BooleanField(default=False) wednesday_service = models.BooleanField(default=False) thursday_service = models.BooleanField(default=False) friday_service = models.BooleanField(default=False) saturday_service = models.BooleanField(default=False) sunday_service = models.BooleanField(default=False) weekend_service = models.BooleanField(default=False) schedule_exception = models.TextField(blank=True, null=True) start_date = models.DateField() end_date = models.DateField(blank=True, null=True) class Meta: ordering = ['-start_date'] verbose_name_plural = "Schedules" def __str__(self): return f"Schedule for {self.account.name}" class Service(models.Model): """Service records for accounts""" STATUS_CHOICES = ( ('scheduled', 'Scheduled'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled'), ) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='services') date = models.DateField() status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='scheduled') team_members = models.ManyToManyField(Profile, related_name='services') notes = models.TextField(blank=True, null=True) deadline_start = models.DateTimeField() deadline_end = models.DateTimeField() class Meta: ordering = ['-date'] verbose_name_plural = "Services" def __str__(self): return f"Service for {self.account.name} on {self.date}" class Project(models.Model): """Project records for customers""" STATUS_CHOICES = ( ('planned', 'Planned'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled'), ) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='projects') account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='projects', blank=True, null=True) date = models.DateField() status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planned') team_members = models.ManyToManyField(Profile, related_name='projects') notes = models.TextField(blank=True, null=True) labor = models.DecimalField(max_digits=10, decimal_places=2) amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) class Meta: ordering = ['-date'] verbose_name_plural = "Projects" def __str__(self): return f"Project for {self.customer.name} on {self.date}" class Invoice(models.Model): """Invoice records""" STATUS_CHOICES = ( ('draft', 'Draft'), ('sent', 'Sent'), ('paid', 'Paid'), ('overdue', 'Overdue'), ('cancelled', 'Cancelled'), ) PAYMENT_CHOICES = ( ('check', 'Check'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer'), ('cash', 'Cash'), ) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) date = models.DateField() customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='invoices') accounts = models.ManyToManyField(Account, related_name='invoices', blank=True) projects = models.ManyToManyField(Project, related_name='invoices', blank=True) revenues = models.ManyToManyField(Revenue, related_name='invoices', blank=True) status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft') date_paid = models.DateField(blank=True, null=True) payment_type = models.CharField(max_length=20, choices=PAYMENT_CHOICES, blank=True, null=True) class Meta: ordering = ['-date'] verbose_name_plural = "Invoices" def __str__(self): return f"Invoice for {self.customer.name} on {self.date}" class Report(models.Model): """Report records""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) date = models.DateField() team_member = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='reports') services = models.ManyToManyField(Service, related_name='reports', blank=True) projects = models.ManyToManyField(Project, related_name='reports', blank=True) class Meta: ordering = ['-date'] verbose_name_plural = "Reports" def __str__(self): return f"Report by {self.team_member.first_name} {self.team_member.last_name} on {self.date}" class Punchlist(models.Model): """ Punchlist records for projects. This is a customizable checklist for service projects with sections for different areas and equipment. Modify the fields to match your specific service requirements. """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) project = models.ForeignKey('Project', on_delete=models.CASCADE, related_name='punchlists') account = models.ForeignKey('Account', on_delete=models.CASCADE, related_name='punchlists') date = models.DateField() second_visit = models.BooleanField(default=False) second_date = models.DateTimeField(blank=True, null=True) # Front area section front_ceiling = models.BooleanField(default=False) front_vents = models.BooleanField(default=False) front_fixtures = models.BooleanField(default=False) front_counter = models.BooleanField(default=False) # Main work area section main_equipment = models.CharField(max_length=20, blank=True) main_equipment_disassemble = models.BooleanField(default=False) main_equipment_reassemble = models.BooleanField(default=False) main_equipment_test = models.BooleanField(default=False) main_equipment_exterior = models.BooleanField(default=False) main_walls = models.BooleanField(default=False) main_fixtures = models.BooleanField(default=False) main_ceiling = models.BooleanField(default=False) main_vents = models.BooleanField(default=False) main_floors = models.BooleanField(default=False) # Equipment section equip_station_1 = models.BooleanField(default=False) equip_station_2 = models.BooleanField(default=False) equip_station_3 = models.BooleanField(default=False) equip_station_4 = models.BooleanField(default=False) equip_station_5 = models.BooleanField(default=False) equip_station_6 = models.BooleanField(default=False) equip_station_7 = models.BooleanField(default=False) equip_sinks = models.BooleanField(default=False) equip_dispensers = models.BooleanField(default=False) equip_other = models.BooleanField(default=False) # Back area section back_ceiling = models.BooleanField(default=False) back_vents = models.BooleanField(default=False) # End of visit section end_trash = models.BooleanField(default=False) end_clean = models.BooleanField(default=False) end_secure = models.BooleanField(default=False) # Notes notes = models.TextField(blank=True) # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) sheet_url = models.URLField(blank=True, null=True) pdf_url = models.URLField(blank=True, null=True) exported_at = models.DateTimeField(blank=True, null=True) class Meta: ordering = ['-date'] verbose_name_plural = "Punchlists" def __str__(self): return f"Punchlist for {self.account.name} on {self.date}"