Warenwirtschaft: einheitliches UI-Design (AP-19)

- Zentrale, wiederverwendbare Design-Partial wawi-ui.blade.php (gescoptes Inline-CSS, kein Build noetig)
- Bausteine: Seitenkopf, Kennzahlen-Kacheln, Karten, Toolbar/Suche, aufgeraeumte Tabellen, Status-Pills, Datenblatt-Definitionsliste, Name-Zelle mit fester Icon-Spalte, Leer-Zustaende
- Umgestellt: alle Uebersicht-/Listen- und Detailseiten unter admin/inventory
- Responsive: Detail-Datenblaetter brechen unter 768px gestapelt um (Label oben, Wert linksbuendig); Icon-Spalte verhindert Versatz bei Zeilenumbruch
- Entwicklungsplan um AP-19 + UI-Konvention ergaenzt

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin Adametz 2026-06-03 13:09:55 +00:00
parent 3ee2d756e9
commit a8f6fef38e
21 changed files with 1609 additions and 955 deletions

View file

@ -508,7 +508,7 @@ Pro `shopping_order_item` Produkt laden:
## 6. Empfohlene Sofort-Reihenfolge (nächste Schritte)
**Erledigt:** AP-00, AP-01, AP-04 (+ AP-04.1), AP-05, AP-06 (+ Nachtrag), AP-07 (+ AP-07.1), AP-08, AP-09 (+ AP-09.1), AP-02 (Sets via Pivot), AP-03 („Nicht vorrätig"), AP-10 (Rohstoffbestand), AP-11 (Produktbestand + Historie), AP-12 (Ausgang / Ausschuss), AP-18 (Platzhalter, laufend zu pflegen).
**Erledigt:** AP-00, AP-01, AP-04 (+ AP-04.1), AP-05, AP-06 (+ Nachtrag), AP-07 (+ AP-07.1), AP-08, AP-09 (+ AP-09.1), AP-02 (Sets via Pivot), AP-03 („Nicht vorrätig"), AP-10 (Rohstoffbestand), AP-11 (Produktbestand + Historie), AP-12 (Ausgang / Ausschuss), AP-18 (Platzhalter, laufend zu pflegen), AP-19 (UI-Vereinheitlichung Warenwirtschaft).
**➡️ Hier geht es weiter:**
1. **AP-13** (Shop-Anbindung: Bestandsabzug beim Versand inkl. Sets) **Entwicklungskonzept liegt vor** (siehe AP-13 in §4). Kernbefund: Bestellungen liegen bereits im System (`shopping_orders`/`shopping_order_items`, Mapping über `products.wp_number`), Versandstatus wird zentral in `SalesController@store` gesetzt → Abzug kann ohne neue Tabelle auf der AP-11-Infrastruktur aufsetzen. **Vorab nur noch zu bestätigen:** Szenario A (Versand wird im Backend gebucht empfohlen, kein neuer Woo-Eingriff) vs. Szenario B (WooCommerce meldet Versand per Webhook/REST-Pull zurück) sowie die Varianten-/Hauptprodukt-Detailfrage. Danach Folge-APs (AP-14AP-17).
@ -516,9 +516,26 @@ Pro `shopping_order_item` Produkt laden:
---
### AP-19 — UI-Vereinheitlichung Warenwirtschaft (Darstellung)
> **Status:** Erledigt (03.06.2026). Rein gestalterisch, keine fachliche Logik geändert.
Ein einheitliches, modernes Erscheinungsbild für alle Warenwirtschafts-Seiten. Zentrale, wiederverwendbare Design-Partial **`resources/views/admin/inventory/partials/wawi-ui.blade.php`** (per `@once` eingebundenes, auf `.wawi-page` gescoptes Inline-CSS **kein** SCSS-Build/`npm run` nötig).
**Bausteine:** Seitenkopf (`wawi-page-head`, Titel + Untertitel + Aktionsbereich), Kennzahlen-Kacheln (`wawi-stats`/`wawi-stat`, teils klickbar als Filter), Karten (`wawi-card` + `wawi-card__header`/`__footer`), Toolbar mit Such-Feld (`wawi-toolbar`/`wawi-search`), aufgeräumte Tabellen (`wawi-table` mit Uppercase-Köpfen, Hover, schmaler Status-Akzentleiste links), Status-Pills (`wawi-pill--ok/--warning/--danger`), Datenblatt-Definitionsliste (`wawi-deflist`/`wawi-deflist__item`), Name-Zelle mit fester Icon-Spalte (`wawi-name-cell`), einheitlicher Leer-Zustand (`wawi-empty`).
**Responsive:** Detail-Datenblätter brechen unter 768 px auf einspaltig um Label klein oben, Wert linksbündig darunter. Die Name-Zelle hält das Icon in eigener Spalte, damit umgebrochener Text nicht unter das Icon rutscht.
**Umgestellt:** alle Übersicht-/Listen- und Detailseiten unter `admin/inventory` (Produktbestand + Historie, Rohstoffbestand inkl. Detail, Ausgang/Ausschuss, Einkauf & Wareneingang inkl. Detail, Produktion inkl. Detail, Hinweise, Produktentwicklung, Einstellungen sowie die Stammdaten-Listen Lieferanten, Lieferanten-Kategorien, Verpackungsmaterial/-artikel, Materialqualität, Lagerorte).
**Offen (bewusst):** Die reinen **Formularseiten** (Anlegen/Bearbeiten) nutzen noch den alten `h4`-Kopf; Angleichung an `wawi-page-head` als Folgeaufgabe vorgemerkt.
---
## 7. Pflege dieses Dokuments
- Jedes abgeschlossene AP hier mit Datum + Kurzbeschreibung + Test-Status protokollieren (analog Umsetzungsprotokoll in `entwicklungsplan.md`).
- Bei DB-Änderungen: Migration-Dateinamen referenzieren; bei Modellen Casts in `casts()`-Methode pflegen (L11-Konvention).
- Vor jedem Commit: `vendor/bin/pint --dirty` und betroffene Tests (`php artisan test --filter=...`).
- **UI-Konvention Datumsfelder:** Datumsfelder in Formularen immer als `<input type="text" class="form-control datepicker-base" value="dd.mm.yyyy">` (kein natives `type="date"`). Der Datepicker wird global über `public/js/custom.js` auf `.datepicker-base` gebunden (Format `dd.mm.yyyy`, deutsche Locale). Modellwerte mit `->format('d.m.Y')` ausgeben; Backend parst `d.m.Y` über `Carbon::parse` bzw. die `date`-Validierungsregel.
- **UI-Konvention Warenwirtschaft (AP-19):** Neue Seiten unter `admin/inventory` verwenden das Design-System aus `resources/views/admin/inventory/partials/wawi-ui.blade.php` (`@include('admin.inventory.partials.wawi-ui')` + Inhalt in `<div class="wawi-page">`). Bausteine: `wawi-page-head`, `wawi-stats`/`wawi-stat`, `wawi-card`, `wawi-toolbar`/`wawi-search`, `wawi-table`, `wawi-pill`, `wawi-deflist`, `wawi-name-cell`, `wawi-empty`. Status immer über `wawi-pill` (ok/warning/danger) statt Bootstrap-`badge-pill`.

View file

@ -1,16 +1,23 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
@include('admin.inventory.partials.wawi-ui')
<div class="card mb-4">
<h6 class="card-header d-flex justify-content-between align-items-center">
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Einstellungen') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Umsatzsteuersätze und Lieferzeit-Vorlagen') }}</p>
</div>
</div>
<div class="wawi-card">
<div class="wawi-card__header">
<span>{{ __('Umsatzsteuersätze') }}</span>
<a href="{{ route('admin.inventory.tax-rates.create') }}"
class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="table table-striped table-bordered wawi-table mb-0">
<a href="{{ route('admin.inventory.tax-rates.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</div>
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -33,9 +40,9 @@
<td>{{ number_format($taxRate->percent, 2, ',', '.') }}&nbsp;%</td>
<td>
@if ($taxRate->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
<span class="wawi-pill wawi-pill--ok">{{ __('Aktiv') }}</span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
<span class="wawi-pill wawi-pill--danger">{{ __('Inaktiv') }}</span>
@endif
</td>
<td>
@ -50,8 +57,7 @@
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-muted">
{{ __('Noch keine Umsatzsteuersätze angelegt.') }}</td>
<td colspan="5"><div class="wawi-empty">{{ __('Noch keine Umsatzsteuersätze angelegt.') }}</div></td>
</tr>
@endforelse
</tbody>
@ -59,14 +65,13 @@
</div>
</div>
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<div class="wawi-card">
<div class="wawi-card__header">
<span>{{ __('Lieferzeit-Vorlagen') }}</span>
<a href="{{ route('admin.inventory.delivery-times.create') }}"
class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="table table-striped table-bordered wawi-table mb-0">
<a href="{{ route('admin.inventory.delivery-times.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</div>
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -89,9 +94,9 @@
<td>{{ $deliveryTime->days !== null ? $deliveryTime->days : '' }}</td>
<td>
@if ($deliveryTime->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
<span class="wawi-pill wawi-pill--ok">{{ __('Aktiv') }}</span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
<span class="wawi-pill wawi-pill--danger">{{ __('Inaktiv') }}</span>
@endif
</td>
<td>
@ -107,12 +112,12 @@
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-muted">
{{ __('Noch keine Lieferzeit-Vorlagen angelegt.') }}</td>
<td colspan="5"><div class="wawi-empty">{{ __('Noch keine Lieferzeit-Vorlagen angelegt.') }}</div></td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View file

@ -1,14 +1,21 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Lagerorte') }}</span>
<a href="{{ route('admin.inventory.locations.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Lagerorte') }}</h1>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.locations.create') }}" class="btn btn-sm btn-primary">
<span class="fas fa-plus mr-1"></span>{{ __('Neu anlegen') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -28,9 +35,9 @@
<td>{{ $value->name }}</td>
<td data-sort="{{ $value->active ? 1 : 0 }}">
@if($value->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
<span class="wawi-pill wawi-pill--ok">{{ __('Aktiv') }}</span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
<span class="wawi-pill wawi-pill--danger">{{ __('Inaktiv') }}</span>
@endif
</td>
<td>
@ -47,6 +54,7 @@
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {

View file

@ -1,14 +1,21 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Rohstoffqualität') }}</span>
<a href="{{ route('admin.inventory.material-qualities.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Rohstoffqualität') }}</h1>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.material-qualities.create') }}" class="btn btn-sm btn-primary">
<span class="fas fa-plus mr-1"></span>{{ __('Neu anlegen') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -41,6 +48,7 @@
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({

View file

@ -1,14 +1,21 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Hinweise') }}</h4>
@include('admin.inventory.partials.wawi-ui')
<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>
</div>
</div>
<div class="card">
<div class="card wawi-card">
<div class="card-body wawi-notices">
{!! $content !!}
</div>
</div>
</div>
@endsection
@section('styles')

View file

@ -1,14 +1,21 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ $pageTitle }}</span>
<a href="{{ route('admin.inventory.packaging-items.create', ['category' => $category]) }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ $pageTitle }}</h1>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.packaging-items.create', ['category' => $category]) }}" class="btn btn-sm btn-primary">
<span class="fas fa-plus mr-1"></span>{{ __('Neu anlegen') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -42,9 +49,9 @@
</td>
<td data-sort="{{ $value->active ? 1 : 0 }}">
@if($value->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
<span class="wawi-pill wawi-pill--ok">{{ __('Aktiv') }}</span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
<span class="wawi-pill wawi-pill--danger">{{ __('Inaktiv') }}</span>
@endif
</td>
<td>
@ -61,6 +68,7 @@
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({

View file

@ -1,14 +1,21 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Verpackungsmaterial') }}</span>
<a href="{{ route('admin.inventory.packaging-materials.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Verpackungsmaterial') }}</h1>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.packaging-materials.create') }}" class="btn btn-sm btn-primary">
<span class="fas fa-plus mr-1"></span>{{ __('Neu anlegen') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -41,6 +48,7 @@
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({

View file

@ -0,0 +1,283 @@
@once
<style>
/* =====================================================================
Warenwirtschaft einheitliches UI (Vorschlag / Prototyp)
Alles auf ".wawi-page" gescoped, damit nichts anderes beeinflusst wird.
===================================================================== */
.wawi-page {
--wawi-accent: #6b8e5a;
--wawi-accent-soft: rgba(107, 142, 90, .12);
--wawi-radius: .625rem;
--wawi-border: rgba(24, 28, 33, .08);
--wawi-shadow: 0 1px 2px rgba(24, 28, 33, .04), 0 2px 8px rgba(24, 28, 33, .04);
--wawi-muted: #8a909d;
}
/* --- Seitenkopf -------------------------------------------------- */
.wawi-page-head {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
gap: 1rem;
margin-bottom: 1.25rem;
}
.wawi-page-head__title {
margin: 0;
font-size: 1.375rem;
font-weight: 600;
line-height: 1.2;
}
.wawi-page-head__subtitle {
margin: .35rem 0 0;
color: var(--wawi-muted);
font-size: .875rem;
}
.wawi-page-head__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .5rem;
}
/* --- Kennzahlen-Kacheln ------------------------------------------ */
.wawi-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 1rem;
margin-bottom: 1.25rem;
}
.wawi-stat {
position: relative;
display: flex;
align-items: center;
gap: .9rem;
padding: 1rem 1.1rem;
background: #fff;
border: 1px solid var(--wawi-border);
border-radius: var(--wawi-radius);
box-shadow: var(--wawi-shadow);
}
.wawi-stat__icon {
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2.75rem;
height: 2.75rem;
border-radius: .55rem;
font-size: 1.1rem;
background: var(--wawi-accent-soft);
color: var(--wawi-accent);
}
.wawi-stat__body { min-width: 0; }
.wawi-stat__value {
font-size: 1.4rem;
font-weight: 600;
line-height: 1.1;
}
.wawi-stat__label {
color: var(--wawi-muted);
font-size: .8rem;
text-transform: uppercase;
letter-spacing: .03em;
}
.wawi-stat--ok .wawi-stat__icon { background: rgba(40, 199, 111, .14); color: #28c76f; }
.wawi-stat--warning .wawi-stat__icon { background: rgba(255, 171, 0, .16); color: #ff9f00; }
.wawi-stat--danger .wawi-stat__icon { background: rgba(255, 91, 92, .14); color: #ff5b5c; }
.wawi-stat.is-clickable { cursor: pointer; transition: box-shadow .15s ease, transform .15s ease; }
.wawi-stat.is-clickable:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(24,28,33,.1); }
.wawi-stat.is-active { border-color: var(--wawi-accent); box-shadow: 0 0 0 1px var(--wawi-accent); }
/* --- Karte + Toolbar --------------------------------------------- */
.wawi-card {
background: #fff;
border: 1px solid var(--wawi-border);
border-radius: var(--wawi-radius);
box-shadow: var(--wawi-shadow);
overflow: hidden;
}
.wawi-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .75rem;
padding: 1rem 1.25rem;
border-bottom: 1px solid var(--wawi-border);
}
.wawi-search {
position: relative;
flex: 1 1 260px;
max-width: 360px;
}
.wawi-search .fas {
position: absolute;
top: 50%;
left: .85rem;
transform: translateY(-50%);
color: var(--wawi-muted);
font-size: .85rem;
pointer-events: none;
}
.wawi-search .form-control {
padding-left: 2.25rem;
border-radius: 2rem;
}
.wawi-toolbar__spacer { flex: 1 1 auto; }
.wawi-card__header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: .5rem;
padding: .9rem 1.25rem;
border-bottom: 1px solid var(--wawi-border);
font-weight: 600;
}
.wawi-card__footer {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .5rem;
padding: .85rem 1.25rem;
border-top: 1px solid var(--wawi-border);
background: rgba(24, 28, 33, .015);
}
.wawi-card + .wawi-card { margin-top: 1.25rem; }
/* --- Tabelle ----------------------------------------------------- */
.wawi-page .wawi-table { margin-bottom: 0; }
.wawi-page .wawi-table thead th {
border-top: 0;
border-bottom: 1px solid var(--wawi-border);
background: rgba(24, 28, 33, .02);
color: var(--wawi-muted);
font-size: .72rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .04em;
padding-top: .8rem;
padding-bottom: .8rem;
white-space: nowrap;
}
.wawi-page .wawi-table tbody td {
vertical-align: middle;
padding-top: .7rem;
padding-bottom: .7rem;
border-top: 1px solid var(--wawi-border);
}
.wawi-page .wawi-table tbody tr { transition: background-color .12s ease; }
.wawi-page .wawi-table tbody tr:hover { background-color: rgba(24, 28, 33, .025); }
/* Status nur über schmale Akzent-Leiste links statt komplettem Zeilen-Rot */
.wawi-page .wawi-table tbody tr.is-warning td:first-child { box-shadow: inset .25rem 0 0 #ff9f00; }
.wawi-page .wawi-table tbody tr.is-danger td:first-child { box-shadow: inset .25rem 0 0 #ff5b5c; }
.wawi-thumb {
width: 42px;
height: 42px;
border-radius: .5rem;
object-fit: cover;
background: rgba(24, 28, 33, .03);
border: 1px solid var(--wawi-border);
}
.wawi-thumb--empty {
display: flex;
align-items: center;
justify-content: center;
color: var(--wawi-muted);
}
.wawi-item-name { font-weight: 600; }
.wawi-item-sub { color: var(--wawi-muted); font-size: .8rem; }
/* Name-Zelle mit fester Icon-Spalte, damit umgebrochener Text nicht unter das Icon rutscht */
.wawi-name-cell {
display: flex;
align-items: flex-start;
gap: .5rem;
}
.wawi-name-cell__icon {
flex: 0 0 auto;
margin-top: .15rem;
}
/* --- Status-Pills ------------------------------------------------ */
.wawi-pill {
display: inline-flex;
align-items: center;
gap: .35rem;
padding: .2rem .6rem;
border-radius: 2rem;
font-size: .75rem;
font-weight: 600;
line-height: 1.4;
}
.wawi-pill::before {
content: "";
width: .5rem;
height: .5rem;
border-radius: 50%;
background: currentColor;
}
.wawi-pill--ok { background: rgba(40, 199, 111, .14); color: #1f9d57; }
.wawi-pill--warning { background: rgba(255, 171, 0, .16); color: #c77d00; }
.wawi-pill--danger { background: rgba(255, 91, 92, .14); color: #d63031; }
/* --- Aktionen ---------------------------------------------------- */
.wawi-actions {
display: inline-flex;
align-items: center;
gap: .35rem;
white-space: nowrap;
}
.wawi-actions .btn { margin: 0; }
/* --- Datenblatt / Definitionsliste ------------------------------- */
.wawi-deflist {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0 2.5rem;
margin: 0;
}
.wawi-deflist__item {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
padding: .7rem 0;
border-bottom: 1px solid var(--wawi-border);
}
.wawi-deflist__label {
flex: 0 0 auto;
color: var(--wawi-muted);
font-size: .8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .03em;
}
.wawi-deflist__value {
text-align: right;
font-weight: 500;
}
.wawi-deflist__value .text-muted { font-weight: 400; }
/* Volle Breite erzwingen (z. B. Notizen) */
.wawi-deflist__item--wide { grid-column: 1 / -1; }
@media (max-width: 767.98px) {
.wawi-deflist { grid-template-columns: minmax(0, 1fr); }
.wawi-deflist__item {
flex-direction: column;
align-items: flex-start;
gap: .15rem;
padding: .55rem 0;
}
.wawi-deflist__label { font-size: .72rem; }
.wawi-deflist__value { text-align: left; }
}
/* --- Leerer Zustand ---------------------------------------------- */
.wawi-empty {
padding: 3rem 1rem;
text-align: center;
color: var(--wawi-muted);
}
.wawi-empty .fas { font-size: 1.75rem; opacity: .5; margin-bottom: .5rem; }
</style>
@endonce

View file

@ -1,9 +1,16 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Produktentwicklung') }}</h4>
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Produktentwicklung') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Bereich in Vorbereitung') }}</p>
</div>
</div>
<div class="card">
<div class="card wawi-card">
<div class="card-body">
<div class="alert alert-info mb-0">
<h6 class="alert-heading font-weight-bold">{{ __('Briefing ausstehend') }}</h6>
@ -16,4 +23,5 @@
</div>
</div>
</div>
</div>
@endsection

View file

@ -6,53 +6,67 @@
5 => __('Mai'), 6 => __('Juni'), 7 => __('Juli'), 8 => __('August'),
9 => __('September'), 10 => __('Oktober'), 11 => __('November'), 12 => __('Dezember'),
];
$hasFilter = $filters['product_id'] || $filters['direction'] || $filters['reason'] || $filters['month'] || $filters['year'];
@endphp
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header">{{ __('Produktbestand Historie') }}</h6>
<div class="card-body pb-0">
<form method="get" action="{{ route('admin.inventory.product-stock.history') }}">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Produktbestand Historie') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Alle Bestandsbewegungen (Eingang / Ausgang)') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.product-stock.index') }}" class="btn btn-outline-secondary btn-sm">
<span class="fas fa-arrow-left mr-1"></span>{{ __('Zum Bestand') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="wawi-toolbar" style="align-items:flex-end;">
<form method="get" action="{{ route('admin.inventory.product-stock.history') }}" class="mb-0 w-100">
<div class="form-row">
<div class="form-group col-md-3">
<label class="small text-muted">{{ __('Produkt') }}</label>
<select name="product_id" class="form-control" onchange="this.form.submit()">
<div class="form-group col-md-3 mb-2">
<label class="small text-muted mb-1">{{ __('Produkt') }}</label>
<select name="product_id" class="form-control form-control-sm" onchange="this.form.submit()">
<option value="">{{ __('Filter aus') }}</option>
@foreach ($products as $p)
<option value="{{ $p->id }}" @selected($filters['product_id'] === $p->id)>{{ $p->name }}</option>
@endforeach
</select>
</div>
<div class="form-group col-md-2">
<label class="small text-muted">{{ __('Eingang / Ausgang') }}</label>
<select name="direction" class="form-control" onchange="this.form.submit()">
<div class="form-group col-md-2 mb-2">
<label class="small text-muted mb-1">{{ __('Eingang / Ausgang') }}</label>
<select name="direction" class="form-control form-control-sm" onchange="this.form.submit()">
<option value="">{{ __('Filter aus') }}</option>
<option value="in" @selected($filters['direction'] === 'in')>{{ __('Eingang') }}</option>
<option value="out" @selected($filters['direction'] === 'out')>{{ __('Ausgang') }}</option>
</select>
</div>
<div class="form-group col-md-3">
<label class="small text-muted">{{ __('Grund') }}</label>
<select name="reason" class="form-control" onchange="this.form.submit()">
<div class="form-group col-md-3 mb-2">
<label class="small text-muted mb-1">{{ __('Grund') }}</label>
<select name="reason" class="form-control form-control-sm" onchange="this.form.submit()">
<option value="">{{ __('Filter aus') }}</option>
@foreach ($reasonOptions as $reason)
<option value="{{ $reason }}" @selected($filters['reason'] === $reason)>{{ $reason }}</option>
@endforeach
</select>
</div>
<div class="form-group col-md-2">
<label class="small text-muted">{{ __('Monat') }}</label>
<select name="month" class="form-control" onchange="this.form.submit()">
<div class="form-group col-md-2 mb-2">
<label class="small text-muted mb-1">{{ __('Monat') }}</label>
<select name="month" class="form-control form-control-sm" onchange="this.form.submit()">
<option value="">{{ __('Alle') }}</option>
@foreach ($months as $num => $label)
<option value="{{ $num }}" @selected($filters['month'] === $num)>{{ $label }}</option>
@endforeach
</select>
</div>
<div class="form-group col-md-2">
<label class="small text-muted">{{ __('Jahr') }}</label>
<select name="year" class="form-control" onchange="this.form.submit()">
<div class="form-group col-md-2 mb-2">
<label class="small text-muted mb-1">{{ __('Jahr') }}</label>
<select name="year" class="form-control form-control-sm" onchange="this.form.submit()">
<option value="">{{ __('Alle') }}</option>
@foreach ($years as $y)
<option value="{{ $y }}" @selected($filters['year'] === $y)>{{ $y }}</option>
@ -60,13 +74,14 @@
</select>
</div>
</div>
@if ($filters['product_id'] || $filters['direction'] || $filters['reason'] || $filters['month'] || $filters['year'])
<a href="{{ route('admin.inventory.product-stock.history') }}" class="btn btn-sm btn-outline-secondary mb-3">{{ __('Filter zurücksetzen') }}</a>
@if ($hasFilter)
<a href="{{ route('admin.inventory.product-stock.history') }}" class="btn btn-sm btn-outline-secondary">{{ __('Filter zurücksetzen') }}</a>
@endif
</form>
</div>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0">
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th>{{ __('Produkt / Material') }}</th>
@ -80,12 +95,12 @@
<tbody>
@forelse ($movements as $movement)
<tr>
<td>{{ $movement->product?->name ?? '—' }}</td>
<td><span class="wawi-item-name">{{ $movement->product?->name ?? '—' }}</span></td>
<td>
@if ($movement->isIn())
<span class="text-success">{{ __('Eingang') }}</span>
<span class="wawi-pill wawi-pill--ok">{{ __('Eingang') }}</span>
@else
<span class="text-danger">{{ __('Ausgang') }}</span>
<span class="wawi-pill wawi-pill--danger">{{ __('Ausgang') }}</span>
@endif
</td>
<td class="text-right">{{ \App\Services\Util::formatNumber($movement->quantity, 0) }} {{ __('Stück') }}</td>
@ -100,11 +115,17 @@
</tr>
@empty
<tr>
<td colspan="6" class="text-center text-muted py-4">{{ __('Keine Bewegungen gefunden.') }}</td>
<td colspan="6">
<div class="wawi-empty">
<div><span class="far fa-clock"></span></div>
{{ __('Keine Bewegungen gefunden.') }}
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View file

@ -1,50 +1,120 @@
@extends('layouts.layout-2')
@php
$total = $rows->count();
$criticalCount = $rows->where('status', 'critical')->count();
$warningCount = $rows->where('status', 'warning')->count();
$okCount = $total - $criticalCount - $warningCount;
@endphp
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<div class="card-header">
<div class="d-flex flex-wrap justify-content-between align-items-center">
<h6 class="mb-0">{{ __('Produktbestand') }}</h6>
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Produktbestand') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Aktueller Lagerbestand aller aktiven Produkte') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.product-stock.history') }}" class="btn btn-outline-secondary btn-sm">
<span class="far fa-clock mr-1"></span>{{ __('Historie') }}
</a>
</div>
</div>
<div class="wawi-stats">
<div class="wawi-stat" data-filter="all">
<div class="wawi-stat__icon"><span class="fas fa-boxes-stacked"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $total }}</div>
<div class="wawi-stat__label">{{ __('Produkte') }}</div>
</div>
</div>
<div class="wawi-stat wawi-stat--ok" data-filter="all">
<div class="wawi-stat__icon"><span class="fas fa-circle-check"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $okCount }}</div>
<div class="wawi-stat__label">{{ __('Bestand OK') }}</div>
</div>
</div>
<div class="wawi-stat wawi-stat--warning is-clickable" data-filter="warning">
<div class="wawi-stat__icon"><span class="fas fa-triangle-exclamation"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $warningCount }}</div>
<div class="wawi-stat__label">{{ __('Niedrig') }}</div>
</div>
</div>
<div class="wawi-stat wawi-stat--danger is-clickable" data-filter="critical">
<div class="wawi-stat__icon"><span class="fas fa-circle-exclamation"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $criticalCount }}</div>
<div class="wawi-stat__label">{{ __('Kritisch') }}</div>
</div>
</div>
</div>
<div class="wawi-card">
<div class="wawi-toolbar">
<div class="wawi-search">
<span class="fas fa-search"></span>
<input type="text" id="ps-search" class="form-control" autocomplete="off"
placeholder="{{ __('Produkt suchen …') }}">
</div>
<div class="wawi-toolbar__spacer"></div>
<label class="form-check d-inline-flex align-items-center mb-0">
<input type="checkbox" id="ps-only-critical" class="form-check-input mr-2">
<span>{{ __('nur kritische anzeigen') }}</span>
</label>
</div>
<div class="form-group row mt-3 mb-0 align-items-center">
<label for="ps-search" class="col-sm-1 col-form-label">{{ __('Suchen') }}</label>
<div class="col-sm-5">
<input type="text" id="ps-search" class="form-control" autocomplete="off">
</div>
</div>
</div>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0" id="ps-table">
<div class="table-responsive">
<table class="table wawi-table" id="ps-table">
<thead>
<tr>
<th style="width:4rem"></th>
<th style="width:3.5rem"></th>
<th>{{ __('Name') }}</th>
<th class="text-right">{{ __('Bestand') }}</th>
<th style="width:18rem">{{ __('Aktion') }}</th>
<th>{{ __('Status') }}</th>
<th class="text-right" style="width:16rem">{{ __('Aktion') }}</th>
</tr>
</thead>
<tbody>
@forelse($rows as $row)
@php
$product = $row['product'];
$rowClass = $row['status'] === 'critical' ? 'table-danger' : ($row['status'] === 'warning' ? 'table-warning' : '');
$rowClass = $row['status'] === 'critical' ? 'is-danger' : ($row['status'] === 'warning' ? 'is-warning' : '');
$stockClass = $row['status'] === 'critical' ? 'text-danger font-weight-bold' : ($row['status'] === 'warning' ? 'text-warning font-weight-bold' : '');
@endphp
<tr class="ps-row {{ $rowClass }}" data-name="{{ Str::lower($product->name) }} {{ Str::lower($product->number ?? '') }}" data-status="{{ $row['status'] }}">
<td>
@if ($product->images->count())
<img class="img-fluid" alt="" style="max-height: 42px"
<img class="wawi-thumb" alt=""
src="{{ route('product_image', [$product->images->first()->slug]) }}">
@else
<span class="wawi-thumb wawi-thumb--empty"><span class="far fa-image"></span></span>
@endif
</td>
<td>{{ $product->name }}</td>
<td class="text-right {{ $stockClass }}">{{ \App\Services\Util::formatNumber($row['stock'], 0) }} {{ __('Stück') }}</td>
<td>
<div class="wawi-item-name">{{ $product->name }}</div>
@if (!empty($product->number))
<div class="wawi-item-sub">{{ $product->number }}</div>
@endif
</td>
<td class="text-right {{ $stockClass }}">
{{ \App\Services\Util::formatNumber($row['stock'], 0) }} <span class="text-muted">{{ __('Stück') }}</span>
</td>
<td>
@if ($row['status'] === 'critical')
<span class="wawi-pill wawi-pill--danger">{{ __('Kritisch') }}</span>
@elseif ($row['status'] === 'warning')
<span class="wawi-pill wawi-pill--warning">{{ __('Niedrig') }}</span>
@else
<span class="wawi-pill wawi-pill--ok">{{ __('OK') }}</span>
@endif
</td>
<td class="text-right">
<div class="wawi-actions">
@if (Auth::user()->isAdmin())
<button type="button" class="btn icon-btn btn-sm btn-success js-ps-move"
data-product="{{ $product->id }}" data-name="{{ $product->name }}" data-direction="in"
@ -54,18 +124,26 @@
title="{{ __('Ausgang buchen') }}"><span class="fas fa-minus"></span></button>
@endif
<a href="{{ route('admin.inventory.productions.create', ['product_id' => $product->id]) }}"
class="btn btn-sm btn-dark">{{ __('Produzieren') }}</a>
class="btn btn-sm btn-outline-secondary">
<span class="fas fa-flask mr-1"></span>{{ __('Produzieren') }}</a>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="text-center text-muted py-4">{{ __('Keine Produkte vorhanden.') }}</td>
<td colspan="5">
<div class="wawi-empty">
<div><span class="fas fa-boxes-stacked"></span></div>
{{ __('Keine Produkte vorhanden.') }}
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@if (Auth::user()->isAdmin())
<div class="modal fade" id="ps-move-modal" tabindex="-1" role="dialog" aria-hidden="true">
@ -126,6 +204,18 @@
$('#ps-search').on('keyup', applyFilter);
$('#ps-only-critical').on('change', applyFilter);
$('.wawi-stat.is-clickable').on('click', function () {
var filter = $(this).data('filter');
var $this = $(this);
var wasActive = $this.hasClass('is-active');
$('.wawi-stat').removeClass('is-active');
if (filter === 'critical' || filter === 'warning') {
$('#ps-only-critical').prop('checked', !wasActive);
if (!wasActive) { $this.addClass('is-active'); }
applyFilter();
}
});
var moveTemplate = "{{ route('admin.inventory.product-stock.movement', ['product' => '__ID__']) }}";
$('.js-ps-move').on('click', function () {
var id = $(this).data('product');

View file

@ -1,14 +1,24 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Produktionen') }}</span>
<a href="{{ route('admin.inventory.productions.create') }}" class="btn btn-sm btn-primary">{{ __('Neue Produktion') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Produktionen') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Erfasste Produktionschargen inkl. Rohstoff- und Verpackungsverbrauch') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.productions.create') }}" class="btn btn-primary btn-sm">
<span class="fas fa-plus mr-1"></span>{{ __('Neue Produktion') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 130px;"></th>
@ -34,12 +44,12 @@
</a>
</td>
<td data-sort="{{ $row->produced_at?->timestamp ?? 0 }}">{{ $row->produced_at?->format('d.m.Y') }}</td>
<td>{{ $row->product?->name ?? '—' }}</td>
<td><span class="wawi-item-name">{{ $row->product?->name ?? '—' }}</span></td>
<td>{{ $row->quantity }}</td>
<td>{{ $row->location?->name ?? '—' }}</td>
<td>
@if($row->mhd_warning)
<span class="badge badge-warning">{{ __('Ja') }}</span>
<span class="wawi-pill wawi-pill--warning">{{ __('Ja') }}</span>
@else
<span class="text-muted"></span>
@endif
@ -50,6 +60,7 @@
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({

View file

@ -5,50 +5,69 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Produktion') }} #{{ $model->id }}</h4>
<div class="card mb-3">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Übersicht') }}</span>
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Produktion') }} #{{ $model->id }}</h1>
<p class="wawi-page-head__subtitle">{{ $model->product?->name ?? '—' }} · {{ $model->produced_at?->format('d.m.Y') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.productions.edit', $model) }}" class="btn btn-sm btn-primary">{{ __('Bearbeiten') }}</a>
<a href="{{ route('admin.inventory.productions.copy', $model) }}" class="btn btn-sm btn-outline-primary">{{ __('Kopieren') }}</a>
<a href="{{ route('admin.inventory.productions.index') }}" class="btn btn-sm btn-outline-secondary">{{ __('Zurück') }}</a>
</div>
</h6>
</div>
<div class="card wawi-card mb-3">
<div class="wawi-card__header">{{ __('Übersicht') }}</div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">{{ __('Produkt') }}</dt>
<dd class="col-sm-9">{{ $model->product?->name ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('Produktionsdatum') }}</dt>
<dd class="col-sm-9">{{ $model->produced_at?->format('d.m.Y') }}</dd>
<dt class="col-sm-3">{{ __('Stückzahl') }}</dt>
<dd class="col-sm-9">{{ $model->quantity }}</dd>
<dt class="col-sm-3">{{ __('Standort') }}</dt>
<dd class="col-sm-9">{{ $model->location?->name ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('Erfasst von') }}</dt>
<dd class="col-sm-9">{{ $model->producedByUser?->getFullName(false) ?: $model->producedByUser?->email ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('MHD-Hinweis') }}</dt>
<dd class="col-sm-9">
<div class="wawi-deflist">
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Produkt') }}</span>
<span class="wawi-deflist__value">{{ $model->product?->name ?? '—' }}</span>
</div>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Produktionsdatum') }}</span>
<span class="wawi-deflist__value">{{ $model->produced_at?->format('d.m.Y') }}</span>
</div>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Stückzahl') }}</span>
<span class="wawi-deflist__value">{{ $model->quantity }}</span>
</div>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Standort') }}</span>
<span class="wawi-deflist__value">{{ $model->location?->name ?? '—' }}</span>
</div>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Erfasst von') }}</span>
<span class="wawi-deflist__value">{{ $model->producedByUser?->getFullName(false) ?: $model->producedByUser?->email ?? '—' }}</span>
</div>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('MHD-Hinweis') }}</span>
<span class="wawi-deflist__value">
@if($model->mhd_warning)
<span class="badge badge-warning">{{ __('Rohstoff-MHD kürzer als Produkt-MHD') }}</span>
<span class="wawi-pill wawi-pill--warning">{{ __('Rohstoff-MHD kürzer als Produkt-MHD') }}</span>
@else
@endif
</dd>
</span>
</div>
@if($model->notes)
<dt class="col-sm-3">{{ __('Notizen') }}</dt>
<dd class="col-sm-9">{{ $model->notes }}</dd>
<div class="wawi-deflist__item wawi-deflist__item--wide">
<span class="wawi-deflist__label">{{ __('Notizen') }}</span>
<span class="wawi-deflist__value">{{ $model->notes }}</span>
</div>
@endif
</dl>
</div>
</div>
</div>
<div class="card mb-3">
<h6 class="card-header">{{ __('Rohstoff-Verbrauch (Chargen)') }}</h6>
<div class="card wawi-card mb-3">
<div class="wawi-card__header">{{ __('Rohstoff-Verbrauch (Chargen)') }}</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped mb-0">
<table class="table wawi-table mb-0">
<thead>
<tr>
<th>{{ __('Inhaltsstoff') }}</th>
@ -78,11 +97,11 @@
</div>
</div>
<div class="card">
<h6 class="card-header">{{ __('Verpackung (Snapshot)') }}</h6>
<div class="card wawi-card">
<div class="wawi-card__header">{{ __('Verpackung (Snapshot)') }}</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped mb-0">
<table class="table wawi-table mb-0">
<thead>
<tr>
<th>{{ __('Artikel') }}</th>
@ -101,4 +120,5 @@
</div>
</div>
</div>
</div>
@endsection

View file

@ -1,36 +1,79 @@
@extends('layouts.layout-2')
@php
$total = $rows->count();
$criticalCount = $rows->whereIn('status', ['critical', 'critical_ordered'])->count();
$warningCount = $rows->where('status', 'warning')->count();
$okCount = $total - $criticalCount - $warningCount;
@endphp
@section('content')
@include('admin.inventory.partials.table-actions-style')
@include('admin.inventory.partials.wawi-ui')
<style>
#rms-table tbody tr.rms-row {
cursor: pointer;
}
#rms-table tbody tr.rms-row:hover {
background-color: rgba(0, 0, 0, 0.05);
}
#rms-table tbody tr.rms-row:hover .fa-edit {
color: #26b4ff !important;
}
#rms-table tbody tr.rms-row { cursor: pointer; }
#rms-table tbody tr.rms-row:hover .fa-edit { color: var(--wawi-accent) !important; }
</style>
<div class="card">
<div class="card-header">
<div class="d-flex flex-wrap justify-content-between align-items-center">
<h6 class="mb-0">{{ __('Rohstoffbestand') }}</h6>
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Rohstoffbestand') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Verfügbare Rohstoffe inkl. Reichweite und Nachbestellung') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.stock-entries.create') }}" class="btn btn-outline-secondary btn-sm">
<span class="fas fa-truck mr-1"></span>{{ __('Einkauf erfassen') }}
</a>
</div>
</div>
<div class="wawi-stats">
<div class="wawi-stat">
<div class="wawi-stat__icon"><span class="fas fa-flask"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $total }}</div>
<div class="wawi-stat__label">{{ __('Rohstoffe') }}</div>
</div>
</div>
<div class="wawi-stat wawi-stat--ok">
<div class="wawi-stat__icon"><span class="fas fa-circle-check"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $okCount }}</div>
<div class="wawi-stat__label">{{ __('Bestand OK') }}</div>
</div>
</div>
<div class="wawi-stat wawi-stat--warning is-clickable" data-filter="warning">
<div class="wawi-stat__icon"><span class="fas fa-triangle-exclamation"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $warningCount }}</div>
<div class="wawi-stat__label">{{ __('Bald nachbestellen') }}</div>
</div>
</div>
<div class="wawi-stat wawi-stat--danger is-clickable" data-filter="critical">
<div class="wawi-stat__icon"><span class="fas fa-circle-exclamation"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $criticalCount }}</div>
<div class="wawi-stat__label">{{ __('Kritisch') }}</div>
</div>
</div>
</div>
<div class="wawi-card">
<div class="wawi-toolbar">
<div class="wawi-search">
<span class="fas fa-search"></span>
<input type="text" id="rms-search" class="form-control" autocomplete="off"
placeholder="{{ __('Rohstoff suchen …') }}">
</div>
<div class="wawi-toolbar__spacer"></div>
<label class="form-check d-inline-flex align-items-center mb-0">
<input type="checkbox" id="rms-only-critical" class="form-check-input mr-2">
<span>{{ __('nur kritische anzeigen') }}</span>
</label>
</div>
<div class="form-group row mt-3 mb-0 align-items-center">
<label for="rms-search" class="col-sm-1 col-form-label">{{ __('Suchen') }}</label>
<div class="col-sm-5">
<input type="text" id="rms-search" class="form-control" autocomplete="off">
</div>
</div>
</div>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0" id="rms-table">
<div class="table-responsive">
<table class="table wawi-table" id="rms-table">
<thead>
<tr>
<th>{{ __('Name') }}</th>
@ -53,22 +96,24 @@
@php
$ingredient = $row['ingredient'];
$rowClass = $row['status'] === 'critical'
? 'table-danger'
: (in_array($row['status'], ['warning', 'critical_ordered'], true) ? 'table-warning' : '');
? 'is-danger'
: (in_array($row['status'], ['warning', 'critical_ordered'], true) ? 'is-warning' : '');
@endphp
<tr class="rms-row {{ $rowClass }}"
data-name="{{ Str::lower($ingredient->name) }} {{ Str::lower($ingredient->inci ?? '') }}"
data-status="{{ $row['status'] }}"
data-href="{{ route('admin.inventory.raw-material-stock.show', $ingredient) }}">
<td>
<i class="far fa-edit text-muted mr-2" title="{{ __('Bestellung öffnen') }}"></i>
{{ $ingredient->name }}
<span class="wawi-name-cell">
<i class="far fa-edit text-muted wawi-name-cell__icon" title="{{ __('Bestellung öffnen') }}"></i>
<span class="wawi-item-name">{{ $ingredient->name }}</span>
</span>
</td>
<td class="text-muted">{{ $ingredient->materialQuality?->name ?? '—' }}</td>
<td class="text-right {{ in_array($row['status'], ['critical', 'critical_ordered'], true) ? 'text-danger font-weight-bold' : '' }}">
{{ \App\Services\Util::formatNumber($row['remaining'], 0) }} g
@if($row['status'] === 'critical_ordered')
<span class="badge badge-info ml-1">{{ __('bestellt') }}</span>
<span class="wawi-pill wawi-pill--warning ml-1">{{ __('bestellt') }}</span>
@endif
</td>
<td class="text-right">
@ -103,13 +148,19 @@
</tr>
@empty
<tr>
<td colspan="7" class="text-center text-muted py-4">{{ __('Keine aktiven Rohstoffe vorhanden.') }}</td>
<td colspan="7">
<div class="wawi-empty">
<div><span class="fas fa-flask"></span></div>
{{ __('Keine aktiven Rohstoffe vorhanden.') }}
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<script>
$(function () {
@ -123,7 +174,7 @@
var $row = $(this);
var matchesTerm = term === '' || ($row.data('name') || '').toString().indexOf(term) !== -1;
var status = $row.data('status');
var matchesCritical = !onlyCritical || (status === 'critical' || status === 'warning');
var matchesCritical = !onlyCritical || (status === 'critical' || status === 'critical_ordered' || status === 'warning');
$row.toggle(matchesTerm && matchesCritical);
});
}
@ -145,6 +196,15 @@
$('#rms-only-critical').on('change', applyFilter);
$('#rms-horizon').on('change', applyForecast);
$('.wawi-stat.is-clickable').on('click', function () {
var $this = $(this);
var wasActive = $this.hasClass('is-active');
$('.wawi-stat').removeClass('is-active');
$('#rms-only-critical').prop('checked', !wasActive);
if (!wasActive) { $this.addClass('is-active'); }
applyFilter();
});
$rows.on('click', function () {
var href = $(this).data('href');
if (href) {

View file

@ -1,78 +1,86 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
@php
$statusBadge = match ($status) {
'critical' => '<span class="badge badge-danger">' . __('Kritisch') . '</span>',
'critical_ordered' => '<span class="badge badge-warning">' . __('Kritisch · bereits bestellt') . '</span>',
'warning' => '<span class="badge badge-warning">' . __('Bald nachbestellen') . '</span>',
default => '<span class="badge badge-success">' . __('Ausreichend') . '</span>',
$statusPill = match ($status) {
'critical' => '<span class="wawi-pill wawi-pill--danger">' . __('Kritisch') . '</span>',
'critical_ordered' => '<span class="wawi-pill wawi-pill--warning">' . __('Kritisch · bereits bestellt') . '</span>',
'warning' => '<span class="wawi-pill wawi-pill--warning">' . __('Bald nachbestellen') . '</span>',
default => '<span class="wawi-pill wawi-pill--ok">' . __('Ausreichend') . '</span>',
};
$stockCritical = in_array($status, ['critical', 'critical_ordered'], true);
@endphp
<div class="card mb-3">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Rohstoffbestellung') }}: {{ $ingredient->name }} {!! $statusBadge !!}</span>
<a href="{{ route('admin.inventory.raw-material-stock.index') }}" class="btn btn-sm btn-outline-secondary">{{ __('Zurück') }}</a>
</h6>
<div class="card-body">
<div class="row">
<div class="col-md-3 col-6 mb-2">
<div class="text-muted small">{{ __('Qualität') }}</div>
<div>{{ $ingredient->materialQuality?->name ?? '—' }}</div>
</div>
<div class="col-md-3 col-6 mb-2">
<div class="text-muted small">{{ __('Gesamtbestand') }}</div>
<div class="{{ in_array($status, ['critical', 'critical_ordered'], true) ? 'text-danger font-weight-bold' : 'font-weight-bold' }}">
{{ \App\Services\Util::formatNumber($remaining, 0) }} g
</div>
</div>
<div class="col-md-3 col-6 mb-2">
<div class="text-muted small">{{ __('Offen bestellt') }}</div>
@section('content')
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
@if($openTotal > 0)
{{ \App\Services\Util::formatNumber($openTotal, 0) }} g
@else
@endif
<h1 class="wawi-page-head__title">
{{ $ingredient->name }} {!! $statusPill !!}
</h1>
<p class="wawi-page-head__subtitle">{{ __('Rohstoffbestellung & Bestand') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.raw-material-stock.index') }}" class="btn btn-outline-secondary btn-sm">
<span class="fas fa-arrow-left mr-1"></span>{{ __('Zurück') }}
</a>
</div>
</div>
<div class="col-md-3 col-6 mb-2">
<div class="text-muted small">{{ __('Verbrauch / Tag') }}</div>
<div>
@if($daily !== null && $daily > 0)
{{ \App\Services\Util::formatNumber($daily, 0) }} g
@else
@endif
<div class="wawi-stats">
<div class="wawi-stat {{ $stockCritical ? 'wawi-stat--danger' : 'wawi-stat--ok' }}">
<div class="wawi-stat__icon"><span class="fas fa-warehouse"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ \App\Services\Util::formatNumber($remaining, 0) }} g</div>
<div class="wawi-stat__label">{{ __('Gesamtbestand') }}</div>
</div>
</div>
<div class="col-md-3 col-6 mb-2">
<div class="text-muted small">{{ __('Voraussichtlich auf Null') }}</div>
<div>
<div class="wawi-stat">
<div class="wawi-stat__icon"><span class="fas fa-truck"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $openTotal > 0 ? \App\Services\Util::formatNumber($openTotal, 0) . ' g' : '—' }}</div>
<div class="wawi-stat__label">{{ __('Offen bestellt') }}</div>
</div>
</div>
<div class="wawi-stat">
<div class="wawi-stat__icon"><span class="fas fa-gauge-high"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ ($daily !== null && $daily > 0) ? \App\Services\Util::formatNumber($daily, 0) . ' g' : '—' }}</div>
<div class="wawi-stat__label">{{ __('Verbrauch / Tag') }}</div>
</div>
</div>
<div class="wawi-stat {{ ($expectedEmpty !== null && $daysUntilEmpty <= 30) ? 'wawi-stat--warning' : '' }}">
<div class="wawi-stat__icon"><span class="fas fa-hourglass-half"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $expectedEmpty !== null ? $expectedEmpty->format('d.m.Y') : '—' }}</div>
<div class="wawi-stat__label">
{{ __('Voraussichtlich auf Null') }}
@if($expectedEmpty !== null)
{{ $expectedEmpty->format('d.m.Y') }}
<span class="text-muted">({{ $daysUntilEmpty }} {{ trans_choice('Tag|Tagen', $daysUntilEmpty) }})</span>
@else
({{ $daysUntilEmpty }} {{ trans_choice('Tag|Tagen', $daysUntilEmpty) }})
@endif
</div>
</div>
</div>
@if($ingredient->min_stock_alert !== null)
<div class="text-muted small mt-2">
{{ __('Meldebestand') }}: {{ \App\Services\Util::formatNumber($ingredient->min_stock_alert, 0) }} g
</div>
<div class="wawi-card" style="margin-bottom:1.25rem;">
<div class="wawi-card__footer" style="border-top:0; background:transparent;">
<span class="text-muted small mr-3">
{{ __('Qualität') }}: <strong>{{ $ingredient->materialQuality?->name ?? '—' }}</strong>
</span>
@if($ingredient->min_stock_alert !== null)
<span class="text-muted small">
{{ __('Meldebestand') }}: <strong>{{ \App\Services\Util::formatNumber($ingredient->min_stock_alert, 0) }} g</strong>
</span>
@endif
</div>
</div>
<div class="card mb-3">
<h6 class="card-header">{{ __('Enthalten in') }}</h6>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0">
<div class="wawi-card">
<div class="wawi-card__header">{{ __('Enthalten in') }}</div>
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th>{{ __('Produkt') }}</th>
@ -95,7 +103,7 @@
</tr>
@empty
<tr>
<td colspan="3" class="text-center text-muted py-3">{{ __('Dieser Rohstoff wird in keiner aktiven Rezeptur verwendet.') }}</td>
<td colspan="3"><div class="wawi-empty">{{ __('Dieser Rohstoff wird in keiner aktiven Rezeptur verwendet.') }}</div></td>
</tr>
@endforelse
</tbody>
@ -103,10 +111,10 @@
</div>
</div>
<div class="card mb-3">
<h6 class="card-header">{{ __('Lieferanten & Bestellung') }}</h6>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0">
<div class="wawi-card">
<div class="wawi-card__header">{{ __('Lieferanten & Bestellung') }}</div>
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th>{{ __('Lieferant') }}</th>
@ -125,12 +133,12 @@
@endphp
<tr>
<td>
{{ $supplier->name }}
<span class="wawi-item-name">{{ $supplier->name }}</span>
@if($supplier->pivot->preferred)
<span class="badge badge-success ml-1">{{ __('bevorzugt') }}</span>
<span class="wawi-pill wawi-pill--ok ml-1">{{ __('bevorzugt') }}</span>
@endif
@if($supplier->pivot->supplier_sku)
<div class="text-muted small">{{ __('Art.-Nr.') }}: {{ $supplier->pivot->supplier_sku }}</div>
<div class="wawi-item-sub">{{ __('Art.-Nr.') }}: {{ $supplier->pivot->supplier_sku }}</div>
@endif
</td>
<td>{{ $deliveryTime ?: '—' }}</td>
@ -143,11 +151,11 @@
</td>
<td class="text-right">
@if($supplier->order_method === 'online_shop' && $orderUrl)
<a href="{{ $orderUrl }}" target="_blank" rel="noopener" class="btn btn-sm btn-dark">{{ __('Zum Shop') }}</a>
<a href="{{ $orderUrl }}" target="_blank" rel="noopener" class="btn btn-sm btn-outline-secondary">{{ __('Zum Shop') }}</a>
@elseif($orderEmail)
<a href="mailto:{{ $orderEmail }}?subject={{ rawurlencode(__('Bestellung') . ': ' . $ingredient->name) }}" class="btn btn-sm btn-dark">{{ __('Per Mail') }}</a>
<a href="mailto:{{ $orderEmail }}?subject={{ rawurlencode(__('Bestellung') . ': ' . $ingredient->name) }}" class="btn btn-sm btn-outline-secondary">{{ __('Per Mail') }}</a>
@elseif($orderUrl)
<a href="{{ $orderUrl }}" target="_blank" rel="noopener" class="btn btn-sm btn-dark">{{ __('Zum Shop') }}</a>
<a href="{{ $orderUrl }}" target="_blank" rel="noopener" class="btn btn-sm btn-outline-secondary">{{ __('Zum Shop') }}</a>
@else
<span class="text-muted"></span>
@endif
@ -155,24 +163,24 @@
</tr>
@empty
<tr>
<td colspan="4" class="text-center text-muted py-3">{{ __('Diesem Rohstoff ist kein Lieferant zugeordnet.') }}</td>
<td colspan="4"><div class="wawi-empty">{{ __('Diesem Rohstoff ist kein Lieferant zugeordnet.') }}</div></td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if(Auth::user()->isAdmin())
<div class="card-footer">
<div class="wawi-card__footer">
<a href="{{ route('admin.inventory.stock-entries.create', ['ingredient_id' => $ingredient->id]) }}" class="btn btn-sm btn-primary">{{ __('Einkauf erfassen') }}</a>
<a href="{{ route('admin.inventory.stock-disposals.create', ['ingredient_id' => $ingredient->id]) }}" class="btn btn-sm btn-outline-danger">{{ __('Ausschuss erfassen') }}</a>
</div>
@endif
</div>
<div class="card mb-3">
<h6 class="card-header">{{ __('Offene Bestellungen / unterwegs') }}</h6>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0">
<div class="wawi-card">
<div class="wawi-card__header">{{ __('Offene Bestellungen / unterwegs') }}</div>
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th>{{ __('Bestellt am') }}</th>
@ -197,23 +205,23 @@
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-muted py-3">{{ __('Keine offenen Bestellungen.') }}</td>
<td colspan="5"><div class="wawi-empty">{{ __('Keine offenen Bestellungen.') }}</div></td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($openOrders->isNotEmpty())
<div class="card-footer text-muted small">
<div class="wawi-card__footer text-muted small">
{{ __('Offene Bestellungen zählen erst nach gebuchtem Wareneingang zum Bestand.') }}
</div>
@endif
</div>
<div class="card mb-3">
<h6 class="card-header">{{ __('Verfügbare Chargen') }}</h6>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0">
<div class="wawi-card">
<div class="wawi-card__header">{{ __('Verfügbare Chargen') }}</div>
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th>{{ __('Charge') }}</th>
@ -234,7 +242,7 @@
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-muted py-3">{{ __('Kein Restbestand vorhanden.') }}</td>
<td colspan="5"><div class="wawi-empty">{{ __('Kein Restbestand vorhanden.') }}</div></td>
</tr>
@endforelse
</tbody>
@ -253,4 +261,5 @@
</table>
</div>
</div>
</div>
@endsection

View file

@ -1,8 +1,20 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Ausgang / Ausschuss erfassen') }}</h4>
<div class="card">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Ausgang / Ausschuss erfassen') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Bestand eines Rohstoffs oder Verpackungsartikels reduzieren') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.stock-disposals.index') }}" class="btn btn-outline-secondary btn-sm">
<span class="fas fa-arrow-left mr-1"></span>{{ __('Zurück') }}
</a>
</div>
</div>
<div class="card wawi-card">
<div class="card-body">
<p class="text-muted small">{{ __('Reduziert den Bestand des gewählten Rohstoffs bzw. Verpackungsartikels. Der Grund ist Pflicht und erscheint in der Ausgangsliste.') }}</p>
<form method="post" action="{{ route('admin.inventory.stock-disposals.store') }}">
@ -116,6 +128,7 @@
</form>
</div>
</div>
</div>
@endsection
@section('scripts')

View file

@ -1,28 +1,35 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<div class="card-header d-flex flex-wrap justify-content-between align-items-center">
<h6 class="mb-0">{{ __('Ausgang / Ausschuss') }}</h6>
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<form method="get" class="d-inline-block mr-2">
<select name="type" class="form-control form-control-sm d-inline-block" style="width:auto"
onchange="this.form.submit()">
<h1 class="wawi-page-head__title">{{ __('Ausgang / Ausschuss') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Erfasste Bestandsabgänge von Rohstoffen und Verpackung') }}</p>
</div>
<div class="wawi-page-head__actions">
@if (Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-disposals.create') }}" class="btn btn-primary btn-sm">
<span class="fas fa-plus mr-1"></span>{{ __('Ausschuss erfassen') }}
</a>
@endif
</div>
</div>
<div class="wawi-card">
<div class="wawi-toolbar">
<form method="get" class="mb-0">
<select name="type" class="form-control form-control-sm" style="width:auto" onchange="this.form.submit()">
<option value="">{{ __('Alle Arten') }}</option>
<option value="ingredient" @selected($typeFilter === 'ingredient')>{{ __('Rohstoff') }}</option>
<option value="packaging" @selected($typeFilter === 'packaging')>{{ __('Verpackung') }}</option>
</select>
</form>
@if (Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-disposals.create') }}" class="btn btn-sm btn-primary">
{{ __('Ausschuss erfassen') }}
</a>
@endif
</div>
</div>
<div class="card-datatable table-responsive">
<table class="table table-striped wawi-table mb-0">
<div class="table-responsive">
<table class="table wawi-table">
<thead>
<tr>
<th>{{ __('Datum') }}</th>
@ -42,12 +49,12 @@
<td>{{ $disposal->disposed_at?->format('d.m.Y') }}</td>
<td>
@if ($disposal->isIngredient())
<span class="badge badge-info">{{ __('Rohstoff') }}</span>
<span class="wawi-pill wawi-pill--ok">{{ __('Rohstoff') }}</span>
@else
<span class="badge badge-secondary">{{ __('Verpackung') }}</span>
<span class="wawi-pill" style="background:rgba(24,28,33,.06);color:#6c757d;">{{ __('Verpackung') }}</span>
@endif
</td>
<td>{{ $disposal->articleName() }}</td>
<td><span class="wawi-item-name">{{ $disposal->articleName() }}</span></td>
<td>
@if ($disposal->stockEntry)
{{ $disposal->stockEntry->batch_number ?: '#'.$disposal->stockEntry->id }}
@ -66,11 +73,17 @@
</tr>
@empty
<tr>
<td colspan="9" class="text-center text-muted py-4">{{ __('Noch keine Ausgänge erfasst.') }}</td>
<td colspan="9">
<div class="wawi-empty">
<div><span class="fas fa-dumpster"></span></div>
{{ __('Noch keine Ausgänge erfasst.') }}
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection

View file

@ -1,16 +1,26 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Wareneingang') }}</span>
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Einkauf & Wareneingang') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Bestellungen und Wareneingänge von Rohstoffen und Verpackung') }}</p>
</div>
<div class="wawi-page-head__actions">
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.create') }}" class="btn btn-sm btn-primary">{{ __('Neuer Einkauf') }}</a>
<a href="{{ route('admin.inventory.stock-entries.create') }}" class="btn btn-primary btn-sm">
<span class="fas fa-plus mr-1"></span>{{ __('Neuer Einkauf') }}
</a>
@endif
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 90px;"></th>
@ -43,18 +53,18 @@
</td>
<td data-sort="{{ $row->status === 'pending' ? 0 : 1 }}">
@if($row->status === 'pending')
<span class="badge badge-warning">{{ __('Offen') }}</span>
<span class="wawi-pill wawi-pill--warning">{{ __('Offen') }}</span>
@else
<span class="badge badge-success">{{ __('Eingegangen') }}</span>
<span class="wawi-pill wawi-pill--ok">{{ __('Eingegangen') }}</span>
@endif
</td>
<td data-sort="{{ $row->ordered_at?->timestamp ?? 0 }}">{{ $row->ordered_at?->format('d.m.Y') }}</td>
<td>{{ $entryTypeLabels[$row->entry_type] ?? $row->entry_type }}</td>
<td>
@if($row->entry_type === 'ingredient' && $row->ingredient)
{{ $row->ingredient->name }}
<span class="wawi-item-name">{{ $row->ingredient->name }}</span>
@elseif($row->packagingItem)
{{ $row->packagingItem->name }}
<span class="wawi-item-name">{{ $row->packagingItem->name }}</span>
@else
@endif
@ -83,6 +93,7 @@
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({

View file

@ -5,18 +5,21 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Wareneingang') }}</h4>
<div class="card mb-3">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">
{{ __('Wareneingang') }}
@if($model->status === 'pending')
<span class="badge badge-warning">{{ __('Offen') }}</span>
<span class="wawi-pill wawi-pill--warning">{{ __('Offen') }}</span>
@else
<span class="badge badge-success">{{ __('Eingegangen') }}</span>
<span class="wawi-pill wawi-pill--ok">{{ __('Eingegangen') }}</span>
@endif
</span>
<span>
</h1>
<p class="wawi-page-head__subtitle">{{ $entryTypeLabels[$model->entry_type] ?? $model->entry_type }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.stock-entries.index') }}" class="btn btn-sm btn-outline-secondary">{{ __('Zurück zur Liste') }}</a>
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.copy', $model) }}" class="btn btn-sm btn-outline-info">{{ __('Duplizieren') }}</a>
@ -24,15 +27,21 @@
@if(Auth::user()->isAdmin() && $model->isPending())
<a href="{{ route('admin.inventory.stock-entries.edit', $model) }}" class="btn btn-sm btn-primary">{{ __('Bearbeiten') }}</a>
@endif
</span>
</h6>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">{{ __('Art') }}</dt>
<dd class="col-sm-9">{{ $entryTypeLabels[$model->entry_type] ?? $model->entry_type }}</dd>
</div>
</div>
<dt class="col-sm-3">{{ __('Artikel') }}</dt>
<dd class="col-sm-9">
<div class="card wawi-card mb-3">
<div class="wawi-card__header">{{ __('Details') }}</div>
<div class="card-body">
<div class="wawi-deflist">
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Art') }}</span>
<span class="wawi-deflist__value">{{ $entryTypeLabels[$model->entry_type] ?? $model->entry_type }}</span>
</div>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Artikel') }}</span>
<span class="wawi-deflist__value">
@if($model->entry_type === 'ingredient' && $model->ingredient)
{{ $model->ingredient->name }}@if($model->ingredient->inci) ({{ $model->ingredient->inci }})@endif
@elseif($model->packagingItem)
@ -43,34 +52,46 @@
@else
@endif
</dd>
</span>
</div>
@if($model->entry_type === 'ingredient' && $model->quality)
<dt class="col-sm-3">{{ __('Rohstoffqualität') }}</dt>
<dd class="col-sm-9">{{ $model->quality->name }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Rohstoffqualität') }}</span>
<span class="wawi-deflist__value">{{ $model->quality->name }}</span>
</div>
@endif
<dt class="col-sm-3">{{ __('Lieferant') }}</dt>
<dd class="col-sm-9">{{ $model->supplier?->name ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Lieferant') }}</span>
<span class="wawi-deflist__value">{{ $model->supplier?->name ?? '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Lagerort') }}</dt>
<dd class="col-sm-9">{{ $model->location?->name ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Lagerort') }}</span>
<span class="wawi-deflist__value">{{ $model->location?->name ?? '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Bestelldatum') }}</dt>
<dd class="col-sm-9">{{ $model->ordered_at?->format('d.m.Y') }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Bestelldatum') }}</span>
<span class="wawi-deflist__value">{{ $model->ordered_at?->format('d.m.Y') }}</span>
</div>
<dt class="col-sm-3">{{ __('Bestellte Menge') }}</dt>
<dd class="col-sm-9">
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Bestellte Menge') }}</span>
<span class="wawi-deflist__value">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->ordered_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->ordered_quantity, 0) }} {{ __('Stk.') }}
@endif
</dd>
</span>
</div>
@if(Auth::user()->isAdmin())
<dt class="col-sm-3">{{ __('Preise (Stufe 1)') }}</dt>
<dd class="col-sm-9">
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Preise (Stufe 1)') }}</span>
<span class="wawi-deflist__value">
@if($model->entry_type === 'ingredient')
@if($model->price_per_kg !== null)
{{ \App\Services\Util::formatNumber($model->price_per_kg) }} / kg {{ __('netto') }}
@ -90,46 +111,61 @@
@endif
@endif
</dd>
</span>
</div>
@endif
<dt class="col-sm-3">{{ __('Bestellt von') }}</dt>
<dd class="col-sm-9">{{ $model->orderedByUser?->getFullName(false) ?: $model->orderedByUser?->email ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Bestellt von') }}</span>
<span class="wawi-deflist__value">{{ $model->orderedByUser?->getFullName(false) ?: $model->orderedByUser?->email ?? '—' }}</span>
</div>
@if($model->isReceived())
<dt class="col-sm-3">{{ __('Eingangsdatum') }}</dt>
<dd class="col-sm-9">{{ $model->received_at?->format('d.m.Y') }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Eingangsdatum') }}</span>
<span class="wawi-deflist__value">{{ $model->received_at?->format('d.m.Y') }}</span>
</div>
<dt class="col-sm-3">{{ __('Eingegangene Menge') }}</dt>
<dd class="col-sm-9">
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Eingegangene Menge') }}</span>
<span class="wawi-deflist__value">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->received_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->received_quantity, 0) }} {{ __('Stk.') }}
@endif
</dd>
</span>
</div>
@if($model->entry_type === 'ingredient')
<dt class="col-sm-3">{{ __('Charge') }}</dt>
<dd class="col-sm-9">{{ $model->batch_number ?: '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Charge') }}</span>
<span class="wawi-deflist__value">{{ $model->batch_number ?: '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Mindesthaltbarkeit') }}</dt>
<dd class="col-sm-9">{{ $model->best_before?->format('d.m.Y') ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Mindesthaltbarkeit') }}</span>
<span class="wawi-deflist__value">{{ $model->best_before?->format('d.m.Y') ?? '—' }}</span>
</div>
<dt class="col-sm-3">{{ __('Materialqualität') }}</dt>
<dd class="col-sm-9">{{ $model->quality?->name ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Materialqualität') }}</span>
<span class="wawi-deflist__value">{{ $model->quality?->name ?? '—' }}</span>
</div>
@endif
<dt class="col-sm-3">{{ __('Eingebucht von') }}</dt>
<dd class="col-sm-9">{{ $model->receivedByUser?->getFullName(false) ?: $model->receivedByUser?->email ?? '—' }}</dd>
<div class="wawi-deflist__item">
<span class="wawi-deflist__label">{{ __('Eingebucht von') }}</span>
<span class="wawi-deflist__value">{{ $model->receivedByUser?->getFullName(false) ?: $model->receivedByUser?->email ?? '—' }}</span>
</div>
@endif
</dl>
</div>
</div>
</div>
@if($model->isPending())
<div class="card">
<h6 class="card-header">{{ __('Wareneingang buchen (Stufe 2)') }}</h6>
<div class="card wawi-card">
<div class="wawi-card__header">{{ __('Wareneingang buchen (Stufe 2)') }}</div>
<div class="card-body">
<form method="post" action="{{ route('admin.inventory.stock-entries.receive', $model) }}">
@csrf
@ -202,4 +238,5 @@
</div>
</div>
@endif
</div>
@endsection

View file

@ -1,14 +1,21 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Lieferanten-Kategorien') }}</span>
<a href="{{ route('admin.inventory.supplier-categories.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Lieferanten-Kategorien') }}</h1>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.supplier-categories.create') }}" class="btn btn-sm btn-primary">
<span class="fas fa-plus mr-1"></span>{{ __('Neu anlegen') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -41,6 +48,7 @@
</table>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({

View file

@ -1,14 +1,22 @@
@extends('layouts.layout-2')
@section('content')
@include('admin.inventory.partials.table-actions-style')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Lieferanten') }}</span>
<a href="{{ route('admin.inventory.suppliers.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered wawi-table">
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Lieferanten') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Lieferanten, Kategorien und Bestellkanäle') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.suppliers.create') }}" class="btn btn-sm btn-primary">
<span class="fas fa-plus mr-1"></span>{{ __('Neu anlegen') }}
</a>
</div>
</div>
<div class="wawi-card">
<div class="card-datatable table-responsive p-2">
<table class="datatables-style table wawi-table">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
@ -41,9 +49,9 @@
</td>
<td data-sort="{{ $value->active ? 1 : 0 }}">
@if($value->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
<span class="wawi-pill wawi-pill--ok">{{ __('Aktiv') }}</span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
<span class="wawi-pill wawi-pill--danger">{{ __('Inaktiv') }}</span>
@endif
</td>
<td>
@ -60,6 +68,7 @@
</table>
</div>
</div>
</div>
<div class="modal fade" id="supplierShowModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">