Versionen im Vergleich

Schlüssel

  • Diese Zeile wurde hinzugefügt.
  • Diese Zeile wurde entfernt.
  • Formatierung wurde geändert.

Inhalt
maxLevel23

Definition

Mit clientseitigen Regel können dynamisch im Layout oder auch über das BO Groovy-Regeln hinterlegt werden. 

Konfiguration

Namensräume und Packages

Ein Namensraum wird pro Nuclet definiert. Ist ein Businessobjekt einem Nuclet zugewiesen, so muss beim Aufruf der entsprechende Namespace angegeben werden (Lokaler Identifizierer des Nuclets) . Ist ein Businessobjekt keinem Nuclet zugeordnet, so wird der Default Namespace 'DEF' verwendet.

Packages werden für die Initialisierung spezieller Bibliotheksregeln benötigt. Alles Inhalte (also Bibliotheksregeln) eines Nuclet-Packages können Spring-managed sein und so innerhalb von Nuclos spezielle Funktionalitäten nutzen und bereitstellen (z.B. aufrufbare Funktionen).


Beispiel Berechnete Werte 

Dynamische Eigenschaften (z.B. Hintergrundfarbe von Zeilendarstellungen) und berechnete Werte (z.B. Berechnungsausdruck bei Attributen) können in Groovy-Code definiert werden.


Clientregeln für berechnete Werte werden immer auf dem Zielfeld definiert. Im Businessobjekt findet sich in der Attributdefinition ein Button 'Berechnungsausdruck'. Hier wird der Groovy-Code hinterlegt.

ApplicationProgrammingInterface 1.jpg

Sie können im Code über die Variable "context" und passende Ausdrücke (im Code: context."Ausdruck" auf die Kontextinformationen und Daten des Objekts zugreifen.

Folgende Ausdrücke werden im Moment unterstützt:

(Info) [entity] steht dabei für den internen Namen des Businessobjekts wie er im BO-Editor angegen ist (nicht der Anzaigename oder Tabellenname).


Referenzierte Kontexte

Häufig wird ein Zugriff auf Werte eines referenzierten Objekts benötigt. Hierfür kann der Ausdruck #{[namespace].[entity].[field].context} verwendet werden. Dieser Ausdruck liefert ein neues Context-Objekt, mit dem Sie in identischer Weise weiterarbeiten können. Zu beachten ist, dass ein referenzierter Kontext häufig nur eingeschränkte Daten liefert. Bei Auswahlfeldern stehen z.B. nur die Werte des referenzierten Datensatzes zur Verfügung - ein erneuter Aufruf von #{[namespace].[entity]} oder #{[namespace].[entity].[field].context} ist also nicht möglich. Sollten die zur Verfügung stehenden Daten nicht ausreichen, müssen Funktionen verwendet werden.



Codeblock
#{[namespace].[entity]} // liefert das aktuelle BO oder die Datensätze eines Unterformulars als Liste
#{[namespace].[entity].[field]} // liefert den Wert eines Attributs. Wenn "id" als Feld eingesetzt wird, kann auf die intid das Datensatzes zugegriffen werden.
#{[namespace].[entity].[field].value} // gleiche Funktion wie #{[namespace].[entity].[field]}
#{[namespace].[entity].[field].id} // liefert den Id-Wert eines Referenzfelds als java.lang.Long
#{[namespace].[entity].[field].context} // liefert den Context für ein referenziertes Objekt

Auswertung des Scripts

Das Script wird ausgeführt wenn:

  • ein neuer Datensatz des BOs angelegt wird
  • sich ein Feld ändert, dass im Script ausgelesen wird

Beispiel

Hier ein Beispiel für die Berechnung eines Gesamtbetrages. Der Gesamtbetrag wird durch Iteration über ein Unterformular 'auftrag_position' ermittelt.


Codeblock
def bBetragBrutto = new java.math.BigDecimal(0.000)
def porto = context."#{WAR.auftrag.auftragPorto}"
def bKundeMitUstBerechnung = context."#{WAR.auftrag.auftragUst}"
 
context."#{WAR.auftrag_position}".each {
    item -> bBetragBrutto = bBetragBrutto.add(java.math.BigDecimal.valueOf(item."#{WAR.auftrag_position.gesamtpreisrechnung}"))
}
if (porto) {
	bPorto = new java.math.BigDecimal(porto)
	bBetragBrutto = bBetragBrutto.add(bPorto)
    if (bKundeMitUstBerechnung)
        bBetragBrutto = bBetragBrutto.add(bPorto.multiply(new java.math.BigDecimal(0.1900)))
 
}
return bBetragBrutto.setScale(4, java.math.RoundingMode.HALF_UP).doubleValue()

Ein weiteres Beispiel zum Iteration mit if - Anweisung.

Codeblock
context."#{ROG.SubF_Barbeleg}".each { 
item ->
if (item."#{ROG.SubF_Barbeleg.positionstyp}" == "Normal"){
	if (null != item."#{ROG.SubF_Barbeleg.gesamtpreis}") {
		 gesamtbetrag = gesamtbetrag.add(item."#{ROG.SubF_Barbeleg.gesamtpreis}")
	}
} 
}
return gesamtbetrag.setScale(2, java.math.RoundingMode.HALF_UP).doubleValue() 

def innenring = context."#{TVI.Auftragspositionen.auftrag.context}"."#{TVI.Auftrag.kunde.context}"."#{TVI.Kunde.innenringbedruckung}"

Hintergrundfarbe von Zeilendarstellungen

In Suchergebnislisten und Unterformularen können Zeilen farblich hinterlegt werden. Die Definition geschieht über einen Groovy-Code der pro Businessobjekt angegeben wird. Im letzten Schritt vom Businessobjekt befindet sich ein Button 'Hintergrundfarbe der Zeilendarstellung konfigurieren'. Hier wird das Skript hinterlegt.

ApplicationProgrammingInterface 2.jpgImage Removed

Die Farben werden im Return-Statement anhand von Hexadezimalen Farbcodes angegeben.

Beispiel

Nachfolgend ein Beispielcode für die Farbeneinstellungen:

Codeblock
if (context."#{WAR.auftrag.auftragGesamtbetrag}" == null) {
    return "#FFA500";
}
else {
    if (context."#{WAR.auftrag.auftragBezahlt}" < context."#{WAR.auftrag.auftragGesamtbetrag}") {
                    return "#DC143C";
                }
    else {
        return "#32CD32";
    }
}


Hintergrundfarbe von einzelnen Feldern

Die Funktion wurde implementiert unter Businessobjekt "Attribut/Allg.Eigenschaften" dort kann man das GroovySkript hinterlegen.

In Detailmasken kann das Skript "übergangen" werden, indem im Layout die Hintergrundfarbe explizit gesetzt wird. Für Suchergebnislisten oder Unterformulare wird dennoch das Skript ausgewertet.

Image Removed

Die Farben werden im Return-Statement anhand von Hexadezimalen Farbcodes angegeben.

Aktivieren und Deaktivieren von Feldern bzw. Buttons in Unterformularen

In den Eigenschaften eines Subformelements im Layout können über Clientregeln die Buttons für Neuanlage, Klonen und Löschen sowie einzelne Spalten aktiviert bzw. deaktiviert werden.

ApplicationProgrammingInterface 3.jpgImage Removed

Die gerade aktuelle Spalte kann mittels

Codeblock
context.field == "#{<Namespace>.<Businessobjektsname>.<Feldname>}"

abgefragt werden.

 Neu / Löschen / Klonen aktiv (dynamsich)

Die Aktivierung/Deaktivierung der Buttons wird entsprechend eines boolschen Rückgabewertes durchgeführt.

  • return true = Button aktiv


  • return false = Button nicht aktiv

    Neu / Löschen / Klonen aktiv (Layout) 



    Bearbeiten aktiv (dynamisch)

    Die Aktivierung/Deaktivierung der Spalten wird entsprechend eines boolschen Rückgabewertes durchgeführt.

    • return true = Feld aktiv

    • return false = Feld nicht aktiv



    Info

    Achtung: Der Ausgangskontext für "Neu aktiv (dynamisch)" ist immer das übergeordnete BO der Subform, der Ausgangskontext für "Bearbeiten aktiv (dynamisch)", "Löschen aktiv (dynamisch)" und "Klonen aktiv (dynamisch)" hingegen ist immer das BO der Subform selbst.

    Will man sich also beispielsweise in einem Subform für Auftragspositionen den Status des Auftrages holen, muss man dies für "Neu" mittels

    context."#{NAME.Auftrag.nuclosStateNumber}"

    tun, für "Bearbeiten", "Löschen" und "Klonen hingegen muss man erst einen Kontext nach oben gehen mittels

    context."#{NAME.Auftragsposition.auftrag.context}"."#{NAME.Auftrag.nuclosStateNumber}".

    Beispiel Beispiel

    Verwendung 1

    Bestimmte Felder sollen abhängig des ausgewählten Wertes (componentType) aktiviert oder deaktiviert sein.

    Codeblock
    if ("Artikel".equals(context."#{DIB.PackageComponent.componentType}")) {
        if (context.field == "#{DIB.PackageComponent.textNumber}")  
    	   return false  
    }
    if ("Text".equals(context."#{DIB.PackageComponent.componentType}")) {
    	if (context.field == "#{DIB.PackageComponent.article}")  
    	   return false   
    	if (context.field == "#{DIB.PackageComponent.price}")  
    	   return false  
       	if (context.field == "#{DIB.PackageComponent.amount}")  
    	   return false  
    	if (context.field == "#{DIB.PackageComponent.weight}")  
    	   return false  
     
    }
    return true

    Verwendung 2

    Feld invoiceamountinhours soll abhängig des ausgewählten Wertes (chargedashours) aktiviert oder deaktiviert sein.

    Vorsicht: Da im Namen des BO ein Leerzeichen enthalten ist, muss es auch im Groovy-Code so angesprochen werden ("External services").

    code

    if (context."#{D3R1.External services.chargedashours}" == false) {
       if (context.field == "#{D3R1.External services.invoiceamountinhours}") {
           return false
       }
     }
     return true



    Funktionen über Bibliotheksregeln

    Funktionen über Bibliotheksregeln 

    Sie können in Bibliotheksregeln Funktionen definieren, die in dynamischen Eigenschaften und berechneten Werten verwendet werden können. Eine Bibliotheksregel muss hierfür mit der Annotation org.springframework.stereotype.Component gekennzeichnet werden, damit sie von der Laufzeitumgebung erkannt wird (Hinweis: es wird nur eine Instanz der Klasse erzeugt - beachten Sie dies beim Einsatz von Klassenvariablen). Falls Sie eine Methode dieser Klasse als Funktion verwenden möchten, kennzeichnen Sie diese mit der Annotation org.nuclos.api.annotation.Function und vergeben Sie einen global eindeutigen Namen. Diesen Namen verwenden Sie später, um die Funktion mit Hilfe über context."#FUNCTION{<Funktionsname>}" aufzurufen. Bei der Implementierung von Funktionen ist darauf zu achten, dass Parameter- und Rückgabe-Typen übereinstimmen. Ggf. notwendige Umwandlungen müssen manuell vorgenommen werden. Ausserdem muss beachtet werden, dass der Namensraum des Packages im Nuclet Mangagement und der Packagename der Componente (Bibliotheksregel) gleich sind.

    Beispiel


    Verwendung 3

    In folgendem Beispiel wird eine automatische Vergabe von Bestellnummern in Abhängigkeit des ausgewählten Kunden implementiert.

     Bibliotheksregel mit Funktion

    Codeblockpackage org.nuclet.rules; import org.nuclos.api.annotation.Function; import org.springframework.stereotype.Component; @Component public class TestBean { @Function("org.nuclet.rules.MyFunction") public String getNewOrderNumber(String kundennr) { return "ON-" + kundennr; } }

    Dynamisch berechneter Wert


    context."#FUNCTION{org.nuclet.rules.MyFunction}"(context."#{DEF.Kunde.kundennr}")



    Logausgaben

    Um Clientregeln zu debuggen, können Logausgaben eingegeben werden:

       

    log.info("Logausgabe")

    Die Ausgabe kann in der Scripting-Ausgabe (Fenster / Ausgabe (Scripting)) eingesehen werden.



    Ausgabe aktueller User

    Aktueller User (Nuclos Version 4.0.15 und höher)

    Die Variable username kann in Groovy Skripten verwendet werden.

    In dieser Variable vom Typ java.lang.String steht der aktuelle User.

    z.B.: def anwender = username

    Der aktuelle User wird in die Variable anwender übertragen.



    Aktivierung/Deaktivierung von Buttons im Layout


    Die Aktivierung/Deaktivierung der Buttons wird entsprechend eines boolschen Rückgabewertes durchgeführt.

    Known

    • return true = Button aktiv

    • return false = Button nicht aktiv


     Known Issues- Best Practice

    Feld aus Elternbusinessobjekt / Hauptbusinessobjekt auslesen


    Codeblock
    languagegroovy
    fieldFromParent = context."#{<NUCLET>.<SUBENTITY>.<REFERENCEFIELD>.context}"."#{<NUCLET>.<PARENTENTITY>.<FIELD>}"


    z.B. Statusnumeral des Elternobjektes ermitteln:


    Codeblock
    languagegroovy
    stateNumeral = context."#{NUC.Rechnungsposition.rechnung.context}"."#{NUC.Rechnung.nuclosStateNumber}"


    (Warnung) Das Feld, das aus dem Elternbusinessobjek ausgelesen werden soll, muss im Layout vorhanden sein. Soll es nicht sichtbar sein für den Benutzer, kann es deaktiviert werden.


    Messagebox anzeigen

    Codeblock
    languagegroovy
    firstline0
    linenumberstrue
    import groovy.swing.SwingBuilder
     import javax.swing.*
     import java.awt.*
     def swing = new SwingBuilder()
     def myMainFrame = new Frame()
     if (context."#{S663.Auftrag.eingangsdatum}" != null) {
     swing.edt {
     JOptionPane.showMessageDialog(
     myMainFrame, "Hello There"
     );
     }
     }


    Debugging

    Exceptions die von Groovy-Regeln zur Laufzeit geworfen werfen, werden von Nuclos nicht an den Benutzer weitergegeben. Zu Debuggingzwecken kann man Exceptions auffangen und mit Hilfe der oben beschriebenen Messagebox anzeigen.

    Codeblock
    languagegroovy
    firstline0
    linenumberstrue
    (...)
     try {
     // some code
     } catch (Exception ex) {
     swing.edt {
     
    JOptionPane.showMessageDialog(
     
    myMainFrame,
     
    "${ex}"
     )
     
    } 
    }
    }


    Berechnungsrichtung abhängig von einem Parameter ändern

    Szenario: Feld A soll aus Feld B berechnet werden, wenn eine boolena-Variable den Wert wahr hat, andernfalls soll Feld B soll aus Feld A berechnet werden.


    MessageBox anzeigen

    Codeblock
    languagegroovy
    firstline0
    titleBerechnungsvorschrift Feld A
    linenumberstrue
    def berechneAausB = context."#{J94R.businessobject.aausb}"
    if (berechneAausB==false) {
    	def currentValue = context."#{J94R.businessobject.felda}"
    	return currentValue
    } else {
    	def feldB = context."#{J94R.businessobject.feldb}"
    	return feldB*10
    }
    Codeblock
    languagegroovy
    firstline0
    titleBerechnungsvorschrift Feld B
    linenumberstrue
    def berechneAausB = context."#{J94R.businessobject.aausb}" if (berechneAausB) { def currentvalue = context."#{J94R.businessobject.feldb}" if (currentvalue == null) throw new Exception("Exit") else return currentvalue } else { def feldA = context."#{J94R.businessobject.felda}" return feldA/10 }
    import groovy.swing.SwingBuilder
    import javax.swing.*
    import java.awt.*
    
    def swing = new SwingBuilder()
    def myMainFrame = new Frame()
    
    if (context."#{S663.Auftrag.eingangsdatum}" != null) {
    swing.edt {
    JOptionPane.showMessageDialog(
    myMainFrame, "Hello There");
    }
    }