Konzept: kit.common.ix.functions.applyDynKwlMap
Ziel
Es soll eine generische ELO Indexserver-Funktion entstehen, die aus einem Workflow heraus per JSON-Konfiguration eine dynamische Stichwortliste aufruft und deren Ergebnis kontrolliert in ELO-Map-Tabellenfelder schreibt.
Die Funktion dient zunächst als sauberer Datenlieferant und Schreibbaustein für Tabellenfelder in der ELO Invoice Solution. Später soll darauf eine Wareneingangsprüfung mit Merge-/Abgleichlogik aufgebaut werden.
Neuer Namespace:
kit.common.ix.functions.applyDynKwlMap
RF-Funktion:
RF_kit_function_applyDynKwlMap
Ausgangssituation
In der ELO Invoice Solution werden Tabellen über Map-Felder abgebildet.
Beispiel:
IX_MAP_PURCHASE_ORDER_NO1
IX_MAP_PURCHASE_ORDER_NO2
IX_MAP_PURCHASE_ORDER_NO3
Dabei steht die Zahl am Ende für die Tabellenzeile.
Zu einer Bestellung können weitere Felder existieren, z. B.:
IX_MAP_PURCHASE_ORDER_DELIVERY_NO1
IX_MAP_PURCHASE_ORDER_DELIVERY_NO2
IX_MAP_PURCHASE_ORDER_DELIVERY_NO3
Die Bestellnummer kann automatisch erkannt oder manuell im Formular eingetragen worden sein. Das ist für die neue Funktion zunächst egal.
Es gibt bereits dynamische Stichwortlisten, die anhand von Bestellnummer, Lieferscheinnummer oder anderen Eingabewerten Positionen aus einer Datenbank laden und Tabellenfelder füllen können.
Diese bestehende DynKwl-Logik soll weiterverwendet werden.
Fachliches Ziel
Die Funktion soll aus dem Workflow heraus aufgerufen werden können und über JSON vollständig steuerbar sein.
Sie soll:
- das aktuelle Dokument/Sord laden
- die vorhandenen Map-Felder lesen
- anhand definierter Lookup-Felder eine oder mehrere Tabellenzeilen auswerten
- pro Zeile eine dynamische Stichwortliste aufrufen
- das Ergebnis der Stichwortliste in strukturierte Zeilen umwandeln
- die Ergebniszeilen je nach Modus in die Zieltabelle schreiben
- optional Statusinformationen schreiben
- optional im DryRun nur protokollieren, aber nichts ändern
Beispiel für Workflow-Konfiguration
{
"scriptName": "sol.invoice.ix.dynkwl.PurchaseOrderLine",
"source": {
"maxLines": 50,
"lookupFields": [
"IX_MAP_PURCHASE_ORDER_NO"
],
"lookupFieldsOptional": [
"IX_MAP_PURCHASE_ORDER_DELIVERY_NO"
],
"focusField": "IX_MAP_PURCHASE_ORDER_NO",
"stopOnEmptyRequiredLookup": true
},
"target": {
"mode": "append",
"appendNewRows": true,
"overwriteExisting": false
},
"options": {
"dryRun": false,
"writeStatus": true,
"statusPrefix": "IX_MAP_DYNKWL_APPLY"
}
}
Geplante Modi
1. append
Die Ergebniszeilen der dynamischen Stichwortliste werden immer unten an die bestehende Tabelle angehängt.
Dieser Modus entspricht grob dem bisherigen Verhalten beim manuellen Button „Bestellpositionen laden“.
Vorteil:
- einfach
- gut für manuelles Nachladen
Nachteil:
- kann Duplikate erzeugen
- kein fachlicher Abgleich
Beispiel:
"target": {
"mode": "append",
"appendNewRows": true,
"overwriteExisting": false
}
2. overwrite
Die Ergebniszeilen werden ab einer definierten Startzeile in die Tabelle geschrieben und vorhandene Werte werden überschrieben.
Dieser Modus ist gefährlich und sollte nicht Standard sein.
Beispiel:
"target": {
"mode": "overwrite",
"startLine": 1,
"overwriteExisting": true
}
3. singleLine
Die Ergebniswerte werden in dieselbe Tabellenzeile geschrieben, aus der der Lookup-Wert stammt.
Beispiel:
"target": {
"mode": "singleLine",
"overwriteExisting": true
}
Beispiel:
Lookup aus PURCHASE_ORDER_NO3
→ Ergebnis wird in Zeile 3 geschrieben
4. merge
Die Ergebniszeilen werden anhand definierter Schlüsselfelder mit vorhandenen Tabellenzeilen abgeglichen.
Beispiel:
"target": {
"mode": "merge",
"appendNewRows": true,
"overwriteExisting": false,
"mergeKeyFields": [
"IX_MAP_PURCHASE_ORDER_NO",
"IX_MAP_PURCHASE_ORDER_LINE_NO",
"IX_MAP_PURCHASE_ORDER_DELIVERY_NO"
]
}
Logik:
Wenn Merge-Schlüssel bereits vorhanden:
- nicht überschreiben, außer overwriteExisting = true
Wenn Merge-Schlüssel nicht vorhanden:
- neue Tabellenzeile anhängen
Wenn Zielzeile geschützt ist:
- ignorieren
Späterer Einsatz für Wareneingangsprüfung
Für die geplante Wareneingangsprüfung soll die Funktion später im Merge-Modus genutzt werden.
Fachliche Regel:
Zeilen mit bereits vorhandener Lieferschein-/Wareneingangsnummer werden standardmäßig ignoriert.
Automatische Prüfung darf vorhandene manuelle Werte nicht überschreiben.
Überschreiben ist nur mit explizitem Parameter erlaubt.
Passende Konfiguration:
{
"scriptName": "sol.invoice.ix.dynkwl.PurchaseOrderLine",
"source": {
"maxLines": 100,
"lookupFields": [
"IX_MAP_PURCHASE_ORDER_NO"
],
"lookupFieldsOptional": [
"IX_MAP_PURCHASE_ORDER_DELIVERY_NO"
],
"focusField": "IX_MAP_PURCHASE_ORDER_NO",
"stopOnEmptyRequiredLookup": true
},
"target": {
"mode": "merge",
"appendNewRows": true,
"overwriteExisting": false,
"mergeKeyFields": [
"IX_MAP_PURCHASE_ORDER_NO",
"IX_MAP_PURCHASE_ORDER_LINE_NO",
"IX_MAP_PURCHASE_ORDER_DELIVERY_NO"
],
"ignoreTargetRowsWhere": [
{
"field": "IX_MAP_PURCHASE_ORDER_DELIVERY_NO",
"operator": "notEmpty"
}
]
},
"options": {
"dryRun": false,
"writeStatus": true,
"statusPrefix": "IX_MAP_GR_CHECK"
}
}
Wichtige Designentscheidung
Die neue Funktion soll sauberer aufgebaut werden als das alte Script.
Das alte Script mischt:
DynKwl-Aufruf
Datenumwandlung
Schreiblogik
Tabellenlogik
Die neue Funktion soll diese Bereiche trennen.
Geplante interne Struktur:
process()
loadSord()
loadMap()
collectLookupContexts()
callDynKwl()
convertDynKwlResultToRows()
applyRows()
writeStatus()
Geplante interne Methoden
process()
Hauptablauf.
Aufgaben:
- Konfiguration prüfen
- Sord laden
- Map lesen
- Lookup-Kontexte sammeln
- DynKwl pro Kontext aufrufen
- Ergebnisse sammeln
- Ergebniszeilen schreiben
- Status schreiben
collectLookupContexts(map, config)
Liest die Tabellenzeilen anhand der Lookup-Felder.
Beispiel aus Map:
PURCHASE_ORDER_NO1 = 45000123
PURCHASE_ORDER_DELIVERY_NO1 = leer
PURCHASE_ORDER_NO2 = 45000456
PURCHASE_ORDER_DELIVERY_NO2 = LS-7788
Ergebnis:
[
{
"sourceLine": 1,
"mapData": {
"IX_MAP_PURCHASE_ORDER_NO": "45000123",
"IX_MAP_PURCHASE_ORDER_DELIVERY_NO": ""
}
},
{
"sourceLine": 2,
"mapData": {
"IX_MAP_PURCHASE_ORDER_NO": "45000456",
"IX_MAP_PURCHASE_ORDER_DELIVERY_NO": "LS-7788"
}
}
]
callDynKwl(context, config)
Ruft die dynamische Stichwortliste auf.
Prinzip:
var keywordsDynamicInfo = new KeywordsDynamicInfo();
keywordsDynamicInfo.mapData = context.mapData;
keywordsDynamicInfo.mapLineFocus = config.source.focusField;
keywordsDynamicInfo.mapScriptName = config.scriptName;
var result = ixConnect.ix().checkoutKeywordsDynamic(keywordsDynamicInfo);
convertDynKwlResultToRows(result)
Wandelt das ELO-DynKwl-Ergebnis in ein sauberes Array aus Objekten um.
Beispiel:
[
{
"IX_MAP_PURCHASE_ORDER_NO": "45000123",
"IX_MAP_PURCHASE_ORDER_LINE_NO": "10",
"IX_MAP_PURCHASE_ORDER_DELIVERY_NO": "LS-1001",
"IX_MAP_PURCHASE_ORDER_ITEM_NO": "A-123",
"IX_MAP_PURCHASE_ORDER_QUANTITY": "5"
}
]
applyRows(rows, map, sord, config)
Verteilt je nach Modus:
append
overwrite
singleLine
merge
applyAppend(rows, map, sord, config)
Hängt Ergebniszeilen unten an die bestehende Tabelle an.
applyOverwrite(rows, map, sord, config)
Schreibt Ergebniszeilen ab einer Startzeile und überschreibt vorhandene Werte.
applySingleLine(rows, sourceLine, map, sord, config)
Schreibt Ergebniswerte in die Quellzeile.
applyMerge(rows, map, sord, config)
Späterer Modus für fachlichen Abgleich.
Aufgaben:
- bestehende Zielzeilen lesen
- Merge-Key je bestehender Zeile bilden
- Merge-Key je neuer Ergebniszeile bilden
- vorhandene Schlüssel überspringen oder überschreiben
- neue Schlüssel unten anhängen
- geschützte Zeilen ignorieren
Feldnamen-Normalisierung
ELO DynKwl arbeitet häufig mit Feldnamen wie:
IX_MAP_PURCHASE_ORDER_NO
IX_GRP_VENDOR_NO
Map-Felder werden aber intern oft ohne Prefix gelesen/geschrieben:
PURCHASE_ORDER_NO1
Daher brauchen wir Hilfsfunktionen.
normalizeFieldName(fieldName)
normalizeFieldName: function (fieldName) {
if (!fieldName) {
return "";
}
if ((fieldName.indexOf("IX_MAP_") === 0) || (fieldName.indexOf("IX_GRP_") === 0)) {
return fieldName.substring(7);
}
return fieldName;
}
getLineFieldName(fieldName, lineNo)
getLineFieldName: function (fieldName, lineNo) {
return this.normalizeFieldName(fieldName) + String(lineNo);
}
Beispiel:
IX_MAP_PURCHASE_ORDER_NO + 1
→ PURCHASE_ORDER_NO1
Merge-Key
Für den späteren Merge wird ein fachlicher Schlüssel gebildet.
Beispiel:
buildMergeKey: function (row, mergeKeyFields) {
var parts = [];
mergeKeyFields.forEach(function (fieldName) {
var value = row[fieldName] || "";
parts.push(String(value).trim());
});
return parts.join("|");
}
Beispiel-Ergebnis:
45000123|10|LS-1001
Wichtig:
Die Lieferanten-Lieferscheinnummer ist fachlich nützlich, aber technisch nicht immer eindeutig. Wenn möglich, sollte später ein robusterer Schlüssel verwendet werden:
Bestellnummer
Bestellposition
Wareneingangsbelegnummer
Wareneingangsposition
Falls diese Felder nicht verfügbar sind, kann zunächst gearbeitet werden mit:
Bestellnummer
Bestellposition
Lieferscheinnummer
Schutzlogik gegen Überschreiben
Für die spätere Wareneingangsprüfung gilt:
Wenn IX_MAP_PURCHASE_ORDER_DELIVERY_NOx bereits gefüllt ist,
darf diese Zeile standardmäßig nicht überschrieben werden.
Beispielkonfiguration:
"ignoreTargetRowsWhere": [
{
"field": "IX_MAP_PURCHASE_ORDER_DELIVERY_NO",
"operator": "notEmpty"
}
]
Bedeutung:
Alle Zielzeilen, bei denen PURCHASE_ORDER_DELIVERY_NOx nicht leer ist,
werden beim automatischen Merge ignoriert.
DryRun
Die Funktion soll einen DryRun-Modus unterstützen.
"options": {
"dryRun": true
}
Dann soll nichts geschrieben werden.
Stattdessen soll geloggt werden:
- welche Lookup-Zeilen gefunden wurden
- welche DynKwl aufgerufen wurde
- wie viele Ergebniszeilen zurückkamen
- welche Felder geschrieben würden
- welche Zeilen übersprungen würden
- welche Zeilen angehängt würden
DryRun ist wichtig für Tests im Kundensystem.
Statusfelder
Optional sollen Statusfelder geschrieben werden können.
Beispiel:
"options": {
"writeStatus": true,
"statusPrefix": "IX_MAP_DYNKWL_APPLY"
}
Mögliche Felder:
IX_MAP_DYNKWL_APPLY_STATUS
IX_MAP_DYNKWL_APPLY_MESSAGE
IX_MAP_DYNKWL_APPLY_DATE
IX_MAP_DYNKWL_APPLY_COUNT
Für WE-Prüfung später:
IX_MAP_GR_CHECK_STATUS
IX_MAP_GR_CHECK_MESSAGE
IX_MAP_GR_CHECK_DATE
IX_MAP_GR_CHECK_COUNT
Erste Umsetzungsphase
Nicht direkt alles bauen.
Version 1
Ziel: Saubere generische DynKwl-Ausführung.
Umfang:
- Namespace kit.common.ix.functions.applyDynKwlMap
- Workflow-Aufruf über onEnterNode/onExitNode
- RF-Aufruf RF_kit_function_applyDynKwlMap
- JSON-Parameter lesen
- Sord und Map lesen
- Lookup-Kontexte aus Tabellenzeilen sammeln
- DynKwl aufrufen
- Ergebnis in Rows umwandeln
- Modus append
- Modus overwrite
- Modus singleLine
- dryRun
- Logging
Noch nicht zwingend:
- vollständiger Merge-Modus
- WE-spezifische Logik
- komplexe Duplikatprüfung
Version 2
Ziel: Merge-Modus.
Umfang:
- bestehende Zielzeilen lesen
- Merge-Key bilden
- neue Ergebniszeilen gegen Bestand prüfen
- appendNewRows
- overwriteExisting
- ignoreTargetRowsWhere
Version 3
Ziel: Wareneingangsprüfung.
Umfang:
- Bestellung + Position + Lieferschein/WE als fachlicher Schlüssel
- Zeilen mit vorhandener Lieferscheinnummer ignorieren
- nur fehlende WE-Zeilen ergänzen
- kein Überschreiben ohne expliziten Parameter
- Status zur WE-Prüfung schreiben
Bekannte Probleme im alten Script
Das alte Script sollte nicht direkt weiter ausgebaut werden, ohne es zu bereinigen.
Auffälligkeiten:
- DynKwl-Abfrage und Schreiben sind vermischt
- fillMap schreibt direkt per checkinMap
- dirty wird nicht sauber zurückgegeben
- RF-Funktion erzeugt vermutlich falschen Klassennamen
- maxLines ist nicht sauber abgesichert
- focusField und scriptName sollten required sein
- optionale Lookup-Felder sollten aktiv geleert werden, wenn leer
- append-Modus prüft keine Duplikate
Daher neue Implementierung unter KIT-Namespace statt Änderung am alten Script.
Zielbild
Am Ende soll es zwei wiederverwendbare Bausteine geben:
1. kit.common.ix.functions.applyDynKwlMap
Generischer Baustein:
DynKwl aufrufen und Ergebnisse kontrolliert in Map-Tabellen schreiben.
2. spätere WE-Prüffunktion
Fachlicher Baustein:
Wareneingänge prüfen, vorhandene Zeilen analysieren, neue WE-Zeilen mergen.
Die WE-Prüffunktion soll später möglichst denselben Datenlieferanten verwenden und nicht nochmal eigene DB-Abfragen duplizieren.
Wichtigster Grundsatz
Die automatische Verarbeitung muss nicht-destruktiv sein.
Standard:
Keine vorhandenen Lieferschein-/Wareneingangszeilen überschreiben.
Keine manuellen Korrekturen zerstören.
Neue Daten nur ergänzen.
Überschreiben nur über expliziten Parameter.