Direkt zum Hauptinhalt

Registrierung eines serverseitigen Plug-in-Schritts

Schema-ID: plugin-step@1 Maschinenlesbares Schema: plugin-step.schema.json

Der maßgebliche Registrierungsrahmen für einen serverseitigen Plug-in-Schritt (Abschnitt 6.6) – serverseitige Logik, die ausgeführt wird, wenn ein Datensatz einer Tabelle erstellt, aktualisiert oder gelöscht wird. Der Rahmen legt fest, wann und wie die Logik ausgeführt wird; eine BS-Lang-Implementierung enthält zusätzlich das Skript inline, das neben diesem Schema auch anhand der BS-Lang-Grammatik validiert wird. Eine Registrierung wird identisch validiert, ob sie aus einem Designer, einem API-Aufruf, einem KI-Assistenten oder einem Lösungsimport stammt.

Aufbau​

FeldTypPflichtHinweise
logicalNamestringjaMit Herausgeberpräfix, Snake-Case in Kleinbuchstaben.
displayNamestringjaLesbarer Name des Schritts.
publisherPrefixstringjaDas Präfix des besitzenden Herausgebers, mit dem logicalName benannt sein muss – nicht das der überwachten Tabelle. Eine Standardtabelle wie account hat bewusst kein Präfix, und die Validierung dagegen machte es unmöglich, einen Schritt für eine solche Tabelle zu benennen.
entitystringjaDie Tabelle, deren Schreibvorgänge den Schritt auslösen.
messagestringjaCreate, Update oder Delete.
stagestringjaPreValidation, PreOperation oder PostOperation.
executionModestringjaSynchronous (inline, innerhalb des Schreibvorgangs) oder Asynchronous (über den Outbox eingereiht und nach dem Commit des Schreibvorgangs ausgeführt). Ein asynchroner Schritt muss PostOperation sein: Er läuft, nachdem der Datensatz bereits gespeichert ist, hat also nichts mehr abzulehnen.
rankintegerjaAusführungsreihenfolge unter den Schritten mit derselben Tabelle + Nachricht + Phase; niedrigere Werte laufen zuerst.
isEnabledbooleanjaEin deaktivierter Schritt wird gespeichert, aber nie ausgeführt.
implementationobjectjaGenau eine Implementierung (siehe unten).

Implementierung​

FeldTypPflichtHinweise
typestringjaBsLangScript oder DotNetAssembly.
scriptstringbei BsLangScriptDer BS-Lang-Quelltext.
assemblyReferencestringbei DotNetAssemblyDer Speicherschlüssel der hochgeladenen Assembly.
typeNamestringbei DotNetAssemblyDer zu instanziierende Plug-in-Typ.

Phasen​

  • PreValidation – läuft vor der Operation; grobe Prüfungen von Form und Sicherheit.
  • PreOperation – läuft unmittelbar vor dem Schreibvorgang; kann Feldwerte des Datensatzes ändern oder die gesamte Operation ablehnen.
  • PostOperation – läuft unmittelbar nach dem Schreibvorgang; für kaskadierende oder abhängige Änderungen.

Ein Schritt lehnt eine Operation ab, indem er throwValidationError("reason") aufruft; das stoppt den Schreibvorgang und gibt den Grund an den Aufrufer zurück.

Beispiel​

{
"logicalName": "par_invoice_reject_negative",
"displayName": "Reject negative invoice total",
"entity": "par_invoice",
"message": "Create",
"stage": "PreOperation",
"executionMode": "Synchronous",
"rank": 1,
"isEnabled": true,
"implementation": {
"type": "BsLangScript",
"script": "if (getValue(\"par_total\") < 0) { throwValidationError(\"Total cannot be negative.\"); }"
}
}

Hostfunktionen für BS-Lang-Skripte​

Ein BsLangScript-Schritt liest und ändert den Datensatz über diese Hostfunktionen:

FunktionZweck
getValue("field")Der aktuelle Wert eines Felds des Datensatzes, der geschrieben wird.
setValue("field", value)Ändert den Wert eines Felds (wird nur in PreOperation berücksichtigt).
getPreValue("field")Der vorherige Wert des Felds (bei Aktualisierungen und Löschungen).
throwValidationError("message")Lehnt die Operation ab und macht sie rückgängig.
message() / stage() / entityName()Die aktuelle Operation, Phase und Tabelle.
userId() / depth()Der auslösende Benutzer und die Rekursionstiefe.

Andere Tabellen erreichen​

Die obigen Funktionen sehen nur den Datensatz, der geschrieben wird. Die folgenden erreichen den Rest der Umgebung, und jede von ihnen läuft als der Benutzer, der den Schritt ausgelöst hat – ein Skript ist kein Weg, eine Berechtigung zu umgehen, und ein Schritt, dessen Aufrufer eine Tabelle nicht lesen darf, erhält daraus nichts zurück.

FunktionZweck
getRecord("table", id)Ein Datensatz als Objekt oder leer, wenn er nicht vorhanden ist. .id ist darauf enthalten.
queryRecords("table", "field", value)Die Zeilen, in denen ein Feld einem Wert entspricht, als Liste von Objekten. Begrenzt auf 200.
runQuery({ … })Führt ein vollständiges parsware-query@1-Dokument aus – verschachtelte UND/ODER-Filter mit zehn Operatoren, Joins, Sortierungen, Seitenaufteilung oder ein aggregate, das Gruppen liefert. Dieselbe Abfragesprache, die die erweiterte Suche erzeugt und eine gespeicherte Abfrage speichert. Zeilen sind auf 200 begrenzt; ein Aggregat nicht. Eine per Join eingebundene Spalte wird unter ihrem Alias gelesen (row.acct.par_industry), und das 'from' einer Verknüpfung muss in Anführungszeichen stehen, weil from ein BS-Lang-Schlüsselwort ist.
runSavedQuery("name")Führt eine gespeicherte Abfrage per logischem Namen aus. Die Komplexität liegt in einem Designer und reist in der Lösung mit, sodass das Skript nur die Frage benennt.
countRecords("table", "field", value)Wie viele es gibt, ohne sie abzurufen – ein echtes COUNT, also auch jenseits der 200-Zeilen-Grenze von queryRecords korrekt.
createRecord("table", { field: value, … })Erstellt einen Datensatz und liefert seine ID.
updateRecord("table", id, { field: value, … })Ändert die genannten Felder, und nur diese.
deleteRecord("table", id)Löscht einen Datensatz.

Ein auf diese Weise ausgeführter Schreibvorgang durchläuft die gewöhnlichen Regeln, sodass ein für die beschriebene Tabelle registrierter Schritt seinerseits ausgeführt wird. Diese Verschachtelung ist auf eine Tiefe von acht begrenzt, und depth() teilt einem Skript mit, wo es sich befindet – aber die Grenze ist eine Absicherung, kein Entwurfsmittel. Ein Schritt, der in die Tabelle schreibt, die er überwacht, löst sich selbst aus; die Lösung ist eine Bedingung oder eine andere Tabelle.

Gemeinsame Logik importieren​

Das Skript eines Schritts kann ein Lösungsskript per Name mit import einbinden, genau wie eine clientseitige Geschäftsregel, sodass eine Regel, die mehrere Schritte durchsetzen, nur einmal geschrieben wird:

import 'par_approval_rules';

if (getValue('par_total') > par_approval_rules.limit()) { … }

Beide Anführungszeichenstile funktionieren. Das Modul muss ein Lösungsskript in derselben Umgebung sein; alles andere schlägt beim Speichern des Schritts fehl, nicht erst bei seiner Ausführung.