Files
GeorgeandClaude Sonnet 5 e1c8ee9015 Initial commit: sap-legacy-webhook-emulator
Reconstruye el Webhook Messenger nativo de SAP B1 (FP2605) para
clientes que aun no estan en esa version: captura eventos via
SBO_SP_PostTransactionNotice, provisiona UserTables OEVC/EVC1/OEVN/EVN1
y despacha CloudEvents al mismo endpoint que el messenger real.

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

104 lines
4.4 KiB
SQL

-- SP_Webhook_CaptureEvent — SAP HANA / SQLScript
--
-- Equivalente HANA de sp-webhook-capture-event-mssql.sql: toda la lógica
-- de captura vive acá, no en el cuerpo de SBO_SP_PostTransactionNotice
-- (hook compartido de SAP, ver recreate_sbo_sp_posttransactionnotice_hana.sql).
--
-- Requiere que la UserTable "@OEVN" ya exista (creada por
-- `npm run provision`, ver src/provisioning/).
--
-- ✅ Verificado contra un HANA real (SAP Business One), instalado y
-- funcionando — incluida la sintaxis del EXIT HANDLER y el filtro de eco
-- (INSERT...SELECT...WHERE, más abajo): se confirmó que un DELETE vía
-- Service Layer sobre una fila de @OEVN ya no genera ningún eco nuevo.
-- ⚠️ El filtro por suscripción activa (EXISTS contra @EVC1/@OEVC, más
-- abajo) es nuevo y todavía NO se probó contra ese ambiente.
--
-- U_UserID: no se completa. CURRENT_USER da el usuario de la conexión a
-- HANA (una cuenta técnica), no el usuario real de SAP que hizo la
-- transacción — confirmado contra este mismo ambiente. Ninguno de los 5
-- parámetros que recibe el hook trae ese dato, así que por ahora el campo
-- queda sin poblar en las dos variantes (ver también mssql.sql).
--
-- Name: en una UserTable de SAP, "Name" también debe ser único (no solo
-- "Code") — confirmado contra Service Layer real (ODBC -2035 al repetir
-- un Name, con Code distinto). Por eso Name usa el mismo UUID que Code:
-- cualquier otro valor (como el propio ObjectType) se repite entre
-- eventos del mismo tipo y hace fallar el INSERT en el segundo evento
-- (en silencio, por el EXIT HANDLER).
--
-- Eco de nuestras propias escrituras: cualquier POST/PATCH/DELETE que
-- hace el dispatcher sobre @OEVC/@EVC1/@OEVN/@EVN1 vía Service Layer
-- dispara este mismo hook de nuevo, con un ObjectType especial de SAP
-- para "operación genérica sobre una UserTable simple": un código
-- negativo + tab + nombre físico de la tabla (confirmado real:
-- "-3\t@OEVN" para un DELETE). Si insertáramos esa fila iba a generar
-- otro eco al borrarla, indefinidamente — por eso el INSERT usa
-- SELECT...FROM DUMMY WHERE en vez de VALUES, para poder filtrar el
-- ObjectType sin agregar una rama procedural nueva (ver docs/DISENO.md,
-- hallazgo 5).
--
-- Filtro por suscripción activa: además de lo anterior, solo se inserta
-- si existe al menos una fila en @EVC1 (con su @OEVC en State='A') para
-- este ObjectType+TransType exacto. No es "lógica de negocio propia" del
-- SP — consulta la misma tabla de configuración que ya es la fuente de
-- verdad para el dispatcher, no una copia/lista fija a mantener
-- sincronizada. Evita ensuciar @OEVN con todo lo que SAP genera y a
-- nadie le interesa (en el ambiente de prueba, cientos de eventos por
-- minuto sin ninguna suscripción real).
DROP PROCEDURE "SP_Webhook_CaptureEvent";
CREATE PROCEDURE "SP_Webhook_CaptureEvent" (
IN object_type NVARCHAR(20),
IN transaction_type NVARCHAR(1),
IN num_of_cols_in_key INTEGER,
IN list_of_key_cols_tab_del NVARCHAR(255),
IN list_of_cols_val_tab_del NVARCHAR(255),
OUT error INTEGER,
OUT error_message NVARCHAR(200)
)
LANGUAGE SQLSCRIPT
AS
BEGIN
DECLARE v_code NVARCHAR(36);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
error := ::SQL_ERROR_CODE;
error_message := SUBSTR(::SQL_ERROR_MESSAGE, 1, 200);
END;
error := 0;
error_message := N'Ok';
-- Sin condiciones de negocio: inserta siempre, salvo que sea un eco
-- de una escritura nuestra sobre estas mismas 4 tablas (arriba).
SELECT SYSUUID INTO v_code FROM DUMMY;
INSERT INTO "@OEVN" (
"Code", "Name",
"U_SourceDB", "U_Status", "U_ObjectType", "U_TransType",
"U_FieldsInKe", "U_FieldNames", "U_FieldValue", "U_ReplayStat"
)
SELECT
:v_code, :v_code,
CURRENT_SCHEMA, 'N', :object_type, :transaction_type,
:num_of_cols_in_key, :list_of_key_cols_tab_del, :list_of_cols_val_tab_del, 'N'
FROM DUMMY
WHERE NOT (
:object_type LIKE '-%' || CHAR(9) || '@OEVC'
OR :object_type LIKE '-%' || CHAR(9) || '@EVC1'
OR :object_type LIKE '-%' || CHAR(9) || '@OEVN'
OR :object_type LIKE '-%' || CHAR(9) || '@EVN1'
)
AND EXISTS (
SELECT 1
FROM "@EVC1" f
INNER JOIN "@OEVC" s ON s."Code" = f."U_WebhookID"
WHERE f."U_ObjectType" = :object_type
AND f."U_TransType" = :transaction_type
AND s."U_State" = 'A'
);
END;