Phasen ohne Rohstoffzeile (reine Hinweisphasen wie „Vorbereitung"/„Nachbereitung")
verschwanden in der Produktionsmaske, weil buildPhaseGroups() leere Gruppen
wegfilterte. Filter so angepasst, dass benannte Phasen erhalten bleiben und nur
der namenlose „lose" Sammeltopf verworfen wird. Die Phasen kommen serverseitig
bereits in pos-Reihenfolge (recipePhases orderBy pos), sodass „Vorbereitung"
korrekt über „Phase A" steht. Test sichert den recipeJson-Datenvertrag ab.
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>
Indizes auf shopping_orders.shipped/shipped_at/created_at (Board-Filter,
Tages-Kacheln) und products.wp_number (SKU-Auflösung). Die
API-Bestellungen-Seite lud das komplette JSON-payload für alle 500 Zeilen
und serialisierte es in Inline-Attribute; jetzt persistierte
positions_count, Liste ohne payload, Inhalt on-demand über show-Endpoint.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Default-Scope 'recipe' blendet rezepturlose Rohstoffe aus, das
Sidebar-Badge zählt aber alle aktiven kritischen — Badge und Seite
konnten sich widersprechen. Die recipe-Ansicht zeigt jetzt einen Hinweis
mit 'Alle anzeigen', sobald das Badge mehr zählt; Empty-State-Text
scope-abhängig.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bestellnummern-Links auf admin_sales_detail (admin >= 7) waren für
Versand-Mitarbeiter tote Links (stiller Redirect) — jetzt rollenabhängig.
Kachel-Zähler und pick/ready-Filter rechnen über alle offenen
Bestellungen statt über die neuesten 500; gekappt wird nur die Anzeige,
der Hinweis-Banner zeigt 'neueste n von total' korrekt. whereDate durch
indexfähiges whereBetween ersetzt; Packlisten-Toggle über data-order-id;
Gewicht über formatNumber().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Brücke hätte live nicht funktioniert: Connector 1.4.0 sendete die
label_url aus dem per 'deny from all' geschützten DHL-Ordner (403), dazu
SSRF-Risiko durch serverseitigen Abruf einer Client-URL und Timeout-
Mismatch (20 s Download vs. 5 s WordPress-Default). Der Connector liest
die PDF jetzt vom Shop-Dateisystem (realpath-Pinning auf wp-uploads) und
sendet sie Base64; storeWpLabel() validiert und speichert ohne Http::get;
tote Shop-Link-Fallbacks aus den Views entfernt; Connector-Timeout 30 s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bestell-Übertragung (wp/order) akzeptiert wp_label_url + wp_tracking_number:
Label-PDF wird als lokale Kopie im privaten Storage gespeichert (Shop-URL ist
öffentlich erratbar; Kopie überlebt Löschung im Shop), Sendungsnummer landet
in shopping_orders.tracking. Neuer Endpoint wp/label zum Nachreichen
(Connector-Hook pr_save_dhl_label_tracking), wp/status liefert die
Sendungsnummer an den Shop zurück. Abwicklungs-Board + Bestell-Details zeigen
Label-Download (copyreader-Dokumentroute type=label, Fallback Shop-Link) und
Sendungsnummer. Download-Fehler blockieren die Übertragung nie.
Doku: Connector-Update-Anleitung (dev/product management), Hinweise,
Testleitfaden §11, Plan-Protokoll. pint.json schließt dev/ (Fremdcode) aus.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Checkbox-Auswahl im Abwicklungs-Board (Alle-auswählen, Zähler in den Buttons)
+ zwei Sammel-Aktionen: „Lieferscheine drucken" und „+ Rechnungen" — je ein
zusammengeführtes PDF (PDFMerger/FPDI) im neuen Tab zum Stapeldruck. Fehlende
Dokumente werden dabei ohne Mailversand erzeugt, auf der Platte fehlende
Altdateien mit bestehender Nummer regeneriert. API-Bestellungen sind von der
Auswahl ausgenommen (deaktivierte Checkbox, Rechnung liegt im externen Shop)
und werden in gemischter Auswahl übersprungen; max. 100 je Druck.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Kundenname mit Ellipsis begrenzt (150px, iPad 110px; voller Name als Tooltip),
Zellen-Padding reduziert, Positionen-Toggle als schmale letzte Spalte —
zusammen mit den Aktionen rechts angepinnt, beim Scrollen bleibt beides sichtbar.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9 → 6 Spalten (Datum unter Bestellnr., Gewicht in die Packliste, Zahlung in
die Status-Spalte); Aktionen-Spalte sticky am rechten Rand (bleibt beim
horizontalen Scrollen sichtbar); unterhalb Desktop-Breite Icon-Buttons ohne
Beschriftung (Tooltip bleibt).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neuer Nav-Punkt „Bestellungen" (Abwicklung + API-Bestellungen als Unterpunkte).
Board für Versand-Mitarbeiter (copyreader): 4 Filter-Kacheln (heute eingegangen /
zu konfektionieren / versandfertig / heute versendet), Packliste je Bestellung
(Sets mit Komponenten), Dokumente (Rechnung/Lieferschein/Kombi-Download über
eigene copyreader-Route, Erstellen ohne Mailversand; API-Bestellungen verlinken
die externe Rechnung), Aktionen Konfektioniert/Versendet (nur vorwärts, Storno
bleibt Verkäufe/admin). Versandfertig-Regel per Kundenentscheid: Rechnung sofort,
Vorkasse nach Zahlungseingang, API immer (ShoppingOrder::isReadyToShip()).
Statuswechsel-Logik in OrderStatusService extrahiert (Verkäufe-Modal + Board
teilen Transaktion/Lock/Margin-Folgen/AP-13-Abzug). Kundenentscheid Punkt 3:
Board-Statuswechsel setzt shipped_status_locked — die Bestell-Detailseiten
ändern den Status dann nicht mehr automatisch (offen → in Bearbeitung).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neuer Navigationspunkt „API-Bestellungen": jede wp/order-Übertragung wird
protokolliert (api_order_logs) — auch abgelehnte (Kunde unbekannt, Bestellung
existiert bereits, Speicherfehler). Positionen ohne Produktzuordnung
(unbekannte WP-Nummer) markieren den Eintrag rot; das Modal zeigt den Inhalt
der Übertragung je Position (SKU, Produkt, Menge, Preis, Meldung übersetzt).
Dazu SKU-Konsistenzprüfung auf der Seite: doppelte WP-Nummern aktiver Produkte
(Warnung) und aktive Produkte ohne WP-Nummer (aufklappbar). Behebt die stille
Lücke, dass „article not found" nur in der API-Antwort unterging und die
Position kommentarlos in Bestellung und Bestandsabzug fehlte (Plan §4).
ShoppingUserController per Pint formatiert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Das Modal zeigt den aktuellen Bestand; ein Ausgang über Bestand fragt
per Bestätigung nach (Inventur-Korrekturen bleiben möglich, Tippfehler
werden abgefangen).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neuer Helper reFormatWawiNumber(): '5.1'/'5.12' werden als 5,1/5,12
gelesen (kommt beim Kopieren von Daten vor); '1.500' bleibt Tausender.
Gilt nur für die WaWi-Eingaben (Mengen, Preise, Gewichte, Rezeptur) —
reFormatNumber() app-weit unverändert. JS-Parser (parseGerman,
parseDeNumber) auf dieselbe Regel vereinheitlicht; parseDeNumber
behandelte '1.500' bisher sogar als 1,5.
Tests: +1 (Parsing-Matrix).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Chargennummern kommen von extern und können kollidieren. Der Lieferant
gehört jetzt zum Gruppenschlüssel (batchGroupKey b:<supplier>:<lot>) —
gleiche Lot-Nummern verschiedener Lieferanten verschmelzen nicht mehr
(kein gemeinsames min-MHD/Summen-Rest). Batch-Check (AP-27) prüft
zusätzlich den Lieferanten des zu buchenden Eingangs.
Tests: +1 (getrennte Optionen je Lieferant); Chargen-Tests auf neues
Schlüsselformat umgestellt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Anforderung 15.07.2026: Nahezu alle WaWi-Einträge sind jetzt löschbar
(Soft-Delete, deleted_at), mit Abhängigkeitsprüfung und Bestätigungs-
warnung vor jeder Löschung.
- Migration: deleted_at auf 14 WaWi-Tabellen (Stammdaten, ingredients,
stock_entries, stock_disposals, productions); SoftDeletes-Trait in
den Models (suppliers/packaging_items hatten es bereits).
- Neuer App\Services\DeletionGuard: zentrale Abhängigkeitsprüfung je
Entität — referenzierte Einträge sind nicht löschbar, Fehlermeldung
nennt die Verwendungen (behebt auch Audit M-4: kein 500er/stiller
Cascade-Verlust mehr beim Löschen verwendeter Stammdaten).
- SettingCrudController::destroy() prüft Blocker; Supplier-,
PackagingItem-, Ingredient-destroy ebenso (Ingredient: vollständige
Prüfung inkl. Produktion/Ausschuss, Route GET→DELETE, Audit M-6).
- Wareneingänge: auch gebuchte Eingänge löschbar, sofern kein
Produktions-Verbrauch/Ausschuss darauf gebucht ist.
- Ausschuss: löschbar (Menge steht dem Bestand wieder zur Verfügung).
- Produktionen: löschbar (admin >= 7); Produktbestand wird per
Gegenbuchung zurückgeführt (reverseProductionStock, append-only,
withTrashed), Rohstoffverbrauch zählt nicht mehr (Bestandsqueries
schließen gelöschte Produktionen aus).
- lastNetPricePerSupplier: DB::query statt Model-Query (Global Scope
auf Sub-Select-Alias).
- Debug-Log-Reste im PackagingItemController entfernt (Audit N-16).
- Views: Lösch-Buttons mit deutschen Bestätigungswarnungen (Ausschuss,
Produktionen, gebuchte Wareneingänge, INCI als DELETE-Formular).
Tests: neuer InventoryDeletionTest (10 Tests), StorageSettingsTest an
neue Löschregel angepasst. Volle Suite: 214 grün, 783 Assertions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Seit 9ce711d rendert layout-2 die scripts-Section zusätzlich zum
Eltern-Layout (application.blade.php:145) — jede Seite band dadurch
alle Event-Handler doppelt. Sichtbares Symptom: „Weitere Charge" in
der Produktionsmaske legte pro Klick zwei Zeilen an.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Chargen-Existenz-Check beim Buchen (JSON-Endpoint batch-check): Hinweis
„Charge bereits im Haus" + Vorbelegung MHD/Qualität aus dem letzten Eingang
- Rohstoffbestand-Detail: Chargen je batch_number summiert
(InventoryService::chargeGroupsForIngredient), Einkäufe als Unterzeilen
- Produktion: Charge-Dropdown je Charge mit Summen-Rest (batches im
Recipe-Payload), Verbrauch wird per expandBatchLines FEFO auf die
Einzel-Einkäufe der Charge verteilt; Service-API bleibt je stock_entry
- Jeder Einkauf bleibt eigener stock_entry (Preis/Datum/Audit)
- Hinweise-Doku + Plan V6.0 §6 fortgeschrieben
Tests: StockEntryChargeMergeTest (7 neu); Suite 193 grün, 698 Assertions
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ingredients.exclude_from_stock (Checkbox im INCI-Formular, z. B. Allergene):
erscheint nie im Rohstoffbestand und nicht im Kritisch-Badge
- Rohstoffbestand-Uebersicht: Umschalter "Alle" / "Nur aus Herstellerrezepturen"
(?scope=recipe, nutzt scopeUsedInActiveRecipes aus AP-21)
- Fix: AP-21-Migrationen hatten identischen Timestamp (add_storage lief vor
create_storage und scheiterte auf MySQL am FK) - umbenannt auf 120753,
Teilreste bereinigt, Migrationen sauber ausgefuehrt
- Hinweise-Doku fortgeschrieben
Tests: RawMaterialStockTest +2 (volle Suite 172 gruen, 624 Assertions)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>