SBO_SP_TransactionNotification: ejemplos para SQL Server y HANA

Por Zain Abbas, Kodenova · Actualizado el

Respuesta cortaSBO_SP_TransactionNotification es un procedimiento almacenado que SAP Business One llama dentro de la misma transacción de base de datos cada vez que un objeto se crea, actualiza, elimina, cancela o cierra. Si su código asigna a error cualquier valor distinto de 0, SAP B1 revierte la transacción y muestra su error_message al usuario. Es la forma más sencilla de imponer una regla de negocio en todos los canales a la vez: el cliente de B1, los add-ons con DI API, Service Layer y las importaciones de datos.

A continuación verá cómo está conectado el procedimiento, las mismas reglas escritas para SQL Server y para HANA, los códigos de tipo de objeto que realmente necesita y los errores que aparecen con más frecuencia en los procedimientos existentes.

¿Cómo funciona SBO_SP_TransactionNotification?

Cada base de datos de empresa viene con el procedimiento ya creado y un bloque vacío “ADD YOUR CODE HERE”. SAP B1 pasa cinco entradas:

ParámetroTipoQué contiene
object_typenvarchar(30)El código del objeto como texto: '17' para un pedido de cliente, '2' para un socio de negocios, o el código de un UDO
transaction_typenchar(1)A crear, U actualizar, D eliminar, C cancelar, L cerrar
num_of_cols_in_keyintCuántas columnas clave tiene el objeto
list_of_key_cols_tab_delnvarchar(255)Nombres de las columnas clave, separados por tabulador (normalmente DocEntry, CardCode o ItemCode)
list_of_cols_val_tab_delnvarchar(255)Los valores clave, separados por tabulador, como texto

Al final, el procedimiento devuelve una fila: el número de error y el mensaje. 0 significa “dejar pasar”. Cualquier otro número bloquea la transacción.

De “dentro de la misma transacción” se derivan dos cosas. Primero, cuando su código se ejecuta, el documento nuevo ya está en ORDR, OINV y las tablas de líneas, así que puede consultarlo como cualquier fila confirmada. Segundo, todo lo que ponga en el procedimiento añade tiempo a cada guardado de la empresa, para cada tipo de objeto. Una consulta lenta en la rama del pedido de cliente solo deja de ralentizar todo lo demás si la rama se filtra primero por object_type.

¿Qué significan los códigos de tipo de objeto?

object_type es el mismo valor que ve en la columna ObjType de cada tabla de cabecera. Los que cubren la mayoría de las reglas:

CódigoObjetoTabla de cabecera
2Socio de negociosOCRD
4ArtículoOITM
13Factura de clientesOINV
14Nota de crédito de clientesORIN
15EntregaODLN
17Pedido de clienteORDR
18Factura de proveedoresOPCH
20Pedido de entrada de mercancíasOPDN
22Pedido de compraOPOR
23Oferta de ventasOQUT
24Pago recibidoORCT
30Asiento contableOJDT
46Pago efectuadoOVPM
59 / 60Entrada de mercancías / salida de mercancíasOIGN / OIGE
67Transferencia de stockOWTR
112Borrador (cualquier tipo de documento)ODRF
202Orden de fabricaciónOWOR

Para un UDO, object_type es el código del UDO que usted registró, no un número. Los detalles a nivel de campo de las tablas principales están en nuestra referencia de tablas de SAP B1.

¿Cómo se bloquea un pedido de cliente sin número de pedido del cliente?

Una primera regla típica: el pedido de cliente debe llevar la referencia del cliente (NumAtCard, que en el cliente aparece como “Número de referencia de deudor” / “Customer Ref. No.”).

SQL Server (T-SQL):

-- inside the "ADD YOUR CODE HERE" block
IF @object_type = '17' AND @transaction_type IN ('A', 'U')
BEGIN
    IF EXISTS (SELECT 1
               FROM ORDR
               WHERE DocEntry = CAST(@list_of_cols_val_tab_del AS INT)
                 AND ISNULL(NumAtCard, '') = '')
    BEGIN
        SET @error = 17001
        SET @error_message = N'Sales order: enter the Customer Ref. No.'
    END
END

SAP HANA (SQLScript):

-- declare at the top of the procedure, next to error and error_message:
-- cnt INT;

IF :object_type = '17' AND (:transaction_type = 'A' OR :transaction_type = 'U') THEN
    SELECT COUNT(*) INTO cnt
    FROM "ORDR"
    WHERE "DocEntry" = CAST(:list_of_cols_val_tab_del AS INT)
      AND IFNULL("NumAtCard", '') = '';

    IF :cnt > 0 THEN
        error := 17001;
        error_message := N'Sales order: enter the Customer Ref. No.';
    END IF;
END IF;

La versión de HANA no es un buscar y reemplazar de la de T-SQL. Importan cuatro diferencias:

  1. Las declaraciones van al principio. SQLScript exige cnt INT; en la sección de declaraciones antes de la primera sentencia, donde ya están declarados error y error_message. Declararla en medio del cuerpo falla al compilar. Si lo prefiere, envuelva la regla en su propio bloque BEGIN ... END con su propio DECLARE.
  2. No existe IF EXISTS (SELECT ...). Cuente en una variable y evalúe la variable.
  3. Lea los parámetros con dos puntos (:object_type) y asigne con :=.
  4. Ponga comillas a los nombres con mayúsculas y minúsculas. "DocEntry" y "NumAtCard" deben ir entre comillas porque HANA trata en mayúsculas los nombres sin comillas.

Los demás cambios de sintaxis (ISNULL a IFNULL, GETDATE a CURRENT_DATE, + a ||) son los mismos que hace en las consultas guardadas. Nuestro conversor de SQL a HANA los resuelve en una primera pasada rápida, y la guía de migración a HANA cubre la reescritura completa del procedimiento.

¿Cómo se validan las líneas del documento?

Las comprobaciones de cabecera son fáciles porque la clave es la cabecera. Para las líneas, una la tabla de líneas por DocEntry. Ejemplo: bloquear una factura de clientes que tenga alguna línea con precio cero para un artículo que se vende, no uno gratuito.

SQL Server:

IF @object_type = '13' AND @transaction_type = 'A'
BEGIN
    IF EXISTS (SELECT 1
               FROM INV1 T1
               INNER JOIN OITM T2 ON T2.ItemCode = T1.ItemCode
               WHERE T1.DocEntry = CAST(@list_of_cols_val_tab_del AS INT)
                 AND T1.Price = 0
                 AND T2.SellItem = 'Y')
    BEGIN
        SET @error = 13001
        SET @error_message = N'A/R invoice: a line has price 0. Check the price list.'
    END
END

SAP HANA:

IF :object_type = '13' AND :transaction_type = 'A' THEN
    SELECT COUNT(*) INTO cnt
    FROM "INV1" T1
    INNER JOIN "OITM" T2 ON T2."ItemCode" = T1."ItemCode"
    WHERE T1."DocEntry" = CAST(:list_of_cols_val_tab_del AS INT)
      AND T1."Price" = 0
      AND T2."SellItem" = 'Y';

    IF :cnt > 0 THEN
        error := 13001;
        error_message := N'A/R invoice: a line has price 0. Check the price list.';
    END IF;
END IF;

Incluya el número de línea en el mensaje cuando pueda ('Line ' + CAST(T1.LineNum + 1 AS NVARCHAR) en SQL Server, 'Line ' || TO_NVARCHAR(T1."LineNum" + 1) en HANA). LineNum empieza en 0, el cliente muestra las filas desde 1 y, si no, los usuarios tendrán que buscar en una factura de 60 líneas.

¿Cómo se hace obligatorio el número de identificación fiscal para la facturación electrónica?

En proyectos de ZATCA, SRI, FEL y DTE, un número de identificación fiscal del comprador ausente o mal formado es un motivo habitual de rechazo de una factura después de haberla contabilizado ya en SAP B1. Detenerlo en el socio de negocios sale más barato que corregir después una factura rechazada.

-- SQL Server: customers must have a tax ID (LicTradNum)
IF @object_type = '2' AND @transaction_type IN ('A', 'U')
BEGIN
    IF EXISTS (SELECT 1 FROM OCRD
               WHERE CardCode = @list_of_cols_val_tab_del
                 AND CardType = 'C'
                 AND ISNULL(LicTradNum, '') = '')
    BEGIN
        SET @error = 2001
        SET @error_message = N'Customer: Federal Tax ID is required for e-invoicing.'
    END
END

Observe que la clave del socio de negocios es CardCode, una cadena, así que aquí no hay conversión. En HANA, el mismo bloque usa "CardCode" = :list_of_cols_val_tab_del y el patrón de conteo de arriba.

Las reglas de formato (dígitos de control, longitud según el tipo de identificación) conviene hacerlas en un add-on o en un validador antes de la captura de datos, porque cambian según el país. Para encontrar los clientes que fallarían hoy, ejecute primero la consulta de socios de negocios sin identificación fiscal, corríjalos y después active la regla. De lo contrario, la primera persona que edite un registro de cliente antiguo quedará bloqueada por algo que no causó.

¿Cuáles son los errores comunes?

Reglas que se saltan los borradores. Si un procedimiento de autorización convierte un pedido de cliente en borrador, SAP B1 lo guarda como tipo de objeto 112 en ODRF/DRF1, no como 17. Su regla para 17 solo se dispara cuando el borrador se convierte en un documento real. A menudo eso está bien, pero si la regla pretende detener datos erróneos antes de que los vea un autorizador, agregue una rama 112 filtrada por ODRF.ObjType = '17'.

Devolver conjuntos de resultados adicionales. En SQL Server, un SELECT o PRINT olvidado para depurar puede producir conjuntos de resultados antes del SELECT @error, @error_message final, y SAP B1 puede leer el equivocado. Mantenga la salida de depuración fuera del procedimiento; si es imprescindible, registre en una tabla.

Ramas lentas. Cada guardado de la empresa espera al procedimiento. Filtre por object_type y transaction_type en el IF exterior, use la clave en la cláusula WHERE y evite los cursores. Una regla que recorre OINV sin el filtro de DocEntry irá bien en una empresa de prueba y será un suplicio en una base de datos con años de facturas.

Códigos de error que nadie puede rastrear. Use un esquema de numeración, por ejemplo el tipo de objeto por 1000 más un contador (17001, 13001, 2001). Cuando un usuario envíe una captura de pantalla, sabrá qué rama se disparó sin leer todo el procedimiento.

Cancelar y cerrar son eventos distintos. C y L llegan como tipos de transacción propios. Una regla escrita para A y U no impedirá que alguien cierre un pedido de cliente. Si necesita bloquear un cierre (por ejemplo, porque aún hay entregas abiertas vinculadas), trate L explícitamente.

Bloquear lo que SAP B1 actualiza en segundo plano. Algunos objetos se actualizan como efecto secundario de otra transacción (los campos de stock de un artículo cuando se contabiliza un documento, el saldo de un socio de negocios). Una regla estricta sobre U para el objeto 4 o 2 puede bloquear un documento de ventas por una actualización secundaria. Mantenga las reglas de actualización acotadas y pruébelas contabilizando documentos, no solo editando datos maestros.

Perder el código en una reconstrucción. El procedimiento vive en la base de datos de la empresa. Guarde el script completo en control de versiones y compárelo después de actualizaciones, copias de empresa y restauraciones, para que ninguna regla desaparezca en silencio.

¿Y SBO_SP_PostTransactionNotice?

Tiene los mismos parámetros, pero SAP B1 lo llama después de confirmar la transacción. No puede bloquear nada. Úselo para tareas secundarias: escribir en una tabla de registro o poner el DocEntry en una cola para una integración (un emisor de facturación electrónica, una notificación por WhatsApp, una sincronización con otro sistema). Manténgalo ligero también, y nunca llame a servicios externos desde dentro. Una tabla de cola más un servicio aparte que la lea es el diseño más seguro.

¿Cómo ven el error los add-ons y Service Layer?

La regla se ejecuta a nivel de base de datos, así que se aplica venga de donde venga el documento. Un Add() de DI API devuelve un resultado distinto de cero y su mensaje vuelve mediante GetLastError. Un POST de Service Layer falla con un error que incluye el texto del mensaje (vea la guía de Service Layer para saber cómo leerlo). Las importaciones con Data Transfer Workbench fallan fila por fila. Por eso el procedimiento es un buen lugar para las reglas que deben cumplirse en todas partes, y por eso sus mensajes deben tener sentido para quien lee un log de integración, no solo para un usuario en el cliente. Para la parte de manejo de errores de DI API, vea nuestra guía de códigos de error de DI API.


¿Necesita reglas como estas escritas y probadas tanto en SQL Server como en HANA? Es parte de nuestro trabajo de desarrollo para SAP Business One.

Fuentes

Servicios relacionados

Preguntas frecuentes

Preguntas frecuentes

¿Dónde encuentro SBO_SP_TransactionNotification?

En SQL Server está en la base de datos de la empresa, en Programmability > Stored Procedures. En HANA está en el esquema de la empresa, en Procedures. Edítelo con SSMS o con SAP HANA Studio / Database Explorer, y pruebe primero en una copia de la empresa.

¿Puedo tener más de una regla para el mismo objeto?

Sí. Agregue varios bloques IF dentro de la misma rama. Compruebe error antes de ejecutar el siguiente bloque, o la última regla que falle sobrescribirá el primer mensaje.

¿TransactionNotification es lo mismo que un procedimiento de autorización?

No. Un procedimiento de autorización envía un documento a alguien para que decida y lo deja pasar tras la aprobación. TransactionNotification simplemente rechaza el documento hasta que los datos sean correctos. Use las autorizaciones para decisiones de criterio y el procedimiento para las reglas estrictas.

¿Listo para empezar?

Cuéntenos lo que necesita. Recibirá una respuesta técnica clara, un alcance definido y un precio fijo, normalmente en 24 horas.