# SmartPlus - plan consolidado vigente de reparto de agua

Los conteos y releases histÃ³ricos de este documento no sustituyen el bloque
**Corte local vigente de auditoria** que aparece al final; ese bloque describe
el estado actual del Ã¡rbol local.

Fecha de auditoría: 2026-10-21

Este documento es la fuente de verdad vigente. Los cortes históricos de otros
documentos se conservan como evidencia, pero no deben usarse para describir el
estado actual.

## Regla global de activación

La funcionalidad exclusiva del reparto se habilita únicamente en la conexión
del tenant cuando existe:

```text
business_turns.value = water_distributor
business_turns.active = true
```

El `DeliveryBusinessTurnGuard` es la única fuente de esta decisión. Si falta
la tabla, el giro está inactivo o la consulta no puede confirmarlo, el sistema
falla cerrado. Los datos históricos no se eliminan.

## Matriz vigente

| Punto | Estado | Evidencia | Pendiente |
|---|---|---|---|
| Tenant/RUC y sucursal | Implementado y probado localmente | `DeliveryTenantController`, login en dos pasos y establecimiento fijo | Segundo tenant/sucursal físico |
| Usuario repartidor | Implementado y probado | `delivery_driver`, formulario, `UserController`, rechazo con giro inactivo | Revisión visual final en otro tenant |
| Restricción del backoffice | Implementado y probado | `DeliveryDriverWebRestriction`, middleware, POS, caja, pagos y comprobantes bloqueados | Matriz visual completa con usuario real |
| Pedido de reparto | Implementado y probado | `OrderNoteController`, modo condicionado al guard | Solo nuevos escenarios comerciales |
| Dirección y contacto | Implementado y probado parcialmente | `person_addresses`, teléfono, referencia, latitud/longitud y varias direcciones | Varias direcciones físicas del mismo cliente |
| Google Maps por tenant | Implementado | `tenant_settings.google_maps_browser_key` | Verificar restricciones de cada cuenta Maps |
| Asignación | Implementado y probado en runtime | Asignación condicional, bloqueo de fila, versión renderizada, outbox y notificación; Playwright produjo `200` y `409` con la misma versión | Repetir solo si se cambia el contrato de asignación |
| Bloqueo de cambio de repartidor | Implementado y probado en runtime | `DeliveryAssignmentNotificationTest`; conflicto obsoleto devuelve HTTP 409 y solo se permite mientras está pendiente | Repetir solo si se cambia el contrato de estados |
| Outbox de notificación | Implementado y probado | Falla antes de mutar si faltan tabla/columnas | Tenant aislado sin migración |
| FCM | Implementado en código y probado con mocks/localmente | Worker, cuenta privada y reintentos | APK cerrada/bloqueada, notificación real y caída real de Firebase |
| Detalle del pedido móvil | Implementado y probado | Producto, presentación, cantidad, total, dirección y teléfono | Presentación no legacy en una orden nueva |
| Aceptar pedido | Implementado y probado localmente | API móvil cambia a `en_route` | Aceptación física, rechazo/reprogramación desde APK |
| Inicio automático de ruta | Implementado y probado localmente | Aceptación crea o reutiliza ruta activa | Ruta física completa con varios pedidos |
| Estados de reparto | Implementado y probado parcialmente | `pending`, `en_route`, `delivered`, `not_delivered`, `rescheduled`, `rejected`, `cancelled` | Confirmación visual APK de rechazado/cancelado |
| GPS activo | Implementado y probado parcialmente | Servicio Android foreground, persistencia y cola offline | Pantalla bloqueada y recuperación completa |
| Última ubicación para Caja | Implementado | API de rutas activas y `latestLocation` | E2E visual durante una ruta real |
| Jornada laboral y mapa de motorizados | Implementado en código, pendiente de despliegue/prueba | `delivery_tracking_sessions`, API `/api/delivery/tracking/*`, servicio Android foreground y mapa web condicionado al giro | Migrar tenants, instalar APK nueva y probar pantalla bloqueada, red caída y cierre de jornada |
| Entrega con GPS | Implementado y probado | Endpoint móvil y coordenadas persistidas | Confirmación sin permiso de ubicación |
| Entrega sin GPS | Implementado | Confirmación alternativa sin exigir coordenadas | Prueba física en navegador/dispositivo |
| Adicionales y faltantes | Implementado | Ajustes, aprobación y snapshots de cantidades | E2E con diferencia real de cantidades |
| Sustituciones | Implementado | Contrato de líneas ajustadas y aprobación | Escenario comercial real |
| Cobro efectivo | Implementado y probado | Monto esperado, recibido, vuelto 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 | Validaciones y estados específicos | Confirmar política comercial |
| Doble cobro | Implementado y probado localmente | Locks, UUID e idempotencia | Concurrencia real entre dos dispositivos |
| Caja | Implementado y probado parcialmente | Validación/rechazo y bloqueo sin comprobante | Comprobante real y revisión completa |
| Comprobante duplicado | Implementado | `can_generate` y `Ya generado` | Verificación adicional sobre boleta nueva |
| Cierre automático | Implementado y probado localmente | `DeliveryRouteAutoFinisher` para estados terminales | Ruta real con liquidación |
| Catálogo de bidones | Implementado y probado localmente | Controladores, permisos y guard | Prueba física |
| QR de bidones | Implementado | Asociación idempotente y validación de tipo/estado | Cámara y QR físico |
| Estados físicos | Implementado | Lleno, vacío, dañado, reparación, prestado, vendido y perdido | Validación física de todos los estados |
| Carga de ruta | Implementado | `RouteContainerSettlementService` | Ruta real conciliada |
| Liquidación de bidones | Implementado | Movimientos transaccionales e idempotentes | Prueba física completa |
| Saldo por cliente | Implementado | `ContainerDepositService` y movimientos | Cliente con varios intercambios |
| Presentaciones | Implementado y probado localmente | `item_unit_types`, snapshots y fallback legacy | Presentación real en POS |
| Precio por presentación | Implementado y probado localmente | `PresentationPriceResolver`, lista, sucursal y canal | Validación comercial por tenant |
| Puntos por presentación | Implementado y probado localmente | Reglas y ledger idempotente | Regla real en POS |
| Canje de 11 puntos | Implementado técnicamente | Campaña y recompensa por presentación | Definir producto/recompensa final |
| No duplicar puntos/canjes | Implementado y probado localmente | Locks, ledger y reversa | Concurrencia en demo |
| Migraciones | Implementado y probado | Cadena ordenada, reversa y tests SQLite | Staging antes de rollback productivo |
| Gate transversal | Implementado y probado | Guard en controladores, servicios, jobs, middleware y APK | Runtime en tenant aislado apagado |
| Compatibilidad PHP productiva | Corregido y probado | Lint PHP 7.4 en los 12 archivos del alcance; sin `match` | Mantener lint en cada release |
| Deploy seguro | Implementado y probado | `flock`, preflight, no-build, healthchecks, marker y rollback | Mantener disco debajo de 90% |

## Evidencia automatizada actual

- PHP: `php artisan test --no-coverage` - **124 aprobadas**.
- Flutter: `flutter analyze` sin incidencias y **19 pruebas aprobadas**.
- Playwright smoke demo: **2/2 aprobadas**.
- Playwright lectura/seguridad: **2/2 aprobadas**.
- `git diff --check`: aprobado.
- Lint PHP 7.4 dentro de FPM para los 12 archivos modificados: aprobado.
- No existen llamadas a `BusinessTurn::isWaterDistributor()` fuera del guard.
- No existe sintaxis `match` en el alcance PHP 7.4 del reparto.

## Evidencia productiva actual

- Commit servido: `09eaf448aa246271790ddc8512bf7e0e9e9382fc`.
- Release: `/var/www/html/smart-release-09eaf448`.
- Rollback: se conservan releases anteriores, incluido `/var/www/html/smart-release-9e4304fa`.
- FPM, worker, scheduler y Nginx sirven el mismo marker.
- `mariadb_smartplus`, `redis_smartplus`, `fpm_smartplus`, `worker_smartplus`,
  `scheduling_smartplus` y `nginx_smartplus` están saludables.
- `/login` responde HTTP 200.
- Deploy realizado con `RUN_MIGRATIONS=0` y `docker compose up -d --no-build`.
- No se ejecutaron Composer, NPM ni Webpack en producción.
- Farmacia y Enterfarma no fueron modificados.

## Pendientes que no deben simularse

La implementación de código está completa dentro del alcance, pero la
certificación E2E total requiere todavía:

1. Caída real de Firebase.
2. Foto real de Yape/Plin y revisión en Caja.
3. QR físico y estados físicos de bidones.
4. Offline con la APK completamente cerrada.
5. Tenant aislado con `water_distributor.active = false`, incluyendo el corte
   del servicio GPS ante HTTP 403.

La concurrencia de asignación ya fue probada contra el release activo con dos
solicitudes simultáneas y una respuesta `200` junto con una respuesta `409`.

## Actualización autoritativa posterior al deploy: 2026-10-21

Esta sección prevalece sobre los valores históricos anteriores del documento.

- La rama local y remota quedaron alineadas en `efced366`; el cambio fue
  exclusivamente documental y no requirió otro deploy.
- Producción usa `/var/www/html/smart` apuntando a
  `/var/www/html/smart-release-6717aa3` y el marker esperado es
  `6717aa3af5d074e55e383ffd0a353796070ca1ea`.
- El preflight y el deploy se ejecutaron sin Composer, NPM, Webpack ni
  `--build`; el disco quedó en 89% y se mantienen releases para rollback.
- Los seis contenedores SmartPlus reales están saludables y los endpoints de
  login de producción y demo respondieron HTTP 200.
- Playwright de lectura y seguridad pasó `2/2`; PHP pasó `119` pruebas y
  el gate del giro inactivo está cubierto por pruebas de login, API, web,
  jobs, GPS, puntos y bidones.
- El flujo de activación sigue siendo único: solo se habilita si el tenant
  tiene `business_turns.value = water_distributor` y `active = true`;
  ante ausencia de tabla, giro o confirmación, se bloquea.
- Las rutas públicas de impresión y descarga ahora pasan por
  `delivery.driver.web`: los invitados y usuarios normales conservan los
  enlaces públicos, pero un repartidor no puede abrir comprobantes,
  documentos o notas de otros clientes usando un identificador conocido.
- Los endpoints heredados de reportes de caja, inventario, personas y QZ
  también aplican `delivery.driver.api` cuando el request trae un usuario
  repartidor, sin cambiar el acceso de clientes públicos o usuarios normales.

Hasta contar con esas condiciones, el estado correcto es **implementado y
parcialmente certificado**, no completamente certificado.

## Corte operativo mas reciente: 2026-10-21

- Produccion sirve el release `09eaf448aa246271790ddc8512bf7e0e9e9382fc`.
- El symlink efectivo es `/var/www/html/smart -> /var/www/html/smart-release-09eaf448`.
- La suite PHP posterior al cambio tiene 123 pruebas aprobadas y Playwright
  paso 2/2.
- El repartidor autenticado recibe HTTP 403 al intentar acceder a impresion
  publica o endpoints heredados bloqueados; la cache de rutas fue limpiada en
  FPM despues del cambio.
- Los seis contenedores SmartPlus estan saludables, el disco esta en 89% y no
  se ejecutaron migraciones, Composer, NPM ni Webpack.

## Seguimiento continuo solicitado por la empresa

El panel actual muestra la ultima ubicacion de las rutas activas y permite
abrirla en Google Maps; aun no es un mapa embebido con todos los motorizados.
Para celulares corporativos, el plan debe contemplar dos estados separados:

1. **Jornada laboral:** rastreo automatico desde el inicio de turno hasta el
   cierre de jornada, aunque no exista una ruta activa.
2. **Fuera de jornada:** rastreo solo si la empresa lo define expresamente para
   un equipo corporativo administrado, con aviso visible, politica informada y
   registro de quien consulta la ubicacion.

 El mapa debe mostrar un marcador por motorizado, ultima hora recibida, estado
`online`, `stale` u `offline`, bateria, señal, ruta/pedidos y un historial con
retencion configurable. Todo este bloque sigue condicionado a
`water_distributor.active = true` y es una ampliacion pendiente del mapa actual.

### Decisión operativa sobre la caja

La apertura de caja no será el disparador de ubicación: puede pertenecer a otro
usuario, permanecer abierta por error o no existir para el motorizado. La caja
se utilizará como información complementaria para conciliación; el disparador
principal es la jornada del repartidor. La aplicación crea o recupera una
jornada al iniciar sesión, la mantiene mientras el dispositivo corporativo
está en servicio y la cierra al cerrar sesión o mediante una acción
administrativa. Si el giro `water_distributor` se desactiva, la API devuelve
`403` y el servicio Android detiene el rastreo.

## Corte local vigente de auditoria: 2026-10-21

Este corte prevalece sobre conteos y releases historicos. La implementacion
local esta condicionada por `DeliveryBusinessTurnGuard` y solo opera cuando
`business_turns.value = water_distributor` y `active = true`. Si falta la
tabla, la conexion o la confirmacion, falla cerrado.

- Suite PHP local: **149 pruebas aprobadas**.
- Flutter: analisis limpio y **20 pruebas aprobadas**.
- Ajustes fiscales: gravados, exonerados e inafectos probados; afectaciones
  desconocidas bloqueadas.
- Duplicidad de lineas y aprobacion con giro inactivo probadas.
- El cierre automatico de ruta espera que los cobros de entregas esten
  validados, pagados o explicitamente marcados como no cobrados.
- Las presentaciones repetidas del mismo producto requieren identificar la
  linea original para evitar aplicar precio o puntos a la linea equivocada.
- La asignacion valida la version visible hasta segundos, pero compara el
  valor original de `updated_at` para tolerar microsegundos de MariaDB; esto
  evita rechazar pedidos nuevos como obsoletos.
- Playwright reprodujo en `demo.sistemax.net.pe` un `409` al asignar `PD01-16`
  con la version `2026-10-21 14:37:44`; el demo aun sirve el release anterior.
  La correccion local esta probada, pero el punto no esta cerrado en runtime
  hasta desplegarla y repetir la asignacion.
- La migracion que registra `water_distributor` usa la conexion configurada
  del tenant y tiene prueba de subida y reversa sin depender del nombre fijo
  `tenant`.
- Si el giro se desactiva mientras el repartidor cierra sesion, el APK acepta
  el `403` del gate como cierre local, detiene el servicio nativo y elimina el
  estado local de jornada; los errores de red siguen bloqueando el cierre.
- El comando programado cierra las jornadas activas cuando el giro se apaga,
  sin aceptar ni registrar nuevos puntos GPS.

La certificacion runtime completa sigue pendiente para FCM real con APK
cerrada/bloqueada, dos dispositivos, camara Yape/Plin y Caja, QR fisico,
migracion real por tenant, prueba con giro apagado y fiscalizacion completa en
staging. Este corte no hizo commit ni deploy.
