Versión 1.3.0: Conexion completa del proyecto

This commit is contained in:
2026-02-03 10:38:54 -07:00
parent 6215fc40a7
commit 0d7cdf51ca
24 changed files with 5038 additions and 149 deletions

View File

@@ -1,56 +1,112 @@
"""
Ticket Model - ServiceManagerWeb
Tickets de soporte - Core del negocio
"""
from sqlalchemy import String, ForeignKey, Text
from sqlalchemy import String, ForeignKey, Text, Integer, CheckConstraint, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import UUID, ENUM
from typing import Optional
from datetime import datetime
import enum
import uuid
from app.core.database import Base
class TicketStatus(str, enum.Enum):
"""Estados posibles de un ticket"""
NEW = "NEW"
TRIAGE = "TRIAGE"
IN_PROGRESS = "IN_PROGRESS"
WAITING_FOR_CLIENT = "WAITING_FOR_CLIENT"
WAITING_CUSTOMER = "WAITING_CUSTOMER" # ✅ CORREGIDO: nombre según schema.sql
RESOLVED = "RESOLVED"
CLOSED = "CLOSED"
REOPENED = "REOPENED"
class TicketPriority(str, enum.Enum):
"""Prioridades posibles de un ticket"""
LOW = "LOW"
MEDIUM = "MEDIUM"
HIGH = "HIGH"
URGENT = "URGENT"
class Ticket(Base):
"""Modelo de tickets de soporte"""
__tablename__ = "tickets"
# Note: id, created_at, updated_at are inherited from Base
tenant_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False)
# Multi-tenancy
tenant_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("tenants.id", ondelete="CASCADE"),
nullable=False
)
# Campos básicos
ticket_number: Mapped[str] = mapped_column(String(20), nullable=False)
subject: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=False)
status: Mapped[TicketStatus] = mapped_column(ENUM(TicketStatus, name="ticket_status_enum", create_type=False), default=TicketStatus.NEW)
priority: Mapped[TicketPriority] = mapped_column(ENUM(TicketPriority, name="ticket_priority_enum", create_type=False), default=TicketPriority.MEDIUM)
# Estado y Prioridad
status: Mapped[TicketStatus] = mapped_column(
ENUM(TicketStatus, name="ticket_status_enum", create_type=False),
default=TicketStatus.NEW,
nullable=False
)
priority: Mapped[TicketPriority] = mapped_column(
ENUM(TicketPriority, name="ticket_priority_enum", create_type=False),
default=TicketPriority.MEDIUM,
nullable=False
)
# Foreign Keys
created_by: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
assigned_to: Mapped[Optional[uuid.UUID]] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=True)
# ✅ CORREGIDO: Foreign Keys apuntan a tablas correctas
created_by: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("users.id"),
nullable=False
)
assigned_to: Mapped[Optional[uuid.UUID]] = mapped_column(
UUID(as_uuid=True),
ForeignKey("users.id"),
nullable=True
)
system_id: Mapped[Optional[uuid.UUID]] = mapped_column(UUID(as_uuid=True), ForeignKey("systems.id"), nullable=True)
category_id: Mapped[Optional[uuid.UUID]] = mapped_column(UUID(as_uuid=True), ForeignKey("categories.id"), nullable=True)
# ✅ CORREGIDO: Renombrado de system_id a affected_system_id
affected_system_id: Mapped[Optional[uuid.UUID]] = mapped_column(
UUID(as_uuid=True),
ForeignKey("affected_systems.id"), # ✅ Tabla correcta
nullable=True
)
# ✅ CORREGIDO: Foreign key a tabla correcta
category_id: Mapped[Optional[uuid.UUID]] = mapped_column(
UUID(as_uuid=True),
ForeignKey("ticket_categories.id"), # ✅ Tabla correcta
nullable=True
)
# ✅ AÑADIDOS: Campos de SLA según schema.sql
sla_response_due: Mapped[Optional[datetime]] = mapped_column(nullable=True)
sla_resolution_due: Mapped[Optional[datetime]] = mapped_column(nullable=True)
first_response_at: Mapped[Optional[datetime]] = mapped_column(nullable=True)
resolved_at: Mapped[Optional[datetime]] = mapped_column(nullable=True)
# ✅ AÑADIDOS: Campos de CSAT (Customer Satisfaction) según schema.sql
rating: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
rating_comment: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
rated_at: Mapped[Optional[datetime]] = mapped_column(nullable=True)
# Relationships
tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="tickets")
system: Mapped["System"] = relationship("System", back_populates="tickets")
category: Mapped["Category"] = relationship("Category", back_populates="tickets")
# ✅ ACTUALIZADO: Nombre de relación y optional
affected_system: Mapped[Optional["System"]] = relationship(
"System",
back_populates="tickets"
)
category: Mapped[Optional["Category"]] = relationship(
"Category",
back_populates="tickets"
)
created_by_user: Mapped["User"] = relationship(
"User",
@@ -63,3 +119,12 @@ class Ticket(Base):
foreign_keys=[assigned_to],
back_populates="assigned_tickets"
)
# ✅ AÑADIDOS: Constraints según schema.sql
__table_args__ = (
UniqueConstraint('tenant_id', 'ticket_number', name='uq_tickets_tenant_number'),
CheckConstraint('rating >= 1 AND rating <= 5', name='check_rating_range'),
)
def __repr__(self) -> str:
return f"<Ticket(id={self.id}, number='{self.ticket_number}', status={self.status})>"