# Auditoría local del plan de reparto de agua

Fecha: 2026-10-21
Alcance: estado del código local; no representa un despliegue nuevo.

## Regla obligatoria

Todo lo exclusivo de reparto se habilita solamente cuando el tenant confirma:

```text
business_turns.value = water_distributor
business_turns.active = true
```

La decisión se centraliza en `App\Services\Tenant\DeliveryBusinessTurnGuard`.
Si la tabla no existe, la consulta falla o el giro está inactivo, la respuesta
es bloqueada. No se deben crear excepciones directas en controladores, jobs,
servicios, rutas ni en el APK.

## Estado punto por punto

| Área del plan | Estado actual | Evidencia | Falta para certificar |
|---|---|---|---|
| Empresa por RUC, tenant y sucursal | Implementado | Login móvil en dos pasos y conexión del usuario a su tenant/sucursal | Probar un segundo tenant y dos sucursales reales |
| Usuario repartidor | Implementado | Marca `delivery_driver`, perfil y restricciones | Revisión visual final con usuario real |
| Restricciones del repartidor | Implementado | Middleware y controladores bloquean POS, caja, comprobantes, clientes y reportes no pertinentes | Matriz E2E completa contra release desplegado |
| Pedido de reparto | Implementado | `OrderNoteController` y guard de giro | Crear pedido nuevo controlado |
| Dirección, teléfono y coordenadas | Implementado | Direcciones múltiples, referencia, celular, latitud/longitud y Maps por tenant | Probar dos direcciones del mismo cliente |
| Asignación y cambio de repartidor | Implementado y probado localmente | Reserva, bloqueo de fila, outbox y conflicto `409` fuera de pendiente | Repetir con dos usuarios en runtime |
| Notificaciones FCM | Implementado | Outbox, worker, reintentos y `order_id` en payload | Caída real de Firebase y APK cerrada/bloqueada |
| Detalle del pedido móvil | Implementado | Producto, presentación, cantidad, total, dirección y celular | Pedido nuevo con presentación real |
| Aceptar, estados y ruta | Implementado | Aceptación idempotente, estados terminales y activación automática de ruta | Rechazo, reprogramación y varios pedidos |
| GPS de entrega | Implementado | Permiso opcional, confirmación con o sin coordenadas y persistencia | Prueba física bloqueada y sin permiso |
| Jornada laboral | Implementado localmente y probado en backend | Sesión independiente de `delivery_routes`, puntos idempotentes, cola offline y cierre administrativo | Migración, APK nueva y jornada física completa |
| Mapa administrativo | Implementado localmente | Endpoint de jornadas activas, estados online/stale/offline y marcador por motorizado | Clave Maps, varios dispositivos y asset desplegado |
| APK y jornada | Diseño corregido | No existe pantalla manual de jornada; el APK inicia/recupera el servicio automáticamente al iniciar sesión | Validar boot, pantalla bloqueada, red caída y recuperación |
| Cobro efectivo | Implementado | Monto esperado, recibido, vuelto, locks e idempotencia | Segundo dispositivo concurrente |
| Yape/Plin/transferencia | Implementado | Método, referencia, archivo y revisión de Caja | Foto real, rechazo y reintento |
| Crédito/no cobrado | Implementado | Estados y validaciones específicas | Confirmar política comercial |
| Caja y comprobantes | Implementado parcialmente | Validación/rechazo, bloqueo de duplicidad y `Ya generado` | Comprobante real y revisión completa |
| Adicionales, faltantes y sustituciones | Implementado | Ajustes aprobables y snapshots de cantidades | E2E con diferencia de cantidades |
| Bidones, QR y estados físicos | Implementado en código | Catálogo, movimientos idempotentes, depósitos y liquidación | Cámara/QR y recorrido físico |
| Presentaciones, precios y puntos | Implementado y probado localmente | Tipos de unidad, precios por lista/sucursal/canal, ledger y reglas | Configuración comercial real |
| Canje de 11 puntos | Implementado técnicamente | Campaña y recompensa por presentación | Definir producto/recompensa final |
| Migraciones | Implementado y probado | Cadena ordenada, reversible y tests SQLite | Staging y backup antes de producción |
| Deploy seguro | Implementado | `flock`, preflight, no-build, healthchecks, marker y rollback | Ejecutar solo con disco bajo 90% |

### Correcciones aplicadas después de la auditoría

- El servicio nativo usa ahora la URL y el host del tenant guardados después del
  login, no los valores compilados por defecto. Esto evita enviar GPS al tenant
  equivocado cuando se usan subdominios.
- Se agregó `delivery:close-stale-tracking`, ejecutado cada cinco minutos, para
  cerrar jornadas sin sincronización reciente. El umbral es configurable con
  `DELIVERY_TRACKING_STALE_MINUTES` y por defecto es de 15 minutos.
- La inserción de puntos usa `insertOrIgnore` y valida el propietario del
  evento, evitando fallos por reintentos simultáneos con el mismo UUID.
- El panel web muestra batería y tipo de red además de la última ubicación.
- El panel web permite consultar el recorrido histórico de una jornada y dibuja
  la polilínea sobre el mapa. La retención se limita mediante
  `DELIVERY_TRACKING_RETENTION_DAYS` (90 días por defecto).

## Correcciones adicionales verificadas

- Las operaciones de reparto, bidones, depÃ³sitos, sustituciones y FCM usan la conexiÃ³n tenant configurada, igual que el guard central.
- El cierre automÃ¡tico de jornadas y los movimientos por lote verifican el giro antes de modificar datos.
- POS y configuraciÃ³n de presentaciones identifican el giro por `value = water_distributor`, no por un ID fijo.

## Jornada: comportamiento aprobado

1. El repartidor inicia sesión en el APK.
2. La API crea o recupera una sesión activa para su dispositivo.
3. Android inicia el servicio foreground y envía ubicación, batería y red.
4. Si no hay señal, conserva puntos y los reintenta después.
5. El panel web muestra la última ubicación y el estado de cada motorizado.
6. Administración puede cerrar la jornada desde el panel.
7. El APK detecta la sesión cerrada y detiene el servicio.
8. Si el giro `water_distributor` se desactiva, las rutas devuelven `403` y el
   servicio móvil detiene el rastreo.

La caja no dispara la jornada: puede pertenecer a otro usuario o quedar abierta
por error. Se usa únicamente para conciliación.

## Verificación local ejecutada

- `php artisan test --no-coverage`: **133 pruebas aprobadas**.
- `flutter analyze`: sin incidencias.
- `flutter test`: **20 pruebas aprobadas**.
- `flutter build apk --debug`: APK generada correctamente.
- `DeliveryTrackingSessionTest`: inicio idempotente, punto duplicado y cierre.
- `TenantDeliveryTrackingSessionMigrationTest`: creación y reversa.
- `TenantMigrationChainTest`: orden de migraciones.
- Prueba Flutter de conexión nativa: confirma que el GPS utiliza el subdominio
  y host guardados por el login del tenant.
- Playwright demo de solo lectura y seguridad: **2/2 aprobadas** usando el
  bypass explícito `SMARTPLUS_E2E_IGNORE_HTTPS_ERRORS=true`; el certificado TLS
  del dominio demo está vencido y debe renovarse, pero no se ignorará en
  producción.

## Pendientes que no deben declararse como terminados

- Migrar las tablas de jornada en los tenants objetivo.
- Instalar la APK nueva y probar pantalla bloqueada, reinicio y pérdida de red.
- Probar dos dispositivos y concurrencia real.
- Simular caída real de Firebase.
- Probar foto Yape/Plin y revisión de Caja.
- Probar QR y estados físicos de bidones.
- Ejecutar el mismo flujo con el giro de agua desactivado y verificar `403` en
  web, API, jobs, GPS, puntos y bidones.

## Corte de auditoria posterior: 2026-10-21

Se corrigieron hallazgos que contradecian la regla de activacion del plan:

- La serializacion de rutas y el calculo del estado de diferencias usan la
  conexion configurada del tenant, no una conexion `tenant` fija.
- Movimientos de bidones, depositos, liquidaciones, puntos, precios, FCM,
  cierres automaticos y aprobacion de diferencias resuelven la conexion del
  tenant antes de consultar o modificar datos.
- La creacion y actualizacion de usuarios usa la misma conexion configurada
  que utiliza el guard.
- Al desactivar `water_distributor`, las mutaciones generales de pedidos de
  reparto historicos quedan bloqueadas: editar, duplicar, aprobar, anular y
  eliminar. Las consultas historicas no se eliminan.
- Se elimino la promocion de propiedades de constructor en el servicio de
  depositos para mantener compatibilidad con el FPM PHP 7.4.
- El total de cobro del reparto ahora usa el total ajustado de las lineas
  entregadas en el recurso, el saldo y el estado de pago, evitando que Caja
  compare contra el total original despues de adicionales o sustituciones.
- El cambio de estado fisico de un bidon se realiza mediante un movimiento
  `adjustment` auditable e idempotente; la edicion generica ya no acepta
  cambiar directamente el estado.

Pruebas nuevas y revalidadas:

- `DeliveryTenantConnectionTest`: 2 pruebas con conexion configurada distinta
  de `tenant`.
- `DeliveryBusinessTurnGateTest`: 13 pruebas; incluye mutaciones generales
  bloqueadas con el giro apagado.
- Suite completa: **133 pruebas aprobadas**.
- Playwright demo de lectura y seguridad: **2/2 aprobadas**.

Este corte sigue siendo local. No constituye un commit ni un despliegue. La
certificacion externa continua pendiente para APK cerrada/pantalla bloqueada,
segundo dispositivo, caida real de Firebase, Yape/Plin con revision de Caja,
QR fisico, estados fisicos de bidones, migraciones de tenants y una prueba
runtime con `water_distributor.active = false`.

No se realizó commit ni despliegue con estos cambios locales.

## Corte de verificacion posterior: documentos, pagos y totales

- La suite completa actual queda en **149 pruebas aprobadas**.
- `DeliveryBusinessTurnGateTest` incluye ahora 18 pruebas: pagos legacy
  vinculados a reparto no pueden saltarse el giro activo y la generacion se
  bloquea mientras exista una diferencia `pending` o `rejected`.
- La validacion se centralizo en `DeliveryDocumentGuard`, por lo que tambien
  cubre las rutas directas de nota de venta y documento, no solo el generador
  por lote.
- Un comprobante de reparto ahora exige que el pedido este `delivered` y que
  exista el detalle final de entrega; no se permite emitirlo antes del cierre.
- Si la entrega tiene un ajuste aprobado, `DeliveryDocumentPayloadBuilder`
  reconstruye las lineas fiscales a partir del detalle final: cantidades
  entregadas, adicionales, devoluciones y sustituciones. El guard permite el
  caso aprobado y mantiene bloqueados los casos `pending` o `rejected`.
- La reconstruccion probada recalcula valor, IGV y total para lineas gravadas,
  exoneradas e inafectas; rechaza cargos, descuentos o anticipos manuales
  mezclados con un ajuste porque requieren una regla fiscal especifica.
- El reporte de entrega rechaza repetir una linea original con dos resultados
  finales, evitando duplicar ajustes por enviar presentaciones diferentes.
- `DeliveryLineUniquenessTest` verifica esa regla sobre el metodo que persiste
  las lineas finales.
- `DeliveryAdjustmentApprovalServiceTest` verifica que la aprobacion se
  bloquea cuando `water_distributor` esta inactivo.
- `DeliveryRouteAutoFinisher` ahora mantiene abierta una ruta con entregas
  realizadas si falta validar el cobro; solo acepta `verified`, `paid` o
  `not_collected`. `DeliveryRouteAutoFinisherTest` cubre cobro pendiente y
  no cobrado explícito.
- Una entrega con el mismo producto en varias presentaciones ahora exige
  `order_note_item_id`; se bloquea el fallback ambiguo por `item_id` para no
  mezclar precio, cantidad o puntos.
- Los adicionales, faltantes, sustituciones y devoluciones recalculan el total
  de cobro; la cantidad devuelta ya se descuenta del total.
- El recurso movil expone `final_total` y `adjustment_status`; el APK puede
  mostrar el total confirmado por servidor sin tratar el total original como
  definitivo.
- El modelo y la pantalla del APK ya consumen esos campos; una orden con ajuste
  aprobado muestra el total final y el estado de aprobacion.
- Sigue pendiente validar el payload reconstruido contra el proveedor fiscal en
  staging, incluyendo afectaciones exoneradas/inafectas, cargos, descuentos,
  anticipos y redondeos; tambien falta reconciliar puntos y canjes despues de
  devoluciones, adicionales o sustituciones.
- El estado de Caja y comprobantes permanece parcialmente certificado: las
  reglas de validacion, rechazo, idempotencia y `Ya generado` estan probadas
  localmente, pero falta una emision fiscal completa y su revision en runtime.
- No se hizo commit ni despliegue.
