# Portal-Skripte Um Probleme bei Updates vorzubeugen wird der Großteil der Portal-Skripte wird verschlüsselt ausgeliefert. Nur so kann garantiert werden, dass die Skripte in den Projekten nicht angepasst werden. Durch die verschlüsselten Skripte können jedoch die Objekte und Funktionen nicht eingesehen werden. dies wird mit diesem Kapitel nachgeholt. # SqlObject() für SQL-Statements Oft müssen in den Projekten spezifische Datenbank-Abfragen zu Stammdaten erfolgen oder Daten sollen zu einem definierten Zeitpunkt in eine Datenbank-Tabelle geschrieben werden. Dies kann mit relativ viel Code über die Funktionen aus der Portal-Script API umgesetzt werden. Beim Verbindungsaufbau wird das Benutzer-Passwort häufig im Klartext hinterlegt und häufig werden bei Fehlern die Datenbank-Verbindungen nicht geschlossen. Sowohl die Invoice- als auch die Mailroom-Lösung greifen häufig lesend und schreibend auf die ausgelieferten Datenbanken zu. Um einfache SQL-Befehle auszuführen wurde eine Bibliothek mit dem **SqlObject()** erstellt. Das Objekt wird initial mit 2 Parametern aufgerufen:
**@param** | **{string}** | **sqlTable ** | **Name der Datenbank-Tabelle** |
**@param** | **{string}** | **dbConnName ** | **Name der Datenbankverbindung aus der dbConn.json** |
**@property** | **{string}** | **Error** | **Fehlermeldung in der Kontext-Sprache des aktuellen Benutzers.** |
**@property** | **{string}** | **Result** | **Wird initial auf boolean 'true' gesetzt und wird bei Fehlern auf 'false' gesetzt. Bei einem SELECT-Befehl enthält das Property das Ergebnis als Array.** |
**@property** | **{string}** | **Log** | **Enthält die kompletten Log-Informationen.** |
**@property** | **{string}** | **SQL** | **Speichert den zuletzt ausgeführten SQL-Befehl.** |
**@property** | **{string}** | **SQLWhere** | **WHERE-Bedingung für SELECT oder UPDATE-Statements. Das "WHERE" selber muss nicht angegeben werden.** |
**@property** | **{boolean}** | **ExpectHits** | **Wenn beim SELECT Statement keine Treffer ermittelt wurden, kann dies optional als Fehler interpretiert werden.** |
**@property** | **{boolean}** | **SelectDistinct** | **Beim SELECT Statement kann optional ein 'SELECT DISTINCT' ausgeführt werden.** |
**@property** | **{int}** | **SelectTop** | **Bei SELECT-Befehlen kann die Trefferanzahl optional auf die gegebene Anzahl eingeschränkt werden.** |
**@property** | **{string}** | **SQLGroupBy** | **Hier können SQL-Spalten angegeben werden, worüber ein SELECT Resultset gruppiert ausgegeben werden soll. Das "GROUP BY" muss selber nicht angegeben werden.** |
**@property** | **{string}** | **SQLOrderBy** | **Angabe von Spalten über welche die Treffer in einem SELECT sortiert ausgegeben werden.** |
**@property** | **{string}** | **SQLTable** | **Speichert den Tabellen-Namen.** |
**@property** | **{string}** | **FullTable** | **Aus dem Tabellen-Namen und den Verbindungsinformationen wird der komplette Tabellen-Name mit Datenbank und bei MS SQL auch inklusive "dbo" generiert.** **Beispiel: "MeineTabelle" -> "Datenbankname.dbo.MeineTabelle"** |
**@property** | **{string}** | **SQLType** | **Speichert den Verbindungs-Namen aus der dbConn.json.** |
**@property** | **{string}** | **ConnType** | **"odbc" oder "mysql" und für Testzwecke "oracle"** |
**@property** | **{string}** | **ConnStr** | **Verbindungsname aus der "dbConn.json"** |
**@property** | **{string}** | **SQLUser** | **Benutzer um die DB-Verbindung aufzubauen** |
**@property** | **{string}** | **DB** | **Datenbankname aus "dbConn.json"** |
**@property** | **{string}** | **DBO** | **"dbo"-Angabe (nur MS SQL) aus "dbConn.json"** |
**@property** | **{string}** | **DateFormat** | **Datumsformat für INSERT / UPDATE** |
**@property** | **{string}** | **ReturnID** | **Beim INSERT kann optional ein Rückgabewert der eingefügten Spalte zurückgegeben werden, wie zum Beispiel eine Auto-Inkrement-Spalte.** |
**@property** | **{string}** | **ReturnAutoColumn** | **Angabe einer Auto-Inkrement-Spalte, dessen Wert nach einem INSERT zurückgegeben werden soll** |
**@property** | **{string}** | **ReturnAutoColWhere** | **Bei MS SQL kann bei einem INSERT Befehl die Rückgabe-Spalte direkt im SQL-Statement angegeben werden. Bei MySQL muss nach dem INSERT ein SELECT auf die Datenbank ausgeführt werden. Für die Abfrage wird eine passende WHERE-Bedingung benötigt, welche hier angegeben werden muss.** |
**@property** | **{\*}** | **ColumnArray** | **Array aus Spalten-Objekten für SELECT / INSERT / UPDATE Befehle.** |
Es bietet sich an die UserExits in den spezifischen User-Exit-Libs für das entsprechende Modul hinzuzufügen. Für Invoice wäre dies zum Beispiel die "**Invoice\_\_UserExit\_CustomLib**".
#### BeforeEdit ```JavaScript /** Das UserExit wird vor der Bearbeitung einer Mappe direkt zu Beginn im Skript aufgerufen. * Bei einem Fehler muss "context.errorMessage" in der Funktion gesetzt werden! * @return {boolean} true / false. **/ function ue_BeforeEdit_Start(){ //context.errorMessage = "..."; return true; } /** Das UserExit wird vor der Bearbeitung einer Mappe am Ende des Skripts aufgerufen. * Bei einem Fehler muss "context.errorMessage" in der Funktion gesetzt werden! * @return {boolean} true / false. **/ function ue_BeforeEdit_End(){ //context.errorMessage = "..."; return true; } ``` #### OnSave ```JavaScript /** Das UserExit wird beim Speichern einer Mappe direkt zu Beginn im Skript aufgerufen. * Bei einem Fehler muss "context.errorMessage" in der Funktion gesetzt werden! * @return {boolean} true / false. **/ function ue_OnSave_Start(){ //context.errorMessage = "..."; return true; } /** Das UserExit wird beim Speichern einer Mappe am Ende des Skripts aufgerufen. * Bei einem Fehler muss "context.errorMessage" in der Funktion gesetzt werden! * @return {boolean} true / false. **/ function ue_OnSave_End(){ //context.errorMessage = "..."; return true; } /** Es gibt ein extra UserExit für Gentable-Operationen. * Der Vorteil hiervon ist, dass das Gentable nicht noch einmal ausgelesen und geschrieben werden muss. * Das erfolgt automatisch im Anschluss. * Bei einem Fehler muss in "this.Error" die Fehlermeldung geschrieben werden und "this.Error" muss auf false gesetzt werden! * @return Kein Rückgabewert erforderlich! **/ Gentable.prototype.ue_InvoiceOnSave = function(){ this.Error = ""; this.Result = true; } Gentable.prototype.ue_ProcurementOnSave = function(){ this.Error = ""; this.Result = true; } ``` #### DecreaseFieldRights ```JavaScript /** Im DecreaseFieldRightsOnFileView-Skript wird beim Durchlauf der Felder für jedes Feld dieses User Exit ausgeführt * @param {string} fieldName Aktueller technischer Feldname * @param {number} e Nummer für enumval * @param {Log} log Optionales Logging-Objekt (nur wenn das Logging aktiv ist) **/ function ue_ChangeFieldRights(fieldName, e, log){ enumval[e] = "-rw"; } ``` #### Aufzählungs-Skripte Invoice Auf einigen Aufzählungs-Feldern liegen Aufzählungs-Skripte und diese enthalten ebenfalls User-Exit-Funktionen zur Manipulation der Werte. ```JavaScript /** Bei der Neu-Erstellung des PropCaches für den Mandant können die Daten manipuliert werden (propCache.InvoicePrincipal). * Feld: Principal * @param {*} principalObject Eingehendes Objekt * @param {string} template Modul (Invoice/Mailroom/Procurement) * @param {Log} log Logging Objekt * @returns {*} Manipuliertes Objekt **/ function ue_AdjustEnumPrincipals(principalObject, template, log){ return principalObject; } function ue_AdjustEnumPrincipalsPropCache_Invoice(principalObject, log){ return principalObject; } /** Am Ende des Skripts kann das enumval für den Mandanten manipuliert werden. * Feld: Principal * @param {*} enumval Eingehendes enumval * @returns {*} Manipuliertes enumval **/ function ue_Adjust_Invoice_Enum_Principals(enumval){ return enumval; } /** Bei der Neu-Erstellung des PropCaches für den Buchungskreis können die Daten manipuliert werden (propCache.InvoicePrincipal). * Feld: CompanyCode * @param {*} companyCodeObject Eingehendes Objekt * @param {string} template Modul (Invoice/Mailroom/Procurement) * @param {Log} log Logging Objekt * @returns {*} Manipuliertes Objekt **/ function ue_AdjustEnumCompanyCodes(companyCodeObject, template, log){ return companyCodeObject; } function ue_AdjustEnumPrincipalsPropCache_Invoice(companyCodeObject, log){ return companyCodeObject; } /** Am Ende des Skripts kann das enumval für den Buchungskreis manipuliert werden. * Feld: CompanyCode * @param {*} enumval Eingehendes enumval * @returns {*} Manipuliertes enumval **/ function ue_Adjust_Invoice_Enum_CompanyCodes(enumval){ return enumval; } /** Über die Funktion können neben Rechnung und Gutschrift weitere Rechnungstypen definiert werden. * Feld: InvoiceCreditVoucher * Hierfür kann direkt auf das enumal zugegriffen werden. * @returns {*} Kein Rückgabewert! **/ function ue_AddInvoiceTypes(){ enumval.push("myinvtype;pf:MyInvoiceType"); } /** Über die Funktion können zusätzliche Zahlstatus definiert werden. * Feld: PaymentStatus * Hierfür kann direkt auf das enumal zugegriffen werden. * @returns {*} Kein Rückgabewert! **/ function ue_AddPaymentStatus(){ enumval.push("mypaytype;pf:MyPaymentType"); } ``` #### Beim Aussteuern ```JavaScript /** Das User-Exit wird direkt zu Beginn im Skript "DEXPRO_Action_Disqualify" ausgeführt. * @param {Log} log Logging-Objekt * @param {string} comment Pflicht-Kommentar des Benutzers * @returns {boolean} true / false (Ausgabe Fehlermeldung) **/ function ue_Disqualify_Start(log, comment){ // context.errorMessage = ""; return true; } /** Das User-Exit archiviert den Vorgang. * @param {string} type Bei ausgesteuerten Belegen immer "Disqualify" * @param {DocFile} docFile Auszusteuerndes DocFile-Objekt * @param {Log} log Logging-Objekt * @returns {boolean} true (Archiviert) / false (Ausgabe Fehlermeldung) **/ function ue_Archiving(type, docFile, log){ // context.errorMessage = ""; return true; } /** Das User-Exit wird nach erfolgreicher Archivierung ausgeführt. * @param {Log} log Logging-Objekt * @param {string} comment Pflicht-Kommentar des Benutzers * @returns {boolean} true / false (Ausgabe Fehlermeldung) **/ function ue_Disqualify_AfterArchiving(log, comment){ // context.errorMessage = ""; return true; } /** Das User-Exit wird nach erfolgreichem Löschen der Mappe ausgeführt. * @param {Log} log Logging-Objekt * @param {string} comment Pflicht-Kommentar des Benutzers * @returns {boolean} true / false (Ausgabe Fehlermeldung) **/ function ue_Disqualify_AfterDelete(log, comment){ // context.errorMessage = ""; return true; } ``` # Versteckte User-Exits Workflow Einige Skripte enthalten User-Exit Funktionen und viele dieser Funktionen sind in den verschiedenen "UserExit"-Bibliotheken enthalten. Wenn neue Funktionen hinzukommen müssten diese Bibliotheken allerdings immer manuell um diese Funktionen erweitert werden. Daher werden neue hinzugefügte UserExit Funktionen nur aufgerufen, wenn Sie als Funktion existieren. Die Funktionen können bei Bedarf in einer UserExit-Bibliothek (zum Beispiel "DEXPRO\_\_UserExit\_CustomLib") hinzugefügt werden. Die Liste bezieht sich jeweils auf die aktuellste Version. Ggf. sind Funktionen in älteren Versionen nicht verfügbar. #### Bei der Initialisierung ```JavaScript /** Für initiale projektspezifische Ausführungen. * Die Modul-spezifischen Funktionen werden nur beim spezifischen Mappentypen aufgerufen, wenn die Funktionen existieren. * "ue_Initialization()" wird nachfolgend aufgerufen. * @portalscript: DEXPRO_WF_Initialization * @param {Log} log Logging-Objekt * @returns {boolean} true/false **/ function ue_Initialization(log){ ; } function ue_Initialization_Invoice(log){ ; } function ue_Initialization_Mailroom(log){ ; } function ue_Initialization_Procurement(log){ ; } /** Für initiale projektspezifische Anpassungen am Gentable (nur Invoice). * @portalscript: DEXPRO_WF_Initialization * @param {Log} log Logging-Objekt * @returns {boolean} true/false **/ Gentable.prototype.ue_Initialization_Invoice = function(log){ ; } /** Wird bei der Erstellung einer initialen Gentable-Zeile ausgeführt. * @portalscript: DEXPRO_WF_Initialization * @param {string} optExecType Optionale Ausführungs-Info ("Initialization"/"ReplacePositionsByOrder") * @returns Kein Rückgabewert! **/ Gentable.prototype.createInitialRow = function(optExecType){ ; } /** Direkt zu Beginn des Workflows kann über den Parameter "Workflow_InitialDelay" * eine Workflow-Verzögerung angesteuert werden. * Nur wenn die Verzögerung angesteuert wird, dann wird vorab dieses User-Exit ausgeführt. * Die Funktion wird lediglich ausgeführt. * @portalscript: DEXPRO_WF_CheckInitDelay * @returns Kein Rückgabewert! **/ function ue_BeforeInitialDelay(){ ; } /** Zu Beginn des Workflows wird das initiale Skript ausgeführt und im Anschluss werden die Workflow-Regeln ermittelt. * In speziellen Fällen wie bei der Erstellung von Split-Mappen kann es gewünscht sein, dass diese Aktionen übersprungen werden sollen. * @portalscript: DEXPRO_WF_InitAbbreviation * @param {Log} log Logging-Objekt * @returns {boolean} true (Abkürzung nehmen) / false **/ function ue_InitAbbreviation(log){ log.info("["+context.file.getid()+"] start function ue_InitAbbreviation()"); return true; } function ue_InitAbbreviation_Invoice(log){ return true; } function ue_InitAbbreviation_Mailroom(log){ return true; } function ue_InitAbbreviation_Procurement(log){ return true; } ``` #### #### Nach der Ermittlung der Workflow-Regeln ```JavaScript /** Die User-Exit-Funktion wird ausgeführt, wenn eine Aktion über die Workflow-Regeln übersprungen wird. * @portalscript: DEXPRO_WF_Rules_ActionCheck_SkipAction * @returns {boolean} true / false (führt zu einem Workflow-Fehler) **/ function ue_OnSkipAction(){ return true; } /** Nach den Workflow-Regeln wird zunächst auf eine technische Aktion und auf das Überspringen der Aktion geprüft. * Nur wenn die Aktion einer Gruppe oder einem Benutzer zugeordnet wird, wird das Skript "DEXPRO_WF_Rules_BeforeUserAction" aufgerufen. * In dem Skript werden vor allem Zugriffsberechtigungen für die ermittelte Gruppe bzw. für den ermittelten Benutzer gesetzt. * Über die Workflow-Regeln wurde bereits der Benutzer bzw. die Gruppe zugeordnet. * Bei einem Split wird das Skript für jede einzelne Split-Mappe ausgeführt! * @portalscript: DEXPRO_WF_Rules_BeforeUserAction * @returns {boolean} true / false (führt zu einem Workflow-Fehler) **/ function ue_BeforeUserAction(){ return true; } ``` #### Bei der Benutzer-Aktion ```JavaScript /** Das UserExit wird bei Übernahme einer Mappe aus einem Gruppenkorb aufgerufen. * Der Aufruf erfolgt über das Skript "DEXPRO_WF_CheckDataForwardUser". * In dem Skript wird zuvor lediglich der aktuelle Benutzer in das Feld "ActionUser" geschrieben. * Bei einem Fehler muss die Fehlermeldung zurückgegeben werden - andernfalls ein Leerstring! * @portalscript: DEXPRO_WF_CheckDataForwardUser * @return {string} ""/ Fehlermeldung. **/ function ue_CheckDataForwardUser(){ return ""; } /** Das UserExit wird beim Zurücklegen in den Gruppenkorb aufgerufen. * Der Aufruf erfolgt über das Skript "DEXPRO_WF_CheckDataBackAccessProfile". * Bei einem Fehler muss die Fehlermeldung zurückgegeben werden - andernfalls ein Leerstring! * @portalscript: DEXPRO_WF_CheckDataBackAccessProfile * @return {string} ""/ error-message. **/ function ue_CheckDataBackAccessProfile(){ return ""; } ``` #### #### Am Aktions-Ende ```JavaScript /** Die Funktionen werden bei Abschluss der Aktion zu Beginn bzw. am Ende ausgeführt. * @portalscript: DEXPRO_WF_CheckActionEnd * @returns {string} Leerstring oder Fehlermeldung **/ function ue_OnActionEnd_Start(){ return ""; } function ue_OnActionEnd_End(){ return ""; } /** Wird am Ende einer Workflow-Aktion ausgeführt. * Hierdurch muss das Gentable nicht mehrfach ausgelesen und geschrieben werden. * Das Gentable wird im Anschluss im aufrufenden Skript in das Gentable-Feld zurück geschrieben. * @portalscript: DEXPRO_WF_CheckActionEnd * @returns Kein Rückgabewert benötigt. Fehler werden über this.Result und this.Error gesteuert. **/ Gentable.prototype.ue_OnActionEnd = function(optExecType){ this.Error = ""; this.Result = true; } /** Nach Abschluss einer Aktion kann eine Verzögerung angesteuert werden. * Dadurch kann der Anwender ggf. schneller weiter arbeiten. * @portalscript: DEXPRO_WF_CheckDelayAfterAction * @returns {boolean} true/false **/ function ue_DelayAtWorkflowActionEnd(){ return true; } ``` #### Am Workflow-Ende ```JavaScript /** Am Workflow-Ende sollte jeder Vorgang archiviert sein und Rechnungen sollten zum Beispiel den Status "gebucht" haben. * Diese User-Exit Funktion wird standardmäßig in der "DEXPRO__USerExit_WorkflowLib" ausgeliefert. * Die Prüfungen können beliebig angepasst werden. * @portalscript: DEXPRO_WF_CheckDataAtTheEndOfTheWorkflow * @return {boolean} true / false. **/ function ue_CheckFileDataAtTheEndOfTheWorkflow(){ return true; } /** Am Workflow-Ende kann eine Mappe gelöscht ("delete") oder versiegelt ("seal") werden oder es passiert nichts mit dem Beleg. * Die Information wird in der WEB-Konfiguration zur Workflow ID angegeben und kann über dieses UserExit pro Mappe manipuliert werden. * @portalscript: DEXPRO_WF_End_Seal / DEXPRO_WF_End_Delete * @param {string} wfEndType Ermittelte Angabe über die WEB-Konfiguration. * @return {string} "delete" / "seal" / "". **/ function ue_ManipulateWorkflowEndType(wfEndType){ return wfEndType; } ``` #### Beim Zurücksenden ```JavaScript /** Das UserExit wird beim Zurücksenden über das Skript "DEXPRO_WF_CheckSendBack" aufgerufen. * In neueren Versionen wurden UserExit-Funktionen pro Modul ergänzt. * portalscript: DEXPRO_Action_SendBack / DEXPRO_Action_SendBack_EventReport / Procurement_Action_SendBackRequester * @return {boolean} true / false (Fehler). **/ function ue_BeforeSendBack(){ // context.errorMessage = "Fehlermeldung"; return true; } /** Nur: DEXPRO_Action_SendBack_EventReport **/ function ue_BeforeSendBack_Invoice(){ return true; } function ue_BeforeSendBack_Mailroom(){ return true; } function ue_BeforeSendBack_Procurement(){ return true; } /** Das UserExit wird beim Zurücksenden über das Skript "DEXPRO_WF_CheckSendBack" aufgerufen. * @portalscript: DEXPRO_WF_CheckSendBack * @return {string} Leerstring/ Fehlermeldung. **/ function ue_OnSendBack(){ return ""; } ``` # Versteckte User-Exits Workflow-Regeln Bei der Ermittlung der Workflow-Regeln kann an bestimmten Punkten eingegriffen werden. ```JavaScript /** Über dieses UserExit kann der Name der Log-Datei angepasst werden. * @param {string} logName Aktueller Dateiname * @param {string} fileId Aktuelle Mappen-ID * @param {string} fileTemplate Mappentyp der aktuellen Mappe * @returns {string} Neuer Name für die Log-Datei **/ function ue_ChangeRulesLogName(logName, fileId, fileTemplate){ return logName + "_" + fileId + "_" + fileTemplate; } /** Über dieses UserExit kann der Zusatz zum Log-Dateinamen angepasst werden. * In der Regel wird ein täglicher oder monatlicher Zeitstempel angefügt, damit die einzelnen Logdateien nicht zu groß werden. * Der zurückgegebene String wird einfach zum Dateinamen angefügt, falls es nicht einer dieser speziellen Rückgabewerte ist: * "year"/"yyyy"/"y" fügt das Aktuelle Jahr hinzu * "month"/"mm"/"m" fügt Jahr und Monat hinzu * "date"/"day"/"dd"/"d" fügt Jahr, Monat und Tag hinzu * "timestamp"/"ts" fügt Jahr, Monat, Tag und Uhrzeit hinzu * @param {string} logName Incoming log-name * @param {string} fileId Aktuelle Mappen-ID * @param {string} fileTemplate Mappentyp der aktuellen Mappe * @returns {string} new log add **/ function ue_ChangeRulesLogAdd(logName, fileId, fileTemplate){ return ""; } /** Das User-Exit wird bei den Workflow-Regeln beim Auslesen des Gentable aufgerufen. * Über die Funktion können Sonderzeichen in String-Werten ersetzt werden. * Die Funktion wird ausschließlich für String-Werte aufgerufen. * @param {string} fieldValue Feldwert * @returns {string} Angepasster String-Wert **/ function ue_FieldConfParseGentableStringValues(fieldValue){ return fieldValue.replace(/\u001e/g, "").replace(/\u001c/g, ""); } /** Das UserExit wird direkt nach der Ermittlung der Workflow-Regeln aufgerufen. * Der Aufruf erfolgt über das Skript "DEXPRO_WF_Rules_DeterminationUser". * Über die Funktion können die ermittelten Regeln manipuliert werden. * Der String-Wert 'rulesJSONStr' wird im Anschluss in das Feld 'RulesJSON' geschrieben. * @param {string} rulesJSONStr Das Ergebnis der Workflow-Regeln als String. * @param {string}workflowid Aktuelle Workflow ID. * @param {string} actionid Aktuelle Aktion ID. * @param {string} techAction Angabe, ob es sich um eine technische Aktion handelt. * @return {string} Angepasste Workflow-Regeln als JSON-string. **/ function ue_CheckWorkflowRules(rulesJSONStr, workflowid, actionid, techAction){ var rulesObj = JSON.parse(rulesJSONStr); /** An dieser Stelle kann das Objekt manipuliert werden.... **/ return JSON.stringify(rulesObj); } /** Die User-Exit-Funktion wird ausgeführt, wenn eine Aktion über die Workflow-Regeln übersprungen wird. * @returns {boolean} true / false (führt zu einem Workflow-Fehler) **/ function ue_OnSkipAction(){ return true; } ``` # Versteckte User-Exits: Workflow-Steuerung Über die Aktions-Konfiguration kann der Anwender definieren ob sich ein Beleg im Ansichtmodus öffnet oder ob der Beleg direkt im Bearbeitungs-Modus geöffnet wird; ob die Mailversendung unterdrückt werden soll und ob die Ablage im Eingangs-Ordner des Benutzers erfolgen soll. Bei Gruppen kann zusätzlich konfiguriert werden ob die Gruppe als Ganzes die Mappe sperrt oder ob die Gruppe aufgelöst wird und die einzelnen Benutzer die Mappe sperren. Zudem kann konfiguriert werden, ob alle Mitglieder der Gruppe oder nur nur ein Mitglied der Gruppe die Aufgabe abschließen muss. Innerhalb des Prozesses kann es projektspezifisch Teilprozesse geben, bei denen die Konfiguration anders laufen soll. Bei einer über den Workflow gesteuerten Rückfrage (Parameter "**Ask\_User\_Type**") soll die Mappe zum Beispiel in den Posteingang des Benutzers abgelegt werden - unabhängig von der aktuellen Workflow-Konfiguration. Zudem soll sich der Beleg nicht direkt im Bearbeitungs-Modus öffnen. Dieses Verhalten wird erst ab der Invoice-Version 1.1.000 unterstützt. Bei anderen projektspezifischen Weiterleitungen kann es ebenfalls gewünscht sein, dass die Aktions-Einstellungen zur Standard-Konfiguration der Workflow-Aktion abweichen. Auch bei der Standard-Zuordnung kann es evtl. gewünscht sein, dass die Konfiguration abhängig von Feldwerten oder abhängig vom Benutzer oder der Gruppe gewählt wird. Um dies zu ermöglichen wurden optionale User-Exit-Funktionen in das Workflow-Routing eingebaut. Technisch werden die Entscheidungen jeweils über 0 und 1 geprüft. Eine Mappe kann nicht im Postingang abgelegt werden (1) oder doch (0) und Sie kann im Bearbeitungs-Modus geönnet werden (1) oder eben nicht (0) und die Mail-Benachrichtigung kann unterdrückt werden (1) oder nicht (0). Hieraus ergeben sich Zahlen-Kombinationen aus 0 und 1. ##### Zuordnung Zugriffsprofil Bei den Zugriffsprofilen wird zuerst geprüft ob die Gruppe aufgelöst werden soll oder nicht. Im Skript ist auch eine Überprüfung der asynchronen Ausführung der Aktion vorgesehen - wird aber bislang nicht weiter berücksichtigt. Bei allen Aktionen ist die Checkbox "Asynchrone Ausführung" deaktiviert. ```JavaScript /** Steuerung ob eine Gruppe aufgelöst werden soll oder nicht. * Erläuterung zum Code: * 1 : asynchrone Ausführung | 0 : keine asynchrone Ausführung (wird derzeit nicht ausgewertet) * 1 : Gruppe auflösen | 0 : Gruppe sperrt die Mappe * @param {string} code Eingehender Code ("00"/"01"/"10"oder"11") * @returns {string} Angepasster Code ("00"/"01"/"10"oder"11") **/ function ue_Workflow_ChangeApAsyncDissolveCode(code){ return code; } ``` Im Anschluss werden weitere Prüfungen durchgeührt. ```JavaScript /** Steuerung Routing für das Zugriffsprofil. * Der Workflow unterstützt nur die 4 angegebenen Code-Kombinationen! * Bei aufgelösten Gruppen werden nur 2 Kombinationen unterstützt ("01100" und "11100")! * Andere Kombinationen können nicht verarbeitet werden! * Erläuterung zum Code: * 1 : Öffnen im Bearbeitungs-Modus | 0 : Öffnen im Ansicht-Modus * 1 : Aktionsliste anzeigen | 0 : Aktionsliste ausblenden * 1 : Kopierliste anzeigen | 0 : Kopierliste ausblenden * 1 : Mailbenachrichtigung unterdrücken | 0 : Mailbenachrichtigung * 1 : Keine Ablage im Posteingang | 0 : Ablage im Posteingang * @param {string} code Eingehender Code ("01100"/"01111"/"11100"oder"11111") * @returns {string} Angepasster Code ("01100"/"01111"/"11100"oder"11111") **/ function ue_Workflow_ChangeApCode(code){ return code; } ``` ##### Zuordnung Benutzer Bei der Zuordnung eines Benutzers wird im Workflow zuerst geprüft ob die Mailversendung unterdrückt werden soll oder nicht. ```JavaScript /** Steuerung ob die Mailbenachrichtigung bei einem Benutzer unterdrückt werden soll oder nicht. * @param {boolean} Eingehender Wert (true:Mailversendung unterdrücken / false:Mailversendung) * @returns {boolean} Ausgehender Wert (true:Mailversendung unterdrücken / false:Mailversendung) **/ function ue_Workflow_UserSuppressMail(suppressMail){ return suppressMail; } ``` Im zweiten Schritt werden weitere Prüfungen durchgeführt. ```JavaScript /** Steuerung Routing für den Benutzer. * Der Workflow unterstützt nur die 4 angegebenen Code-Kombinationen! * Andere Kombinationen können nicht verarbeitet werden! * Erläuterung zum Code: * 1 : Öffnen im Bearbeitungs-Modus | 0 : Öffnen im Ansicht-Modus * 1 : Aktionsliste anzeigen | 0 : Aktionsliste ausblenden * 1 : Kopierliste anzeigen | 0 : Kopierliste ausblenden * 1 : Mailbenachrichtigung unterdrücken | 0 : Mailbenachrichtigung * 1 : Keine Ablage im Posteingang | 0 : Ablage im Posteingang * @param {string} code Eingehender Code ("0110"/"0111"/"1110"oder"1111") * @returns {string} Angepasster Code ("0110"/"0111"/"1110"oder"1111") **/ function ue_Workflow_ChangeUserCode(code){ return code; } ``` # Versteckte User-Exits: Navigation nach Abschluss der Aktion Bei Abschluss der aktuellen Workflow-Aktion über einen ausgehenden Kontrollfluss gesteuert werden. Der Nachteil dieser Umsetzung ist, dass der Kontrollfluss nur eine Beschriftung möglich ist. Da sich alle Workflow-Aktionen technisch im Hintergrund dieselbe Aktion teilen kann nur eine Beschriftung für den Button (pf:**WfButton\_ActionEnd**) gesetzt werden und diese Anzeige gilt dann für alle Workflow-Aktionen wo "Bearbeitung abschließen" ausgewählt wurde. Dafür greift die ausgewählte Navigation ("Mappe beibehalten", "Nächste Mappe", "Zum Eingangsordner") exakt so funktioniert wie man es vom Documents-Workflow kennt. [](https://docs.squeeze.one/uploads/images/gallery/2021-10/image-1633681732002.png) Häufig wird von den Kunden jedoch gewünscht, dass pro Workflow-Aktion eine spezifische Beschriftung angezeigt wird. Für den Abschluss bei der Validierung soll zum Beispiel "Validiert" angezeigt werden und bei Abschluss einer Freigabe soll auf dem Button "Freigeben" stehen. Das ist nur technisch nur möglich indem statt dem Kontrollfluss-Button ein Button als benutzerdefinierte Aktion angezeigt wird. Hier kann dasselbe Skript auf beliebig viele benutzerdefinierte Aktionen mit unterschiedlichen Beschriftungen gelegt werden. Es können auch projektspezifisch eigene Skripte mit Parametern verwendet werden. Bei einer benutzerdefinierte Aktion muss die Navigation als Rückgabewert über das Skript gesteuert werden. Hierfür wurde das "**NavigationReturnObject()**" erstellt. Ein großer Nachteil ist, dass das Verhalten vor allem bei der Navigation "Nächste Mappe" nicht 1:1 wie bei der Navigation über einen Kontrollfluss nachgestellt werden kann. Zum Beispiel kann bei selektierten Belegen in einem Ordner nicht automatisch einer der nächsten selektierten Belege angezeigt werden, da "context.selectedFiles" auch bei selektierten Belegen ein leeres FileResultset zurückliefert. Es kann auch nicht irgendein Beleg aus dem aktuellen Ordner angezeigt werden, da "context.folder", "context.folderName" und "context.folderFiles" keine Werte liefern. Für die Anzeige des nächsten Belegs wird der Aufgaben-Ordner des aktuellen Benutzers ermittelt und es wird aus diesem Ordner ein möglichst passender Beleg ermittelt. Diese Ermittlung kann unter Umständen lange dauern, wenn der Anwender sehr viele Belege in seinen Aufgaben hat. Projektspezifisch könnte der neu anzuzeigende Beleg zum Beispiel über einen spezifischen Filterordner ermittelt werden und der Filterordner kann anhand einiger Daten wie die Aktion-ID und ggf. weiteren Informationen wie dem Mandanten abgeleitet werden. Für solche Umsetzungen wird das NavigationReturnObject() ab der Invoice Version 1.1. um optionale UserExit-Funktion pro Navigation erweitert. Sobald eine der Funktionen definiert ist und diese Funktion den Wert true zurückliefert ersetzt diese das Navigations-Standardverhalten! Folgende UserExit-Funktionen wurden ergänzt: - **ue\_Keep()** - **ue\_Next()** - **ue\_Overview()** - **ue\_Folder()** - **ue\_Inbox()** Alle Funktionen haben denselben Aufbau - daher wird nur eine der Funktionen ein Beispiel-Skript angezeigt.Wenn eine UserExit-Funktion verwendet wird, dann wird der Standard-Code nicht ausgeführt! Die Rückgabe-Werte müssen in jedem Fall durch die UserExit-Funktion gesetzt werden!
```JavaScript /** Rückgabewert für die Navigation 'Mappe beibehalten'. * Diese Funktion zeigt ein Beispiel wie man nach der Validierung den nächsten Validierungs-Beleg anzeigen kann. * @returns {boolean} true (verwende UserExit Navigation) / false (verwerfe Rückgabewerte und ermittle nach Standard) **/ NavigationReturnObject.prototype.ue_Next(){ if( this.ActionID==="ValidationInvoice" ){ var folderIter = context.getFoldersByName("ValidationInvoice", "dynamicpublic"); if( folderIter && folderIter.size()===1 ){ var folder = folderIter.first(); var files = folder.getFiles(); /* ... nächste Mappe ermitteln. */ var showFile = ... this.ReturnType = "multipleAction"; this.ReturnVal = JSON.stringify([ { returnType : 'showFolder', returnValue : folder.id }, { returnType : 'showFile', returnValue : showFile.getid() } ]); return true; } } return false; } ``` Das NavigationReturnObject() hat Folgende Attribute:##### **Attribut** | ##### **Beschreibung** | ##### **Voreinstellung** |
**ReturnType** | Rückgabetyp (context.returnType) | "stay" |
**ReturnVal** | Rückgabewert | |
**ForwardAction** | Weiterleitungs-Art | "FinishAction"/"Forward"/"AnswerQuestion" |
**Navigation** | Navigation | "keepfile"/"next"/"overview"/"inbox"/"folder" |
**DocFile** | Aktuelle Mappe | context.file |
**DocFileID** | Documents ID der aktuellen Mappe | context.file.getid() |
**Filetype** | Mappentyp der aktuellen Mappe | context.file.getAttribute("FromTemplate") |
**ActionID** | Aktion ID der aktuellen Mappe | context.file.ActionID |
**WorkflowID** | Workflow ID der aktuellen Mappe | context.file.WorkflowID |
**SystemUser** | Aktueller Benutzer als SystemUser Objekt | context.getSystemUser() |
**MonoView** | Angabe, ob beim aktuellen Benutzer der MonoView aktiv ist (ab Invoice 1.1.015). | ""/"true" |
**Eigenschaft** | **Typ** | **Beschreibung** |
Error | String | Fehlermeldung |
Result | Boolean | Ergebnis |
DocFile | DocFile-Objekt | Aktuelle Mappe |
DocFileID | DocFile-ID | Aktuelle Mappen-ID |
Log | String | Logging-Informationen |
RightsInitial | Objekt | Objekt mit den initialen Berechtigungen |
RightsInitialStr | String | GACL-String mit den initialen Berechtigungen |
RightsWF | Objekt | Objekt mit den Workflow-Berechtigungen |
RightsWFStr | String | GACL-String mit den Workflow-Berechtigungen |