DACH-Codes und Default-Country in country-defaults.ts zentralisiert statt
in jedem Screen hardcoded. API-Fetch-Fehler fallen auf DACH-Fallback zurück
statt stiller Fehlerignorierung. Country-Validierung in Pflichtfeld-Formularen
ergänzt.
Backend: Country-Record (Shared Kernel), InMemoryCountryRepository mit
~249 Ländern und DACH-Priorisierung, ListCountries-UseCase,
GET /api/countries?q= Endpoint.
Frontend: CountryPicker-Komponente mit Fuzzy-Suche, DACH-Favoriten bei
leerem Query. SupplierCreate-, CustomerCreate- und AddDeliveryAddress-
Screens verwenden jetzt den CountryPicker statt Freitext. Detail-Screens
zeigen den Ländercode in der Adressanzeige.
Closes#71
ListStorageLocations ignorierte active=false und gab stattdessen
alle Lagerorte zurück. Jetzt wird bei gesetztem active-Parameter
korrekt nach aktivem/inaktivem Status gefiltert.
StockPicker-Komponente für Bestandssuche nach Artikel-/Lagerort-Namen.
StockBatchEntryScreen nutzt StockPicker statt manueller UUID-Eingabe.
StockListScreen mit Suchfilter [s] und Namensanzeige statt IDs.
StockDetailScreen zeigt Artikel-/Lagerort-Namen im Header.
DELETE /api/inventory/stocks/{stockId}/reservations/{reservationId}
gibt eine bestehende Reservierung frei und stellt die verfügbare
Menge wieder her. Zusätzlich Liquibase-Changeset 025 idempotent
gemacht (ON CONFLICT DO NOTHING).
Implementiert Story 4.1: Reservierung von Beständen mit automatischer
FEFO-Allokation (First-Expired-First-Out) über verfügbare Chargen.
Domain: Reservation-Entity, StockBatchAllocation, ReservationDraft,
FEFO-Logik in Stock.reserve(), availableQuantity() berücksichtigt
bestehende Allokationen. Neue Error-Varianten für InsufficientStock,
InvalidReferenceType, InvalidReservationPriority, ReservationNotFound.
API: POST /api/inventory/stocks/{stockId}/reservations → 201 Created.
Liquibase: reservations + stock_batch_allocations Tabellen mit FK- und
CHECK-Constraints.
Tests: 43 neue Tests (22 Domain, 10 UseCase, 11 Integration) für
FEFO-Logik, Validierung, Mengenprüfung, Auth und Edge Cases.
ProductionOrder-Aggregate mit DDD + Clean Architecture eingeführt.
Produktionsleiter können Aufträge mit Rezept, Menge, Termin und
Priorität planen. Validierung: Quantity > 0, PlannedDate nicht in
Vergangenheit, Priority (LOW/NORMAL/HIGH/URGENT), Recipe ACTIVE.
StorageLocationEditScreen mit Formular für Name und Temperaturbereich.
StorageType wird als immutable angezeigt. Erreichbar über [Bearbeiten] im
DetailScreen. Vervollständigt Story 1.2 im Frontend.
DeactivateStorageLocation prüft jetzt via StockRepository ob Bestände am
Lagerort existieren bevor deaktiviert wird (Story 1.2 Akzeptanzkriterium).
5 Unit Tests und 1 Integrationstest für den neuen Check.
Frontend: @sentry/node mit instrument.ts, globale Error-Handler, 5xx-Interceptor.
Backend: sentry-spring-boot-starter-jakarta, Sentry.captureException im GlobalExceptionHandler.
Konfiguration über SENTRY_DSN Env-Variable, Bugsink via make bugsink startbar.
Chargen: Liste mit Statusfilter, Planen, Starten, Verbrauch erfassen,
Abschließen und Stornieren. Bestände: Liste, Anlegen, Detailansicht
mit Chargen sperren/entsperren/entfernen. Types, API-Client, Hooks,
Navigation und Screens für beide Bounded Contexts vollständig ergänzt.
GET /api/inventory/stocks/below-minimum zeigt Bestände, deren verfügbare
Menge (AVAILABLE + EXPIRING_SOON) unter dem konfigurierten Mindestbestand
liegt. DB-Prefilter via findAllWithMinimumLevel() + Domain-Filter als
Defense in Depth. StockResponse.from() nutzt nun Stock.availableQuantity()
statt duplizierter Logik.
Closes#11
PLANNED und IN_PRODUCTION Chargen können mit Angabe eines
Stornierungsgrundes storniert werden. COMPLETED und bereits
CANCELLED Chargen werden abgelehnt.
Batch.complete() mit Ist-Menge, Ausschuss und Bemerkungen.
Invarianten: nur IN_PRODUCTION→COMPLETED, mind. eine Consumption,
ActualQuantity > 0, Waste >= 0. Full Vertical Slice mit Domain Event
Stub (BatchCompleted), REST POST /api/batches/{id}/complete und
Liquibase-Migration für die neuen Spalten.
Täglicher Scheduler prüft alle StockBatches und setzt Status auf
EXPIRED bzw. EXPIRING_SOON basierend auf MHD und MinimumShelfLife.
Reihenfolge: erst EXPIRED, dann EXPIRING_SOON — BLOCKED bleibt unangetastet.
- Managed-Entity-Update statt detach/merge (verhindert DELETE+INSERT-Churn)
- @Version für Optimistic Locking mit ConcurrentModification-Error
- Null-Checks für quantityUsed/quantityUnit vor BigDecimal-Parsing
- Duplicate-Check nach Consumption.create() für robustere Validierung
- FetchType.EAGER→LAZY für BatchEntity.consumptions
- Liquibase-Migration 020 für version-Spalte
PLANNED-Chargen können in Produktion genommen werden (IN_PRODUCTION),
anschließend wird der Rohstoff-Verbrauch pro InputBatch dokumentiert.
Bildet die Grundlage für die Chargen-Genealogie (Tracing).
Stock.update(StockUpdateDraft) ermöglicht optionale Aktualisierung von
MinimumLevel und MinimumShelfLife mit identischer Validierung wie create().
PUT /api/inventory/stocks/{id} Endpoint, UpdateStock Use Case + Tests.
Closes#9
Full Vertical Slice für Batch-Lese-Endpoints:
- GET /api/production/batches/{id}
- GET /api/production/batches (Filter: status, productionDate, articleId)
- GET /api/production/batches/by-number/{batchNumber}
articleId-Filter löst über RecipeRepository.findByArticleId() die
zugehörigen Recipes auf und sucht dann Batches per findByRecipeIds().
Closes#34
- StockError.InvalidFilterCombination statt InvalidArticleId für
ungültige Filter-Kombination in ListStocks
- CreateStockResponse als eigenständiges DTO für POST createStock
(ohne batches/quantities), StockResponse bleibt für GET-Endpoints
- MinimumLevelResponse als shared Top-Level-Record extrahiert
Query-Endpoints für Stock-Aggregate: GET /api/inventory/stocks (mit
optionalen Filtern storageLocationId/articleId) und GET /api/inventory/stocks/{id}.
StockResponse enthält nun Batches, totalQuantity, availableQuantity und
quantityUnit. Abgesichert über @PreAuthorize STOCK_READ.
Backend:
- articleId als Pflichtfeld im Recipe-Aggregate (Domain, Application, Infrastructure)
- Liquibase-Migration 015 mit defaultValue für bestehende Daten
- Alle Tests angepasst (Unit, Integration)
Frontend:
- UoM-Carousel-Selektor in RecipeCreateScreen, AddBatchScreen, AddIngredientScreen
- ArticlePicker-Komponente mit Typeahead-Suche für Artikelauswahl
- Auto-Position bei Zutatenzugabe (kein manuelles Feld mehr)
- Automatische subRecipeId-Erkennung bei Artikelauswahl
- Zutaten-Reorder per Drag im RecipeDetailScreen (Remove + Re-Add)
- Artikelnamen statt UUIDs in der Rezept-Detailansicht
- Navigation-Context: replace()-Methode ergänzt
- LocalDateTime → OffsetDateTime (UTC) in allen Domain-Klassen, JPA Entities, DTOs und Tests
- Liquibase-Migration 017: TIMESTAMP → TIMESTAMP WITH TIME ZONE für bestehende Spalten
- Custom DateTimeProvider für Spring Data @CreatedDate-Kompatibilität mit OffsetDateTime
- Neue Sequenztabelle (016) mit JPA Entity + PESSIMISTIC_WRITE Lock statt COUNT-basierter
Batch-Nummernvergabe (Race Condition Fix)
- Quantity.reconstitute(amount, uom) 2-Parameter-Overload für bessere Lesbarkeit
- AuditEvents STOCK_BATCH_BLOCKED/UNBLOCKED hinzugefügt, reason wird als Audit-Details geloggt
- LocalDate.now() aus Stock.unblockBatch() entfernt, referenceDate als Parameter (Application Layer übergibt)
- Defensive Null-Prüfung für expiryDate in unblockBatch MHD-Check
- Ticket 003 erstellt zur Klärung ob reason im Domain-Model persistiert werden soll
Gesperrte Chargen können nicht entnommen oder reserviert werden.
blockBatch: AVAILABLE/EXPIRING_SOON → BLOCKED; unblockBatch: BLOCKED → AVAILABLE/EXPIRING_SOON (MHD-Check).
Verhindert zirkuläre Abhängigkeiten (A→B→A, A→B→C→A) beim Hinzufügen
von Sub-Rezepten als Zutaten. Iterative DFS-Prüfung mit Pfad-Tracking
für aussagekräftige Fehlermeldungen.
Production: Rezeptliste mit Status-Filter (Draft/Active/Archived), Rezept
archivieren für aktive Rezepte, list() gibt RecipeSummaryDTO zurück.
Inventory: Charge einbuchen (AddBatch) mit neuem Stocks-Resource und Screens.
GET /api/recipes/{id} liefert vollständiges Rezept inkl. Zutaten und Schritte.
GET /api/recipes?status=ACTIVE liefert Summary-Liste mit ingredientCount/stepCount.
StockBatch als Child-Entity im Stock-Aggregat mit BatchReference
(batchId + batchType), Quantity, ExpiryDate und Status AVAILABLE.
POST /api/inventory/stocks/{stockId}/batches → 201.
Produktionsschritte im RecipeDetailScreen anzeigen, hinzufügen und
entfernen. Neuer AddProductionStepScreen mit Formular für stepNumber,
description, durationMinutes und temperatureCelsius.
Alle 9 Integration-Test-Klassen nutzen eine gemeinsame Base-Class mit
geteiltem Spring Context (MOCK statt RANDOM_PORT), vorberechnetem
BCrypt-Hash und gemeinsamen Helper-Methoden. Eliminiert ~600 Zeilen
Duplikation und Runtime-BCrypt-Aufrufe im Test-Setup.