microtech-graphql

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

microtech GraphQL - Skill-Referenz

microtech GraphQL - 技能参考

Kompakte Referenz fuer die GraphQL-Schnittstelle des microtech ERP. Optimiert fuer Query-Generierung ueber einen MCP-Server mit
graphql_query
-Tool.
ASCII: Dieses Dokument verwendet
ae/oe/ue/ss
statt Umlaute.
WICHTIG - Introspection: NIEMALS das vollstaendige Schema abrufen (
__schema
). 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!

microtech ERP系统GraphQL接口的简明参考文档。 针对通过MCP服务器使用
graphql_query
工具生成查询进行优化。
ASCII说明: 本文档使用
ae/oe/ue/ss
替代变音字符。
重要 - 自省(Introspection): 切勿获取完整Schema(
__schema
),应针对性地查询单个类型。请参考
references/introspection-guide.md
字段目录: 如需各表的详细字段列表,请查看
references/feldkatalog-*.md
进阶主题: 归档功能、外部编辑、权限、跨表模式等内容,请查看
references/vorgaenge-erweitert.md
变更模式: 乐观锁、rowCopy、ifNotExists、深度嵌套、批量操作等内容,请查看
references/mutation-patterns.md
查询模式: keyFilter详情、算术运算、skip值、扩展@onNull、Schema元数据等内容,请查看
references/erweiterte-query-patterns.md
地址管理: 地址、联系地址和联系人的实操示例(读取、创建、修改、删除),含嵌套链接、乐观锁、rowCopy、批量操作和assign参数,请查看
references/adressen-verwaltung.md
处理地址相关任务时,请优先查阅此文档!
参数表: 读取配置和参数表(单据类型、付款条件、税码、单位、配送方式、货币、催款级别、项目状态等),请查看
references/parametertabellen.md
关于客户端特定配置的问题,请优先查阅此文档!
工作流示例: 17个完整场景(创建发票、转换单据、冲销、库存查询等),请查看
references/workflow-beispiele.md
处理复杂任务时,请优先查阅此文档!

Kritische Regeln (Top 9)

核心规则(Top 9)

  1. keyFilter-First: Bei JEDEM Filter zuerst pruefen, ob ein passender
    by...
    -Index existiert und
    keyFilter
    verwenden. Erst wenn kein passender Index vorhanden ist, auf
    fastFilter
    oder
    slowFilter
    zurueckfallen. Siehe Abschnitt 5 fuer den verbindlichen Workflow.
  2. @oneOf-Regel: Pro Filterobjekt nur EIN Operator. Mehrere Bedingungen mit
    and
    /
    or
    kombinieren.
  3. fnPost
    /
    fnReverse
    /
    fnConvert
    nur in
    rowRead
    -Kontext
    (nicht
    rowModify
    !), immer in
    mutation
    .
  4. rowSave
    vor
    fnPost
    - ohne
    rowSave
    sind Positionen nicht gespeichert und werden nicht gebucht.
  5. Nach
    rowSave
    nicht mehr schreiben
    im selben Block - fuehrt zu Laufzeitfehler und Rollback.
  6. Leere Belegnummer = Operation fehlgeschlagen - immer
    fldBelegNr
    in
    fn...
    -Rueckgaben pruefen.
  7. Prozessketten-Integritaet - NIEMALS fn-Funktionen durch manuelle Operationen ersetzen! ERP-Prozessfunktionen (
    fnPost
    ,
    fnReverse
    ,
    fnConvert
    ,
    fnMoveToArchive
    ) 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.
  8. NIEMALS
    rowNew
    als Ersatz fuer
    fnConvert
    - 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.
  9. NIEMALS
    rowDelete
    als Ersatz fuer
    fnReverse
    - eine Loesung per
    rowDelete
    umgeht die Storno-Logik: keine Storno-Belegnummer, keine Gegenbuchungen, keine Lagerkorrektur, kein Audit-Trail. Stattdessen:
    fnReverse
    verwenden, bei Fehler Nutzer informieren.

  1. keyFilter优先: 所有过滤操作前,先检查是否存在对应的
    by...
    索引,优先使用
    keyFilter
    。仅当无对应索引时,再使用
    fastFilter
    slowFilter
    。请查看第5节的强制工作流。
  2. @oneOf规则: 每个过滤对象仅允许一个操作符。多条件需用
    and
    /
    or
    组合。
  3. fnPost
    /
    fnReverse
    /
    fnConvert
    仅可在
    rowRead
    上下文使用
    (不可在
    rowModify
    中!),且必须在
    mutation
    内。
  4. rowSave
    需在
    fnPost
    之前
    - 未执行
    rowSave
    的话,行项目不会被保存,也无法过账。
  5. 同一区块内执行
    rowSave
    后禁止再写入
    - 会导致运行时错误和回滚。
  6. 空单据编号 = 操作失败 - 务必检查
    fn...
    返回结果中的
    fldBelegNr
  7. 流程链完整性 - 切勿用手动操作替代fn函数! ERP流程函数(
    fnPost
    fnReverse
    fnConvert
    fnMoveToArchive
    )是修改单据状态的唯一正确方式。它们确保流程链的完整性(如报价→订单确认→发货单→发票)、关联单据、更新库存、生成记账凭证并执行合理性检查。若fn函数执行失败,立即停止操作并通知用户 - 切勿尝试替代方案。
  8. 切勿用
    rowNew
    替代
    fnConvert
    - 手动创建的后续单据与原单据无关联,会导致流程链中断、数量统计错误、交付状态无法更新。正确做法:排查失败原因(未配置转换路径、单据已归档等)并询问用户。
  9. 切勿用
    rowDelete
    替代
    fnReverse
    - 使用
    rowDelete
    删除单据会绕过冲销逻辑:无冲销单据编号、无反向记账、无库存调整、无审计追踪。正确做法:使用
    fnReverse
    ,若失败则通知用户。

Arbeitsweise: So bearbeitest du Nutzeranfragen

工作流程:如何处理用户请求

Schritt 1: Anfrage klassifizieren

步骤1:分类请求

  • Lesen/Suchen
    query
    (Daten anzeigen, suchen, auflisten, zaehlen)
  • Schreiben/Aendern
    mutation
    (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.
  • 读取/搜索
    query
    (显示、搜索、列出、统计数据)
  • 写入/修改
    mutation
    (创建、修改、删除、过账、转换)
  • 未知字段/表 → 先执行自省操作(
    references/introspection-guide.md
权威来源: 若对字段名称、可写性或可用操作存疑,务必通过自省检查实时API。文档中的字段列表可能过时 - API是唯一可靠的来源。

Schritt 2: Tabelle identifizieren

步骤2:识别表

  • Nutze die Tabelle aus "Schnellreferenz" unten
  • Bei Unsicherheit: Introspection oder Nutzer fragen
  • 使用下方“快速参考”中的表
  • 若存疑:执行自省或询问用户

Schritt 3: Reference-Dateien konsultieren

步骤3:查阅参考文档

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.
在构建查询或变更之前: 检查是否存在对应的示例或详细说明。
  • 复杂任务? → 先搜索
    references/workflow-beispiele.md
    (17个场景)
  • 字段名称不明确? → 查阅对应的
    references/feldkatalog-*.md
  • 过滤逻辑? → 查看
    references/erweiterte-query-patterns.md
    (keyFilter、算术运算、日期过滤)
  • 地址/联系地址/联系人? → 查看
    references/adressen-verwaltung.md
    (嵌套链接、rowReAnsNr、assign参数、批量操作)
  • 变更模式? → 查看
    references/mutation-patterns.md
    (乐观锁、rowCopy、深度嵌套)
  • 单据函数? → 查看
    references/vorgaenge-erweitert.md
    (归档、外部编辑)
  • 参数表/配置? → 查看
    references/parametertabellen.md
    (单据类型、税码、付款条件、单位、配送方式、货币、催款级别、项目状态、文档类型等)
  • 需要自省? → 查看
    references/introspection-guide.md
    (类型命名规范)
切勿猜测 - 查阅文档! 参考文档包含经过测试的正确示例。与其花时间构建错误的查询,不如花30秒查阅文档。

Schritt 4a: Query bauen (Lesen)

步骤4a:构建查询(读取)

  1. Filter-Strategie (keyFilter-First!):
    • Gibt es einen
      by...
      -Index fuer das Suchfeld? →
      keyFilter
      verwenden
    • Kein Index? →
      fastFilter
      (einfache Gleichheit auf
      fld...
      )
    • Komplex? →
      slowFilter
      (Arithmetik,
      in
      ,
      fn...
      )
  2. Query mit
    graphql_query
    -Tool ausfuehren
  3. Ergebnis dem Nutzer verstaendlich praesentieren
  1. 过滤策略(keyFilter优先!):
    • 是否存在针对搜索字段的
      by...
      索引? → 使用
      keyFilter
    • 无索引? → 使用
      fastFilter
      (针对
      fld...
      字段的简单相等匹配)
    • 复杂过滤? → 使用
      slowFilter
      (算术运算、
      in
      fn...
      函数)
  2. 使用
    graphql_query
    工具执行查询
  3. 向用户清晰展示结果

Schritt 4b: Mutation bauen (Schreiben)

步骤4b:构建变更(写入)

  1. Operation waehlen:
    • Neuer Datensatz →
      rowNew
      (ggf. mit
      assignAddress
      /
      assignProduct
      )
    • Aendern →
      rowModify
    • Buchen/Stornieren/Wandeln →
      rowRead
      +
      fnPost
      /
      fnReverse
      /
      fnConvert
    • Loeschen →
      rowDelete
      (aber NICHT als Ersatz fuer
      fnReverse
      !)
  2. @acquireLocks
    pruefen:
    Bei Multi-Tabellen-Mutations immer angeben
  3. Speicher-Strategie:
    rowSave
    vs.
    rowSaveAndModify
    - bei Vorgaengen mit Positionen immer
    rowSaveAndModify
    + inneres
    rowSave
    vor
    fnPost
  4. Mutation ausfuehren und Ergebnis pruefen (leere Belegnummer = fehlgeschlagen!)
  1. 选择操作:
    • 新记录 →
      rowNew
      (可搭配
      assignAddress
      /
      assignProduct
    • 修改 →
      rowModify
    • 过账/冲销/转换 →
      rowRead
      +
      fnPost
      /
      fnReverse
      /
      fnConvert
    • 删除 →
      rowDelete
      (但切勿替代
      fnReverse
      !)
  2. 检查
    @acquireLocks
    多表变更时务必指定
  3. 保存策略:
    rowSave
    vs
    rowSaveAndModify
    - 带行项目的单据务必使用
    rowSaveAndModify
    +
    fnPost
    前执行内部
    rowSave
  4. 执行变更并检查结果(空单据编号 = 操作失败!)

Schritt 5: Ergebnis verifizieren

步骤5:验证结果

  • Query: Kamen Daten zurueck? Paginierung noetig (
    hasNextPage
    )?
  • Mutation:
    fldBelegNr
    pruefen - leer bedeutet die Operation ist fehlgeschlagen
  • Fehler: Abschnitt "Troubleshooting" pruefen, Nutzer informieren
  • Nie stillschweigend Fehler ignorieren - immer dem Nutzer mitteilen was passiert ist
  • 查询: 是否返回数据?是否需要分页(
    hasNextPage
    )?
  • 变更: 检查
    fldBelegNr
    - 为空则表示操作失败
  • 错误: 查看“故障排除”部分,通知用户
  • 切勿静默忽略错误 - 务必告知用户发生的情况

Typische Nutzer-Szenarien

典型用户场景

Nutzer sagt...Du machst...
"Zeig mir Artikel X"
query
tblProducts
rowRead
mit keyFilter
"Alle Kunden aus PLZ 5xxxx"
query
tblAddresses
lnkPostalAddresses
→ slowFilter auf fldPLZ
"Erstell eine Rechnung fuer Kunde 10000"
mutation
mit
@acquireLocks
tblTransactions
rowNew
+
rowSave
+
fnPost
"Wandle Angebot 12345 in AB"
mutation
tblTransactions
rowRead
+
fnConvert
"Wieviele Artikel haben wir?"
query
tblProducts
conRead(first: 100)
→ paginieren +
hasNextPage
"Storniere Rechnung 67890"
mutation
tblTransactions
rowRead
+
fnReverse
"Zeig mir Adresse 10000 mit Anschriften und Ansprechpartnern"
query
tblAddresses
lnkPostalAddresses
lnkContactPeople
→ siehe
references/adressen-verwaltung.md
"Leg einen neuen Kunden / Lieferanten / Interesenten an mit Anschrift und Ansprechpartner"
mutation
→ verschachtelt:
rowNew
+
rowSave
+
lnkPostalAddresses
+
lnkContactPeople
→ siehe
references/adressen-verwaltung.md
"Welche Vorgangsarten gibt es?"
query
tblTransactionTypes
rowsRead
→ siehe
references/parametertabellen.md
"Welche Zahlungsbedingungen sind konfiguriert?"
query
tblPaymentTerms
rowsRead
→ siehe
references/parametertabellen.md
"Zeig mir die Steuerschluessel"
query
tblValueAddedTaxTypes
rowsRead
→ siehe
references/parametertabellen.md
"Welche Einheiten/Waehrungen gibt es?"
query
tblUnitsOfMeasure
/
tblCurrencies
→ siehe
references/parametertabellen.md

用户需求...你需要...
"显示商品X"
query
tblProducts
→ 带keyFilter的
rowRead
"所有邮编为5xxxx的客户"
query
tblAddresses
lnkPostalAddresses
→ 对fldPLZ使用slowFilter
"为客户10000创建一张发票"
@acquireLocks
mutation
tblTransactions
rowNew
+
rowSave
+
fnPost
"将报价12345转换为订单确认"
mutation
tblTransactions
rowRead
+
fnConvert
"我们有多少种商品?"
query
tblProducts
conRead(first: 100)
→ 分页 + 检查
hasNextPage
"冲销发票67890"
mutation
tblTransactions
rowRead
+
fnReverse
"显示地址10000及其联系地址和联系人"
query
tblAddresses
lnkPostalAddresses
lnkContactPeople
→ 参考
references/adressen-verwaltung.md
"创建新客户/供应商/潜在客户,含联系地址和联系人"
mutation
→ 嵌套操作:
rowNew
+
rowSave
+
lnkPostalAddresses
+
lnkContactPeople
→ 参考
references/adressen-verwaltung.md
"有哪些单据类型?"
query
tblTransactionTypes
rowsRead
→ 参考
references/parametertabellen.md
"配置了哪些付款条件?"
query
tblPaymentTerms
rowsRead
→ 参考
references/parametertabellen.md
"显示税码"
query
tblValueAddedTaxTypes
rowsRead
→ 参考
references/parametertabellen.md
"有哪些单位/货币?"
query
tblUnitsOfMeasure
/
tblCurrencies
→ 参考
references/parametertabellen.md

Schnellreferenz

快速参考

Wichtigste Tabellen

核心表

GraphQL-TabelleBeschreibungSchreibbar
tblAddresses
Adressenja
tblPostalAddresses
Anschriftenja
tblContactPeople
Ansprechpartnerja
tblContacts
Kontakteja
tblProducts
Artikelja
tblProductGroups
Warengruppenja
tblSuppliers
Artikel-Lieferantenja
tblTransactions
Vorgaengeja
tblTransactionItems
Vorgangspositionenja (verschachtelt in tblTransactions)
tblTransactionsArchive
Archiv Vorgaengeja
tblProjects
Projekteja
tblDocuments
Dokumenteja
tblCalendar
Kalenderja
tblWarehouses
Lagerja
tblClient
Mandant (Einzeldatensatz)ja
tblOpenItems
Offene Postennein
tblInventory
Lagerbestaendenein
tblUsers
Benutzernein
Parametertabellen
tblTransactionTypes
Vorgangsarten (Wandlungskette, Buchungsparameter)nein
tblPostingParameters
Buchungsparameternein
tblUnitsOfMeasure
Mengeneinheiten (UN/ECE-Codes fuer E-Rechnung)nein
tblValueAddedTaxTypes
Steuerschluessel mit Saetzen und DATEV-Zuordnungnein
tblPaymentTerms
Zahlungsbedingungennein
tblCurrencies
Fremdwaehrungen mit Wechselkursennein
tblShippingTypes
Versandartennein
tblAddressStatuses
Adressstatus mit Nummernkreisennein
tblSalutations
Anredennein
tblDunningLevels
Mahnstufennein
tblPaymentMethods
Zahlungsartennein
tblProjectTypes
Projektartennein
tblProjectStates
Projektstatus (Workflow)nein
tblDocumentTypes
Dokumentenarten (DMS)nein
tblDocumentStates
Dokumentenstatus (Workflow)nein
tblCommunicationTypes
Kommunikationsartennein
GraphQL表描述可编辑
tblAddresses
地址
tblPostalAddresses
联系地址
tblContactPeople
联系人
tblContacts
联系人信息
tblProducts
商品
tblProductGroups
商品组
tblSuppliers
商品供应商
tblTransactions
业务单据
tblTransactionItems
单据行项目是(嵌套在tblTransactions中)
tblTransactionsArchive
归档单据
tblProjects
项目
tblDocuments
文档
tblCalendar
日历
tblWarehouses
仓库
tblClient
客户端(单条记录)
tblOpenItems
未清项
tblInventory
库存
tblUsers
用户
参数表
tblTransactionTypes
单据类型(转换链、过账参数)
tblPostingParameters
过账参数
tblUnitsOfMeasure
计量单位(电子发票用UN/ECE代码)
tblValueAddedTaxTypes
税码(含税率和DATEV映射)
tblPaymentTerms
付款条件
tblCurrencies
外币(含汇率)
tblShippingTypes
配送方式
tblAddressStatuses
地址状态(含编号范围)
tblSalutations
称谓
tblDunningLevels
催款级别
tblPaymentMethods
付款方式
tblProjectTypes
项目类型
tblProjectStates
项目状态(工作流)
tblDocumentTypes
文档类型(DMS)
tblDocumentStates
文档状态(工作流)
tblCommunicationTypes
通信类型

Haeufigste Patterns (Copy-Paste-fertig)

常用模式(可直接复制使用)

graphql
undefined
graphql
undefined

LESEN - Einzelner Datensatz

读取 - 单条记录

query { tblProducts { rowRead(kf1ArtNr: { string: "LUTSCHER" }) { fldArtNr fldSuchBeg fldBez1(as: DISPLAY_TEXT) } } }
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) } } }
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 } } } }
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) } } }
mutation { tblProducts { rowNew { fldArtNr(set: { string: "NEU-001" }) fldBez1(set: { text: "新商品" } as: DISPLAY_TEXT) } } }

SCHREIBEN - Datensatz aendern

写入 - 修改记录

mutation { tblProducts { rowModify(kf1ArtNr: { string: "LUTSCHER" }) { fldBez1(set: { text: "Neuer Name" } as: DISPLAY_TEXT) } } }
mutation { tblProducts { rowModify(kf1ArtNr: { string: "LUTSCHER" }) { fldBez1(set: { text: "新名称" } 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! } } } }

---
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必须在fnPost之前! } } } }

---

1. Grundstruktur

1. 基础结构

Tabellenzugriff

表访问

Alle Operationen beginnen mit
tbl...
auf oberster Ebene:
graphql
query {
  tblProducts { ... }       # Artikel
  tblAddresses { ... }      # Adressen
  tblTransactions { ... }   # Vorgaenge
}
所有操作均以顶层的
tbl...
开头:
graphql
query {
  tblProducts { ... }       # 商品
  tblAddresses { ... }      # 地址
  tblTransactions { ... }   # 业务单据
}

Leseoperationen

读取操作

FeldBeschreibungRueckgabe
rowRead(...)
Einzelnen Datensatz lesen
Row
oder
null
rowsRead(...)
Liste lesen
[Row]
conRead(...)
Paginierte Liste (Relay Connection)
Connection
字段描述返回值
rowRead(...)
读取单条记录
Row
null
rowsRead(...)
读取列表
[Row]
conRead(...)
读取分页列表(Relay Connection)
Connection

Einzeldatensatz-Tabellen

单记录表

tblClient
braucht keine Suchparameter:
graphql
query { tblClient { rowRead { fldMandNr fldMandTyp } } }

tblClient
无需搜索参数:
graphql
query { tblClient { rowRead { fldMandNr fldMandTyp } } }

2. Datensatzauswahl

2. 记录选择

exactMatch
- Exakte Suche

exactMatch
- 精确搜索

graphql
rowRead(exactMatch: { byNr: { kf1ArtNr: { string: "PROD-001" } } })
graphql
rowRead(exactMatch: { byNr: { kf1ArtNr: { string: "PROD-001" } } })

Verschachtelte Schluesselfelder

嵌套关键字段

graphql
rowRead(exactMatch: {
  byArtNrLagNr: {
    kf1ArtNr: { string: "LUTSCHER"
      kf2LagNr: { string: "HAUPT" }
    }
  }
})
graphql
rowRead(exactMatch: {
  byArtNrLagNr: {
    kf1ArtNr: { string: "LUTSCHER"
      kf2LagNr: { string: "HAUPT" }
    }
  }
})

Verkuerzte Schreibweisen

简写方式

graphql
undefined
graphql
undefined

Statt:

完整写法:

rowRead(exactMatch: { byNr: { kf1ArtNr: { string: "LUTSCHER" } } })
rowRead(exactMatch: { byNr: { kf1ArtNr: { string: "LUTSCHER" } } })

Kurz (impliziert exactMatch + byNr):

简写(隐含exactMatch + byNr):

rowRead(kf1ArtNr: { string: "LUTSCHER" })
rowRead(kf1ArtNr: { string: "LUTSCHER" })

Bereich:

范围查询:

rowsRead(kf1ArtNr: { from: { string: "0" }, to: { string: "12000" } })
undefined
rowsRead(kf1ArtNr: { from: { string: "0" }, to: { string: "12000" } })
undefined

Weitere Sucharten

其他搜索类型

graphql
undefined
graphql
undefined

Naechster Treffer

最近匹配

rowRead(nearestMatch: { byNr: { kf1ArtNr: { string: "PROD-000" } } })
rowRead(nearestMatch: { byNr: { kf1ArtNr: { string: "PROD-000" } } })

Aktueller Benutzer

当前用户

tblUsers { rowRead(using: current) { fldAnmNa } }
tblUsers { rowRead(using: current) { fldAnmNa } }

Bereich mit exklusiver Obergrenze

带排他上限的范围

rowsRead(allBetween: { byNr: { kf1ArtNr: { from: { string: "A" }, to: { string: "Z" }, toExclusive: true } } })

---
rowsRead(allBetween: { byNr: { kf1ArtNr: { from: { string: "A" }, to: { string: "Z" }, toExclusive: true } } })

---

3. Datenfelder und Ausgabeformate

3. 数据字段与输出格式

Praefix-System

前缀体系

PraefixBedeutungBeispiel
tbl...
Tabelle
tblProducts
fld...
Datenfeld
fldArtNr
,
fldPreis
kf...
Schluesselfeld
kf1ArtNr
,
kf2LagNr
by...
Sortierfolge
byNr
,
bySuchBeg
row...
Verweis auf verknuepften Datensatz
rowWgrNr
lnk...
Verlinkung zu Datentabelle
lnkInventory
aco...
Betragsgruppe
acoEPr
,
acoGPreis
img...
Bildfeld
imgBild
fn...
Funktion
fnPost
,
fnIsCustomer
_...
System-Feld
_if
,
_string
前缀含义示例
tbl...
tblProducts
fld...
数据字段
fldArtNr
,
fldPreis
kf...
关键字段
kf1ArtNr
,
kf2LagNr
by...
排序规则
byNr
,
bySuchBeg
row...
关联记录引用
rowWgrNr
lnk...
数据表链接
lnkInventory
aco...
金额组
acoEPr
,
acoGPreis
img...
图片字段
imgBild
fn...
函数
fnPost
,
fnIsCustomer
_...
系统字段
_if
,
_string

as
-Parameter - Ausgabeformat

as
参数 - 输出格式

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
graphql
fldPreis                    # 标准格式
fldPreis(as: TEXT)          # "99,95 EUR"(本地化)
fldPreis(as: FLOAT)         # 99.95
fldPreis(as: STRING)        # 原始字符串
fldPreis(as: INT)           # 整数
fldPreis(as: DISPLAY_TEXT)  # 显示格式(RTF字段会转换为纯文本)
fldGueltigAb(as: TEXT)      # "24.12.2023"(本地化)
fldGspKz(as: BOOLEAN)       # true/false

Gemeinsame Felder (in fast allen Tabellen)

通用字段(几乎所有表都包含)

undefined
undefined

Identifikation

标识

fldID # Automatische ID fldModifyLSN / fldInsertLSN # Versionierung (BigInt)
fldID # 自动ID fldModifyLSN / fldInsertLSN # 版本号(BigInt)

Audit-Trail

审计追踪

fldErstDat / fldErstBzr # Erstellungsdatum / -benutzer fldAendDat / fldAendBzr # Aenderungsdatum / -benutzer
fldErstDat / fldErstBzr # 创建日期/创建用户 fldAendDat / fldAendBzr # 修改日期/修改用户

Sperren

锁定

fldGspKz # Gesperrt-Kennzeichen (Boolean) fldGspDat / fldGspInfo # Sperrdatum / -information fldGspGrp # Gesperrtgruppe
fldGspKz # 锁定标识(Boolean) fldGspDat / fldGspInfo # 锁定日期/锁定信息 fldGspGrp # 锁定组

Freitext

自由文本

fldInfo # Informationsfeld fldMemo # Memo-Feld fldQuickInfo # QuickInfo

---
fldInfo # 信息字段 fldMemo # 备注字段 fldQuickInfo # 快速信息

---

4. Verknuepfungen

4. 关联关系

Verweise (
row...
) - Einzelner verknuepfter Datensatz

引用(
row...
)- 单个关联记录

graphql
tblProducts {
  rowsRead {
    fldArtNr
    fldWgrNr                  # Fremdschluessel-Wert
    rowWgrNr { fldBez }       # Verknuepfte Warengruppe
    rowEinh { fldKuBez }      # Verknuepfte Einheit
  }
}
graphql
tblProducts {
  rowsRead {
    fldArtNr
    fldWgrNr                  # 外键值
    rowWgrNr { fldBez }       # 关联的商品组
    rowEinh { fldKuBez }      # 关联的单位
  }
}

Verlinkungen (
lnk...
) - Liste verknuepfter Datensaetze

链接(
lnk...
)- 关联记录列表

WICHTIG:
by.../using...
gehoeren auf
rowsRead
INNERHALB des Links, NICHT auf
lnk...
:
graphql
undefined
重要:
by.../using...
需放在链接内部的
rowsRead
上,而非
lnk...
上:
graphql
undefined

RICHTIG:

正确写法:

lnkInventory { rowsRead(byArtNrLagNrArtBDat: { usingArtNr: {} }) { fldLagNr fldMge } }
lnkInventory { rowsRead(byArtNrLagNrArtBDat: { usingArtNr: {} }) { fldLagNr fldMge } }

FALSCH:

错误写法:

lnkInventory(byArtNrLagNrArtBDat: { usingArtNr: {} }) { rowsRead { ... } }
undefined
lnkInventory(byArtNrLagNrArtBDat: { usingArtNr: {} }) { rowsRead { ... } }
undefined

Verschachtelte Tabellen (
tbl...
innerhalb Row)

嵌套表(Row内的
tbl...

graphql
tblClient {
  rowRead {
    fldMandNr
    tblBnkVb { rowsRead { fldNr fldIBAN } }
  }
}

graphql
tblClient {
  rowRead {
    fldMandNr
    tblBnkVb { rowsRead { fldNr fldIBAN } }
  }
}

5. Filter

5. 过滤

VERBINDLICHER WORKFLOW: keyFilter-First

强制工作流:keyFilter优先

BEVOR du einen Filter schreibst, fuehre IMMER diese Schritte aus:
  1. Sortierfolgen ermitteln: Pruefe per Introspection, welche
    by...
    -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:
    ProductSlowAnyFields
    ,
    TransactionSlowAnyFields
    ,
    AddressSlowAnyFields
    . Sonderfaelle:
    CalendarEntrySlowAnyFields
    ,
    AccountSlowAnyFields
    ,
    BillOfMaterialsEntrySlowAnyFields
    ,
    InventoryEntrySlowAnyFields
    . Gleiches Muster gilt fuer
    {SingularName}FastAnyFields
    . Die
    by...
    -Parameter findest du auf den
    rowsRead
    /
    conRead
    -Argumenten. Alternativ: Probiere
    rowsRead(allBetween: { by
    und schau welche Vorschlaege die API akzeptiert.
  2. keyFilter pruefen: Gibt es eine
    by...
    -Sortierfolge, die das gewuenschte Filterfeld als
    kf...
    -Schluessel enthaelt? → keyFilter verwenden!
    graphql
    # Beispiel: Laender ohne Staatsangehoerigkeitsnummer
    # byStaatsNr existiert mit kf1StaatsNr → keyFilter nutzen!
    rowsRead(allBetween: {
      byStaatsNr: { keyFilter: { isNull: { field: kf1StaatsNr } } }
    })
  3. Fallback: Nur wenn KEIN passender
    by...
    -Index existiert:
    • fastFilter
      fuer einfache Vergleiche auf
      fld...
      -Felder
    • slowFilter
      NUR fuer Arithmetik,
      in
      -Operator,
      fn...
      -Funktionen
NIEMALS direkt
slowFilter
verwenden, ohne vorher
keyFilter
geprueft zu haben!
在编写过滤条件前,务必执行以下步骤:
  1. 确定排序规则: 通过自省检查表包含哪些
    by...
    排序规则:
    graphql
    { __type(name: "{SingularName}SlowAnyFields") { enumValues { name } } }
    重要: 枚举名称使用实体的单数名称,而非表名! 示例:
    ProductSlowAnyFields
    ,
    TransactionSlowAnyFields
    ,
    AddressSlowAnyFields
    。 特殊情况:
    CalendarEntrySlowAnyFields
    ,
    AccountSlowAnyFields
    ,
    BillOfMaterialsEntrySlowAnyFields
    ,
    InventoryEntrySlowAnyFields
    {SingularName}FastAnyFields
    遵循相同模式。
    by...
    参数可在
    rowsRead
    /
    conRead
    的参数中找到。或者:尝试输入
    rowsRead(allBetween: { by
    ,查看API支持的选项。
  2. 检查keyFilter: 是否存在包含目标过滤字段作为
    kf...
    关键字的
    by...
    排序规则? → 使用keyFilter!
    graphql
    # 示例:无税号的国家
    # 存在byStaatsNr规则,含kf1StaatsNr → 使用keyFilter!
    rowsRead(allBetween: {
      byStaatsNr: {
        keyFilter: { isNull: { field: kf1StaatsNr } }
      }
    })
  3. 备选方案: 仅当无对应
    by...
    索引时:
    • fastFilter
      用于
      fld...
      字段的简单比较
    • slowFilter
      仅用于算术运算、
      in
      操作符、
      fn...
      函数
切勿直接使用slowFilter,务必先检查keyFilter!

Drei Filtertypen

三种过滤类型

FilterPerformanceBesonderheiten
keyFilter
Am schnellstenFilter auf
kf...
-Felder innerhalb
by...
- IMMER bevorzugen!
fastFilter
Schnell (DB-seitig)Nur einfache Vergleiche auf
fld...
slowFilter
Langsam (App-seitig)Arithmetik,
in
-Operator,
fn...
- nur als letzter Ausweg
过滤类型性能特点
keyFilter
最快
by...
参数内的
kf...
字段过滤 - 始终优先使用!
fastFilter
快(数据库层面)仅支持
fld...
字段的简单比较
slowFilter
慢(应用层面)支持算术运算、
in
操作符、
fn...
函数 - 仅作为最后选择

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 } }
graphql
{ eq: [{field: fldStatus}, {value: 1}] }       # 等于
{ ne: [{field: fldStatus}, {value: 0}] }       # 不等于
{ gt: [{field: fldMge}, {value: 0}] }          # 大于
{ lt: [{field: fldMge}, {value: 100}] }        # 小于
{ ge: [{field: fldMge}, {value: 1}] }          # 大于等于
{ le: [{field: fldMge}, {value: 99}] }         # 小于等于
{ isNull: { field: fldAuftrNr } }              # 检查NULL
{ isNotNull: { field: fldAuftrNr } }

Boolesche Kombinationen

布尔组合

graphql
{ and: [Ausdruck1, Ausdruck2] }
{ or:  [Ausdruck1, Ausdruck2] }
{ not: Ausdruck }
graphql
{ and: [表达式1, 表达式2] }
{ or:  [表达式1, 表达式2] }
{ not: 表达式 }

WICHTIG: @oneOf-Regel

重要:@oneOf规则

Pro Filterobjekt nur EIN Operator:
graphql
undefined
每个过滤对象仅允许一个操作符:
graphql
undefined

FALSCH:

错误写法:

fastFilter: { eq: [...], gt: [...] }
fastFilter: { eq: [...], gt: [...] }

RICHTIG:

正确写法:

fastFilter: { and: [ { eq: [...] }, { gt: [...] } ] }
undefined
fastFilter: { and: [ { eq: [...] }, { gt: [...] } ] }
undefined

IN-Operator (nur slowFilter)

IN操作符(仅slowFilter支持)

graphql
{ in: { left: { field: fldLagBestArt }, list: [1, 7] } }
Alle Werte muessen denselben Typ haben (@sametype-Regel).
graphql
{ in: { left: { field: fldLagBestArt }, list: [1, 7] } }
所有值必须为同一类型(@sametype规则)。

keyFilter - Performantester Filter

keyFilter - 性能最优的过滤

Filtert auf
kf...
-Felder INNERHALB des
by...
-Parameters. Schneller als fast/slowFilter.
graphql
undefined
by...
参数内的
kf...
字段过滤,比fast/slowFilter更快。
graphql
undefined

Alle Artikel ab Nummer "10"

所有编号从"10"开始的商品

rowsRead( allBetween: { byNr: { keyFilter: { ge: [{ field: kf1ArtNr }, { value: "10" }] } kf1ArtNr: { from: { string: "0" }, to: { string: "ZZZZZ" } } } } )
rowsRead( allBetween: { byNr: { keyFilter: { ge: [{ field: kf1ArtNr }, { value: "10" }] } kf1ArtNr: { from: { string: "0" }, to: { string: "ZZZZZ" } } } } )

Mehrere Werte per OR

多值OR查询

keyFilter: { or: [ { eq: [{ field: kf1AdrNr }, { value: "10000" }] } { eq: [{ field: kf1AdrNr }, { value: "70000" }] } ] }

**Prioritaet:** `keyFilter` > `fastFilter` > `slowFilter` (immer schnellsten moegl. Typ waehlen).

Detaillierte keyFilter-Patterns siehe `references/erweiterte-query-patterns.md`.
keyFilter: { or: [ { eq: [{ field: kf1AdrNr }, { value: "10000" }] } { eq: [{ field: kf1AdrNr }, { value: "70000" }] } ] }

**优先级:** `keyFilter` > `fastFilter` > `slowFilter`(始终选择最快的可用类型)。

详细keyFilter模式请查看`references/erweiterte-query-patterns.md`。

Funktionen in Filtern (nur slowFilter)

过滤中的函数(仅slowFilter支持)

graphql
{ fnPos: [{ value: "Suchtext" }, { field: fldSuchBeg }] }   # Textsuche (>0 = gefunden, NUR String-Felder!)
{ fnGetAktDate: [] }                                      # Aktuelles Datum
{ fnIncDate: [{ fnGetAktDate: [] }, { value: 0 }, { value: -12 }] }  # Datum +/- Monate
graphql
{ fnPos: [{ value: "搜索文本" }, { field: fldSuchBeg }] }   # 文本搜索(>0表示找到,仅支持字符串字段!)
{ fnGetAktDate: [] }                                      # 当前日期
{ fnIncDate: [{ fnGetAktDate: [] }, { value: 0 }, { value: -12 }] }  # 日期加减月份

Arithmetik in slowFilter

slowFilter中的算术运算

graphql
undefined
graphql
undefined

Brutto > 100 pruefen

检查总价>100

slowFilter: { gt: [{ mul: [{ field: fldVk0_Preis }, { value: 1.19 }] }, { value: 100 }] }

Operatoren: `add`, `sub`, `mul`, `div`, `mod`, `neg`. Details in `references/erweiterte-query-patterns.md`.

---
slowFilter: { gt: [{ mul: [{ field: fldVk0_Preis }, { value: 1.19 }] }, { value: 100 }] }

操作符:`add`, `sub`, `mul`, `div`, `mod`, `neg`。详情请查看`references/erweiterte-query-patterns.md`。

---

6. Direktiven

6. 指令

@store
- Wert in Variable speichern

@store
- 将值存入变量

Variable muss in der Operations-Signatur deklariert werden:
graphql
query ($storedValue: Any = null) {
  tblProducts {
    rowsRead {
      fldSuchBeg @store(in: $storedValue)
      echo: _any(value: $storedValue)
    }
  }
}
@store
funktioniert ueber Verschachtelungsebenen hinweg (aeussere Tabelle → verschachtelte tbl/lnk).
变量需在操作签名中声明:
graphql
query ($storedValue: Any = null) {
  tblProducts {
    rowsRead {
      fldSuchBeg @store(in: $storedValue)
      echo: _any(value: $storedValue)
    }
  }
}
@store
可跨嵌套层级使用(外层表 → 嵌套tbl/lnk)。

@onNull
- Null-Behandlung

@onNull
- NULL处理

graphql
fldSuchBeg @onNull(returnValue: "Nicht gefunden")     # Alternativwert
fldPreis @onNull(returnValue: skip)                    # Feld ueberspringen
fldArtNr @onNull(errorMessage: "ArtNr fehlt")          # Fehler ausloesen
graphql
fldSuchBeg @onNull(returnValue: "未找到")     # 替代值
fldPreis @onNull(returnValue: skip)                    # 跳过字段
fldArtNr @onNull(errorMessage: "商品编号缺失")          # 触发错误

@acquireLocks
- Praventive Sperren (nur mutation)

@acquireLocks
- 预防性锁定(仅mutation支持)

graphql
mutation @acquireLocks(
  forWriting: [tblTransactions, tblTransactionItems],
  forReading: [tblAddresses, tblProducts]
) { ... }
Verhindert Deadlocks bei Multi-Tabellen-Mutationen.
graphql
mutation @acquireLocks(
  forWriting: [tblTransactions, tblTransactionItems],
  forReading: [tblAddresses, tblProducts]
) { ... }
避免多表变更时的死锁。

@skip
/
@include

@skip
/
@include

graphql
fldPreis @include(if: $includePrice)

graphql
fldPreis @include(if: $includePrice)

7. System-Felder

7. 系统字段

graphql
undefined
graphql
undefined

Wertausgabe

值输出

_any(value: $variable) _string(expr: { add: [{field: fldArtNr}, {value: " - "}, {field: fldSuchBeg}] }) _boolean(expr: { gt: [{field: fldLagMge}, {value: 0}] }) _localDate(expr: { fnGetAktDate: [] })
_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 }
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" )
status: _ifThenElse( expr: { gt: [{field: fldLagMge}, {value: 0}] }, then: "有库存", else: "无库存" )

Fehler ausloesen

触发错误

_raise(message: "Negative Lagermenge!")

---
_raise(message: "库存数量为负!")

---

8. Mutationen

8. 变更操作

query vs. mutation

query vs mutation

  • query
    : Snapshot-Transaktion, keine Sperren, null bei Fehler
  • mutation
    : Normale Transaktion, Alles-oder-Nichts (Fehler = Rollback)
  • query
    :快照事务,无锁定,错误时返回null
  • mutation
    :常规事务,全有或全无(错误则回滚)

Schreiboperationen

写入操作

OperationZweck
rowNew
Neuen Datensatz erstellen
rowNew(ifNotExists: ...)
Nur erstellen wenn nicht vorhanden (gibt
null
statt Fehler)
rowCopy(exactMatch: ...)
Datensatz kopieren (nicht-gesetzte Felder vom Original)
rowModify(exactMatch: ...)
Datensatz aendern
rowDelete(exactMatch: ...)
Datensatz loeschen
rowsModify/rowsCopy/rowsDelete
Massenoperationen (Fehler bei einem → Rollback aller)
操作用途
rowNew
创建新记录
rowNew(ifNotExists: ...)
仅当记录不存在时创建(返回
null
而非错误)
rowCopy(exactMatch: ...)
复制记录(未设置的字段继承原记录值)
rowModify(exactMatch: ...)
修改记录
rowDelete(exactMatch: ...)
删除记录
rowsModify/rowsCopy/rowsDelete
批量操作(一条记录失败则所有操作回滚)

Row-Kontexte

Row上下文

KontextSchreiben
lnk...
tbl...
schreibbar
RowMutationNew/Copy/Modify
janeinja
RowMutationRead
(nach rowSave)
neinja (mit Schreiben)nein
RowMutationDelete
neinneinnein
上下文可写入
lnk...
可用
tbl...
可编辑
RowMutationNew/Copy/Modify
RowMutationRead
(rowSave后)
是(可写入)
RowMutationDelete

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
null
zurueck):
graphql
rowNew(ifNotExists: { exactMatch: { byNr: { kf1ArtNr: { string: "PROD-001" } } } })
graphql
mutation {
  tblProducts {
    rowNew {
      fldArtNr(set: { string: "PROD-001" })
      fldBez1(set: { text: "新商品" } as: DISPLAY_TEXT)
      fldVk0_Preis(set: { float: 99.95 })
    }   # 块结束时自动保存
  }
}
条件创建(记录已存在时无错误,返回
null
):
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
.
graphql
mutation {
  tblProducts {
    rowCopy(kf1ArtNr: { string: "LUTSCHER" }) {
      fldArtNr(set: { string: "LUTSCHER-V2" })
      # 其他字段(价格、名称等)继承原记录值
      fldBez1(as: DISPLAY_TEXT)
      fldVk0_Preis(as: FLOAT)
    }
  }
}
rowCopy、ifNotExists和乐观锁的详情请查看
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
    }
  }
}
graphql
mutation {
  tblProducts {
    rowModify(kf1ArtNr: { string: "PROD-001" }) {
      fldVk0_Preis(set: { float: 129.95 })
      fldBez1(as: DISPLAY_TEXT)       # 仅读取
    }
  }
}

Datensatz loeschen

删除记录

graphql
mutation {
  tblProducts {
    rowDelete(kf1ArtNr: { string: "PROD-001" }, ignoreWarnings: true) {
      fldArtNr                         # Noch lesbar vor Loeschung
    }
  }
}
graphql
mutation {
  tblProducts {
    rowDelete(kf1ArtNr: { string: "PROD-001" }, ignoreWarnings: true) {
      fldArtNr                         # 删除前仍可读取
    }
  }
}

Speicheroperationen

保存操作

graphql
undefined
graphql
undefined

rowSave - Speichern, dann Read-Kontext (lnk... verfuegbar)

rowSave - 保存后进入Read上下文(lnk...可用)

rowNew { fldArtNr(set: { string: "X" }) rowSave { fldID fldModifyLSN } # Ab hier nur lesen + lnk... }
rowNew { fldArtNr(set: { string: "X" }) rowSave { fldID fldModifyLSN } # 从此处开始仅可读取 + 使用lnk... }

rowSaveAndModify - Speichern, weiter schreiben

rowSaveAndModify - 保存后可继续写入

rowNew { fldArtNr(set: { string: "X" }) rowSaveAndModify { fldBez1(set: { text: "Name" } as: DISPLAY_TEXT) tblTransactionItems { ... } # Verschachtelte Tabelle schreibbar } }

**WICHTIG:** Nach `rowSave`/`rowSaveAndModify` NICHT mehr im aeusseren Block schreiben!
rowNew { fldArtNr(set: { string: "X" }) rowSaveAndModify { fldBez1(set: { text: "名称" } as: DISPLAY_TEXT) tblTransactionItems { ... } # 嵌套表可编辑 } }

**重要:** 执行`rowSave`/`rowSaveAndModify`后,切勿在外层块中继续写入!

Feldmanipulation (
set
-Parameter)

字段操作(
set
参数)

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
graphql
fldArtNr(set: { string: "PROD-005" })           # 字符串
fldVk0_Preis(set: { float: 99.95 })             # 浮点数
fldVk0_Preis(set: { text: "99,95" })            # 本地化文本
fldGspAbDat(set: { text: "2023-12-24" })          # 日期(ISO格式YYYY-MM-DD,也支持localdate)
fldGspKz(set: { boolean: true })                 # 布尔值
fldArt(set: { text: "Rechnung I" })              # 单据类型(始终使用文本,编号可能因配置不同而变化!)
fldMge(set: { int: 5 })                         # 整数
fldGspAbDat(set: {})                             # 设置为NULL

Auto-Nummerierung (naechste freie Nr wenn vergeben)

自动编号(保存时分配下一个可用编号)

fldAdrNr(set: { text: "10000", allowAutoNrOnSave: true })
fldAdrNr(set: { text: "10000", allowAutoNrOnSave: true })

Kaskadierende Updates unterdruecken

抑制级联更新

fldZahlBed(set: { text: "30 Tage netto", suppressRelatedUpdates: true })
undefined
fldZahlBed(set: { text: "30天净付", suppressRelatedUpdates: true })
undefined

Assign-Parameter

Assign参数

graphql
undefined
graphql
undefined

Row-Assign: Felder aus anderem Datensatz befuellen

Row-Assign:从其他记录填充字段

rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) { ... } rowNew(assignUser: { using: current }) { ... }
rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) { ... } rowNew(assignUser: { using: current }) { ... }

Feld-Assign: Einzelnes Feld per Suche zuweisen

Feld-Assign:通过搜索分配单个字段

fldWgrNr(assignProductGroup: { exactMatch: { byBez: { kf1Bez: { text: "Elektronik" } } } }) fldSBzrNr(assignUser: { using: current })
fldWgrNr(assignProductGroup: { exactMatch: { byBez: { kf1Bez: { text: "电子" } } } }) fldSBzrNr(assignUser: { using: current })

using: context in Verlinkungen (nimmt aeusseren Datensatz)

using: 链接中的上下文(使用外层记录)

lnkProjects { rowNew(assignAddress: { using: context }) { ... } }
undefined
lnkProjects { rowNew(assignAddress: { using: context }) { ... } }
undefined

Verlinkungen in Mutationen (nur in RowMutationRead)

变更中的链接(仅RowMutationRead支持)

graphql
mutation {
  tblAddresses {
    rowRead(kf1AdrNr: { string: "10000" }) {
      lnkPostalAddresses {
        rowNew(setAdrNrAnsNr: usingAdrNr) {
          fldNa2(set: { text: "Lieferanschrift" })
          rowSave { fldAnsNr }
        }
      }
    }
  }
}
set...
-Parameter bei
rowNew
in Verlinkungen: befuellt Schluesselfelder automatisch aus Kontext.

graphql
mutation {
  tblAddresses {
    rowRead(kf1AdrNr: { string: "10000" }) {
      lnkPostalAddresses {
        rowNew(setAdrNrAnsNr: usingAdrNr) {
          fldNa2(set: { text: "送货地址" })
          rowSave { fldAnsNr }
        }
      }
    }
  }
}
链接中
rowNew
set...
参数:自动从上下文中填充关键字段。

9. Vorgaenge - Kern-Workflow

9. 业务单据 - 核心工作流

Vorgangsarten

单据类型

WICHTIG: Immer
text
statt
int
verwenden - Nummern koennen pro Installation abweichen!
graphql
fldArt(set: { text: "Rechnung I" })     # RICHTIG - Bezeichnung
fldArt(set: { int: 70 })                # VERMEIDEN - Nummer kann abweichen
Hinweis:
fnConvert
akzeptiert den Parameter
transactionTypeNo
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
重要: 始终使用
text
而非
int
- 编号可能因安装配置不同而变化!
graphql
fldArt(set: { text: "Rechnung I" })     # 正确写法 - 使用名称
fldArt(set: { int: 70 })                # 避免使用 - 编号可能不同
注意:
fnConvert
接受
transactionTypeNo
参数,支持整数和文本:
graphql
fnConvert(transactionTypeNo: 50, ...)              # 整数 - 标准编号(50=发货单)
fnConvert(transactionTypeNo: "Lieferschein", ...)  # 文本 - 名称
标准编号和名称请查看
references/vorgangsarten.md
所有单据类型的完整列表(含标准编号和过账参数):
references/vorgangsarten.md

Funktionsuebersicht

函数概述

FunktionBeschreibungKontextRueckgabe
fnPost
Buchen
rowRead
Einzelwert
fnReverse
Stornieren
rowRead
Einzelwert
fnConvert
Wandeln
rowRead
Array
fnMoveToArchive
Archivieren
rowRead
Einzelwert
Alle erfordern:
mutation
+
rowRead
-Kontext. Leere Belegnummer = fehlgeschlagen.
函数描述上下文返回值
fnPost
过账
rowRead
单个值
fnReverse
冲销
rowRead
单个值
fnConvert
转换
rowRead
数组
fnMoveToArchive
归档
rowRead
单个值
所有函数要求:
mutation
+
rowRead
上下文。空单据编号 = 操作失败。

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:
  • fldArtNr
    akzeptiert Artikelnummern UND Barcodes (Auto-Resolve)
  • fldArt
    kann als Nummer (
    int: 50
    ) oder Text (
    text: "Rechnung I"
    ) gesetzt werden
  • fldZeilenNr(set: { int: 0 })
    fuegt Position an den Anfang
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 })
          }
        }
      }
    }
  }
}
注意:
  • fldArtNr
    接受商品编号和条形码(自动解析)
  • fldArt
    可设置为编号(
    int: 50
    )或文本(
    text: "Rechnung I"
  • fldZeilenNr(set: { int: 0 })
    将行项目添加到开头

Vorgang buchen (
fnPost
)

过账单据(
fnPost

graphql
undefined
graphql
undefined

Bestehenden Vorgang buchen

过账已有单据

mutation { tblTransactions { rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500005" } } }) { fnPost { fldBelegNr } # Leer = nicht gebucht! } } }
undefined
mutation { tblTransactions { rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500005" } } }) { fnPost { fldBelegNr } # 空值表示未过账! } } }
undefined

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
        }
      }
    }
  }
}
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 {                    # 重要:先保存!
          fnPost { fldBelegNr }      # 再过账
        }
      }
    }
  }
}

Vorgang stornieren (
fnReverse
)

冲销单据(
fnReverse

graphql
mutation {
  tblTransactions {
    rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500002" } } }) {
      fnReverse { fldBelegNr }
    }
  }
}
graphql
mutation {
  tblTransactions {
    rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500002" } } }) {
      fnReverse { fldBelegNr }
    }
  }
}

Vorgang wandeln (
fnConvert
)

转换单据(
fnConvert

Gibt ein Array zurueck (kann mehrere Vorgaenge erzeugen):
graphql
mutation {
  tblTransactions {
    rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "AN2500001" } } }) {
      fnConvert(transactionTypeNo: 50, deliveryQuantityHandling: IGNORE) {
        fldBelegNr
      }
    }
  }
}
deliveryQuantityHandling
:
IGNORE
|
KEEP_UNCHANGED
|
RECALCULATE
|
RECALCULATE_IF_ZERO
返回数组(可能生成多个单据):
graphql
mutation {
  tblTransactions {
    rowRead(exactMatch: { byBelegNr: { kf1BelegNr: { string: "AN2500001" } } }) {
      fnConvert(transactionTypeNo: 50, deliveryQuantityHandling: IGNORE) {
        fldBelegNr
      }
    }
  }
}
deliveryQuantityHandling
:
IGNORE
|
KEEP_UNCHANGED
|
RECALCULATE
|
RECALCULATE_IF_ZERO

Preisberechnung nach Speichern

保存后的价格计算

graphql
rowSave {
  acoGPreis {
    totalGrossAmount          # Brutto
    totalNetAmount            # Netto
    totalTaxAmount            # Steuer
  }
}

graphql
rowSave {
  acoGPreis {
    totalGrossAmount          # 总价(含税)
    totalNetAmount            # 净价
    totalTaxAmount            # 税额
  }
}

10. Betragsgruppen (
aco...
)

10. 金额组(
aco...

Kurzbeispiel Lesen:
acoEPr { totalGrossAmount totalNetAmount totalTaxAmount }
Details (Lesen, Schreiben, Preisberechnung nach Speichern):
references/betragsgruppen.md

读取示例:
acoEPr { totalGrossAmount totalNetAmount totalTaxAmount }
详情(读取、写入、保存后价格计算):
references/betragsgruppen.md

11. Paginierung (Relay Connection)

11. 分页(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:
edgeCount
gibt nur die Anzahl der zurueckgegebenen Edges zurueck, NICHT die Gesamtanzahl der Datensaetze. Es gibt kein
totalCount
-Feld. Zum Zaehlen:
first
mit ausreichend grossem Wert setzen und
hasNextPage
pruefen.

graphql
query ($pageSize: Int = 10, $after: String) {
  tblProducts {
    conRead(first: $pageSize, after: $after) {
      edges {
        cursor
        node { fldArtNr fldSuchBeg }
      }
      pageInfo { hasNextPage endCursor startCursor edgeCount }
    }
  }
}
重要:
edgeCount
仅返回当前返回的Edges数量,而非总记录数。无
totalCount
字段。如需统计:设置足够大的
first
值并检查
hasNextPage

12. Haeufige Fehler vermeiden

12. 常见错误规避

FehlerLoesung
fnPost
in
rowModify
-Kontext
Nur in
rowRead
-Kontext
fnPost
ohne
rowSave
Immer
rowSave
vor
fnPost
Schreiben nach
rowSave
im selben Block
Nach
rowSave
nur lesen, oder
rowSaveAndModify
lnk...
in schreibendem Kontext
Erst
rowSave
, dann
lnk...
im Read-Kontext
Mehrere Operatoren in einem Filterobjekt
and
/
or
verwenden (@oneOf-Regel)
fnMoveToArchive
nach
fnPost
in einer Mutation
Getrennte Mutationen (Auto-Archivierung!)
Leere Belegnummer nicht geprueftImmer
fldBelegNr
in
fn...
-Rueckgabe pruefen
by.../using...
auf
lnk...
statt auf
rowsRead
Gehoeren auf
rowsRead
/
rowsModify
innerhalb des Links
@store(as: "name")
Korrekt:
@store(in: $variable)
mit Deklaration in Signatur
"Argument \"set\" is not defined"
Fehlende Berechtigung -
set
wird ohne Recht nicht angeboten
Feldreihenfolge nicht beachtetFelder werden sequentiell ausgefuehrt - Menge VOR Preis setzen
rowCopy
ohne neuen Schluessel
Kopie braucht eigenen Primaerschluessel
rowsDelete
bricht ab
Ein Fehler bei einem Datensatz → Rollback ALLER Loeschungen
fnConvert
schlaegt fehl →
rowNew
als Workaround
VERBOTEN! Bricht die Prozesskette. Fehlerursache klaeren und Nutzer informieren
fnReverse
schlaegt fehl →
rowDelete
als Workaround
VERBOTEN! Umgeht Storno-Logik (keine Gegenbuchungen, kein Audit-Trail). Nutzer informieren
fnPost
schlaegt fehl → naechsten Schritt trotzdem ausfuehren
VERBOTEN! Folgeschritte (z.B. Wandlung) setzen erfolgreiche Buchung voraus. Nutzer informieren
fnMoveToArchive
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
edgeCount
als Gesamtanzahl interpretiert
edgeCount
zaehlt nur zurueckgegebene Edges, NICHT alle Datensaetze.
first: 0
ergibt immer
edgeCount: 0
. Zum Zaehlen:
first: 100+
und
hasNextPage
pruefen
fldNa2
/
fldOrt
/
fldPLZ
/
fldStr
auf
tblAddresses
Diese Felder leben in
tblPostalAddresses
, Zugriff ueber
lnkPostalAddresses

错误解决方案
rowModify
上下文中使用
fnPost
仅在
rowRead
上下文中使用
未执行
rowSave
就调用
fnPost
始终在
fnPost
前执行
rowSave
同一区块内
rowSave
后继续写入
rowSave
后仅读取,或使用
rowSaveAndModify
写入上下文中使用
lnk...
先执行
rowSave
,再在Read上下文中使用
lnk...
单个过滤对象中使用多个操作符使用
and
/
or
组合(@oneOf规则)
单次变更中
fnPost
后调用
fnMoveToArchive
拆分为独立变更(自动归档!)
未检查空单据编号始终检查
fn...
返回结果中的
fldBelegNr
by.../using...
放在
lnk...
而非
rowsRead
应放在链接内部的
rowsRead
/
rowsModify
使用
@store(as: "name")
正确写法:
@store(in: $variable)
并在签名中声明变量
出现
"Argument \"set\" is not defined"
错误
无权限 - 无此字段的写入权限
未注意字段顺序字段按顺序执行 - 先设置数量再设置价格
rowCopy
未设置新关键字段
副本需有独立主键
rowsDelete
执行中断
一条记录失败则所有删除操作回滚
fnConvert
失败后用
rowNew
作为替代方案
禁止! 会中断流程链。排查失败原因并通知用户
fnReverse
失败后用
rowDelete
作为替代方案
禁止! 绕过冲销逻辑(无反向记账、无审计追踪)。通知用户
fnPost
失败后仍执行后续步骤
禁止! 后续步骤(如转换)依赖成功过账。通知用户
fnMoveToArchive
失败后手动复制/删除数据
禁止! 归档有专属逻辑。通知用户
跳过流程步骤(如直接创建发票而非订单确认→发货单→发票)仅当用户明确确认时执行,需告知用户可能的流程中断风险
edgeCount
视为总记录数
edgeCount
仅统计当前返回的Edges,而非总记录数。
first: 0
始终返回
edgeCount: 0
。如需统计:设置
first: 100+
并检查
hasNextPage
tblAddresses
中访问
fldNa2
/
fldOrt
/
fldPLZ
/
fldStr
这些字段属于
tblPostalAddresses
,需通过
lnkPostalAddresses
访问

13. Troubleshooting

13. 故障排除

Fehlermeldung:
"Argument \"set\" is not defined"

错误信息:
"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:
fldDat
,
fldVerk
,
fldKredLimit
,
fldInExtBeaKz
,
fldGspKz
,
fldEPreis
.
原因: 权限不足。API用户无此字段的写入权限。 解决方案: 在ERP中检查权限。需权限的字段:
fldDat
,
fldVerk
,
fldKredLimit
,
fldInExtBeaKz
,
fldGspKz
,
fldEPreis

Fehlermeldung:
"Address \"10002\" is blocked."

错误信息:
"Address \"10002\" is blocked."

Ursache: Adresse hat
fldGspKz = true
(Gesperrt-Kennzeichen). Loesung: Andere Adresse verwenden oder Sperrung im ERP aufheben.
原因: 地址的
fldGspKz = true
(锁定标识)。 解决方案: 使用其他地址或在ERP中解除锁定。

Leere Belegnummer nach
fnPost
/
fnReverse
/
fnConvert

fnPost
/
fnReverse
/
fnConvert
后返回空单据编号

Ursache: Funktion konnte nicht ausgefuehrt werden (Voraussetzungen nicht erfuellt). Loesung: Immer Belegnummer pruefen:
fnPost { fldBelegNr }
. Leer = fehlgeschlagen.
原因: 函数执行失败(未满足前置条件)。 解决方案: 始终检查单据编号:
fnPost { fldBelegNr }
。空值表示执行失败。

Rollback nach
fnPost
trotz erfolgreicher Buchung

fnPost
成功过账后仍回滚

Ursache: Eine NACHFOLGENDE Operation in derselben Mutation ist fehlgeschlagen. Alles-oder-Nichts. Loesung:
fnPost
und Folgeoperationen in getrennte Mutationen aufteilen, oder Fehlerquelle beheben.
原因: 同一次变更中的后续操作失败,触发全量回滚。 解决方案:
fnPost
与后续操作拆分为独立变更,或修复错误源。

fnPost
hat keine Wirkung (Positionen fehlen)

fnPost
无效果(行项目缺失)

Ursache:
rowSave
vor
fnPost
vergessen.
fnPost
arbeitet nur auf gespeicherten Daten. Loesung: Immer
rowSave { fnPost { fldBelegNr } }
-
rowSave
VOR
fnPost
.
原因: 调用
fnPost
前未执行
rowSave
fnPost
仅处理已保存的数据。 解决方案: 始终使用
rowSave { fnPost { fldBelegNr } }
-
rowSave
必须在
fnPost
之前。

lnk...
-Felder nicht verfuegbar

lnk...
字段不可用

Ursache: Datensatz ist noch in schreibendem Kontext (
RowMutationNew/Copy/Modify
). Loesung: Erst
rowSave
aufrufen → wechselt zu
RowMutationRead
lnk...
wird verfuegbar.
原因: 记录仍处于写入上下文(
RowMutationNew/Copy/Modify
)。 解决方案: 先调用
rowSave
→ 切换到
RowMutationRead
上下文 →
lnk...
可用。

Laufzeitfehler nach
rowSave

rowSave
后出现运行时错误

Ursache: Versuch im selben Block nach
rowSave
noch Felder zu schreiben. Loesung:
rowSaveAndModify
verwenden wenn danach noch geschrieben werden soll, oder alle Schreiboperationen VOR
rowSave
platzieren.
原因: 同一区块内
rowSave
后尝试写入字段。 解决方案: 如需继续写入,使用
rowSaveAndModify
,或所有写入操作放在
rowSave
之前。

Deadlock bei parallelen Mutationen

并行变更时出现死锁

Ursache:
@acquireLocks
fehlt oder unvollstaendig. Loesung:
@acquireLocks(forWriting: [...], forReading: [...])
mit ALLEN beteiligten Tabellen.
原因: 缺少或不完整的
@acquireLocks
解决方案: 使用
@acquireLocks(forWriting: [...], forReading: [...])
指定所有涉及的表。

fn-Funktion schlaegt fehl (fnConvert/fnPost/fnReverse/fnMoveToArchive)

fn函数执行失败(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
    rowNew
    einen Ersatzbeleg anlegen (bricht Prozesskette)
  • NICHT per
    rowDelete
    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 →
    fnConvert
    nur auf aktive Vorgaenge moeglich
  • Fehlende Berechtigung → API-Nutzer-Rechte pruefen
  • Geschaeftsregel verletzt → Beleg-Daten pruefen (z.B. fehlende Pflichtfelder)
原因: 多种可能 - 未配置转换路径、单据已归档、权限不足、违反业务规则、单据被锁定。 解决方案: 停止操作 - 切勿尝试替代方案!尤其是:
  • 禁止
    rowNew
    创建替代单据(会中断流程链)
  • 禁止
    rowDelete
    删除单据替代冲销(绕过审计追踪)
  • 禁止在当前步骤失败后执行后续流程 正确做法:向用户展示错误信息并共同排查原因。可能的原因:
  • ERP中未配置转换路径 → 需ERP管理员启用路径
  • 单据已归档 →
    fnConvert
    仅支持活跃单据
  • 权限不足 → 检查API用户权限
  • 违反业务规则 → 检查单据数据(如必填字段缺失)

Query gibt
null
zurueck obwohl Datensatz existiert

Ursache (in mutation):
modifyLSN
stimmt nicht ueberein (Optimistic Locking Konflikt). Ursache (in query): Schluessel falsch oder Sortierfolge stimmt nicht. Loesung:
@onNull
-Pattern verwenden um Konflikte abzufangen. Siehe
references/mutation-patterns.md
.