Skip to content

Gotchas: Dónde Vive La Lógica No Obvia

Las trampas que cuestan horas porque no están donde uno las busca. Si vas a depurar o explicar algo de estos temas, lee esto primero.

Lógica Que Vive En Zoho (no en el repo Node)

Mucha lógica de negocio no está en el código TypeScript sino en functions Deluge de Zoho Creator, que se editan en su consola web y no se deployan desde este repo.

  • Stripping del sufijo -N de sigmas escaneados (ej. S1424855-2S1424855): el cliente (BarcodeScanScreen.tsx, CheckpointsScreen.tsx) y el backend Node pasan el código sin tocar. Quien quita el -N es Zoho, en validateCheckPoint / generateCheckPoint. Si reaparece "no se puede asociar tarea con guión" o "el checkpoint no crea la tarea con sufijo", mira primero la function de Zoho, no la normalización en Node.
    • ⚠️ No agregues un replace(/-\d+$/, "") en normalizeScanId del backend: duplicaría lógica que ya vive en Zoho y rompería casos donde el sufijo debe preservarse.
  • Schedules y notificaciones, intake de PARED/SIGMA, SCAN, caja/arqueo: viven como forms, functions y schedules de Zoho. El repo apps/zoho-app es un espejo, no la fuente de deploy.

Regla mental: si una transformación "debería" estar en el backend y no la encuentras, asume que está en Zoho antes de concluir que falta.

El .env Local Apaga Cosas A Propósito (y no refleja prod)

El .env.example trae defaults pensados para desarrollo local, no los valores de producción. En particular, varios comportamientos pesados o que "regeneran" cosas vienen apagados a propósito para que probar en local no dispare trabajo de fondo.

  • ROUTE_ENGINE_SESSIONS_ENABLED=false en local es intencional. Evita que la optimización de ruta se re-dispare/regenere cada vez que cambia el set de tareas mientras desarrollas. En producción está true y es el path que gobierna el orden de ruta (ver orden de ruta).
  • El engine sigue el mismo criterio: en su .env.example vienen en false por default ROS_ASYNC_OPTIMIZE_ENABLED, ROS_ETA_SYNC_ENABLED, ROS_AUTH_ENABLED, ROS_RATE_LIMIT_ENABLED — trabajo de fondo y controles que no quieres activos mientras pruebas en local.

Consecuencia para el dev: el valor productivo de estos flags vive solo en Railway/Cloudflare. No infieras el comportamiento de prod desde el .env local — verifica el flag en Railway antes de concluir "esto está apagado". Y si necesitas reproducir prod en local (p. ej. ver la reoptimización de ruta), actívalo a mano en tu .env.

El Orden De Ruta Lo Decide La Sesión, No El GPS

El engine de rutas (apps/engine-backend, VRPTW/OR-Tools) se invoca desde drivers-backend por dos caminos que escriben el mismo tasks.numeroEntregaRuta:

  1. engineSession / applyTaskAssignment — es el que gobierna el orden en producción. Optimiza desde un depot FIJO hardcodeado (8.9824,-79.5199, zona Albrook/Calidonia — no el GPS del conductor), con shift_start: "06:00" hardcodeado, minimizando el lazo completo (no "lo más cercano primero"), sin ventanas y con todas las tareas como critical.
  2. Legacy /api/route/optimize sí usa GPS + hora real + ventanas, pero es código muerto en la app desplegada (no lo llama ninguna pantalla). Riesgo latente, no compite hoy.

Consecuencias al depurar:

  • "¿Por qué lo mandó lejos teniendo algo cerca?" → porque el orden se calcula desde el depot fijo y minimiza el recorrido total; una parada cercana al conductor puede ser óptima como #4.
  • "¿Por qué los ETA salen ~6:57/7:00 AM?" → por el shift_start 06:00 hardcodeado + re-sync que no re-ancla el inicio. No es zona horaria.
  • El reoptimize de sesión se dispara por eventos de tarea (assigned/completed/rescheduled) y un cron de reconciliación cada 2 min solo ante drift del set de tareas. El GPS nunca reordena.

Base De Datos Compartida

  • core-backend y drivers-backend usan la MISMA Postgres (la "Main Dashboard DB"), ambos por DATABASE_URL. No hay DB separada de drivers en runtime.
  • El schema lo posee core (packages/core-shared/src/schema.ts); drivers tiene un espejo (shared/schema.ts). core-backend ... check corre check-schema-mirror.ts para cazar drift.
  • db:push está deshabilitado en drivers a propósito. Aplica el schema solo desde core-backend.
  • MAIN_DB_URL/DRIVERS_DB_URL (en el .env.example de drivers) son solo del script históricoscripts/migrate-to-main-db.ts, no de runtime.
  • Migraciones = deuda técnica, por dos carriles (db:generate/db:migrate están en desuso). Lo aditivo (columnas/índices) se aplica solo en cada deploy vía una lista idempotente en core-backend/server/routes.ts (auto-migrator). Lo no-aditivo va como SQL fechado a mano en migrations/manual/, aplicado a prod con railway run -- psql … -f. Procedimiento completo en Setup → Migraciones.

Pendiente recomendado: una DB de dev

Hoy el desarrollo local apunta a producción (con cuidado de no escribir), lo cual es frágil. Una opción razonable: una Postgres de dev (efímera o con dump anonimizado de prod) a la que apunten ambos backends vía DATABASE_URL, y aplicar el schema desde core-backend (db:push). Como comparten DB, un solo DATABASE_URL de dev sirve para los dos.

Dos "dashboards" De dash-web

core-backend puede bundlear dash-web al construir el server (queda embebido en el servicio de Railway), pero el dashboard de producción que usan los operadores es el Cloudflare Worker (frontend-dash-rnb-logistics-prod), que se deploya manualmente.

No asumas que "el dash de prod es el que embebe core-backend": si mergeas un cambio de dash-web y no corres deploy:prod, el frontend que ven los usuarios no cambia, aunque el backend ya se haya redeployado. Es el olvido clásico de cierre de ticket.

El Mapa De drivers-app Es Server-Rendered (y sin telemetría)

El mapa de la app de conductores no es client-side: GoogleMapsWebView.tsx hace POST /api/map al drivers-backend (Railway), que devuelve HTML con Google Maps + markers renderizado en un WebView.

  • La pantalla "Mapa no disponible" se dispara solo cuando ese POST falla (red caída, respuesta no-2xx, cold start/502 de Railway, 520/521 de Cloudflare, o GOOGLE_MAPS_KEY_DRIVER_APP vacío → 500).
  • Punto ciego: el catch solo hace console.error; no llama a Sentry.captureException ni reintenta. Por eso no verás excepciones de mapa caído en Sentry — el único rastro es si el conductor lo reporta a mano. Distinto del flood de missing coords y de "No hay tareas para hoy".

Worktrees Sin node_modules

Los .claude/worktrees/* no tienen node_modules. Correr pnpm install ahí satura la máquina y rompe el shell. Desarrolla en el worktree, prueba en el checkout principal. Detalle y limpieza post-merge en Flujo de trabajo.

Resumen De "Mira Primero Aquí"

SíntomaMira primero
Sigma con -N no asocia / checkpoint no crea tareaFunction de Zoho (no Node)
Un flag local está apagado / difiere de prodDefault de dev a propósito; valor real en Railway/Cloudflare
Orden de ruta "raro" / ETA ~7 AMPath de sesión del engine (depot + shift_start fijos)
Cambié schema y drivers no ve el cambioSchema se aplica desde core; revisa mirror/drift
Voy a cambiar una tabla / el schemaAditivo → auto-migrator en routes.ts; no-aditivo → migrations/manual/ + railway run psql (Setup → Migraciones)
Cambié dash-web y prod no cambióFaltó el deploy:prod a Cloudflare
"Mapa no disponible" sin nada en SentryPOST /api/map falló; no hay telemetría
Schema/lógica que "falta" en el backendProbablemente vive en Zoho

Borrador vivo basado en codigo. Validar con operaciones antes de convertir en procedimiento oficial.