hbox-docs: entrada, incidencias, standup y agentes (27-oct); propuesta del cotizador
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013SfRSvDd335NPYEbBrDFGE
This commit is contained in:
parent
1b940e8ff5
commit
e1d04d5901
@ -0,0 +1,150 @@
|
|||||||
|
---
|
||||||
|
title: "Instrucciones para el proyecto del cotizador: ligar órdenes de compra al centro de mando"
|
||||||
|
date: 2026-10-27
|
||||||
|
tags: [project-drogheda, cotizador, compras, inventario, integracion, instrucciones]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Instrucciones para el proyecto del cotizador
|
||||||
|
|
||||||
|
**27-oct-2026 · v1.0.** Para la sesión de Claude que trabaja en `cotizador-helgen` (Apps Script + Sheets + Cloudflare).
|
||||||
|
Diseño completo y decisiones: [Cotizador ↔ centro de mando](integracion-cotizador.md). Aquí está **qué cambiar en el
|
||||||
|
cotizador**, en orden, con sus pruebas. Todo respeta las reglas del `CLAUDE.md` del cotizador (§7): totales en el servidor,
|
||||||
|
datos de compras solo en su libro, `CAMBIOS_API`, manual actualizado en el mismo turno, pruebas en PRUEBAS antes del real.
|
||||||
|
|
||||||
|
## Para pegar en el proyecto del cotizador
|
||||||
|
|
||||||
|
> Lee `CLAUDE.md` completo y después este documento (`2026-10-27_Drogheda_Instrucciones-Cotizador-Integracion_ES_v1.0.md`).
|
||||||
|
> Vamos a ligar las órdenes de compra de los proyectos ligados (hoy DROGHEDA, `PRY-2026-0002`) con el centro de mando
|
||||||
|
> drogheda.helgen.online, siguiendo el contrato de la sección «Contrato». Trabaja por pasos (1 a 7), con respaldos antes
|
||||||
|
> de tocar hojas, pruebas en Node y una ronda en PRUEBAS con `"prueba": true` antes de publicar. No mandes montos al centro
|
||||||
|
> de mando. No pongas el secreto en el código ni en el chat: va en las propiedades del script (`PORTAL_SECRETO`) y me lo
|
||||||
|
> pides a mí cuando haga falta. Al terminar, actualiza `CLAUDE.md` (§4.5.3, §4.5.4, §5, §6.12, §6.13 y una §6.15 nueva)
|
||||||
|
> y el Manual del Cotizador.
|
||||||
|
|
||||||
|
## Lo que decidió Leo (27-oct)
|
||||||
|
|
||||||
|
- El material se **recibe en el centro de mando** (el responsable del sitio da «Recibir»). El cotizador **cierra solo**
|
||||||
|
la requisición cuando el centro de mando dice que llegó todo. «Recibí el material» en el cotizador se queda como respaldo
|
||||||
|
para proyectos no ligados y para cuando el centro de mando no esté disponible.
|
||||||
|
- La autorización, los montos, los pagos y los proveedores **se quedan en el cotizador**.
|
||||||
|
- Cada OC de un proyecto ligado lleva un **sitio de entrega** de 3 letras: `VER` Veracruz (ICAVE) · `ZLO` Manzanillo
|
||||||
|
(TIMSA) · `ESE` Ensenada (EIT) · `LZC` Lázaro Cárdenas (LCT) · `QRO` Querétaro.
|
||||||
|
|
||||||
|
## Paso 1 · Propiedades del script
|
||||||
|
|
||||||
|
| Propiedad | Valor | Nota |
|
||||||
|
|---|---|---|
|
||||||
|
| `PORTAL_URL` | `https://drogheda.helgen.online` | |
|
||||||
|
| `PORTAL_SECRETO` | (lo genera Leo; el mismo que `COTIZADOR_SECRETO` en el servidor) | Nunca en el código, en `CLAUDE.md` ni en el chat |
|
||||||
|
| `PORTAL_PROYECTOS` | `{"PRY-2026-0002": "drogheda"}` | Llave interna del proyecto → proyecto del centro de mando |
|
||||||
|
| `PORTAL_SITIOS` | `VER,ZLO,ESE,LZC,QRO` | Con sus nombres en la interfaz (Veracruz · ICAVE, Manzanillo · TIMSA, …) |
|
||||||
|
|
||||||
|
Agregar `script.external_request` (ya existe por la IA de claves SAT si está activa) y `script.scriptapp` (para el
|
||||||
|
activador del paso 6) a `appsscript.json`: el dueño (efrain@) tendrá que volver a autorizar.
|
||||||
|
|
||||||
|
## Paso 2 · Sitio de entrega e ítem del inventario en la requisición
|
||||||
|
|
||||||
|
- `Requisiciones`: columna nueva **`sitio_entrega`** (al final, como se agregó `req_folio`; las filas viejas quedan vacías).
|
||||||
|
Obligatoria cuando el proyecto está en `PORTAL_PROYECTOS`; el servidor lo revisa en `guardarRequisicion` (no solo la
|
||||||
|
interfaz). En la interfaz: un selector con los 5 sitios, junto al proyecto.
|
||||||
|
- `RequisicionesRenglones`: columna nueva **`inventario_item`** (código del ítem, opcional). En la interfaz, cada renglón
|
||||||
|
de un proyecto ligado tiene un buscador con los ítems del centro de mando (`GET /api/integracion/inventario/items`,
|
||||||
|
guardado en `CacheService` 6 h). Se puede dejar vacío: el centro de mando lo liga después.
|
||||||
|
- `OrdenesCompra`: columnas nuevas **`sitio_entrega`** (se copia de la requisición al autorizar), **`paqueteria`**,
|
||||||
|
**`guia`**, **`fecha_estimada`**, **`en_camino_en`**, **`en_camino_por`**. Los renglones de la OC guardan el
|
||||||
|
`inventario_item` de su renglón de requisición (si compras divide o junta renglones, conserva el ítem).
|
||||||
|
|
||||||
|
## Paso 3 · Estado «En camino»
|
||||||
|
|
||||||
|
- Nueva función *(UI)* **`marcarEnCamino(folio, {paqueteria, guia, fechaEstimada})`**: la puede usar **compras** o **el
|
||||||
|
solicitante de esa requisición**. Solo desde Autorizada/Emitida o Pagada; guía obligatoria salvo «lo trae el proveedor»;
|
||||||
|
fecha estimada no anterior a hoy. Con candado e idempotencia (`ENC_<idSolicitud>`), como las demás.
|
||||||
|
- Requisición y OC pasan a **«En camino»** (estado nuevo entre Pagada y Recibida; también puede venir de Autorizada si se
|
||||||
|
paga después). El solicitante lo ve en su historial con la fecha, sin montos.
|
||||||
|
- Una OC «En camino» ya no se cancela desde el cotizador sin motivo explícito (avisa al centro de mando con `oc.cancelada`).
|
||||||
|
- `CAMBIOS_API`: `marcarEnCamino` cambia `requisiciones` y `oc`.
|
||||||
|
|
||||||
|
## Paso 4 · Avisos al centro de mando (empujar)
|
||||||
|
|
||||||
|
Función interna **`avisarPortal_(tipo, folioOC)`**: arma el cuerpo del contrato con los datos de la OC **sin montos**
|
||||||
|
(nada de costo, importe, subtotal, IVA, total, tipo de cambio ni margen), lo firma y lo manda con `UrlFetchApp.fetch`
|
||||||
|
(`muteHttpExceptions: true`). Se llama **después** de que el cambio quedó guardado:
|
||||||
|
|
||||||
|
| Momento | `tipo` |
|
||||||
|
|---|---|
|
||||||
|
| `autorizarRequisicion` (emite la OC) | `oc.autorizada` |
|
||||||
|
| `registrarPagoProveedor` | `oc.pagada` |
|
||||||
|
| `marcarEnCamino` | `oc.en_camino` |
|
||||||
|
| `cancelarOrdenCompra` | `oc.cancelada` |
|
||||||
|
| `cerrarRequisicionCompras` (compras cierra a mano) | `req.cerrada` |
|
||||||
|
|
||||||
|
- Solo si el proyecto de la OC está en `PORTAL_PROYECTOS`.
|
||||||
|
- `evento_id` = `<folio OC>:<tipo>:<n>` (n = cuántas veces se mandó ese tipo para esa OC), para que un reintento no duplique.
|
||||||
|
- Si la respuesta no es 200 `ok`, el aviso se guarda en una pestaña nueva **`PortalPendientes`** del libro de compras
|
||||||
|
(`evento_id`, `cuerpo`, `intentos`, `ultimo_error`, `creado`) y lo reintenta el activador del paso 6. La acción del
|
||||||
|
usuario **no** falla por eso: se le muestra «Guardado. El centro de mando se actualizará en unos minutos».
|
||||||
|
- Firma: `X-Hbox-Tiempo` = hora ISO con zona; `X-Hbox-Firma` = `sha256=` + hex de
|
||||||
|
`Utilities.computeHmacSha256Signature(tiempo + "\n" + cuerpo, PORTAL_SECRETO)`.
|
||||||
|
|
||||||
|
## Paso 5 · Recepciones (consultar y cerrar solo)
|
||||||
|
|
||||||
|
Función **`traerRecepcionesPortal()`**: `GET /api/integracion/cotizador/recepciones?desde=<PORTAL_ULTIMA_CONSULTA>`
|
||||||
|
(firmada igual, cuerpo vacío). Por cada recepción:
|
||||||
|
|
||||||
|
- `completa: true` → cierra la requisición con `cerrarReq_` (estado Cerrada, OC Recibida) y nota «Recibido en ZLO por
|
||||||
|
jesush en el centro de mando, 30-oct-2026». Si la requisición ya estaba Cerrada, no hace nada.
|
||||||
|
- `completa: false` → solo agrega una nota de recepción parcial («Llegaron 3 de 4 soportes») y deja la requisición En camino.
|
||||||
|
- Guarda `PORTAL_ULTIMA_CONSULTA` al final, solo si todo salió bien.
|
||||||
|
- Bitácora en cada cierre automático (quién: «centro de mando»).
|
||||||
|
|
||||||
|
## Paso 6 · Activador cada 15 minutos
|
||||||
|
|
||||||
|
`instalarActivadorPortal()` (se corre una vez desde el editor): `ScriptApp.newTrigger('cadaQuinceMinutos').timeBased()
|
||||||
|
.everyMinutes(15)`. `cadaQuinceMinutos` = reintentar `PortalPendientes` + `traerRecepcionesPortal`. Aprovecha para llenar el
|
||||||
|
caché (la idea de §6.14).
|
||||||
|
|
||||||
|
## Paso 7 · Pruebas y publicación
|
||||||
|
|
||||||
|
1. **Node** (`tests/portal.test.js`): el cuerpo nunca lleva montos; la firma coincide con un vector de prueba; sitio
|
||||||
|
obligatorio solo en proyectos ligados; `evento_id` estable; un 500 del portal deja el aviso en `PortalPendientes`;
|
||||||
|
recepción completa cierra, parcial solo anota, repetida no hace nada.
|
||||||
|
2. **PRUEBAS** con `"prueba": true`: requisición → autorizar → en camino → (Leo da «Recibir» en el centro de mando) →
|
||||||
|
la requisición se cierra sola en ≤ 15 min. El centro de mando guarda las de prueba aparte y no toca el inventario.
|
||||||
|
3. Respaldos de los libros (como siempre), `clasp push`, comparar el editor, `clasp deploy` (misma API_URL),
|
||||||
|
`wrangler deploy`.
|
||||||
|
4. Primera OC real de DROGHEDA: revisar juntos en el centro de mando (Compras y la pestaña del sitio en Inventario).
|
||||||
|
|
||||||
|
## Contrato (lo que espera el centro de mando)
|
||||||
|
|
||||||
|
```json
|
||||||
|
POST /api/integracion/cotizador/evento
|
||||||
|
{ "evento_id": "OC-2026-0012:oc.en_camino:1",
|
||||||
|
"tipo": "oc.en_camino",
|
||||||
|
"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", "autorizo": "tania@helgen.tech",
|
||||||
|
"renglones": [ { "n": 1, "descripcion": "Soporte para RTG", "unidad": "pza", "cantidad": 4, "item": "SOP-RTG" },
|
||||||
|
{ "n": 2, "descripcion": "Tornillo 3/8 x 2 in", "unidad": "pza", "cantidad": 32, "item": null } ] } }
|
||||||
|
|
||||||
|
→ 200 { "ok": true, "estado_portal": "en-camino", "tarea_id": "t-oc-2026-0012", "por_ligar": [2] }
|
||||||
|
→ 200 { "ok": true, "repetido": true } (mismo evento_id)
|
||||||
|
→ 400 { "ok": false, "error": "Sitio inválido: MZO (usa VER, ZLO, ESE, LZC o QRO)" }
|
||||||
|
→ 401 { "ok": false, "error": "Firma inválida o vencida" }
|
||||||
|
|
||||||
|
GET /api/integracion/inventario/items
|
||||||
|
→ 200 { "ok": true, "items": [ { "codigo": "SOP-RTG", "descripcion": "Soporte para RTG", "unidad": "pza", "categoria": "instalacion" } ] }
|
||||||
|
|
||||||
|
GET /api/integracion/cotizador/recepciones?desde=2026-10-28T00:00:00-06:00
|
||||||
|
→ 200 { "ok": true, "hasta": "2026-10-30T12:40:00-06:00",
|
||||||
|
"recepciones": [ { "oc_folio": "OC-2026-0012", "req_folio": "REQ-2026-0031", "sitio": "ZLO", "completa": false,
|
||||||
|
"recibio": "jesush", "cuando": "2026-10-30T12:31:00-06:00",
|
||||||
|
"renglones": [ { "n": 1, "pedido": 4, "recibido": 4 }, { "n": 2, "pedido": 32, "recibido": 20 } ] } ] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Hasta que el centro de mando publique estas rutas (fase 1 del lado del sitio), el cotizador puede programar y probar todo
|
||||||
|
contra un servidor simulado en Node con estas mismas respuestas.
|
||||||
@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
title: "Cotizador ↔ centro de mando: órdenes de compra, inventario por sitio y tareas"
|
||||||
|
date: 2026-10-27
|
||||||
|
tags: [project-drogheda, hbox, compras, inventario, cotizador, integracion, sql]
|
||||||
|
estado: 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](integracion-cotizador-instrucciones.md).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Resumen
|
||||||
|
|
||||||
|
1. **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.
|
||||||
|
2. **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.
|
||||||
|
3. **«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.
|
||||||
|
4. **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.
|
||||||
|
5. **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.
|
||||||
|
6. **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`.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
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](integracion-cotizador-instrucciones.md) |
|
||||||
|
| 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_id` evita 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.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 326 KiB |
@ -1 +1 @@
|
|||||||
Subproject commit 5c937e44bdbe1deeb52f6404b6d9458f46a90112
|
Subproject commit 742e534af20f8d78c50c55ea9c5eccc6d707a746
|
||||||
Loading…
Reference in New Issue
Block a user