feat: Funcion de sistema tenants

This commit is contained in:
2026-02-23 13:01:24 -07:00
parent ceea67eb2b
commit 1ccc39732b
58 changed files with 1889 additions and 315 deletions

215
scripts/seed_tickets.py Normal file
View File

@@ -0,0 +1,215 @@
"""
Seed de tickets de demostración para ServiceManagerWeb.
Crea tickets variados con categorías, sistemas y prioridades diferentes.
"""
import asyncio
import sys
sys.path.insert(0, '/app')
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy import select
from app.core.config import get_settings
from app.models.tenant import Tenant
from app.models.user import User
from app.models.category import Category
from app.models.system import System
from app.models.ticket import Ticket, TicketStatus, TicketPriority
from app.models.comment import TicketComment
settings = get_settings()
TENANT_SLUG = "aduanasoft-demo"
DEMO_TICKETS = [
{
"subject": "Error en módulo de importaciones del ERP",
"description": "Al intentar registrar una nueva importación, el sistema arroja el error 'NullReferenceException' en el módulo de gestión aduanera. Esto bloquea completamente el flujo de trabajo de importaciones para todos los usuarios del área.",
"priority": TicketPriority.URGENT,
"category_name": "Incidentes Críticos",
"system_name": "ERP Aduanero",
"status": TicketStatus.IN_PROGRESS,
},
{
"subject": "No puedo acceder al portal web desde el lunes",
"description": "Desde el lunes 18 de febrero, el portal web no carga correctamente. La página queda en blanco y en la consola del navegador aparece un error 503. He probado desde diferentes equipos y navegadores con el mismo resultado.",
"priority": TicketPriority.HIGH,
"category_name": "Soporte Técnico",
"system_name": "Portal Web",
"status": TicketStatus.NEW,
},
{
"subject": "Solicitud de capacitación en módulo de reportes",
"description": "Nuestro equipo necesita una sesión de capacitación para el nuevo módulo de reportes y BI. Somos 8 personas del área de contabilidad. Por favor confirmar disponibilidad para la semana del 3 de marzo.",
"priority": TicketPriority.LOW,
"category_name": "Capacitación",
"system_name": "Reportes y BI",
"status": TicketStatus.NEW,
},
{
"subject": "Discrepancia en factura #F-2024-0892",
"description": "La factura F-2024-0892 emitida el 15 de febrero muestra un monto de $47,500 MXN pero según nuestros registros el importe correcto es $45,200 MXN. Favor revisar y emitir nota de crédito si corresponde.",
"priority": TicketPriority.MEDIUM,
"category_name": "Facturación",
"system_name": None,
"status": TicketStatus.WAITING_CUSTOMER,
},
{
"subject": "La app móvil no sincroniza pedidos pendientes",
"description": "En la aplicación móvil versión 3.2.1, los pedidos creados desde el celular no se sincronizan al ERP. Ya desinstalé y reinstalé la app. El problema persiste en iOS y Android.",
"priority": TicketPriority.HIGH,
"category_name": "Soporte Técnico",
"system_name": "App Móvil",
"status": TicketStatus.IN_PROGRESS,
},
{
"subject": "Consulta sobre proceso de pedimento de exportación",
"description": "¿Cuáles son los documentos necesarios para tramitar un pedimento de exportación bajo la clave A1? Necesito la lista actualizada con los cambios del SAT de enero 2025.",
"priority": TicketPriority.LOW,
"category_name": "Consultas Generales",
"system_name": None,
"status": TicketStatus.RESOLVED,
},
{
"subject": "Servidores lentos en horario pico (11am-2pm)",
"description": "Durante el horario de 11am a 2pm el ERP se vuelve extremadamente lento. Las consultas que normalmente tardan 2 segundos pueden llevar hasta 45 segundos. El problema afecta a todos los usuarios concurrentemente.",
"priority": TicketPriority.URGENT,
"category_name": "Infraestructura",
"system_name": "ERP Aduanero",
"status": TicketStatus.IN_PROGRESS,
},
{
"subject": "Error al generar reporte mensual de exportaciones",
"description": "El reporte de exportaciones del mes de enero no se genera correctamente. Al dar clic en 'Generar PDF' aparece un error: 'Timeout al procesar el reporte'. Necesito este reporte para la reunión del viernes.",
"priority": TicketPriority.MEDIUM,
"category_name": "Soporte Técnico",
"system_name": "Reportes y BI",
"status": TicketStatus.NEW,
},
]
DEMO_COMMENTS = {
"Error en módulo de importaciones del ERP": [
("admin@aduanasoft.com", "Hemos identificado el problema. Es un error en la versión 4.2.1 del ERP. Estamos aplicando el parche de emergencia. Estimamos resolución en 2 horas.", False),
("cliente@empresa-demo.com", "Gracias por la respuesta rápida. ¿Podemos continuar con el registro manual mientras tanto?", False),
],
"No puedo acceder al portal web desde el lunes": [
("agente@aduanasoft.com", "Hemos recibido tu ticket. ¿Puedes indicarnos el mensaje exacto que aparece en la consola del navegador? Esto nos ayudará a diagnosticar más rápido.", False),
],
}
async def main():
engine = create_async_engine(settings.DATABASE_URL, echo=False)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with async_session() as session:
# Obtener tenant
result = await session.execute(select(Tenant).where(Tenant.slug == TENANT_SLUG))
tenant = result.scalar_one_or_none()
if not tenant:
print(f"ERROR: Tenant '{TENANT_SLUG}' no encontrado.")
return
tenant_id = tenant.id
print(f"✅ Tenant: {tenant.name}")
# Obtener usuario cliente para asignar como creador
client_user_result = await session.execute(
select(User).where(User.email == "cliente@empresa-demo.com").limit(1)
)
client_user = client_user_result.scalar_one_or_none()
if not client_user:
print("ERROR: cliente@empresa-demo.com no encontrado")
return
# Obtener agente para comentarios
agent_result = await session.execute(
select(User).where(User.email == "agente@aduanasoft.com").limit(1)
)
agent_user = agent_result.scalar_one_or_none()
admin_result = await session.execute(
select(User).where(User.email == "admin@aduanasoft.com").limit(1)
)
admin_user = admin_result.scalar_one_or_none()
# Mapear categorías y sistemas
cats_result = await session.execute(select(Category).where(Category.tenant_id == tenant_id))
cats = {c.name: c for c in cats_result.scalars().all()}
sys_result = await session.execute(select(System).where(System.tenant_id == tenant_id))
systems = {s.name: s for s in sys_result.scalars().all()}
# Verificar tickets existentes
existing_tickets_result = await session.execute(
select(Ticket).where(Ticket.tenant_id == tenant_id)
)
existing_subjects = {t.subject for t in existing_tickets_result.scalars().all()}
print(f"\n🎫 Creando tickets de demostración:")
created = 0
ticket_objects = {}
for ticket_data in DEMO_TICKETS:
subject = ticket_data["subject"]
if subject in existing_subjects:
print(f" ⏭ Ya existe: {subject[:50]}...")
continue
cat_name = ticket_data.pop("category_name", None)
sys_name = ticket_data.pop("system_name", None)
category = cats.get(cat_name) if cat_name else None
system = systems.get(sys_name) if sys_name else None
ticket = Ticket(
tenant_id=tenant_id,
category_id=category.id if category else None,
affected_system_id=system.id if system else None,
created_by=client_user.id,
**ticket_data,
)
session.add(ticket)
await session.flush() # Para obtener el ID
ticket_objects[subject] = ticket
print(f" ✓ [{ticket_data['priority'].value}] {subject[:60]}")
created += 1
await session.flush()
# Crear comentarios de demostración
print(f"\n💬 Añadiendo comentarios:")
for subject, comments in DEMO_COMMENTS.items():
ticket = ticket_objects.get(subject)
if not ticket:
continue
for author_email, content, is_internal in comments:
if author_email == "admin@aduanasoft.com":
author = admin_user
elif author_email == "agente@aduanasoft.com":
author = agent_user
else:
author = client_user
if author:
comment = TicketComment(
ticket_id=ticket.id,
author_id=author.id,
content=content,
is_internal=is_internal,
)
session.add(comment)
print(f" ✓ Comentario en '{subject[:40]}...' por {author_email}")
await session.commit()
print(f"\n{created} tickets creados exitosamente.")
await engine.dispose()
print("🎉 Seed de tickets completado.")
if __name__ == "__main__":
asyncio.run(main())