# Atención al paso: clasificación de servicios y estado de la suite

Fecha de revisión: 2026-09-28

## Clasificación actual

Los servicios clínicos reutilizan la tabla `items`. Actualmente el tipo se deduce por relaciones o por descarte:

- Consulta: `specialties.item_id`.
- Procedimiento: `procedures.item_id`.
- Enfermería: `configurations.clinic_nursing_category_id`.
- Consulta global antigua: `configurations.clinic_appointment_item_id`.
- Laboratorio: ítem clínico que no coincide con las reglas anteriores.

Este último criterio explica por qué servicios como `Consulta médica`, `Retiro de puntos` y `Retiro de implantes` llegaron a mostrarse en Laboratorio. No se mezclaron tablas ni se modificaron sus históricos; el problema era la clasificación implícita por descarte.

La corrección inmediata aplicada en `AppointmentController` y `ExamController` excluye del catálogo de Laboratorio el servicio global de consulta, la categoría de enfermería, los servicios ligados a consulta/procedimiento y los ítems inactivos. La validación del POST utiliza la misma regla.

## Clasificación explícita futura

Se recomienda agregar una columna nullable `items.clinic_service_kind`, de tipo `VARCHAR`, con estos valores:

```text
consulta | laboratorio | procedimiento | enfermeria | legacy | NULL
```

La clasificación debe ser exclusiva. Si un mismo ítem se necesita para dos conceptos distintos, se deben crear dos servicios clínicos separados.

Para no alterar reportes históricos, las nuevas líneas de `clinic_appointment_items` también deberían guardar un snapshot `clinic_service_kind`. La lectura durante la transición sería:

1. Usar el snapshot de la línea si existe.
2. Usar la clasificación explícita del ítem para registros nuevos.
3. Mantener el fallback histórico únicamente para líneas antiguas.

No se debe ejecutar un backfill automático sin auditoría. El proceso recomendado es `dry-run` por tenant, reporte de conflictos, clasificación automática solo de casos inequívocos y estado `legacy` para casos ambiguos.

Precedencia propuesta:

1. Servicio global de consulta → `legacy`.
2. Categoría de enfermería → `enfermeria`.
3. Liga con especialidad → `consulta`.
4. Liga con procedimiento → `procedimiento`.
5. Examen administrado desde Laboratorio → `laboratorio`.
6. Duplicidad o evidencia insuficiente → `legacy` y revisión manual.

La migración no debe cambiar `item_id`, precios, IGV, ventas, compras, lotes ni comprobantes. Los servicios históricos deben desactivarse, no eliminarse físicamente.

## Flujo de atención mixta

En Atención al paso se permite Consulta + Laboratorio. La cita conserva un único `doctor_id`; la especialidad solo corresponde a la consulta y el médico queda como solicitante del laboratorio. La orden formal de laboratorio continúa creándose después desde el flujo de Laboratorio, sin duplicarse al registrar la atención.

## Estado de la suite global

Última ejecución completa: **842 correctas, 9 fallos y 23 omitidas**. Los fallos son previos y no pertenecen al cambio de Atención al paso.

### 1. `ClinicaBillingF2Test`

El reporte de ingresos ahora incluye consulta, productos, laboratorio y farmacia, pero el test todavía comprueba la fórmula antigua `consulta + productos = total`.

Solución: actualizar la aserción para sumar las cuatro categorías y agregar una prueba específica para cada fuente. No se debe quitar laboratorio/farmacia del reporte.

### 2. `InventoryMutationContainmentTest`

El test intenta activar lotes en un producto que ya tiene stock sin lote. El controlador lo bloquea con el mensaje de regularización, que es la protección esperada.

Solución: adaptar el test para validar el rechazo o preparar primero una regularización de lotes. No eliminar la guardia.

### 3–5. `PharmacyReportsFeatureTest`

- `tenant.documents.not_sent` no está registrado al renderizar la vista en el contexto de pruebas porque las rutas del módulo dependen del hostname activo durante el arranque.
- `recurrent-stockouts` no devuelve filas porque el escenario demo no genera suficientes eventos de quiebre según sus filtros.
- El escenario demo no encuentra los seis productos esperados (`DEMO-PHARM-*`); el builder resuelve productos existentes del tenant y no garantiza crear esos identificadores.

Solución: desacoplar el registro de rutas del hostname dinámico o preparar el tenant antes de cargar rutas; hacer que el fixture cree datos mínimos deterministas; y generar eventos que cumplan explícitamente el umbral del reporte. No relajar las consultas del reporte solo para hacer pasar el test.

### 6. `QASimulacionComprasTest`

Al anular una compra por presentación, la operación no encuentra el lote generado. Es una inconsistencia previa entre la línea de compra, el código de lote y la resolución de la presentación.

Solución: reproducir con una prueba aislada, verificar que la anulación use el `item_lot_group_id` de la línea y no reconstruya el lote únicamente por código; después validar unidades base, stock por almacén y kardex.

### 7–8. `WhatsAppBotControllerGateTest`

El cliente Evolution falla cerrado porque el entorno no tiene `EVOLUTION_API_URL` ni `EVOLUTION_API_KEY`. El código devuelve “Evolution no configurado”, mientras los tests esperan el mensaje antiguo “Proxy del bot no configurado”.

Solución: unificar el contrato de error y actualizar las aserciones. Para pruebas del camino feliz se debe inyectar un cliente HTTP falso; nunca depender de credenciales reales.

### 9. `WhatsappControllerSendTest`

El test intenta enviar usando Evolution sin configuración local y falla antes de poder verificar la normalización del número.

Solución: configurar un fake/mock del cliente Evolution y conservar la aserción del número internacional normalizado. No habilitar credenciales reales en la suite.

## Criterio antes de corregir estos fallos

Cada corrección debe conservar el comportamiento productivo esperado, ejecutarse en una prueba focalizada y luego repetirse en la suite global. Los fallos de fixture/configuración deben arreglarse en el entorno de prueba, no alterando reglas de inventario, reportes, seguridad o integración.

