# SmartPlus — estado consolidado del plan de reparto de agua

Fecha del corte: 2026-10-21 (evidencia posterior al despliegue). Este
documento es la referencia operativa del plan. “Implementado” significa que
existe código; “probado” exige una prueba automatizada o runtime concreta; un
flujo no se declara terminado solo porque pase una prueba unitaria.

## Regla de activación

Toda funcionalidad exclusiva de reparto, bidones, GPS, notificaciones móviles,
pagos del repartidor, puntos y configuración de presentaciones se habilita
solo cuando el tenant tiene `business_turns.value = water_distributor` y
`active = true`.

Cuando el giro está apagado:

- el login y la API del APK responden 403;
- el panel, configuración, puntos y bidones no se exponen;
- los servicios internos y jobs rechazan nuevas operaciones;
- el APK limpia pedidos y cola de reparto al recibir el bloqueo;
- los datos históricos no se borran.

Evidencia: `DeliveryBusinessTurnGuard`, `DeliveryBusinessTurnGateTest`,
`PresentationConfigurationBusinessTurnGateTest`,
`PosPresentationPointMetadataTest`, `ContainerCatalogTest` y
`ContainerMovementServiceTest`.

## Matriz punto por punto

| Punto del plan | Estado | Evidencia actual | Falta para cerrarlo |
|---|---|---|---|
| Resolver empresa por RUC antes del login | Implementado | `DeliveryTenantController`, login del APK | Probar con un segundo tenant real |
| Usuario ligado a sucursal/tenant | Implementado | sesión y conexión tenant | Validar dos sucursales en runtime |
| Marcar usuario como repartidor | Implementado | `delivery_driver`, formulario de usuarios | Validación visual final |
| Restringir menú y backoffice del repartidor | Implementado | `DeliveryDriverWebRestriction`, logout permitido | E2E visual con usuario repartidor |
| Impedir POS, caja, comprobantes y clientes completos | Implementado | middleware y controladores de POS, caja, pagos y documentos | Repetir comprobación contra release activo |
| Crear pedido de reparto | Implementado | `OrderNoteController`; exige giro activo | Crear pedido nuevo controlado en demo |
| Dirección, teléfono, referencia y coordenadas | Implementado | request, ubicaciones por cliente y recursos de entrega | Probar varias direcciones del mismo cliente |
| Maps por tenant | Implementado | `tenant_settings.google_maps_browser_key` | Verificar clave y restricciones de cada tenant |
| Asignar repartidor | Implementado | actualización condicional y reserva de ruta | E2E con pedido nuevo |
| No cambiar repartidor fuera de Pendiente | Implementado y probado local | `DeliveryAssignmentNotificationTest` | Confirmar en runtime con dos usuarios |
| No asignar sin outbox de notificación | Corregido local | asignación devuelve 503 y revierte si falta la tabla | Desplegar y probar migración ausente en entorno aislado |
| Aceptar pedido en APK | Implementado | `DeliveryController`, `delivery_repository.dart` | Aceptar pedido nuevo en dispositivo |
| Inicio automático de ruta | Implementado | aceptación crea/reutiliza ruta y activa tracking | E2E con pedido nuevo |
| Estados de reparto | Implementado | transiciones API y estados terminales | Completar matriz con pedido real |
| Impedir que web inicie “En ruta” | Corregido local | controlador y opciones Vue ya lo bloquean | Verificar asset compilado en release |
| Confirmar entrega solo desde APK | Implementado | endpoint web rechaza `delivered`; API móvil confirma | E2E con coordenadas y sin coordenadas |
| Productos, presentaciones, cantidades y total en APK | Implementado y verificado parcialmente | `DeliveryOrderResource`, pantalla de detalle y contratos Flutter | Pedido nuevo con presentación real |
| Adicionales, faltantes, devoluciones y sustituciones | Implementado | `recordDeliveredItems`, aprobación de diferencias | Probar cantidades y cobro final en E2E |
| Cobro efectivo y vuelto | Implementado | `reportDeliveryPayment`, liquidación de ruta | E2E repartidor → Caja |
| Yape/Plin/transferencia con evidencia | Implementado | referencia, archivo y validación posterior | Probar foto real y revisión de Caja |
| No cobrado/crédito autorizado | Implementado | validaciones y estados específicos | Validar política comercial |
| Evitar doble cobro | Implementado y probado local | bloqueo, estado e idempotencia; prueba concurrente local | Repetir con dos dispositivos contra runtime |
| Caja valida/rechaza cobro | Implementado | endpoints y contabilización protegida | E2E con comprobante existente |
| Cierre automático de ruta | Implementado | `DeliveryRouteAutoFinisher` | Varios pedidos terminales en E2E |
| GPS de jornada y ruta | Implementado en código | servicio Android foreground, sesión de jornada y cola offline | Migración, APK, pantalla bloqueada, red caída y recuperación |
| Jornada laboral del dispositivo corporativo | Implementado en código | sesión independiente de `delivery_routes`, servicio Android foreground y cierre al salir | Migración, APK y prueba física de jornada completa |
| Mapa de todos los motorizados | Implementado en código | endpoint administrativo y mapa Google Maps con marcadores | Desplegar asset, verificar clave Maps y probar varios dispositivos |
| Detener GPS cuando se completa la ruta | Implementado en código | consulta periódica de ruta y auto-finisher | Evidencia física con ruta cerrada |
| FCM mediante outbox/reintentos | Implementado | cuenta privada montada; worker y tabla outbox | Notificación real con APK cerrada/bloqueada |
| Abrir pedido correcto desde notificación | Implementado en código | payload incluye `order_id` y `route_id` | Tocar notificación en dispositivo bloqueado |
| Bidones: catálogo y QR | Implementado | controladores, servicios y permisos | Prueba con cámara física |
| Bidones llenos/vacíos/dañados/prestados/vendidos/perdidos | Implementado | movimientos transaccionales e idempotentes | Validación física de cada estado |
| Carga y liquidación de ruta | Implementado | `RouteContainerSettlementService` | Ruta real con conciliación |
| Depósitos de bidones por cliente | Implementado | `ContainerDepositService` y saldos | Probar cliente con más de una operación |
| Producto maestro y presentaciones | Implementado | `item_unit_types`, resolver y snapshots | Crear presentaciones reales |
| Precio por presentación/lista/sucursal | Implementado | `PresentationPriceResolver` | Validación comercial en tenant |
| Puntos por presentación | Implementado | reglas por presentación y ledger | Configurar reglas reales |
| Canje configurable de 11 puntos | Implementado técnicamente | campañas y recompensa por presentación | Definir recompensa comercial y probar POS |
| No duplicar puntos/canjes | Implementado y probado local | ledger, locks e idempotencia | Prueba concurrente en demo |
| Migraciones reversibles | Implementado y probado con SQLite | cadena de migraciones y tests de reversa | No hacer rollback productivo sin backup |
| Deploy seguro | Implementado | lock, preflight, no-build, healthchecks, release y rollback | Mantener disco bajo umbral y repetir preflight |

## Estado del runtime productivo

- Release activo verificado: `e48c425e9236b4ef6ca20210385595a4bc89d1d9`.
- Symlink verificado: `/var/www/html/smart -> /var/www/html/smart-release-e48c425e`.
- Contenedores SmartPlus saludables: MariaDB, Redis, FPM, worker, scheduler y
  Nginx.
- FCM privado visible para FPM/worker/scheduler; no está dentro del repositorio.
- Migraciones tenant de `website_id=5` aplicadas después de backup identificado.
- Disco observado: 89%; el preflight bloquea si supera 90%.
- Farmacia/Enterfarma no forman parte de este cambio.

## Pruebas ejecutadas

- `php artisan test --no-coverage`: 106 aprobadas.
- `flutter analyze`: sin incidencias.
- `flutter test`: 17 aprobadas.
- `npm run lint`: correcto.
- `git diff --check`: correcto.
- APK release instalada y abierta en el dispositivo `373ead9f`.
- Playwright: login, Maps y creación controlada de `PD01-8` contra demo.
  La asignación a `Repartidor QA Android` continúa devolviendo HTTP 500 y el
  pedido permanece Sin asignar; no se declara FCM probado.

## Pendientes reales de cierre

1. Ejecutar un pedido nuevo controlado: POS → asignación → FCM → APK cerrada y
   pantalla bloqueada → aceptación → GPS → entrega → cobro → Caja.
2. Verificar el mismo flujo con efectivo y con Yape, incluyendo rechazo y
   reintento sin duplicar.
3. Probar cantidades adicionales, sustitución, faltante y aprobación.
4. Probar bidones con QR y liquidación completa.
5. Ejecutar una prueba runtime con `water_distributor` apagado y confirmar que
   web, API, APK, GPS, jobs, puntos y bidones queden bloqueados.
6. Configurar en un tenant de prueba las presentaciones y la campaña real de
   11 puntos.

Hasta cerrar estos seis puntos, el resultado correcto es **implementado y
parcialmente certificado**, no “completamente terminado”.

## Incidencia de entorno pendiente

El código actual valida la existencia y estructura mínima de
`delivery_push_outbox` y devuelve 503 sin mutar el pedido si el tenant no está
alineado. La prueba local cubre tabla ausente. Demo todavía responde HTTP 500,
por lo que se debe revisar su release, migraciones y logs antes de repetir el
flujo móvil.

## Runtime evidence update (2026-10-21)

The previous assignment HTTP 500 was fixed in release `ec40075d` because the
notification job used PHP 8 constructor property promotion while production
FPM runs PHP 7.4. The controlled demo order `PD01-8` now passed assignment
HTTP 200, FCM delivery and notification deep-link, automatic route activation,
foreground GPS recording, delivery confirmation with device coordinates, and
cash collection of S/ 7.00 with S/ 0.00 change. Caja showed the collection as
pending validation and correctly blocked validation because no receipt exists.

Active release: `ec40075d0872949c2a48721659df6e2d1006b953`.
Active symlink: `/var/www/html/smart -> /var/www/html/smart-release-ec40075d`.

Remaining runtime evidence: receipt generation and Caja validation, Yape photo,
locked-screen notification, offline/recovery, rejection/rescheduling, bottle
settlement, points/presentations, and the disabled water-distributor gate.
The older note above stating that demo still returned HTTP 500 is historical and
must not be used as current status; the runtime evidence in this section is the
current result after deployment.

## Corte canónico vigente: 2026-10-21

Esta sección reemplaza cualquier estado anterior del documento que mencione
releases, conteos de pruebas o pedidos anteriores.

- Release activo verificado en servidor: `/var/www/html/smart-release-37b4a253`.
- Disco del servidor: 88% usado y aproximadamente 19 GB libres; los
  despliegues siguen bloqueados si supera 90%.
- Los seis contenedores SmartPlus están saludables: `nginx_smartplus`,
  `fpm_smartplus`, `worker_smartplus`, `scheduling_smartplus`,
  `redis_smartplus` y `mariadb_smartplus`.
- La prueba E2E `PD01-13` completó creación, asignación, notificación FCM,
  apertura desde la notificación, aceptación, inicio automático de ruta, GPS,
  cobro en efectivo y entrega.
- Para un total de S/8.26, el APK recibió S/10.00 y calculó vuelto de S/1.74;
  el backend persistió ambos importes y el vuelto.
- La ruta se cerró automáticamente y la liquidación quedó en Caja como
  `submitted`, con esperado S/8.26, recibido S/8.26 y diferencia S/0.00.
- El repositorio quedó limpio después del commit `91b7cbff`, que documenta la
  evidencia del vuelto y la liquidación.
- Revalidación local posterior: `php artisan test --no-coverage` terminó con
  113 pruebas aprobadas; `flutter test` terminó con 19 pruebas aprobadas y
  `flutter analyze` no reportó incidencias.
- Revalidación posterior a la corrección del guard: `php artisan test
  --no-coverage` terminó con 116 pruebas aprobadas; el incremento corresponde
  a las pruebas de tabla ausente y conexión tenant configurada.
- Código preparado en remoto: `4065a92b` más los commits posteriores de esta
  corrección. El release servido en producción continúa siendo
  `37b4a253`; por tanto, la corrección aún no se presenta como validada en
  runtime productivo.
- Preflight real en `serversoftlte` ejecutado sin mutar Docker: RAM disponible
  3667 MiB, swap usada 50%, disco 88%, sin OOM reciente, sin webpack/npm activo,
  contenedores SmartPlus verificados y Compose montando el symlink activo.

## Deploy vigente posterior: 2026-10-21

- Release activo: `/var/www/html/smart-release-4065a92b`.
- Marcador servido por FPM, worker, scheduler y Nginx:
  `4065a92b9196629e288e38cb50daaf2969da9eca`.
- El endpoint HTTP de producción respondió `200` en `/login`.
- Los seis contenedores reportaron `healthy` después del cambio.
- Se ejecutó con `RUN_MIGRATIONS=0`; no se ejecutaron Composer, NPM, Webpack ni
  `docker compose up --build`.
- El release anterior `/var/www/html/smart-release-37b4a253` permanece
  disponible para rollback.
- Validación posterior al deploy: `demo-smoke.spec.js` pasó 2/2 con Playwright
  y PHP 7.4 dentro de `fpm_smartplus` pasó lint en los dos archivos funcionales.

### Estado actual frente al objetivo

El código de reparto está implementado y condicionado al giro
`water_distributor.active = true`. La certificación funcional principal ya
incluye el flujo real de pedido, notificación, APK, GPS, cobro efectivo,
vuelto, entrega y liquidación. No se debe afirmar aún certificación completa:
siguen pendientes las pruebas que necesitan un segundo dispositivo o una
condición externa real: concurrencia entre repartidores, caída real de FCM,
Yape/Plin con foto y revisión de Caja, QR físico, estados físicos de bidones,
offline con la aplicación cerrada y una prueba runtime en un tenant aislado
con el giro desactivado.

## Corte vigente de centralizacion global y despliegue: 2026-10-21

- Se eliminaron los ultimos usos directos de `BusinessTurn::isWaterDistributor()` en login, usuarios, POS, bidones, QR, items y configuracion de presentaciones. Todos consultan `DeliveryBusinessTurnGuard` con la conexion configurada del tenant y fallan cerrado si falta la tabla o el giro esta inactivo.
- El commit funcional es `31900a3f68cfa5967bef45ef866bef76225306f6`.
- El release activo verificado es `/var/www/html/smart-release-31900a3f` y el release anterior `/var/www/html/smart-release-2e112350` permanece disponible para rollback.
- El preflight paso con disco al 89%, RAM disponible de 3620 MiB, swap usada al 51%, sin OOM reciente y sin procesos Webpack/NPM activos.
- Se desplego con `RUN_MIGRATIONS=0` y `docker compose up -d --no-build`; no se ejecutaron Composer, NPM ni Webpack en produccion.
- Las siete huellas SHA-256 transferidas coincidieron con el checkout local. `vendor` y `public/js/app.js` estaban presentes en el release.
- FPM, worker, scheduler y Nginx sirven el marcador esperado; los seis contenedores SmartPlus quedaron saludables y `/login` respondio HTTP 200.
- La suite PHP termino con `116 passed`; `git diff --check` y el lint de PHP 7.4 en los archivos desplegados pasaron correctamente.

La certificacion E2E global continua parcial por las condiciones externas ya documentadas: segundo dispositivo y concurrencia, caida real de Firebase, Yape/Plin con foto y revision de Caja, QR fisico, offline con la aplicacion cerrada y runtime en un tenant aislado con `water_distributor.active = false`.

## Corte final de compatibilidad y runtime: 2026-10-21

- Se eliminaron tambien las consultas duplicadas del giro en `PointLedgerService`, `PresentationPointIntegrationService` y `PresentationPriceResolver`; ahora todos usan `DeliveryBusinessTurnGuard`.
- Se corrigio compatibilidad con PHP 7.4 en `PresentationPriceResolver` y `ContainerMovementService`, reemplazando `match` por construcciones compatibles con el FPM productivo.
- Commit desplegado: `9e4304fa4a74dbfddda97eb06afcd78cd8ffa015`.
- Release activo: `/var/www/html/smart-release-9e4304fa`; release anterior `/var/www/html/smart-release-82421f77` disponible.
- Lint PHP 7.4 dentro de FPM, worker y scheduler: correcto en los servicios modificados. `/login` respondio HTTP 200.
- Playwright demo: smoke `2/2`; contratos de lectura y seguridad `2/2`.

El estado sigue siendo implementado y parcialmente certificado: la funcionalidad esta protegida por `water_distributor.active = true`, pero las pruebas fisicas y externas listadas arriba no se pueden declarar realizadas sin esos escenarios reales.
## 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.

- PHP: **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 no finaliza rutas con cobros reportados o ausentes;
  exige `verified`, `paid` o `not_collected` para entregas realizadas.
- Si un producto aparece con varias presentaciones, el cierre de entrega
  exige el identificador de la linea original y rechaza la seleccion ambigua.
- La asignacion usa el valor original de `updated_at` en el bloqueo optimista;
  la interfaz sigue enviando la version visible hasta segundos, compatible
  con timestamps MariaDB con microsegundos.
- Playwright reprodujo en `demo.sistemax.net.pe` un `409` al asignar `PD01-16`
  con la version `2026-10-21 14:37:44`; el release servido aun no contiene
  la correccion local, por lo que falta desplegarla y repetir el flujo.
- La migracion de activacion del giro usa la conexion configurada del tenant;
  su alta y reversa estan cubiertas por prueba.
- El cierre de sesion del APK no queda bloqueado por el `403` esperado cuando
  el giro se desactiva; el servicio nativo se detiene localmente y se limpia
  el estado de jornada, mientras los errores de red se conservan como fallo.
- La tarea de limpieza cierra jornadas activas al detectar el giro apagado y
  no modifica ni registra puntos nuevos.

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. No se hizo commit ni deploy en este corte.
