Formelstücklisten

Stücklisten anhand Formeln automatisch generieren, den Produktkonfigurator definieren

Stückliste mit Formeln

in Ihrem Kieselstein ERP stehen auch Formeln für die Errechnung von Mengen und weiteren Eingabefeldern je Stücklistenposition bzw. Arbeitsplanposition zur Verfügung.
Wenden Sie sich für die Freischaltung dieser Funktion bitte vertrauensvoll an Ihren Kieselstein ERP Betreuer.

In manchen Anwendungen wird diese Funktionalität auch Produktkonfigurator genannt.

Die Idee hinter dieser Funktionalität ist, dass abhängig von verschiedenen Eingabeparametern, welche je Stückliste unterschiedlich sein können, entsprechende Berechnungen für die Ermittlung der Positionsdaten wie Menge, Abmessungen (B x H x T) und Artikel durchgeführt werden können. In weiterer Folge kann anhand der errechneten Daten eine echte Stückliste erzeugt werden, mit der die Produktion dann die entsprechenden Vorgaben bekommt.

Das bedeutet, dass die grundsätzliche Idee ist, dass anhand der Parameter “nur” die Menge der jeweiligen Stücklistenposition bzw. Arbeitsplanposition verändert wird.
Beispiel: Hat man nun verschiedene Werte oder auch Maschinen, die abhängig von Parametern zum Einsatz kommen, so definiert man alle Maschinen mit den vorerst gedachten Rüst und Stückzeiten. In den Formeln ändert man dann die jeweilige Rüst bzw. Stückzeit so ab, dass nur die gewünschte Maschine eine echte Sollzeit hat.
Analog wird bei den Positionen vorgegangen.

Vorgehensweise

Identifizieren Sie die Stücklisten bei denen Sie die Formeln hinterlegen möchten. In aller Regel werden dies neue Stücklisten sein. Haken Sie bei diesen Stücklisten mit Formeln an. Damit wird der Reiter Parameter freigeschaltet. Definieren Sie im Reiter Parameter nun die Parameter die für diese Stückliste gelten sollten. Dies könnte z.B. wie folgt aussehen:

Das bedeutet, dass die Kennung, mit der Sie dann weiter rechnen, LH ist und die Bezeichnung entsprechend sprechend für Ihre Anwender. Ist Pflichtfeld angehakt, so muss hier etwas eingegeben werden. Sind Min bzw. Max-Wert definiert, so darf der erfasste Wert nur zwischen diesen Grenzen liegen.

Zusätzlich steht noch bei der Texteingabe auch die Möglichkeit der Combobox zur Verfügung. D.h. wird bei String noch Combobox angehakt, so ist der Bereich zu definieren. Die auswählbaren Werte sind jeweils durch einen senkrechten Strich | (vertical Bar, Pipe) getrennt. Ist Pflichtfeld nicht agehakt, so kann auch leer ausgewählt werden, sonst wird der erste Wert aus Bereich vorgeschlagen.

Kopieren von Parametern

Gerade wenn neue Stücklisten Bäume aufgebaut werden, so müssen die Parameter durchgängig definiert sein.
Das kann natürlich manuell gemacht werden, es können aber zusätzlich von übergeordneten Stücklisten aus der Baumstruktur heraus die Parameter übernommen werden. Klicken Sie dazu bitte in der Parameterauswahlliste auf fehlende Parameter nachtragen. Sie erhalten nun eine Auswahlliste in der die über dieser (Formel-)Stückliste liegenden Formelstücklisten enthalten Stücklisten aufgelistet sind. In anderen Worten man baut von oben nach unten den Stücklistenbaum auf und übernimmt dann ebenfalls von oben nach unten die Parameter. Da dies nicht immer so sein wird gibt es in der Auswahlliste der Formelstücklisten auch die Möglichkeit alle Formelstücklisten anzuhaken.

Was bedeuten die verschiedenen Fehlermeldungen?

Da bei der Eingabe der Formeln direkt oder nur indirekt erkennbare Fehler auftreten können, haben wir entsprechende Fehlermeldungen eingebaut. Diese können sowohl bei der Erfassung der Formel, als auch bei der Interpretation der Formel auftreten.
So könnte z.B. folgender Fehler bei der Interpretation auftreten: Dies bedeutet, dass in einer Stückliste auf einer Position eine fehlerhafte Formel gespeichert wurde. Da bei der Speicherung bereits ein Fehler gemeldet wurde, wurde das erforderliche Class-File nicht erzeugt. Um die richtige Stücklistenposition zu finden, klicken Sie einfach auf den GoTo Button bei Stücklistenposition und Sie stehen direkt auf der fehlerhaften Formel. Bessern Sie bitte die falsche Formel entsprechend aus.

Diese Meldung, welche z.B. beim Speichern der Formel auftritt besagt, dass eine entsprechende Methode nicht gefunden wurde.
Nachfolgend finden Sie eine Liste oft verwendeter Funktionen.

Tritt diese Meldung beim Ausführen der Gesamtkalkulation auf, so bedeutet dies, dass eine Formel hinterlegt wurde, deren Rückgabewert nicht den geforderten Werten entspricht. Bitte beachten Sie, dass in den Positionen für die Menge ein BigDecimal und im Arbeitsplan kein Rückgabewert (nur setzen von Stück- und Rüstzeit) vorgegeben sind. Das würde in obigem Beispiel bedeuten, dass im Arbeitsplan ein Wert retourniert wird, weil z.B. eine Formel aus der Position kopiert wurde.

Hier liegt ein Fehler in der Formel vor, der erst bei der Ausführung festgestellt wird.

Woran erkennt man, ob hinter einer Stücklistenposition eine Formel hinterlegt ist?

Stücklistenpositionen mit Formeln werden in Kieselstein ERP blau dargestellt.

Es können über die Zwischenablage keine neuen Positionen eingefügt werden?

Bitte prüfen Sie ob alle Parameter die in der/den Formeln enthalten sind, auch in der neuen Stückliste, in die eingefügt werden sollte, vorhanden sind.

Wie werden Variablen aufgelöst?

Wichtig bei der Verwendung von Variablen ist deren Einsatz

  • in ihrer Reihenfolge
  • in ihrer Ebene / Unterebene

D.h. Werte aus Variablen können erst verwendet werden, wenn sie initialisiert und errechnet sind.
Zusätzlich ist zu beachten dass die Berechnung der Stücklisten immer je Stückliste erfolgt und danach erst die Unterstücklisten berechnet werden.
Dies hat den Effekt, wenn in einer Stückliste(n Ebene) die Variable einer Unterstückliste benötigt werden, so stehen diese, da die übergeordnete Stückliste zuerst durchlaufen wird, nicht zur Verfügung. Erst durch den “zweiten” Lauf, in dem die Unterstücklisten aufgelöst werden, werden die Variablen initialisiert und stehen somit immer erst eine Ebene darunter zur Verfügung.

Wie wirkt der Parameter Kunde?

Wenn in einer Formelstückliste Kunden mit dem Parameter KundeId abgefragt wird, so bewirkt dies, dass der vom Anwender gewählte Kunde, der dann in aller Regel ein Pflichtfeld sein sollte, bei der Überleitung in Produktstücklisten, in alle (Unter-)Stücklisten die Aufgrund der Formeln neu erzeugt werden, eingetragen wird.

Wie können Produktstücklisten erzeugt werden?

Neben der Variante, dass aus dem Angebot heraus direkt Stücklisten in Verkaufssicht vorkalkuliert und dann durch die Erzeugung eines Angebotes auch Produktstücklisten angelegt werden können, können auch aus dem Modul Stücklisten heraus, Menüpunkt, Bearbeiten, Konfigurieren eine entsprechende Produktstückliste mit den gegebenen Werten / Parametern angelegt werden.
Es wird dazu, die Formelstückliste mit allen formelbehafteten Unterstücklisten kopiert und anstelle der Formeln die errechneten Werte eingetragen. Die Artikelnummer der Stücklisten werden aus den Formelstücklisten übernommen und um eine laufende 2+4 stellige Nummer ergänzt. Die ersten zwei Stellen sind das Geschäftsjahr der Anlage gefolgt von einer vierstelligen laufenden Nummer. Diese wird auf die angelegte maximale Länge der Artikelnummer rechtsbündig durch Ergänzung um _ (Underline / Unterstrich) aufgefüllt.
Um eine entsprechende Durchgängigkeit zu erreichen, werden diese Produktstücklistennummern zentrale verwaltet und über alle Arten von Formelstücklisten hochgezählt. Damit wird erreich, dass eventuell angelegte nur Unterstücklisten, z.B. für die Fertigung von Ersatzteilen, nicht mit anderen Formelstücklisten kollidieren.

Bei der Erzeugung der Produktstücklisten wird weiters, bei eingeschaltetem Parameter DIMENSIONEN_BESTELLEN diejenigen Artikel angelegt, bei denen die Dimensionen für die stückgenaue Beschaffung der für Sie gefertigten Artikel benötigt werden. Details siehe

Praktische Formeln / Anwendung

Hier der Versuch einer Beschreibung, wie die einfache Berechnung eines Fenster aussehen könnte.
Bitte bedenke du bist hier in der Programmiersprache Java. D.h. alles was in Java7 zur Verfügung steht kann verwendet werden. So kann auch durch Endlosschleifen der Server zum Absturz gebracht werden. Das bedeutet, Schleifen nur sehr vorsichtig verwenden.

Hinweis: Reservierte Worte wie do, while, for usw. dürfen natürlich NICHT für Variablen verwendet werden.
Die Fehlermeldung die dann kommt sagt …. error not a statement

Übersicht

Die Zusatzfunktionsberechtigung muss aktiv sein.
Das Häkchen in der Stückliste Kopfdaten “mit Formel” muss gesetzt sein. Wenn gesetzt, werden auch Formeln ausgewertet (sofern gesetzt). Ist das Häkchen nicht gesetzt, wird auch nicht ausgewertet.
Formel kann in der jeweiligen Position (im Reiter Position bzw. Arbeitsplan) eingegeben werden.
Der Editor ist sehr einfach gehalten. So wird z.B. die Formatierung vernichtet.
Beim Speichern/Erzeugen der Stücklistenposition wird die jeweils aktuelle Formel übersetzt.
D.h. gibt es einen Compile-Fehler beim Speichern, hat der Editor immer noch den neuen - vom Anwender geänderten Source-Text.
Speichert man nun zweites Mal, wird scheinbar fehlerfrei gespeichert, jedoch ist die Formel nicht aktualisiert.

Im Reiter Parameter können die in dieser Stückliste vom Benutzer abzufragenden Parameter hinterlegt werden.
Im Menü Bearbeiten gibt es einen zusätzlichen Menüpunkt “Konfigurieren”, mit dem der Anwender die definierten Parameter erfassen kann. Mittels Klick auf OK wird dann eine Gesamtkalkulation der Stückliste (mit Formeln) durchgeführt. Hier bitte die Reportvariante mit Formeln auswählen / hinterlegen. Siehe Reportsammlung

Details zu den Formeln / Arbeitsweise in der Stücklistenposition

Im einfachsten Falle ist das so etwas wie: “return new BigDecimal(getMenge().multiply(new BigDecimal(“3”)));” um die in der Position vorhandene Menge mit 3 zu multiplizieren.
WICHTIG: Die Methode muss immer(!) ein BigDecimal zurückgeben. Dieser wird als Positionsmenge verwendet.

java.lang und java.math sind implizit vorhanden (importiert). Alle anderen Pakete müssen mit dem kompletten Klassennamen aufgerufen werden.
Z.B. BigDecimal pi = com.lp.util.Helper.rundeKaufmaennisch(new BigDecimal(“3.141592”));

Der Zugriff auf Parameter ist mittels “$P{Parametername}” möglich.
Zum Zeitpunkt des compiles wird dann zuerst gesucht, ob diese/aktuelle(!) Stückliste den Parameter kennt.
Übersetzt wird das in eine Art “(Datentyp) getParam(String parametername)”.
D.h. wenn man einen Parameter “Laenge” mit java.math.BigDecimal. definiert hat, und dann “…. $P{Laenge}.multiply(BigDecimal.TEN) " macht (also die Länge mit 10 multiplizieren), wird intern " … ((java.math.BigDecimal)getParam(“Laenge”)).multiply(BigDecimal.TEN) " daraus.
Parameter können nicht verändert werden.
Das bedeutet auch, dass wenn in der Kopfstückliste ein Parameter definiert ist, muss dieser auch(!) in der Unterstückliste definiert (Reiter Parameter) werden, sofern in der Unterstückliste darauf zugegriffen wird (was der compile ja feststellt). Zum Zeitpunkt des Compilierens der Unterstückliste kennt die ja nicht die aufrufende Stückliste.

Wenn die “Gesamtkalkulation” aufgerufen wird und es sich um eine Formelstückliste handelt, wird am Beginn der jeweiligen Stückliste diese “kompiliert”. Hat sich die Klasse ansich nicht geändert, wird die alte/bekannte instanziert. Wenn sie sich geändert hat, wird die neue Klasse geladen und instanziert.

Grundsätzliche Definition einer Formel

Bewährt hat sich folgende Vorgehensweise.

  • Beschreibung was dieser Block genau macht / machen sollte
  • Deklaration der übernommenen Parameter aus den Eingaben
  • Denke daran, dass immer die Menge der Position zurückgegeben werden muss.
/* Übertragen des Parameters Scheiben auf die Menge */
Integer anz=$P{Scheiben}; /* Übertragen des vom Anwender eingegebenen Wertes für den Parameter Scheiben in das Objekt anz */
double mng; /* Deklaration einer Rechenvariablen */
mng = anz.doubleValue();  /* Umwandeln des Integer-Objektes in ein double */

/* Übertragen des Parameters Breite in die Dimension1 */
Integer breite=$P{Breite};
Double BreiteInM = new Double ((breite.doubleValue() - 0.0) / 1000.0);	/*  0 = Zugabe etc. */

setDimension1( BreiteInM );

/* Übertragen des Parameters Hoehe in die Dimension2 */
Integer hoehe=$P{Hoehe};
Double HoeheInM = new Double ((hoehe.doubleValue() - 0.0) / 1000.0);	/*  0 = Zugabe etc. */

setDimension2( HoeheInM );

return new BigDecimal(mng);

Welche Funktionen stehen zur Verfügung

Funktion Beschreibung
setDimension1( double d ); Setzen der Breite
setDimension2( double h ); Setzen der Höhe
setDimension1( double t ); Setzen der Tiefe
Double getDimension1(); Auslesen der Breite
Double getDimension2(); Auslesen der Höhe
Double getDimension3(); Auslesen der Tiefe
String getEinheitCnr(); Auslesen der Mengeneinheit der Position
setEinheitCnr(String einheit); Setzen der Mengeneinheit der Position
BigDecimal getMenge(); Auslesen der Menge der Position
setMenge(BigDecimal menge); Setzen der Menge der Position
setArtikel($P{Artikel}); Setzen des verwendeten Artikels in der Stücklistenposition
$P{Artikel} ist jener Parameter, der zu Beginn des Konfigurieren beim Anwender abgefragt wird.
Ist derzeit nur in der Stücklistenposition implementiert. Es kann nur dieser Parameter gesetzt werden.
FLRArtikelliste getArtikel(); Öffnen der Artikelauswahl, damit der Anwender seinen Artikel eingeben kann.
String getKundeKurznr(KundeId kundeId); Holen der KundenKurznummer des ausgewählten Kunden. Liefert null, wenn kein Kunde gewählt oder die Kundenkurznummer nicht gesetzt.
Die ID des Artikels com.lp.server.stueckliste.service.ItemId artikel = $P{Artikel}; // Der vom Anwender ausgewählte Artikel, Integer artikeliid = artikel.getId();

Zusätzlich Funktionen, damit man ins Logfile des Servers schreiben kann:

Log-Funktion Beschreibung
debug(String message) um eine Nachricht mit Priorität DEBUG ins log zu schreiben
info(String message) um eine INFO Message ins log zu bekommen
warn(String message) für eine WARN Message
error(String message) für eine ERROR Message

Meldungen die an den Report der Gesamtkalkulation übergeben werden können:

Meldungs-Funktion Beschreibung
report.debug(“Nachricht…”); Ausgabe der Nachricht im Feld “ReportDebug”
report.info(“Nachricht…”); Ausgabe der Nachricht im Feld “ReportInfo”
report.warn(“Nachricht…”); Ausgabe der Nachricht im Feld “ReportWarn”
report.error(“Nachricht…”); Ausgabe der Nachricht im Feld “ReportError”

Ein mittels “report.debug(“wichtige DebugInformation”)” erstellter Text landet im Report der Gesamtkalkulation im Feld “ReportDebug” usw..
Die Felder “Report(Debug|Info|Warn|Error)” sind vom Datentyp java.lang.String.
Die Methoden report.debug(…) nehmen ebenfalls einen String entgegen.
Die gleichen report.xxx Methoden können innerhalb einer Formel mehrfach aufgerufen werden. Jede einzelne Nachricht wird gespeichert. Bei der Übergabe an den Report werden dann - für den Fall, dass mehrere gleichartige Nachrichten in der Formel ausgegeben worden sind, die Einzelnachrichten mittels “\r\n” miteinander verkettet.
Das Report-Feld enthält null, wenn keine Nachricht dieser Priorität in der Formel ausgegeben wurde.

weitere Funktionen für die Stücklistenpositionen

Log-Funktion Beschreibung
generiereArtikelCnr(String artikelnummer); erzeugen einer Artikelnummer siehe unten generiereUebergeordneteArtikelCnr
String getArtikelCnr(); ACHTUNG: Liefert nur das was gesetzt wurde, aber NICHT den erzeugten Artikel
setUebergeordneteArtikelCnr(String artikelnummer); Erzeugen einer Artikelnummer für den Kopf der aktuellen Stückliste
generiereUebergeordneteArtikelCnr(String artikelnummer);
String getUebergeordneteArtikelCnr();
setArtikelBezeichnung(String bez, String kbez, String zbez, String zbez2); Setzen der Artikelbezeichnungen.
Wird "” übergeben, wird das Feld auf null gesetzt. Wenn null übergeben wird wird NICHTS verändert, also der Text aus dem Originalartikel übernommen.
setUebergeordneteArtikelBezeichnung(String bez, String kbez, String zbez, String zbez2);

Diese Methoden können zwar sowohl im Kontext Berechnen (Gesamtkalkulation) als auch der Produktionsstückliste verwendet werden, haben aber nur im Zuge der Produktionsstückliste eine Wirkung. Beide Methoden können auch “null” übergeben (Beispiel: setArtikelCnr(null);)

Bei “setArtikelCnr(“gewuenschteArtikelnummer”)” wird dabei beim Erzeugen der Produktionsstückliste jener Artikel der aktuell mit “getArtikel()” ermittelt werden kann mit der “gewuenschtenArtikelnummer” erzeugt. Bei “generiereArtikelCnr(“gewuenschteArtikelnummer”)” wird beim Erzeugen der Produktionsstückliste jener Artikel mit “gewuenschterArtikelnummer” durch die “generiereArtikelnummer(…)” Methode am Server geschickt.

setUebergeordneteArtikelCnr(“gewuneschteArtikelnummer”) bestimmt beim Erzeugen der Produktionsstückliste die Artikelnummer jener Stueckliste die die aktuelle Position beeinhaltet. Wird diese Methode von mehreren Positionen aufgerufen, wird die Artikelnummer der ersten Stücklistenposition verwendet die die übergeordnete Artikelnummer gesetzt hat. Mit “generiereUebergeordneteArtikelCnr(“gewuenschteArtikelnummer”)” wird die “gewuenschteArtikelnummer” durch die “generiereArtikelnummer(…” Methode an den Server übergeben.

Sowohl generiereArtikelCnr() als auch generiereUebergeordneteArtikelCnr() setzen automatisch die korrespondierende “getArtikelCnr()” bzw. “getUebergeordneteArtikelCnr()”.

Mit “setArtikelBezeichnung(…)” bzw. “setUebergeordneteArtikelBezeichnung(…)” können die einzelnen Eigenschaften “Bezeichnung” (bez), “Kurzbezeichung” (kbez), “Zusatzbezeichnung” (zbez) und “Zusatzbezeichnung2” (zbez2) des korrespondierenden Artikels während der Erzeugung der Produktionsstückliste gesetzt werden.

Es muss diese Vorgangsweise beim Ablauf der Formeln berücksichtigt werden. Formeln (und damit Positionen) werden jeweils pro zugehöriger Stückliste komplett für diese Stückliste durchlaufen. Erst danach wird eine etwaige Unterstückliste einer Position durchlaufen. Dies ist anders, als der Server-Code, der die Stückliste durchgeht. Dieser durchläuft sie Position für Position, und steigt sofort in die Unterstückliste ein.

Beispiel:
generiereUebergeordneteArtikelCnr(artnr+“0000”); // und GENERIERE_ARTIKELNUMMER_ZIFFERNBLOCK = 1 !!

D.h. es wird hinter der generischen Artikelnummer auch 0000 dazugehängt UND wenn eine weitere Stkl/Artikel des gleichen bereiches erzeugt wird um eins raufgezählt. Funktioniert auch bei generiereArtikelnummer

Beispiele

Division

BigDecimal lh = $P{LH};
  return (lh.divide(new BigDecimal(100)));

Der Parameter LH wird durch 100 dividiert und als Menge zurückgegeben.

Ein Tausenstel von LL mit 4 multipliziert und zum Ergebnis 5 dazu

BigDecimal ll = $P{LL};
   return (ll.multiply(new BigDecimal(0.001)).multiply(new BigDecimal(4.0))).add(new BigDecimal(5));

Auf eine gewisse Anzahl erhöhen / runden

BigDecimal ll = $P{LL};
int anzahl;
anzahl = ll.intValue() / 1000;
anzahl += 4;
return new BigDecimal(anzahl);

In Worten: LL auf ganzzahlige Anzahl umrechnen (pro einem Meter ein Artikel) und dann noch vier dazuzählen und dies als Menge retour.

Bedingung

In Worten: Wenn LL größer 1000 dann gib 1 zurück, sonst 0,5

BigDecimal ll = $P{LL};
if(ll.compareTo(new BigDecimal(1000))>0) {
   return (new BigDecimal(1));
}
return(new BigDecimal(0.5));

Bitte beachten, dass die Menge immer als BigDecimal zurückgegeben werden muss.

Bedingung anhand von Texten

String af = $P{AF};
BigDecimal m;
if (af.toUpperCase().contains("ALU")) {
    m = new BigDecimal(1.00);
} else {
    m = new BigDecimal(0.00); }
return m;

Formeln im Arbeitsplan:

Formelrückgabewert ist java.lang.Long(!) (im Gegensatz zur Stücklistenposition, wo es java.math.BigDecimal ist). Es wird die Stückzeit zurückgegeben.

Funktion Beschreibung
Long getStueckzeit(); Stückzeit lesen in ms
setStueckzeit(Long stueckzeit); Setzen der Stückzeit in ms
Long getRuestzeit(); Rüstzeit lesen in ms
setRuestzeit(Long ruestzeit); Setzen der Rüstzeit in ms

Beispiel für Stückzeit aus Zykluszeitberechnung:

/* Spritzen errechnet sich aus der Zykluszeit und den Kavitäten
Die Zeiten sind in Millisekunden anzugeben
*/

BigDecimal zykluszeit = $P{Zykluszeit};	/* in Sekunden */
Integer kavitaeten = $P{Kavitaeten};

double Zeit = zykluszeit.doubleValue() / kavitaeten.doubleValue() * 1000.0;

long stkzeit = new Double(Zeit).longValue();

return (new Long(stkzeit));

Beispiel für einfachen Rüstzeit Eintrag:

/* Rüsten, sind immer die fixen Stunden umgerechnet auf millisekunden */

BigDecimal Ruestzeit = $P{Ruestzeit};	/* in Stunden */
BigDecimal R = Ruestzeit.multiply(new BigDecimal(1000)).multiply(new BigDecimal(3600));	/* in ms */

long rz;
rz = R.longValue();
setRuestzeit(new Long(rz));

return getStueckzeit();

Beispiel für Rüstzeit für eine bestimmte Maschine:
Beachte dass du durch Long(0L) definierst, dass diese Zahl ein long und kein integer ist.

/* Berechnung für die Maschine 280t */
String masch = $P{Maschine};	/*  Maschine 100t|150t-160t|25t|280t|35t|50t-60t */
if (!(masch.startsWith("280t")))	{ return new Long(0L);}

BigDecimal Ruestzeit = $P{Ruestzeit};	/* in Stunden */
BigDecimal R = Ruestzeit.multiply(new BigDecimal(1000)).multiply(new BigDecimal(3600));	/* in ms */

long rz;
rz = R.longValue();
setRuestzeit(new Long(rz));

return getStueckzeit();

Fehler die trotz Prüfung zur Laufzeit auftreten können:

Je nach Content der Formel kann es sein, dass die Prüfung beim Speichern keinen Fehler ergibt, aber beim Aufruf während der Berechnung ein entsprechender Fehler auftritt.
Das kann z.B. dann auftreten wenn die Rückgabewerte nicht richtig deklariert sind (Long anstatt BigDecimal usw.)
Derzeit bekommen wir bei der Interpretation der Formel zur Laufzeit zu wenig Infos um eine gute sprechende Fehlermeldung ausgeben zu können.
D.h. die Fehlersuche kann mühsam werden. Ändere daher die Stückliste Schritt für Schritt (so wie beim Anpassen von komplexen Formularen auch)

Punkte auf die man gerne reinfällt und nicht so schnell findet:

1.) Eine Variable wird deklariert aber nicht initialisiert (gesetzt) und dann damit gerechnet.
Bitte in jedem Falle initialisieren, sonst kommt ein Compiler Fehler.

2.) Wenn das Speichern = Compilieren geht, aber par too die Gesamtkalkulation doch immer wieder Fehler meldet. Dann doch mal den Kieselstein ERP Server neu starten (der Client kann da ruhig offen bleiben)

Bedingungen

Es gehen auch die “iReport” If-Konstrukte. D.h. z.B.

	double lh = $P{LH}.doubleValue();
	lh == null ? return das eine : return das andere
	usw.

Es gibt zusätzlich drei Methoden um mit “globalen Variablen” arbeiten zu können:

1.) setVar(String variablenname, Object wert)
Zum Setzen einer Variable mit einem bestimmten Wert.
Beispiel: setVar(“Gewicht”, new BigDecimal(“25.89”));

2.) getVar(String variablenname)
Zum Ermitteln des Werts der Variable
Beispiel: BigDecimal gewicht = (BigDecimal) getVar(“Gewicht”);

3.) existsVar(String variablenname)
Zum Überprüfen, ob die Variable existiert Beispiel:

if(existsVar("Gewicht")) {
	info("Es wurde das Gewicht '" + 
		((BigDecimal)getVar("Gewicht)).toPlainString() + "' erfasst.");

In welcher (Stücklisten) Ebene die Variable gesetzt wird, ist dabei egal.
Auch in welcher Ebene die Variable gelesen wird. Ein sinnvoller Wert (also != null) kann natürlich nur gelesen werden, wenn die Variable zuerst gesetzt wird.

Übersteuern der erzeugten Kopf-Stücklisten-Artikelnummer und der Artikelnummer

Es geht darum, dass aus der Formel strukturierte Stücklisten und dazugehörende “Einkaufs-“Artikel angelegt werden können.
Dafür gibt es:
1.) setUebergeordneteArtikelCnr(String artikelnummer);
Bedeutet wenn auf Produktionsstückliste erzeugen geklickt wird, wird die soeben erzeugte Produktionsstückliste auf diese CNr gesetzt.
Es darf diese Funktion nur einmal je Stückliste(nebene) verwendet werden.

2.) setArtikelCnr(String artikelnummer);
Bedeutet, dass der Artikel der bearbeiteten Stücklistenposition (also der Artikel der hinter der Zeile mit der Formel liegt) auf die (neue) Artikelnummer kopiert wird.

3.) generiereUebergeordneteArtikelCnr(artnr+“0000”); // und GENERIERE_ARTIKELNUMMER_ZIFFERNBLOCK = 1 !!

Schleifen

Es können, da der gesamte Java Syntax zur Verfügung steht auch Schleifen usw. eingebaut werden. Damit sind auch unendliche Schleifen möglich, welche zum Stillstand des Kieselstein ERP führen können.
Daher bitte mit Hirn verwenden.

// === Eine while-Schleife

 BigDecimal laenge = $P{LL};

  /* Vielfache von Zehn ermitteln */
 int l = laenge.intValue();
 int zehner = 0;

 while(l > 0) {
     zehner++;
     l -= 10; /* alternativ l = l - 10; */
 }

 setMenge(new BigDecimal(zehner));
 return BigDecimal.ZERO;

// === Eine while-Schleife mit vorzeitigem Ende

 BigDecimal laenge = $P{LL};

  /* Vielfache von Zehn ermitteln */
 int l = laenge.intValue();
 int zehner = 0;

 while(l > 0) {
     zehner++;
     l -= 10; 

     if(l < 500) {
        break;
    }
 }

 setMenge(new BigDecimal(zehner));

 return BigDecimal.ZERO;

// === Eine For-Schleife mit 10er Inkrement

 BigDecimal laenge = $P{LL};

  /* Vielfache von Zehn ermitteln */
 int lMax = laenge.intValue();
 int zehner = 0;
 for(int l = 0; l < lMax; l += 10) {
     zehner++;
 }

 setMenge(new BigDecimal(zehner));

 return BigDecimal.ZERO;

// === Eine einfache For-Schleife zaehlt nach oben

 BigDecimal laenge = $P{LL};

  /* Simples nach oben durchzaehlen */
 int lMax = laenge.intValue();
 int zaehler = 0;
 for(int l = 0; l < lMax; l++) {
     zaehler++;
 }

 setMenge(new BigDecimal(zaehler));

 return BigDecimal.ZERO;

// === Eine einfache For-Schleife zaehlt nach unten

 BigDecimal laenge = $P{LL};

  /* Simples nach oben durchzaehlen */
 int lMax = laenge.intValue();
 int zaehler = 0;
 for(int l = lMax; l >= 0; l--) {
     zaehler++;
 }

 setMenge(new BigDecimal(zaehler));

 return BigDecimal.ZERO;

weitere Formel-Beispiele

/* Prüfen ob Parameter Laenge gesetzt, falls größer 100 wird 3 zurückgegeben, ansonsten 2 */
    if($P{Laenge} == null) {
      return BigDecimal.ONE;
    }

    if($P{Laenge}.compareTo(new BigDecimal("100")) > 0) {
      return new BigDecimal("3");
    } else {
      return new BigDecimal("2");
    }

/* Die Einheit auf mm setzen, die Menge auswerten, mit 1000 multiplizieren und 12460mm dazuzählen (x = 12460 + n * 1000) */
    setEinheitCnr("mm");
    return new BigDecimal("12460").add(getMenge().movePointRight(3));

/* Abhängig eines Stringinhaltes was berechnen */
String af = $P{AF};
BigDecimal m;
if (af.toUpperCase().contains("ALU")) {
	m = new BigDecimal(1.00);
} else {
	m = new BigDecimal(0.00);
}
return m;

/* Abhängig von einer Länge eine Stückzahl über int herausrechnen */
BigDecimal ll = $P{LL};
int anzahl;
anzahl = ll.intValue() / 1000;
anzahl += 4;
return new BigDecimal(anzahl); 

/* bei einem Eindimensionalen Artikel die Breite (=Dimension1) setzen und die Menge gleich belassen */
BigDecimal ll = $P{LL};
BigDecimal mng = getMenge();
Double d1 = new Double ((ll.doubleValue() - 175) / 1000);
setDimension1( d1 );
return (mng);

/* oder auch */
BigDecimal ll = $P{LL};
double l;
l = (ll.doubleValue()-175)/1000;
setDimension1(new Double(l));
return getMenge();

weitere Formeln im Arbeitsplan

/* im Arbeitsplan muss die Ruestzeit als Long gesetzt werden
   Die Stückzeit muss als Long zurückgegeben werden
   sowohl Stückzeit wie Rüstzeit sind in MilliSekunden */
BigDecimal ll = $P{LL};
long x;
x = ll.longValue() * 1000;
setRuestzeit(new Long(x) );
return getStueckzeit();

/* je 1000mm 10Minuten Rüstzeit 
   im Arbeitsplan muss die Ruestzeit als Long zurückgegeben werden
   Die Stückzeit muss immer und als Long zurückgegeben werden
  sowohl Stückzeit wie Rüstzeit sind in MilliSekunden
  D.h. 1000 = 1 Sekunde
          60.000 = 1 Minute
          3600.000 = 1 Std.
long = langer int(eger)
double = fließkomma ähnlich float
Es muss sowohl Stück wie Rüstzeit explizit gesetzt werden */

BigDecimal ll = $P{LL};
int anzahl;
long rz;
long sz;
anzahl = ll.intValue() / 1000; /* das sind die Meter */
rz = anzahl * 10 * 60000;
sz = 0;
setRuestzeit(new Long(rz) );
return (new Long(sz)); /* Das ist auch die Stückzeit, die gesetzt werden muss */

Fehler auf die man gerne reinfällt

  • man verwendet eine Variable und die ist noch nicht initialisiert, dann kommt beim Compilieren kein Fehler aber bei der Ausführung null / Methode nicht (initialisiert oder so ähnlich) und es ist eigentlich der Fehler unlogisch
  • Man macht einen SQLEXECUTE und der DB Zugriff geht nicht, weil Anwender Parameter auf die DB falsch
  • Es kommt auch der Ausführungsfehler (Methode nicht initialisiert)

per SQl Query auf DB Felder zugreifen

Sowohl in Formeln für die Stuecklistenpositionen als auch Arbeitsplan(positionen) stehen zusätzlich zur Verfügung:

Object sqlExecute(String sql);

Object[] sqlExecutes(String sql); // Hinweis: Das sqlExecutes nur verwenden wenn wirklich mehrere Felder erwartet werden.
sqlExecute liefert - wie P_SQLEXEC.execute() aus den Reports - genau eine Spalte einer Zeile zurück.
sqlExecutes liefert - wie P_SQLEXEC.executes() aus den Reports - mehrere Spalten einer Zeile zurück.

Beispiel:

// === sqlExecute im Script
/* Auf Basis der Artikel-Id möchte ich die C_NR ermitteln */
Integer artikelId = getArtikel().getI_id();
String cnr = (String) sqlExecute("SELECT C_NR FROM WW_ARTIKEL WHERE I_ID = " + artikelId.toString() + ";");

Hinweis: sqlExecute liefert Daten vom Typ Object. Da wir wissen, dass in der C_NR in der Tabelle ein “String” (character varying(25)) ist, können/müssen wir dieses “Object” in einen “String” casten, was mit “(String)” passiert. Vergisst man das, wird mit hoher Wahrscheinlichkeit zwar der compile des Scripts noch korrekt sein, aber beim “Konfigurieren” (Stückliste::Bearbeiten::Konfigurieren) gibt es einen Fehler bezüglich “Datentypen sind nicht kompatibel”, weil der Compiler hier beim Erstellen der kompletten Klasse feststellt, dass sqlExecute nur Object liefern kann, aber die Zuweisung an eine Variable vom Typ String erfolgt.

Natürlich können innerhalb des Scripts mehrere sqlExecute(s)() durchgeführt werden. Sie werden genau in der Reihenfolge abgearbeitet, wie sie im Script stehen.

Object sqlExecutes im Script

/* Auf Basis der Artikel-Id möchte ich das Artikelgewicht und das Materialgewicht ermitteln */
Integer artikelId = getArtikel().getI_id();
Object[] o = sqlExecutes("SELECT C_NR, F_GEWICHTKG, F_MATERIALGEWICHT FROM WW_ARTIKEL " +
  "WHERE I_ID = " + artikelId.toString() + ";");
String cnr = (String) o[0];
Double artikelgewicht = (Double) o[1];
Double materialgewicht = (Double) o[2];

warn("Artikelgewicht in kg: " + (artikelgewicht == null ? "keines angegeben":  artikelgewicht.toString()));

Runden:

.setScale(4,BigDecimal.ROUND_HALF_EVEN)