microtech GraphQL - Skill-Referenz
Kompakte Referenz fuer die GraphQL-Schnittstelle des microtech ERP.
Optimiert fuer Query-Generierung ueber einen MCP-Server mit
-Tool.
ASCII: Dieses Dokument verwendet
statt Umlaute.
WICHTIG - Introspection: NIEMALS das vollstaendige Schema abrufen (
). Stattdessen gezielt einzelne Typen abfragen. Siehe
references/introspection-guide.md
.
Feldkataloge: Fuer detaillierte Feldlisten pro Tabelle siehe
references/feldkatalog-*.md
.
Erweiterte Themen: Fuer Archiv-Funktionen, externe Bearbeitung, Berechtigungen, Cross-Table-Patterns siehe
references/vorgaenge-erweitert.md
.
Mutation-Patterns: Fuer Optimistic Locking, rowCopy, ifNotExists, Deep Nesting, Massenoperationen siehe
references/mutation-patterns.md
.
Query-Patterns: Fuer keyFilter-Details, Arithmetik, skip-Wert, erweitertes @onNull, Schema-Metadaten siehe
references/erweiterte-query-patterns.md
.
Adressen-Verwaltung: Praxisbeispiele fuer Adressen, Anschriften und Ansprechpartner (lesen, anlegen, aendern, loeschen) mit verschachtelten Links, Optimistic Locking, rowCopy, Massenoperationen und assign-Parametern in
references/adressen-verwaltung.md
.
Bei Adress-Aufgaben dort zuerst nachschlagen!
Parametertabellen: Konfigurations- und Parametertabellen auslesen (Vorgangsarten, Zahlungsbedingungen, Steuerschluessel, Einheiten, Versandarten, Waehrungen, Mahnstufen, Projektstatus u.v.m.) in
references/parametertabellen.md
.
Bei Fragen zur mandantenspezifischen Konfiguration dort nachschlagen!
Workflow-Beispiele: 17 vollstaendige Szenarien (Rechnung erstellen, Vorgang wandeln, Storno, Lagerbestand u.v.m.) in
references/workflow-beispiele.md
.
Bei komplexen Aufgaben dort zuerst nachschlagen!
Kritische Regeln (Top 9)
- keyFilter-First: Bei JEDEM Filter zuerst pruefen, ob ein passender -Index existiert und verwenden. Erst wenn kein passender Index vorhanden ist, auf oder zurueckfallen. Siehe Abschnitt 5 fuer den verbindlichen Workflow.
- @oneOf-Regel: Pro Filterobjekt nur EIN Operator. Mehrere Bedingungen mit / kombinieren.
- // nur in -Kontext (nicht !), immer in .
- vor - ohne sind Positionen nicht gespeichert und werden nicht gebucht.
- Nach nicht mehr schreiben im selben Block - fuehrt zu Laufzeitfehler und Rollback.
- Leere Belegnummer = Operation fehlgeschlagen - immer in -Rueckgaben pruefen.
- Prozessketten-Integritaet - NIEMALS fn-Funktionen durch manuelle Operationen ersetzen!
ERP-Prozessfunktionen (, , , ) sind die EINZIGE korrekte Art, den Belegstatus zu veraendern. Sie sichern die Prozesskette (z.B. Angebot → AB → Lieferschein → Rechnung), verknuepfen Belege, aktualisieren Lagerbestaende, erzeugen Buchungssaetze und fuehren Plausibilitaetspruefungen durch. Wenn eine fn-Funktion fehlschlaegt, STOPPEN und den Nutzer informieren - KEINEN Workaround versuchen.
- NIEMALS als Ersatz fuer - ein manuell angelegter Folgebeleg hat KEINE Verknuepfung zum Vorgaenger. Die Prozesskette ist unterbrochen, Mengen werden nicht korrekt gefuehrt, Lieferstatus wird nicht aktualisiert. Stattdessen: Fehlerursache klaeren (Wandlungspfad nicht konfiguriert, Beleg bereits archiviert, etc.) und Nutzer fragen.
- NIEMALS als Ersatz fuer - eine Loesung per umgeht die Storno-Logik: keine Storno-Belegnummer, keine Gegenbuchungen, keine Lagerkorrektur, kein Audit-Trail. Stattdessen: verwenden, bei Fehler Nutzer informieren.
Arbeitsweise: So bearbeitest du Nutzeranfragen
Schritt 1: Anfrage klassifizieren
- Lesen/Suchen → (Daten anzeigen, suchen, auflisten, zaehlen)
- Schreiben/Aendern → (anlegen, aendern, loeschen, buchen, wandeln)
- Unbekannte Felder/Tabelle → Erst Introspection (
references/introspection-guide.md
)
Source of Truth: Bei Unsicherheit ueber Feldnamen, Schreibbarkeit oder verfuegbare Operationen IMMER die Live-API per Introspection pruefen. Dokumentierte Feldlisten koennen veraltet sein - die API ist die einzig zuverlaessige Quelle.
Schritt 2: Tabelle identifizieren
- Nutze die Tabelle aus "Schnellreferenz" unten
- Bei Unsicherheit: Introspection oder Nutzer fragen
Schritt 3: Reference-Dateien konsultieren
BEVOR du eine Query oder Mutation baust: Pruefe ob ein passendes Beispiel oder Detail-Wissen existiert.
- Komplexe Aufgabe? → Zuerst
references/workflow-beispiele.md
durchsuchen (17 Szenarien)
- Feldnamen unklar? → Passenden
references/feldkatalog-*.md
lesen
- Filter-Logik? →
references/erweiterte-query-patterns.md
(keyFilter, Arithmetik, Datums-Filter)
- Adressen/Anschriften/Ansprechpartner? →
references/adressen-verwaltung.md
(verschachtelte Links, rowReAnsNr, assign-Parameter, Massenoperationen)
- Mutation-Pattern? →
references/mutation-patterns.md
(Optimistic Locking, rowCopy, Deep Nesting)
- Vorgangsfunktionen? →
references/vorgaenge-erweitert.md
(Archiv, externe Bearbeitung)
- Parametertabellen/Konfiguration? →
references/parametertabellen.md
(Vorgangsarten, Steuerschluessel, Zahlungsbedingungen, Einheiten, Versandarten, Waehrungen, Mahnstufen, Projektstatus, Dokumentenarten u.v.m.)
- Introspection noetig? →
references/introspection-guide.md
(Typ-Namenskonventionen)
Nicht raten - nachschlagen! Die Reference-Dateien enthalten getestete, korrekte Beispiele. Lieber 30 Sekunden lesen als eine fehlerhafte Query bauen.
Schritt 4a: Query bauen (Lesen)
- Filter-Strategie (keyFilter-First!):
- Gibt es einen -Index fuer das Suchfeld? → verwenden
- Kein Index? → (einfache Gleichheit auf )
- Komplex? → (Arithmetik, , )
- Query mit -Tool ausfuehren
- Ergebnis dem Nutzer verstaendlich praesentieren
Schritt 4b: Mutation bauen (Schreiben)
- Operation waehlen:
- Neuer Datensatz → (ggf. mit /)
- Aendern →
- Buchen/Stornieren/Wandeln → + //
- Loeschen → (aber NICHT als Ersatz fuer !)
- pruefen: Bei Multi-Tabellen-Mutations immer angeben
- Speicher-Strategie: vs. - bei Vorgaengen mit Positionen immer + inneres vor
- Mutation ausfuehren und Ergebnis pruefen (leere Belegnummer = fehlgeschlagen!)
Schritt 5: Ergebnis verifizieren
- Query: Kamen Daten zurueck? Paginierung noetig ()?
- Mutation: pruefen - leer bedeutet die Operation ist fehlgeschlagen
- Fehler: Abschnitt "Troubleshooting" pruefen, Nutzer informieren
- Nie stillschweigend Fehler ignorieren - immer dem Nutzer mitteilen was passiert ist
Typische Nutzer-Szenarien
| Nutzer sagt... | Du machst... |
|---|
| "Zeig mir Artikel X" | → → mit keyFilter |
| "Alle Kunden aus PLZ 5xxxx" | → → → slowFilter auf fldPLZ |
| "Erstell eine Rechnung fuer Kunde 10000" | mit → → + + |
| "Wandle Angebot 12345 in AB" | → → + |
| "Wieviele Artikel haben wir?" | → → → paginieren + |
| "Storniere Rechnung 67890" | → → + |
| "Zeig mir Adresse 10000 mit Anschriften und Ansprechpartnern" | → → → → siehe references/adressen-verwaltung.md
|
| "Leg einen neuen Kunden / Lieferanten / Interesenten an mit Anschrift und Ansprechpartner" | → verschachtelt: + + + → siehe references/adressen-verwaltung.md
|
| "Welche Vorgangsarten gibt es?" | → → → siehe references/parametertabellen.md
|
| "Welche Zahlungsbedingungen sind konfiguriert?" | → → → siehe references/parametertabellen.md
|
| "Zeig mir die Steuerschluessel" | → → → siehe references/parametertabellen.md
|
| "Welche Einheiten/Waehrungen gibt es?" | → / → siehe references/parametertabellen.md
|
Schnellreferenz
Wichtigste Tabellen
| GraphQL-Tabelle | Beschreibung | Schreibbar |
|---|
| Adressen | ja |
| Anschriften | ja |
| Ansprechpartner | ja |
| Kontakte | ja |
| Artikel | ja |
| Warengruppen | ja |
| Artikel-Lieferanten | ja |
| Vorgaenge | ja |
| Vorgangspositionen | ja (verschachtelt in tblTransactions) |
| Archiv Vorgaenge | ja |
| Projekte | ja |
| Dokumente | ja |
| Kalender | ja |
| Lager | ja |
| Mandant (Einzeldatensatz) | ja |
| Offene Posten | nein |
| Lagerbestaende | nein |
| Benutzer | nein |
| Parametertabellen | | |
| Vorgangsarten (Wandlungskette, Buchungsparameter) | nein |
| Buchungsparameter | nein |
| Mengeneinheiten (UN/ECE-Codes fuer E-Rechnung) | nein |
| Steuerschluessel mit Saetzen und DATEV-Zuordnung | nein |
| Zahlungsbedingungen | nein |
| Fremdwaehrungen mit Wechselkursen | nein |
| Versandarten | nein |
| Adressstatus mit Nummernkreisen | nein |
| Anreden | nein |
| Mahnstufen | nein |
| Zahlungsarten | nein |
| Projektarten | nein |
| Projektstatus (Workflow) | nein |
| Dokumentenarten (DMS) | nein |
| Dokumentenstatus (Workflow) | nein |
| Kommunikationsarten | nein |
Haeufigste Patterns (Copy-Paste-fertig)
graphql
# LESEN - Einzelner Datensatz
query { tblProducts { rowRead(kf1ArtNr: { string: "LUTSCHER" }) { fldArtNr fldSuchBeg fldBez1(as: DISPLAY_TEXT) } } }
# LESEN - Liste mit Filter
query { tblProducts { rowsRead(slowFilter: { gt: [{field: fldVk0_Preis}, {value: 0}] }) { fldArtNr fldVk0_Preis(as: TEXT) } } }
# LESEN - Paginiert
query ($after: String) { tblProducts { conRead(first: 10, after: $after) { edges { node { fldArtNr } cursor } pageInfo { hasNextPage endCursor } } } }
# SCHREIBEN - Neuer Datensatz
mutation { tblProducts { rowNew { fldArtNr(set: { string: "NEU-001" }) fldBez1(set: { text: "Bezeichnung" } as: DISPLAY_TEXT) } } }
# SCHREIBEN - Datensatz aendern
mutation { tblProducts { rowModify(kf1ArtNr: { string: "LUTSCHER" }) { fldBez1(set: { text: "Neuer Name" } as: DISPLAY_TEXT) } } }
# SCHREIBEN - Vorgang anlegen + buchen
mutation @acquireLocks(forWriting: [tblTransactions, tblTransactionItems], forReading: [tblAddresses, tblProducts]) {
tblTransactions {
rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) {
fldArt(set: { text: "Rechnung I" })
rowSaveAndModify {
fldBelegNr
tblTransactionItems {
rowNew(assignProduct: { kf1ArtNr: { text: "LUTSCHER" } }) { fldMge(set: { float: 10 }) }
}
rowSave { fnPost { fldBelegNr } } # rowSave VOR fnPost!
}
}
}
}
1. Grundstruktur
Tabellenzugriff
Alle Operationen beginnen mit
auf oberster Ebene:
graphql
query {
tblProducts { ... } # Artikel
tblAddresses { ... } # Adressen
tblTransactions { ... } # Vorgaenge
}
Leseoperationen
| Feld | Beschreibung | Rueckgabe |
|---|
| Einzelnen Datensatz lesen | oder |
| Liste lesen | |
| Paginierte Liste (Relay Connection) | |
Einzeldatensatz-Tabellen
braucht keine Suchparameter:
graphql
query { tblClient { rowRead { fldMandNr fldMandTyp } } }
2. Datensatzauswahl
- Exakte Suche
graphql
rowRead(exactMatch: { byNr: { kf1ArtNr: { string: "PROD-001" } } })
Verschachtelte Schluesselfelder
graphql
rowRead(exactMatch: {
byArtNrLagNr: {
kf1ArtNr: { string: "LUTSCHER"
kf2LagNr: { string: "HAUPT" }
}
}
})
Verkuerzte Schreibweisen
graphql
# Statt:
rowRead(exactMatch: { byNr: { kf1ArtNr: { string: "LUTSCHER" } } })
# Kurz (impliziert exactMatch + byNr):
rowRead(kf1ArtNr: { string: "LUTSCHER" })
# Bereich:
rowsRead(kf1ArtNr: { from: { string: "0" }, to: { string: "12000" } })
Weitere Sucharten
graphql
# Naechster Treffer
rowRead(nearestMatch: { byNr: { kf1ArtNr: { string: "PROD-000" } } })
# Aktueller Benutzer
tblUsers { rowRead(using: current) { fldAnmNa } }
# Bereich mit exklusiver Obergrenze
rowsRead(allBetween: { byNr: { kf1ArtNr: { from: { string: "A" }, to: { string: "Z" }, toExclusive: true } } })
3. Datenfelder und Ausgabeformate
Praefix-System
| Praefix | Bedeutung | Beispiel |
|---|
| Tabelle | |
| Datenfeld | , |
| Schluesselfeld | , |
| Sortierfolge | , |
| Verweis auf verknuepften Datensatz | |
| Verlinkung zu Datentabelle | |
| Betragsgruppe | , |
| Bildfeld | |
| Funktion | , |
| System-Feld | , |
-Parameter - Ausgabeformat
graphql
fldPreis # Standardformat
fldPreis(as: TEXT) # "99,95 EUR" (lokalisiert)
fldPreis(as: FLOAT) # 99.95
fldPreis(as: STRING) # Roher String
fldPreis(as: INT) # Ganzzahl
fldPreis(as: DISPLAY_TEXT) # Anzeigeformat (auch fuer RTF-Felder → Klartext)
fldGueltigAb(as: TEXT) # "24.12.2023" (lokalisiert)
fldGspKz(as: BOOLEAN) # true/false
Gemeinsame Felder (in fast allen Tabellen)
# Identifikation
fldID # Automatische ID
fldModifyLSN / fldInsertLSN # Versionierung (BigInt)
# Audit-Trail
fldErstDat / fldErstBzr # Erstellungsdatum / -benutzer
fldAendDat / fldAendBzr # Aenderungsdatum / -benutzer
# Sperren
fldGspKz # Gesperrt-Kennzeichen (Boolean)
fldGspDat / fldGspInfo # Sperrdatum / -information
fldGspGrp # Gesperrtgruppe
# Freitext
fldInfo # Informationsfeld
fldMemo # Memo-Feld
fldQuickInfo # QuickInfo
4. Verknuepfungen
Verweise () - Einzelner verknuepfter Datensatz
graphql
tblProducts {
rowsRead {
fldArtNr
fldWgrNr # Fremdschluessel-Wert
rowWgrNr { fldBez } # Verknuepfte Warengruppe
rowEinh { fldKuBez } # Verknuepfte Einheit
}
}
Verlinkungen () - Liste verknuepfter Datensaetze
WICHTIG: gehoeren auf
INNERHALB des Links, NICHT auf
:
graphql
# RICHTIG:
lnkInventory {
rowsRead(byArtNrLagNrArtBDat: { usingArtNr: {} }) { fldLagNr fldMge }
}
# FALSCH:
lnkInventory(byArtNrLagNrArtBDat: { usingArtNr: {} }) {
rowsRead { ... }
}
Verschachtelte Tabellen ( innerhalb Row)
graphql
tblClient {
rowRead {
fldMandNr
tblBnkVb { rowsRead { fldNr fldIBAN } }
}
}
5. Filter
VERBINDLICHER WORKFLOW: keyFilter-First
BEVOR du einen Filter schreibst, fuehre IMMER diese Schritte aus:
-
Sortierfolgen ermitteln: Pruefe per Introspection, welche
-Sortierfolgen die Tabelle hat:
graphql
{ __type(name: "{SingularName}SlowAnyFields") { enumValues { name } } }
WICHTIG: Der Enum-Name verwendet den
Singular-Namen der Entitaet, NICHT den Tabellennamen!
Beispiele:
,
,
.
Sonderfaelle:
CalendarEntrySlowAnyFields
,
,
BillOfMaterialsEntrySlowAnyFields
,
InventoryEntrySlowAnyFields
.
Gleiches Muster gilt fuer
{SingularName}FastAnyFields
.
Die
-Parameter findest du auf den
/
-Argumenten. Alternativ: Probiere
rowsRead(allBetween: { by
und schau welche Vorschlaege die API akzeptiert.
-
keyFilter pruefen: Gibt es eine
-Sortierfolge, die das gewuenschte Filterfeld als
-Schluessel enthaelt? →
keyFilter verwenden!
graphql
# Beispiel: Laender ohne Staatsangehoerigkeitsnummer
# byStaatsNr existiert mit kf1StaatsNr → keyFilter nutzen!
rowsRead(allBetween: {
byStaatsNr: { keyFilter: { isNull: { field: kf1StaatsNr } } }
})
-
Fallback: Nur wenn KEIN passender
-Index existiert:
- fuer einfache Vergleiche auf -Felder
- NUR fuer Arithmetik, -Operator, -Funktionen
NIEMALS direkt verwenden, ohne vorher geprueft zu haben!
Drei Filtertypen
| Filter | Performance | Besonderheiten |
|---|
| Am schnellsten | Filter auf -Felder innerhalb - IMMER bevorzugen! |
| Schnell (DB-seitig) | Nur einfache Vergleiche auf |
| Langsam (App-seitig) | Arithmetik, -Operator, - nur als letzter Ausweg |
Vergleichsoperatoren
graphql
{ eq: [{field: fldStatus}, {value: 1}] } # gleich
{ ne: [{field: fldStatus}, {value: 0}] } # ungleich
{ gt: [{field: fldMge}, {value: 0}] } # groesser
{ lt: [{field: fldMge}, {value: 100}] } # kleiner
{ ge: [{field: fldMge}, {value: 1}] } # groesser-gleich
{ le: [{field: fldMge}, {value: 99}] } # kleiner-gleich
{ isNull: { field: fldAuftrNr } } # NULL-Pruefung
{ isNotNull: { field: fldAuftrNr } }
Boolesche Kombinationen
graphql
{ and: [Ausdruck1, Ausdruck2] }
{ or: [Ausdruck1, Ausdruck2] }
{ not: Ausdruck }
WICHTIG: @oneOf-Regel
Pro Filterobjekt nur EIN Operator:
graphql
# FALSCH:
fastFilter: { eq: [...], gt: [...] }
# RICHTIG:
fastFilter: { and: [ { eq: [...] }, { gt: [...] } ] }
IN-Operator (nur slowFilter)
graphql
{ in: { left: { field: fldLagBestArt }, list: [1, 7] } }
Alle Werte muessen denselben Typ haben (@sametype-Regel).
keyFilter - Performantester Filter
Filtert auf
-Felder INNERHALB des
-Parameters. Schneller als fast/slowFilter.
graphql
# Alle Artikel ab Nummer "10"
rowsRead(
allBetween: {
byNr: {
keyFilter: { ge: [{ field: kf1ArtNr }, { value: "10" }] }
kf1ArtNr: { from: { string: "0" }, to: { string: "ZZZZZ" } }
}
}
)
# Mehrere Werte per OR
keyFilter: {
or: [
{ eq: [{ field: kf1AdrNr }, { value: "10000" }] }
{ eq: [{ field: kf1AdrNr }, { value: "70000" }] }
]
}
Prioritaet: >
>
(immer schnellsten moegl. Typ waehlen).
Detaillierte keyFilter-Patterns siehe
references/erweiterte-query-patterns.md
.
Funktionen in Filtern (nur slowFilter)
graphql
{ fnPos: [{ value: "Suchtext" }, { field: fldSuchBeg }] } # Textsuche (>0 = gefunden, NUR String-Felder!)
{ fnGetAktDate: [] } # Aktuelles Datum
{ fnIncDate: [{ fnGetAktDate: [] }, { value: 0 }, { value: -12 }] } # Datum +/- Monate
Arithmetik in slowFilter
graphql
# Brutto > 100 pruefen
slowFilter: { gt: [{ mul: [{ field: fldVk0_Preis }, { value: 1.19 }] }, { value: 100 }] }
Operatoren:
,
,
,
,
,
. Details in
references/erweiterte-query-patterns.md
.
6. Direktiven
- Wert in Variable speichern
Variable muss in der Operations-Signatur deklariert werden:
graphql
query ($storedValue: Any = null) {
tblProducts {
rowsRead {
fldSuchBeg @store(in: $storedValue)
echo: _any(value: $storedValue)
}
}
}
funktioniert ueber Verschachtelungsebenen hinweg (aeussere Tabelle → verschachtelte tbl/lnk).
- Null-Behandlung
graphql
fldSuchBeg @onNull(returnValue: "Nicht gefunden") # Alternativwert
fldPreis @onNull(returnValue: skip) # Feld ueberspringen
fldArtNr @onNull(errorMessage: "ArtNr fehlt") # Fehler ausloesen
- Praventive Sperren (nur mutation)
graphql
mutation @acquireLocks(
forWriting: [tblTransactions, tblTransactionItems],
forReading: [tblAddresses, tblProducts]
) { ... }
Verhindert Deadlocks bei Multi-Tabellen-Mutationen.
/
graphql
fldPreis @include(if: $includePrice)
7. System-Felder
graphql
# Wertausgabe
_any(value: $variable)
_string(expr: { add: [{field: fldArtNr}, {value: " - "}, {field: fldSuchBeg}] })
_boolean(expr: { gt: [{field: fldLagMge}, {value: 0}] })
_localDate(expr: { fnGetAktDate: [] })
# Bedingte Ausfuehrung
details: _if(expr: { gt: [{field: fldLagMge}, {value: 0}] }) {
fldStdPreis
fldLagMge
}
# Bedingter Skalarwert
status: _ifThenElse(
expr: { gt: [{field: fldLagMge}, {value: 0}] },
then: "Verfuegbar",
else: "Nicht auf Lager"
)
# Fehler ausloesen
_raise(message: "Negative Lagermenge!")
8. Mutationen
query vs. mutation
- : Snapshot-Transaktion, keine Sperren, null bei Fehler
- : Normale Transaktion, Alles-oder-Nichts (Fehler = Rollback)
Schreiboperationen
| Operation | Zweck |
|---|
| Neuen Datensatz erstellen |
| Nur erstellen wenn nicht vorhanden (gibt statt Fehler) |
| Datensatz kopieren (nicht-gesetzte Felder vom Original) |
rowModify(exactMatch: ...)
| Datensatz aendern |
rowDelete(exactMatch: ...)
| Datensatz loeschen |
rowsModify/rowsCopy/rowsDelete
| Massenoperationen (Fehler bei einem → Rollback aller) |
Row-Kontexte
| Kontext | Schreiben | | schreibbar |
|---|
RowMutationNew/Copy/Modify
| ja | nein | ja |
| (nach rowSave) | nein | ja (mit Schreiben) | nein |
| nein | nein | nein |
Datensatz erstellen
graphql
mutation {
tblProducts {
rowNew {
fldArtNr(set: { string: "PROD-001" })
fldBez1(set: { text: "Neuer Artikel" } as: DISPLAY_TEXT)
fldVk0_Preis(set: { float: 99.95 })
} # Automatisch gespeichert am Block-Ende
}
}
Bedingt (kein Fehler wenn vorhanden, gibt
zurueck):
graphql
rowNew(ifNotExists: { exactMatch: { byNr: { kf1ArtNr: { string: "PROD-001" } } } })
Datensatz kopieren
graphql
mutation {
tblProducts {
rowCopy(kf1ArtNr: { string: "LUTSCHER" }) {
fldArtNr(set: { string: "LUTSCHER-V2" })
# Alle anderen Felder (Preis, Bez., etc.) vom Original uebernommen
fldBez1(as: DISPLAY_TEXT)
fldVk0_Preis(as: FLOAT)
}
}
}
Details zu rowCopy, ifNotExists und Optimistic Locking in
references/mutation-patterns.md
.
Datensatz aendern
graphql
mutation {
tblProducts {
rowModify(kf1ArtNr: { string: "PROD-001" }) {
fldVk0_Preis(set: { float: 129.95 })
fldBez1(as: DISPLAY_TEXT) # Nur lesen
}
}
}
Datensatz loeschen
graphql
mutation {
tblProducts {
rowDelete(kf1ArtNr: { string: "PROD-001" }, ignoreWarnings: true) {
fldArtNr # Noch lesbar vor Loeschung
}
}
}
Speicheroperationen
graphql
# rowSave - Speichern, dann Read-Kontext (lnk... verfuegbar)
rowNew {
fldArtNr(set: { string: "X" })
rowSave { fldID fldModifyLSN } # Ab hier nur lesen + lnk...
}
# rowSaveAndModify - Speichern, weiter schreiben
rowNew {
fldArtNr(set: { string: "X" })
rowSaveAndModify {
fldBez1(set: { text: "Name" } as: DISPLAY_TEXT)
tblTransactionItems { ... } # Verschachtelte Tabelle schreibbar
}
}
WICHTIG: Nach
/
NICHT mehr im aeusseren Block schreiben!
Feldmanipulation (-Parameter)
graphql
fldArtNr(set: { string: "PROD-005" }) # String
fldVk0_Preis(set: { float: 99.95 }) # Float
fldVk0_Preis(set: { text: "99,95" }) # Lokalisierter Text
fldGspAbDat(set: { text: "2023-12-24" }) # Datum (ISO-Format YYYY-MM-DD, auch localdate moeglich)
fldGspKz(set: { boolean: true }) # Boolean
fldArt(set: { text: "Rechnung I" }) # Vorgangsart (immer Text, Nummern koennen abweichen!)
fldMge(set: { int: 5 }) # Integer
fldGspAbDat(set: {}) # NULL setzen
# Auto-Nummerierung (naechste freie Nr wenn vergeben)
fldAdrNr(set: { text: "10000", allowAutoNrOnSave: true })
# Kaskadierende Updates unterdruecken
fldZahlBed(set: { text: "30 Tage netto", suppressRelatedUpdates: true })
Assign-Parameter
graphql
# Row-Assign: Felder aus anderem Datensatz befuellen
rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) { ... }
rowNew(assignUser: { using: current }) { ... }
# Feld-Assign: Einzelnes Feld per Suche zuweisen
fldWgrNr(assignProductGroup: { exactMatch: { byBez: { kf1Bez: { text: "Elektronik" } } } })
fldSBzrNr(assignUser: { using: current })
# using: context in Verlinkungen (nimmt aeusseren Datensatz)
lnkProjects { rowNew(assignAddress: { using: context }) { ... } }
Verlinkungen in Mutationen (nur in RowMutationRead)
graphql
mutation {
tblAddresses {
rowRead(kf1AdrNr: { string: "10000" }) {
lnkPostalAddresses {
rowNew(setAdrNrAnsNr: usingAdrNr) {
fldNa2(set: { text: "Lieferanschrift" })
rowSave { fldAnsNr }
}
}
}
}
}
-Parameter bei
in Verlinkungen: befuellt Schluesselfelder automatisch aus Kontext.
9. Vorgaenge - Kern-Workflow
Vorgangsarten
WICHTIG: Immer
statt
verwenden - Nummern koennen pro Installation abweichen!
graphql
fldArt(set: { text: "Rechnung I" }) # RICHTIG - Bezeichnung
fldArt(set: { int: 70 }) # VERMEIDEN - Nummer kann abweichen
Hinweis: akzeptiert den Parameter
sowohl als Integer als auch als Text:
graphql
fnConvert(transactionTypeNo: 50, ...) # Integer - Standard-Nummer (50 = Lieferschein)
fnConvert(transactionTypeNo: "Lieferschein", ...) # Text - Bezeichnung
Standard-Nummern und Bezeichnungen finden sich in
references/vorgangsarten.md
.
Vollstaendige Liste aller Vorgangsarten mit Standard-Nummern und Buchungsparametern:
references/vorgangsarten.md
Funktionsuebersicht
| Funktion | Beschreibung | Kontext | Rueckgabe |
|---|
| Buchen | | Einzelwert |
| Stornieren | | Einzelwert |
| Wandeln | | Array |
| Archivieren | | Einzelwert |
Alle erfordern: +
-Kontext. Leere Belegnummer = fehlgeschlagen.
Vorgang anlegen mit Positionen
graphql
mutation @acquireLocks(
forWriting: [tblTransactions, tblTransactionItems]
forReading: [tblAddresses, tblProducts]
) {
tblTransactions {
rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) {
fldArt(set: { text: "Rechnung I" })
rowSaveAndModify {
fldBelegNr
tblTransactionItems {
pos1: rowNew(assignProduct: { kf1ArtNr: { text: "LUTSCHER" } }) {
fldMge(set: { float: 10 })
}
pos2: rowNew(assignProduct: { kf1ArtNr: { text: "MYSTERYBOX" } }) {
fldMge(set: { float: 2 })
}
}
}
}
}
}
Hinweise:
- akzeptiert Artikelnummern UND Barcodes (Auto-Resolve)
- kann als Nummer () oder Text () gesetzt werden
fldZeilenNr(set: { int: 0 })
fuegt Position an den Anfang
Vorgang buchen ()
graphql
# Bestehenden Vorgang buchen
mutation {
tblTransactions {
rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500005" } } }) {
fnPost { fldBelegNr } # Leer = nicht gebucht!
}
}
}
Anlegen + Buchen in einer Mutation
graphql
mutation @acquireLocks(
forWriting: [tblTransactions, tblTransactionItems]
forReading: [tblAddresses, tblProducts]
) {
tblTransactions {
rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) {
fldArt(set: { text: "Rechnung I" })
rowSaveAndModify {
fldBelegNr
tblTransactionItems {
pos1: rowNew(assignProduct: { kf1ArtNr: { text: "LUTSCHER" } }) {
fldMge(set: { float: 10 })
}
}
rowSave { # WICHTIG: Erst speichern!
fnPost { fldBelegNr } # Dann buchen
}
}
}
}
}
Vorgang stornieren ()
graphql
mutation {
tblTransactions {
rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500002" } } }) {
fnReverse { fldBelegNr }
}
}
}
Vorgang wandeln ()
Gibt ein Array zurueck (kann mehrere Vorgaenge erzeugen):
graphql
mutation {
tblTransactions {
rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "AN2500001" } } }) {
fnConvert(transactionTypeNo: 50, deliveryQuantityHandling: IGNORE) {
fldBelegNr
}
}
}
}
Preisberechnung nach Speichern
graphql
rowSave {
acoGPreis {
totalGrossAmount # Brutto
totalNetAmount # Netto
totalTaxAmount # Steuer
}
}
10. Betragsgruppen ()
Kurzbeispiel Lesen:
acoEPr { totalGrossAmount totalNetAmount totalTaxAmount }
Details (Lesen, Schreiben, Preisberechnung nach Speichern):
references/betragsgruppen.md
11. Paginierung (Relay Connection)
graphql
query ($pageSize: Int = 10, $after: String) {
tblProducts {
conRead(first: $pageSize, after: $after) {
edges {
cursor
node { fldArtNr fldSuchBeg }
}
pageInfo { hasNextPage endCursor startCursor edgeCount }
}
}
}
WICHTIG: gibt nur die Anzahl der zurueckgegebenen Edges zurueck, NICHT die Gesamtanzahl der Datensaetze. Es gibt kein
-Feld. Zum Zaehlen:
mit ausreichend grossem Wert setzen und
pruefen.
12. Haeufige Fehler vermeiden
| Fehler | Loesung |
|---|
| in -Kontext | Nur in -Kontext |
| ohne | Immer vor |
| Schreiben nach im selben Block | Nach nur lesen, oder |
| in schreibendem Kontext | Erst , dann im Read-Kontext |
| Mehrere Operatoren in einem Filterobjekt | / verwenden (@oneOf-Regel) |
| nach in einer Mutation | Getrennte Mutationen (Auto-Archivierung!) |
| Leere Belegnummer nicht geprueft | Immer in -Rueckgabe pruefen |
| auf statt auf | Gehoeren auf / innerhalb des Links |
| Korrekt: mit Deklaration in Signatur |
"Argument \"set\" is not defined"
| Fehlende Berechtigung - wird ohne Recht nicht angeboten |
| Feldreihenfolge nicht beachtet | Felder werden sequentiell ausgefuehrt - Menge VOR Preis setzen |
| ohne neuen Schluessel | Kopie braucht eigenen Primaerschluessel |
| bricht ab | Ein Fehler bei einem Datensatz → Rollback ALLER Loeschungen |
| schlaegt fehl → als Workaround | VERBOTEN! Bricht die Prozesskette. Fehlerursache klaeren und Nutzer informieren |
| schlaegt fehl → als Workaround | VERBOTEN! Umgeht Storno-Logik (keine Gegenbuchungen, kein Audit-Trail). Nutzer informieren |
| schlaegt fehl → naechsten Schritt trotzdem ausfuehren | VERBOTEN! Folgeschritte (z.B. Wandlung) setzen erfolgreiche Buchung voraus. Nutzer informieren |
| schlaegt fehl → manuell Daten kopieren/loeschen | VERBOTEN! Archivierung hat eigene Logik. Nutzer informieren |
| Prozessschritte ueberspringen (z.B. direkt RE statt AB→LI→RE) | Nur wenn Nutzer dies explizit bestaetigt. Auf moeglichen Kettenverlust hinweisen |
| als Gesamtanzahl interpretiert | zaehlt nur zurueckgegebene Edges, NICHT alle Datensaetze. ergibt immer . Zum Zaehlen: und pruefen |
| /// auf | Diese Felder leben in , Zugriff ueber |
13. Troubleshooting
Fehlermeldung: "Argument \"set\" is not defined"
Ursache: Fehlende Berechtigung. Der API-Nutzer hat kein Recht, dieses Feld zu setzen.
Loesung: Berechtigung im ERP pruefen. Berechtigungs-pflichtige Felder:
,
,
,
,
,
.
Fehlermeldung: "Address \"10002\" is blocked."
Ursache: Adresse hat
(Gesperrt-Kennzeichen).
Loesung: Andere Adresse verwenden oder Sperrung im ERP aufheben.
Leere Belegnummer nach //
Ursache: Funktion konnte nicht ausgefuehrt werden (Voraussetzungen nicht erfuellt).
Loesung: Immer Belegnummer pruefen:
. Leer = fehlgeschlagen.
Rollback nach trotz erfolgreicher Buchung
Ursache: Eine NACHFOLGENDE Operation in derselben Mutation ist fehlgeschlagen. Alles-oder-Nichts.
Loesung: und Folgeoperationen in getrennte Mutationen aufteilen, oder Fehlerquelle beheben.
hat keine Wirkung (Positionen fehlen)
Ursache: vor
vergessen.
arbeitet nur auf gespeicherten Daten.
Loesung: Immer
rowSave { fnPost { fldBelegNr } }
-
VOR
.
-Felder nicht verfuegbar
Ursache: Datensatz ist noch in schreibendem Kontext (
RowMutationNew/Copy/Modify
).
Loesung: Erst
aufrufen → wechselt zu
→
wird verfuegbar.
Laufzeitfehler nach
Ursache: Versuch im selben Block nach
noch Felder zu schreiben.
Loesung: verwenden wenn danach noch geschrieben werden soll, oder alle Schreiboperationen VOR
platzieren.
Deadlock bei parallelen Mutationen
Ursache: fehlt oder unvollstaendig.
Loesung: @acquireLocks(forWriting: [...], forReading: [...])
mit ALLEN beteiligten Tabellen.
fn-Funktion schlaegt fehl (fnConvert/fnPost/fnReverse/fnMoveToArchive)
Ursache: Vielfaeltig - fehlende Konfiguration (Wandlungspfad), Beleg bereits archiviert, fehlende Berechtigung, Geschaeftsregel verletzt, Beleg gesperrt.
Loesung: STOPP - KEINEN Workaround versuchen! Insbesondere:
- NICHT per einen Ersatzbeleg anlegen (bricht Prozesskette)
- NICHT per einen Beleg loeschen statt zu stornieren (umgeht Audit-Trail)
- NICHT den naechsten Prozessschritt ausfuehren wenn der aktuelle fehlschlug
Stattdessen: Fehlermeldung dem Nutzer zeigen und gemeinsam klaeren. Moegliche Ursachen:
- Wandlungspfad im ERP nicht konfiguriert → ERP-Administrator muss Pfad freischalten
- Beleg bereits im Archiv → nur auf aktive Vorgaenge moeglich
- Fehlende Berechtigung → API-Nutzer-Rechte pruefen
- Geschaeftsregel verletzt → Beleg-Daten pruefen (z.B. fehlende Pflichtfelder)
Query gibt zurueck obwohl Datensatz existiert
Ursache (in mutation): stimmt nicht ueberein (Optimistic Locking Konflikt).
Ursache (in query): Schluessel falsch oder Sortierfolge stimmt nicht.
Loesung: -Pattern verwenden um Konflikte abzufangen. Siehe
references/mutation-patterns.md
.