1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 15:59:35 +01:00
effigenix/backend/docs/MASTERDATA_MANUAL_TESTS.md
Sebastian Frick 8c042925eb docs: manuelle Testfälle für Inventory und Production BC ergänzen
Testfälle aus allen GitHub-Issues mit Status 'Done' abgeleitet:
- TC-SL (1-15): Lagerorte
- TC-STK (1-5): Bestandspositionen
- TC-BATCH (1-10): Chargen
- TC-REC (1-22): Rezepte
- TC-CYCLE (1-3): Zyklus-Erkennung
- TC-BAT (1-4): Chargenplanung
- TC-INV-CROSS/TC-PROD-CROSS: Übergreifende Tests
2026-02-20 16:40:39 +01:00

677 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Manuelle Testfälle Masterdata BC
## Kontext
Der Masterdata Bounded Context ist vollständig implementiert und umfasst vier Aggregate:
- **ProductCategory** einfaches Kategorie-Aggregate
- **Supplier** Lieferanten mit Zertifikaten, Bewertungen, Adressen
- **Article** Artikel mit Verkaufseinheiten (SalesUnits), Lieferantenzuordnung
- **Customer** Kunden (B2B/B2C) mit Lieferadressen, Präferenzen, Rahmenvertrag (nur B2B)
Die TUI ist das Testwerkzeug für das Backend. Alle Schreiboperationen erfordern `MASTERDATA_WRITE`-Permission.
---
## Voraussetzungen
- Backend läuft (`./mvnw spring-boot:run` oder Docker)
- TUI läuft (`pnpm dev` im CLI-Verzeichnis)
- Eingeloggt als User **mit** `MASTERDATA_WRITE`-Permission (z.B. `admin`)
- Eingeloggt als User **ohne** `MASTERDATA_WRITE`-Permission (z.B. `viewer`) für Authz-Tests
---
## TC-CAT: Produktkategorien
### TC-CAT-01: Kategorie erstellen (Happy Path)
1. Masterdata → Produktkategorien → `[n]` Neu
2. Name: `Obst & Gemüse`, Beschreibung: `Frische Produkte` → Enter
- [x] **Erwartung:** Kategorie erscheint in der Liste; Name und Beschreibung korrekt
### TC-CAT-02: Kategorie erstellen ohne Beschreibung
1. Name: `Milchprodukte`, Beschreibung: leer → Enter
- [x] **Erwartung:** Kategorie wird angelegt; Beschreibung fehlt ohne Fehler
### TC-CAT-03: Kategorie bearbeiten
1. Kategorie `Obst & Gemüse` auswählen → `[e]`
2. Name ändern auf `Obst und Gemüse`, Beschreibung auf `Saisonale Frische`
- [x] **Erwartung:** Änderungen werden gespeichert und korrekt angezeigt
### TC-CAT-04: Doppelter Name wird abgelehnt
1. Neue Kategorie mit Name `Milchprodukte` (existiert bereits) anlegen
- [x] **Erwartung:** Fehlermeldung Name bereits vergeben; kein Datensatz angelegt
### TC-CAT-05: Kategorie löschen
1. Kategorie `Milchprodukte` in der Liste auswählen → `[d]`
2. Bestätigungsdialog mit `Ja` bestätigen
- [c] **Erwartung:** Kategorie verschwindet aus der Liste
<!-- TODO: Löschen nicht implementiert -->
### TC-CAT-06: Leerer Name wird abgelehnt
1. Neue Kategorie, Name leer → Enter
- [x] **Erwartung:** Fehler / Eingabe nicht möglich; kein API-Aufruf
---
## TC-SUP: Lieferanten
### TC-SUP-01: Lieferant erstellen Pflichtfelder
1. Lieferanten → `[n]` Neu
2. Name: `Frisch AG`, Telefon: `+49 30 12345` → restliche Felder leer → Enter
- [x] **Erwartung:** Lieferant erscheint in der Liste, Status `AKTIV`
### TC-SUP-02: Lieferant erstellen alle Felder
1. Name: `Bio GmbH`, Telefon: `+49 89 999`, E-Mail: `bio@example.com`
2. Ansprechpartner: `Max Muster`
3. Adresse: `Gartenstraße 5`, `12`, `80333`, `München`, `Deutschland`
4. Zahlungsziel: `30` Tage
- [/] **Erwartung:** Alle Daten in der Detailansicht korrekt angezeigt
<!-- TODO: Wenn 'Deutschland' eingegeben wird bekommt man einen Fehler dass nur Länderkennzeichen z.B. DE erlaubt sind -->
### TC-SUP-03: Lieferant erstellen ohne Pflichtfelder
1. Name leer → versuchen zu speichern
- [x] **Erwartung:** Fehler; kein Lieferant angelegt
3. Name gefüllt, Telefon leer → versuchen zu speichern
- [x] **Erwartung:** Fehler; kein Lieferant angelegt
### TC-SUP-04: Doppelter Name wird abgelehnt
1. Neuen Lieferanten `Frisch AG` anlegen (Name existiert bereits)
- [x] **Erwartung:** Fehlermeldung Name bereits vergeben
### TC-SUP-05: Lieferant deaktivieren und aktivieren
1. `Frisch AG` → Detailansicht → `[Deaktivieren]` → Bestätigen
- [x] **Erwartung:** Status wechselt auf `INAKTIV` (roter Punkt in Liste)
3. Erneut öffnen → `[Aktivieren]` → Bestätigen
- [x] **Erwartung:** Status wechselt auf `AKTIV`
### TC-SUP-06: Lieferant filtern
1. Lieferantenliste: `[A]` nur Aktive
- [x] **Erwartung:** Nur AKTIV-Lieferanten sichtbar
3. `[I]` nur Inaktive
- [x] **Erwartung:** Nur INAKTIV-Lieferanten sichtbar
5. `[a]` alle
- [x] **Erwartung:** Alle Lieferanten sichtbar
### TC-SUP-07: Lieferant bewerten
1. `Frisch AG``[Bewerten]`
2. Qualität: 4, Lieferung: 3, Preis: 5 → Enter
- [x] **Erwartung:** Bewertung in Detailansicht sichtbar; Durchschnitt = 4.0
4. In der Liste: Stern-Anzeige `★ 4.0`
### TC-SUP-08: Bewertung Grenzen (1 und 5)
1. Alle Scores auf 1 setzen → Speichern
- [x] **Erwartung:** Gespeichert; Durchschnitt = 1.0
3. Alle Scores auf 5 → Speichern
- [x] **Erwartung:** Gespeichert; Durchschnitt = 5.0
### TC-SUP-09: Zertifikat hinzufügen (gültig)
1. `Frisch AG``[Zertifikat hinzufügen]`
2. Typ: `ISO9001`, Aussteller: `TÜV`, ab: `2024-01-01`, bis: `2027-01-01`
- [x] **Erwartung:** Zertifikat erscheint in Detailansicht; Anzahl in Liste = 1
### TC-SUP-10: Zertifikat hinzufügen (abgelaufen)
1. Zertifikat, bis: `2023-12-31` (Datum in der Vergangenheit)
- [x] **Erwartung:** Zertifikat wird angelegt (keine Ablauf-Prüfung beim Hinzufügen); in Detail sichtbar
*(Edge Case: System soll abgelaufene Zertifikate anzeigen, nicht blocken)*
### TC-SUP-11: Doppeltes Zertifikat wird abgelehnt
1. Erneut `ISO9001`, `TÜV`, `2024-01-01` hinzufügen
- [x] **Erwartung:** Fehlermeldung Duplikat abgelehnt
<!-- TODO: Duplikat wird abgelehnt, ABER Fehlermeldung schlecht 'Unexpected error occurred. ...' -->
### TC-SUP-12: Zertifikat entfernen
1. `Frisch AG``[Zertifikat entfernen]` → Zertifikat auswählen → Enter
- [x] **Erwartung:** Zertifikat aus Detailansicht verschwunden
<!-- TODO: Zertifikatsauswahl zeigt kein Datum, daher schwer zu entscheiden wenn mehrere gleiche Zertifikate für untersch. Jahre -->
---
## TC-ART: Artikel
### TC-ART-01: Artikel erstellen PIECE_FIXED
1. Artikel → `[n]` Neu
2. Name: `Äpfel Gala`, Nummer: `OG-001`
3. Kategorie: `Obst & Gemüse` (mit ← →)
4. Einheit: `PIECE_FIXED`, Preis: `1.99`
- [x] **Erwartung:** Artikel in Liste; Preismodell automatisch `FIXED`
### TC-ART-02: Artikel erstellen KG (gewichtsbasiert)
1. Name: `Bananen`, Nummer: `OG-002`
2. Einheit: `KG`, Preis: `2.49`
- [x] **Erwartung:** Preismodell automatisch `WEIGHT_BASED`
### TC-ART-03: Artikel erstellen HUNDRED_GRAM und PIECE_VARIABLE
1. Einheit `HUNDRED_GRAM` → Preismodell `WEIGHT_BASED`
2. Einheit `PIECE_VARIABLE` → Preismodell `WEIGHT_BASED`
- [x] **Erwartung:** Konsistenz in beiden Fällen korrekt
### TC-ART-04: Doppelte Artikelnummer wird abgelehnt
1. Neuen Artikel mit Nummer `OG-001` anlegen
- [x] **Erwartung:** Fehlermeldung Artikelnummer bereits vergeben
### TC-ART-05: Artikel deaktivieren und aktivieren
1. `Äpfel Gala``[Deaktivieren]` → Bestätigen
- [x] **Erwartung:** Status INAKTIV
- [x] **Erwartung:** Status AKTIV
### TC-ART-06: Artikel filtern
1. `[A]` nur Aktive, `[I]` nur Inaktive, `[a]` alle
- [x] **Erwartung:** Filter wirkt korrekt
### TC-ART-07: Verkaufseinheit hinzufügen
1. `Äpfel Gala``[Verkaufseinheit hinzufügen]`
2. Einheit: `KG`, Preis: `3.50`
- [x] **Erwartung:** Zweite VE in Detailansicht; Anzahl VE in Liste = 2
### TC-ART-08: Doppelte Einheit wird abgelehnt
1. Erneut `PIECE_FIXED` für `Äpfel Gala` hinzufügen
- [x] **Erwartung:** Fehlermeldung Einheit bereits vorhanden
### TC-ART-09: Letzte Verkaufseinheit kann nicht entfernt werden
1. Artikel mit genau einer VE → `[Verkaufseinheit entfernen]`
- [x] **Erwartung:** Aktion nicht verfügbar / Fehler mindestens eine VE erforderlich
### TC-ART-10: Verkaufseinheit entfernen (wenn 2+ vorhanden)
1. `Äpfel Gala` hat 2 VE → `[Verkaufseinheit entfernen]` → KG-Einheit wählen
- [x] **Erwartung:** VE entfernt; nur noch PIECE_FIXED vorhanden
### TC-ART-11: Lieferant dem Artikel zuweisen *(falls TUI-Unterstützung vorhanden)*
1. `Äpfel Gala` → Lieferant `Frisch AG` zuweisen
- [ ] **Erwartung:** Lieferant in Detailansicht sichtbar
<!-- TODO: Aktuell noch nicht testbar -->
---
## TC-CUS: Kunden
### TC-CUS-01: B2C-Kunde erstellen
1. Kunden → `[n]` Neu
2. Typ: `B2C`, Name: `Max Mustermann`, Telefon: `+49 176 12345`
3. Rechnungsadresse: `Musterstr. 1`, `2`, `10115`, `Berlin`, `Deutschland`
- [ ] **Erwartung:** Kunde in Liste, Typ-Badge `B2C`, Status `AKTIV`
### TC-CUS-02: B2B-Kunde erstellen
1. Typ: `B2B`, Name: `Gastro GmbH`, Telefon: `+49 30 9876`
2. Rechnungsadresse vollständig ausfüllen
- [ ] **Erwartung:** Kunde in Liste, Typ-Badge `B2B`
### TC-CUS-03: Pflichtfelder Validierung
1. Name leer → Fehler
2. Telefon leer → Fehler
3. Rechnungsadresse unvollständig → Fehler
- [ ] **Erwartung:** Jeweils spezifische Fehlermeldung
### TC-CUS-04: Doppelter Kundenname wird abgelehnt
1. Erneut `Gastro GmbH` anlegen
- [ ] **Erwartung:** Fehlermeldung Name bereits vergeben
### TC-CUS-05: Kunde deaktivieren und aktivieren
1. `Max Mustermann``[Deaktivieren]` → Bestätigen → `[Aktivieren]`
- [ ] **Erwartung:** Status wechselt korrekt
### TC-CUS-06: Kunden filtern (Status + Typ)
1. `[A]` → nur Aktive; `[I]` → nur Inaktive; `[a]` → alle
2. `[B]` → nur B2B; `[C]` → nur B2C; `[b]` → alle
3. Kombination: Aktive B2B-Kunden
- [ ] **Erwartung:** Alle Filter wirken korrekt und kombinierbar
### TC-CUS-07: Lieferadresse hinzufügen
1. `Gastro GmbH``[Lieferadresse hinzufügen]`
2. Label: `Hauptküche`, Straße: `Kochstr.`, Nr: `12`, PLZ: `10963`, Stadt: `Berlin`, Land: `Deutschland`
3. Ansprechpartner: `Koch Müller`, Lieferhinweis: `Bitte kühlen`
- [ ] **Erwartung:** Lieferadresse in Detailansicht; Anzahl in Liste = 1
### TC-CUS-08: Lieferadresse entfernen
1. `[Lieferadresse entfernen]``Hauptküche` auswählen
- [ ] **Erwartung:** Lieferadresse entfernt
### TC-CUS-09: Mehrere Lieferadressen
1. Zwei Lieferadressen `Filiale Nord` und `Filiale Süd` hinzufügen
- [ ] **Erwartung:** Beide in Detailansicht; Anzahl in Liste = 2
### TC-CUS-10: Präferenzen setzen
1. `Max Mustermann``[Präferenzen setzen]`
2. `BIO` und `REGIONAL` aktivieren → Enter
- [ ] **Erwartung:** Präferenzen in Detailansicht sichtbar
4. Erneut öffnen → nur `HALAL` aktivieren → Enter
- [ ] **Erwartung:** Nur `HALAL` gesetzt (Set wird ersetzt, nicht ergänzt)
### TC-CUS-11: Alle Präferenzen abwählen
1. `[Präferenzen setzen]` → alle deaktivieren → Enter
- [ ] **Erwartung:** Keine Präferenzen mehr sichtbar
---
## TC-B2B: Rahmenverträge (B2B-spezifisch)
*Vorbedingung: `Gastro GmbH` (B2B) existiert; Artikel `Äpfel Gala` existiert*
### TC-B2B-01: Rahmenvertrag für B2B-Kunden erstellen *(falls TUI-Unterstützung vorhanden)*
1. `Gastro GmbH` → Rahmenvertrag-Bereich
2. Gültig ab: `2025-01-01`, bis: `2025-12-31`, Rhythmus: `WEEKLY`
3. Position: Artikel `Äpfel Gala`, vereinbarter Preis: `1.50`, Menge: `100`
- [ ] **Erwartung:** Rahmenvertrag in Detailansicht sichtbar
### TC-B2B-02: Rahmenvertrag für B2C-Kunden nicht möglich *(Backend-Test via API)*
```
POST /api/customers/{b2c-id}/frame-contract
```
- [ ] **Erwartung:** HTTP 400/422, Fehler `FrameContractNotAllowed`
---
## TC-AUTH: Autorisierung
### TC-AUTH-01: Lesezugriff ohne MASTERDATA_WRITE
1. Als `viewer` (ohne MASTERDATA_WRITE) einloggen
2. Alle Listen aufrufen (Kategorien, Lieferanten, Artikel, Kunden)
- [ ] **Erwartung:** Daten sichtbar, kein Fehler
### TC-AUTH-02: Schreibzugriff ohne MASTERDATA_WRITE wird abgelehnt
1. Als `viewer` versuchen: neue Kategorie erstellen
- [ ] **Erwartung:** HTTP 403 / Fehlermeldung in TUI; kein Datensatz angelegt
3. Dasselbe für Lieferant, Artikel, Kunde anlegen
- [ ] **Erwartung:** Jeweils Ablehnung
### TC-AUTH-03: Schreibzugriff mit MASTERDATA_WRITE funktioniert
1. Als `admin` (mit MASTERDATA_WRITE) → alle CRUD-Operationen möglich
- [ ] **Erwartung:** Alle Operationen erfolgreich
---
## TC-CROSS: Übergreifende / Integrations-Tests
### TC-CROSS-01: Artikel-Lieferant-Verknüpfung konsistent
1. Lieferant `Frisch AG` einem Artikel zuweisen
2. `Frisch AG` deaktivieren
3. Artikel aufrufen → Lieferant weiterhin referenziert (keine Zwangstrennung)
- [ ] **Erwartung:** Artikel zeigt `Frisch AG` trotz INAKTIV-Status
### TC-CROSS-02: Kategorie in Artikelauswahl verfügbar
1. Neue Kategorie `Getränke` anlegen
2. Artikel erstellen → Kategorie-Auswahl enthält `Getränke`
- [ ] **Erwartung:** Neue Kategorien sofort im Artikel-Formular verfügbar
### TC-CROSS-03: Sequenz Kompletter Lieferant-Workflow
1. Lieferant erstellen → bewerten → Zertifikat hinzufügen → deaktivieren → wieder aktivieren → Zertifikat entfernen
- [ ] **Erwartung:** Alle Schritte funktionieren in Folge ohne Datenverlust
### TC-CROSS-04: Sequenz Kompletter Artikel-Workflow
1. Kategorie erstellen → Artikel erstellen (mit Kategorie) → 2. VE hinzufügen → 1. VE entfernen → Artikel deaktivieren → aktivieren
- [ ] **Erwartung:** Konsistenz über alle Schritte
### TC-CROSS-05: Sequenz B2B-Kunde vollständig
1. B2B-Kunde erstellen → 2 Lieferadressen → Präferenzen setzen → 1 Adresse entfernen → Präferenzen ändern → deaktivieren → aktivieren
- [ ] **Erwartung:** Konsistenz über alle Schritte
---
# Manuelle Testfälle Inventory BC
## Kontext
Der Inventory Bounded Context umfasst zwei Aggregate:
- **StorageLocation** Lagerorte mit Typ und optionalem Temperaturbereich
- **Stock** Bestandspositionen mit chargengenauer Führung (Batches)
Alle Schreiboperationen erfordern `INVENTORY_WRITE`-Permission. API-Basis: `/api/inventory/`
---
## TC-SL: Lagerorte (GitHub #1, #2, #3)
### TC-SL-01: Lagerort erstellen Pflichtfelder (Happy Path)
1. `POST /api/inventory/storage-locations`
2. Body: `name: "Kühlraum 1"`, `storageType: "COLD_ROOM"` → ohne Temperaturbereich
- [ ] **Erwartung:** 201 Created; Lagerort hat ID, Status aktiv
### TC-SL-02: Lagerort erstellen mit Temperaturbereich
1. `POST /api/inventory/storage-locations`
2. Body: `name: "Tiefkühllager"`, `storageType: "FREEZER"`, `minTemperature: -25`, `maxTemperature: -18`
- [ ] **Erwartung:** 201 Created; Temperaturbereich korrekt gespeichert
### TC-SL-03: Lagerort erstellen ungültiger Temperaturbereich (min >= max)
1. Body: `name: "Fehler-Lager"`, `storageType: "COLD_ROOM"`, `minTemperature: 10`, `maxTemperature: 5`
- [ ] **Erwartung:** 400 Bad Request; Validierungsfehler
### TC-SL-04: Lagerort erstellen Temperatur außerhalb Grenzen
1. Body: `minTemperature: -60`, `maxTemperature: 90` (Grenzen: -50 bis +80)
- [ ] **Erwartung:** 400 Bad Request
### TC-SL-05: Doppelter Name wird abgelehnt
1. Lagerort `Kühlraum 1` erneut anlegen (Name existiert bereits)
- [ ] **Erwartung:** 409 Conflict; spezifische Fehlermeldung
### TC-SL-06: Leerer Name wird abgelehnt
1. Body: `name: ""`, `storageType: "COLD_ROOM"`
- [ ] **Erwartung:** 400 Bad Request
### TC-SL-07: Lagerort bearbeiten Name und Temperatur ändern
1. `PUT /api/inventory/storage-locations/{id}`
2. Name auf `Kühlraum A` ändern, Temperaturbereich anpassen
- [ ] **Erwartung:** 200 OK; Änderungen gespeichert
- [ ] **Erwartung:** StorageType ist unverändert (immutable)
### TC-SL-08: Lagerort bearbeiten Unique-Check bei Namensänderung
1. Lagerort umbenennen auf einen bereits existierenden Namen
- [ ] **Erwartung:** 409 Conflict
### TC-SL-09: Lagerort deaktivieren (ohne Bestand)
1. `PATCH /api/inventory/storage-locations/{id}/deactivate` (Lagerort ohne Stock)
- [ ] **Erwartung:** 200 OK; Status inaktiv
### TC-SL-10: Lagerort deaktivieren (mit Bestand) schlägt fehl
1. Lagerort hat zugeordneten Stock → Deaktivieren versuchen
- [ ] **Erwartung:** 400/409; Fehlermeldung Bestand existiert
### TC-SL-11: Lagerort aktivieren
1. Deaktivierten Lagerort reaktivieren
- [ ] **Erwartung:** 200 OK; Status aktiv
### TC-SL-12: Doppelte Deaktivierung/Aktivierung
1. Bereits inaktiven Lagerort erneut deaktivieren
- [ ] **Erwartung:** Spezifischer Fehler (bereits inaktiv)
2. Bereits aktiven Lagerort erneut aktivieren
- [ ] **Erwartung:** Spezifischer Fehler (bereits aktiv)
### TC-SL-13: Lagerorte auflisten
1. `GET /api/inventory/storage-locations`
- [ ] **Erwartung:** Liste aller Lagerorte mit id, name, storageType, temperatureRange, active
### TC-SL-14: Lagerorte filtern nach Typ
1. `GET /api/inventory/storage-locations?storageType=COLD_ROOM`
- [ ] **Erwartung:** Nur Kühlraum-Lagerorte
### TC-SL-15: Lagerorte filtern nach Status
1. `GET /api/inventory/storage-locations?active=true`
- [ ] **Erwartung:** Nur aktive Lagerorte
---
## TC-STK: Bestandspositionen (GitHub #4)
### TC-STK-01: Bestandsposition erstellen (Happy Path)
1. `POST /api/inventory/stocks`
2. Body: `articleId: "{existierendeId}"`, `storageLocationId: "{existierendeId}"`
- [ ] **Erwartung:** 201 Created; Stock angelegt
### TC-STK-02: Bestandsposition erstellen mit MinimumLevel und MinimumShelfLife
1. Body: `articleId`, `storageLocationId`, `minimumLevel: 10`, `minimumShelfLife: 5`
- [ ] **Erwartung:** 201 Created; Werte korrekt gespeichert
### TC-STK-03: Doppelte Bestandsposition (gleicher Artikel + Lagerort)
1. Gleiche Kombination `articleId + storageLocationId` erneut anlegen
- [ ] **Erwartung:** 409 Conflict
### TC-STK-04: Bestandsposition MinimumLevel negativ
1. Body: `minimumLevel: -1`
- [ ] **Erwartung:** 400 Bad Request
### TC-STK-05: Bestandsposition MinimumShelfLife 0 oder negativ
1. Body: `minimumShelfLife: 0`
- [ ] **Erwartung:** 400 Bad Request
---
## TC-BATCH: Chargen einbuchen/entnehmen/sperren (GitHub #5, #6, #7)
### TC-BATCH-01: Charge einbuchen (Happy Path)
1. `POST /api/inventory/stocks/{stockId}/batches`
2. Body: `batchId: "CH-001"`, `batchType: "PURCHASED"`, `quantity: 50`, `expiryDate: "2026-06-01"`
- [ ] **Erwartung:** 201 Created; Status AVAILABLE; receivedAt automatisch gesetzt
### TC-BATCH-02: Charge einbuchen Menge 0 oder negativ
1. Body: `quantity: 0`
- [ ] **Erwartung:** 400 Bad Request
2. Body: `quantity: -5`
- [ ] **Erwartung:** 400 Bad Request
### TC-BATCH-03: Doppelte BatchReference im selben Stock
1. Erneut `batchId: "CH-001"`, `batchType: "PURCHASED"` einbuchen
- [ ] **Erwartung:** 400/409; Duplikat abgelehnt
### TC-BATCH-04: Charge teilweise entnehmen
1. `POST /api/inventory/stocks/{stockId}/batches/{batchId}/remove`
2. Body: `quantity: 20` (von 50 verfügbar)
- [ ] **Erwartung:** 200 OK; Restmenge = 30
### TC-BATCH-05: Charge vollständig entnehmen
1. Restmenge (30) komplett entnehmen
- [ ] **Erwartung:** 200 OK; Charge wird entfernt
### TC-BATCH-06: Entnahme übersteigt Bestand
1. Mehr entnehmen als verfügbar (z.B. 100 von 50)
- [ ] **Erwartung:** 400; NegativeStockNotAllowed
### TC-BATCH-07: Charge sperren
1. `POST /api/inventory/stocks/{stockId}/batches/{batchId}/block`
2. Body: `reason: "Qualitätsprüfung ausstehend"`
- [ ] **Erwartung:** 200 OK; Status wechselt auf BLOCKED; Grund dokumentiert
### TC-BATCH-08: Gesperrte Charge kann nicht entnommen werden
1. Entnahme aus BLOCKED-Charge versuchen
- [ ] **Erwartung:** 400; Entnahme verweigert
### TC-BATCH-09: Charge entsperren
1. `POST /api/inventory/stocks/{stockId}/batches/{batchId}/unblock`
- [ ] **Erwartung:** 200 OK; Status zurück auf AVAILABLE (oder EXPIRING_SOON)
### TC-BATCH-10: Doppeltes Sperren/Entsperren
1. Bereits gesperrte Charge erneut sperren
- [ ] **Erwartung:** Spezifischer Fehler
2. Bereits freigegebene Charge erneut entsperren
- [ ] **Erwartung:** Spezifischer Fehler
---
# Manuelle Testfälle Production BC
## Kontext
Der Production Bounded Context umfasst:
- **Recipe** Rezeptverwaltung mit Zutaten, Produktionsschritten, Status-Lifecycle
- **Batch** Chargenplanung und -produktion
- **Quantity** Value Object mit Catch-Weight-Unterstützung
API-Basis: `/api/recipes` und `/api/batches`
---
## TC-REC: Rezepte (GitHub #26, #27, #28, #29, #30, #31)
### TC-REC-01: Rezept erstellen (Happy Path)
1. `POST /api/recipes`
2. Body: `name: "Bratwurst Classic"`, `version: 1`, `recipeType: "FINISHED_PRODUCT"`, `yieldPercentage: 85`, `shelfLifeDays: 14`, `outputQuantity: {amount: 10, uom: "KILOGRAM"}`
- [ ] **Erwartung:** 201 Created; Status = DRAFT
### TC-REC-02: Rezept erstellen YieldPercentage Grenzen
1. Body: `yieldPercentage: 0`
- [ ] **Erwartung:** 400; InvalidYieldPercentage
2. Body: `yieldPercentage: 201`
- [ ] **Erwartung:** 400; InvalidYieldPercentage
### TC-REC-03: Rezept erstellen ShelfLifeDays 0 bei FINISHED_PRODUCT
1. Body: `recipeType: "FINISHED_PRODUCT"`, `shelfLifeDays: 0`
- [ ] **Erwartung:** 400; InvalidShelfLife
### TC-REC-04: Doppelter Name + Version wird abgelehnt
1. Erneut `name: "Bratwurst Classic"`, `version: 1` anlegen
- [ ] **Erwartung:** 409 Conflict
### TC-REC-05: Zutat zu DRAFT-Rezept hinzufügen (Happy Path)
1. `POST /api/recipes/{id}/ingredients`
2. Body: `position: 1`, `articleId: "{id}"`, `quantity: {amount: 5, uom: "KILOGRAM"}`, `substitutable: false`
- [ ] **Erwartung:** 200 OK; Zutat im Rezept
### TC-REC-06: Zweite Zutat hinzufügen
1. Body: `position: 2`, andere ArticleId, Menge
- [ ] **Erwartung:** 200 OK; 2 Zutaten im Rezept
### TC-REC-07: Zutat mit doppelter Position
1. Body: `position: 1` (bereits vergeben)
- [ ] **Erwartung:** 400; DuplicatePosition
### TC-REC-08: Zutat zu ACTIVE-Rezept hinzufügen
1. Rezept aktivieren, dann Zutat hinzufügen versuchen
- [ ] **Erwartung:** 400; NotInDraftStatus
### TC-REC-09: Zutat mit SubRecipeId (Zwischen-Rezept)
1. INTERMEDIATE-Rezept als SubRecipe anlegen
2. Zutat mit `subRecipeId` zum Hauptrezept hinzufügen
- [ ] **Erwartung:** 200 OK; Zutat referenziert Sub-Rezept
### TC-REC-10: Zutat entfernen
1. `DELETE /api/recipes/{id}/ingredients/{ingredientId}`
- [ ] **Erwartung:** 200 OK; Zutat entfernt, Position-Lücke erlaubt
### TC-REC-11: Produktionsschritt hinzufügen (Happy Path)
1. `POST /api/recipes/{id}/steps`
2. Body: `stepNumber: 1`, `description: "Fleisch wolfen"`, `durationMinutes: 15`, `temperatureCelsius: 4`
- [ ] **Erwartung:** 200 OK; Schritt im Rezept
### TC-REC-12: Produktionsschritt leere Beschreibung
1. Body: `stepNumber: 2`, `description: ""`
- [ ] **Erwartung:** 400; Validierungsfehler
### TC-REC-13: Produktionsschritt zu ACTIVE-Rezept
1. Schritt zu aktiviertem Rezept hinzufügen versuchen
- [ ] **Erwartung:** 400; NotInDraftStatus
### TC-REC-14: Produktionsschritt entfernen
1. `DELETE /api/recipes/{id}/steps/{stepNumber}`
- [ ] **Erwartung:** 200 OK; Schritt entfernt, keine automatische Umnummerierung
### TC-REC-15: Rezept aktivieren (Happy Path)
1. Rezept mit mindestens einer Zutat → `POST /api/recipes/{id}/activate`
- [ ] **Erwartung:** 200 OK; Status = ACTIVE
### TC-REC-16: Rezept aktivieren ohne Zutaten
1. Leeres DRAFT-Rezept aktivieren
- [ ] **Erwartung:** 400; NoIngredients
### TC-REC-17: Rezept aktivieren bereits ACTIVE
1. Aktives Rezept erneut aktivieren
- [ ] **Erwartung:** 400; InvalidStatusTransition
### TC-REC-18: Rezept archivieren (Happy Path)
1. `POST /api/recipes/{id}/archive` (Rezept ist ACTIVE)
- [ ] **Erwartung:** 200 OK; Status = ARCHIVED
### TC-REC-19: Rezept archivieren aus DRAFT
1. DRAFT-Rezept archivieren versuchen
- [ ] **Erwartung:** 400; InvalidStatusTransition
### TC-REC-20: Rezept per ID abfragen
1. `GET /api/recipes/{id}`
- [ ] **Erwartung:** 200 OK; vollständiges Rezept mit Zutaten und Schritten
### TC-REC-21: Rezept nicht gefunden
1. `GET /api/recipes/{nicht-existierende-id}`
- [ ] **Erwartung:** 404 Not Found
### TC-REC-22: Rezepte nach Status filtern
1. `GET /api/recipes?status=ACTIVE`
- [ ] **Erwartung:** Nur aktive Rezepte; Liste enthält Zutaten-Count aber nicht volle Zutaten
---
## TC-CYCLE: Zyklus-Erkennung (GitHub #32)
### TC-CYCLE-01: Lineare Verschachtelung erlaubt (A → B → C)
1. Rezept C (ACTIVE, mit Zutat)
2. Rezept B (DRAFT) → Zutat mit `subRecipeId: C`
3. Rezept A (DRAFT) → Zutat mit `subRecipeId: B`
- [ ] **Erwartung:** Alle Zuweisungen erfolgreich; keine Fehler
### TC-CYCLE-02: Direkte Zirkularität (A → B → A)
1. Rezept A enthält Zutat mit `subRecipeId: B`
2. Rezept B: Zutat mit `subRecipeId: A` hinzufügen
- [ ] **Erwartung:** 400; CyclicDependencyDetected mit Pfad-Angabe
### TC-CYCLE-03: Indirekte Zirkularität (A → B → C → A)
1. A → B → C existiert
2. Zutat mit `subRecipeId: A` zu Rezept C hinzufügen
- [ ] **Erwartung:** 400; CyclicDependencyDetected mit Pfad-Angabe
---
## TC-BAT: Chargenplanung (GitHub #33)
### TC-BAT-01: Charge planen (Happy Path)
1. `POST /api/batches`
2. Body: `recipeId: "{id}"`, `plannedQuantity: {amount: 50, uom: "KILOGRAM"}`, `productionDate: "2026-03-01"`, `bestBeforeDate: "2026-03-15"`
- [ ] **Erwartung:** 201 Created; Status = PLANNED; BatchNumber automatisch generiert (Format `P-YYYY-MM-DD-XXX`)
### TC-BAT-02: Charge planen PlannedQuantity 0
1. Body: `plannedQuantity: {amount: 0, uom: "KILOGRAM"}`
- [ ] **Erwartung:** 400; Menge muss positiv sein
### TC-BAT-03: Charge planen BestBeforeDate vor ProductionDate
1. Body: `productionDate: "2026-03-15"`, `bestBeforeDate: "2026-03-01"`
- [ ] **Erwartung:** 400; BestBeforeDate muss nach ProductionDate liegen
### TC-BAT-04: BatchNumber-Format prüfen
1. Mehrere Chargen am selben Tag planen
- [ ] **Erwartung:** BatchNumbers haben Format `P-YYYY-MM-DD-XXX` mit aufsteigender Sequenz
---
## TC-INV-CROSS: Übergreifende Tests Inventory
### TC-INV-CROSS-01: Sequenz Kompletter Lagerort-Workflow
1. Lagerort erstellen → Name ändern → Temperatur anpassen → deaktivieren (ohne Stock) → reaktivieren
- [ ] **Erwartung:** Alle Schritte funktionieren ohne Datenverlust
### TC-INV-CROSS-02: Sequenz Kompletter Chargen-Workflow
1. Lagerort erstellen → Stock anlegen → Charge einbuchen → Teilentnahme → Charge sperren → Charge entsperren → Rest entnehmen
- [ ] **Erwartung:** Konsistenz über alle Schritte; Mengen korrekt
### TC-INV-CROSS-03: Lagerort mit Bestand kann nicht deaktiviert werden
1. Lagerort mit Stock + Charge → Deaktivierung versuchen
- [ ] **Erwartung:** Fehler; Bestand muss erst entfernt werden
---
## TC-PROD-CROSS: Übergreifende Tests Production
### TC-PROD-CROSS-01: Sequenz Rezept-Lifecycle komplett
1. Rezept erstellen (DRAFT) → 2 Zutaten hinzufügen → 2 Schritte hinzufügen → 1 Zutat entfernen → aktivieren → archivieren
- [ ] **Erwartung:** Konsistenz über alle Schritte
### TC-PROD-CROSS-02: Sequenz Rezept + Chargenplanung
1. Rezept erstellen → Zutaten → aktivieren → Charge planen mit diesem Rezept
- [ ] **Erwartung:** Charge referenziert Rezept korrekt
### TC-PROD-CROSS-03: Verschachteltes Rezept vollständig
1. INTERMEDIATE-Rezept erstellen + aktivieren → FINISHED_PRODUCT-Rezept mit SubRecipe-Zutat → aktivieren → Charge planen
- [ ] **Erwartung:** Alle Abhängigkeiten korrekt aufgelöst
---
## Verifikation / Testdurchführung
```bash
# Backend starten
./mvnw spring-boot:run
# TUI starten
cd frontend/apps/cli && pnpm dev
# Backend-Logs beobachten (Fehler sichtbar machen)
# Direkte API-Tests (für TC-B2B-02, TC-AUTH, Inventory, Production)
curl -X POST http://localhost:8080/api/... -H "Authorization: Bearer <token>"
```
**Checkliste nach Test-Durchlauf:**
- [ ] Alle TC-CAT (1-6) durchgeführt
- [ ] Alle TC-SUP (1-12) durchgeführt
- [ ] Alle TC-ART (1-11) durchgeführt
- [ ] Alle TC-CUS (1-11) durchgeführt
- [ ] TC-B2B (1-2) durchgeführt
- [ ] TC-AUTH (1-3) durchgeführt
- [ ] TC-CROSS (1-5) durchgeführt
- [ ] Alle TC-SL (1-15) durchgeführt
- [ ] Alle TC-STK (1-5) durchgeführt
- [ ] Alle TC-BATCH (1-10) durchgeführt
- [ ] Alle TC-REC (1-22) durchgeführt
- [ ] TC-CYCLE (1-3) durchgeführt
- [ ] TC-BAT (1-4) durchgeführt
- [ ] TC-INV-CROSS (1-3) durchgeführt
- [ ] TC-PROD-CROSS (1-3) durchgeführt