Aktuell umgesetzte Version ist 9.0.2
Echte Response-Beispiele aus der Praxis sind ausdruecklich willkommen. Wenn Sie konkrete Responses aus produktiven oder realistischen Testszenarien zur Verfuegung stellen koennen, hilft das sehr dabei, Parser, Datenmodell und Darstellung gezielt gegen die tatsaechlich vorkommenden Varianten zu verbessern.
Die mitgelieferte YAML/OpenAPI-Dokumentation ist nicht in allen Punkten auf dem neuesten Stand. Für die aktuelle Implementierung wurden daher zusätzlich die realen Beispiel-Responses als Referenz verwendet.
Wichtige Erkenntnisse aus den Beispiel-Responses:
- Einige Felder kommen je nach Lieferant sowohl als String als auch als numerischer JSON-Wert vor, z. B.
gtin,packagingQuantity,durabilityPeriododerstandardDeliveryPeriod. - Datumsfelder sind nicht vollständig einheitlich. Neben ISO-Datumswerten kommen auch Formate wie
YYYYMMDDvor. additional.expiringProductist nicht zuverlässig nur boolesch interpretierbar. In den Responses kommen Zustände wieNo,YesundYes-Successorvor.- In Attributlisten treten zusätzliche Felder wie
attributeClass,attributeValue2,attributeValue1DescundattributeValue2Descauf. - In einzelnen Responses existieren Feldabweichungen bzw. Inkonsistenzen wie
reachDatastattreachDate. - Bei Attribut-Beschreibungsfeldern gibt es eine Benennungsabweichung: die Doku und generierte DTOs verwenden eher
attributeValue1Descr/attributeValue2Descr, reale Lieferanten-Responses liefern jedoch auchattributeValue1Desc/attributeValue2Desc. - Dokument- und Medienlisten unterscheiden sich je nach Lieferant teilweise in Vollständigkeit und Typisierung, deshalb sollte der Parser tolerant gegen fehlende optionale Felder bleiben.
- Die Diskussion zu
prices.rawMaterialzeigt, dass einzelne Rohstoff-Beispielabbildungen in der bereitgestellten Doku fachlich widerspruechlich oder spaeter als fehlerhaft korrigiert sind. Insbesondere die Kombination ausweightBasis,basisUnit,proportionByWeightundquotationOfRawMaterialsollte immer gegen aktuelle Herstellerbeispiele oder abgestimmte Fachinterpretationen geprueft werden. - Fuer Rohstoffangaben ist relevant, dass
rawMaterialmehrfach vorkommen kann. Die Daten sollten daher als Liste und nicht als Einzelobjekt behandelt werden. - In der Diskussion wird zusaetzlich ein moegliches Feld
rawMaterial/materialpriceerwaehnt. Dieses Feld ist nicht Teil der aktuell umgesetzten 9.0.2-Struktur und wird in der Bibliothek derzeit nicht geparst.
Die aktuelle Delphi-Implementierung ist auf diese Abweichungen ausgelegt und versucht, die Daten möglichst robust und verlustarm zu laden.
Aktuell berücksichtigt der Loader insbesondere folgende Fälle:
- Robustes Einlesen von String- und Zahlenwerten für identische Fachfelder.
- Robustes Parsen gängiger Datumsformate aus den bekannten Lieferanten-Responses.
- Unterstützung der neueren Attributfelder in
additional.attribute. - Unterstützung beider Schreibweisen bei Attribut-Beschreibungen:
...Descund...Descr. - Unterstützung des erweiterten Auslaufstatus über
expiringProduct. - Fallback von
reachDateaufreachData. - Unterstützung zusätzlicher Dokumenttypen wie
PL. - Unterstützung von Rohstofflisten unter
prices.rawMaterial, inklusiveweightBasis,basisUnit,proportionByWeight,proportionUnit,quotationOfRawMaterialundcurrentQuotationOfRawMaterial. - Sichtbare HTML-Ausgabe für Alternativartikel, Nachfolgeartikel, Zubehörartikel und Rohstoffangaben.
- JSON-
nullwird als leerer Wert behandelt und nicht als Textnullübernommen. - GTIN-Werte mit führender Null werden verlustfrei gelesen, obwohl sie formal kein gültiges JSON sind.
- Achtstellige Datumsangaben werden sowohl als
YYYYMMDDals auch alsDDMMYYYYerkannt. - Preisstaffeln bleiben vollständig erhalten:
listPrice,netPriceundrrpführen weiterhin die erste Stufe, alle Stufen stehen zusätzlich inlistPriceScale,netPriceScaleundrrpScale. - Fallback von
weightauf die Spec-Schreibweiseweigthinlogistics, analog zum bereits vorhandenenheigth. - Unterstützung von
additional.attributesim Plural, wie ihn einzelne Lieferanten senden. Document.languageundLinePrice.descriptiionaus der Spec 9.0.0.- Der optionale Query-Parameter
customerIdlässt sich überSetCustomerIdsetzen. - Die Sonderstatus
950und951liefern laut Spezifikation ein vollständiges Produkt, nämlich den Alternativ- bzw. Nachfolgeartikel. Deren Antwort wird geparst; der Statuscode bleibt überGetLastErrorCodeabfragbar.
Antworten mit 429, 502, 503 oder 504 werden standardmäßig zweimal wiederholt, mit 1 und 2 Sekunden Abstand. Alle übrigen Statuscodes werden nicht wiederholt, weil sie beim zweiten Versuch dieselbe Antwort ergäben.
Nennt der Server im Header Retry-After eine Wartezeit in Sekunden, hat diese Vorrang. Liegt sie über der zugestandenen Obergrenze von 10 Sekunden, wird nicht gewartet, sondern der Fehler gemeldet. Hintergrund: der Aufruf blockiert, und eine Anwendung, die eine Minute lang nicht reagiert, wirkt abgestürzt. Der Aufrufer kann anhand von GetLastErrorCode selbst entscheiden, ob und wann er es erneut versucht.
Beides lässt sich anpassen, etwa für Hintergrunddienste ohne Oberfläche:
client.SetRetryPolicy(3,60); //drei Wiederholungen, bis zu 60 Sekunden Wartezeit
client.SetRetryPolicy(0,0); //Wiederholungen abschaltenBei Sammelabrufen ist zu beachten, dass sich die Wartezeiten über alle Artikel summieren.
Die folgenden Felder sind noch nicht Teil der umgesetzten Version 9.0.2. Sie werden bereits gelesen, damit nichts verlorengeht, sobald ein Echtsystem sie liefert. In Antworten nach 9.0.2 bleiben sie leer.
statusje Artikel (200,404,950,951,952,960), als Zahl inTOpenMasterdataAPI_Result.status.0bedeutet, dass die Antwort kein Statusfeld enthält.prices.promotionalPriceals Liste von Aktionspreisen mitstartOfValidityundendOfValidity.lowerBoundan Preisen, die untere Staffelgrenze. In OM 11 istnetPriceeine Staffel ausBulkPrice; die Werte stehen innetPriceScale.basic.noOrderBefore,basic.noDeliveryBefore,basic.noMarketingBefore.basic.sparepartsystemURLundbasic.sparepartsystemdescription.additional.accessorieGroupIdManufacturerundadditional.accessorieGroupDescrManufacturer.
Da die Bibliothek künftig gegen reale Antworten statt gegen die Dokumentation abgeglichen wird, sollten neue Beispiel-Responses immer über die Tests geprüft werden.
Unter Tests liegt ein Konsolenprogramm mit Regressionstests für den Parser und die HTML-Ausgabe.
Tests\run-tests.bat
Das Skript sucht eine installierte Delphi-Version, kompiliert die Tests und führt sie aus. Der Rückgabewert ist 0, wenn alle Tests bestanden wurden. Liegt der Ordner Testresponses vor, wird zusätzlich jede dort abgelegte Lieferanten-Antwort als Smoketest geparst.
Neue Beispiel-Responses lassen sich damit direkt gegen die vorhandene Parserlogik prüfen.
Die Tests unter Tests arbeiten ohne Netzwerk. Ob die hinterlegten Zugänge noch gelten, prüft ein zweites Konsolenprogramm:
Samples\LoginTest\run-logintest.bat
Es liest Samples\configuration.ini, meldet sich bei jedem darin konfigurierten Lieferanten an und ruft eine Artikelnummer aus ArtNoAsCommatext ab. Ausgegeben werden nur der Endpunkt, das Ergebnis und im Fehlerfall die Antwort des Servers — nicht die Zugangsdaten und nicht die OAuth-Antwort, die Zugriffs- und Refresh-Token im Klartext enthält.
run-logintest.bat Sonepar nur Zugänge, deren Name das enthält
run-logintest.bat Sonepar cc zusätzlich den Grant-Type übersteuern
Der zweite Parameter (pw oder cc) hilft bei der Eingrenzung, wenn ein Endpunkt den konfigurierten Grant-Type ablehnt. Der Rückgabewert ist 0, wenn sich alle geprüften Zugänge anmelden konnten und einen Artikel geliefert haben.
Abgefragt werden die für den Lieferanten konfigurierten Datenpakete in einem Aufruf; die Antwort wird eingelesen und es wird gemeldet, welche Bereiche tatsächlich gefüllt sind. Scheitert dieser Abruf, sucht das Programm die Ursache: es wiederholt zuerst denselben Aufruf unverändert — gelingt er dann, war die Störung vorübergehend —, probiert danach den jeweils anderen DataPackageSendMode und schließlich jedes Datenpaket einzeln. Damit lässt sich unterscheiden, ob ein Lieferant die Paketliste anders erwartet oder ob er ein bestimmtes Datenpaket nicht ausliefern kann.
Welche Datenpakete abgefragt werden, steuert der optionale Schlüssel DataPackages je Lieferant, etwa DataPackages=basic,descriptions,logistics,pictures,documents. Als Trenner gelten Komma, Semikolon, senkrechter Strich, Leerzeichen, Tabulator und Zeilenumbruch. Das hilft bei Lieferanten, die ein einzelnes Paket nicht ausliefern können und die gesamte Abfrage daran scheitern lassen.
Fehlt der Schlüssel, ist er leer oder nennt er kein einziges bekanntes Paket, werden alle Pakete angefragt — eine leere Auswahl würde jede Abfrage scheitern lassen. Nicht erkannte Namen werden übergangen und zusätzlich gemeldet, damit ein Tippfehler nicht unbemerkt bleibt.
Als Vorlage für die Konfiguration dient Samples\configuration.sample.ini. Die echte configuration.ini enthält Zugangsdaten und ist von der Versionsverwaltung ausgenommen.
Die Dokumentation zu den Rohstoffangaben ist nicht durchgehend konsistent. In der mitgelieferten Diskussion zu rawMaterial wird ein urspruengliches Beispiel spaeter ausdruecklich als fachlich fehlerhaft bezeichnet.
Fuer die Implementierung bedeutet das:
prices.rawMaterialwird als Liste geladen, da mehrere Rohstoffzuschlaege pro Artikel vorkommen koennen.- Die aktuell umgesetzten Felder sind
material,weightBasis,basisUnit,proportionByWeight,proportionUnit,quotationOfRawMaterialundcurrentQuotationOfRawMaterial. - Die fachliche Bedeutung von
weightBasisundbasisUnitsollte bei neuen Lieferanten nicht allein aus der Doku abgeleitet werden, sondern immer gegen echte Responses oder abgestimmte Fachbeispiele verifiziert werden. - Das in der Diskussion genannte Feld
materialpriceist derzeit nicht Teil des Parsers, weil es in den bisher beruecksichtigten 9.x-Beispielen nicht stabil als Response-Feld belegt ist.
Wenn neue Lieferanten angebunden werden, sollten die gelieferten Beispiel-Responses immer gegen die vorhandene Parserlogik geprüft werden, auch wenn sie formal zur 9.x-Dokumentation passen.
Weitere Informationen unter
- https://www.itek.de/beratung/open-masterdata
- https://itek-branchenwissen.atlassian.net/wiki/spaces/DS/pages/535593021/Open+Masterdata
Die Tabelle hält fest, was die Lieferanten in der Praxis erwarten. Die Spaltennamen entsprechen den Schlüsseln in configuration.ini, ausgewertet wird davon derzeit allein CustomernumberRequired; ClientIDRequired, UsernameRequired und ClientSecretRequired sind Notizen für die Einrichtung und werden vom Code nicht gelesen.
CustomernumberRequired entscheidet, ob die Kundennummer Teil der Anmeldung ist. Verlangt ein Lieferant sie, wird sie mit einem Tabulator getrennt an den Benutzernamen gehängt; ist kein Benutzername gesetzt, geht sie allein als username hinaus. Verlangt er sie nicht, muss sie beim Login außen vor bleiben, sonst weist der Server die Zugangsdaten zurück. Fehlt der Schlüssel, wird eine eingetragene Kundennummer gesendet.
In der Konfiguration sind True/False die üblichen Werte; ja/nein werden ebenfalls verstanden.
| Lieferant | ClientIDRequired | GrantType | DataPackageSendMode | UsernameRequired | CustomerNumberRequired | ClientSecretRequired |
|---|---|---|---|---|---|---|
| MAINMETALL Grosshandelsgesellschaft m.b.H. | ja | password | pipedelimited | ja | nein | nein |
| GC-Gruppe GC ONLINE PLUS | ja | client_credentials | pipedelimited | ja | nein | ja |
| WIEDEMANN GmbH & Co. KG | ja | password | pipedelimited | ja | nein | nein |
| HSH Rose GmbH | ja | password | pipedelimited | ja | nein | ja |
| Mosecker Osnabrueck | ja | password | pipedelimited | ja | nein | ja |
| Buderus Deutschland | ja | password | pipedelimited | ja | nein | ja |
| FEGA & Schmitt Elektrogroßhandel GmbH | ja | password | pipedelimited | ja | ja | ja |
| Pietsch Haustechnik GmbH | ja | password | pipedelimited | ja | nein | nein |
| Sanitär-Heinze GmbH & Co. KG | ja | password | pipedelimited | ja | nein | nein |
| Friedrich Lange GmbH | ja | password | pipedelimited | ja | ja | ja |
| Sonepar | ja | password | exploded | ja | ja | nein |
| Richter+Frenzel | ja | password | pipedelimited | ja | nein | ja |
| Reisser AG | ja | password | pipedelimited | ja | ja | nein |
| Viessmann | ja | password | pipedelimited | ja | Nein | Ja |
License OpenMasterdata-for-Delphi
Copyright (C) 2026 Landrix Software GmbH & Co. KG Sven Harazim, info@landrix.de
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Zwei Änderungen können bestehenden Code betreffen:
- Die Objekt- und Listen-Properties der Datentypen sind schreibgeschützt (
readstattread/write), etwaprices.listPrice,logistics.measureAoderadditional.attribute. Eine Zuweisung von außen hätte die im Konstruktor erzeugte Instanz lecken lassen. Die Objekte selbst sind unverändert veränderbar, nur das Ersetzen der Instanz entfällt. - Die Aufzählungstypen
TOpenMasterdataAPI_CarryingCategory,TOpenMasterdataAPI_PackageTypeundTOpenMasterdataAPI_RawMaterialhaben neue Werte erhalten. Dadurch verschieben sich die Ordinalwerte der bestehenden Einträge. Wer diese Werte als Zahl gespeichert hat, muss die Daten umsetzen.omdCarryingCategory_Nonesteht neu an erster Stelle, weilomdCarryingCategory_0eine gültige Beförderungskategorie ist und nicht „nicht angegeben" bedeutet.
Geändertes Verhalten bei gleicher Signatur:
LoadFromJsonundTryLoadFromJsonleeren das Ergebnisobjekt vor jedem Ladevorgang. Ein wiederverwendetes Objekt behält damit keine Werte des zuvor geladenen Artikels mehr.NewOpenMasterdataConnectionübernimmt bei bereits bekanntem Verbindungsnamen die übergebenen Zugangsdaten. Weichen sie ab, wird der bisherige Token verworfen.GetLastErrorCodeliefert nach einem erfolgreichen Abruf 0. Bei den Sonderstatus 950 und 951 bleibt der Statuscode erhalten, obwohl der Abruf als erfolgreich gilt.AsHtmlreicht Lieferanten-HTML nicht mehr unverändert durch. Nicht freigegebene Tags und sämtliche Attribute werden entfernt, Adressen nur mit den Schematahttp,httpsundmailtoverlinkt.- Fehlgeschlagene Bild- und Dokumentdownloads werden nicht mehr zwischengespeichert, sondern beim nächsten Zugriff erneut versucht.