- products.whitelabel_variant_id, product_images.position/size_cm mit Backfill aus Produktnamen
- WhiteLabelResolver: Farbe vom Produkt, Kundenlogo je Position, Fehler statt stillem Rückfall; Altlogik für Produkte ohne Variante
- Attribute: Variantenwert-Parsing und Farbname ohne Größe
- Produkt- und Kundenformular um Variante, Position und Größe ergänzt
- Lieferschein noch nicht umgestellt (WL-03, wartet auf K6/K7)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kundenfeedback 09.09.2026: Das Kästchen „Produktionsrelevant" oben im Produktformular wurde nicht gefunden. Es sitzt jetzt in der Karte „Warenwirtschaft" direkt unter „Im Produktbestand anzeigen", heißt „In der Produktion anzeigen (Produkt wird selbst hergestellt)" und erklärt im Hilfetext die Wirkung (Zukaufartikel wie Aloe abwählen: keine Anzeige in der Produkt-Auswahl der Produktion und in „Enthalten in"; Produktionsergebnis, Produktbestand und Verkauf unberührt). Feld hält jetzt auch old()-Werte nach Validierungsfehlern.
In-App-Doku (Hinweise, Leitfaden §13/§20) entsprechend umformuliert; Test prüft Label und Platzierung.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mfg_phase_id[] je Phasenblock; Zuordnung bestehender Phasen über ID, dann Name,
dann gemeinsame Rohstoffe. Ein vorangestellter neuer Block übernimmt nicht mehr
die ID einer umbenannten Phase; Produktionszeilen behalten ihre Phase.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Lagerplatz-Zuordnung (Raum/Regal/Fach) gab es bisher nur bei Rohstoffen. Jetzt
dieselbe Storage-Hierarchie (StorageRoom/Shelf/Slot) auch für Produkte und
Verpackungsartikel, inkl. Anzeige an den passenden Stellen.
- Migration: storage_room_id/shelf/slot an products + packaging_items
(nullable, FK constrained, nullOnDelete).
- Models Product + PackagingItem: fillable + storageRoom/Shelf/Slot-Relationen +
storageLabel() („Raum 1 | Regal 3 | A").
- Produktformular (Inventar-Abschnitt) + Verpackungsformular: Auswahl-Zeile
Raum/Regal/Fach; Optionen kommen aktiv/sortiert aus den zentral pflegbaren
Bausteinen (Einstellungen → Allgemein).
- Speichern: ProductRepository normalisiert leere Auswahl -> null (FK);
PackagingItem-Requests + Repository um die drei Felder erweitert.
- Anzeige: Produktbestand (Sub-Zeile mit Marker), Produktverpackung-Übersicht
(neue Spalte „Lagerplatz").
- DeletionGuard: Lagerbaustein ist nicht löschbar, solange Produkte/Verpackung
darauf verweisen (analog Rohstoffe).
Tests: 4 neue (Produkt speichern + leer->null, Verpackung speichern + Anzeige,
Löschsperre, freier Baustein löschbar). Migration auf Dev-/Live-DB gelaufen.
Suite 331 grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AP-45: Verpackungsverbrauch (Etikett/Tiegel) wird jetzt aus dem tatsächlichen
Produktionsergebnis gebucht — je Ergebnisprodukt dessen eigene Stückliste ×
Ergebnismenge, über alle Outputs summiert — statt aus der geplanten Stückzahl.
syncPackagingSnapshot liest die production_outputs und läuft daher nach
persistOutputs (store + update umgestellt).
AP-47: Neues Produkt-Kennzeichen „produktionsrelevant" (Boolean, Default true).
Nur so markierte Produkte erscheinen in der Produktions-Auswahl
(ProductionController create/edit/copy) und in der „Enthalten in"-Box des
Rohstoffbestands (blendet z. B. Aloe-Handelsware und Zweitgrößen aus).
Checkbox im Produktformular (mit Hidden-Fallback 0). Migration Default true,
damit Bestandsdaten unverändert bleiben.
Migrationen add_production_relevant auf Dev-/Live-DB ausgeführt. 4 Tests, Suite 314 grün.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AP-35 (Mengenberechnung): Die Hersteller-Rezeptur ist in Prozent hinterlegt; die
Sollmengen skalieren jetzt auf das Füllgewicht statt fest auf ~100 g. Neuer
Faktor recipeGramScale = Füllgewicht/100. Basis = neues, optionales Produkt-Feld
"fill_weight" (g); ist es leer, gilt bei ml-Produkten (unit=1) die Füllmenge
(contents_total) als Gramm (1 g/ml). Ohne Massenbasis (z. B. Stück-Produkte)
bleibt es beim Altverhalten (Anteil = Gramm), sodass Bestandsprodukte/Tests
unverändert rechnen. Skala wird an die Maske durchgereicht (recipe_gram_scale),
damit Client und Server identisch rechnen (Service + 4 JS-Stellen).
AP-39 (Abwiegen): Die Ist-Menge je INCI muss nicht mehr aufs Gramm passen —
assertLinesMatchRecipe erlaubt ±5 % (kleiner absoluter Sockel), gebucht wird der
reale Ist-Wert. Status-Ampel in der Maske: Orange = nichts eingetragen,
Grün = eingetragen und ±5 %, Rot = >5 % daneben (Zahlendreher-Schutz).
Migration add_fill_weight_to_products auf Dev-/Live-DB ausgeführt (nullable).
Product fillable/cast + Produktformular-Feld ergänzt. 5 Tests, Suite 310 grün.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Das Lösch-Formular für gespeicherte Rezeptur-Inhaltsstoffe war in das
Produkt-Formular verschachtelt; Browser verwerfen das innere <form>-Tag,
wodurch _method=DELETE ins Speichern-Formular leakte. Jetzt Button, der
per JS ein eigenes Formular außerhalb erzeugt und abschickt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>