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:
parent
1484988832
commit
522373dae4
6 changed files with 192 additions and 5 deletions
|
|
@ -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),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
140
resources/docs/testleitfaden.md
Normal file
140
resources/docs/testleitfaden.md
Normal 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).
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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']);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue