# Vorgänge in microtech GraphQL - Funktionsreferenz

<span class="custom-button red-button">Gen. 24 Enterprise</span>

Diese Dokumentation beschreibt alle GraphQL-Funktionen rund um Vorgänge (`tblTransactions`), das Vorgangsarchiv (`tblTransactionsArchive`) sowie die analogen Archiv-Funktionen für Projekte (`tblProjects`, `tblProjectsArchive`).

Grundlegende Konzepte wie Row-Kontexte, Speicheroperationen und Feldmanipulation werden im **[GraphQL Handbuch Teil 2 - Mutationen](graphqlmutations.md)** ausführlich erklärt.

---

## Inhaltsverzeichnis

1. [Überblick](#1-uberblick)
2. [Vorgang anlegen](#2-vorgang-anlegen)
3. [Vorgang ändern](#3-vorgang-andern)
4. [Vorgang buchen (fnPost)](#4-vorgang-buchen-fnpost)
5. [Vorgang stornieren (fnReverse)](#5-vorgang-stornieren-fnreverse)
6. [Vorgang wandeln (fnConvert)](#6-vorgang-wandeln-fnconvert)
7. [Vorgang löschen](#7-vorgang-loschen)
8. [Externe Bearbeitung sperren und entsperren](#8-externe-bearbeitung-sperren-und-entsperren)
9. [Archiv-Funktionen](#9-archiv-funktionen)
10. [Berechtigungen](#10-berechtigungen)
11. [Atomizität und Sperren](#11-atomizitat-und-sperren)
12. [Best Practices](#12-best-practices)

---

## 1. Überblick

### Betroffene Tabellen

#### Vorgänge

| Tabelle | Beschreibung |
|---------|-------------|
| `tblTransactions` | Vorgänge (Übersicht) |
| `tblTransactionsArchive` | Vorgangsarchiv |
| `tblTransactionItems` | Vorgangspositionen (verschachtelte Tabelle) |

#### Projekte

| Tabelle | Beschreibung |
|---------|-------------|
| `tblProjects` | Projekte |
| `tblProjectsArchive` | Projektarchiv |

!!! info "Hinweis"

    Vorgänge und Projekte sind eigenständige Bereiche. Projekte unterstützen ausschließlich die Archiv-Funktionen (`fnMoveToArchive`, `fnRestoreToProjects`). Alle anderen Funktionen wie `fnPost`, `fnReverse` und `fnConvert` sind nur für Vorgänge verfügbar.

### Funktionsübersicht

#### Vorgangsfunktionen (`tblTransactions`)

| Funktion | Beschreibung | Kontext | Rückgabe |
|----------|-------------|---------|----------|
| `fnPost` | Vorgang buchen | `rowRead` | Einzelwert |
| `fnReverse` | Vorgang stornieren | `rowRead` | Einzelwert |
| `fnConvert` | Vorgang wandeln | `rowRead` | **Array** |
| `fnMoveToArchive` | Ins Archiv verschieben | `rowRead` | Einzelwert |
| `fnUnlockFromExternalProcessing` | Externe Bearbeitung aufheben | `rowRead` | Einzelwert |

**Rückgabewerte:** Alle Funktionen geben über `fldBelegNr` die Belegnummer des betroffenen Vorgangs zurück. Eine leere Belegnummer bedeutet, dass die Operation nicht erfolgreich war. `fnConvert` ist die einzige Funktion, die ein **Array** zurückgibt, da eine Wandlung mehrere neue Vorgänge erzeugen kann (z.B. bei Streckengeschäften). Alle anderen Funktionen geben einen einzelnen Datensatz zurück.

**Wichtige Grundregel:** Alle Vorgangsfunktionen (`fnPost`, `fnReverse`, `fnConvert`, `fnMoveToArchive`, etc.) sind ausschließlich innerhalb einer `mutation` verfügbar und erfordern, dass der Datensatz **nicht** im Modify-Status ist. Der Datensatz muss sich im `RowMutationRead`-Kontext befinden - also entweder über `rowRead` geöffnet oder nach einem `rowSave` gespeichert sein.

#### Archiv-Funktionen

| Funktion | Beschreibung | Verfügbar in | Kontext | Rückgabe |
|----------|-------------|-------------|---------|----------|
| `fnRestoreToTransactions` | Vorgang aus Archiv zurückkopieren | `tblTransactionsArchive` | `rowRead` | Einzelwert |
| `fnRestoreToProjects` | Projekt aus Archiv zurückkopieren | `tblProjectsArchive` | `rowRead` | Einzelwert |
| `fnMoveToArchive` | Projekt ins Archiv verschieben | `tblProjects` | `rowRead` | Einzelwert |

---

## 2. Vorgang anlegen

Neue Vorgänge werden über `rowNew` in `tblTransactions` erstellt. Positionen werden über die verschachtelte Tabelle `tblTransactionItems` angelegt.

### Einfacher Vorgang mit Positionen

```graphql
mutation VorgangMitPositionen {
  tblTransactions {
    rowNew {
      # Kopfdaten
      fldAdrNr(set: { text: "10000" })    # Adressnummer des Kunden
      fldArt(set: { int: 70 })            # Vorgangsart als Nummer

      rowSaveAndModify {
        fldBelegNr                          # Generierte Belegnummer abrufen

        # Positionen anlegen
        tblTransactionItems {
          pos1: rowNew {
            fldArtNr(set: { string: "1" })  # Artikelnummer
            fldBez(set: { text: "Erste Position" })
            fldMge(set: { float: 1 })       # Menge
          }

          pos2: rowNew {
            fldArtNr(set: { string: "2" })
            fldBez(set: { text: "Abweichende Bezeichnung" })  # Optional: überschreibt die Vorgabe vom Artikel
            fldMge(set: { float: 2 })
          }    

          pos3: rowNew {
            fldArtNr(set: { string: "3" })
            fldBez(set: { text: "Wird erste Position" })
            fldZeilenNr(set: { int: 0 })    # Als erste Position einfügen
            fldMge(set: { float: 3 })
          }
        }
      }
    }
  }
}
```

**Hinweise:**

*    `fldArt` kann als Nummer (`int: 70`) oder als Text (`text: "Rechnung I"`) angegeben werden

*    `fldBez` muss nur gesetzt werden, wenn die Bezeichnung von der Artikelvorgabe abweichen soll. Ohne Angabe wird die Bezeichnung automatisch vom Artikel übernommen

*    Positionen werden über Aliase (`pos1`, `pos2`, `pos3`) angelegt, um sie in der Antwort unterscheiden zu können

*    `fldZeilenNr` steuert die Einfügeposition: `int: 0` fügt die Position an den Anfang ein

*    `fldArtNr` akzeptiert auch Barcodes - der Barcode wird automatisch zum Artikel aufgelöst

### Vorgang mit Barcode-Eingabe

```graphql
mutation VorgangMitBarcode {
  tblTransactions {
    rowNew {
      # Kopfdaten
      fldAdrNr(set: { text: "10004" })
      fldArt(set: { int: 70 })

      rowSaveAndModify {
        fldBelegNr

        # Positionen mit Barcode statt Artikelnummer
        tblTransactionItems {
          pos1: rowNew {
            fldArtNr(set: { string: "123456" })  # Barcode wird automatisch aufgelöst
            fldMge(set: { float: 1 })
          }
        }
      }
    }
  }
}
```

### Vorgang mit Preisberechnung

Nach dem Speichern eines Vorgangs kann der berechnete Gesamtbetrag über `acoGPreis` abgefragt werden:

```graphql
mutation VorgangMitPreisberechnung {
  tblTransactions {
    rowNew {
      # Kopfdaten
      fldAdrNr(set: { text: "10001" })
      fldArt(set: { int: 70 })

      rowSaveAndModify {
        fldBelegNr

        # Positionen anlegen
        tblTransactionItems {
          pos1: rowNew {
            fldArtNr(set: { string: "1" })
            fldMge(set: { int: 1 })
          }
        }

        # Speichern und berechnete Preise abrufen
        rowSave {
          acoGPreis {
            totalGrossAmount                 # Gesamtbetrag brutto
            totalNetAmount                   # Gesamtbetrag netto
            totalTaxAmount                   # Steuerbetrag
          }
        }
      }
    }
  }
}
```

### Vorgang mit erweiterten Kopfdaten

```graphql
mutation VorgangMitKopfdaten {
  tblTransactions {
    rowNew {
      # Kopfdaten
      fldAdrNr(set: { text: "10001" })
      fldArt(set: { int: 70 })
      fldWaehr(set: { string: "USD" })            # Währung, die nicht der Standard Währung entspricht
      fldDat(set: { localdate: "2025-08-17" })     # Vorgangsdatum
      fldUStKat(set: { text: "2 Ausland" })        # Umsatzsteuerkategorie setzt nur die Kat, aber nicht den Steuerschlüssel

      rowSaveAndModify {
        fldBelegNr

        # Positionen anlegen
        tblTransactionItems {
          pos1: rowNew {
            fldArtNr(set: { string: "1" })
            fldMge(set: { int: 1 })
          }
        }
      }
    }
  }
}
```

### Vorgang mit Lager- und Stellplatzdaten

Für Lagerbuchungen können pro Position Lager, Lagernummer und Stellplatz angegeben werden. Da dieses Beispiel auf mehrere Tabellen schreibt, wird [`@acquireLocks`](#112-transaktionssperren-mit-acquirelocks) verwendet.

```graphql
mutation WarenausgangMitStellplatz
  # Sperren vorab anfordern, um Deadlocks zu vermeiden (siehe Abschnitt 11.2)
  @acquireLocks(
    forWriting: [tblTransactions, tblTransactionItems]
    forReading: [tblAddresses, tblProducts]
  )
{
  tblTransactions {
    # Adresse per assignAddress zuweisen (siehe Handbuch Teil 2, Abschnitt 13.2)
    rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) {
      # Kopfdaten
      fldArt(set: { int: 50 })                     # Vorgangsart
      fldBez(set: { text: "Warenausgang-Buchung" }) # Bezeichnung
      fldDat(set: { text: "23.10.2025" })           # Datum

      rowSaveAndModify {
        fldBelegNr

        # Positionen mit Lager- und Stellplatzdaten
        tblTransactionItems {
          pos1: rowNew(assignProduct: { kf1ArtNr: { text: "1" } }) {
            fldMge(set: { float: 10 })               # Menge
            fldAusLag(set: { text: "104" })           # Ausgangslager
            fldAusLagNr(set: { text: "1" })           # Ausgangslager-Nr.
            fldAusStellplatz(set: { text: "1/S1/E1" })# Stellplatz
            fldNachLag(set: { int: 102 })             # Ziellager
            rowSave {
              fldID
              fldPosNr
              fldArtNr
              fldMge
            }
          }
        }

        # Erst speichern, dann buchen (siehe Abschnitt 4)
        rowSave {
          fnPost {
            fldBelegNr                                # Belegnummer des gebuchten Vorgangs
          }
        }
      }
    }
  }
}
```

!!! warning "Beachten Sie"

    `fldAusStellplatz` wird nur im Schema angeboten, wenn die Lizenz für die XL-Version mit Zusatzmodul Lager und Bestand aktiviert ist und der Benutzer die entsprechende Berechtigung besitzt.

!!! info "Info"

    Für Details zu `assignAddress` und `assignProduct` siehe **[Handbuch Teil 2, Abschnitt 13.2 - Row-Assign-Parameter](graphqlmutations.md#132-row-assign-parameter)**.

---

## 3. Vorgang ändern

Bestehende Vorgänge werden über `rowModify` geändert. Die Identifikation erfolgt über die Belegnummer.

### Felder ändern

```graphql
mutation VorgangAendern {
  tblTransactions {
    rowModify(
      exactMatch: { kf1BelegNr: { string: "RE12500003" } }
    ) {
      # Felder setzen
      fldUStKat(set: { text: "2 Ausland" })   # Umsatzsteuerkategorie ändern
    }
  }
}
```

### Verkürzte Schreibweise

Statt der vollständigen `exactMatch`-Syntax kann eine verkürzte Form verwendet werden:

```graphql
# Vollständig
rowModify(exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500003" } } })

# Verkürzt (gleichwertig)
rowModify(exactMatch: { kf1BelegNr: { string: "RE12500003" } })

# Noch kürzer
rowModify(kf1BelegNr: { text: "RE12500003" })
```

!!! info "Info"

    Für Details zur verkürzten Schreibweise siehe **[Handbuch Teil 1, Abschnitt 4 - Verkürzte Schreibweisen](graphqldoku.md#4-verkurzte-schreibweisen-durch-implizite-parameter)**.

<!-- Ergänzt gemäß TR-705 09.07.2026 -->

### Positionen mehrerer Vorgänge per Sammelfilter anpassen

Über `rowsRead` in Kombination mit `fastFilter` lassen sich mehrere Vorgänge gleichzeitig filtern und deren Positionen in einer Mutation anpassen. Das folgende Beispiel filtert Lieferscheine, bei denen das Kennzeichen `fldSel76` nicht gesetzt ist, überträgt die jeweilige Belegnummer in die Positionen und bucht anschließend.

**Szenario:** Für alle Lieferscheine ohne gesetztes Kennzeichen `fldSel76` soll in jeder Positionszeile das Feld „Ursprungsbelegnr." mit der aktuellen Lieferscheinnummer gefüllt und der Vorgang anschließend gebucht werden.

```graphql
mutation LieferscheineBearbeiten(
  $belegNr: String = ""
)
  @reportStackTrace
  @acquireLocks(
    forWriting: [tblTransactions, tblTransactionItems]
  ) {
  tblTransactions {
    rowsRead(fastFilter: {
      and: [
        { eq: [{ field: fldArt }, { value: 50 }] }             # Nur Lieferscheine
        { or: [
          { eq: [{ field: fldSel76 }, { value: null }] }
          { eq: [{ field: fldSel76 }, { value: 0 }] }
        ]}
      ]
    }) {
      # Belegnummer lesen und für die Positionsebene zwischenspeichern
      fldBelegNr @store(in: $belegNr)

      # Ursprungsbelegnr. in allen Positionen mit der Belegnummer füllen
      tblTransactionItems {
        rowsModify {
          fldUrsBelegNr(set: { text: $belegNr })
        }
      }

      # Speichern und Buchen
      fnPost {
        fldBelegNr
      }
    }
  }
}
```

!!! info "Info"

    **Hinweise:**

    *    `rowsRead` mit `fastFilter` filtert mengenbasiert über mehrere Datensätze hinweg – im Gegensatz zu `rowRead`, das einen einzelnen Datensatz über `exactMatch` adressiert
    
    *    Die Verschachtelung von `and`/`or` im Filter erlaubt die Kombination mehrerer Bedingungen: Vorgangsart UND (Kennzeichen leer ODER Kennzeichen falsch)
    
    *    `@store(in: $belegNr)` speichert die Belegnummer der aktuell verarbeiteten Vorgangszeile zwischen, damit sie in der verschachtelten Positionsebene wiederverwendet werden kann
    
    *    Der Default-Wert der Variable (`$belegNr: String = ""`) dient nur als Platzhalter; der tatsächliche Wert wird pro verarbeiteter Zeile über `@store` überschrieben
    
    *    `@acquireLocks` ist hier erforderlich, da die Mutation sowohl auf `tblTransactions` als auch auf `tblTransactionItems` schreibt (siehe auf dieser Seite: Abschnitt "11.2 Transaktionssperren mit `@acquireLocks`")

!!! warning "Beachten Sie"

    Bei `rowsModify`-Massenoperationen führt ein Fehler bei der Bearbeitung eines einzelnen Datensatzes zum Abbruch der **gesamten** Operation. Alle bereits durchgeführten Änderungen werden zurückgerollt – eine Teilausführung ist nicht möglich (siehe **[Handbuch Teil 2, Abschnitt 12.7 - Massenoperationen mit Listen-Feldern](graphqlmutations.md#127-massenoperationen-mit-listen-feldern)**).

<!-- Ende Ergänzung TR-705 -->

---

## 4. Vorgang buchen (fnPost)

`fnPost` bucht einen Vorgang. Die Funktion ist ausschließlich in `tblTransactions` verfügbar (nicht im Archiv).

**Rückgabewert:** Die Belegnummer des gebuchten Vorgangs. Ist die Belegnummer leer, wurde der Vorgang nicht gebucht.

### Bestehenden Vorgang buchen

```graphql
mutation VorgangBuchen {
  tblTransactions {
    rowRead(
      exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500005" } } }
    ) {
      fldBelegNr
      fldArt

      fnPost {
        fldBelegNr     # Belegnummer des gebuchten Vorgangs (leer = nicht gebucht)
      }
    }
  }
}
```

### Vorgang anlegen und buchen in einer Mutation

Wenn ein Vorgang in derselben Mutation angelegt und gebucht werden soll, muss der Vorgang vor dem Buchen gespeichert werden. `fnPost` sieht nur Änderungen, die bereits mit `rowSave` persistiert wurden.

```graphql
mutation VorgangAnlegenUndBuchen
  # Sperren vorab anfordern (siehe Abschnitt 11.2)
  @acquireLocks(
    forWriting: [tblTransactions, tblTransactionItems]
    forReading: [tblAddresses, tblProducts]
  )
{
  tblTransactions {
    rowNew(assignAddress: { kf1AdrNr: { text: "10000" } }) {
      # Kopfdaten
      fldArt(set: { int: 50 })
      fldBez(set: { text: "Warenausgang-Buchung" })

      rowSaveAndModify {
        fldBelegNr

        # Positionen anlegen
        tblTransactionItems {
          pos1: rowNew(assignProduct: { kf1ArtNr: { text: "1" } }) {
            fldMge(set: { float: 10 })
          }
        }

        # Erst speichern, damit fnPost die Positionen sieht
        rowSave {
          fnPost {
            fldBelegNr  # Belegnummer des gebuchten Vorgangs (leer = nicht gebucht)
          }
        }
      }
    }
  }
}
```

!!! info "Info"

    **Warum `rowSave` vor `fnPost`?**

    Innerhalb von `rowSaveAndModify` befindet sich der Datensatz im Modify-Status. Änderungen wie das Hinzufügen von Positionen sind zu diesem Zeitpunkt noch nicht in der Datenbank gespeichert. `fnPost` operiert auf dem gespeicherten Stand des Datensatzes. Ohne vorheriges `rowSave` würde `fnPost` den Vorgang ohne die neuen Positionen buchen - beispielsweise würde keine Lagerbestandsbuchung erfolgen.

---

## 5. Vorgang stornieren (fnReverse)

`fnReverse` storniert einen bestehenden Vorgang.

**Rückgabewert:** Die Belegnummer des stornierten Vorgangs. Ist die Belegnummer leer, wurde der Vorgang nicht storniert.

```graphql
mutation VorgangStornieren {
  tblTransactions {
    rowRead(
      exactMatch: { byBelegNr: { kf1BelegNr: { string: "LI12500002" } } }
    ) {
      fldBelegNr
      fldArt

      fnReverse {
        fldBelegNr     # Belegnummer des stornierten Vorgangs (leer = nicht storniert)
      }
    }
  }
}
```

---

## 6. Vorgang wandeln (fnConvert)

`fnConvert` wandelt einen Vorgang in eine andere Vorgangsart um (z.B. Angebot in Auftragsbestätigung, Lieferschein in Rechnung).

**Rückgabewert:** Ein **Array** von Belegnummern der neu erstellten Vorgänge. Im Gegensatz zu allen anderen Vorgangsfunktionen (`fnPost`, `fnReverse`, etc.), die einen einzelnen Datensatz zurückgeben, gibt `fnConvert` immer eine Liste zurück - da eine Wandlung mehrere neue Vorgänge erzeugen kann (z.B. bei Streckengeschäften, bei denen aus einem Auftrag mehrere Bestellungen an verschiedene Lieferanten entstehen).

### Parameter

| Parameter | Typ | Beschreibung |
|-----------|-----|-------------|
| `transactionTypeNo` | Int | Zielvorgangsart-Nummer. Ohne Angabe wird die Vorgabe aus den Vorgangsart-Parametern verwendet. |
| `deliveryDate` | LocalDateTime | Lieferdatum im Format `YYYY-MM-DD` (z.B. "`2025-12-12`"). Ohne Angabe wird die Vorgabe aus den Vorgangsart-Parametern verwendet. |
| `deliveryQuantityHandling` | Enum | Steuerung der Liefermengenberechnung (siehe unten). Ohne Angabe wird die Vorgabe aus den Vorgangsart-Parametern verwendet. |

### deliveryQuantityHandling - Optionen

| Wert | Beschreibung |
|------|-------------|
| `IGNORE` | Liefermenge nicht beachten |
| `KEEP_UNCHANGED` | Liefermenge nicht ändern und beachten |
| `RECALCULATE` | Liefermenge anhand des aktuellen Lagerbestands aktualisieren und beachten |
| `RECALCULATE_IF_ZERO` | Nur wenn Liefermenge = 0: anhand des aktuellen Lagerbestands aktualisieren und beachten |

### Wandeln ohne Parameter

Verwendet die Vorgaben aus der Vorgangsart:

```graphql
mutation VorgangWandeln {
  tblTransactions {
    rowRead(
      exactMatch: { byBelegNr: { kf1BelegNr: { string: "AN2500001" } } }
    ) {
      fldBelegNr
      fldArt

      # Wandlung mit Vorgaben aus der Vorgangsart
      fnConvert {
        fldBelegNr     # Belegnummer(n) der erstellten Vorgänge
      }
    }
  }
}
```

### Wandeln mit Parametern

```graphql
mutation VorgangWandelnMitParametern {
  tblTransactions {
    rowRead(
      exactMatch: { byBelegNr: { kf1BelegNr: { string: "BK2500002" } } }
    ) {
      fldBelegNr

      # Wandlung mit expliziten Parametern
      fnConvert(
        transactionTypeNo: 50                # Zielvorgangsart
        deliveryDate: "2025-12-12"           # Lieferdatum
        deliveryQuantityHandling: IGNORE     # Liefermenge nicht beachten
      ) {
        fldBelegNr     # Belegnummern der erstellten Vorgänge
      }
    }
  }
}
```

---

## 7. Vorgang löschen

Vorgänge können über `rowDelete` gelöscht werden. Der optionale Parameter `ignoreWarnings` unterdrückt Warnmeldungen, die die Löschung sonst blockieren würden.

```graphql
mutation VorgangLoeschen(
  $belegNr: Any = "RE12500003"
  $ignoreWarnings: Boolean = true
) {
  tblTransactions {
    rowDelete(
      exactMatch: { byBelegNr: { kf1BelegNr: { string: $belegNr } } }
      ignoreWarnings: $ignoreWarnings    # Warnmeldungen unterdrücken
    ) {
      fldBez                             # Bezeichnung des gelöschten Vorgangs
    }
  }
}
```

!!! info "Info"

    Für Details zu `rowDelete` und `ignoreWarnings` siehe **[Handbuch Teil 2, Abschnitt 12.5 - Löschen von Datensätzen](graphqlmutations.md#125-loschen-von-datensatzen-mit-rowdelete)**.

---

## 8. Externe Bearbeitung sperren und entsperren

Vorgänge können über das Kennzeichen "Vorgang in externer Bearbeitung" (`fldInExtBeaKz`) für die externe Bearbeitung gesperrt werden. Dies verhindert, dass andere Benutzer oder Programmfunktionen den Vorgang gleichzeitig in microtech ERP bearbeiten können.

!!! info "Info"

    Das Thema wird in einer eigenen Dokumentation ausführlich behandelt: **[Vorgänge - Externe Bearbeitung](graphqlvorgangexternebearbeitung.md)**

---

## 9. Archiv-Funktionen

Die Archiv-Funktionen stehen sowohl für Vorgänge als auch für Projekte zur Verfügung. Sie sind ausschließlich innerhalb einer `mutation` nutzbar (nicht in `query`).

### 9.1 Ins Archiv verschieben (fnMoveToArchive)

`fnMoveToArchive` verschiebt einen Datensatz aus der Übersicht ins Archiv.

**Rückgabewert:** Die Belegnummer bzw. Projektnummer, wenn das Verschieben erfolgreich war.

#### Vorgang ins Archiv verschieben

```graphql
mutation VorgangArchivieren {
  tblTransactions {
    rowRead(
      exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500001" } } }
    ) {
      fldBelegNr

      fnMoveToArchive {
        fldBelegNr   # Belegnummer des archivierten Vorgangs (leer = nicht verschoben)
      }
    }
  }
}
```

#### Projekt ins Archiv verschieben

```graphql
mutation ProjektArchivieren {
  tblProjects {
    rowRead(
      exactMatch: { byNr: { kf1PrjNr: { string: "PJ-12345" } } }
    ) {
      fldPrjNr

      fnMoveToArchive {
        fldPrjNr     # Projektnummer des archivierten Projekts (leer = nicht verschoben)
      }
    }
  }
}
```

### 9.2 Aus Archiv zurückkopieren

#### Vorgang aus Archiv zurückkopieren (fnRestoreToTransactions)

`fnRestoreToTransactions` kopiert einen Vorgang aus dem Archiv zurück in die Vorgangsübersicht.

```graphql
mutation VorgangAusArchivWiederherstellen {
  tblTransactionsArchive {
    rowRead(
      exactMatch: { byBelegNr: { kf1BelegNr: { string: "RE12500001" } } }
    ) {
      fldBelegNr

      # allowOverwrite: false → Fehler wenn Vorgang schon in Übersicht existiert
      fnRestoreToTransactions(allowOverwrite: false) {
        fldBelegNr   # Belegnummer des wiederhergestellten Vorgangs (leer = fehlgeschlagen)
      }
    }
  }
}
```

#### Projekt aus Archiv zurückkopieren (fnRestoreToProjects)

```graphql
mutation ProjektAusArchivWiederherstellen {
  tblProjectsArchive {
    rowRead(
      exactMatch: { kf1PrjNr: { string: "ES25000001" } }
    ) {
      fldPrjNr

      # allowOverwrite: true → bestehendes Projekt in Übersicht wird überschrieben
      fnRestoreToProjects(allowOverwrite: true) {
        fldPrjNr     # Projektnummer des wiederhergestellten Projekts (leer = fehlgeschlagen)
      }
    }
  }
}
```

### 9.3 Der Parameter `allowOverwrite`

| Wert | Verhalten |
|------|-----------|
| `false` | Wenn der Vorgang/das Projekt bereits in der Übersicht existiert, wird ein Fehler zurückgegeben. |
| `true` | Ein bereits existierender Vorgang/Projekt in der Übersicht wird überschrieben. Erfordert die Berechtigung zum Löschen in der Übersicht. |

### 9.4 Wichtige Hinweise

*    `fnPost` ist im Archiv (`tblTransactionsArchive`) **nicht** verfügbar. Um einen archivierten Vorgang erneut zu bearbeiten, muss er zuerst mit `fnRestoreToTransactions` in die Übersicht zurückkopiert werden.

*    Prüfen Sie nach dem Zurückkopieren immer die zurückgegebene Belegnummer bzw. Projektnummer, um den Erfolg der Operation zu bestätigen.

*    Archiv-Funktionen sind nur im `rowRead`-Kontext innerhalb einer `mutation` verfügbar, nicht in einer `query`.

---

## 10. Berechtigungen

Der Zugriff auf Felder und Funktionen in Vorgängen unterliegt dem Berechtigungssystem von microtech ERP. Berechtigungen werden **feldbasiert** geprüft. Wenn einem Benutzer die Berechtigung für ein bestimmtes Feld fehlt, wird der `set`-Parameter für dieses Feld im Schema nicht angeboten.

### Berechtigungsrelevante Felder und Funktionen

| Feld / Funktion | Erforderliche Berechtigung |
|-----------------|---------------------------|
| `fldInExtBeaKz` | Kennzeichen "Vorgang in externer Bearbeitung" ändern |
| `fnUnlockFromExternalProcessing` | Kennzeichen "Vorgang in externer Bearbeitung" ändern |
| `fldDat` | Vorgangseingabe: Vorgangsdatum ändern |
| `fldVerk` | Vorgangseingabe: Verkäufer ändern |
| `fldKredLimit` | Kreditlimit ändern |
| `fldGspKz` | Gesperrt-Kennzeichen ändern |
| `fldEPreis` | Wunschpreis eingeben (Gesamtpreis editieren) |

### Verhalten ohne Berechtigung

Wenn die Berechtigung fehlt, ist der `set`-Parameter im GraphQL-Schema nicht vorhanden. Ein Versuch, das Feld zu setzen, führt zu folgendem Fehler:

```
"Argument \"set\" is not defined"
```

### Gesperrte Adressen

Beim Anlegen eines Vorgangs mit einer gesperrten Adresse (`fldGspKz = true`) wird ein Fehler zurückgegeben:

```
"Address \"10002\" is blocked."
```

---

## 11. Atomizität und Sperren

### 11.1 Atomizität

Mutationen in microtech GraphQL folgen dem **Alles-oder-Nichts-Prinzip**: Eine Mutation wird nur festgeschrieben, wenn sie vollständig und fehlerfrei durchläuft. Jeder Laufzeitfehler führt zum sofortigen Abbruch und automatischen Zurückrollen aller bereits durchgeführten Änderungen.

**Beispiel:** Wenn in einer Mutation ein Vorgang gebucht wird (`fnPost`) und eine nachfolgende Operation in derselben Mutation fehlschlägt, wird auch die Buchung zurückgerollt. Der Vorgang bleibt ungebucht.

!!! info "Info"

    Für Details siehe **[Handbuch Teil 2, Abschnitt 11.2.3 - Atomizität als Grundprinzip](graphqlmutations.md#1123-atomizitat-als-grundprinzip)**.

### 11.2 Transaktionssperren mit `@acquireLocks`

Bei Mutationen, die auf mehrere Tabellen schreiben oder in hochfrequenten Szenarien ausgeführt werden, sollte die `@acquireLocks`-Direktive verwendet werden. Sie verhindert Deadlocks, indem alle benötigten Sperren vor der Ausführung der Mutation atomar angefordert werden.

```graphql
mutation BeispielMitSperren
  @acquireLocks(
    forWriting: [tblTransactions, tblTransactionItems]  # Tabellen mit Schreibzugriff
    forReading: [tblAddresses, tblProducts]              # Tabellen mit Lesezugriff
  )
{
  tblTransactions {
    # Mutation wird nur ausgeführt, wenn alle Sperren verfügbar sind
    # ...
  }
}
```

**Wann `@acquireLocks` verwenden?**

*    Bei allen Schreiboperationen, die mehrere Tabellen betreffen

*    Bei hochfrequenten Operationen

*    Bei komplexen Workflows (z.B. Anlegen + Buchen in einer Mutation)

!!! info "Info"

    Für Details zum Sperrsystem siehe **[Handbuch Teil 2, Kapitel 15 - Transaktionssperren und Optimierung](graphqlmutations.md#15-transaktionssperren-und-optimierung)**.

---

## 12. Best Practices

### Vorgangsfunktionen erfordern gespeicherten Zustand

`fnPost`, `fnReverse`, `fnConvert` und die Archiv-Funktionen arbeiten immer auf dem gespeicherten Stand des Datensatzes. Wenn Sie in derselben Mutation Änderungen vornehmen und anschließend eine dieser Funktionen aufrufen, müssen Sie vorher `rowSave` aufrufen.

```graphql
# Richtig: rowSave vor fnPost
rowSaveAndModify {
  tblTransactionItems { ... }
  rowSave {
    fnPost { fldBelegNr }
  }
}

# Falsch: fnPost sieht die neuen Positionen nicht
rowSaveAndModify {
  tblTransactionItems { ... }
  fnPost { fldBelegNr }
}
```

### Belegnummer immer prüfen

Die Rückgabewerte von `fnPost`, `fnReverse`, `fnConvert`, `fnMoveToArchive`, `fnRestoreToTransactions` und `fnUnlockFromExternalProcessing` enthalten eine Belegnummer. Eine leere Belegnummer bedeutet, dass die Operation nicht erfolgreich war.

### Archiv-Operationen von Buchung trennen

Wenn die Buchungsparameter eines Vorgangs vorsehen, dass der Vorgang nach dem Buchen automatisch ins Archiv verschoben wird, darf `fnMoveToArchive` nicht in derselben Mutation nach `fnPost` aufgerufen werden. Der Vorgang existiert nach der automatischen Archivierung nicht mehr in der Übersicht, was zu einem Fehler führt. Durch das Atomizitätsprinzip wird dann die gesamte Mutation einschließlich der Buchung zurückgerollt.

**Empfehlung:** `fnPost` und `fnMoveToArchive` in separate Mutationen aufteilen, wenn die Buchungsparameter eine automatische Archivierung vorsehen.

### Sperren zeitnah freigeben

Wenn Sie Vorgänge für externe Bearbeitung sperren (`fldInExtBeaKz`), halten Sie die Sperre so kurz wie möglich. Gesperrte Vorgänge können im microtech ERP nicht bearbeitet werden. Implementieren Sie eine Fehlerbehandlung, die sicherstellt, dass die Sperre auch bei Fehlern wieder aufgehoben wird.

### Fehlerbehandlung implementieren

*    Prüfen Sie die Antworten auf Fehler, bevor Sie mit dem nächsten Schritt fortfahren

*    Planen Sie ein, was bei fehlgeschlagenen Operationen passiert

*    Bedenken Sie das Atomizitätsprinzip: Ein Fehler rollt die gesamte Mutation zurück
