Files
sap-intercompany-worker/README.md
T
GeorgeandClaude Sonnet 5 ca57403663 Initial commit: sap-intercompany-worker
Motor de escenarios de replicacion entre bases SAP Business One
(Service Layer). Poll periodico sobre webhook_events, escenarios
configurables via Prisma (filter + transform por escenario).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 07:45:57 -05:00

118 lines
5.9 KiB
Markdown

# sap-intercompany-worker
Worker que replica datos maestros entre bases de SAP Business One (Service
Layer), a partir de **escenarios** configurables — no hay una casa matriz
fija ni una lista fija de subsidiarias: cualquier base registrada puede ser
origen o destino, según lo que declare cada escenario.
## Qué hace
Cada `POLL_INTERVAL_SECONDS` (default 30s), revisa la tabla `webhook_events`
en Postgres — la misma que llena [`sap-webhook-api`](../sap-webhook-api) al
recibir un webhook de SAP — buscando eventos pendientes. Por cada uno:
1. Busca los **escenarios activos** que aplican a ese `businessObject`
(puede haber más de uno, ej. "clientes" y "proveedores" sobre
`BusinessPartners`, cada uno con su propio filtro), y descarta los que
no vinieron realmente de la base origen que ese escenario declara (ver
protección contra loops en `docs/ARQUITECTURA.md`).
2. Lee el registro completo desde la base origen y lo pasa por el `filter`
del escenario (`src/scenarios/<code>.ts`) — si no aplica, sigue con el
próximo. Si aplica, por cada destino lo pasa por `transform` (con acceso
a mapeos de valor cargados en Postgres vía `ctx.mapValue`) y resuelve si
ya existe en ese destino — por clave directa o por un UDF de
correspondencia, según el `keyStrategy` del escenario — para decidir
`PATCH` o `POST`.
3. Registra cada intento (escenario, verbo usado, HTTP status, error, y el
**payload ya transformado enviado**) en `replication_attempts`.
4. Calcula el estado final del evento sobre la suma de todos los intentos
de todos los escenarios que aplicaron: `done` / `partial` / `failed` /
`filtered` (ningún escenario aceptó el mensaje).
Es un proceso separado de `sap-webhook-api` a propósito: SAP tiene un
timeout de 10s para la respuesta del webhook, y replicar hacia varios
destinos (con sus propios logins y llamadas) puede tardar más que eso — por
eso el trabajo pesado corre acá, a su propio ritmo, sin poner en riesgo la
suscripción del webhook.
Ver [`docs/ARQUITECTURA.md`](./docs/ARQUITECTURA.md) para el detalle
completo del diseño (por qué escenarios y no reglas fijas, `keyStrategy`,
diagrama de flujo, reglas de agregación de estado),
[`docs/ESCENARIOS.md`](./docs/ESCENARIOS.md) para la guía paso a paso de
cómo configurar uno con ejemplos reales, y
[`docs/FLUJO.md`](./docs/FLUJO.md) para el diagrama de secuencia
archivo-por-archivo, función por función, de todo el camino de un mensaje
(incluye `sap-webhook-api`).
**Implementado**: datos maestros con destino fijo (`destinationMode = 'fixed'`
— `BP_PROVEEDORES`, `CHARTOFACCOUNTS`, `PROFITCENTERS`, `SALESPERSONS`) y
transacciones con destino resuelto desde el propio mensaje
(`destinationMode = 'payload'` — `JOURNALENTRIES`, primer caso real). Un
escenario también puede declarar un `destinationBusinessObject` distinto al
de origen (ej. `PurchaseOrders` → `Orders`), para cuando el objeto cambia
de nombre entre bases.
**Trabajo futuro**: `sap-intercompany-viewer` (frontend) +
`sap-intercompany-api` (backend nuevo, dueño de `databases`/`scenarios`/
`scenario_destinations`/`scenario_value_mappings`/`webhook_events`/
`replication_attempts` vía HTTP, ya que este worker no expone HTTP a
propósito) — visor único de eventos/log/escenarios con reproceso, en
reemplazo de `sap-webhook-viewer`.
## Quick start
```bash
npm install
cp .env.example .env # completar DATABASE_URL / POLL_INTERVAL_SECONDS
# Postgres: instancia nativa (Homebrew) en localhost:5432 — ya no se usa
# Docker para esto (docker-compose.yml queda sin uso; si tenés un Postgres
# corriendo por Homebrew/apt en el mismo puerto, ojo con el conflicto:
# localhost prioriza la instancia nativa sobre un contenedor Docker).
npx prisma migrate deploy
# Registrar bases y al menos un escenario antes de correr el worker
# (ver docs/ESCENARIOS.md para la guía completa con ejemplos reales)
npx tsx scripts/manage-config.ts database:add HQ01 "Casa Matriz" <baseUrl> <companyDb> <user> <pass>
npx tsx scripts/manage-config.ts scenario:add <code> <name> <businessObject> HQ01 <keyField>
npx tsx scripts/manage-config.ts scenario:add-destination <code> <databaseCode>
# editar src/scenarios/<code>.ts (se generó solo con la primera línea)
npm run run-once # una sola pasada, útil para probar
npm run start # corre en loop
```
Sin destinos configurados para un escenario, el worker igual corre: lee el
registro de la base origen y deja el evento en `failed` (0 intentos), sin
romper — útil para probar el flujo antes de tener todas las bases reales.
## Estructura
```
prisma/schema.prisma # modelos (ver docs/TABLAS.md)
docs/
ARQUITECTURA.md # conceptos, keyStrategy, diagrama de flujo, reglas de estado
TABLAS.md # referencia de columnas por tabla, con ejemplos reales
ESCENARIOS.md # guía paso a paso con 5 ejemplos reales configurados
FLUJO.md # diagrama de secuencia + funciones principales por archivo
scripts/manage-config.ts # CLI de administración (databases, scenarios, destinos, mapeos)
src/
config.ts # solo POLL_INTERVAL_SECONDS; todo lo demás vive en Postgres
serviceLayer.ts # cliente mínimo del Service Layer (Login/GET/POST/PATCH)
replicationConfig.ts # carga de config activa desde Postgres en cada tick
scenarioTransform.ts # carga/ejecuta filter+transform de un escenario + ctx.mapValue
replicateEvents.ts # motor: resuelve N escenarios por evento (datos maestros y transacciones)
scenarios/ # un archivo <code>.ts por escenario (filter + transform)
index.ts # cron / --once
```
## Stack
- Script plano en TypeScript (ESM), ejecutado con `tsx` — sin framework,
porque no expone HTTP.
- Prisma 7 + `@prisma/adapter-pg` contra Postgres.
- `node-cron` para el loop periódico.
Contexto completo de por qué se armó así (decisiones de arquitectura,
alternativas descartadas) en `CLAUDE.md`, en la raíz del repo.