# Backend de actas — configuración en el servidor (una sola vez) Servicio Flask que recibe los formularios firmados de los técnicos y los guarda como JSON por número de serie en `/srv/projects/drogheda/actas/`. Corre detrás de nginx en `127.0.0.1:8081`; nginx expone `/api/` en el mismo dominio, con la misma contraseña. ## 1. Instalar (en el servidor, como leo) ```bash cd /srv/projects/drogheda/hbox-docs/backend python3 -m venv venv ./venv/bin/pip install -r requirements.txt # carpeta donde se guardan las actas mkdir -p /srv/projects/drogheda/actas ``` ## 2. Servicio systemd (arranque automático) ```bash sudo cp actas-hbox.service /etc/systemd/system/actas-hbox.service sudo systemctl daemon-reload sudo systemctl enable --now actas-hbox systemctl status actas-hbox # debe decir "active (running)" curl -s http://127.0.0.1:8081/api/salud # {"ok":true,...} ``` ## 3. nginx — exponer /api/ en el mismo dominio Dentro del bloque `server { ... }` de `drogheda.helgen.online` (el mismo archivo `/etc/nginx/sites-enabled/drogheda.helgen.online.conf`, en la parte de `listen 443`), agrega **antes** del `location / {`: ```nginx location /api/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # hereda el auth_basic del server; si quieres, puedes repetirlo aquí. } ``` Luego: ```bash sudo nginx -t && sudo systemctl reload nginx curl -u leo:TU_PASS https://drogheda.helgen.online/api/salud # {"ok":true,...} ``` ## 4. Ver las actas recibidas - Índice rápido por tipo: `/srv/projects/drogheda/actas//_indice.csv` (ábrelo en Excel). - Listado JSON: `https://drogheda.helgen.online/api/actas` - Un acta concreta: `https://drogheda.helgen.online/api/actas/prueba-v5/V5-07_....json` - Respaldo: basta con copiar la carpeta `/srv/projects/drogheda/actas/`. ## Actualizar el backend Tras un `git pull` que cambie `app.py`: ```bash sudo systemctl restart actas-hbox ``` ## 5. Colecciones del 13-oct — tableros, plan, inventario y sitios Mismo esquema que las actas: **un JSON por registro** más un `_indice.csv` que se regenera en cada cambio. Carpetas (variables de entorno, con su valor por omisión junto a `ACTAS_DIR`): | Variable | Omisión | Qué guarda | |---|---|---| | `TAREAS_DIR` | `/srv/projects/drogheda/tareas` | pendientes del tablero (kanban) | | `INVENTARIO_DIR` | `/srv/projects/drogheda/inventario` | `items/` y `movimientos/` | | `SITIOS_DIR` | `/srv/projects/drogheda/sitios` | un JSON por sitio (`icave.json`…) | | `PLAN_FILE` | `/srv/projects/drogheda/plan-produccion.json` | el plan del pedido (570 → 113/327/130) | Reglas comunes: toda escritura exige el usuario autenticado (`X-Remote-User`; sin él, 403); cada registro guarda `creado_por/creado_en/modificado_por/modificado_en` y un `historial` de `{quien, cuando, cambio}`; **solo un administrador borra** (`ACTAS_ADMINS`). Un `POST` puede traer su propio `id` (lo usa `seed_1013.py` para ser idempotente): si ya existe → 409. Siembra inicial (plan, 4 sitios, ítems de inventario, pendientes del 13-oct), idempotente: ```bash cd /srv/projects/drogheda/hbox-docs && ./backend/venv/bin/python3 backend/seed_1013.py # agrega --usuario leo (omisión) para firmar la siembra; --extra tareas.json para importar más pendientes ``` ### Pendientes (tablero kanban) — `tareas` Campos: `id`, `titulo`, `descripcion`, `pista` ∈ ingenieria·produccion·mecanica·compras·campo, `estado` ∈ por-hacer·en-curso·bloqueado·hecho, `responsable`, `fecha_limite` (AAAA-MM-DD o null), `enlaces` [{titulo,url}], `bloqueada_por` [ids], `orden`, `etiquetas` [], `version_tarjeta` ∈ V5.5·V5.6·V5.7·v6·—. ```bash U=leo:PASS; D=https://drogheda.helgen.online curl -s -u $U "$D/api/tareas?pista=ingenieria&estado=en-curso&responsable=michelle" curl -s -u $U -H 'Content-Type: application/json' -X POST $D/api/tareas \ -d '{"titulo":"V5.6 pasada 2","pista":"ingenieria","responsable":"michelle","estado":"en-curso", "version_tarjeta":"V5.6","fecha_limite":"2026-10-17","enlaces":[{"titulo":"Pasada 2","url":"/hardware/v56-layout-pasada-2/"}]}' # mover de columna (y posición): PATCH parcial; el historial anota quién y qué cambió curl -s -u $U -H 'Content-Type: application/json' -X PATCH "$D/api/tarea?id=T261013-ab12" \ -d '{"estado":"hecho","orden":0,"_nota":"commit d9f1 subido"}' # renumerar una columna después de arrastrar (no escribe historial) curl -s -u $U -H 'Content-Type: application/json' -X POST $D/api/tareas/orden \ -d '{"estado":"en-curso","ids":["T261013-ab12","t-v57-fuente"]}' curl -s -u $U -X DELETE "$D/api/tarea?id=T261013-ab12" # solo admin → 403 para los demás curl -s -u $U $D/api/tareas/resumen # conteo pista×estado + vencidas ``` ### Plan de producción — `plan` ```bash curl -s -u $U $D/api/plan # si no existe el archivo devuelve el plan por omisión con "_default": true curl -s -u $U -H 'Content-Type: application/json' -X PUT $D/api/plan -d '{ "pedido_total": 570, "versiones": [{"version":"V5.6","objetivo":113,"nota":"con Mean Well SD-15B","serie_prefijo":"V56","fecha":"2026-11-15"}, {"version":"V5.7","objetivo":327,"nota":"fuente a bordo","serie_prefijo":"V57"}, {"version":"v6","objetivo":130,"nota":"+ reemplazos futuros","serie_prefijo":"V6"}]}' # solo admin ``` `etapas` (embudo) se puede omitir: se conserva la que ya había. Cada etapa dice de qué tipos de acta se cuenta (`tipos`), si solo cuentan las aprobadas (`ok`) y si solo cuentan unidades HBOX (`solo_unidades`). El tablero de producción hace el conteo en el navegador con `/api/actas`. ### Inventario — `inventario` + `movimientos` El stock **no se edita a mano**: es la suma de los movimientos del ítem (`entrada` +n, `salida` −n, `ajuste` = conteo físico; el servidor calcula el delta). Una salida mayor al stock registrado → 409. ```bash curl -s -u $U "$D/api/inventario?categoria=fuente&aplica_a=V5.6&alertas=1" # alerta = stock < minimo curl -s -u $U -H 'Content-Type: application/json' -X POST $D/api/inventario \ -d '{"codigo":"SD-15B-05","descripcion":"Mean Well SD-15B-05","categoria":"fuente","aplica_a":["V5.6"], "unidad":"pza","minimo":113,"ubicacion":"Anaquel B1","proveedor":"Mouser","codigo_proveedor":"709-SD15B05","stock_inicial":0}' curl -s -u $U -H 'Content-Type: application/json' -X PATCH "$D/api/inventario/item?id=SD-15B-05" -d '{"ubicacion":"Anaquel B2","minimo":120}' curl -s -u $U -H 'Content-Type: application/json' -X POST $D/api/inventario/movimiento \ -d '{"item_id":"SD-15B-05","tipo":"entrada","cantidad":50,"motivo":"llegó pedido Mouser","referencia":"OC-0042"}' curl -s -u $U -H 'Content-Type: application/json' -X POST $D/api/inventario/movimiento \ -d '{"item_id":"EB-V56","tipo":"salida","cantidad":1,"motivo":"ensamble","referencia":"HBOX5-E07-A010-R0006"}' curl -s -u $U -H 'Content-Type: application/json' -X POST $D/api/inventario/movimiento \ -d '{"item_id":"EB-V56","tipo":"ajuste","cantidad":12,"motivo":"conteo físico 13-oct"}' curl -s -u $U "$D/api/inventario/movimientos?item_id=EB-V56&n=50" curl -s -u $U -o inventario.csv $D/api/inventario/export.csv ``` ### Sitios — `sitios` ```bash curl -s -u $U "$D/api/sitios" # activos; &todos=1 incluye los inactivos curl -s -u $U -H 'Content-Type: application/json' -X POST $D/api/sitios -d '{"id":"icave","nombre":"ICAVE", "cliente":"Hutchison Ports","ubicacion":"Veracruz, Ver.","alias":["Veracruz"], "contactos":[{"nombre":"por confirmar","rol":"supervisor de patio","telefono":"","correo":""}],"notas":""}' # solo admin curl -s -u $U -H 'Content-Type: application/json' -X PATCH "$D/api/sitio?id=icave" -d '{"activo":true,"ubicacion":"Veracruz, Ver."}' # solo admin curl -s -u $U "$D/api/sitio/unidades?id=icave" ``` `/api/sitio/unidades` junta las actas de envío, recepción, instalación, diagnóstico y daño cuyo campo `sitio` sea el slug del sitio (`coincidencia: "sitio"`), y también las que solo coinciden por texto (`terminal` de la flota, destino del envío) con el nombre o un alias (`coincidencia: "texto"`), agrupadas por unidad con su marca de daño vigente.