nexus-2/backend/api/models.py
2026-01-26 10:12:01 -05:00

311 lines
12 KiB
Python

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}"