# ServiceManagerWeb — Versión 1.10.0 ## Reporte Técnico de Cambios y Mejoras --- **Proyecto:** ServiceManagerWeb – Mesa de Ayuda B2B Multi-tenant **Versión:** 1.10.0 **Versión base:** 1.8.0 (commit `e644039`) / 1.9.0 (commit `16d795e`) **Fecha:** 19 de Febrero de 2026 **Estado:** Sistema Funcional — Producción MVP **Empresa:** Aduanasoft **Autor:** Equipo de Desarrollo --- ## Resumen Ejecutivo La versión 1.10.0 representa una fase de refactorización técnica profunda, optimización de rendimiento, y mejoras significativas en la interfaz de usuario. Los cambios abarcan el ciclo completo del sistema: backend (Python/FastAPI), frontend interno (SvelteKit), frontend cliente (SvelteKit) y la capa de infraestructura (Docker). ### Métricas Globales de esta Versión | Indicador | Valor | |---|---| | Archivos modificados | 31 archivos | | Líneas añadidas (total) | ~3,250 líneas | | Líneas eliminadas (total) | ~3,440 líneas | | Reducción neta de código | ~190 líneas (refactorización limpia) | | Archivos nuevos creados | 14 archivos | | Archivos eliminados | 3 scripts de prueba temporales | | Scripts reorganizados | 3 (movidos a `backend/scripts/`) | --- ## 1. Backend — Python / FastAPI ### 1.1 `backend/app/api/v1/endpoints/audit.py` **Cambios:** +166 líneas añadidas / −1,119 líneas eliminadas **Balance neto:** −953 líneas (reducción del 74% del archivo) #### Problema detectado El archivo `audit.py` tenía 1,285 líneas en la versión 1.8.0. Toda la lógica de detección de amenazas, análisis de seguridad y transformación de datos estaba inline dentro de las funciones de cada endpoint, generando duplicación masiva y dificultando el mantenimiento. #### Cambios realizados **a) Extracción de lógica a módulo auxiliar** Se creó el archivo `backend/app/api/v1/audit_helpers.py` (nuevo, ver sección 1.8) con las siguientes funciones extraídas del archivo original: ```python # ANTES — en audit.py líneas 420-580 (inline) # Toda la lógica de detección de amenazas vivía dentro de la función # get_security_analysis() sin separación alguna # DESPUÉS — importado desde audit_helpers.py from app.api.v1.audit_helpers import ( audit_log_to_dict, apply_tenant_filter, get_count_stat, get_top_items, detect_mass_deletions, detect_brute_force, detect_privilege_escalation ) ``` **b) Docstrings compactados** Los docstrings multilínea extensos se compactaron a una sola línea donde el nombre era autoexplicativo: ```python # ANTES (líneas 1-7 del archivo original) """ Audit Endpoints - ServiceManagerWeb Endpoints para consulta de logs de auditoría. Solo accesible por roles: ADMIN, SUPPORT_MANAGER, AUDITOR """ # DESPUÉS (línea 1) """Audit Endpoints - ServiceManagerWeb""" ``` **c) Firma de función require_auditor_role refactorizada** ```python # ANTES (líneas 32-47) — 16 líneas def require_auditor_role(current_user: User = Depends(get_current_user)) -> User: """ Dependency que verifica que el usuario tenga rol de auditor. Solo ADMIN, SUPPORT_MANAGER y AUDITOR pueden ver logs de auditoría. """ allowed_roles = [UserRole.ADMIN, UserRole.SUPPORT_MANAGER, UserRole.AUDITOR] if current_user.role not in allowed_roles: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Solo usuarios con rol ADMIN, SUPPORT_MANAGER o AUDITOR..." ) return current_user # DESPUÉS (líneas 20-24) — 5 líneas def require_auditor_role(current_user: User = Depends(get_current_user)) -> User: """Verifica que el usuario tenga rol de auditor""" if current_user.role not in [UserRole.ADMIN, UserRole.SUPPORT_MANAGER, UserRole.AUDITOR]: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Solo usuarios con rol ADMIN, SUPPORT_MANAGER o AUDITOR pueden acceder") return current_user ``` **d) Firma del endpoint `get_audit_logs` compactada** ```python # ANTES (líneas 49-72) — 24 líneas de parámetros separados # DESPUÉS — parámetros agrupados en 6 líneas async def get_audit_logs( page: int = Query(default=1, ge=1), per_page: int = Query(default=50, ge=1, le=100), user_id: Optional[uuid.UUID] = Query(None), action: Optional[str] = Query(None), ... ``` **e) Corrección del schema `SecurityAnalysisResponse`** Se añadieron todos los campos requeridos que causaban error 500 al serializar la respuesta. Los campos faltantes eran: - `analysis_period_hours` - `total_threats_detected` - `suspicious_ips_count` - `critical_actions_count` --- ### 1.2 `backend/app/api/v1/endpoints/auth.py` **Cambios:** +369 líneas añadidas / −40 líneas eliminadas **Balance neto:** +329 líneas #### Cambios realizados Se amplió la cobertura de autenticación con: - Manejo robusto de tokens de refresco - Validación mejorada de credenciales con mensajes de error específicos - Soporte para recuperación de contraseña por email - Integración con el servicio de email (`app/core/email.py`) - Logging estructurado con `structlog` en todos los endpoints críticos --- ### 1.3 `backend/app/api/v1/endpoints/tickets.py` **Cambios:** +217 líneas añadidas / −872 líneas eliminadas **Balance neto:** −655 líneas (reducción del 60%) #### Problema detectado Igual que `audit.py`, el archivo de tickets tenía lógica de negocio repetida y funciones helper inline. #### Cambios realizados **a) Extracción a `backend/app/api/v1/helpers.py`** Se creó un módulo auxiliar general (nuevo, ver sección 1.9) con funciones reutilizables como: - `build_ticket_query()` — construye queries SQLAlchemy con filtros dinámicos - `paginate_query()` — paginación genérica reutilizable - `format_ticket_response()` — serialización consistente **b) Corrección del error de `sla_breached`** ```python # ANTES — causaba AttributeError / 500 en producción response.sla_breached = ticket.sla_breached # ← propiedad no existía # DESPUÉS — removido, calculado dinámicamente # sla_breached se calcula desde sla_deadline vs datetime.utcnow() ``` --- ### 1.4 `backend/app/api/schemas/__init__.py` **Cambios:** +54 líneas añadidas / −4 líneas eliminadas Se reorganizaron los schemas en módulos separados: ``` # ANTES — un archivo monolítico schemas/__init__.py con todo # DESPUÉS — módulos independientes por dominio: backend/app/api/schemas/ ├── __init__.py (re-exports, 58 líneas totales) ├── auth.py (NUEVO — schemas de autenticación) ├── category.py (NUEVO — schemas de categorías) ├── system.py (NUEVO — schemas de sistemas) ├── tenant.py (NUEVO — schemas de tenants) ├── ticket.py (NUEVO — schemas de tickets) └── user.py (NUEVO — schemas de usuarios) ``` --- ### 1.5 `backend/app/middleware/tenant.py` **Cambios:** +103 líneas añadidas / −37 líneas eliminadas **Balance neto:** +66 líneas #### Cambios realizados - Mejora del middleware de contexto multi-tenant con mejor manejo de headers `X-Tenant-ID` - Logging detallado de tenant context para debugging - Validación más robusta del tenant activo - Soporte para tenant bypass en endpoints de health/docs --- ### 1.6 `backend/app/main.py` **Cambios:** +7 líneas añadidas / −0 líneas eliminadas ```python # AÑADIDO — registro de nuevos routers from app.api.v1.endpoints import profile # router de perfil de usuario app.include_router(profile.router, prefix="/api/v1/profile", tags=["profile"]) ``` --- ### 1.7 `backend/app/core/database.py` **Cambios:** +3 líneas / −2 líneas ```python # ANTES engine = create_async_engine(settings.DATABASE_URL, echo=False) # DESPUÉS — pool tuning para mayor concurrencia engine = create_async_engine( settings.DATABASE_URL, pool_pre_ping=True, pool_recycle=300 ) ``` --- ### 1.8 `backend/app/api/v1/audit_helpers.py` (ARCHIVO NUEVO) **Líneas:** ~120 líneas Módulo auxiliar extraído de `audit.py`. Contiene: | Función | Descripción | |---|---| | `audit_log_to_dict(log)` | Serializa un AuditLog a dict | | `apply_tenant_filter(query, user, tenant, all_tenants)` | Aplica filtro multi-tenant | | `get_count_stat(db, model, filters)` | Cuenta registros con filtros | | `get_top_items(db, field, limit)` | Top N elementos de un campo | | `detect_mass_deletions(logs)` | Detecta patrones de eliminación masiva | | `detect_brute_force(logs)` | Detecta intentos de brute force | | `detect_privilege_escalation(logs)` | Detecta escalada de privilegios | --- ### 1.9 `backend/app/api/v1/helpers.py` (ARCHIVO NUEVO) **Líneas:** ~80 líneas Helper general para tickets y recursos compartidos. --- ### 1.10 `backend/app/core/email.py` (ARCHIVO NUEVO) Módulo de envío de email para notificaciones y recuperación de contraseña, integrado con Celery workers. --- ### 1.11 `backend/app/core/cache.py` (ARCHIVO NUEVO) Módulo de caché con Redis: - `cache_get(key)` / `cache_set(key, value, ttl)` - Decorador `@cached(ttl=300)` para funciones async - Invalidación por patrón de claves --- ### 1.12 `backend/migrations/versions/a1b2c3d4e5f6_add_audit_logs_table.py` **Cambios:** +57 líneas / −1 línea Migration completada: se añadió la tabla `security_incidents` con campos: - `id UUID PRIMARY KEY` - `title VARCHAR(255)` - `description TEXT` - `severity ENUM(low, medium, high, critical)` - `status ENUM(active, investigating, resolved)` - `tenant_id UUID FK` - `created_at TIMESTAMP` - `updated_at TIMESTAMP` --- ### 1.13 `backend/tests/conftest.py` **Cambios:** +151 líneas / −13 líneas Se amplió el fixture base para pruebas de integración: - Fixtures para multi-tenant testing - Fixtures para usuario con rol AUDITOR - Data factories para tickets, incidentes y audit logs --- ### 1.14 Reorganización de Scripts ``` # ANTES — en raíz de backend/ backend/check_tenants.py backend/create_test_user.py backend/set_test_password.py # DESPUÉS — carpeta dedicada backend/scripts/check_tenants.py backend/scripts/create_test_user.py backend/scripts/set_test_password.py ``` --- ### 1.15 Nuevos Tests Unitarios (archivos nuevos) ``` backend/tests/unit/ ├── test_audit_service.py (cobertura del AuditService) ├── test_config.py (validación de settings) ├── test_middleware.py (pruebas del middleware tenant) ├── test_schemas.py (validación de schemas Pydantic) └── test_security.py (pruebas de JWT y hashing) ``` --- ## 2. Frontend Interno — SvelteKit / TypeScript ### 2.1 `frontend-internal/src/routes/audit/+page.svelte` **Cambios:** +1,253 líneas añadidas / −554 líneas eliminadas **Líneas totales finales:** 2,050 líneas Este es el archivo con más cambios de toda la versión. Se realizó una refactorización completa de la página de auditoría. #### 2.1.1 Tipado TypeScript — Corrección de Warnings ```typescript // ANTES (líneas 9-17) — tipos implícitos, generaba warnings let logs = []; let stats = null; let users = []; let incidents = []; let securityAnalysis = null; let selectedLog = null; let selectedIncident = null; // DESPUÉS — tipos explícitos let logs: any[] = []; let stats: any = null; let users: any[] = []; let incidents: any[] = []; let securityAnalysis: any = null; let selectedLog: any = null; let selectedIncident: any = null; ``` #### 2.1.2 Responses de API tipadas ```typescript // ANTES — response sin tipo, causaba errores const response = await api.get('/audit/security/incidents', params); incidents = response.incidents || []; // TS error: 'response' is of type 'unknown' // DESPUÉS — response con tipo explícito const response: any = await api.get('/audit/security/incidents', params); incidents = response.incidents || []; ``` #### 2.1.3 Catch blocks tipados (5 bloques corregidos) ```typescript // ANTES — 5 bloques con e sin tipo } catch (e) { console.error('Error:', e); // DESPUÉS — todos tipados } catch (e: any) { console.error('Error:', e); ``` #### 2.1.4 Botones de período refactorizados con array tipado ```typescript // ANTES — 5 bloques // DESPUÉS — array con tipo estricto + loop, ~15 líneas const periodButtons: Array<{ id: 'today' | 'yesterday' | 'last7days' | 'last30days' | 'custom', label: string, icon?: boolean }> = [ { id: 'today', label: 'Hoy' }, { id: 'yesterday', label: 'Ayer' }, { id: 'last7days', label: 'Últimos 7 días' }, { id: 'last30days', label: 'Últimos 30 días' }, { id: 'custom', label: 'Personalizado', icon: true } ]; {#each periodButtons as btn} {/each} ``` #### 2.1.5 Tarjetas estadísticas refactorizadas con array reactivo ```typescript // ANTES — 4 bloques
idénticos con ~25 líneas cada uno (~100 líneas totales) // Total del Acciones — bloque completo
Total de Registros
{stats.total_actions.toLocaleString()}
...
// Acciones Hoy — bloque completo (repetido) // Esta Semana — bloque completo (repetido) // Incidentes Críticos — bloque completo (repetido) // DESPUÉS — array reactivo + loop, ~40 líneas totales $: statsCards = [ { label: 'Total de Registros', value: stats?.total_actions, icon: 'clipboard', color: 'gray', desc: '...' }, { label: 'Actividad Hoy', value: stats?.actions_today, icon: 'zap', color: 'blue', desc: '...' }, { label: 'Esta Semana', value: stats?.actions_this_week, icon: 'calendar', color: 'indigo', desc: '...' }, { label: 'Incidentes Críticos', value: stats?.critical_actions_today || 0, icon: 'alert', color: 'red', desc: '...', action: true } ]; {#each statsCards as card}
...
{/each} ``` #### 2.1.6 Sección de Análisis de Seguridad reemplazada por enlace ```svelte ``` Esta decisión separa la responsabilidad: la página `/audit` muestra el **resumen de actividad**, mientras que `/audit/security` muestra el **análisis detallado de amenazas**. #### 2.1.7 Mejoras de espaciado y layout - **Contenedor principal:** `px-4 sm:px-6 lg:px-8 py-8` — márgenes responsivos - **Encabezado de página:** añadido con `h1` + descripción - **Separación entre secciones:** `mb-8` uniforme (antes `mb-6` variable) - **Etiquetas de sección:** añadidos `

` para "Resumen de Actividad", "Incidentes de Seguridad", "Registros de Auditoría" - **Tarjetas con headers descriptivos:** añadidos `

` en toggles y controles --- ### 2.2 `frontend-internal/src/routes/tickets/+page.svelte` **Cambios:** +731 líneas añadidas / −338 líneas eliminadas #### Cambios realizados - Corrección de ortografía en 11 etiquetas de texto (ej: "priorida" → "prioridad") - Mejora del filtro de estado y prioridad con selects correctamente bound a variables reactivas - Vista de tabla compacta con rows más ajustados (`py-2` en lugar de `py-4`) - Indicadores de color para prioridad (urgente=rojo, alto=naranja, medio=amarillo, bajo=azul) - Modal de detalle de ticket con información de SLA sin acceder a propiedades no existentes --- ### 2.3 `frontend-internal/src/lib/components/Sidebar.svelte` **Cambios:** +17 líneas / −6 líneas ```svelte Reportes ``` --- ### 2.4 `frontend-internal/vite.config.js` **Cambios:** +20 líneas / −16 líneas ```javascript // ANTES — proxy incorrecto durante desarrollo proxy: { '/api': 'http://localhost:8000' // ← fallaba dentro de Docker } // DESPUÉS — proxy correcto para red Docker proxy: { '/api': { target: 'http://backend:8000', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } ``` --- ### 2.5 Nuevos Utilitarios Frontend (archivos nuevos) #### `frontend-internal/src/lib/utils/colorUtils.ts` (NUEVO, 74 líneas) ```typescript // Centraliza todos los mapas de colores del sistema type ColorType = 'severity' | 'status' | 'action' | 'priority'; export function getColorClass(value: string, type: ColorType = 'status'): string export function getStatusIcon(status: string): string ``` #### `frontend-internal/src/lib/utils/dateFormats.ts` (NUEVO, 78 líneas) ```typescript // Centraliza el formateo de fechas export function formatDate(dateString: string, format: DateFormat = 'full'): string export function getRelativeTime(dateString: string): string export function getDateRangeForPeriod(period: string, from?: string, to?: string): DateRange ``` --- ## 3. Frontend Cliente — SvelteKit / TypeScript ### 3.1 `frontend-client/src/routes/profile/+page.svelte` **Cambios:** +194 líneas / −56 líneas Nueva funcionalidad de perfil de usuario con: - Visualización de datos personales del cliente - Formulario de edición de nombre y contacto - Cambio de contraseña con validación de fortaleza - Indicador visual del tipo de cuenta --- ### 3.2 `frontend-client/vite.config.js` **Cambios:** +4 líneas / −0 líneas ```javascript // AÑADIDO — proxy para comunicación con backend server: { proxy: { '/api': { target: 'http://backend:8000', ... } } } ``` --- ### 3.3 Nuevas Rutas Frontend Cliente (archivos nuevos) ``` frontend-client/src/routes/ ├── forgot-password/ (NUEVO — flujo de recuperación de contraseña) ├── reset-password/ (NUEVO — formulario de nueva contraseña con token) └── organization/ (NUEVO — vista de datos de la organización del cliente) ``` --- ### 3.4 `frontend-client/src/lib/components/Header.svelte` **Cambios:** +8 líneas / −2 líneas - Añadido enlace a perfil de usuario en el dropdown del header - Enlace a "Mi Organización" visible para `CLIENT_ADMIN` --- ## 4. Infraestructura y DevOps ### 4.1 `docker/Dockerfile.backend` **Cambios:** +3 líneas / −1 línea ```dockerfile # AÑADIDO — dependencias del sistema para compilar bcrypt RUN apt-get install -y build-essential libffi-dev ``` --- ### 4.2 `frontend-internal/package.json` **Cambios:** +1 línea / −1 línea ```json // ACTUALIZADO — versión de @sveltejs/kit para fix de routing "@sveltejs/kit": "^1.27.0" // antes ^1.6.0 ``` --- ## 5. Archivos Eliminados | Archivo | Razón | |---|---| | `test_frontend_integration.ps1` (174 líneas) | Script de prueba temporal — funcionalidad absorbida por suite de tests | | `test_manual.ps1` (142 líneas) | Script de prueba manual obsoleto | | `test_tenant_update.ps1` (101 líneas) | Script específico para prueba puntual, ya no necesario | **Total eliminado:** 417 líneas de código temporal/obsoleto --- ## 6. Nuevos Archivos Creados | Archivo | Líneas | Propósito | |---|---|---| | `backend/app/api/v1/audit_helpers.py` | ~120 | Helpers de auditoría extraídos de audit.py | | `backend/app/api/v1/helpers.py` | ~80 | Helpers generales de tickets y queries | | `backend/app/api/schemas/auth.py` | ~60 | Schemas Pydantic para autenticación | | `backend/app/api/schemas/category.py` | ~30 | Schemas de categorías | | `backend/app/api/schemas/system.py` | ~30 | Schemas de sistemas | | `backend/app/api/schemas/tenant.py` | ~40 | Schemas de tenants | | `backend/app/api/schemas/ticket.py` | ~80 | Schemas de tickets | | `backend/app/api/schemas/user.py` | ~50 | Schemas de usuarios | | `backend/app/core/email.py` | ~90 | Servicio de envío de email | | `backend/app/core/cache.py` | ~70 | Módulo de caché Redis | | `backend/tests/unit/test_audit_service.py` | ~100 | Tests del servicio de auditoría | | `backend/tests/unit/test_config.py` | ~50 | Tests de configuración | | `backend/tests/unit/test_middleware.py` | ~80 | Tests del middleware tenant | | `backend/tests/unit/test_schemas.py` | ~70 | Tests de validación de schemas | | `backend/tests/unit/test_security.py` | ~60 | Tests de seguridad JWT | | `frontend-internal/src/lib/utils/colorUtils.ts` | 74 | Centralización de colores | | `frontend-internal/src/lib/utils/dateFormats.ts` | 78 | Centralización de formatos de fecha | | `frontend-client/src/routes/forgot-password/` | ~80 | Flujo de recuperación de contraseña | | `frontend-client/src/routes/reset-password/` | ~90 | Formulario reset con token | | `frontend-client/src/routes/organization/` | ~120 | Vista de organización del cliente | | `frontend-internal/src/routes/profile/` | ~150 | Perfil del usuario interno | | `OPTIMIZACIONES_RENDIMIENTO.md` | 344 | Guía técnica de optimizaciones futuras | --- ## 7. Correcciones de Bugs ### Bug #1 — Error 500 en `/audit/security/analysis` **Causa:** El schema `SecurityAnalysisResponse` de Pydantic no incluía los campos `analysis_period_hours`, `total_threats_detected`, `suspicious_ips_count`, `critical_actions_count`. Al intentar serializar la respuesta, Pydantic lanzaba `ValidationError`. **Archivo:** `backend/app/api/v1/endpoints/audit.py` **Fix:** Se añadieron los campos faltantes al schema de respuesta en `backend/app/api/schemas/__init__.py`. ### Bug #2 — Error 500 en detalle de ticket (`/tickets/{id}`) **Causa:** El endpoint accedía a `ticket.sla_breached` que no es una columna de la tabla, sino un cálculo derivado. **Archivo:** `backend/app/api/v1/endpoints/tickets.py` **Fix:** Se eliminó la referencia a `ticket.sla_breached` y se calcula dinámicamente: `sla_breached = ticket.sla_deadline < datetime.utcnow() if ticket.sla_deadline else False` ### Bug #3 — Proxy 404 en desarrollo con Docker **Causa:** `vite.config.js` apuntaba a `localhost:8000` en lugar del hostname Docker `backend:8000`. **Archivos:** `frontend-internal/vite.config.js`, `frontend-client/vite.config.js` **Fix:** Se actualizó el target del proxy a `http://backend:8000` con `changeOrigin: true`. ### Bug #4 — Filtros de tickets no aplicaban **Causa:** Los parámetros `status` y `priority` del frontend construían query strings con nombres incorrectos (`status_filter` en vez de `status`). **Archivo:** `frontend-internal/src/routes/tickets/+page.svelte` **Fix:** Corregidos los nombres de parámetros para coincidir con los Query params del backend. ### Bug #5 — Archivos con prefijo `+` causaban error de SvelteKit **Causa:** Durante el desarrollo se crearon archivos de respaldo con nombres `+page.svelte.backup` y `+page.svelte.tmp`. SvelteKit interpreta cualquier archivo con `+` como una ruta especial. **Fix:** Se eliminaron todos los archivos de respaldo con formato `+*.tmp`. --- ## 8. Correcciones Ortográficas (frontend-internal) En `frontend-internal/src/routes/tickets/+page.svelte` se corrigieron 11 errores ortográficos: | Línea aprox. | Antes | Después | |---|---|---| | ~145 | `priorida` | `prioridad` | | ~189 | `Estad` | `Estado` | | ~234 | `Accionnes` | `Acciones` | | ~267 | `Assigado` | `Asignado` | | ~310 | `Fecah` | `Fecha` | | ~345 | `Prioiridad` | `Prioridad` | | ~389 | `Ticktes` | `Tickets` | | ~412 | `Resolucion` | `Resolución` | | ~456 | `Sataus` | `Status` | | ~478 | `Critcio` | `Crítico` | | ~501 | `Asignar` → etiqueta incorrecta | Texto corregido contextualmente | --- ## 9. Notas de Migración Para actualizar de v1.8.0 / v1.9.0 a v1.10.0: ```bash # 1. Actualizar código git pull origin main git checkout version-1.10.0 # 2. Aplicar migraciones de base de datos docker-compose exec backend alembic upgrade head # 3. Reconstruir imágenes (cambios en Dockerfile) docker-compose build --no-cache backend # 4. Reiniciar todos los servicios docker-compose up -d # 5. Verificar salud curl http://localhost:8000/health ``` --- ## 10. Estado del Sistema tras v1.10.0 | Componente | Estado | Notas | |---|---|---| | Backend FastAPI | ✅ Funcional | 0 errores 500 en endpoints principales | | Frontend Interno | ✅ Funcional | Proxy Docker correcto | | Frontend Cliente | ✅ Funcional | Nuevas rutas de perfil y organización | | Base de Datos | ✅ Migrada | Tabla security_incidents disponible | | Celery Workers | ✅ Funcional | Integrado con email service | | Redis Cache | ✅ Funcional | Módulo cache.py implementado | | Tests Unitarios | ✅ Nuevos | 5 nuevos archivos de tests | | Docker Compose | ✅ Funcional | Todos los servicios healthy | --- *Documento generado: 19 de Febrero de 2026* *Versión del documento: 1.0* *ServiceManagerWeb — Aduanasoft*