""" Contact submission models for user inquiries and support tickets. """ import uuid import pghistory from django.db import models from django.utils import timezone @pghistory.track() class ContactSubmission(models.Model): """ User-submitted contact form messages and support tickets. Tracks all communication from users for admin follow-up. """ STATUS_CHOICES = [ ('pending', 'Pending Review'), ('in_progress', 'In Progress'), ('resolved', 'Resolved'), ('archived', 'Archived'), ] CATEGORY_CHOICES = [ ('general', 'General Inquiry'), ('bug', 'Bug Report'), ('feature', 'Feature Request'), ('abuse', 'Report Abuse'), ('data', 'Data Correction'), ('account', 'Account Issue'), ('other', 'Other'), ] id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # Contact Information name = models.CharField(max_length=255) email = models.EmailField() subject = models.CharField(max_length=255) message = models.TextField() category = models.CharField( max_length=50, choices=CATEGORY_CHOICES, default='general' ) # Status & Assignment status = models.CharField( max_length=20, choices=STATUS_CHOICES, default='pending', db_index=True ) ticket_number = models.CharField( max_length=20, unique=True, null=True, blank=True, help_text="Auto-generated ticket number for tracking" ) # User Association (if logged in when submitting) user = models.ForeignKey( 'users.User', null=True, blank=True, on_delete=models.SET_NULL, related_name='contact_submissions' ) # Assignment & Resolution assigned_to = models.ForeignKey( 'users.User', null=True, blank=True, on_delete=models.SET_NULL, related_name='assigned_contacts' ) admin_notes = models.TextField( null=True, blank=True, help_text="Internal notes for admin use only" ) resolved_at = models.DateTimeField(null=True, blank=True) resolved_by = models.ForeignKey( 'users.User', null=True, blank=True, on_delete=models.SET_NULL, related_name='resolved_contacts' ) # Timestamps created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = 'Contact Submission' verbose_name_plural = 'Contact Submissions' ordering = ['-created_at'] indexes = [ models.Index(fields=['status', '-created_at']), models.Index(fields=['category', '-created_at']), models.Index(fields=['ticket_number']), ] def __str__(self): ticket = f" ({self.ticket_number})" if self.ticket_number else "" return f"{self.name} - {self.get_category_display()}{ticket}" def save(self, *args, **kwargs): # Auto-generate ticket number if not set if not self.ticket_number: # Format: CONT-YYYYMMDD-XXXX from django.db.models import Max today = timezone.now().strftime('%Y%m%d') prefix = f"CONT-{today}" # Get the highest ticket number for today last_ticket = ContactSubmission.objects.filter( ticket_number__startswith=prefix ).aggregate(Max('ticket_number'))['ticket_number__max'] if last_ticket: # Extract the sequence number and increment seq = int(last_ticket.split('-')[-1]) + 1 else: seq = 1 self.ticket_number = f"{prefix}-{seq:04d}" # Set resolved_at when status changes to resolved if self.status == 'resolved' and not self.resolved_at: self.resolved_at = timezone.now() super().save(*args, **kwargs)