# Referencia de tablas Todas viven en la base Postgres compartida (`docker-compose.yml` en la raíz del workspace), definidas en [`prisma/schema.prisma`](../prisma/schema.prisma). Los ejemplos de esta página usan datos reales de la config actual del ambiente de desarrollo (bases `HQ01`/`SUB_01`.."SUB_04", escenario `BP_PROVEEDORES`). ## `webhook_events` Bandeja de entrada. `sap-webhook-api` inserta una fila acá por cada notificación real que recibe de SAP (además de guardar el `.json` local). No se toca a mano — el worker es el único que la actualiza. | Columna | Tipo | Notas | |---|---|---| | `id` | `String` (PK) | El `id` del CloudEvent que manda SAP. | | `business_object` | `String` | `'BusinessPartners'`, `'SalesPersons'`, `'ProfitCenters'`, ... | | `operation` | `String` | `'Created'` \| `'Updated'`. | | `subject` | `String` | Clave del registro en la base que lo generó (`CardCode`, `SalesEmployeeCode`, `CenterCode`, según el objeto). | | `payload` | `Json` | El CloudEvent completo tal cual llegó — incluye `source` (`/default/sap.b1/`), que el motor usa para la protección contra loops (ver `ARQUITECTURA.md`). | | `status` | `EventStatus` | `pending` → `processing` → `done` \| `partial` \| `failed` \| `filtered`. | | `received_at` | `DateTime` | | ## `databases` Registro único de toda base SAP alcanzable por Service Layer. Sin distinción HQ/subsidiaria — ver [`ARQUITECTURA.md`](./ARQUITECTURA.md). | Columna | Tipo | Notas | |---|---|---| | `id` | `Int` (PK, autoincremental) | Identificador interno, no se usa como referencia legible. | | `code` | `String` (único) | Mnemónico corto y **estable**, asignado una sola vez al crear (ej. `HQ01`, `SUB_01`). No autoincremental — es lo que aparece en los logs. | | `name` | `String` | Descriptivo, solo para mostrar. | | `base_url` | `String` | URL del Service Layer, ej. `https://sap10:50000/b1s/v2`. | | `company_db` | `String` | `CompanyDB` de esa base. | | `username` / `password` | `String` | Credenciales de login del Service Layer. | | `active` | `Boolean` | Una base inactiva no se usa como origen ni destino en ningún escenario. | Ejemplo (ambiente de desarrollo actual): | code | name | base_url | company_db | active | |---|---|---|---|---| | `HQ01` | HQ DemoCL | `https://sap10:50000/b1s/v2` | `SBODemoCL` | `true` | | `SUB_01` | SUB 01 DemoCL | `https://sap10:50000/b1s/v2` | `SUBDemoCL_01` | `true` | | `SUB_02` | SUB 02 DemoCL | `https://sap10:50000/b1s/v2` | `SUBDemoCL_02` | `true` | | `SUB_03` | SUB 03 DemoCL | `https://sap10:50000/b1s/v2` | `SUBDemoCL_03` | `true` | | `SUB_04` | SUB 04 DemoCL | `https://sap10:50000/b1s/v2` | `SUBDemoCL_04` | `true` | ## `scenarios` Una fila por escenario. Ver [`ESCENARIOS.md`](./ESCENARIOS.md) para cómo armar uno con el CLI. | Columna | Tipo | Notas | |---|---|---| | `id` | `Int` (PK) | | | `code` | `String` (único) | Identificador legible del escenario, en mayúsculas (ej. `BP_PROVEEDORES`). Determina el nombre de su archivo (`filter` + `transform`) en minúsculas: `src/scenarios/.ts` (ej. `bp_proveedores.ts`) — ver `scenarioFileSlug` en `src/scenarioTransform.ts`. | | `name` | `String` | Descriptivo. | | `business_object` | `String` | Objeto que dispara el escenario: con qué se matchea `webhook_events` y con qué se lee el registro del origen. | | `destination_business_object` | `String?` | Objeto a usar en el `GET`/`POST`/`PATCH` del **destino**, si es distinto al de origen (ej. origen `PurchaseOrders` → destino `Orders`, típico de comercio intercompañía). `null` ⇒ mismo objeto que `business_object` (el caso común). | | `origin_database_code` | `String` (FK → `databases.code`) | De dónde se lee el registro completo. | | `destination_mode` | `ScenarioDestinationMode` | `fixed` (lista fija en `scenario_destinations`) \| `payload` (destino resuelto por `routing_field` — ambos implementados). | | `key_field` | `String` | Campo clave del registro, ej. `CardCode`, `SalesEmployeeCode`. | | `key_type` | `ScenarioKeyType` | `string` (va entre comillas en la URL OData) \| `number` (sin comillas — un campo numérico con comillas da HTTP 400). | | `key_strategy` | `ScenarioKeyStrategy` | `portable` (la clave vale lo mismo en origen y destino) \| `udfLink` (SAP asigna la clave por base; se busca por `match_field`). | | `match_field` | `String?` | Solo si `key_strategy = udfLink`: nombre del UDF en el destino que guarda la clave de origen (ej. `U_ITC_Source_JdtNum`). | | `routing_field` | `String?` | Solo si `destination_mode = payload`: UDF en el origen cuyo valor es el `code` de la base destino (ej. `U_ITC_Target`). | | `active` | `Boolean` | | No hay columnas de filtro: el filtro es la función `filter()` del archivo del escenario (ver [`ARQUITECTURA.md`](./ARQUITECTURA.md)). Ejemplo (los 5 escenarios activos hoy en desarrollo): | code | business_object | destination_business_object | origin | destinations | key_field | key_strategy | |---|---|---|---|---|---|---| | `BP_PROVEEDORES` | `BusinessPartners` | — | `HQ01` | `SUB_01`, `SUB_02` | `CardCode` | `portable` | | `CHARTOFACCOUNTS` | `ChartOfAccounts` | — | `HQ01` | `SUB_01`, `SUB_02` | `Code` | `portable` | | `JOURNALENTRIES` | `JournalEntries` | — | `HQ01` | (payload: `U_ITC_Target`) | `JdtNum` | `udfLink` (`U_ITC_Source_JdtNum`) | | `PROFITCENTERS` | `ProfitCenters` | — | `HQ01` | `SUB_01`, `SUB_02`, `SUB_03` | `CenterCode` | `portable` | | `SALESPERSONS` | `SalesPersons` | — | `HQ01` | `SUB_01`, `SUB_03` | `SalesEmployeeCode` | `udfLink` (`U_HQ_SlpCode`) | Ninguno usa todavía `destination_business_object` (ninguno lo necesita por ahora) — quedaría, por ejemplo, `PurchaseOrders` → `Orders` en un futuro escenario de compras intercompañía. ## `scenario_destinations` Destinos fijos de un escenario (solo aplica a `destination_mode = fixed`). Único por `(scenario_id, database_code)`. | Columna | Tipo | Notas | |---|---|---| | `scenario_id` | `Int` (FK → `scenarios.id`) | | | `database_code` | `String` (FK → `databases.code`) | | ## `scenario_value_mappings` Datos puros que el archivo de transformación de un escenario consulta vía `ctx.mapValue(fieldName, sourceValue)` — ver [`ARQUITECTURA.md`](./ARQUITECTURA.md). El código decide QUÉ campo mapear y QUÉ hacer si falta el valor (tirar error, dejar sin traducir, etc.); acá solo vive la equivalencia. Escopada por escenario (no global por objeto+campo) porque el mismo campo puede tener dominios de valores distintos según el escenario (ej. `GroupCode` de clientes vs. de proveedores). | Columna | Tipo | Notas | |---|---|---| | `scenario_id` | `Int` (FK) | | | `field_name` | `String` | Ej. `GroupCode`. | | `database_code` | `String` (FK → `databases.code`) | El destino al que aplica esta traducción puntual. | | `source_value` | `String` | Valor tal como viene de la base origen. | | `target_value` | `String` | Valor a usar en ese destino. | Único por `(scenario_id, field_name, database_code, source_value)`. Ejemplo real (`BP_PROVEEDORES`, `GroupCode`, hoy 1:1 en ambas subsidiarias): | scenario | field_name | database_code | source_value | target_value | |---|---|---|---|---| | `BP_PROVEEDORES` | `GroupCode` | `SUB_01` | `101` | `101` | | `BP_PROVEEDORES` | `GroupCode` | `SUB_01` | `110` | `110` | | `BP_PROVEEDORES` | `GroupCode` | `SUB_02` | `101` | `101` | | ... | | | | (7 códigos × 2 destinos = 14 filas) | ## `replication_attempts` Log de auditoría inmutable. Una fila por cada intento de un escenario hacia una base destino. | Columna | Tipo | Notas | |---|---|---| | `event_id` | `String` (FK → `webhook_events.id`) | | | `scenario_id` | `Int` (FK → `scenarios.id`) | Imprescindible porque un evento puede pasar por varios escenarios a la vez. | | `origin_database` | `String` | Código de la base origen usada en **este** intento — texto libre, no FK: sobrevive si la base se borra o renombra después. | | `destination_database` | `String` | Ídem para el destino. `'N/A'` si el intento falló antes de resolver un destino (ej. error leyendo el origen). | | `verb_used` | `String` | `POST` \| `PATCH` \| `SKIPPED` (falta mapeo, o transform tiró error) \| `N/A` (error antes de llamar al destino). | | `http_status` | `Int?` | | | `error_message` | `String?` | | | `payload_sent` | `Json?` | El payload ya transformado enviado al destino — se guarda **siempre**, en éxito y en falla. | | `result_key` | `String?` | Para `key_strategy=udfLink`: la clave propia del destino (encontrada o recién asignada por SAP en un `POST`). `null` en `key_strategy=portable` y en datos maestros sin esta estrategia. | | `attempted_at` | `DateTime` | | Índices sobre `event_id`, `scenario_id` y `attempted_at`, pensados para la futura UI de consulta (`sap-intercompany-viewer`, ver `ARQUITECTURA.md`). Ejemplo real (`SALESPERSONS`, vendedor nuevo `SalesEmployeeCode=7` en HQ01, `POST` en ambas subsidiarias — SAP le asignó el mismo número por coincidencia, pero el motor no lo asume, lo captura del response): | event_id | scenario | destination_database | verb_used | http_status | result_key | |---|---|---|---|---|---| | `149be29e-...` | `SALESPERSONS` | `SUB_01` | `POST` | `201` | `7` | | `149be29e-...` | `SALESPERSONS` | `SUB_03` | `POST` | `201` | `7` |