Nummernkreise dienen der Vergabe von eindeutigen Nummern für Instanzen von Business Objects, die über diese Nummer, welche normalerweise den fachlichen Schlüssel darstellt, identifiziert werden. Die über einen Nummernkreis vergebenen Nummern entsprechen einem in der Nummernkreisdefinition festgelegten Format und das Hochzählen erfolgt in einer bestimmten Schrittweite.
7.1 Nummernvergabearten
Bei der freien Nummernvergabe ist eine unbenutzte Nummer nicht wieder verwendbar, die Nummern für Instanzen eines Business Objects können lückenhaft vergeben werden. Das passiert zum Beispiel, wenn bei der Neuanlage einer Instanz eine Nummer gezogen wird, die Instanz dann verworfen wird. Diese Art der Vergabe ist oft ausreichend für bestimmte Nummerierungen von Instanzen, da es hauptsächlich nur um ein bestimmtes Format und die Eindeutigkeit des fachlichen Schlüssels geht.
Ist es notwendig, Nummern für Instanzen eines Business Objects (z.B. Kundenrechnungen) lückenlos zu vergeben, muss die Nummernvergabe innerhalb einer Transaktion erfolgen. Dabei wird die Nummer für eine neu anzulegende Instanz erst beim Abspeichern in die Datenbank gezogen und zwar beim commit() der zugehörigen Top-Level-Transaktion. Da die Nummer erst nach der Datenerfassung durch den Benutzer gezogen wird, kann sie bei der Neuerfassung noch nicht angezeigt werden.
7.2 Definition von Nummernkreisen
Die Definition eines Nummernkreises erfolgt in der Anwen-dung „Nummernkreise“. Ein Nummernkreis basiert immer auf einem Nummernkreistyp, der aus einer Menge vorge-gebener Typen ausgewählt werden kann. Soll die maxi-male Länge der zu vergebenden Nummern von der eingestellten Länge des Nummernkreistyps abweichen, kann eine kleinere Länge angegeben werden. Weiterhin müssen der Startwert der Nummernzählung, die Schrittweite und der Endwert definiert werden.
In der Tabelle wird festgelegt, aus welchen Bestandteilen eine Nummer besteht. Dabei wird die Reihenfolge und die Länge der Bestandteile festgelegt. Für jeden Bestandteil wird die Herkunft der Daten festgelegt und ein Format ausgewählt. Zudem wird festgelegt, ob der Bestandteil als Teil der Nummer angezeigt wird, und ob der Bestandteil einen neuen Zähler hervorruft.
Die folgende Abbildung zeigt eine Nummernkreisdefinition. In diesem Beispiel wurde der Nummernkreis CH mit der Bezeichnung „Charge“ angelegt. Er basiert auf dem Nummernkreistyp Ausprägungen. Die Nummernlänge wird mit 6 festgelegt. Die erste Nummer startet bei 1000, es wird in Einer-Schritten erhöht bis zur höchsten Nummer 9999. Danach kann keine Nummer mehr gezogen werden und das Feld bleibt nach Ausführen der entsprechenden Aktion leer. In diesem Fall muss entweder der Nummernkreis erweitert oder ein anderer gültiger zugeordnet werden. In der Tabelle kann für die einzelnen Bestandteile der Nummer das Verhalten festgelegt werden. Auf den ersten beiden Nummernpositionen steht in diesem Beispiel die Konstante „CH“. Die restlichen vier Nummernpositionen sind mit der Nummerierung belegt, wofür die Herkunft „Zähler“ verwendet wird. Dieser Bestandteil nummeriert nach dem unter der Rubrik „Zählung“ festgelegten Verhalten.
Für die Standard-Nummernfunktion wird die letzte vergebene Nummer vom Persistenzdienst automatisch in dem Business Object com.cisag.app.general.obj.NumberRangeUse gespeichert. Bei der Nummernkreisdefinition kann auch die Vergabe von Positionsnummern konfiguriert werden. Es können die Startnummer, die maximale Nummer sowie die Schrittweite angegeben werden.
7.3 Benutzung von frei gezogenen Nummern
In der Anwendung kann der zu benutzende Nummernkreis über eine beim Business Object definierte Beziehung oder direkt über den Objektmanager geladen werden.
Laden über Beziehung:
NumberRange nr= salesOrderType.retrieveNumberRange();
Laden über den Namen:
NumberRange nr= (NumberRange)
om.getObject(NumberRange.buildByCodeKey(“CH”));
Zum Ziehen einer Nummer über den geladenen Nummernkreis muss der Nummernkreismanager (com.cisag.pgm.appserver.CisNumberRangeManager) benutzt werden, der die dafür benötigte Funktionalität zur Verfügung stellt.
CisNumberRangeManager nm= env.getNumberRangeManager();
Zum Ziehen der nächsten Nummer wird am Nummernkreismanager die Funktion getNextNumber() mit dem Nummernkreis als Parameter aufgerufen. In der Regel übergibt man für den Parameter „parms” null. Dieser Parameter dient dazu, zusätzliche Daten für selbstdefinierte Funktionen zur Nummernberechnung zu übergeben. Die Signatur der Methode ist:
String getNextNumber(NumberRange nr, Object[] parms)
Zum Ziehen der nächsten Positionsnummer zu einem Nummernkreis dient die Methode getNextDetailNumber() des Nummernkreismanagers. Zur Ermittlung der nächsten Positionsnummer ist etwas mehr Aufwand nötig, da die Positionsnummern nicht vom Nummernkreis selbst verwaltet werden, sondern nur die Berechnung für die nächste folgende Nummer zu einer übergebenen Nummer erfolgt. Normalerweise wird die höchste Positionsnummer zu einer Hauptnummer bei deren Business Object in einem Attribut gespeichert oder muss durch eine Datenbankanfrage erst ermittelt werden.
Die Signatur der Methode ist:
long getNextDetailNumber(NumberRange nr, long maxPosNumber,
Object[] parms)
Beispiele
Ziehen einer Hauptnummer
Im folgenden Quelltextauszug wird als Beispiel das Ziehen einer Hauptnummer gezeigt:
…
CisNumberRangeManager nm = env.getNumberRangeManager();
//Holen des Nummernkreises über Beziehung
NumberRange numberRange = salesOrderType.retrieveNumberRange();
String number;
if (numberRange == null) {
mm.sendMessage(“SAL”, 90);
// kein Nummernkreis erhalten
…
}
else {
number = nm.getNextNumber(numberRange, null);
if (number==null || number.trim().equals(“”)) {
mm.sendMessage(“SAL”, 855);
// Nummernkreis lieferte keine Nr.
…
}
…
}
…
Die gezogene Nummer wird automatisch im Business Object com.cisag.app.general.obj.NumberRangeUse gespeichert. Bei der Nichtbenutzung einer Nummer entsteht eine Lücke, dass heißt die Nummer kann nicht wieder benutzt werden.
Ziehen einer Positionsnummer
Im folgenden Quelltextauszug wird als Beispiel das Ziehen einer Positionsnummer über die in der Anwendung implementierte Methode getNextDetailNumber() gezeigt. Dieser werden der Nummernkreis, der die Positionszählung festlegt und die maximale Positionsnummer übergeben. In der Nummernkreisdefinition ist die Art und Weise der Positionszählung festgelegt.
Die Speicherung der höchsten vergebenen Positionsnummer erfolgt nicht zentral über den Nummernkreis und muss deshalb von der Anwendung anderweitig ermittelt werden. Zum Beispiel ist eine Möglichkeit, die maximale Positionsnummer zu einer Hauptnummer in einem Attribut der Business-Object-Instanz zu speichern. Die Methode der Anwendung getNextDetailNumber() kapselt die eigentliche Methode des Nummenkreismanagers zum Ziehen der Nummer und erweitert diese um zusätzliche Funktionalitäten. Die Methode getNextDetailNumber() am Nummernkreismanager benutzt als Datentyp long. In diesem Beispiel wird von der Anwendung eine Nummer vom Typ integer benötigt und deshalb konvertiert. Ist die gezogene Nummer größer als der maximale Integerwert, wird immer der maximale Integerwert geliefert. Anschließend wird geprüft, ob die gezogene Positionsnummer gleich der bisherigen maximalen Positionsnummer ist. Wenn ja ist das Ende des Positionsnummern-kreises erreicht und es wird eine Fehlermeldung in die Message Queue gestellt.
CisNumberRangeManager nm = env.getNumberRangeManager();
…
public int getNextDetailNumber(NumberRange nr, int maxPosNumber) {
int nextNumberInt= 0;
long nextNumberLong= nm.getNextDetailNumber(nr,
maxPosNumber, null);
if (nextNumberLong > Integer.MAX_VALUE) {
//verflow: NumberRange supports long. In this example is only an int value allowed.
nextNumberLong = Integer.MAX_VALUE;
}
nextNumberInt= (int) nextNumberLong;
if (nextNumberInt == maxPosNumber) {
//Fehlermeldung: Das Ende der Positionsnummerierung ist erreicht.
mm.sendMessage(“GEN”, 5869);
}
return nextNumberInt;
}
7.4 Benutzung von lückenlosen Nummern
Bei der lückenlosen Nummernvergabe wird Funktionalität vom Persistenzdienst verwendet, um keine Lücken entstehen zu lassen. Die Nummern werden erst beim erfolgreichen Abschluss der zugehörigen Top-Level-Transaktion mit commit() gezogen. Das bedeutet, die zugeteilte Nummer ist vorher nicht bekannt und kann so z.B. bei der Datenerfassung zum Business Object noch nicht angezeigt werden. Damit der Persistenzdienst erkennen kann, dass eine Sonderbehandlung bei der Erzeugung einer Business-Object-Instanz notwendig ist, muss die Definition des Business Objects ein paar Bedingungen erfüllen. Der logische Datentyp für das Nummernkreisattribut muss von dem logischen Datentyp com.cisag.app.general.AutoNumberRange abgeleitet sein, der für das Nummernattribut von com.cisag.app.general.AutoDocumentNumber. Beide Attribute bilden zusammen einen Index des Business Objects.
Bei der Erzeugung einer Business-Object-Instanz wird in das Nummernkreisattribut die GUID des zu verwendenden Nummernkreises gesetzt. Zur Erzwingung des automatischen Ziehens der Nummer wird das Nummernattribut entweder auf null gesetzt, oder es bekommt einen Nummern-Identifier zugewiesen. Ist das Nummernattribut leer, wird immer eine neue Nummer gezogen. Manchmal ist es notwendig, dass mehrere Business-Object-Instanzen die gleiche Nummer für denselben Nummernkreis bekommen. Im Nummernattribut kann deshalb ein beliebiger String gesetzt werden, der als Identifier dient. Der Persistenzdienst setzt dann für einen Nummernkreis bei den Business-Object-Instanzen dieselbe gezogene Nummer ein, wo der gleiche Identifier eingetragen ist. Ein Identifier ist immer pro Top-Level-Transaktion gültig. Nach dem Einfügen der Instanz mit putObject() in den Transaction-Cache wird die Transaktion mit commit() beendet. Der Persistenzdienst sucht beim Anlegen einer Business-Object-Instanz nach einem definierten Index, der aus zwei Attributen mit den oben genannten Datentypen besteht. Hat er so einen Index gefunden, zieht er automatisch eine Nummer zum übergebenen Nummernkreis im Nummernkreisattribut und speichert diese im Nummernattribut der Instanz und in der zentralen Tabelle NummernRangeUse. Wurde zu einem Nummern-Identifier schon eine Nummer gezogen, so wird diese verwendet. Das geschieht innerhalb einer Transaktion. So ist gewährleistet, dass eine lückenlose Nummernvergabe erfolgt. Werden mehrere Business-Object-Instanzen innerhalb einer Transaktion gespeichert, kann man über das spezielle Flag ORDERED_COMMIT angeben, dass die Reihenfolge der Nummernvergabe der Reihenfolge von putObject() zu entsprechen hat.
In dem folgenden Beispiel werden in einer Transaktion zwei Business-Object-Instanzen erzeugt und durch den Persistenzdienst jeweils automatisch eine Nummer vergeben. Die Reihenfolge der Nummernvergabe wird durch die Reihenfolge der putObject() bestimmt.
try (CisTransaction txn = tm.createNew (tm.ORDERED_COMMIT){
…
NumberRange nr= (NumberRange)
om.getObject(NumberRange.buildByCodeKey(“Name”));
InventoryTransaction obj= om.getObject(key, om.READ_WRITE);
obj.setNumberRange(nr.getGuid());
//Setzen des Nummernkreises
obj.setNumber(null);
//erzwingt neue Nummer
InventoryTransaction obj2=(InventoryTransaction) om.getObject(key, om.READ_WRITE);
obj2.setNumberRange(nr.getGuid());
//Setzen des Nummernkreises
obj2.setNumber(“Number-ID“);
//erzwingt neue Nummer oder Verwendung der schon früher unter der angegebenen Number-ID gezogenen Nummer
…
om.putObject(obj2);
om.putObject(obj);
…
txn.commit();
}