✨ Características Nuevas: - Cálculo automático de SLA en tickets basado en categoría - Auto-asignación de tickets según configuración de categoría - Auditoría completa en operaciones de categorías (create/update/delete) - Visualización de estado SLA en listado y detalle de tickets 🐛 Correcciones: - Fix actualización de status en tenants (manejo correcto de enum TenantStatus) - Corrección de campos contact_phone y contact_email en tenants - Corrección de modelo TicketResponse (agregar campos SLA y usar ConfigDict) - Eliminación de archivo changelog duplicado 🔧 Mejoras de Infraestructura: - Agregar montaje de backend en workers y beat para imports correctos - Mejorar path handling en sla_tasks.py para Docker - Scripts de testing integrados (test_frontend_integration, test_manual, test_tenant_update) - Agregar database.py en workers/app/core para sesiones async 📝 Frontend: - Actualizar UI de tenants con nuevos campos (email, teléfono, status enum) - Agregar columna de SLA en listado de tickets - Mostrar información detallada de SLA en vista de ticket individual - Indicadores visuales de estado de SLA (vencido, cumplido, en plazo)
119 lines
3.6 KiB
Python
119 lines
3.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
from pydantic import BaseModel, ConfigDict, EmailStr
|
|
from typing import List, Optional
|
|
import uuid
|
|
|
|
from app.core.database import get_db
|
|
from app.models.tenant import Tenant, TenantStatus
|
|
from app.api import deps
|
|
|
|
router = APIRouter()
|
|
|
|
class TenantBase(BaseModel):
|
|
name: str
|
|
slug: str
|
|
domain: Optional[str] = None
|
|
contact_email: Optional[EmailStr] = None
|
|
contact_phone: Optional[str] = None
|
|
|
|
class TenantCreate(TenantBase):
|
|
pass
|
|
|
|
class TenantUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
slug: Optional[str] = None
|
|
domain: Optional[str] = None
|
|
contact_email: Optional[EmailStr] = None
|
|
contact_phone: Optional[str] = None
|
|
status: Optional[TenantStatus] = None
|
|
|
|
class TenantResponse(TenantBase):
|
|
id: uuid.UUID
|
|
status: TenantStatus
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
@router.get("/", response_model=List[TenantResponse])
|
|
async def read_tenants(
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user = Depends(deps.get_current_active_superuser)
|
|
):
|
|
query = select(Tenant).offset(skip).limit(limit)
|
|
result = await db.execute(query)
|
|
return result.scalars().all()
|
|
|
|
@router.post("/", response_model=TenantResponse)
|
|
async def create_tenant(
|
|
tenant: TenantCreate,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user = Depends(deps.get_current_active_superuser)
|
|
):
|
|
# Check existing slug
|
|
query = select(Tenant).where(Tenant.slug == tenant.slug)
|
|
result = await db.execute(query)
|
|
if result.scalar_one_or_none():
|
|
raise HTTPException(status_code=400, detail="Tenant slug already exists")
|
|
|
|
db_tenant = Tenant(**tenant.model_dump())
|
|
db.add(db_tenant)
|
|
await db.commit()
|
|
await db.refresh(db_tenant)
|
|
return db_tenant
|
|
|
|
@router.get("/{tenant_id}", response_model=TenantResponse)
|
|
async def read_tenant(
|
|
tenant_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user = Depends(deps.get_current_active_superuser)
|
|
):
|
|
tenant = await db.get(Tenant, tenant_id)
|
|
if not tenant:
|
|
raise HTTPException(status_code=404, detail="Tenant not found")
|
|
return tenant
|
|
|
|
@router.put("/{tenant_id}", response_model=TenantResponse)
|
|
async def update_tenant(
|
|
tenant_id: uuid.UUID,
|
|
tenant_in: TenantUpdate,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user = Depends(deps.get_current_active_superuser)
|
|
):
|
|
tenant = await db.get(Tenant, tenant_id)
|
|
if not tenant:
|
|
raise HTTPException(status_code=404, detail="Tenant not found")
|
|
|
|
update_data = tenant_in.model_dump(exclude_unset=True)
|
|
if "status" in update_data:
|
|
# Convertir string a enum TenantStatus
|
|
status_value = update_data.pop("status")
|
|
if isinstance(status_value, str):
|
|
tenant.status = TenantStatus(status_value)
|
|
else:
|
|
tenant.status = status_value
|
|
|
|
for field, value in update_data.items():
|
|
setattr(tenant, field, value)
|
|
|
|
await db.commit()
|
|
await db.refresh(tenant)
|
|
return tenant
|
|
|
|
@router.delete("/{tenant_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_tenant(
|
|
tenant_id: uuid.UUID,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user = Depends(deps.get_current_active_superuser)
|
|
):
|
|
"""Eliminar un cliente (tenant) por ID."""
|
|
tenant = await db.get(Tenant, tenant_id)
|
|
if not tenant:
|
|
raise HTTPException(status_code=404, detail="Tenant not found")
|
|
|
|
await db.delete(tenant)
|
|
await db.commit()
|
|
return {"message": "Tenant deleted successfully"}
|