Merkmale (API: categories) #
Der EULANDA-Merkmalbaum ist die zentrale Filter-Struktur der Stammdaten:
Datensätze werden Baumknoten zugeordnet (Mehrfachzuordnung möglich) und
darüber gefiltert. Die API führt diese Struktur unter dem Namen
categories - der branchenübliche Begriff für einen hierarchischen
Kategoriebaum mit Mehrfachzuordnung. Abgrenzung: productGroup
(Warengruppe) ist ein einwertiges Feld am Artikel, categories ist der
mehrwertige Baum.
Unterstützt für articles und addresses (weitere Ressourcen folgen).
Endpunkte #
| Methode | Pfad | Scope | Zweck |
|---|---|---|---|
| GET | /api/v1/articles/categories | articles:read | Merkmalbaum der Artikel |
| GET | /api/v1/addresses/categories | addresses:read | Merkmalbaum der Adressen |
| GET | /api/v1/articles/{id}/categories | articles:read | Merkmale eines Artikels |
| GET | /api/v1/addresses/{id}/categories | addresses:read | Merkmale einer Adresse |
| PUT | /api/v1/{entity}/{id}/categories | <entity>:write | Zuordnung komplett ersetzen |
| PATCH | /api/v1/{entity}/{id}/categories | <entity>:write | Zuordnung ändern (add/remove) |
| POST | /api/v1/{entity}/categories | categories:manage | Knoten anlegen |
| PATCH | /api/v1/{entity}/categories/{nodeId} | categories:manage | Knoten umbenennen/verschieben |
| DELETE | /api/v1/{entity}/categories/{nodeId} | categories:manage | Knoten löschen (?force=true für Äste) |
Der Baum kommt als verschachtelte Struktur; ?lang= (bzw.
Accept-Language) steuert die Sprache des rootLabel:
{
"rootLabel": "Artikel",
"nodes": [
{
"id": 103,
"path": "\\KEYWORDS",
"name": "KEYWORDS",
"type": "folder",
"children": [
{ "id": 108, "path": "\\KEYWORDS\\EANCOM", "name": "EANCOM", "type": "manual" }
]
}
]
}
pathist der DB-Pfad ohne Stamm-Präfix - exakt dieser Wert geht in die Filter-Parameter.rootLabeldient nur der Anzeige.type:folder(Ordner),manual(Listen-Merkmal, direkte Zuordnung),smart(SQL-Merkmal, dynamische Menge - siehe unten).- Knoten mit führendem Punkt im Namen (
.USER-Zweige) sowie Arbeitslisten und CustomValues erscheinen nicht im Baum.
Die Merkmale eines Datensatzes liefern dieselben Knotenwerte, damit
der Client sie direkt gegen den Baum abgleichen kann. Ein unbekannter
Datensatz liefert 404 NOT_FOUND:
{
"items": [
{ "id": 108, "path": "\\KEYWORDS\\EANCOM", "type": "manual" },
{ "id": 124, "path": "\\Shop\\Software", "type": "manual" }
]
}
Zuordnung schreiben #
PUT ersetzt die Zuordnung komplett (leere Liste = alle Merkmale
entfernen), PATCH ändert sie im Delta. Die Antwort zählt echte
Änderungen; skipped sind No-Ops (add eines bereits zugeordneten bzw.
remove eines nicht zugeordneten Pfades):
PUT { "paths": ["\\KEYWORDS\\EANCOM", "\\Shop\\Software"] }
PATCH { "add": ["\\KEYWORDS\\EANCOM"], "remove": ["\\Shop\\Sonstiges"] }
Antwort: { "added": 1, "removed": 1, "skipped": 0 }
- Pfade zielen auf den exakten Knoten (keine Nachfahren). Ordner
dürfen zugeordnet werden, SQL-Merkmale (smart) nicht (422
CATEGORY_NOT_ASSIGNABLE). - PUT ersetzt nur den sichtbaren Bestand - Zuordnungen in
ausgeblendeten Zweigen (
.USERu.a.) bleiben unberührt. - Das Anwenden läuft in einer Transaktion; das Entfernen ist gegen den Merkmalbaum der eigenen Tabelle abgesichert.
Listen-Filter #
Jede Listen-Ressource mit Merkmalbaum versteht drei zusätzliche, wiederholbare Query-Parameter:
| Parameter | Verknüpfung |
|---|---|
categoryAnd | jeder Pfad muss zutreffen (UND) |
categoryOr | mindestens ein Pfad muss zutreffen (ODER-Gruppe) |
categoryNot | keiner der Pfade darf zutreffen (NICHT-Gruppe) |
Gesamtformel: And1 AND And2 AND (Or1 OR Or2) AND NOT (Not1 OR Not2) -
identisch zur Merkmal-Filterung in EULANDA selbst. Ein Pfad trifft zu,
wenn der Datensatz dem Knoten oder einem seiner Unterknoten zugeordnet
ist. Die Filter kombinieren sich frei mit q, Gleichheitsfiltern,
changedSince, Paging und Sortierung.
Beispiele #
$h = @{ Authorization = "Bearer $key" }
# Merkmalbaum der Artikel (deutsch)
Invoke-RestMethod 'http://localhost:8100/api/v1/articles/categories?lang=de' -Headers $h
# Artikel mit Merkmal \KEYWORDS\EANCOM, aber ohne \Shop\Sonstiges
$url = 'http://localhost:8100/api/v1/articles?fields=id,articleNumber,name' +
'&categoryAnd=' + [uri]::EscapeDataString('\KEYWORDS\EANCOM') +
'&categoryNot=' + [uri]::EscapeDataString('\Shop\Sonstiges')
Invoke-RestMethod $url -Headers $h
# ODER-Gruppe: Parameter einfach wiederholen
Invoke-RestMethod ('http://localhost:8100/api/v1/articles?categoryOr=%5CShop%5CSoftware&categoryOr=%5CShop%5CHardware') -Headers $h
curl.exe -H "Authorization: Bearer %KEY%" "http://localhost:8100/api/v1/articles/categories?lang=de"
curl.exe -H "Authorization: Bearer %KEY%" "http://localhost:8100/api/v1/articles?categoryAnd=%5CKEYWORDS%5CEANCOM&categoryNot=%5CShop%5CSonstiges&fields=id,articleNumber,name"
curl.exe -H "Authorization: Bearer %KEY%" "http://localhost:8100/api/v1/addresses?categoryOr=%5CShop&categoryOr=%5CTEST"
Der Backslash muss im Query-String URL-kodiert werden (%5C).
Baumpflege (Scope categories:manage)
#
Die Baumpflege hat bewusst einen eigenen Scope, getrennt von den Entity-Scopes - wer Datensätze pflegen darf, darf damit noch nicht die Merkmalstruktur umbauen.
Anlegen (POST): Body { "path": "\\Neu\\Unterordner", "type":
"folder" | "manual", "description"? }. Fehlende Zwischenordner legt
EULANDA selbst an (cn_MerkOpenPath). Existiert der Pfad bereits mit
demselben Typ, ist der Aufruf idempotent (200 statt 201); bei anderem
Typ 409 CATEGORY_CONFLICT.
Umbenennen/Verschieben (PATCH /{nodeId}): Body mit einer
Kombination aus name, parentId (0 = direkt unter die Wurzel),
description, color. Verschieben gilt für den ganzen Ast und ist im
Adjazenzlisten-Modell ein einziges Update - Pfade werden zur Laufzeit
berechnet, nichts muss umgeschrieben werden. Abgesichert: Zyklen (422)
und Namenskollisionen am Zielort (409 CATEGORY_CONFLICT). Achtung:
Gespeicherte Filter-Presets mit alten Pfaden zeigen nach dem
Verschieben ins Leere - wie in EULANDA selbst.
Löschen (DELETE /{nodeId}): ohne Parameter nur leere Knoten;
?force=true löscht den ganzen Ast samt aller Zuordnungen in einer
Transaktion. Antwort 204.
Bewusste Grenzen: SQL-Merkmale (smart) sind per REST weder anlegbar
noch ist ihre SqlBedingung änderbar - wer die Bedingung ändern darf,
schreibt SQL, das der Server ausführt; diese Pflege bleibt der
EULANDA-Oberfläche vorbehalten. Umbenennen/Verschieben/Löschen von
smart-Knoten ist erlaubt. Wurzel, versteckte Zweige (führender Punkt),
Arbeitslisten und CustomValues sind nicht erreichbar.
SQL-Merkmale (smart) #
SQL-Merkmale speichern statt einer Zuordnungsliste eine WHERE-Bedingung in
der Datenbank; ihre Treffermenge ist dynamisch (im Baum type: "smart").
Der Filter bindet sie automatisch ein: Ein Pfad trifft zu, wenn der
Datensatz einem Knoten des Teilbaums zugeordnet ist oder eine der
SQL-Bedingungen des Teilbaums erfüllt - exakt wie in EULANDA selbst.
Alias-Platzhalter ([THIS]., historische Tabellen-Aliasse) werden dabei
serverseitig auf die Zieltabelle umgeschrieben, ausschließlich außerhalb
von Stringliteralen und Kommentaren.
Die Bedingungen sind admin-gepflegter Datenbank-Inhalt (Vertrauensstufe
wie Views und Prozeduren). Wer sie in der API nicht möchte, setzt in der
xrest.config.json "EnableSmartCategories": false - Filter-Pfade mit
SQL-Merkmal im Teilbaum liefern dann 400 CATEGORY_SMART_DISABLED statt
stillschweigend unvollständiger Ergebnisse.
Fehler #
| Status | Code | Bedeutung |
|---|---|---|
| 400 | VALIDATION_CATEGORY_UNKNOWN | Merkmalpfad existiert nicht (Pfad steht in detail und errors[]) |
| 400 | CATEGORY_SMART_DISABLED | Pfad enthält ein SQL-Merkmal (smart) und EnableSmartCategories ist aus |
| 400 | VALIDATION_QUERY_INVALID | Ressource ohne Merkmalbaum oder ungültiger Pfad (z.B. nur der Stamm) |
| 400 | VALIDATION_CATEGORY_INVALID | Ungültiger Pfad im Request-Body (z.B. nur der Stamm) |
| 404 | NOT_FOUND | Datensatz zu /{id}/categories existiert nicht |
| 409 | CATEGORY_CONFLICT | Pfad existiert mit anderem Typ bzw. Namenskollision am Zielort |
| 409 | CATEGORY_NOT_EMPTY | Knoten hat Unterknoten/Zuordnungen und force fehlt |
| 422 | CATEGORY_NOT_ASSIGNABLE | Zuordnungsziel ist ein SQL-Merkmal (smart) |
| 422 | VALIDATION_FIELD_INVALID | Ungültiger Feldwert (z.B. type, parentId-Zyklus) |
| 422 | VALIDATION_NO_FIELDS | PUT ohne paths bzw. PATCH ohne änderbare Felder |