Avvii un progetto

SBO_SP_TransactionNotification: esempi per SQL Server e HANA

By Zain Abbas, Kodenova · Updated

Short answerSBO_SP_TransactionNotification è una stored procedure che SAP Business One richiama all'interno della stessa transazione di database ogni volta che un oggetto viene aggiunto, aggiornato, eliminato, annullato o chiuso. Se il suo codice imposta error su un valore diverso da 0, SAP B1 annulla la transazione e mostra all'utente il suo error_message. È il modo più semplice per imporre una regola aziendale su tutti i canali contemporaneamente: il client B1, gli add-on con DI API, il Service Layer e le importazioni di dati.

Di seguito vedrà come è collegata la procedura, le stesse regole scritte per SQL Server e per HANA, i codici del tipo di oggetto che servono davvero e gli errori che compaiono più spesso nelle procedure esistenti.

Come funziona SBO_SP_TransactionNotification?

Ogni database aziendale contiene già la procedura, con un blocco vuoto “ADD YOUR CODE HERE”. SAP B1 passa cinque parametri di input:

ParametroTipoCosa contiene
object_typenvarchar(30)Il codice dell’oggetto come testo: '17' per un ordine cliente, '2' per un business partner, oppure il codice di un UDO
transaction_typenchar(1)A aggiunta, U aggiornamento, D eliminazione, C annullamento, L chiusura
num_of_cols_in_keyintQuante colonne chiave ha l’oggetto
list_of_key_cols_tab_delnvarchar(255)I nomi delle colonne chiave, separati da tabulazione (di solito DocEntry, CardCode o ItemCode)
list_of_cols_val_tab_delnvarchar(255)I valori chiave, separati da tabulazione, come testo

Alla fine la procedura restituisce una riga: il numero di errore e il messaggio. 0 significa “lascia passare”. Qualsiasi altro numero blocca la transazione.

Dal fatto che la procedura giri “nella stessa transazione” derivano due conseguenze. Primo: quando il suo codice viene eseguito, il nuovo documento si trova già in ORDR, OINV e nelle tabelle di riga, quindi può interrogarlo come qualsiasi riga già registrata. Secondo: tutto ciò che inserisce nella procedura allunga ogni salvataggio della società, per ogni tipo di oggetto. Una query lenta nel ramo degli ordini cliente non rallenta nient’altro solo se il ramo viene prima filtrato per object_type.

Che cosa significano i codici del tipo di oggetto?

object_type è lo stesso valore che compare nella colonna ObjType di ogni tabella di testata. Quelli che coprono la maggior parte delle regole:

CodiceOggettoTabella di testata
2Business partnerOCRD
4ArticoloOITM
13Fattura clientiOINV
14Nota di credito clientiORIN
15ConsegnaODLN
17Ordine clienteORDR
18Fattura fornitoriOPCH
20Entrata merci da ordine d’acquistoOPDN
22Ordine d’acquistoOPOR
23Offerta di venditaOQUT
24IncassoORCT
30Registrazione contabileOJDT
46Pagamento in uscitaOVPM
59 / 60Entrata merci / uscita merciOIGN / OIGE
67Trasferimento di magazzinoOWTR
112Bozza (qualsiasi tipo di documento)ODRF
202Ordine di produzioneOWOR

Per un UDO, object_type è il codice UDO che ha registrato, non un numero. I dettagli a livello di campo per le tabelle principali sono nel nostro riferimento alle tabelle SAP B1.

Come si blocca un ordine cliente senza il numero d’ordine del cliente?

Una tipica prima regola: l’ordine cliente deve riportare il riferimento del cliente (NumAtCard, mostrato nel client come “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 versione HANA non è un semplice “trova e sostituisci” di quella T-SQL. Contano quattro differenze:

  1. Le dichiarazioni vanno all’inizio. SQLScript richiede cnt INT; nella sezione delle dichiarazioni, prima della prima istruzione, dove sono già dichiarati error ed error_message. Una dichiarazione a metà del corpo genera un errore di compilazione. Se preferisce, racchiuda la regola in un proprio blocco BEGIN ... END con il suo DECLARE.
  2. Niente IF EXISTS (SELECT ...). Conti in una variabile e verifichi la variabile.
  3. I parametri si leggono con i due punti (:object_type) e si assegnano con :=.
  4. I nomi con maiuscole e minuscole vanno tra virgolette. "DocEntry" e "NumAtCard" devono essere racchiusi tra virgolette, perché HANA tratta i nomi senza virgolette come maiuscoli.

Le altre sostituzioni di sintassi (da ISNULL a IFNULL, da GETDATE a CURRENT_DATE, da + a ||) sono le stesse che si fanno nelle query salvate. Il nostro convertitore da SQL a HANA le gestisce per un primo passaggio rapido, e la guida alla migrazione a HANA copre la riscrittura completa della procedura.

Come si convalidano le righe del documento?

I controlli sulla testata sono semplici, perché la chiave è la testata. Per le righe, unisca la tabella di riga su DocEntry. Esempio: bloccare una fattura clienti che ha una riga con prezzo zero per un articolo destinato alla vendita, non omaggio.

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;

Quando possibile, inserisca il numero di riga nel messaggio ('Line ' + CAST(T1.LineNum + 1 AS NVARCHAR) su SQL Server, 'Line ' || TO_NVARCHAR(T1."LineNum" + 1) su HANA). LineNum parte da 0, il client mostra le righe a partire da 1, e altrimenti gli utenti dovranno cercare la riga in una fattura di 60 righe.

Come si rende obbligatorio l’identificativo fiscale per la fatturazione elettronica?

Nei progetti ZATCA, SRI, FEL e DTE, un identificativo fiscale dell’acquirente mancante o errato è un motivo frequente di scarto di una fattura già registrata in SAP B1. Fermare l’errore sul business partner costa meno che correggere in seguito una fattura scartata. Lo stesso vale in Italia, dove lo SdI controlla la partita IVA o il codice fiscale del cliente prima del recapito: veda SAP Business One in Italia.

-- 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

Noti che la chiave del business partner è CardCode, una stringa, quindi qui non serve alcuna conversione. In HANA lo stesso blocco usa "CardCode" = :list_of_cols_val_tab_del e lo schema con il conteggio visto sopra.

Le regole di formato (cifre di controllo, lunghezza per tipo di identificativo) è meglio gestirle in un add-on o in un validatore prima dell’inserimento dei dati, perché cambiano da paese a paese. Per trovare i clienti che oggi non supererebbero il controllo, esegua prima la query dei business partner senza identificativo fiscale, li sistemi e solo dopo attivi la regola. Altrimenti la prima persona che modifica una vecchia anagrafica cliente viene bloccata per qualcosa che non ha causato.

Quali sono gli errori più comuni?

Regole che ignorano le bozze. Se una procedura di autorizzazione trasforma un ordine cliente in bozza, SAP B1 lo salva come tipo di oggetto 112 in ODRF/DRF1, non come 17. La sua regola per 17 scatta solo quando la bozza viene convertita in un documento reale. Spesso va bene così, ma se la regola deve fermare i dati errati prima che li veda chi approva, aggiunga un ramo 112 filtrato su ODRF.ObjType = '17'.

Result set aggiuntivi. Su SQL Server, una SELECT o una PRINT dimenticata per il debug può produrre result set prima della SELECT @error, @error_message finale, e SAP B1 potrebbe leggere quello sbagliato. Tenga l’output di debug fuori dalla procedura; se proprio serve, scriva in una tabella di log.

Rami lenti. Ogni salvataggio della società attende la procedura. Filtri per object_type e transaction_type nell’IF esterno, usi la chiave nella clausola WHERE ed eviti i cursori. Una regola che scorre OINV senza il filtro su DocEntry sembrerà a posto in una società di test e diventerà pesante in un database con anni di fatture.

Codici di errore impossibili da rintracciare. Usi uno schema di numerazione, per esempio il tipo di oggetto per 1000 più un contatore (17001, 13001, 2001). Quando un utente invia uno screenshot, saprà quale ramo è scattato senza leggere l’intera procedura.

Annullamento e chiusura sono eventi distinti. C e L arrivano come tipi di transazione a sé. Una regola scritta per A e U non impedisce a nessuno di chiudere un ordine cliente. Se deve bloccare una chiusura (per esempio quando ci sono ancora consegne aperte collegate), gestisca L in modo esplicito.

Bloccare ciò che SAP B1 aggiorna in background. Alcuni oggetti vengono aggiornati come effetto collaterale di un’altra transazione (i campi di giacenza di un articolo quando si registra un documento, il saldo di un business partner). Una regola rigida su U per l’oggetto 4 o 2 può bloccare un documento di vendita a causa di un aggiornamento collaterale. Mantenga ristrette le regole sugli aggiornamenti e le provi registrando documenti, non solo modificando le anagrafiche.

Perdere il codice in una ricostruzione. La procedura risiede nel database aziendale. Conservi lo script completo in un sistema di controllo versione e lo confronti dopo aggiornamenti, copie di società e ripristini, così che nessuna regola sparisca senza che nessuno se ne accorga.

E SBO_SP_PostTransactionNotice?

Ha gli stessi parametri, ma SAP B1 la richiama dopo il commit della transazione. Non può bloccare nulla. La usi per attività accessorie: scrivere in una tabella di log o mettere il DocEntry in una coda per un’integrazione (un invio di fatture elettroniche, una notifica WhatsApp, una sincronizzazione con un altro sistema). Anche questa va mantenuta leggera, e non deve mai richiamare servizi esterni al suo interno. Una tabella di coda più un servizio separato che la legge è la soluzione più sicura.

Come vedono l’errore gli add-on e il Service Layer?

La regola gira a livello di database, quindi si applica indipendentemente dalla provenienza del documento. Un Add() della DI API restituisce un risultato diverso da zero e il suo messaggio torna tramite GetLastError. Un POST al Service Layer fallisce con un errore che contiene il testo del messaggio (veda la guida al Service Layer per sapere come leggerlo). Le importazioni con Data Transfer Workbench falliscono riga per riga. Per questo la procedura è il posto giusto per le regole che devono valere ovunque, e per questo i suoi messaggi devono essere comprensibili a chi legge il log di un’integrazione, non solo a un utente nel client. Per la gestione degli errori lato DI API, veda la nostra guida ai codici di errore della DI API.


Le servono regole come queste, scritte e testate sia su SQL Server sia su HANA? Fa parte del nostro lavoro di sviluppo SAP Business One.

Sources

Related services

FAQ

Frequently asked questions

Dove si trova SBO_SP_TransactionNotification?

Su SQL Server si trova nel database aziendale, in Programmability > Stored Procedures. Su HANA si trova nello schema aziendale, in Procedures. La modifichi con SSMS oppure con SAP HANA Studio / Database Explorer, e la provi prima in una copia della società.

Posso avere più di una regola per lo stesso oggetto?

Sì. Aggiunga più blocchi IF all'interno dello stesso ramo. Controlli error prima di eseguire il blocco successivo, altrimenti l'ultima regola non superata sovrascrive il primo messaggio.

TransactionNotification equivale a una procedura di autorizzazione?

No. Una procedura di autorizzazione invia un documento a una persona perché decida e lo lascia passare dopo l'approvazione. TransactionNotification si limita a rifiutare il documento finché i dati non sono corretti. Usi le autorizzazioni per le decisioni di merito e la procedura per le regole rigide.

Iniziamo?

Ci descriva di cosa ha bisogno. Riceverà una risposta tecnica chiara, un perimetro definito e un prezzo, di norma entro 24 ore.