1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 10:19:35 +01:00

docs: Sequenzdiagramme

This commit is contained in:
Sebastian Frick 2026-02-24 22:19:10 +01:00
parent 11fb62383b
commit 85f96d685e
2 changed files with 1499 additions and 0 deletions

View file

@ -0,0 +1,615 @@
# Effigenix ERP End-to-End Real-Life Workflows
> Reale Tagesabläufe einer Fleischerei, BC-übergreifend.
> Alle Diagramme nutzen Mermaid-Syntax.
---
## Inhaltsverzeichnis
1. [Morgenroutine: Wareneingang Frischfleisch](#1-morgenroutine-wareneingang-frischfleisch)
2. [Tagesproduktion: Bratwurst herstellen](#2-tagesproduktion-bratwurst-herstellen)
3. [Wochenplanung & Bedarfsermittlung](#3-wochenplanung--bedarfsermittlung)
4. [Rückruf-Szenario: Salmonellen beim Lieferanten](#4-rückruf-szenario-salmonellen-beim-lieferanten)
5. [Qualitätsproblem in der Produktion](#5-qualitätsproblem-in-der-produktion)
6. [MHD-Management: Ablaufende Ware & Abverkauf](#6-mhd-management-ablaufende-ware--abverkauf)
7. [Inventur: Monatliche Bestandszählung](#7-inventur-monatliche-bestandszählung)
8. [Zentrale Produktion & Filialbelieferung](#8-zentrale-produktion--filialbelieferung)
---
## 1. Morgenroutine: Wareneingang Frischfleisch
**Szenario:** Montag, 5:30 Uhr. Der LKW von Lieferant „Müller Fleisch" kommt mit 200 kg Schweineschulter und 80 kg Rindfleisch. Der Wareneingangs-Mitarbeiter muss die Lieferung prüfen, dokumentieren und einbuchen.
**Beteiligte BCs:** Procurement, Quality, Inventory, Master Data
```mermaid
sequenceDiagram
actor Fahrer as LKW-Fahrer (Müller Fleisch)
actor WE as Wareneingangs-Mitarbeiter
participant MD as Master Data BC
participant Proc as Procurement BC<br/>(PurchaseOrder, GoodsReceipt)
participant QM as Quality BC<br/>(GoodsReceiptInspection)
participant Inv as Inventory BC<br/>(Stock, StockMovement)
Note over Fahrer,Inv: 🕠 05:30 LKW-Anlieferung
Fahrer->>WE: Lieferschein übergeben<br/>(200kg Schweineschulter, 80kg Rind,<br/>Charge "MF-2026-0847", MHD 2026-03-03)
WE->>Proc: Wareneingang erfassen
Note right of Proc: GoodsReceipt anlegen<br/>Referenz: PurchaseOrder #PO-2026-041<br/>Lieferanten-Charge: MF-2026-0847<br/>MHD: 2026-03-03
Note over WE,QM: 🌡️ HACCP-Pflichtprüfung
WE->>QM: Qualitätsprüfung starten
Note right of QM: GoodsReceiptInspection erstellt
WE->>QM: 1. Temperaturmessung
Note right of QM: Kerntemperatur Schwein: 2,1°C ✅<br/>Kerntemperatur Rind: 3,4°C ✅<br/>(Grenzwert: ≤ 4°C für Frischfleisch)
WE->>QM: 2. Sichtkontrolle
Note right of QM: Verpackung: unbeschädigt ✅<br/>Farbe: frisch, keine Verfärbung ✅<br/>Geruch: neutral, arttypisch ✅
WE->>QM: 3. MHD-Prüfung
Note right of QM: MHD 2026-03-03 → noch 7 Tage ✅<br/>(Mindest-Restlaufzeit: 3 Tage)
WE->>QM: 4. Dokumentenprüfung
Note right of QM: Lieferschein: vorhanden ✅<br/>Veterinärbescheinigung: vorhanden ✅<br/>Identitätskennzeichen: DE NW 20145 ✅
QM-->>QM: Alle Prüfungen bestanden
QM->>Proc: GoodsReceiptAccepted
Note right of Proc: GoodsReceipt Status → ACCEPTED<br/>PurchaseOrder → FULLY_RECEIVED
Note over WE,Inv: 📦 Bestand einbuchen
Proc->>Inv: Event: GoodsReceiptAccepted
Inv->>Inv: Stock.addBatch(Schweineschulter)
Note right of Inv: StockBatch: MF-2026-0847<br/>BatchType: PURCHASED<br/>200 kg, MHD 2026-03-03<br/>Lagerort: KÜHLRAUM_1
Inv->>Inv: Stock.addBatch(Rindfleisch)
Note right of Inv: StockBatch: MF-2026-0847-R<br/>100 kg, MHD 2026-03-03
Inv->>Inv: StockMovement.record(GOODS_RECEIPT, IN)
Note right of Inv: 2× Bewegungen protokolliert<br/>(immutable Audit-Trail)
Note over WE,Inv: ✅ 05:45 Ware eingelagert, System aktuell
```
### Sonderfall: Prüfung fehlgeschlagen
```mermaid
sequenceDiagram
actor WE as Wareneingangs-Mitarbeiter
actor Fahrer as LKW-Fahrer
participant QM as Quality BC
participant Proc as Procurement BC
Note over WE,Proc: ❌ Temperatur zu hoch
WE->>QM: Temperaturmessung: 7,2°C
Note right of QM: GRENZWERT ÜBERSCHRITTEN!<br/>Max. erlaubt: 4°C<br/>Gemessen: 7,2°C
QM->>QM: TemperatureCriticalLimitExceeded
QM->>Proc: GoodsReceiptRejected
Note right of Proc: Grund: Kühlkette unterbrochen<br/>GoodsReceipt Status → REJECTED<br/>PurchaseOrder bleibt CONFIRMED
WE->>Fahrer: Ware zurückweisen
Note right of WE: Ablehnungsprotokoll erstellt<br/>Fotos dokumentiert<br/>Lieferant wird informiert
Note over WE,Proc: Kein Bestand eingebucht!<br/>Reklamation beim Lieferant
```
---
## 2. Tagesproduktion: Bratwurst herstellen
**Szenario:** Dienstag, 6:00 Uhr. Der Metzgermeister soll laut Wochenplan 150 kg Nürnberger Bratwurst herstellen. Rezept „R-NB-001" (Ausbeute 85%, Haltbarkeit 5 Tage). Zutaten: Schweineschulter, Speck, Gewürzmischung, Naturdärme.
**Beteiligte BCs:** Production, Inventory, (Labeling, Quality)
```mermaid
sequenceDiagram
actor PL as Produktionsleiter
actor MM as Metzgermeister
participant PO as Production BC<br/>(ProductionOrder)
participant Batch as Production BC<br/>(Batch)
participant Recipe as Production BC<br/>(Recipe)
participant Stock as Inventory BC<br/>(Stock)
participant SM as Inventory BC<br/>(StockMovement)
participant Label as Labeling BC<br/>(ProductLabel)
Note over PL,Label: 🕕 06:00 Produktionsauftrag vorbereiten
PL->>PO: Produktionsauftrag anlegen
Note right of PO: Rezept: R-NB-001 (Nürnberger)<br/>Menge: 150 kg<br/>Datum: 2026-02-24<br/>Priorität: NORMAL<br/>Status → PLANNED
PL->>PO: Auftrag freigeben
Note right of PO: Prüfung: Rezept R-NB-001 ist ACTIVE ✅<br/>Status → RELEASED
PL->>PO: Produktion starten
PO->>Batch: Charge automatisch erstellen
Note right of Batch: BatchNumber: P-2026-02-24-001<br/>Geplante Menge: 150 kg<br/>Status → IN_PRODUCTION
PO-->>PO: Status → IN_PRODUCTION<br/>BatchId verknüpft
Note over MM,SM: 🥩 06:15 Material vorbereiten & reservieren
PO->>Recipe: Materialbedarf berechnen
Note right of Recipe: Ausbeute 85% → Bedarf = 150/0,85 = 176 kg<br/>Schweineschulter: 60% → 106 kg<br/>Speck: 25% → 44 kg<br/>Gewürzmischung: 10% → 18 kg<br/>Naturdärme: 5% → 9 kg
MM->>Stock: Material reservieren (FEFO)
Note right of Stock: Schweineschulter: 106 kg reservieren
Stock->>Stock: FEFO-Allokation
Note right of Stock: Charge MF-2026-0847 (MHD 03.03): 106 kg ✅<br/>(älteste Charge zuerst)
Stock-->>MM: Reservierung erstellt
MM->>Stock: Speck reservieren
Note right of Stock: Charge SP-2026-0812 (MHD 05.03): 44 kg ✅
MM->>Stock: Gewürze + Därme reservieren
Note right of Stock: GW-2026-0099 (MHD 15.08): 18 kg ✅<br/>ND-2026-0201 (MHD 20.04): 9 kg ✅
Note over MM,SM: 🔪 06:30 Produktion & Entnahme
MM->>Stock: Reservierungen bestätigen (entnehmen)
Stock->>SM: StockMovement(PRODUCTION_CONSUMPTION, OUT)
Note right of SM: 4× Entnahme-Bewegungen protokolliert
MM->>Batch: Verbrauch dokumentieren
Note right of Batch: Consumption 1: MF-2026-0847, 106 kg Schweineschulter<br/>Consumption 2: SP-2026-0812, 44 kg Speck<br/>Consumption 3: GW-2026-0099, 18 kg Gewürze<br/>Consumption 4: ND-2026-0201, 9 kg Därme
Note right of Batch: → Chargen-Genealogie aufgebaut!<br/>Rückverfolgbar bis zum Lieferanten
Note over MM,Label: ⚖️ 09:30 Produktion abschließen
MM->>Batch: Charge abschließen
Note right of Batch: Ist-Menge: 148 kg (gewogen)<br/>Verschnitt: 3,5 kg<br/>Ausbeute real: 148/177 = 83,6%<br/>MHD berechnet: 2026-02-24 + 5 = 2026-03-01<br/>Status → COMPLETED
Batch->>Stock: Event: BatchCompleted
Stock->>Stock: addBatch(P-2026-02-24-001)
Note right of Stock: Neuer StockBatch:<br/>148 kg Nürnberger Bratwurst<br/>BatchType: PRODUCED<br/>MHD: 2026-03-01<br/>Lagerort: KÜHLRAUM_2
Stock->>SM: StockMovement(PRODUCTION_OUTPUT, IN)
PL->>PO: Produktionsauftrag abschließen
Note right of PO: Prüfung: Charge COMPLETED ✅<br/>Status → COMPLETED
Note over MM,Label: 🏷️ Etikett steht bereit
Note right of Label: ProductLabel für Nürnberger verfügbar:<br/>Zutaten: Schweinefleisch (60%),<br/>Speck (25%), Gewürze...<br/>Allergene: SELLERIE, SENF (auto)<br/>MHD: 01.03.2026<br/>Nährwerte pro 100g berechnet
```
---
## 3. Wochenplanung & Bedarfsermittlung
**Szenario:** Freitag Nachmittag. Der Produktionsleiter plant die Produktion für nächste Woche. Er prüft Bestände, legt Aufträge an und das System ermittelt automatisch den Materialbedarf und löst Bestellungen aus.
**Beteiligte BCs:** Production, Inventory, Procurement, Master Data
```mermaid
sequenceDiagram
actor PL as Produktionsleiter
actor EK as Einkäufer
participant PO as Production BC<br/>(ProductionOrder)
participant Recipe as Production BC<br/>(Recipe)
participant Stock as Inventory BC<br/>(Stock)
participant Demand as Procurement BC<br/>(DemandPlan)
participant Order as Procurement BC<br/>(PurchaseOrder)
participant MD as Master Data BC<br/>(Supplier)
Note over PL,MD: 📋 Freitag 14:00 Wochenplanung
PL->>Stock: Aktuelle Bestände prüfen
Stock-->>PL: Bestandsübersicht:
Note right of Stock: Schweineschulter: 94 kg (MHD 03.03)<br/>Speck: 36 kg (MHD 05.03)<br/>Rindfleisch: 80 kg (MHD 03.03)<br/>Gewürze: 45 kg (MHD 15.08)<br/>⚠️ Schweineschulter unter Mindestbestand (100 kg)!
PL->>PO: Aufträge für nächste Woche anlegen
Note right of PO: Mo: 200 kg Bratwurst (R-NB-001)<br/>Di: 100 kg Leberwurst (R-LW-003)<br/>Mi: 150 kg Wiener (R-WI-002)<br/>Do: 80 kg Leberkäse (R-LK-005)
PL->>PO: Alle Aufträge freigeben
PO->>PO: 4× Status → RELEASED
Note over PL,MD: 📊 Bedarfsermittlung
PO->>Demand: Event: ProductionOrderReleased (4×)
Demand->>Recipe: Materialbedarf aus Rezepten berechnen
Note right of Recipe: Mo Bratwurst: 235 kg Rohstoffe<br/>Di Leberwurst: 125 kg Rohstoffe<br/>Mi Wiener: 180 kg Rohstoffe<br/>Do Leberkäse: 100 kg Rohstoffe
Demand->>Stock: Aktuelle Bestände abfragen
Stock-->>Demand: Verfügbare Mengen
Demand->>Demand: Bedarfsplan generieren
Note right of Demand: Schweineschulter:<br/> Bedarf: 320 kg<br/> Bestand: 94 kg<br/> Fehlmenge: 226 kg<br/> + Sicherheitsbestand: 50 kg<br/> → Bestellvorschlag: 276 kg<br/><br/>Speck:<br/> Bedarf: 95 kg<br/> Bestand: 36 kg<br/> → Bestellvorschlag: 109 kg<br/><br/>Rindfleisch:<br/> Bedarf: 60 kg<br/> Bestand: 80 kg<br/> → kein Bedarf ✅<br/><br/>Gewürze: → kein Bedarf ✅
Note over EK,MD: 🛒 Bestellungen aufgeben
EK->>Demand: Bedarfsplan prüfen & genehmigen
Note right of Demand: Status → APPROVED
EK->>MD: Lieferantenkonditionen prüfen
MD-->>EK: Müller Fleisch: Mindestbestellmenge 50 kg,<br/>Lieferzeit 1 Tag, Preis 5,80€/kg
EK->>Order: Bestellung anlegen
Note right of Order: PurchaseOrder #PO-2026-048<br/>Lieferant: Müller Fleisch<br/>Pos 1: 280 kg Schweineschulter (≥ MBM)<br/>Pos 2: 110 kg Speck<br/>Lieferdatum: Montag 05:00<br/>Gesamtwert: 2.262,00€<br/>Status → ORDERED
EK->>Order: Bestellung an Lieferant senden
Note right of Order: Status → CONFIRMED<br/>(Lieferant hat bestätigt)
Note over PL,MD: ✅ Planung abgeschlossen<br/>Mo-Lieferung und Produktion gesichert
```
---
## 4. Rückruf-Szenario: Salmonellen beim Lieferanten
**Szenario:** Mittwoch, 10:00 Uhr. Das Veterinäramt informiert, dass bei Lieferant „Müller Fleisch" Salmonellen in der Charge „MF-2026-0847" (Schweineschulter, geliefert am Montag) nachgewiesen wurden. Sofortmaßnahmen sind nötig.
**Beteiligte BCs:** Quality, Production (Traceability), Inventory, Filiales
```mermaid
sequenceDiagram
actor Amt as Veterinäramt
actor QM as Qualitätsmanager
actor FL as Filialleiter (3 Filialen)
participant Trace as Production BC<br/>(TraceabilityService)
participant Quality as Quality BC<br/>(QualityHold)
participant Stock as Inventory BC<br/>(Stock)
participant SM as Inventory BC<br/>(StockMovement)
participant Branch as Filiales BC
Note over Amt,Branch: ☎️ 10:00 Veterinäramt meldet Kontamination
Amt->>QM: Salmonellen in Charge MF-2026-0847<br/>(Schweineschulter, Müller Fleisch)
Note over QM,Branch: 🔍 10:05 Vorwärts-Tracing starten
QM->>Trace: traceForward("MF-2026-0847")
Trace->>Trace: Suche: Welche Chargen verwenden MF-2026-0847?
Trace-->>QM: Betroffene Chargen ermittelt:
Note right of Trace: Stufe 1 Direkte Verwendung:<br/> P-2026-02-24-001: 148 kg Nürnberger Bratwurst<br/> P-2026-02-24-003: 95 kg Leberwurst<br/><br/>Stufe 2 Weiterverarbeitung:<br/> (keine Endprodukte)
QM->>SM: Wo ist die Ware jetzt?
SM-->>QM: Bestandsbewegungen:
Note right of SM: P-2026-02-24-001 (Bratwurst 148kg):<br/> → 50 kg Filiale Hauptstraße (verkauft: 12 kg)<br/> → 40 kg Filiale Marktplatz (verkauft: 8 kg)<br/> → 58 kg Zentrale (Lager, unverkauft)<br/><br/>P-2026-02-24-003 (Leberwurst 95kg):<br/> → 95 kg Zentrale (Lager, unverkauft)<br/><br/>Rohstoff-Rest:<br/> → 0 kg MF-2026-0847 (vollständig verbraucht)
Note over QM,Branch: 🚨 10:15 Sofortige Qualitätssperre
QM->>Quality: QualityHold initiieren
Note right of Quality: Grund: CONTAMINATION_SUSPECTED<br/>Referenz: Veterinäramt-Meldung #VA-2026-0412
Quality->>Stock: Event: QualityHoldCreated (Bratwurst)
Stock->>Stock: blockBatch(P-2026-02-24-001)
Note right of Stock: Bratwurst: AVAILABLE → BLOCKED<br/>Kann nicht mehr verkauft/entnommen werden
Quality->>Stock: Event: QualityHoldCreated (Leberwurst)
Stock->>Stock: blockBatch(P-2026-02-24-003)
Note right of Stock: Leberwurst: AVAILABLE → BLOCKED
Note over QM,Branch: 📞 10:20 Filialen informieren
QM->>Branch: Betroffene Filialen ermitteln
Branch-->>QM: Filiale Hauptstraße, Filiale Marktplatz
QM->>FL: SOFORT: Bratwurst Charge P-2026-02-24-001<br/>aus Theke nehmen! Gesperrt wegen<br/>Salmonellen-Verdacht Rohstoff.
FL->>Stock: Ware aus Verkauf entfernt
Note right of Stock: Physisch separiert & gekennzeichnet
Note over QM,Branch: 📋 10:30 Rückruf-Dokumentation
QM->>Quality: Korrekturmaßnahmen dokumentieren
Note right of Quality: 1. Alle betroffenen Chargen gesperrt ✅<br/>2. Filialen informiert ✅<br/>3. Ware aus Verkauf entfernt ✅<br/>4. Veterinäramt Rückmeldung: 20 kg verkauft<br/>5. Kundeninformation vorbereitet
QM->>Quality: Bereits verkaufte Ware:
Note right of Quality: 20 kg verkauft (12+8 kg)<br/>→ Kundenrückruf-Aushang in Filialen<br/>→ Social-Media-Warnung<br/>→ Veterinäramt informiert
Note over QM,Branch: 🧪 Nächste Tage Laborergebnis abwarten
alt Laborergebnis: Keine Salmonellen in Endprodukt
Note over QM,Branch: ✅ Entwarnung
actor Approver as Chef (≠ QM)
QM->>Quality: Entwarnung dokumentieren
Approver->>Quality: QualityHold freigeben (4-Augen)
Note right of Quality: initiatedBy ≠ releasedBy ✅
Quality->>Stock: Event: QualityHoldReleased
Stock->>Stock: unblockBatch(P-2026-02-24-001)
Stock->>Stock: unblockBatch(P-2026-02-24-003)
Note right of Stock: Status: BLOCKED → AVAILABLE<br/>Ware wieder verkaufbar
else Laborergebnis: Salmonellen bestätigt
Note over QM,Branch: ❌ Vernichtung
QM->>Quality: Ablehnung dokumentieren
Quality->>Stock: Event: QualityHoldRejected
Stock->>Stock: removeBatch(P-2026-02-24-001, FULL)
Stock->>Stock: removeBatch(P-2026-02-24-003, FULL)
Stock->>SM: StockMovement(WASTE, OUT)
Note right of SM: 2× Vernichtungs-Buchungen<br/>Grund: Salmonellen-Kontamination<br/>Referenz: VA-2026-0412
end
```
---
## 5. Qualitätsproblem in der Produktion
**Szenario:** Während der Bratwurstproduktion misst der Metzgermeister bei der Kerntemperaturprüfung nach dem Brühen 65°C statt der geforderten mindestens 72°C. Das CCP (Critical Control Point) ist nicht erreicht.
**Beteiligte BCs:** Quality, Production, Inventory
```mermaid
sequenceDiagram
actor MM as Metzgermeister
actor QM as Qualitätsmanager
actor Chef as Betriebsleiter
participant Batch as Production BC<br/>(Batch)
participant QC as Quality BC<br/>(ProcessParameter)
participant QH as Quality BC<br/>(QualityHold)
participant Stock as Inventory BC
Note over MM,Stock: 🌡️ 08:45 CCP-Messung nach Brühvorgang
MM->>QC: Kerntemperatur messen
Note right of QC: CCP: CORE_TEMPERATURE<br/>Charge: P-2026-02-24-002 (Wiener)<br/>Gemessen: 65°C<br/>Akzeptabler Bereich: 7278°C<br/>Status: OUT_OF_SPEC ❌
QC->>QC: DeviationRecorded
Note right of QC: Abweichung: -7°C unter Minimum
Note over MM,Stock: 🚨 Automatische Eskalation
QC->>QH: QualityHold automatisch erstellt
Note right of QH: Grund: CCP_DEVIATION<br/>Charge: P-2026-02-24-002<br/>Details: Kerntemp 65°C < 72°C Minimum
MM->>QM: Abweichung melden
Note over QM,Stock: 🔧 Korrekturmaßnahme
QM->>QH: Korrekturmaßnahme festlegen
Note right of QH: Maßnahme: Erneutes Brühen<br/>auf 75°C Kerntemperatur<br/>Verantwortlich: Metzgermeister<br/>Frist: sofort
MM->>Batch: Erneut brühen (Prozess wiederholen)
MM->>QC: Zweite Kerntemperatur-Messung
Note right of QC: CCP: CORE_TEMPERATURE<br/>Gemessen: 74°C<br/>Akzeptabler Bereich: 7278°C<br/>Status: IN_SPEC ✅
QM->>QH: Korrekturmaßnahme dokumentieren
Note right of QH: Ergebnis: Erfolgreich nachgebrüht<br/>Zweitmessung: 74°C ✅
Chef->>QH: QualityHold freigeben
Note right of QH: 4-Augen: QM initiiert, Chef gibt frei ✅
Note over MM,Stock: ✅ Produktion kann fortgesetzt werden
MM->>Batch: Charge abschließen
Note right of Batch: Alle CCP-Werte dokumentiert<br/>Korrekturmaßnahme nachvollziehbar<br/>Status → COMPLETED
Batch->>Stock: Event: BatchCompleted
Note right of Stock: Ware eingebucht<br/>Audit-Trail lückenlos
```
---
## 6. MHD-Management: Ablaufende Ware & Abverkauf
**Szenario:** Der tägliche System-Job (6:00 Uhr) erkennt, dass 25 kg Leberwurst morgen ablaufen und 40 kg Aufschnitt in 2 Tagen. Das Team muss reagieren.
**Beteiligte BCs:** Inventory, (Sales, Labeling noch nicht implementiert, daher vereinfacht)
```mermaid
sequenceDiagram
participant Cron as Täglicher Scheduler<br/>(06:00 Uhr)
participant Stock as Inventory BC<br/>(Stock)
actor VK as Verkaufsleiter
actor Theke as Theken-Mitarbeiter
participant SM as Inventory BC<br/>(StockMovement)
Note over Cron,SM: ⏰ 06:00 Tägliche MHD-Prüfung
Cron->>Stock: markExpiredBatches(today = 2026-02-24)
Stock->>Stock: Alle Chargen prüfen
Note right of Stock: ❌ EXPIRED (MHD überschritten):<br/> LW-Charge P-2026-02-19-004: 3 kg Weißwurst<br/> (MHD war 2026-02-23 → gestern abgelaufen)<br/><br/>⚠️ EXPIRING_SOON (innerhalb MinimumShelfLife):<br/> LW-Charge P-2026-02-20-002: 25 kg Leberwurst<br/> (MHD 2026-02-25 → morgen, MinShelfLife=2d)<br/><br/> AS-Charge P-2026-02-21-001: 40 kg Aufschnitt<br/> (MHD 2026-02-26 → übermorgen)
Stock-->>Stock: Events publiziert:
Note right of Stock: BatchExpired(Weißwurst, 3kg)<br/>BatchExpiringSoon(Leberwurst, 25kg, 1 Tag)<br/>BatchExpiringSoon(Aufschnitt, 40kg, 2 Tage)
Note over VK,SM: 📊 06:30 Verkaufsleiter prüft Warnungen
VK->>Stock: MHD-Warnungen abrufen
Stock-->>VK: 3 Positionen mit Handlungsbedarf
Note over VK,SM: 🗑️ Abgelaufene Ware entsorgen
VK->>Theke: Weißwurst 3 kg entsorgen
Theke->>Stock: removeBatch(P-2026-02-19-004, 3kg)
Stock->>SM: StockMovement(WASTE, OUT)
Note right of SM: Grund: MHD abgelaufen<br/>3 kg Weißwurst vernichtet
Note over VK,SM: 💰 Bald ablaufende Ware rabattieren
VK->>Theke: Leberwurst 25 kg: 30% Rabatt-Etikett
Note right of Theke: Preisreduzierung im Verkauf<br/>"Heute 30% günstiger"
VK->>Theke: Aufschnitt 40 kg: Aktionsplatzierung
Note right of Theke: Sichtbare Platzierung in Theke<br/>Ggf. morgen rabattieren
Note over VK,SM: 🔄 Bei Verkauf normale Entnahme (FEFO)
Note right of SM: FEFO stellt automatisch sicher,<br/>dass älteste Chargen zuerst<br/>entnommen werden auch<br/>im regulären Verkauf
```
---
## 7. Inventur: Monatliche Bestandszählung
**Szenario:** Letzter Freitag im Monat. Inventur im Kühlraum 1 (Rohstoffe). Der Lagermitarbeiter zählt, der Betriebsleiter genehmigt. Abweichungen werden automatisch ausgeglichen.
**Beteiligte BCs:** Inventory
```mermaid
sequenceDiagram
actor Lager as Lagermitarbeiter (Zähler)
actor Chef as Betriebsleiter (Prüfer)
participant IC as Inventory BC<br/>(InventoryCount)
participant Stock as Inventory BC<br/>(Stock)
participant SM as Inventory BC<br/>(StockMovement)
Note over Lager,SM: 📋 Freitag 16:00 Inventur starten
Lager->>IC: Inventur anlegen für KÜHLRAUM_1
IC->>Stock: Aktuelle Soll-Bestände abrufen
Stock-->>IC: Bestände nach Artikel:
Note right of IC: Soll-Bestände auto-befüllt:<br/>Schweineschulter: 94,0 kg<br/>Rindfleisch: 80,0 kg<br/>Speck: 36,0 kg<br/>Hackfleisch: 22,5 kg<br/>Status → COUNTING
Note over Lager,SM: 📦 16:15 Physische Zählung
Lager->>IC: Schweineschulter: 91,5 kg (Ist)
Note right of IC: Soll: 94,0 kg → Differenz: -2,5 kg
Lager->>IC: Rindfleisch: 80,0 kg (Ist)
Note right of IC: Soll: 80,0 kg → Differenz: 0 kg ✅
Lager->>IC: Speck: 37,2 kg (Ist)
Note right of IC: Soll: 36,0 kg → Differenz: +1,2 kg
Lager->>IC: Hackfleisch: 20,0 kg (Ist)
Note right of IC: Soll: 22,5 kg → Differenz: -2,5 kg
Note over Lager,SM: ✍️ 16:45 Zählung einreichen
Lager->>IC: Zählung einreichen (submitForApproval)
Note right of IC: Status → SUBMITTED<br/>Zähler: Lagermitarbeiter
Note over Chef,SM: 👀 17:00 4-Augen-Prüfung
Chef->>IC: Abweichungen prüfen
Note right of IC: Abweichungen:<br/>Schweineschulter: -2,5 kg (Schwund?)<br/>Speck: +1,2 kg (Wiegefehler?)<br/>Hackfleisch: -2,5 kg (nicht erfasste Entnahme?)
Chef->>IC: Inventur genehmigen
Note right of IC: Prüfung: Chef ≠ Lagermitarbeiter ✅ (4-Augen)<br/>Status → COMPLETED
Note over Chef,SM: ⚖️ Automatische Ausgleichsbuchungen
IC->>SM: Ausgleich Schweineschulter
Note right of SM: StockMovement(ADJUSTMENT, OUT)<br/>-2,5 kg Schweineschulter<br/>Grund: Inventurdifferenz
IC->>SM: Ausgleich Speck
Note right of SM: StockMovement(ADJUSTMENT, IN)<br/>+1,2 kg Speck<br/>Grund: Inventurdifferenz
IC->>SM: Ausgleich Hackfleisch
Note right of SM: StockMovement(ADJUSTMENT, OUT)<br/>-2,5 kg Hackfleisch<br/>Grund: Inventurdifferenz
IC->>Stock: Bestände korrigiert
Note right of Stock: Schweineschulter: 94,0 → 91,5 kg<br/>Rindfleisch: 80,0 kg (unverändert)<br/>Speck: 36,0 → 37,2 kg<br/>Hackfleisch: 22,5 → 20,0 kg
Note over Lager,SM: ✅ 17:10 Inventur abgeschlossen<br/>Alle Bewegungen im Audit-Trail
```
---
## 8. Zentrale Produktion & Filialbelieferung
**Szenario:** Die Zentrale (Hauptfiliale mit Produktion) stellt morgens 300 kg Bratwurst her und verteilt sie an 3 Filialen. Der Transport muss chargengenau dokumentiert werden für die Rückverfolgbarkeit.
**Beteiligte BCs:** Production, Inventory, Filiales
```mermaid
sequenceDiagram
actor PL as Produktionsleiter (Zentrale)
actor FL_A as Filialleiter Hauptstraße
actor FL_B as Filialleiter Marktplatz
actor Fahrer as Fahrer/Auslieferer
participant Batch as Production BC<br/>(Batch)
participant DP as Filiales BC<br/>(DistributionPlan)
participant IBT as Filiales BC<br/>(InterBranchTransfer)
participant Stock_Z as Inventory BC<br/>(Stock Zentrale)
participant Stock_A as Inventory BC<br/>(Stock Hauptstraße)
participant Stock_B as Inventory BC<br/>(Stock Marktplatz)
participant SM as Inventory BC<br/>(StockMovement)
Note over PL,SM: 🏭 06:00-09:00 Zentrale Produktion
PL->>Batch: Charge P-2026-02-24-001 abschließen
Note right of Batch: 300 kg Bratwurst<br/>MHD: 2026-03-01<br/>Status → COMPLETED
Batch->>Stock_Z: Event: BatchCompleted
Stock_Z->>Stock_Z: addBatch(P-2026-02-24-001, 300 kg)
Note right of Stock_Z: 300 kg im Zentrallager
Note over PL,SM: 📦 09:15 Verteilplan erstellen
PL->>DP: Verteilplan anlegen
Note right of DP: Charge: P-2026-02-24-001 (300 kg)<br/>Produzierende Filiale: Zentrale<br/><br/>Verteilung:<br/> → Hauptstraße: 100 kg<br/> → Marktplatz: 80 kg<br/> → Verbleibt Zentrale: 120 kg<br/><br/>Summe: 300 kg ✅
Note over PL,SM: 🚚 09:30 Transfers anlegen
DP->>IBT: Transfer Zentrale → Hauptstraße
Note right of IBT: 100 kg Bratwurst<br/>Charge: P-2026-02-24-001<br/>MHD: 2026-03-01<br/>Status → REQUESTED
DP->>IBT: Transfer Zentrale → Marktplatz
Note right of IBT: 80 kg Bratwurst<br/>Charge: P-2026-02-24-001<br/>MHD: 2026-03-01<br/>Status → REQUESTED
FL_A->>IBT: Transfer genehmigen
Note right of IBT: Status → APPROVED
FL_B->>IBT: Transfer genehmigen
Note over Fahrer,SM: 🚚 10:00 Auslieferung
Fahrer->>IBT: Versand bestätigen (beide)
IBT->>Stock_Z: Event: TransferShipped
Stock_Z->>Stock_Z: removeBatch(P-2026-02-24-001, 100 kg)
Stock_Z->>Stock_Z: removeBatch(P-2026-02-24-001, 80 kg)
Stock_Z->>SM: 2× StockMovement(INTER_BRANCH_TRANSFER, OUT)
Note right of Stock_Z: Zentrale: 300 → 120 kg
Note over Fahrer,SM: 📍 10:30 Ankunft Hauptstraße
FL_A->>IBT: Empfang bestätigen (98 kg erhalten)
Note right of IBT: Geplant: 100 kg<br/>Empfangen: 98 kg<br/>(2 kg Transportverlust)<br/>Status → RECEIVED
IBT->>Stock_A: Event: TransferReceived
Stock_A->>Stock_A: addBatch(P-2026-02-24-001, 98 kg)
Stock_A->>SM: StockMovement(INTER_BRANCH_TRANSFER, IN)
Note right of Stock_A: Hauptstraße: 0 → 98 kg Bratwurst<br/>Gleiche Charge-ID! Rückverfolgbar.
Note over Fahrer,SM: 📍 11:00 Ankunft Marktplatz
FL_B->>IBT: Empfang bestätigen (80 kg erhalten)
Note right of IBT: Geplant: 80 kg, Empfangen: 80 kg ✅
IBT->>Stock_B: Event: TransferReceived
Stock_B->>Stock_B: addBatch(P-2026-02-24-001, 80 kg)
Stock_B->>SM: StockMovement(INTER_BRANCH_TRANSFER, IN)
Note right of Stock_B: Marktplatz: 0 → 80 kg Bratwurst
DP->>DP: Alle Lieferungen abgeschlossen
Note right of DP: Status → COMPLETED
Note over PL,SM: ✅ Ergebnis: 300 kg verteilt
Note right of SM: Zentrale: 120 kg<br/>Hauptstraße: 98 kg (2 kg Schwund)<br/>Marktplatz: 80 kg<br/><br/>Chargen-ID überall identisch!<br/>→ Bei Rückruf: alle 3 Filialen<br/> sofort identifizierbar
```
---
## Workflow-Zusammenfassung
| # | Workflow | Beteiligte BCs | Status |
|---|---------|----------------|--------|
| 1 | Wareneingang Frischfleisch | Procurement, Quality, Inventory | Quality/Procurement: geplant, Inventory: implementiert |
| 2 | Tagesproduktion Bratwurst | Production, Inventory, Labeling | Production: teilw. impl., Inventory: implementiert, Labeling: geplant |
| 3 | Wochenplanung & Bedarfsermittlung | Production, Inventory, Procurement | Procurement: geplant |
| 4 | Rückruf-Szenario | Quality, Production, Inventory, Filiales | Tracing: offen (#43/#44), Quality: geplant |
| 5 | Qualitätsproblem Produktion | Quality, Production | Quality: geplant |
| 6 | MHD-Management | Inventory | Implementiert (#10, #11) |
| 7 | Inventur | Inventory | Offen (#17#20) |
| 8 | Zentrale Produktion & Filialbelieferung | Production, Inventory, Filiales | Filiales: geplant |
### Legende
| Symbol | Bedeutung |
|--------|-----------|
| `actor` | Menschlicher Akteur mit Rolle |
| `participant` | Bounded Context + Aggregate |
| `Note` | Fachliche Details, Validierungen, Berechnungen |
| `alt/else` | Entscheidungspunkt |
| `→ Event:` | Domain Event zwischen BCs |
| ✅ | Prüfung bestanden / Aktion erfolgreich |
| ❌ | Prüfung fehlgeschlagen / Fehler |
| ⚠️ | Warnung / Handlungsbedarf |

View file

@ -0,0 +1,884 @@
# Effigenix ERP Fachliche Sequenzdiagramme
> Generiert aus den GitHub Issues und DDD-Planungsdokumenten.
> Alle Diagramme nutzen Mermaid-Syntax.
---
## Inhaltsverzeichnis
1. [Stammdaten: Lagerorte verwalten](#1-stammdaten-lagerorte-verwalten)
2. [Bestände: Bestandsposition & Chargen verwalten](#2-bestände-bestandsposition--chargen-verwalten)
3. [Chargen einbuchen & entnehmen](#3-chargen-einbuchen--entnehmen)
4. [Chargen sperren/entsperren & MHD-Überwachung](#4-chargen-sperrenentsperren--mhd-überwachung)
5. [Reservierungen (FEFO)](#5-reservierungen-fefo)
6. [Bestandsbewegungen (Stock Movements)](#6-bestandsbewegungen-stock-movements)
7. [Inventur](#7-inventur)
8. [Rezeptverwaltung](#8-rezeptverwaltung)
9. [Chargenplanung & Produktion (Batch Lifecycle)](#9-chargenplanung--produktion-batch-lifecycle)
10. [Produktionsaufträge (Production Orders)](#10-produktionsaufträge-production-orders)
11. [Produktion über Auftrag starten (End-to-End)](#11-produktion-über-auftrag-starten-end-to-end)
12. [Integration: Production → Inventory](#12-integration-production--inventory)
13. [Integration: Procurement → Inventory](#13-integration-procurement--inventory)
14. [Integration: Quality → Inventory](#14-integration-quality--inventory)
15. [Traceability: Vorwärts- & Rückwärts-Tracing](#15-traceability-vorwärts---rückwärts-tracing)
---
## 1. Stammdaten: Lagerorte verwalten
**Issues:** #1 (anlegen), #2 (bearbeiten/deaktivieren), #3 (abfragen)
```mermaid
sequenceDiagram
actor User as Verwaltung
participant Ctrl as StorageLocationController
participant UC as CreateStorageLocation
participant SL as StorageLocation (Aggregate)
participant Repo as StorageLocationRepository
Note over User,Repo: Story 1.1 Lagerort anlegen
User->>Ctrl: POST /api/storage-locations
Ctrl->>UC: execute(CreateStorageLocationCommand)
UC->>Repo: existsByName(name)
Repo-->>UC: false
UC->>SL: StorageLocation.create(draft)
SL-->>UC: Result.ok(storageLocation)
UC->>Repo: save(storageLocation)
UC-->>Ctrl: Result.ok(storageLocationId)
Ctrl-->>User: 201 Created
Note over User,Repo: Story 1.2 Lagerort bearbeiten
User->>Ctrl: PUT /api/storage-locations/{id}
Ctrl->>UC: execute(UpdateStorageLocationCommand)
UC->>Repo: findById(id)
Repo-->>UC: storageLocation
UC->>SL: update(updateDraft)
SL-->>UC: Result.ok()
UC->>Repo: save(storageLocation)
UC-->>Ctrl: Result.ok()
Ctrl-->>User: 200 OK
Note over User,Repo: Story 1.2 Lagerort deaktivieren
User->>Ctrl: POST /api/storage-locations/{id}/deactivate
Ctrl->>UC: execute(DeactivateStorageLocationCommand)
UC->>Repo: findById(id)
UC->>SL: deactivate()
SL-->>UC: Result.ok()
UC->>Repo: save(storageLocation)
Ctrl-->>User: 200 OK
Note over User,Repo: Story 1.3 Lagerorte abfragen
User->>Ctrl: GET /api/storage-locations?type=COLD_ROOM&active=true
Ctrl->>UC: execute(ListStorageLocationsQuery)
UC->>Repo: findAll(filter)
Repo-->>UC: List<StorageLocation>
UC-->>Ctrl: List<StorageLocationResponse>
Ctrl-->>User: 200 OK [...]
```
---
## 2. Bestände: Bestandsposition & Chargen verwalten
**Issues:** #4 (anlegen), #8 (abfragen), #9 (Parameter ändern)
```mermaid
sequenceDiagram
actor User as Produktion/Lager
participant Ctrl as StockController
participant UC as CreateStock / UpdateStockParams
participant Stock as Stock (Aggregate)
participant Repo as StockRepository
Note over User,Repo: Story 2.1 Bestandsposition anlegen
User->>Ctrl: POST /api/stocks
Ctrl->>UC: execute(CreateStockCommand)
UC->>Repo: existsByArticleAndLocation(articleId, locationId)
Repo-->>UC: false
UC->>Stock: Stock.create(articleId, locationId)
Stock-->>UC: Result.ok(stock)
UC->>Repo: save(stock)
Ctrl-->>User: 201 Created {stockId}
Note over User,Repo: Story 2.6 Bestandsparameter ändern
User->>Ctrl: PATCH /api/stocks/{id}/parameters
Ctrl->>UC: execute(UpdateStockParamsCommand)
UC->>Repo: findById(stockId)
Repo-->>UC: stock
UC->>Stock: updateMinimumLevel(newLevel)
Stock-->>UC: Result.ok()
UC->>Stock: updateMinimumShelfLife(newDays)
Stock-->>UC: Result.ok()
UC->>Repo: save(stock)
Ctrl-->>User: 200 OK
Note over User,Repo: Story 2.5 Bestandsposition abfragen
User->>Ctrl: GET /api/stocks/{id}
Ctrl->>UC: execute(GetStockQuery)
UC->>Repo: findById(stockId)
Repo-->>UC: stock (inkl. Batches & Reservierungen)
UC-->>Ctrl: StockResponse
Ctrl-->>User: 200 OK {totalQuantity, batches[], reservations[]}
```
---
## 3. Chargen einbuchen & entnehmen
**Issues:** #5 (einbuchen), #6 (entnehmen)
```mermaid
sequenceDiagram
actor User as Lager/Produktion
participant Ctrl as StockController
participant UC as AddBatch / RemoveBatch
participant Stock as Stock (Aggregate)
participant Repo as StockRepository
Note over User,Repo: Story 2.2 Charge einbuchen (addBatch)
User->>Ctrl: POST /api/stocks/{id}/batches
Note right of User: {batchRef, quantity, expiryDate, batchType}
Ctrl->>UC: execute(AddBatchCommand)
UC->>Repo: findById(stockId)
Repo-->>UC: stock
UC->>Stock: addBatch(batchReference, quantity, expiryDate)
Note right of Stock: Validierung: qty > 0, expiryDate in Zukunft
Stock-->>UC: Result.ok()
Note right of Stock: totalQuantity += quantity
UC->>Repo: save(stock)
Ctrl-->>User: 201 Created
Note over User,Repo: Story 2.3 Charge entnehmen (removeBatch)
User->>Ctrl: POST /api/stocks/{id}/batches/{batchId}/remove
Note right of User: {quantity}
Ctrl->>UC: execute(RemoveBatchCommand)
UC->>Repo: findById(stockId)
Repo-->>UC: stock
UC->>Stock: removeBatch(batchId, quantity)
Note right of Stock: Prüfung: Batch vorhanden, qty ≤ verfügbar
alt Menge reicht
Stock-->>UC: Result.ok()
UC->>Repo: save(stock)
Ctrl-->>User: 200 OK
else Nicht genug Bestand
Stock-->>UC: Result.error(InsufficientStock)
Ctrl-->>User: 409 Conflict
end
```
---
## 4. Chargen sperren/entsperren & MHD-Überwachung
**Issues:** #7 (sperren/entsperren), #10 (MHD-Markierung), #11 (Mindestbestand)
```mermaid
sequenceDiagram
actor User as QM/Lager
participant Ctrl as StockController
participant UC as BlockBatch / UnblockBatch
participant Stock as Stock (Aggregate)
participant Repo as StockRepository
Note over User,Repo: Story 2.4 Charge sperren
User->>Ctrl: POST /api/stocks/{id}/batches/{batchId}/block
Note right of User: {reason}
Ctrl->>UC: execute(BlockBatchCommand)
UC->>Repo: findById(stockId)
UC->>Stock: blockBatch(batchId, reason)
Note right of Stock: Status: AVAILABLE → BLOCKED
Stock-->>UC: Result.ok()
UC->>Repo: save(stock)
Ctrl-->>User: 200 OK
User->>Ctrl: POST /api/stocks/{id}/batches/{batchId}/unblock
Ctrl->>UC: execute(UnblockBatchCommand)
UC->>Repo: findById(stockId)
UC->>Stock: unblockBatch(batchId)
Note right of Stock: Status: BLOCKED → AVAILABLE
Stock-->>UC: Result.ok()
UC->>Repo: save(stock)
Ctrl-->>User: 200 OK
```
```mermaid
sequenceDiagram
participant Scheduler as Täglicher Scheduler
participant UC as MarkExpiredBatches
participant Repo as StockRepository
participant Stock as Stock (Aggregate)
Note over Scheduler,Stock: Story 3.1 Abgelaufene & bald ablaufende Chargen
Scheduler->>UC: execute()
UC->>Repo: findAllWithBatches()
Repo-->>UC: List<Stock>
loop Für jeden Stock
UC->>Stock: markExpiredBatches(today)
Note right of Stock: expiryDate < today EXPIRED
Note right of Stock: expiryDate - today < minimumShelfLife EXPIRING_SOON
Stock-->>UC: List<DomainEvent>
Note right of UC: Events: BatchExpired, BatchExpiringSoon
end
UC->>Repo: saveAll(stocks)
Note over Scheduler,Stock: Story 3.2 Bestände unter Mindestbestand
Scheduler->>UC: execute()
UC->>Repo: findAllBelowMinimumLevel()
Repo-->>UC: List<Stock>
loop Für jeden Stock unter Minimum
Note right of UC: Event: StockLevelBelowMinimum
end
```
---
## 5. Reservierungen (FEFO)
**Issues:** #12 (reservieren), #13 (freigeben), #14 (bestätigen/entnehmen)
```mermaid
sequenceDiagram
actor User as Produktion/Verkauf
participant Ctrl as StockController
participant UC as ReserveStock
participant Stock as Stock (Aggregate)
participant FEFO as FEFO-Algorithmus
participant Repo as StockRepository
Note over User,Repo: Story 4.1 Bestand reservieren (FEFO)
User->>Ctrl: POST /api/stocks/{id}/reservations
Note right of User: {quantity, referenceType, referenceId}
Ctrl->>UC: execute(ReserveStockCommand)
UC->>Repo: findById(stockId)
Repo-->>UC: stock
UC->>Stock: reserve(quantity, refType, refId)
Stock->>FEFO: allocate(availableBatches, quantity)
Note right of FEFO: Sortiere nach expiryDate ASC
Note right of FEFO: Allokiere von ältester Charge zuerst
FEFO-->>Stock: List<Allocation> [(batch1, qty1), (batch2, qty2)]
Stock-->>UC: Result.ok(reservationId)
UC->>Repo: save(stock)
Ctrl-->>User: 201 Created {reservationId}
Note over User,Repo: Story 4.2 Reservierung freigeben
User->>Ctrl: DELETE /api/stocks/{id}/reservations/{resId}
Ctrl->>UC: execute(ReleaseReservationCommand)
UC->>Repo: findById(stockId)
UC->>Stock: releaseReservation(reservationId)
Note right of Stock: Allokierte Mengen → wieder AVAILABLE
Stock-->>UC: Result.ok()
UC->>Repo: save(stock)
Ctrl-->>User: 200 OK
Note over User,Repo: Story 4.3 Reservierung bestätigen (entnehmen)
User->>Ctrl: POST /api/stocks/{id}/reservations/{resId}/confirm
Ctrl->>UC: execute(ConfirmReservationCommand)
UC->>Repo: findById(stockId)
UC->>Stock: confirmReservation(reservationId)
Note right of Stock: Reservierte Batches physisch entnommen
Note right of Stock: totalQuantity -= reservedQuantity
Note right of Stock: Reservierung entfernt
Stock-->>UC: Result.ok(removedBatches)
UC->>Repo: save(stock)
Note right of UC: → StockMovement wird erstellt (Story 5.1)
Ctrl-->>User: 200 OK
```
---
## 6. Bestandsbewegungen (Stock Movements)
**Issues:** #15 (erfassen), #16 (abfragen)
```mermaid
sequenceDiagram
actor User as Lager/System
participant Ctrl as StockMovementController
participant UC as RecordStockMovement
participant SM as StockMovement (Aggregate)
participant Repo as StockMovementRepository
participant StockRepo as StockRepository
Note over User,Repo: Story 5.1 Bestandsbewegung erfassen
User->>Ctrl: POST /api/stock-movements
Note right of User: {articleId, batchId, movementType,<br/>direction, quantity, fromLocation,<br/>toLocation, reason, referenceDoc}
Ctrl->>UC: execute(RecordStockMovementCommand)
UC->>SM: StockMovement.create(draft)
Note right of SM: Validierung:<br/>- GOODS_RECEIPT: fromLocation = null<br/>- WASTE: reason pflicht<br/>- TRANSFER: from ≠ to
SM-->>UC: Result.ok(stockMovement)
UC->>Repo: save(stockMovement)
Note right of UC: Immutabel keine Änderung möglich
Ctrl-->>User: 201 Created {movementId}
Note over User,Repo: Story 5.2 Bestandsbewegungen abfragen
User->>Ctrl: GET /api/stock-movements?articleId=...&from=...&to=...
Ctrl->>UC: execute(ListStockMovementsQuery)
UC->>Repo: findByFilters(articleId, period, type, batchRef)
Repo-->>UC: List<StockMovement>
Ctrl-->>User: 200 OK [{movementId, type, direction, qty, date, ...}]
```
---
## 7. Inventur
**Issues:** #17 (anlegen), #18 (durchführen), #19 (abschließen), #20 (abbrechen/abfragen)
```mermaid
sequenceDiagram
actor Counter as Zähler
actor Approver as Prüfer (≠ Zähler)
participant Ctrl as InventoryCountController
participant UC as InventoryCount Use Cases
participant IC as InventoryCount (Aggregate)
participant StockRepo as StockRepository
participant SMRepo as StockMovementRepository
participant Repo as InventoryCountRepository
Note over Counter,Repo: Story 6.1 Inventur anlegen
Counter->>Ctrl: POST /api/inventory-counts
Note right of Counter: {storageLocationId, countDate}
Ctrl->>UC: execute(CreateInventoryCountCommand)
UC->>StockRepo: findByLocation(locationId)
StockRepo-->>UC: List<Stock> (aktuelle Bestände)
UC->>IC: InventoryCount.create(location, date, expectedItems)
Note right of IC: CountItems[] auto-befüllt:<br/>Artikel + Soll-Menge aus Bestand
IC-->>UC: Result.ok(inventoryCount)
UC->>Repo: save(inventoryCount)
Ctrl-->>Counter: 201 Created {countId, items[]}
Note over Counter,Repo: Story 6.2 Ist-Mengen erfassen
Counter->>Ctrl: PATCH /api/inventory-counts/{id}/items/{itemId}
Note right of Counter: {actualQuantity}
Ctrl->>UC: execute(RecordCountItemCommand)
UC->>Repo: findById(countId)
UC->>IC: recordActualQuantity(itemId, actualQty)
Note right of IC: variance = actualQty - expectedQty
IC-->>UC: Result.ok()
UC->>Repo: save(inventoryCount)
Ctrl-->>Counter: 200 OK
Note over Counter,Repo: Story 6.3 Inventur abschließen (4-Augen)
Counter->>Ctrl: POST /api/inventory-counts/{id}/submit
Ctrl->>UC: submitForApproval(countId, countedBy)
UC->>IC: submit(countedBy)
IC-->>UC: Result.ok()
Approver->>Ctrl: POST /api/inventory-counts/{id}/approve
Note right of Approver: approvedBy ≠ countedBy (4-Augen-Prinzip)
Ctrl->>UC: execute(ApproveInventoryCountCommand)
UC->>Repo: findById(countId)
UC->>IC: approve(approvedBy)
Note right of IC: Prüfung: approvedBy ≠ countedBy
IC-->>UC: Result.ok(variances[])
loop Für jede Abweichung
UC->>SMRepo: save(StockMovement.ADJUSTMENT)
Note right of SMRepo: Automatische Ausgleichsbuchung<br/>IN bei Mehr, OUT bei Weniger
end
UC->>Repo: save(inventoryCount)
Ctrl-->>Approver: 200 OK {variances[], adjustments[]}
Note over Counter,Repo: Story 6.4 Inventur abbrechen
Counter->>Ctrl: POST /api/inventory-counts/{id}/cancel
Ctrl->>UC: execute(CancelInventoryCountCommand)
UC->>IC: cancel()
Note right of IC: Nur möglich wenn noch nicht abgeschlossen
IC-->>UC: Result.ok()
UC->>Repo: save(inventoryCount)
Ctrl-->>Counter: 200 OK
```
---
## 8. Rezeptverwaltung
**Issues:** #26 (anlegen), #27 (Zutaten), #28 (Schritte), #29 (aktivieren), #30 (archivieren), #31 (abfragen), #32 (Zykluserkennung)
```mermaid
sequenceDiagram
actor User as Metzgermeister
participant Ctrl as RecipeController
participant UC as Recipe Use Cases
participant Recipe as Recipe (Aggregate)
participant CycleCheck as CycleDependencyChecker
participant Repo as RecipeRepository
Note over User,Repo: US-P02 Rezept anlegen (DRAFT)
User->>Ctrl: POST /api/recipes
Note right of User: {articleId, name, recipeType,<br/>yieldPercentage, shelfLifeDays}
Ctrl->>UC: execute(CreateRecipeCommand)
UC->>Recipe: Recipe.create(draft)
Note right of Recipe: Status = DRAFT
Recipe-->>UC: Result.ok(recipe)
UC->>Repo: save(recipe)
Ctrl-->>User: 201 Created {recipeId}
Note over User,Repo: US-P03 Zutaten verwalten
User->>Ctrl: POST /api/recipes/{id}/ingredients
Note right of User: {articleId, quantity, unit, subRecipeId?}
Ctrl->>UC: execute(AddIngredientCommand)
UC->>Repo: findById(recipeId)
UC->>Recipe: addIngredient(ingredientDraft)
Note right of Recipe: Nur im DRAFT-Status erlaubt
alt SubRecipeId angegeben
UC->>CycleCheck: checkForCycles(recipeId, subRecipeId)
Note right of CycleCheck: DFS-Traversierung aller<br/>verschachtelten Rezepte
CycleCheck-->>UC: noCycle / cycleDetected
end
Recipe-->>UC: Result.ok()
UC->>Repo: save(recipe)
Ctrl-->>User: 201 Created
Note over User,Repo: US-P04 Produktionsschritte verwalten
User->>Ctrl: POST /api/recipes/{id}/steps
Note right of User: {stepNumber, description,<br/>durationMinutes, temperatureCelsius}
Ctrl->>UC: execute(AddProductionStepCommand)
UC->>Recipe: addProductionStep(stepDraft)
Recipe-->>UC: Result.ok()
UC->>Repo: save(recipe)
Ctrl-->>User: 201 Created
Note over User,Repo: US-P05 Rezept aktivieren
User->>Ctrl: POST /api/recipes/{id}/activate
Ctrl->>UC: execute(ActivateRecipeCommand)
UC->>Repo: findById(recipeId)
UC->>Recipe: activate()
Note right of Recipe: Prüfung: mind. 1 Zutat vorhanden<br/>Status: DRAFT → ACTIVE
Recipe-->>UC: Result.ok()
UC->>Repo: save(recipe)
Ctrl-->>User: 200 OK
Note over User,Repo: US-P06 Rezept archivieren
User->>Ctrl: POST /api/recipes/{id}/archive
Ctrl->>UC: execute(ArchiveRecipeCommand)
UC->>Recipe: archive()
Note right of Recipe: Status: ACTIVE → ARCHIVED
Recipe-->>UC: Result.ok()
UC->>Repo: save(recipe)
Ctrl-->>User: 200 OK
```
---
## 9. Chargenplanung & Produktion (Batch Lifecycle)
**Issues:** #33 (planen), #34 (abfragen), #35 (starten + Verbrauch), #36 (abschließen), #37 (stornieren)
```mermaid
sequenceDiagram
actor User as Metzgermeister
participant Ctrl as BatchController
participant UC as Batch Use Cases
participant Batch as Batch (Aggregate)
participant RecipeRepo as RecipeRepository
participant Repo as BatchRepository
Note over User,Repo: US-P09 Charge planen
User->>Ctrl: POST /api/batches
Note right of User: {recipeId, plannedQuantity, productionDate}
Ctrl->>UC: execute(PlanBatchCommand)
UC->>RecipeRepo: findById(recipeId)
RecipeRepo-->>UC: recipe (muss ACTIVE sein)
UC->>Batch: Batch.plan(draft)
Note right of Batch: BatchNumber generiert: P-2026-02-24-001<br/>BestBeforeDate = productionDate + shelfLifeDays<br/>Status = PLANNED
Batch-->>UC: Result.ok(batch)
UC->>Repo: save(batch)
Ctrl-->>User: 201 Created {batchId, batchNumber}
Note over User,Repo: US-P10 Produktion starten
User->>Ctrl: POST /api/batches/{id}/start
Ctrl->>UC: execute(StartBatchCommand)
UC->>Repo: findById(batchId)
UC->>Batch: startProduction()
Note right of Batch: Status: PLANNED → IN_PRODUCTION
Batch-->>UC: Result.ok()
UC->>Repo: save(batch)
Ctrl-->>User: 200 OK
Note over User,Repo: US-P10 Rohstoffverbrauch dokumentieren
User->>Ctrl: POST /api/batches/{id}/consumptions
Note right of User: {inputBatchId, quantity, unit}
Ctrl->>UC: execute(RecordConsumptionCommand)
UC->>Repo: findById(batchId)
UC->>Batch: recordConsumption(consumptionDraft)
Note right of Batch: Nur im Status IN_PRODUCTION<br/>inputBatchId muss eindeutig sein<br/>→ Chargen-Genealogie wird aufgebaut
Batch-->>UC: Result.ok()
UC->>Repo: save(batch)
Ctrl-->>User: 201 Created
Note over User,Repo: US-P11 Charge abschließen
User->>Ctrl: POST /api/batches/{id}/complete
Note right of User: {actualQuantity, waste, remarks}
Ctrl->>UC: execute(CompleteBatchCommand)
UC->>Repo: findById(batchId)
UC->>Batch: complete(actualQty, waste, remarks)
Note right of Batch: Prüfung: mind. 1 Consumption<br/>actualQuantity > 0<br/>Status: IN_PRODUCTION → COMPLETED
Batch-->>UC: Result.ok()
Note right of Batch: Event: BatchCompleted
UC->>Repo: save(batch)
Ctrl-->>User: 200 OK
Note over User,Repo: US-P12 Charge stornieren
User->>Ctrl: POST /api/batches/{id}/cancel
Note right of User: {reason} (Pflichtfeld)
Ctrl->>UC: execute(CancelBatchCommand)
UC->>Batch: cancel(reason)
Note right of Batch: Status: PLANNED/IN_PRODUCTION → CANCELLED
Batch-->>UC: Result.ok()
UC->>Repo: save(batch)
Ctrl-->>User: 200 OK
```
---
## 10. Produktionsaufträge (Production Orders)
**Issues:** #38 (anlegen), #39 (freigeben), #40 (Produktion starten), #41 (abschließen/stornieren), #42 (umterminieren/abfragen)
```mermaid
sequenceDiagram
actor User as Produktionsleiter
participant Ctrl as ProductionOrderController
participant UC as ProductionOrder Use Cases
participant PO as ProductionOrder (Aggregate)
participant RecipeRepo as RecipeRepository
participant BatchUC as PlanBatch Use Case
participant Repo as ProductionOrderRepository
Note over User,Repo: US-P13 Produktionsauftrag anlegen
User->>Ctrl: POST /api/production-orders
Note right of User: {recipeId, plannedQuantity,<br/>plannedDate, priority}
Ctrl->>UC: execute(CreateProductionOrderCommand)
UC->>PO: ProductionOrder.create(draft)
Note right of PO: Status = PLANNED<br/>Priority = NORMAL/HIGH/URGENT
PO-->>UC: Result.ok(productionOrder)
UC->>Repo: save(productionOrder)
Ctrl-->>User: 201 Created {orderId}
Note over User,Repo: US-P14 Produktionsauftrag freigeben
User->>Ctrl: POST /api/production-orders/{id}/release
Ctrl->>UC: execute(ReleaseProductionOrderCommand)
UC->>Repo: findById(orderId)
UC->>RecipeRepo: findById(recipeId)
RecipeRepo-->>UC: recipe
UC->>PO: release(recipe)
Note right of PO: Prüfung: Rezept muss ACTIVE sein<br/>Status: PLANNED → RELEASED
PO-->>UC: Result.ok()
UC->>Repo: save(productionOrder)
Ctrl-->>User: 200 OK
Note over User,Repo: US-P15 Produktion über Auftrag starten
User->>Ctrl: POST /api/production-orders/{id}/start-production
Ctrl->>UC: execute(StartProductionFromOrderCommand)
UC->>Repo: findById(orderId)
UC->>BatchUC: planBatch(recipeId, plannedQty, date)
Note right of BatchUC: Neue Charge wird automatisch erstellt
BatchUC-->>UC: batchId
UC->>PO: startProduction(batchId)
Note right of PO: Status: RELEASED → IN_PRODUCTION<br/>BatchId wird verknüpft
PO-->>UC: Result.ok()
UC->>Repo: save(productionOrder)
Ctrl-->>User: 200 OK {batchId}
Note over User,Repo: US-P16 Auftrag abschließen
User->>Ctrl: POST /api/production-orders/{id}/complete
Ctrl->>UC: execute(CompleteProductionOrderCommand)
UC->>Repo: findById(orderId)
UC->>PO: complete()
Note right of PO: Prüfung: verknüpfte Charge<br/>muss COMPLETED sein<br/>Status: IN_PRODUCTION → COMPLETED
PO-->>UC: Result.ok()
UC->>Repo: save(productionOrder)
Ctrl-->>User: 200 OK
Note over User,Repo: US-P17 Auftrag umterminieren
User->>Ctrl: PATCH /api/production-orders/{id}/reschedule
Note right of User: {newPlannedDate}
Ctrl->>UC: execute(RescheduleProductionOrderCommand)
UC->>PO: reschedule(newDate)
Note right of PO: Nur in PLANNED/RELEASED erlaubt
PO-->>UC: Result.ok()
UC->>Repo: save(productionOrder)
Ctrl-->>User: 200 OK
```
---
## 11. Produktion über Auftrag starten (End-to-End)
**Issues:** #38#42 (Production Orders), #33#37 (Batches), #12#14 (Reservierungen)
```mermaid
sequenceDiagram
actor PL as Produktionsleiter
actor MM as Metzgermeister
participant PO as ProductionOrder
participant Batch as Batch
participant Stock as Stock (Inventory)
participant SM as StockMovement
Note over PL,SM: End-to-End: Vom Auftrag zur fertigen Charge
PL->>PO: 1. Auftrag anlegen (recipeId, qty, date)
Note right of PO: Status: PLANNED
PL->>PO: 2. Auftrag freigeben
Note right of PO: Prüft: Rezept ACTIVE<br/>Status: PLANNED → RELEASED
PL->>PO: 3. Produktion starten
PO->>Batch: Charge wird automatisch erstellt
Note right of Batch: BatchNumber: P-2026-02-24-001<br/>Status: PLANNED → IN_PRODUCTION
Note right of PO: Status: RELEASED → IN_PRODUCTION
MM->>Stock: 4. Material reservieren (FEFO)
Note right of Stock: Älteste Chargen zuerst allokiert
MM->>Stock: 5. Reservierung bestätigen (entnehmen)
Stock->>SM: StockMovement PRODUCTION_CONSUMPTION (OUT)
Note right of Stock: totalQuantity sinkt
MM->>Batch: 6. Verbrauch dokumentieren
Note right of Batch: Consumption(inputBatchId, qty)<br/>→ Chargen-Genealogie
MM->>Batch: 7. Charge abschließen
Note right of Batch: actualQty, waste, remarks<br/>Status: IN_PRODUCTION → COMPLETED
Batch->>Stock: 8. Fertiges Produkt einbuchen
Stock->>SM: StockMovement PRODUCTION_OUTPUT (IN)
Note right of Stock: Neuer StockBatch mit MHD
PL->>PO: 9. Auftrag abschließen
Note right of PO: Prüft: Charge COMPLETED<br/>Status: IN_PRODUCTION → COMPLETED
```
---
## 12. Integration: Production → Inventory
**Issues:** #22 (Produktionsergebnis einbuchen), #23 (Produktionsverbrauch verbuchen)
```mermaid
sequenceDiagram
participant Batch as Batch (Production BC)
participant EventBus as Domain Event Bus
participant Handler as Integration Event Handler
participant Stock as Stock (Inventory BC)
participant SM as StockMovement
participant StockRepo as StockRepository
Note over Batch,StockRepo: Story 7.2 BatchCompleted → Bestand einbuchen
Batch->>EventBus: BatchCompleted {batchId, articleId,<br/>actualQuantity, bestBeforeDate}
EventBus->>Handler: handle(BatchCompleted)
Handler->>StockRepo: findByArticleAndLocation(articleId, productionLocation)
StockRepo-->>Handler: stock
Handler->>Stock: addBatch(batchRef=PRODUCED, qty, expiryDate)
Stock-->>Handler: Result.ok()
Handler->>SM: StockMovement.create(PRODUCTION_OUTPUT, IN)
Handler->>StockRepo: save(stock)
Note right of Handler: Fertiges Produkt ist jetzt im Bestand
Note over Batch,StockRepo: Story 7.3 ConsumptionRecorded → Bestand entnehmen
Batch->>EventBus: ConsumptionRecorded {batchId,<br/>inputBatchId, quantity}
EventBus->>Handler: handle(ConsumptionRecorded)
Handler->>StockRepo: findByBatchReference(inputBatchId)
StockRepo-->>Handler: stock
Handler->>Stock: removeBatch(inputBatchId, quantity)
Stock-->>Handler: Result.ok()
Handler->>SM: StockMovement.create(PRODUCTION_CONSUMPTION, OUT)
Handler->>StockRepo: save(stock)
Note right of Handler: Rohstoff-Bestand reduziert
```
---
## 13. Integration: Procurement → Inventory
**Issue:** #21 (Wareneingang verarbeiten)
```mermaid
sequenceDiagram
actor Lager as Wareneingang
participant GR as GoodsReceipt (Procurement BC)
participant QI as GoodsReceiptInspection (Quality BC)
participant EventBus as Domain Event Bus
participant Handler as Integration Event Handler
participant Stock as Stock (Inventory BC)
participant SM as StockMovement
Note over Lager,SM: Story 7.1 Wareneingang → Bestand einbuchen
Lager->>GR: Wareneingang erfassen
Note right of GR: supplierBatchNumber, qty, expiryDate
GR->>QI: Qualitätsprüfung anstoßen
Note right of QI: Temperatur ≤ 4°C (Fleisch)<br/>Verpackung OK<br/>MHD ausreichend<br/>Dokumente vollständig
alt Prüfung bestanden
QI-->>GR: ACCEPTED
GR->>EventBus: GoodsReceiptAccepted {articleId,<br/>supplierBatchNumber, qty, expiryDate}
EventBus->>Handler: handle(GoodsReceiptAccepted)
Handler->>Stock: addBatch(batchRef=PURCHASED,<br/>supplierBatchNumber, qty, expiryDate)
Handler->>SM: StockMovement.create(GOODS_RECEIPT, IN)
Note right of Stock: Ware im Bestand verfügbar
else Prüfung fehlgeschlagen
QI-->>GR: REJECTED
GR->>EventBus: GoodsReceiptRejected {reason}
Note right of GR: Ware wird zurückgewiesen<br/>Kein Bestand eingebucht
end
```
---
## 14. Integration: Quality → Inventory
**Issue:** #24 (Qualitätssperre/Freigabe)
```mermaid
sequenceDiagram
actor QM as Qualitätsmanager
actor Approver as Freigeber (≠ QM)
participant QH as QualityHold (Quality BC)
participant EventBus as Domain Event Bus
participant Handler as Integration Event Handler
participant Stock as Stock (Inventory BC)
Note over QM,Stock: Story 7.4 Qualitätssperre → Charge sperren
QM->>QH: Qualitätssperre initiieren
Note right of QH: Grund: TEMPERATURE_DEVIATION /<br/>SAMPLE_FAILED / CONTAMINATION
QH->>EventBus: QualityHoldInitiated {batchId, reason}
EventBus->>Handler: handle(QualityHoldInitiated)
Handler->>Stock: blockBatch(batchId, reason)
Note right of Stock: StockBatch Status: AVAILABLE → BLOCKED<br/>Kann nicht entnommen werden
Note over QM,Stock: Korrekturmaßnahmen dokumentieren
QM->>QH: addCorrectiveAction(description)
Note over QM,Stock: Freigabe (4-Augen-Prinzip)
Approver->>QH: release(approvedBy)
Note right of QH: Prüfung: initiatedBy ≠ approvedBy
QH->>EventBus: QualityHoldReleased {batchId}
EventBus->>Handler: handle(QualityHoldReleased)
Handler->>Stock: unblockBatch(batchId)
Note right of Stock: StockBatch Status: BLOCKED → AVAILABLE
Note over QM,Stock: Alternative: Charge abgelehnt
QM->>QH: reject(reason)
QH->>EventBus: QualityHoldRejected {batchId}
EventBus->>Handler: handle(QualityHoldRejected)
Handler->>Stock: removeBatch(batchId, fullQuantity)
Note right of Stock: Charge komplett aus Bestand entfernt
```
---
## 15. Traceability: Vorwärts- & Rückwärts-Tracing
**Issues:** #43 (Vorwärts-Tracing / Rückruf), #44 (Rückwärts-Tracing / Herkunft)
```mermaid
sequenceDiagram
actor QM as Qualitätsmanager
participant Ctrl as TraceabilityController
participant Svc as BatchTraceabilityService
participant BatchRepo as BatchRepository
participant SMRepo as StockMovementRepository
Note over QM,SMRepo: US-P18 Vorwärts-Tracing (Rückruf-Szenario)
Note over QM,SMRepo: "Welche Endprodukte sind von<br/>Rohstoff-Charge X betroffen?"
QM->>Ctrl: GET /api/traceability/forward/{inputBatchId}
Ctrl->>Svc: traceForward(inputBatchId)
Svc->>BatchRepo: findByConsumptionInputBatchId(inputBatchId)
BatchRepo-->>Svc: List<Batch> (Stufe 1: direkte Verwendung)
loop Für jede gefundene Charge
Svc->>BatchRepo: findByConsumptionInputBatchId(batchId)
Note right of Svc: Rekursiv: auch Zwischenprodukte<br/>die in weiteren Chargen verwendet wurden
end
Svc->>SMRepo: findByBatchIds(allAffectedBatchIds)
Note right of SMRepo: Welche Bewegungen?<br/>An welche Filialen geliefert?
Svc-->>Ctrl: TraceabilityResult {<br/> affectedBatches[],<br/> affectedBranches[],<br/> stockMovements[]<br/>}
Ctrl-->>QM: 200 OK Rückruf-Daten
Note over QM,SMRepo: US-P19 Rückwärts-Tracing (Herkunftsnachweis)
Note over QM,SMRepo: "Woher stammen die Zutaten<br/>von Endprodukt-Charge Y?"
QM->>Ctrl: GET /api/traceability/backward/{outputBatchId}
Ctrl->>Svc: traceBackward(outputBatchId)
Svc->>BatchRepo: findById(outputBatchId)
BatchRepo-->>Svc: batch (mit Consumptions)
loop Für jede Consumption
Note right of Svc: inputBatchId → Herkunft ermitteln
Svc->>BatchRepo: findById(inputBatchId)
BatchRepo-->>Svc: inputBatch
Note right of Svc: Falls inputBatch eigene Consumptions hat:<br/>Rekursiv weiter zurückverfolgen
end
Svc-->>Ctrl: TraceabilityResult {<br/> genealogyTree: {<br/> outputBatch → [inputBatch1, inputBatch2]<br/> inputBatch1 → [rawMaterialBatch1]<br/> },<br/> supplierBatches[] (Blätter des Baums)<br/>}
Ctrl-->>QM: 200 OK Herkunftsnachweis
```
---
## Legende
| Symbol | Bedeutung |
|--------|-----------|
| `actor` | Menschlicher Akteur (Rolle) |
| `participant` | Systemkomponente |
| `Note` | Erklärung / Invarianten |
| `alt/else` | Bedingte Verzweigung |
| `loop` | Schleife / Wiederholung |
| `→` | Synchroner Aufruf |
| `-->>` | Rückantwort |
| Event: `XXX` | Domain Event |