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-2 → S1424855): 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 sí 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.