Weiterführende Themen
Reservationen (Bestand und Ladeeinheit)
Einführung
Reservationen sichern geplante Lageroperationen ab, bevor sie ausgeführt werden. Es gibt Reservationen auf Bestandsebene (stock_reservation_*, Schlüssel stock_id) und auf Ladeeinheitsebene (unit_reservation_*, Schlüssel unit_id).
Mehrere Reservationen können über das Attribut composition (UUID) zu einer Gruppe zusammengefasst werden. Eine Gruppe erfüllt drei Zwecke:
- Governing Reservation — bestimmt die reservierte Menge für abbuchende Operationen (Split, Outbound).
- Ausführungsreihenfolge — innerhalb einer Composition gilt: Split → Umlagerung (xfer) → Outbound.
- Bestandsbezug nach Split — nach einem Split beziehen sich nachfolgende Reservationen der Composition auf den neuen Bestand.
Implementierung (Bestand): werner-backend.domain.stock-reservation.* inkl. constraints und availability. Ladeeinheiten: werner-backend.domain.unit-reservation.* inkl. constraints; hierarchische Vorbedingungen (Abschnitte 2.5–2.6) über werner-backend.domain.reservation.feasibility.
Gemeinsame Spalten auf abstract-Ebene (Auszug): state, sort, composition, ready_from (Zeitpunkt, ab dem eine Reservation ausführbar ist — siehe 3. Vorbedingungen für das Ausführen), unit_barcode / unit_id_persisted bzw. stock_barcode / stock_id_persisted zur persistenten Nachverfolgung.
1. Reservationsarten
1.1 Bestand (stock_reservation_*)
| Typ | Tabelle | Kurzbeschreibung |
|---|---|---|
| Umlagerung (xfer) | stock_reservation_xfer | Bestand an neuen Standort (Lager, Adresse, Fahrzeug, Unit, anderer Bestand mit Merge). Reserviert den gesamten Bestand. |
| Outbound | stock_reservation_outbound | Ausgang — Bestand wird entnommen oder (Miete) umgebucht. Mengenbasiert. |
| Split | stock_reservation_split | Teilung in mehrere neue Bestände. Mengenbasiert (amount[]). |
| Inventur | stock_reservation_inventory | Attribute oder Menge des Bestands ändern; Menge 0 = Löschen. |
| Sperre (lock) | stock_reservation_lock | Setzt stock.locking_reason_id. |
| Entsorgung (destroy) | stock_reservation_destroy | Archiviert den Bestand. |
Composition-Typen (Kette): Split, xfer, Outbound. Isolierte Typen: Inventur, lock, destroy.
1.2 Ladeeinheit (unit_reservation_*)
| Typ | Tabelle | Kurzbeschreibung |
|---|---|---|
| Umlagerung (xfer) | unit_reservation_xfer | Ladeeinheit inkl. verschachtelter Inhalte an neuen Standort. |
| Outbound | unit_reservation_outbound | Auslagern der Unit inkl. enthaltener Units und Bestände (rekursiv). |
| Inventur | unit_reservation_inventory | Attribute einer oder mehrerer Units ändern. |
| Sperre (lock) | unit_reservation_lock | Verhindert Änderungen an der Unit und enthaltenen Units/Beständen. |
| Entsorgung (destroy) | unit_reservation_destroy | Entsorgen der Unit inkl. enthaltener Units und Bestände (rekursiv). |
Unit-xfer und Unit-outbound können wie bei Beständen in Compositions gruppiert werden; lock, inventory und destroy sind isoliert.
2. Vorbedingungen beim Anlegen einer Reservation
2.1 Legende der Zellwerte
| Wert | Bedeutung |
|---|---|
| OK | Reservation darf angelegt werden. |
| NOK | Reservation darf nicht angelegt werden. |
| N/A | Nicht anwendbar (falscher Bezugsebene-Typ). |
| CN1 | Nur wenn amount > 0 und amount ≤ available_amount (Bestand; siehe can-create-reservation?). |
| CN2 | Nur wenn kein stock.locking_reason_id gesetzt ist (Outbound-Ausführung; siehe check-stock-not-locked-for-outbound). |
Abschnitte mit Hinweis (Zielbild) sind fachlich verbindlich definiert; die Backend-Prüfung für ready_from beim Ausführen folgt in Issue #358.
2.2 Matrix — bestehende Reservation auf demselben Bestand
Quelle für Bestand: werner-backend.domain.stock-reservation.constraints/can-create-reservation?.
Zeilen: anzulegende Reservation · Spalten: pending (state = init) bestehende Reservation auf demselben stock_id
| Anlage ↓ / Bestehend → | xfer | outbound | split | inventory | lock | destroy |
|---|---|---|---|---|---|---|
| stock_reservation_xfer | OK | OK | OK | NOK | NOK | NOK |
| stock_reservation_outbound | OK | OK | OK (CN1) | NOK | NOK | NOK |
| stock_reservation_split | OK | OK | OK (CN1) | NOK | NOK | NOK |
| stock_reservation_inventory | OK | OK | OK | OK | OK | NOK |
| stock_reservation_lock | NOK | NOK | NOK | OK | OK | NOK |
| stock_reservation_destroy | NOK | NOK | NOK | NOK | NOK | NOK |
Hinweise:
- Mehrere lock- oder inventory-Reservationen auf demselben Bestand sind zulässig (sequenziell bzw. parallel bei lock+inventory).
- Eine zweite destroy-Reservation auf demselben Bestand ist nicht zulässig (kein zusätzlicher Nutzen; Ausführung würde am bereits archivierten Bestand scheitern).
- xfer prüft keine Menge, reserviert aber über
availabilityden vollen Bestand — weitere abbuchende Reservationen können über CN1 scheitern.
2.3 Matrix — Unit-Reservationen vs. N/A auf Bestandsspalten
Unit-Reservationen können nicht auf Beständen angelegt werden; Spalten „Bestand (aktuell / untergeordnet)“ sind für Zeilen 8–12 N/A.
| Anlage ↓ / Bestehend → | xfer | outbound | split | inventory | lock | destroy |
|---|---|---|---|---|---|---|
| unit_reservation_xfer | N/A | N/A | N/A | N/A | N/A | N/A |
| unit_reservation_outbound | N/A | N/A | N/A | N/A | N/A | N/A |
| unit_reservation_inventory | N/A | N/A | N/A | N/A | N/A | N/A |
| unit_reservation_lock | N/A | N/A | N/A | N/A | N/A | N/A |
| unit_reservation_destroy | N/A | N/A | N/A | N/A | N/A | N/A |
2.4 Matrix — bestehende Reservation auf derselben Ladeeinheit
Quelle: werner-backend.domain.unit-reservation.constraints/can-create-reservation?.
| Anlage ↓ / Bestehend → | xfer | outbound | inventory | lock | destroy |
|---|---|---|---|---|---|
| unit_reservation_xfer | OK | OK | NOK | NOK | NOK |
| unit_reservation_outbound | OK | OK | NOK | NOK | NOK |
| unit_reservation_inventory | OK | OK | OK | OK | NOK |
| unit_reservation_lock | NOK | NOK | OK | OK | NOK |
| unit_reservation_destroy | NOK | NOK | NOK | NOK | NOK |
Eine zweite destroy-Reservation auf derselben Unit ist nicht zulässig (analog zu Bestand).
2.5 Matrix — Anlage von Reservationen auf einer Unit unter Beachtung von Reservationen auf enthaltenen Units/Beständen
Implementiert über werner-backend.domain.reservation.feasibility (Feature child/stock/*).
Zeilen: anzulegende Reservation auf der Parent-Unit · Spalten: pending Reservation auf enthaltenem Bestand (split/xfer/outbound/inventory/lock/destroy)
| Anlage (Parent) ↓ / Bestehend auf Inhalt → | stock xfer | stock outbound | stock split | stock inventory | stock lock | stock destroy |
|---|---|---|---|---|---|---|
| unit_reservation_xfer | OK | OK | OK | OK | NOK | NOK |
| unit_reservation_outbound | NOK | NOK | NOK | NOK | NOK | NOK |
| unit_reservation_inventory | OK | OK | OK | OK | OK | OK |
| unit_reservation_lock | NOK | NOK | NOK | NOK | OK | NOK |
| unit_reservation_destroy | NOK | NOK | NOK | NOK | NOK | NOK |
Begründung:
- Routing-xfer auf Unit-Ebene bewegt Container und Inhalt zu einem Versandplatz; pending split/xfer/outbound auf enthaltenen Beständen bleiben gültig (Picking kann vor oder nach dem Routing angelegt werden). Nur lock und destroy auf Inhalten blockieren die Parent-Umlagerung.
- unit_reservation_outbound setzt die rekursive Auslagerungsannahme voraus — jede pending Reservation auf Inhalten (inkl. Inventur, die Menge/Artikel ändern kann) macht die Parent-Outbound-Reservation ungültig.
- unit_reservation_inventory auf der Parent-Unit betrifft nur Unit-Attribute; pending Reservationen auf enthaltenen Beständen stehen dem nicht entgegen.
2.5b Matrix — Anlage auf einer Unit unter Beachtung von Reservationen auf enthaltenen Units
Implementiert über werner-backend.domain.reservation.feasibility (Feature child/unit/*).
Zeilen: anzulegende Reservation auf der Parent-Unit · Spalten: pending Reservation auf enthaltener Unit (xfer / outbound / inventory / lock / destroy)
| Anlage (Parent) ↓ / Bestehend auf enthaltener Unit → | unit xfer | unit outbound | unit inventory | unit lock | unit destroy |
|---|---|---|---|---|---|
| unit_reservation_xfer | OK | OK | OK | NOK | NOK |
| unit_reservation_outbound | NOK | NOK | NOK | NOK | NOK |
| unit_reservation_inventory | OK | OK | OK | OK | OK |
| unit_reservation_lock | NOK | NOK | NOK | OK | NOK |
| unit_reservation_destroy | NOK | NOK | NOK | NOK | NOK |
Symmetrisch zu Abschnitt 2.5 (enthaltene Bestände): Routing-xfer auf Parent-Ebene toleriert pending Operationen auf enthaltenen Units, ausser lock/destroy; Parent-outbound und Parent-destroy blockieren bei jeder pending Child-Unit-Reservation.
2.6 Matrix — Anlage von Reservationen auf einer Unit/einem Bestand unter Beachtung von Reservationen auf übergeordneten Units
Implementiert über werner-backend.domain.reservation.feasibility (Feature parent/unit/*).
Zeilen: anzulegende Reservation auf untergeordnetem Bestand oder Unit · Spalten: pending Reservation auf der übergeordneten Unit (xfer / outbound / inventory / lock / destroy)
| Anlage auf Inhalt ↓ / Bestehend auf Parent-Unit → | unit xfer | unit outbound | unit inventory | unit lock | unit destroy |
|---|---|---|---|---|---|
| stock_reservation_xfer | OK | NOK | OK | NOK | NOK |
| stock_reservation_outbound | OK | NOK | OK | NOK | NOK |
| stock_reservation_split | OK | NOK | OK | NOK | NOK |
| stock_reservation_inventory | OK | NOK | OK | OK | NOK |
| stock_reservation_lock | NOK | NOK | NOK | OK | NOK |
| stock_reservation_destroy | NOK | NOK | NOK | NOK | NOK |
| unit_reservation_xfer | OK | NOK | OK | NOK | NOK |
| unit_reservation_outbound | OK | NOK | OK | NOK | NOK |
| unit_reservation_inventory | OK | NOK | OK | OK | NOK |
| unit_reservation_lock | NOK | NOK | NOK | OK | NOK |
| unit_reservation_destroy | NOK | NOK | NOK | NOK | NOK |
Begründung:
- Pending Parent-outbound blockiert jede weitere Outbound-Reservation auf Inhalten (der Parent-Outbound umfasst den gesamten Inhalt rekursiv).
- Pending Parent-xfer blockiert kein Picking auf Inhalten (Routing zuerst, Split/Outbound danach — symmetrisch zu Abschnitt 2.5); ein Child-xfer ist ebenfalls zulässig, solange kein Parent-outbound pending ist.
- Pending Parent-inventory erlaubt Child-inventory (sequenziell); Child-outbound unter Parent-inventory ist nicht zulässig (Inventur auf Parent kann die Outbound-Annahme invalidieren).
2.7 Feasibility-Schicht (Batch-Prüfung)
Namespace: werner-backend.domain.reservation.feasibility · Regeldaten: reservation.rules.
Für Kandidatenlisten (Picking, Wareneingang, UI-Vorschau) und als Gatekeeper beim Anlegen:
- Stufe A (boolean):
emit-feature-matrixsammelt pending Reservationen auf dem Kandidaten selbst (self/*), enthaltenen Beständen (child/stock/*), enthaltenen Units (child/unit/*) und übergeordneten Units (parent/unit/*) in einem SQL-Snapshot.check-boolean-feasibilitywendet die Regelmasken aus den Matrizen 2.2–2.6 an. - Stufe B (quantitativ): Für Split/Outbound auf Bestand delegiert die Schicht an
stock-reservation.availability(CN1) — keine doppelte Reserved-Amount-Logik.
Gatekeeper-Modus (Create-Pfade): Stufe A und ggf. B in derselben Transaktion; überlebende Kandidaten werden über stock/unit FOR UPDATE gesperrt. Außerhalb von Transaktionen (Strategie-Vorfilter, UI) ist das Ergebnis ein beratender Snapshot.
3. Vorbedingungen für das Ausführen
| Bedingung | Ebene | Status |
|---|---|---|
state = init | alle | implementiert |
ready_from IS NOT NULL | alle | Zielbild — Spalte vorhanden; Prüflogik folgt |
| Composition-Reihenfolge: vorherige Schritte der Gruppe ausgeführt | split/xfer/outbound | implementiert (execute-reservation-with-composition!) |
Bestand nicht gesperrt (locking_reason_id) | stock outbound | implementiert |
| Inventur bei pending split/outbound: nur Mengenänderung, neue Menge ≥ reservierte Menge | stock inventory | implementiert (can-finish-inventory?) |
4. Nachbedingungen beim Ausführen
4.1 Bestand
| Typ | Nach Ausführung (state = done) |
|---|---|
| xfer | Bestand am Zielstandort; ggf. Merge; Composition-Kette auf neuen Bestand umgehängt. |
| split | Quellbestand reduziert; neue Bestände angelegt; nachfolgende Composition-Schritte auf letzten Split-Bestand. |
| outbound | Bestand entnommen oder (Miete) umgebucht; stock_outbound_shipping_advice wird bei Unit-Outbound-Ausführung für enthaltene Bestände geschrieben. |
| inventory | Bestandsattribute/Menge aktualisiert; bei Menge 0 Bestand gelöscht. |
| lock | stock.locking_reason_id gesetzt. |
| destroy | Bestand archiviert (archived gesetzt). |
4.2 Ladeeinheit
| Typ | Nach Ausführung |
|---|---|
| xfer | Unit am Zielstandort; verschachtelte Inhalte bewegen mit. |
| outbound | Unit und Inhalt ausgelagert; unit_outbound_shipping_advice rekursiv für enthaltene Units; pro enthaltenem Bestand stock_outbound_shipping_advice. |
| inventory | Unit-Attribute aktualisiert. |
| lock | unit.locking_reason_id gesetzt; Inhalt rekursiv gesperrt (unit und stock.locking_reason_id). |
| destroy | Unit und Inhalt rekursiv archiviert/entsorgt. |
5. Nachbedingungen beim Stornieren
Stornieren setzt state = cancelled (nur aus init).
| Typ | Verhalten |
|---|---|
| split, xfer, outbound | Gesamte Composition wird storniert (alle pending Schritte der Gruppe). |
| inventory, lock, destroy | Nur die einzelne Reservation wird storniert. |
Keine Seiteneffekte auf Bestand/Unit — es wurden noch keine physischen Änderungen vorgenommen.
6. Verfügbare Menge (Bestand)
available_amount = stock.amount − Σ(reservierte Mengen aller Gruppen)
Pro Gruppe (Composition oder isoliert): Governing Reservation nach Priorität outbound > split > xfer > lock/inventory/destroy. Bei pending lock, inventory oder destroy auf demselben Bestand: available = 0.
Single source of truth: werner-backend.domain.stock-reservation.availability.
7. Referenzen
| Thema | Quelle |
|---|---|
| Constraint-Implementierung Bestand | werner-backend/.../stock_reservation/constraints.clj |
| Constraint-Implementierung Ladeeinheit (gleiche Unit) | werner-backend/.../unit_reservation/constraints.clj |
| Hierarchie + Batch-Feasibility | werner-backend/.../reservation/feasibility.clj, .../reservation/rules.clj |
| Verfügbare Menge | werner-backend/.../stock_reservation/availability.clj |
| Composition-Ausführung | werner-backend/.../stock_reservation/executor.clj, .../unit_reservation/executor.clj |
| Unit-Reservationen | werner-backend/.../unit_reservation/ |
| Agent-Regel (Pflichtlektüre bei Änderungen) | werner/.cursor/rules/reservation-reference.mdc |
| Konsistenz-Axiome Bestand | werner/.cursor/rules/stock-reservation-consistency.mdc |