SBO_SP_TransactionNotification: Beispiele für SQL Server und HANA
Von Zain Abbas, Kodenova · Aktualisiert am
Im Folgenden sehen Sie, wie die Prozedur aufgebaut ist, dieselben Regeln einmal für SQL Server und einmal für HANA, die Objekttyp-Codes, die Sie wirklich brauchen, und die Fehler, die in bestehenden Prozeduren am häufigsten auftauchen.
Wie funktioniert SBO_SP_TransactionNotification?
Jede Mandantendatenbank wird mit der bereits angelegten Prozedur und einem leeren Block „ADD YOUR CODE HERE“ ausgeliefert. SAP B1 übergibt fünf Eingabeparameter:
| Parameter | Typ | Inhalt |
|---|---|---|
object_type | nvarchar(30) | Der Objektcode als Text: '17' für einen Kundenauftrag, '2' für einen Geschäftspartner oder der Code eines UDO |
transaction_type | nchar(1) | A Anlegen, U Ändern, D Löschen, C Stornieren, L Abschließen |
num_of_cols_in_key | int | Anzahl der Schlüsselspalten des Objekts |
list_of_key_cols_tab_del | nvarchar(255) | Namen der Schlüsselspalten, durch Tabulator getrennt (meist DocEntry, CardCode oder ItemCode) |
list_of_cols_val_tab_del | nvarchar(255) | Die Schlüsselwerte, durch Tabulator getrennt, als Text |
Am Ende gibt die Prozedur eine Zeile zurück: die Fehlernummer und die Meldung. 0 bedeutet „durchlassen“. Jede andere Zahl blockiert die Transaktion.
Aus „innerhalb derselben Transaktion“ folgen zwei Dinge. Erstens steht der neue Beleg bereits in ORDR, OINV und den Positionstabellen, wenn Ihr Code läuft, Sie können ihn also wie jede gespeicherte Zeile abfragen. Zweitens verlängert alles, was Sie in die Prozedur schreiben, jeden Speichervorgang im Mandanten, und zwar für jeden Objekttyp. Eine langsame Abfrage im Zweig für Kundenaufträge bremst nur dann nichts anderes aus, wenn der Zweig zuerst nach object_type gefiltert wird.
Was bedeuten die Objekttyp-Codes?
object_type ist derselbe Wert, den Sie in der Spalte ObjType jeder Kopftabelle sehen. Diese Codes decken die meisten Regeln ab:
| Code | Objekt | Kopftabelle |
|---|---|---|
| 2 | Geschäftspartner | OCRD |
| 4 | Artikel | OITM |
| 13 | Ausgangsrechnung | OINV |
| 14 | Ausgangsgutschrift | ORIN |
| 15 | Lieferung | ODLN |
| 17 | Kundenauftrag | ORDR |
| 18 | Eingangsrechnung | OPCH |
| 20 | Wareneingang zur Bestellung | OPDN |
| 22 | Bestellung | OPOR |
| 23 | Verkaufsangebot | OQUT |
| 24 | Zahlungseingang | ORCT |
| 30 | Journalbuchung | OJDT |
| 46 | Zahlungsausgang | OVPM |
| 59 / 60 | Wareneingang / Warenausgang | OIGN / OIGE |
| 67 | Umlagerung | OWTR |
| 112 | Entwurf (jede Belegart) | ODRF |
| 202 | Produktionsauftrag | OWOR |
Bei einem UDO ist object_type der Code, unter dem Sie das UDO registriert haben, keine Zahl. Details auf Feldebene zu den wichtigsten Tabellen finden Sie in unserer SAP-B1-Tabellenreferenz.
Wie blockieren Sie einen Kundenauftrag ohne Bestellnummer des Kunden?
Eine typische erste Regel: Der Kundenauftrag muss die Referenz des Kunden enthalten (NumAtCard, im Client als Kundenreferenznummer angezeigt, im englischen Client „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;
Die HANA-Version entsteht nicht durch Suchen und Ersetzen aus der T-SQL-Version. Vier Unterschiede sind entscheidend:
- Deklarationen stehen am Anfang. SQLScript erwartet
cnt INT;im Deklarationsteil vor der ersten Anweisung, dort, woerrorunderror_messagebereits deklariert sind. Eine Deklaration mitten im Rumpf schlägt beim Kompilieren fehl. Alternativ können Sie die Regel in einen eigenenBEGIN ... END-Block mit eigenemDECLAREeinschließen. - Kein
IF EXISTS (SELECT ...). Zählen Sie in eine Variable und prüfen Sie die Variable. - Parameter lesen Sie mit Doppelpunkt (
:object_type), zugewiesen wird mit:=. - Namen in gemischter Schreibweise in Anführungszeichen setzen.
"DocEntry"und"NumAtCard"brauchen Anführungszeichen, weil HANA Namen ohne Anführungszeichen als Großbuchstaben behandelt.
Die übrigen Syntaxänderungen (ISNULL zu IFNULL, GETDATE zu CURRENT_DATE, + zu ||) sind dieselben wie bei gespeicherten Abfragen. Unser SQL-zu-HANA-Konverter übernimmt sie für einen schnellen ersten Durchgang, und der Leitfaden zur HANA-Migration behandelt das vollständige Umschreiben der Prozedur.
Wie prüfen Sie Belegpositionen?
Prüfungen im Belegkopf sind einfach, weil der Schlüssel der Kopf ist. Für Positionen verknüpfen Sie die Positionstabelle über DocEntry. Beispiel: Eine Ausgangsrechnung wird blockiert, wenn eine Position den Preis null hat, obwohl der Artikel ein Verkaufsartikel und kein Gratisartikel ist.
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;
Nehmen Sie nach Möglichkeit die Zeilennummer in die Meldung auf ('Line ' + CAST(T1.LineNum + 1 AS NVARCHAR) auf SQL Server, 'Line ' || TO_NVARCHAR(T1."LineNum" + 1) auf HANA). LineNum beginnt bei 0, der Client zählt Zeilen ab 1, und ohne diese Angabe suchen Anwender die richtige Zeile in einer Rechnung mit 60 Positionen.
Wie machen Sie die Steuernummer für die E-Rechnung zur Pflicht?
In Projekten für ZATCA, SRI, FEL und DTE ist eine fehlende oder fehlerhafte Steuernummer des Käufers ein häufiger Grund dafür, dass eine Rechnung abgelehnt wird, nachdem sie in SAP B1 bereits gebucht ist. Den Fehler beim Geschäftspartner abzufangen, ist günstiger, als später eine abgelehnte Rechnung zu korrigieren.
-- 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
Beachten Sie, dass der Schlüssel des Geschäftspartners CardCode ist, ein String, deshalb gibt es hier keinen Cast. In HANA verwendet derselbe Block "CardCode" = :list_of_cols_val_tab_del und das Zählmuster von oben.
Formatregeln (Prüfziffern, Länge je nach Art der Kennung) sind in einem Add-on oder einem Validator vor der Datenerfassung besser aufgehoben, weil sie sich von Land zu Land unterscheiden. Um die Kunden zu finden, die heute an der Regel scheitern würden, führen Sie zuerst die Abfrage für Geschäftspartner ohne Steuernummer aus, bereinigen die Daten und schalten die Regel erst dann ein. Andernfalls wird die erste Person blockiert, die einen alten Kundenstamm bearbeitet, und zwar für etwas, das sie gar nicht verursacht hat.
Welche Fehler passieren häufig?
Regeln, die Entwürfe übergehen. Macht ein Genehmigungsverfahren aus einem Kundenauftrag einen Entwurf, speichert SAP B1 ihn als Objekttyp 112 in ODRF/DRF1, nicht als 17. Ihre Regel für 17 greift erst, wenn der Entwurf in einen echten Beleg umgewandelt wird. Oft ist das in Ordnung. Soll die Regel fehlerhafte Daten aber stoppen, bevor ein Genehmiger sie sieht, ergänzen Sie einen Zweig für 112, gefiltert auf ODRF.ObjType = '17'.
Zusätzliche Ergebnismengen zurückgeben. Auf SQL Server kann ein vergessenes SELECT oder PRINT zum Debuggen Ergebnismengen vor dem abschließenden SELECT @error, @error_message erzeugen, und SAP B1 liest dann womöglich die falsche. Halten Sie Debug-Ausgaben aus der Prozedur heraus; wenn nötig, protokollieren Sie in eine Tabelle.
Langsame Zweige. Jeder Speichervorgang im Mandanten wartet auf die Prozedur. Filtern Sie im äußeren IF nach object_type und transaction_type, verwenden Sie den Schlüssel in der WHERE-Klausel und verzichten Sie auf Cursor. Eine Regel, die OINV ohne Filter auf DocEntry durchsucht, wirkt im Testmandanten harmlos und wird in einer Datenbank mit Rechnungen aus vielen Jahren zur Qual.
Fehlercodes, die niemand zuordnen kann. Verwenden Sie ein Nummernschema, zum Beispiel Objekttyp mal 1000 plus Zähler (17001, 13001, 2001). Schickt ein Anwender einen Screenshot, wissen Sie sofort, welcher Zweig gegriffen hat, ohne die ganze Prozedur zu lesen.
Stornieren und Abschließen sind eigene Ereignisse. C und L kommen als eigene Transaktionstypen an. Eine Regel für A und U hindert niemanden daran, einen Kundenauftrag abzuschließen. Wenn Sie das Abschließen blockieren müssen (etwa weil noch offene Lieferungen verknüpft sind), behandeln Sie L ausdrücklich.
Dinge blockieren, die SAP B1 im Hintergrund aktualisiert. Manche Objekte werden als Nebeneffekt einer anderen Transaktion geändert (die Bestandsfelder eines Artikels beim Buchen eines Belegs, der Saldo eines Geschäftspartners). Eine strenge Regel auf U für Objekt 4 oder 2 kann deshalb einen Verkaufsbeleg blockieren, nur wegen einer solchen Nebenänderung. Halten Sie Änderungsregeln eng gefasst und testen Sie sie, indem Sie Belege buchen, nicht nur, indem Sie Stammdaten bearbeiten.
Code bei einem Neuaufbau verlieren. Die Prozedur liegt in der Mandantendatenbank. Bewahren Sie das vollständige Skript in der Versionsverwaltung auf und vergleichen Sie es nach Upgrades, Mandantenkopien und Wiederherstellungen, damit keine Regel unbemerkt verschwindet.
Und was ist mit SBO_SP_PostTransactionNotice?
Sie hat dieselben Parameter, aber SAP B1 ruft sie erst auf, nachdem die Transaktion festgeschrieben wurde. Blockieren kann sie nichts. Nutzen Sie sie für Nebenaufgaben: in eine Protokolltabelle schreiben oder den DocEntry in eine Warteschlange für eine Schnittstelle stellen (einen Versanddienst für E-Rechnungen, eine WhatsApp-Benachrichtigung, eine Synchronisation mit einem anderen System). Halten Sie auch sie schlank und rufen Sie aus ihr heraus niemals externe Dienste auf. Eine Warteschlangentabelle plus ein separater Dienst, der sie ausliest, ist das sicherere Design.
Wie sehen Add-ons und der Service Layer den Fehler?
Die Regel läuft auf Datenbankebene und gilt daher unabhängig davon, woher der Beleg kommt. Ein Add() über die DI API liefert ein Ergebnis ungleich null, und Ihre Meldung kommt über GetLastError zurück. Ein POST an den Service Layer schlägt mit einem Fehler fehl, der den Meldungstext enthält (wie Sie ihn auslesen, steht in der Anleitung zum Service Layer). Importe mit der Data Transfer Workbench scheitern Zeile für Zeile. Deshalb ist die Prozedur der richtige Ort für Regeln, die überall gelten müssen, und deshalb sollten Ihre Meldungen auch für jemanden verständlich sein, der ein Schnittstellenprotokoll liest, nicht nur für einen Anwender im Client. Zur Fehlerbehandlung auf Seiten der DI API lesen Sie unseren Leitfaden zu DI-API-Fehlercodes.
Sie brauchen solche Regeln, geschrieben und getestet für SQL Server und HANA? Das gehört zu unseren Leistungen in der Entwicklung für SAP Business One.
Quellen
- SAP Community: Implementing SBO_SP_TransactionNotification of SAP Business One, version for SAP HANA (2013)
- SAP Help Portal: SAP Business One
Passende Leistungen
Häufige Fragen
Wo finde ich SBO_SP_TransactionNotification?
Auf SQL Server liegt sie in der Mandantendatenbank unter Programmierbarkeit > Gespeicherte Prozeduren. Auf HANA finden Sie sie im Schema des Mandanten unter Procedures. Bearbeiten Sie sie mit SSMS oder SAP HANA Studio bzw. dem Database Explorer und testen Sie zuerst in einer Kopie des Mandanten.
Kann ich mehrere Regeln für dasselbe Objekt anlegen?
Ja. Fügen Sie mehrere IF-Blöcke im selben Zweig hinzu. Prüfen Sie error, bevor der nächste Block läuft, sonst überschreibt die letzte fehlschlagende Regel die erste Meldung.
Ist TransactionNotification dasselbe wie ein Genehmigungsverfahren?
Nein. Ein Genehmigungsverfahren leitet einen Beleg zur Entscheidung an eine Person weiter und lässt ihn nach der Freigabe durch. TransactionNotification weist den Beleg einfach so lange zurück, bis die Daten stimmen. Nutzen Sie Genehmigungen für Ermessensentscheidungen und die Prozedur für feste Regeln.
Bereit für den Start?
Sagen Sie uns, was Sie brauchen. Sie erhalten eine klare technische Antwort, einen festen Leistungsumfang und einen Preis, in der Regel innerhalb von 24 Stunden.
