Files
service_manager/backend/app/models/issue.py
2026-03-17 11:09:30 -06:00

102 lines
3.0 KiB
Python

"""
TicketIssue Model - ServiceManagerWeb
Asuntos de escalación asociados a tickets.
Creados por el dueño del ticket o el CLIENT_ADMIN del tenant.
"""
from sqlalchemy import String, ForeignKey, Text, Table, Column
from sqlalchemy.orm import Mapped, mapped_column, relationship
from typing import Optional, List
from datetime import datetime
import uuid
from app.core.database import Base, GUID
from app.models.ticket import TicketPriority
from sqlalchemy import Enum as SAEnum
from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM
# Tabla de relación N:M entre TicketIssue y User (usuarios etiquetados)
ticket_issue_tagged_users = Table(
"ticket_issue_tagged_users",
Base.metadata,
Column(
"issue_id",
GUID(),
ForeignKey("ticket_issues.id", ondelete="CASCADE"),
primary_key=True,
nullable=False,
),
Column(
"user_id",
GUID(),
ForeignKey("users.id", ondelete="CASCADE"),
primary_key=True,
nullable=False,
),
)
class TicketIssue(Base):
"""
Asunto de escalación de un ticket.
Solo puede crearlo:
- El usuario que creó el ticket (created_by del Ticket)
- El CLIENT_ADMIN del mismo tenant
"""
__tablename__ = "ticket_issues"
# Multi-tenancy — siempre filtrar por esto
tenant_id: Mapped[uuid.UUID] = mapped_column(
GUID(),
ForeignKey("tenants.id", ondelete="CASCADE"),
nullable=False,
)
# Relación con el ticket padre
ticket_id: Mapped[uuid.UUID] = mapped_column(
GUID(),
ForeignKey("tickets.id", ondelete="CASCADE"),
nullable=False,
)
# Quién lo creó
created_by: Mapped[uuid.UUID] = mapped_column(
GUID(),
ForeignKey("users.id"),
nullable=False,
)
# Contenido
content: Mapped[str] = mapped_column(Text, nullable=False)
priority: Mapped[TicketPriority] = mapped_column(
SAEnum(TicketPriority, name="ticket_priority_enum", native_enum=False).with_variant(
PG_ENUM(TicketPriority, name="ticket_priority_enum", create_type=True),
"postgresql",
),
default=TicketPriority.MEDIUM,
nullable=False,
)
# Adjunto opcional (reutiliza file_handler igual que TicketAttachment)
attachment_path: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
attachment_filename: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
attachment_mime_type: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
# Relationships
ticket: Mapped["Ticket"] = relationship("Ticket", back_populates="issues")
created_by_user: Mapped["User"] = relationship(
"User",
foreign_keys=[created_by],
)
tagged_users: Mapped[List["User"]] = relationship(
"User",
secondary=ticket_issue_tagged_users,
)
def __repr__(self) -> str:
return f"<TicketIssue(id={self.id}, ticket={self.ticket_id}, priority={self.priority})>"