Warenwirtschaft: Menüpunkt „Leitfaden" (Einstellungen) rendert testleitfaden.md

- Route admin/inventory/guide (superadmin) + NoticeController::guide()
- NoticeController auf gemeinsamen Markdown-Renderer umgestellt
  (Titel/Untertitel parametrisiert, View wiederverwendet)
- Sidenav: „Leitfaden" unter Einstellungen neben „Hinweise"
- resources/docs/testleitfaden.md eingecheckt; Tests ergänzt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-07 10:14:42 +00:00
parent 1484988832
commit 522373dae4
6 changed files with 192 additions and 5 deletions

View file

@ -11,10 +11,32 @@ class NoticeController extends Controller
{
public function index(): View
{
$path = resource_path('docs/hinweise.md');
$markdown = File::exists($path) ? File::get($path) : __('Noch keine Hinweise hinterlegt.');
return $this->renderMarkdown(
'hinweise.md',
__('Hinweise'),
__('Allgemeine Hinweise zur Warenwirtschaft'),
__('Noch keine Hinweise hinterlegt.')
);
}
public function guide(): View
{
return $this->renderMarkdown(
'testleitfaden.md',
__('Leitfaden'),
__('Testleitfaden durch die neuen Funktionen der Warenwirtschaft'),
__('Noch kein Leitfaden hinterlegt.')
);
}
protected function renderMarkdown(string $file, string $title, string $subtitle, string $fallback): View
{
$path = resource_path('docs/'.$file);
$markdown = File::exists($path) ? File::get($path) : $fallback;
return view('admin.inventory.notices.index', [
'title' => $title,
'subtitle' => $subtitle,
'content' => Str::markdown($markdown),
]);
}

View file

@ -0,0 +1,140 @@
# Testleitfaden Warenwirtschaft — Anforderungsrunde 12.06.2026
> **Stand:** 06.07.2026 · Alle Punkte aus `nächsten Anforderungen an die WaWi.md` (12.06.2026) sind umgesetzt.
> Dieser Leitfaden führt einmal durch alle neuen Funktionen — in der Reihenfolge eines typischen Arbeitsablaufs, sodass jede Stelle direkt im Browser nachgeschaut und getestet werden kann.
> Die fachlichen Erklärungen dazu stehen wie gewohnt in der App unter den **Hinweisen zur Warenwirtschaft** (`resources/docs/hinweise.md`).
---
## 1. Einstellungen: Lagerort-Bausteine & Ausschuss-Gründe
**Wo:** Menü **Einstellungen → Allgemein** (`/admin/inventory/general`)
**Neu:**
- Drei neue Karten **Räume**, **Regale**, **Fächer** — die zentralen Bausteine für Lagerorte nach dem Schema *Raum 1 | Regal 3 | A*.
- Karte **Ausschuss-Gründe** (aus Runde 12.06.): Gründe für Ausgang/Ausschuss selbst anlegen, umbenennen, deaktivieren.
**So testen:**
1. Je einen Raum (z. B. „Raum 1"), ein Regal („Regal 3") und ein Fach („A") anlegen.
2. Einen Eintrag umbenennen → die Änderung wirkt übergeordnet überall, wo der Baustein verwendet wird.
3. Einen Ausschuss-Grund anlegen (z. B. „Verkleckert") — der taucht später in Schritt 8 auf.
---
## 2. INCI-Ebene: Lieferanten mit Bestellweg, Alternativname, Lagerort
**Wo:** Menü **Produkte → Inhaltsstoffe**, einen INCI öffnen (z. B. ZinClear XP) (`/admin/product/ingredients`)
**Neu:**
- **Lieferanten-Zeilen mit „+ weiteren Lieferanten anlegen":** je Zeile Lieferant + **URL**. URL eingetragen ⇒ Bestellung im Online-Shop; URL leer ⇒ Bestellung per Mail.
- Feld **Alternativer Name** (Handelsname, z. B. LECITHIN → *Phospholipon 80 H*).
- Drei Dropdowns **Raum / Regal / Fach** (die Bausteine aus Schritt 1).
- Checkbox **„Kein Rohstoffbestand"** (für Allergene u. Ä. — wirkt in Schritt 3).
**So testen:**
1. Beim INCI zwei Lieferanten anlegen: einen **mit** URL (z. B. Dragonspice-Shoplink), einen **ohne**.
2. Alternativnamen und Lagerort (Raum 1 | Regal 3 | A) setzen, speichern.
3. **Filter „nur aktive"** oben in der INCI-Liste anhaken → es bleiben nur INCI übrig, die in der Herstellerrezeptur mindestens eines aktiven Produkts vorkommen.
**Ergebnis prüfen:** In der Rohstoffbestand-Detailansicht (Schritt 3) erscheint je Lieferant der Button **„Zum Shop"** bzw. **„Per Mail"**; Alternativname und Lagerort werden im Kopf angezeigt.
---
## 3. Rohstoffbestand: Filter & Ausblendungen
**Wo:** Menü **Rohstoffbestand** (`/admin/inventory/raw-material-stock`)
**Neu:**
- Umschalter **„Alle" / „Nur aus Herstellerrezepturen"** oben in der Übersicht.
- INCI mit Häkchen **„Kein Rohstoffbestand"** erscheinen gar nicht (auch nicht im roten Kritisch-Zähler).
**So testen:**
1. Auf „Nur aus Herstellerrezepturen" klicken → Rohstoffe, die nur für Neuentwicklungen eingekauft wurden, verschwinden; auf „Alle" sind sie wieder da.
2. Bei einem Test-INCI „Kein Rohstoffbestand" anhaken → er verschwindet in beiden Modi.
---
## 4. Produktebene: Rezeptur-Phasen & Chargen-Präfix
**Wo:** Menü **Produkte → Übersicht**, ein Produkt bearbeiten (z. B. Tattoocreme)
**Neu:**
- **Hersteller-Rezeptur in Phasen** (Phase A, B, …): je Phase eigene Rohstoffe, ein **Notizfeld** (z. B. „auf 70 °C erhitzen"), Umbenennen möglich; Rohstoffe lassen sich per Drag & Drop **innerhalb und zwischen Phasen** verschieben. Die 100-%-Summe gilt weiterhin **über alle Phasen zusammen**.
- Feld **„Chargen-Nr.-Präfix"** in der Warenwirtschaft-Karte (z. B. `TP1` = Tattoopflege + Produkt-Nr. 1).
- **Lieferbestand** (aus Runde 12.06.): Feld „Wieder lieferbar ab" als **Datum**; das Produkt bleibt kaufbar, im Shop erscheint die tagesaktuell berechnete Anzahl Tage.
**So testen:**
1. In der Hersteller-Rezeptur „+ Neue Phase" klicken, Rohstoffe zuordnen, eine Notiz eintragen, einen Rohstoff in eine andere Phase ziehen → Summe unten muss weiter 100 % ergeben.
2. Präfix `TP1` eintragen und speichern → wirkt in Schritt 7 (Produktion).
3. „Wieder lieferbar ab" auf ein Datum in 12 Tagen setzen → Produkt im Shop ansehen: kaufbar, mit Tage-Hinweis.
4. Produkt **kopieren** → Phasenstruktur wird mitkopiert.
---
## 5. Einkauf & Wareneingang: gleiche Charge zusammenführen
**Wo:** Menü **Einkauf & Wareneingang** (`/admin/inventory/stock-entries`)
**Neu:**
- Beim **Buchen des Wareneingangs** (Stufe 2) prüft die Maske beim Eintippen der Chargennummer automatisch, ob diese Charge für den Rohstoff **schon im Haus** ist: Hinweis „Charge bereits im Haus" mit Restbestand, Lagerort und MHD — **MHD und Qualität werden vorbelegt** (bleiben überschreibbar).
- Der Einkaufsvorgang bleibt trotzdem **separat erfasst** (eigener Eintrag mit Preis und Datum).
**So testen:**
1. Sonnenblumenöl bestellen und mit Charge `XY-123` als eingegangen buchen.
2. Sonnenblumenöl **erneut** bestellen und beim Buchen wieder `XY-123` eintippen → der blaue Hinweis erscheint, MHD ist vorbelegt.
3. Danach Schritt 6 ansehen: die Charge erscheint dort **einmal mit summiertem Rest**.
---
## 6. Rohstoffbestand-Detail: Chargen summiert
**Wo:** Menü **Rohstoffbestand**, den Rohstoff aus Schritt 5 anklicken
**Neu:** Tabelle **„Verfügbare Chargen"** zeigt je Chargennummer **eine Zeile mit Summen-Rest** und dem Vermerk „2 Einkäufe"; darunter die einzelnen Einkäufe als eingerückte Unterzeilen („Einkauf vom …"). Außerdem: Spalte **„Letzter Einkauf (Netto/kg)"** je Lieferant und die Bestell-Buttons aus Schritt 2.
**So testen:** Restbestand der Charge = Summe beider Einkäufe; beide Einkaufszeilen mit eigenem Datum sichtbar.
---
## 7. Produktion: neue Maske mit Produktionsergebnis
**Wo:** Menü **Produktion → Produktionen → Neue Produktion** (`/admin/inventory/productions/create`)
**Neu (Kernstück der Runde):**
- Oben nur noch **Planung**: „Geplante Stückzahl (Planung)" — **bucht keinen Bestand**.
- **INCI-Liste** der Herstellerrezeptur: Name, **Alternativer Name**, Qualität, Anteil %, Gramm gesamt, **Regal** — mit Summenzeile.
- **Chargen-Zuordnung nach Phasen** gegliedert (inkl. Phasen-Notiz) mit **Status-Ampel** je Rohstoff (grün = eingetragene Menge entspricht dem Soll).
- Das **Chargen-Dropdown** zeigt zusammengeführte Chargen mit Summen-Rest (z. B. „… — Rest 8.000 g (2 Einkäufe)"); der Verbrauch wird automatisch nach frühestem MHD auf die Einkäufe verteilt.
- Ganz unten **„Produktionsergebnis"**: Zeilen mit Produkt + produzierte Stückzahl + **Chargen-Nr.** (vorbelegt aus Präfix + Produktionsdatum, z. B. `TP1150626` — editierbar), „+ weitere Ergebniszeile" für **mehrere Output-Produkte**.
**So testen (Beispiel aus der Anforderung):**
1. 50 × „50 ml Deobalm" **planen**, Chargen zuordnen (Ampeln grün).
2. Im Produktionsergebnis eintragen: **40 × 50 ml** und (zweite Zeile) **50 × 5 ml** — Chargen-Nr. wird je Zeile automatisch vorgeschlagen.
3. Speichern → Detailseite zeigt die Karte „Produktionsergebnis".
4. **Produktbestand prüfen** (Schritt 8): 50-ml-Produkt +40, 5-ml-Produkt +50 — **nicht** +50 aus der Planung.
5. Produktion **bearbeiten**, Ergebnis ändern → der Produktbestand korrigiert sich als Differenz-Gegenbuchung (Historie bleibt vollständig).
6. Rohstoffbestand (Schritt 6) prüfen: abgezogen wurden die **je Charge eingetragenen Gramm** — unabhängig vom Ergebnis.
---
## 8. Produktbestand & Ausschuss (aus Runde 12.06., bereits länger live)
**Wo:** Menü **Produktbestand → Übersicht** (`/admin/inventory/product-stock`) und **Ausgang / Ausschuss** (`/admin/inventory/stock-disposals`)
**Neu seit 12.06.:**
- Übersicht **standardmäßig nach Dringlichkeit sortiert**; alle **vier Status-Kacheln** oben sind klickbare Filter.
- Spalte **„Verbrauch pro Monat"** (Durchschnitt der letzten 6 Monate).
- Auf Produktebene Häkchen **„Im Produktbestand anzeigen"** — z. B. Logo-Etiketten-Abrechnungen ausblendbar.
- Ausschuss-Erfassung nutzt die **selbst gepflegten Gründe** aus Schritt 1; Chargen-Auswahl setzt den Lagerort automatisch.
**So testen:** Kacheln durchklicken, ein Produkt per Häkchen ausblenden, einen Ausschuss mit eigenem Grund buchen und in der Historie wiederfinden.
---
## Hinweis am Rande
Die Listen **Einkauf & Wareneingang** und **Produktionen** zeigen aus Performance-Gründen die neuesten 500 Einträge; darüber erscheint ein Link **„Alle anzeigen"**. Offene Bestellungen sind immer vollständig gelistet.
## Was bewusst noch nicht dabei ist (nachgelagert)
Shop-Bestandsabzug beim Versand (inkl. Sets), Audit-Trail, 2FA, blockbasierte Rechte und die WaWi-Einstellungsseite folgen in der nächsten Runde — hier fließen zuvor noch weitere Anforderungen ein (siehe Entwicklungsplan V6.0, §3).

View file

@ -5,8 +5,8 @@
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Hinweise') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Allgemeine Hinweise zur Warenwirtschaft') }}</p>
<h1 class="wawi-page-head__title">{{ $title ?? __('Hinweise') }}</h1>
<p class="wawi-page-head__subtitle">{{ $subtitle ?? __('Allgemeine Hinweise zur Warenwirtschaft') }}</p>
</div>
</div>

View file

@ -322,7 +322,8 @@
'admin/inventory/locations*',
'admin/inventory/material-qualities*',
'admin/inventory/packaging-materials*',
'admin/inventory/notices*')) open @endif">
'admin/inventory/notices*',
'admin/inventory/guide*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-cog"></i>
<div>Einstellungen</div>
@ -360,6 +361,12 @@
<div>{{ __('Hinweise') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/inventory/guide*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.guide') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-compass"></i>
<div>{{ __('Leitfaden') }}</div>
</a>
</li>
</ul>
</li>
@endif

View file

@ -288,6 +288,7 @@ Route::domain(config('app.domain'))->group(function () {
Route::resource('packaging-materials', InventoryPackagingMaterialController::class)->except(['show']);
Route::get('general', [InventoryGeneralSettingController::class, 'index'])->name('general');
Route::get('notices', [InventoryNoticeController::class, 'index'])->name('notices');
Route::get('guide', [InventoryNoticeController::class, 'guide'])->name('guide');
Route::resource('tax-rates', InventoryTaxRateController::class)->except(['index', 'show']);
Route::resource('delivery-times', InventoryDeliveryTimeController::class)->except(['index', 'show']);
Route::resource('disposal-reasons', InventoryDisposalReasonController::class)->except(['index', 'show']);

View file

@ -39,3 +39,20 @@ test('nicht-superadmin hat keinen zugriff auf hinweise-seite', function () {
$this->get(route('admin.inventory.notices'))->assertRedirect('/home');
});
test('leitfaden-seite rendert testleitfaden.md als html', function () {
$this->actingAs(noticeUser(8), 'user');
$response = $this->get(route('admin.inventory.guide'));
$response->assertSuccessful();
$response->assertSee('Leitfaden');
$response->assertSee('Testleitfaden Warenwirtschaft', false);
$response->assertSee('<h2', false);
});
test('nicht-superadmin hat keinen zugriff auf leitfaden-seite', function () {
$this->actingAs(noticeUser(7), 'user');
$this->get(route('admin.inventory.guide'))->assertRedirect('/home');
});