Panel de Administración
El panel de Administración concentra la configuración del sistema y los tableros de control: usuarios internos, laboratorios cliente, tipos de servicio, parámetros, el log de auditoría, las notificaciones automáticas, los KPIs de gestión y la integración con Odoo. Es la consola de gobierno del sistema — casi todo lo que se toca acá queda registrado en el audit trail.
Quién puede entrar
El acceso al panel no lo determina el campo "rol" del usuario (que es una etiqueta libre,
de texto), sino un booleano dedicado del token: es_admin_local.
Solo los usuarios con ese flag en true ven el panel y pueden operar sus acciones administrativas.
es_admin_local
El sistema no infiere permisos de administrador a partir del texto del rol. La puerta es el booleano
es_admin_local del token. Escribir "Administrador" en el campo de rol de un usuario
no le da acceso al panel; hay que marcar la casilla "Admin local" en su
ficha. Del mismo modo, setearle una contraseña a alguien no lo promueve a administrador.
Las 7 pestañas del panel
El panel se organiza en siete pestañas. "Sistema" no es una pestaña propia: es un sub-toggle dentro de Parámetros.
| Pestaña | Función principal |
|---|---|
| KPIs / Métricas | Tableros de gestión (solo lectura): totales, GMP vs correctivos, vencidos, comparativos. |
| Usuarios | Alta, edición y archivado de usuarios internos (@ion-target.com); designación de administradores. |
| Tipos | Tipos de servicio y su periodicidad por defecto. |
| Laboratorios | Laboratorios cliente y su vínculo con Odoo (empresa). |
| Parámetros | Pares clave/valor de configuración + sub-toggle "Sistema" (estado de controles). |
| Logs | Audit trail append-only con filtros, diff y correlation ID. |
| Notificaciones | Resúmenes de vencimientos GMP/STD y envíos manuales de reportes. |
Usuarios
Lista únicamente los usuarios internos de ION-target (correo @ion-target.com).
Desde acá se dan de alta, se editan, se archivan y se restauran, y se designa quién es administrador.
Campos del alta / edición
| Campo | Obligatorio | Detalle |
|---|---|---|
| Nombre | ✅ Sí | — |
| Apellido | ✅ Sí | — |
| ✅ Sí | Único en el sistema; debe ser corporativo (@ion-target.com). | |
| Rol | — | Etiqueta libre (texto). No otorga permisos; es descriptivo. |
| Sector | ✅ Sí | Área interna del usuario. |
| Odoo User ID | — | Vincula la ficha a la cuenta interna de Odoo (res.users). |
| Admin local | — | Checkbox. Si se marca, la contraseña pasa a ser obligatoria y de ≥ 8 caracteres. Es lo que setea es_admin_local. |
Setear o cambiar una contraseña no promueve al usuario a administrador. La condición de admin la fija exclusivamente la casilla "Admin local" (que a su vez exige una contraseña de al menos 8 caracteres). Sin esa casilla, aunque tenga contraseña, el usuario no ve el panel.
Acciones sobre usuarios
| Acción | Endpoint | Condiciones |
|---|---|---|
| Listar | GET /admin/usuarios | Solo usuarios internos (@ion-target.com). |
| Crear | POST /admin/usuarios | Nombre, apellido, email (único) y sector obligatorios. Si "Admin local" → contraseña ≥ 8. |
| Editar | PUT /admin/usuarios/{id} | Mismas validaciones. |
| Archivar | PATCH /admin/usuarios/{id}/archivar | Motivo obligatorio. |
| Restaurar | PATCH /admin/usuarios/{id}/restaurar | — |
| Eliminar | DELETE → 405 | No existe borrado físico: solo archivado. |
Laboratorios
Administra los laboratorios cliente y, sobre todo, su vínculo con la empresa en Odoo. La estructura interna de sectores y usuarios de cada laboratorio se gestiona en el módulo Laboratorios, no acá.
| Campo | Obligatorio | Detalle |
|---|---|---|
| Nombre | ✅ Sí | Nombre del cliente. |
| Dirección | — | Texto libre. |
| Teléfono | — | Texto libre. |
| — | Contacto del laboratorio. | |
| Ubicación | — | URL de Google Maps. |
| Odoo Partner ID | — | Se elige con un buscador que consulta GET /odoo/empresas. Vincula el laboratorio a la empresa en Odoo. |
La grilla muestra el conteo de sectores de cada laboratorio como referencia, pero crear/editar sectores y usuarios de sector se hace en el módulo Laboratorios.
| Acción | Endpoint | Condiciones |
|---|---|---|
| Listar | GET /admin/laboratorios | — |
| Crear | POST /admin/laboratorios | Nombre obligatorio. |
| Editar | PUT /admin/laboratorios/{id} | — |
| Archivar / Restaurar | PATCH /admin/laboratorios/{id}/archivar · /restaurar | Archivar pide motivo. |
| Buscar empresa Odoo | GET /odoo/empresas | Alimenta el buscador de Odoo Partner ID. |
Tipos de servicio
Define los tipos de servicio disponibles y su periodicidad. Son 11 tipos GMP. Las categorías AT (Asistencia Técnica) y RB (Reparación) corresponden a asistencias (trabajos correctivos), no a servicios GMP planificados.
| Campo | Detalle |
|---|---|
| Nombre | Nombre del tipo de servicio. |
| Descripción | Texto libre. |
| Periodicidad por defecto (meses) | Cada cuántos meses se repite por defecto ese servicio; se usa para calcular el próximo vencimiento. |
La clasificación de un tipo como GMP o como correctivo (AT/RB) se resuelve por coincidencia de substring en el nombre, no con un campo dedicado. Por eso el nombre importa: cambiarlo puede cambiar cómo se clasifica.
| Acción | Endpoint | Condiciones |
|---|---|---|
| Listar | GET /admin/tipos-servicio | — |
| Crear | POST /admin/tipos-servicio | Nombre obligatorio. |
| Editar | PUT /admin/tipos-servicio/{id} | Editar uno archivado → 409. |
| Archivar / Restaurar | PATCH /admin/tipos-servicio/{id}/archivar · /restaurar | Archivar pide motivo. |
| Eliminar | DELETE → 405 | No hay borrado físico. |
Parámetros del sistema
Pares clave / valor con edición inline. El guardado tiene efecto inmediato.
El parámetro funcional principal es dias_alerta_vencimiento (por defecto 30),
que fija con cuántos días de anticipación se consideran "próximos a vencer" los servicios.
El remitente de las notificaciones sale de la variable de entorno RESEND_FROM,
no de un parámetro editable como "email_remitente". Del mismo modo, la copia (CC) a
support@ion-target.com es fija en el código: no es un parámetro configurable.
Cambiar valores en esta pantalla no altera el remitente ni el CC.
A diferencia del resto del sistema, modificar un parámetro no deja registro en el audit trail. Es una mejora pendiente. Mientras tanto, documentá los cambios de parámetros por fuera del sistema.
Sub-toggle "Sistema"
Dentro de Parámetros hay un toggle "Sistema" de solo lectura que muestra el estado de los controles de seguridad y cumplimiento: rol de aplicación de la base, RLS del log, algoritmo JWT, endpoint JWKS, duración de sesión, etc. No es editable.
Log de actividad
El audit trail del sistema: append-only con RLS. Los registros no se pueden modificar ni eliminar — ni siquiera el propietario ni un administrador. Cada evento lleva un correlation ID (que agrupa todos los pasos de una misma operación) y la IP de origen.
El log es de solo escritura, garantizado a nivel de base de datos mediante Row Level Security (RLS) en PostgreSQL: INSERT y SELECT permitidos, UPDATE y DELETE bloqueados para todo rol de aplicación. Ni el owner de la fila puede alterarla.
El registro de auditoría se escribe en una sesión de base separada: si esa escritura llega a fallar, el evento cae a los logs de la aplicación en lugar de perderse silenciosamente o de tumbar la operación principal.
Qué ve cada quién
El administrador ve todos los eventos de todos los usuarios. Un técnico solo ve sus propias acciones.
Filtros disponibles
| Filtro | Detalle |
|---|---|
| Buscar (texto) | Búsqueda libre sobre el registro. |
| Acción | Tipo de evento (ver catálogo abajo). |
| Entidad | Tipo de objeto afectado. |
| Usuario | Solo administrador (el técnico no puede filtrar por otros). |
| Resultado | ok / error. |
| Desde / Hasta | Rango de fechas. |
Paginado de 50 registros por página. Cada fila tiene un botón para ver las diferencias (valor anterior vs. valor nuevo).
El botón de exportar del log abre una ventana imprimible (HTML → PDF). No descarga un CSV. La exportación a CSV pertenece al módulo KPIs, no al log.
Catálogo de acciones
Estructura de cada registro
- correlation_id — UUID que agrupa los eventos de una misma operación.
- usuario — quién ejecutó la acción.
- acción — tipo de evento (catálogo de arriba).
- entidad — tipo de objeto afectado.
- entidad_id — ID del registro afectado.
- valor_anterior / valor_nuevo — snapshot del cambio (alimenta el diff).
- detalle — información adicional (motivo, contexto).
- IP — dirección de origen.
- resultado — ok / error.
- fecha — timestamp del evento.
Notificaciones
Muestra resúmenes de vencimientos y permite disparar envíos manuales. Los resúmenes en pantalla son de solo lectura; los envíos (POST) son solo para administradores.
| Elemento | Endpoint | Detalle |
|---|---|---|
| Resumen vencimientos GMP | GET /notificaciones/vencimientos | Servicios GMP próximos a vencer. |
| Resumen vencimientos STD | GET /notificaciones/vencimientos-std | Instrumentos/estándares/solventes a 90 días. |
| Enviar alerta GMP | POST (solo admin) | Envío manual del aviso de vencimientos GMP. |
| Enviar reporte mensual | POST (solo admin) | Resumen mensual. |
| Enviar reporte STD | POST (solo admin) | Vencimientos STD a 90 días. |
| Test de email | POST (solo admin) | Prueba de envío. |
Los reportes van automáticamente a todos los usuarios @ion-target.com activos, con
CC fijo a support@ion-target.com. No existe una pantalla para elegir destinatarios:
quién recibe se deriva de los usuarios internos activos.
Scheduler automático
El scheduler (APScheduler) corre el último día del mes a las 08:00 (hora Argentina) y envía dos correos:
Vencimientos GMP del mes siguiente.
Vencimientos de instrumentos/estándares/solventes a 90 días.
KPIs / Métricas
Tablero de gestión de solo lectura, exclusivo de administradores
(GET /analytics/opciones + GET /analytics/kpis). Reúne los indicadores del sistema
con filtros por período y un modo comparativo.
| KPI | Qué cuenta |
|---|---|
| Total | Total de servicios del período. |
| GMP | Servicios GMP. |
| Correctivos | Asistencias AT/RB. |
| Finalizados | Servicios en estado Finalizado OR Resuelto. |
| Vencidos | Solo GMP (las asistencias correctivas no tienen vencimiento). |
| Equipos / Clientes / Técnicos | Conteos distintos (ver nota de granularidad). |
La unidad por defecto es el servicio (COUNT(*)). Las excepciones son
Equipos, Clientes y Técnicos, que se cuentan como valores distintos
(COUNT(DISTINCT)) — un mismo equipo o técnico no suma más de una vez.
Los gráficos (barras, torta, líneas) se dibujan en SVG a mano. El tablero exporta a CSV / PNG / PDF y ofrece un modo Comparativo de hasta 4 períodos (año, trimestre o rango de meses) con deltas porcentuales entre ellos.
Integración con Odoo
El sistema se integra con Odoo por webhook (entrada) y por sincronizaciones manuales (salida/actualización). Tras la revisión de seguridad CR-005, todos los endpoints que mutan o exponen datos requieren token.
El webhook está protegido por un secreto en la URL: si no hay secreto configurado responde 503; si el secreto es inválido responde 403. La comparación se hace en tiempo constante (resistente a timing attacks).
| Operación | Endpoint | Detalle |
|---|---|---|
| Webhook de cambio de etapa | POST /odoo/webhook-stage | Lo dispara la automatización nativa de Odoo al cambiar de etapa (proyectos 12/13). Protegido por secreto. |
| Sincronizar usuarios | POST /odoo/sincronizar-usuarios | Cruza usuarios ITS con Odoo por email. |
| Sincronizar laboratorios | POST /odoo/sincronizar | Actualiza laboratorios contra Odoo. |
| Empresas (buscador) | GET /odoo/empresas | Alimenta el buscador de Odoo Partner ID de Laboratorios. |
stage_id numérico
La correspondencia entre la etapa de Odoo y el estado en ITS se resuelve por el ID numérico de la etapa, no por su nombre — así es robusto a renombres en Odoo. Para AT/RB, la etapa "Finalizado" de Odoo equivale a "Resuelto" en ITS.
Preguntas frecuentes
es_admin_local. En la ficha del usuario (Admin → Usuarios) marcá la casilla "Admin local", que además exige una contraseña de ≥ 8 caracteres. Escribir "Administrador" en el rol no habilita nada.DELETE. Es una decisión de cumplimiento GMP: no hay borrado físico, para no perder el historial.RESEND_FROM. El CC a support@ion-target.com también es fijo en el código. Para cambiar el remitente hay que ajustar RESEND_FROM en el entorno del servidor, no en Parámetros.