Warenwirtschaft: Rohstoffbestand — Default „Nur aus Herstellerrezepturen" (Kundenentscheid 17.07.2026)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-17 12:07:54 +00:00
parent 4f9dca88de
commit e6af987237
5 changed files with 16 additions and 13 deletions

View file

@ -23,7 +23,8 @@ class RawMaterialStockController extends Controller
public function index(Request $request): View
{
// AP-20: Modus „alle" / „nur aus Herstellerrezepturen (aktiver Produkte)"
$scope = $request->query('scope') === 'recipe' ? 'recipe' : 'all';
// Kundenentscheid 17.07.2026: Default = nur Rohstoffe aus Herstellerrezepturen
$scope = $request->query('scope') === 'all' ? 'all' : 'recipe';
$ingredients = Ingredient::query()
->where('active', true)

View file

@ -273,6 +273,7 @@ Der Warenkorb kommt also **per Knopfdruck** (Fulfillment-Übergabe), nicht autom
| 16.07.2026 | **AP-13 (API-Bestellungen — Protokoll + SKU-Fehlermarkierung, Kundenanforderung 16.07.)** | Neue Tabelle **`api_order_logs`** (Migration `2026_07_16_090000`) + Model `ApiOrderLog`; `ShoppingUserController` protokolliert **jede** `wp/order`-Übertragung — erfolgreiche inkl. Zuordnungsergebnis je Position (SKU, Produkt, Menge, Preis, API-Meldungen), abgelehnte mit Grund (Kunde unbekannt, Bestellung existiert bereits, Speicherfehler). Positionen ohne Produktzuordnung ⇒ `has_errors` + Zusammenfassung („x von y Positionen ohne Produktzuordnung"). **Neuer Menüpunkt „API-Bestellungen"** (copyreader, `admin/inventory/api-orders`): Liste (Limit 500/`?all=1`, Filter „nur Fehler"), rote Markierung, **Modal „Ansehen"** mit Positionstabelle (Meldungen deutsch übersetzt, Roh-Payload bei abgelehnten). Dazu SKU-Konsistenz (Plan §4 „mitzulösen"): Warnkarte **doppelte WP-Nummern** aktiver Produkte, aufklappbare Liste **aktive Produkte ohne WP-Nummer**. Commit `71d5591`. | Neuer `ApiOrderLogTest` (8 grün, Ende-zu-Ende über `wp/order` mit `Passport::actingAs`). Suite: **257 grün, 940 Assertions** |
| 16.07.2026 | **Bugfix Logistik-Mail (Kundenmeldung)** | Fehlversand an die Rechnungsadresse: die Versand-Mail (`MailLogistic`) hängte den **Lieferschein nur bei White-Label-Bestellungen** an. Jetzt wird er **immer** mitgesendet (unabhängig von Logo/White-Label); fehlt die PDF-Datei auf der Platte, wird sie mit bestehender Rechnungsnummer regeneriert. Dazu **tote Duplikat-Klasse `app/Mail/MailLogitic.php` entfernt** (Tippfehler-Dateiname, definierte `MailLogistic` in einer Altversion ohne Lieferschein-Anhang — Kollisionsrisiko im Classmap-Autoload). Commit `7d77314`. **Nachtrag:** gleiche Regeneration auch für die **Rechnungs**-PDF — vorher brach `build()` bei fehlender Rechnungsdatei still ab (Mail ohne Anhänge trotz Erfolgsmeldung im UI). | Neuer `LogisticMailTest` (4 grün: Anhang ohne White-Label, Regeneration Lieferschein + Rechnung, Versand-Button). Volle Suite: **276 grün, 1016 Assertions** |
| 17.07.2026 | **AP-32-Brückenlösung (DHL-Label aus WooCommerce übernehmen)** | Bis zur echten DHL-API-Anbindung: Das im Shop erstellte Label (Plugin „DHL for WooCommerce", Meta `_pr_shipment_dhl_label_tracking`) kommt **bei der Bestell-Übertragung mit**. `POST wp/order` akzeptiert `wp_label_url` + `wp_tracking_number` (String oder Array); das System lädt die Label-PDF als **lokale Kopie** in den privaten Storage (`wp-labels/`, Migration `2026_07_17_090000`: `shopping_orders.wp_label_url/wp_label_path`; Sendungsnummer in bestehender Spalte `tracking`) — Begründung: Shop-URL ist öffentlich + erratbar (Kundenadresse!), Kopie überlebt Löschung im Shop; Download-Fehler blockieren die Übertragung nie. **Neuer Endpoint `POST wp/label`** zum Nachreichen (Connector-Hook `pr_save_dhl_label_tracking`); `wp/status` liefert je Bestellung `tracking_number` zurück (Tracking-Link Endkunde). **Anzeige:** Abwicklungs-Board + Bestell-Details mit „Label"-Download (lokale Kopie über copyreader-Dokumentroute `type=label`, Fallback Shop-Link) + Sendungsnummer. Nötiges Connector-Update (WP-Seite) dokumentiert in `dev/product management /ap-32-brueckenloesung-connector-update.md`. Hinweise-Doku + Testleitfaden (§11) fortgeschrieben. Nebenbei: `pint.json` schließt `dev/` (Fremdcode-Referenzen) von der Formatierung aus. | Neuer `WpLabelTransferTest` (10 grün: Übertragung mit Label/Sendungsnr., Download-Fehler + Nicht-PDF blockieren nicht, Mehrpaket-Nummern, Nachreichen, unbekannte Nummer, Status-Poll, Board-Anzeige, Label-Download + 404). Volle Suite: **286 grün, 1052 Assertions** |
| 17.07.2026 | **Kundenentscheid: Rohstoffbestand-Default „Nur aus Herstellerrezepturen"** | `RawMaterialStockController@index`: Default-Scope von `all` auf `recipe` gedreht (`?scope=all` zeigt weiterhin alles); Button-Gruppe entsprechend verlinkt, Hinweise-Doku angepasst. | `RawMaterialStockTest` angepasst (Default filtert, `scope=all` zeigt alle). Volle Suite: **286 grün, 1052 Assertions** |
| _offen_ | AP-32 | Versand im System: DHL-Etiketten per API — **neu aufgenommen + Ansatz dokumentiert (§4, 15.07.2026)**; Klärungspunkte (DHL-Konto, Versandprodukte, Druck-Setup) für nächste Anforderungsrunde notiert. **Zwischenstand 17.07.2026: Brückenlösung aktiv** (Label aus dem Shop, s. Protokollzeile) — deckt den Versand-Flow ab, bis die DHL-Klärungen da sind | – |
| _offen_ | AP-14/15/16/17 | Audit-Trail, Blockrechte, 2FA, WaWi-Einstellungen | – |

View file

@ -230,9 +230,10 @@
- **INCI-Liste „nur aktive":** Der Filter zeigt nur Inhaltsstoffe, die in der
**Hersteller-Rezeptur mindestens eines aktiven Produkts** vorkommen – nicht zu
verwechseln mit dem Aktiv-Häkchen am INCI selbst.
- **Rohstoffbestand-Filter:** Die Übersicht kennt zwei Modi – **„Alle"** (auch
Rohstoffe ohne Rezeptur-Bezug, z. B. für Neuentwicklungen eingekauft) und
**„Nur aus Herstellerrezepturen"** (aktiver Produkte). INCI mit dem Häkchen
- **Rohstoffbestand-Filter:** Die Übersicht kennt zwei Modi –
**„Nur aus Herstellerrezepturen"** (aktiver Produkte, **Standard** seit
17.07.2026) und **„Alle"** (auch Rohstoffe ohne Rezeptur-Bezug, z. B. für
Neuentwicklungen eingekauft). INCI mit dem Häkchen
**„Kein Rohstoffbestand"** (z. B. Allergen-Angaben) erscheinen nie im
Rohstoffbestand und zählen nicht im Kritisch-Badge.
- **Rezeptur-Phasen:** Die Hersteller-Rezeptur ist in Phasen gegliedert

View file

@ -65,9 +65,9 @@
placeholder="{{ __('Rohstoff suchen …') }}">
</div>
<div class="btn-group btn-group-sm ml-2" role="group" aria-label="{{ __('Rohstoff-Filter') }}">
<a href="{{ route('admin.inventory.raw-material-stock.index') }}"
<a href="{{ route('admin.inventory.raw-material-stock.index', ['scope' => 'all']) }}"
class="btn {{ $scope === 'all' ? 'btn-primary' : 'btn-outline-secondary' }}">{{ __('Alle') }}</a>
<a href="{{ route('admin.inventory.raw-material-stock.index', ['scope' => 'recipe']) }}"
<a href="{{ route('admin.inventory.raw-material-stock.index') }}"
class="btn {{ $scope === 'recipe' ? 'btn-primary' : 'btn-outline-secondary' }}">{{ __('Nur aus Herstellerrezepturen') }}</a>
</div>
<div class="wawi-toolbar__spacer"></div>

View file

@ -231,7 +231,7 @@ test('uebersicht rendert rohstoff mit bestand', function () {
ap10MakeStock($ing, $location, $supplier, $user, ['received_quantity' => 2578]);
$this->actingAs($user, 'user');
$response = $this->get(route('admin.inventory.raw-material-stock.index'));
$response = $this->get(route('admin.inventory.raw-material-stock.index', ['scope' => 'all']));
$response->assertSuccessful();
$response->assertSee('Rohstoffbestand');
@ -283,7 +283,7 @@ test('kein-rohstoffbestand-flag blendet inci aus uebersicht und kritisch-zaehler
expect(app(InventoryService::class)->criticalIngredientCount())->toBe(1);
$this->actingAs($user, 'user');
$response = $this->get(route('admin.inventory.raw-material-stock.index'));
$response = $this->get(route('admin.inventory.raw-material-stock.index', ['scope' => 'all']));
$response->assertSuccessful();
$response->assertSee('AP20-Sichtbar');
@ -307,18 +307,18 @@ test('modus recipe zeigt nur rohstoffe aus herstellerrezepturen aktiver produkte
$this->actingAs($user, 'user');
// Default „alle": beide sichtbar, Pills werden gerendert
// Default (Kundenentscheid 17.07.2026): nur Rohstoffe aus Herstellerrezepturen
$this->get(route('admin.inventory.raw-material-stock.index'))
->assertSuccessful()
->assertSee('AP20-InRezeptur')
->assertSee('AP20-Neuentwicklung')
->assertDontSee('AP20-Neuentwicklung')
->assertSee('Nur aus Herstellerrezepturen');
// Modus „recipe": nur der Rezeptur-Rohstoff
$this->get(route('admin.inventory.raw-material-stock.index', ['scope' => 'recipe']))
// Modus „alle": beide sichtbar
$this->get(route('admin.inventory.raw-material-stock.index', ['scope' => 'all']))
->assertSuccessful()
->assertSee('AP20-InRezeptur')
->assertDontSee('AP20-Neuentwicklung');
->assertSee('AP20-Neuentwicklung');
});
test('letzter netto-preis je lieferant kommt aus dem neuesten einkauf', function () {