mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 12:09:35 +01:00
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
677 lines
27 KiB
Markdown
677 lines
27 KiB
Markdown
# 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
|