from django.conf import settings from django.db import models class TicketStatus(models.TextChoices): PENDING_INFO = "pending_info", "Pending info" PENDING = "pending", "Pending" IN_PROGRESS = "in_progress", "In progress" PENDING_CONFIRM = "pending_confirm", "Pending confirm" RESOLVED = "resolved", "Resolved" CLOSED = "closed", "Closed" class TicketPriority(models.TextChoices): LOW = "low", "Low" MEDIUM = "medium", "Medium" HIGH = "high", "High" URGENT = "urgent", "Urgent" class TicketCategory(models.Model): parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL) name = models.CharField(max_length=120) code = models.CharField(max_length=80, unique=True) description = models.TextField(blank=True) enabled = models.BooleanField(default=True) sort_order = models.PositiveIntegerField(default=0) class Meta: ordering = ["sort_order", "name"] def __str__(self) -> str: return self.name class Ticket(models.Model): ticket_no = models.CharField(max_length=32, unique=True) title = models.CharField(max_length=200) description = models.TextField() category = models.ForeignKey(TicketCategory, null=True, blank=True, on_delete=models.SET_NULL) status = models.CharField( max_length=32, choices=TicketStatus.choices, default=TicketStatus.PENDING, ) priority = models.CharField( max_length=20, choices=TicketPriority.choices, default=TicketPriority.MEDIUM, ) impact_scope = models.CharField(max_length=80, blank=True) environment = models.CharField(max_length=80, blank=True) related_system = models.CharField(max_length=120, blank=True) requester = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="requested_tickets", ) assignee = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="assigned_tickets", ) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="created_tickets", ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) resolved_at = models.DateTimeField(null=True, blank=True) closed_at = models.DateTimeField(null=True, blank=True) class Meta: ordering = ["-created_at"] indexes = [ models.Index(fields=["ticket_no"]), models.Index(fields=["status", "priority"]), models.Index(fields=["created_at"]), ] def __str__(self) -> str: return f"{self.ticket_no} {self.title}" class TicketComment(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name="comments") content = models.TextField() comment_type = models.CharField(max_length=40, default="user") visibility = models.CharField(max_length=40, default="internal") created_by = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, ) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["created_at"] class TicketAttachment(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name="attachments") file_name = models.CharField(max_length=255) file_type = models.CharField(max_length=80, blank=True) mime_type = models.CharField(max_length=120, blank=True) file_size = models.PositiveBigIntegerField(default=0) storage_path = models.CharField(max_length=500) ocr_text = models.TextField(blank=True) parsed_text = models.TextField(blank=True) created_by = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, ) created_at = models.DateTimeField(auto_now_add=True) class TicketEvent(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name="events") event_type = models.CharField(max_length=80) from_value = models.JSONField(default=dict, blank=True) to_value = models.JSONField(default=dict, blank=True) operator = models.ForeignKey( settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, ) remark = models.TextField(blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["created_at"]