Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SfRSvDd335NPYEbBrDFGE
13 KiB
| title | date | tags | estado | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Cotizador ↔ centro de mando: órdenes de compra, inventario por sitio y tareas | 2026-10-27 |
|
propuesta con 3 decisiones tomadas (27-oct); falta construir |
Cotizador ↔ centro de mando: órdenes de compra, inventario por sitio y tareas
27-oct-2026 · v1.0 · Claude (CTO/CSO) con Leo. Responde al pendiente t-cotizador-integracion: que la
requisición, la orden de compra y la llegada del material sean un solo hilo que termine en el inventario de cada sitio,
con su pendiente en el tablero. Las instrucciones para el proyecto del cotizador están en
Instrucciones para el cotizador.
Resumen
- El cotizador se queda con el dinero y la autorización. Ya tiene requisición → autorización de compras → orden de compra (OC) → pago → «Recibí el material», con roles y montos protegidos. No se duplica en el sitio.
- El centro de mando recibe lo operativo de cada OC, sin montos: folio, proyecto, sitio de entrega (código de 3 letras), proveedor, renglones con cantidad y su ítem del inventario, fechas, paquetería y guía.
- «En camino» es nuevo: cuando el proveedor manda el material, compras o quien pidió lo confirma en el cotizador con la guía. En ese momento el material aparece en camino en la pestaña del sitio del inventario.
- Se recibe en el sitio, en el centro de mando (decisión de Leo, 27-oct): el responsable del sitio da Recibir con lo que llegó de verdad; el cotizador se entera solo y cierra la requisición.
- Cada OC abre su pendiente (pista Compras, su sitio, el responsable del sitio, vence en la fecha de entrega) y se cierra solo cuando se recibe todo.
- Base de datos: SQLite en el servidor del sitio (decisión de Leo, 27-oct), empezando por las órdenes de compra; el inventario pasa después, con una prueba de que los números no cambian.
Decisiones
| # | Decisión | Estado |
|---|---|---|
| D-27-1 | El material de una OC se recibe en el centro de mando (pestaña del sitio, botón Recibir). El cotizador cierra la requisición solo. | Tomada (Leo, 27-oct) |
| D-27-2 | Base de datos SQLite en el servidor del sitio (/srv/projects/drogheda/drogheda.db). PostgreSQL solo si otra aplicación tiene que escribir directo en ella. |
Tomada (Leo, 27-oct) |
| D-27-3 | Código de 3 letras por sitio = el de su aeropuerto y su puerto (UN/LOCODE): VER Veracruz (ICAVE) · ZLO Manzanillo (TIMSA) · ESE Ensenada (EIT) · LZC Lázaro Cárdenas (LCT) · QRO Querétaro. Son los que vienen en guías y documentos de embarque; el sitio ya los acepta. | Propuesta de Claude (Leo pidió códigos de 3 letras) |
| D-27-4 | La autorización de la OC se queda en el cotizador (rol de compras). El centro de mando nunca muestra montos: todo el equipo lo ve. | Propuesta |
| D-27-5 | Solo las OC de proyectos ligados van al centro de mando. Hoy: DROGHEDA = PRY-2026-0002-DRO → proyecto drogheda. |
Propuesta |
Lo que hay hoy
| Cotizador | Centro de mando (drogheda.helgen.online) | |
|---|---|---|
| Dónde vive | Google Apps Script + Sheets + Drive; interfaz en Cloudflare; entra con cuenta @helgen.tech | Servidor de Helgen (Flask + nginx), página de entrada propia desde el 27-oct |
| Proyecto | PRY-2026-0002-DRO «Helgen — DROGHEDA», MXN, sin contrato |
drogheda |
| Requisición | REQ-AAAA-NNNN: Solicitada → Autorizada → Pagada → Cerrada (también Devuelta, Rechazada, Cancelada). Piden sin precios: adriana@, miguel@, paola@, jesus@ (solo requisiciones) y efrain@, tania@, leo@ |
— |
| Orden de compra | OC-AAAA-NNNN: Emitida → Pagada → Recibida, o Cancelada. Sale siempre de una requisición autorizada. Sin recepciones parciales |
— |
| Sitio de entrega | No existe (solo «lugar de entrega» en texto) | icave · timsa · eit · lzc · qro en cada pendiente, movimiento y miembro |
| Material | Renglones de texto libre (descripción, unidad, cantidad) | Ítems con código, existencia por sitio, envíos «en camino» con Recibir (también parcial), kit por instalación |
| Tareas | — | Pendientes con pista, sitio, responsable y vencimiento |
Lo que falta para unirlos: el sitio de entrega en la requisición, el ítem del inventario en cada renglón, el estado «En camino», un canal entre los dos y una forma de cerrar la requisición desde la recepción.
Una restricción que define el diseño: el cotizador puede llamar al centro de mando (Apps Script tiene UrlFetchApp),
pero el centro de mando no puede llamar al cotizador, porque cada llamada al cotizador exige una sesión de Google de
una persona (su regla 10, que no se quita). Por eso: el cotizador avisa (empuja) cada cambio de una OC, y consulta
cada 15 minutos lo que ya se recibió.
El flujo, paso por paso
| Paso | Dónde | Quién | Qué pasa |
|---|---|---|---|
| 1. Pedir | Cotizador | Quien necesita el material | Requisición en DROGHEDA con sitio de entrega (obligatorio en proyectos ligados) y renglones; cada renglón puede llevar su ítem del inventario (lista que el cotizador baja del centro de mando). |
| 2. Autorizar | Cotizador | Compras (Efraín, Tania, Leo) | Emite la OC como hoy. Aviso oc.autorizada → el centro de mando crea la OC (sin montos) y el pendiente «Recibir OC-… en ZLO» para el responsable del sitio, que vence en la fecha de entrega. |
| 3. Pagar | Cotizador | Compras | Como hoy. Aviso oc.pagada (solo el estado). |
| 4. En camino | Cotizador | Compras o quien pidió | «El proveedor ya lo mandó»: paquetería, guía y fecha estimada. Aviso oc.en_camino → en la pestaña del sitio aparece cada renglón en camino (un envío desde «Proveedor o fuera del inventario», con el folio de la OC como referencia). Los renglones sin ítem quedan en Compras → por ligar del centro de mando. |
| 5. Recibir | Centro de mando | Responsable del sitio (Alán VER, Jesús Honorato ZLO, Miguel ESE, Antonio LZC, Jesús Romero QRO) | Recibir con lo que llegó de verdad (puede ser parcial). La OC queda «recibida» o «recibida en parte»; el pendiente se cierra solo cuando llega todo. |
| 6. Cerrar | Cotizador (solo) | — | Cada 15 min el cotizador consulta las recepciones: si llegó todo, cierra la requisición («Recibido en ZLO por jesush en el centro de mando, 30-oct») y la OC queda Recibida. Si llegó una parte, lo anota en las notas de recepción. |
| Cancelar | Cotizador | Compras | Aviso oc.cancelada → el centro de mando cancela los envíos abiertos y el pendiente. |
Datos (SQLite)
Un archivo, /srv/projects/drogheda/drogheda.db, en modo WAL (aguanta a los dos procesos del servicio). Se respalda
con el resto de /srv/projects/drogheda.
CREATE TABLE sitios (codigo TEXT PRIMARY KEY, -- VER ZLO ESE LZC QRO
id_portal TEXT UNIQUE NOT NULL, -- icave timsa eit lzc qro
nombre TEXT NOT NULL, responsable TEXT);
CREATE TABLE proyectos (id TEXT PRIMARY KEY, -- drogheda
pry_folio TEXT UNIQUE NOT NULL, -- PRY-2026-0002
clave TEXT, nombre TEXT);
CREATE TABLE ordenes (folio TEXT PRIMARY KEY, -- OC-2026-0012
req_folio TEXT, proyecto_id TEXT REFERENCES proyectos(id),
sitio TEXT REFERENCES sitios(codigo), proveedor TEXT,
estado TEXT CHECK (estado IN ('autorizada','pagada','en-camino','recibida-parcial','recibida','cancelada')),
fecha_entrega TEXT, paqueteria TEXT, guia TEXT, en_camino_en TEXT,
solicitante TEXT, autorizo TEXT, tarea_id TEXT, prueba INTEGER DEFAULT 0,
creado_en TEXT, actualizado_en TEXT);
CREATE TABLE renglones (oc_folio TEXT REFERENCES ordenes(folio), n INTEGER,
descripcion TEXT, unidad TEXT, cantidad REAL,
item_codigo TEXT, -- ítem del inventario (NULL = por ligar)
envio_id TEXT, -- el envío «en camino» que se creó en el inventario
recibido REAL DEFAULT 0, PRIMARY KEY (oc_folio, n));
CREATE TABLE eventos (evento_id TEXT PRIMARY KEY, -- idempotencia: el mismo aviso dos veces no duplica
oc_folio TEXT, tipo TEXT, cuando TEXT, quien TEXT,
origen TEXT CHECK (origen IN ('cotizador','portal')), datos TEXT);
Fases. 1) Órdenes de compra en SQLite; el inventario sigue en sus JSON y la OC crea sus envíos con la API de hoy. 2) El inventario (ítems y movimientos) pasa a SQLite con una comparación de existencias por sitio antes y después. 3) Activos: una fila por unidad y por pieza con serie (HBOX, tarjetas, módulos) con su sitio, estado, vehículo y actas — lo que hoy se arma leyendo las actas.
Cómo se hablan (API del centro de mando para el cotizador)
Seguridad. Las rutas /api/integracion/… no usan la sesión de una persona: cada llamada va firmada con un secreto
compartido (HMAC-SHA256 del cuerpo y la hora). El secreto vive en las propiedades del script del cotizador
(PORTAL_SECRETO) y en el servicio del servidor (COTIZADOR_SECRETO); nunca en el código ni en un chat. Se rechaza
una firma de hace más de 5 minutos.
POST /api/integracion/cotizador/evento
Encabezados: X-Hbox-Tiempo: 2026-10-28T10:15:00-06:00
X-Hbox-Firma: sha256=<hex de HMAC(secreto, tiempo + "\n" + cuerpo)>
Cuerpo:
{ "evento_id": "OC-2026-0012:oc.en_camino:1",
"tipo": "oc.en_camino", // oc.autorizada · oc.pagada · oc.en_camino · oc.cancelada · req.cerrada
"cuando": "2026-10-28T10:15:00-06:00", "quien": "tania@helgen.tech", "prueba": false,
"oc": { "folio": "OC-2026-0012", "req_folio": "REQ-2026-0031",
"proyecto_folio": "PRY-2026-0002", "proyecto_clave": "DRO", "sitio": "ZLO",
"proveedor": "Ferretería del Pacífico", "fecha_entrega": "2026-10-30",
"paqueteria": "Estafeta", "guia": "123456789", "solicitante": "jesus@helgen.tech",
"renglones": [ { "n": 1, "descripcion": "Soporte para RTG", "unidad": "pza", "cantidad": 4, "item": "SOP-RTG" } ] } }
Respuesta: { "ok": true, "estado_portal": "en-camino", "tarea_id": "t-oc-2026-0012", "por_ligar": [] }
(el mismo evento_id otra vez → { "ok": true, "repetido": true })
GET /api/integracion/inventario/items → [{codigo, descripcion, unidad, categoria}] (para el selector)
GET /api/integracion/cotizador/recepciones?desde=… → [{oc_folio, req_folio, sitio, completa, recibio, cuando,
renglones: [{n, pedido, recibido}]}]
"prueba": true (rondas del proyecto de PRUEBAS del cotizador) se guarda aparte y no toca el inventario ni el tablero.
Qué se construye y quién
| Lado | Qué | Quién |
|---|---|---|
| Centro de mando | backend/compras.py (SQLite, las 3 rutas firmadas, envíos en camino, pendiente por OC, cierre al recibir); página Compras (OC por sitio y estado, renglones por ligar, liga desde el envío en camino del inventario); ruta en nginx sin sesión para /api/integracion/ (la firma la revisa el backend); pruebas |
Claude (fase 1), Leo despliega |
| Cotizador | Sitio de entrega y su regla; ítem por renglón; estado «En camino»; avisos firmados con reintento; consulta de recepciones cada 15 min y cierre automático; pruebas en Node y en PRUEBAS | El proyecto del cotizador (Claude con Efraín) — ver instrucciones |
| Los dos | El secreto compartido (Leo lo genera y lo pone en los dos lados) y una prueba de punta a punta con una OC de PRUEBA | Leo |
Riesgos y cómo se cubren
- El centro de mando no contesta cuando el cotizador avisa → el cotizador guarda el aviso en una pestaña de
pendientes y lo reintenta; el
evento_idevita duplicados. - Una OC llega antes que su proyecto o su sitio → se rechaza con un error claro (sitio inválido, proyecto no ligado) y el cotizador lo muestra a compras.
- Doble captura durante la transición → las OC de antes no se migran; solo las nuevas de proyectos ligados.
- Fuga del secreto → se cambia en los dos lados; las firmas viejas dejan de valer al momento.
- Montos → el centro de mando no los recibe; si un aviso los trae, se descartan antes de guardar.
