hbox-docs/backend/README.md

162 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/<tipo>/_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.