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:
parent
3ee2d756e9
commit
a8f6fef38e
21 changed files with 1609 additions and 955 deletions
|
|
@ -1,113 +1,164 @@
|
|||
@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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Name') }}</th>
|
||||
<th>{{ __('Qualität') }}</th>
|
||||
<th class="text-right">{{ __('Bestand') }}</th>
|
||||
<th class="text-right">{{ __('Offen bestellt') }}</th>
|
||||
<th class="text-right">{{ __('Verbrauch / Tag') }}</th>
|
||||
<th>{{ __('Voraussichtlich auf Null') }}</th>
|
||||
<th class="text-right" style="min-width: 13rem;">
|
||||
<select id="rms-horizon" class="form-control form-control-sm">
|
||||
@foreach($horizonOptions as $days => $label)
|
||||
<option value="{{ $days }}" @selected($days === $defaultHorizon)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($rows as $row)
|
||||
@php
|
||||
$ingredient = $row['ingredient'];
|
||||
$rowClass = $row['status'] === 'critical'
|
||||
? 'table-danger'
|
||||
: (in_array($row['status'], ['warning', 'critical_ordered'], true) ? 'table-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 }}
|
||||
</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>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if($row['open_order'] > 0)
|
||||
{{ \App\Services\Util::formatNumber($row['open_order'], 0) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if($row['daily'] !== null && $row['daily'] > 0)
|
||||
{{ \App\Services\Util::formatNumber($row['daily'], 0) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($row['expected_empty'] !== null)
|
||||
{{ $row['expected_empty']->format('d.m.Y') }}
|
||||
<span class="text-muted">({{ $row['days_until_empty'] }} {{ trans_choice('Tag|Tagen', $row['days_until_empty']) }})</span>
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-right rms-forecast" data-daily="{{ $row['daily'] !== null ? $row['daily'] : 0 }}">
|
||||
@if($row['daily'] !== null && $row['daily'] > 0)
|
||||
{{ \App\Services\Util::formatNumber($row['daily'] * $defaultHorizon, 0) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table wawi-table" id="rms-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted py-4">{{ __('Keine aktiven Rohstoffe vorhanden.') }}</td>
|
||||
<th>{{ __('Name') }}</th>
|
||||
<th>{{ __('Qualität') }}</th>
|
||||
<th class="text-right">{{ __('Bestand') }}</th>
|
||||
<th class="text-right">{{ __('Offen bestellt') }}</th>
|
||||
<th class="text-right">{{ __('Verbrauch / Tag') }}</th>
|
||||
<th>{{ __('Voraussichtlich auf Null') }}</th>
|
||||
<th class="text-right" style="min-width: 13rem;">
|
||||
<select id="rms-horizon" class="form-control form-control-sm">
|
||||
@foreach($horizonOptions as $days => $label)
|
||||
<option value="{{ $days }}" @selected($days === $defaultHorizon)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($rows as $row)
|
||||
@php
|
||||
$ingredient = $row['ingredient'];
|
||||
$rowClass = $row['status'] === 'critical'
|
||||
? '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>
|
||||
<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="wawi-pill wawi-pill--warning ml-1">{{ __('bestellt') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if($row['open_order'] > 0)
|
||||
{{ \App\Services\Util::formatNumber($row['open_order'], 0) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if($row['daily'] !== null && $row['daily'] > 0)
|
||||
{{ \App\Services\Util::formatNumber($row['daily'], 0) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($row['expected_empty'] !== null)
|
||||
{{ $row['expected_empty']->format('d.m.Y') }}
|
||||
<span class="text-muted">({{ $row['days_until_empty'] }} {{ trans_choice('Tag|Tagen', $row['days_until_empty']) }})</span>
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-right rms-forecast" data-daily="{{ $row['daily'] !== null ? $row['daily'] : 0 }}">
|
||||
@if($row['daily'] !== null && $row['daily'] > 0)
|
||||
{{ \App\Services\Util::formatNumber($row['daily'] * $defaultHorizon, 0) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<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>
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1,256 +1,265 @@
|
|||
@extends('layouts.layout-2')
|
||||
|
||||
@php
|
||||
$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
|
||||
|
||||
@section('content')
|
||||
@include('admin.inventory.partials.table-actions-style')
|
||||
@include('admin.inventory.partials.wawi-ui')
|
||||
|
||||
@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>',
|
||||
};
|
||||
@endphp
|
||||
<div class="wawi-page">
|
||||
<div class="wawi-page-head">
|
||||
<div>
|
||||
<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="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 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 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="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 class="col-md-3 col-6 mb-2">
|
||||
<div class="text-muted small">{{ __('Offen bestellt') }}</div>
|
||||
<div>
|
||||
@if($openTotal > 0)
|
||||
{{ \App\Services\Util::formatNumber($openTotal, 0) }} g
|
||||
@else
|
||||
—
|
||||
@endif
|
||||
</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 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>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 mb-2">
|
||||
<div class="text-muted small">{{ __('Voraussichtlich auf Null') }}</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="wawi-card">
|
||||
<div class="wawi-card__header">{{ __('Enthalten in') }}</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table wawi-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Produkt') }}</th>
|
||||
<th class="text-right">{{ __('Produktbestand') }}</th>
|
||||
<th class="text-right">{{ __('Rezeptur-Anteil (g/Stück)') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($ingredient->products as $product)
|
||||
<tr>
|
||||
<td>{{ $product->name }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($productStock[$product->id] ?? 0, 0) }} {{ __('Stück') }}</td>
|
||||
<td class="text-right">
|
||||
@if($product->pivot->gram !== null && $product->pivot->gram !== '')
|
||||
{{ \App\Services\Util::formatNumber($product->pivot->gram, 2) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="3"><div class="wawi-empty">{{ __('Dieser Rohstoff wird in keiner aktiven Rezeptur verwendet.') }}</div></td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<th>{{ __('Lieferzeit') }}</th>
|
||||
<th class="text-right">{{ __('Letzter Einkauf (Netto/kg)') }}</th>
|
||||
<th class="text-right" style="width: 11rem;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($ingredient->suppliers as $supplier)
|
||||
@php
|
||||
$deliveryTime = $ingredient->delivery_time ?: $supplier->delivery_time;
|
||||
$lastPrice = $lastPriceBySupplier[$supplier->id] ?? null;
|
||||
$orderUrl = $supplier->order_url ?: $supplier->url;
|
||||
$orderEmail = $supplier->order_email ?: $supplier->email;
|
||||
@endphp
|
||||
<tr>
|
||||
<td>
|
||||
<span class="wawi-item-name">{{ $supplier->name }}</span>
|
||||
@if($supplier->pivot->preferred)
|
||||
<span class="wawi-pill wawi-pill--ok ml-1">{{ __('bevorzugt') }}</span>
|
||||
@endif
|
||||
@if($supplier->pivot->supplier_sku)
|
||||
<div class="wawi-item-sub">{{ __('Art.-Nr.') }}: {{ $supplier->pivot->supplier_sku }}</div>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $deliveryTime ?: '—' }}</td>
|
||||
<td class="text-right">
|
||||
@if($lastPrice !== null)
|
||||
{{ \App\Services\Util::formatNumber($lastPrice, 2) }} €
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if($supplier->order_method === 'online_shop' && $orderUrl)
|
||||
<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-outline-secondary">{{ __('Per Mail') }}</a>
|
||||
@elseif($orderUrl)
|
||||
<a href="{{ $orderUrl }}" target="_blank" rel="noopener" class="btn btn-sm btn-outline-secondary">{{ __('Zum Shop') }}</a>
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<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="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>
|
||||
|
||||
<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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Produkt') }}</th>
|
||||
<th class="text-right">{{ __('Produktbestand') }}</th>
|
||||
<th class="text-right">{{ __('Rezeptur-Anteil (g/Stück)') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($ingredient->products as $product)
|
||||
<div class="wawi-card">
|
||||
<div class="wawi-card__header">{{ __('Offene Bestellungen / unterwegs') }}</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table wawi-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ $product->name }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($productStock[$product->id] ?? 0, 0) }} {{ __('Stück') }}</td>
|
||||
<td class="text-right">
|
||||
@if($product->pivot->gram !== null && $product->pivot->gram !== '')
|
||||
{{ \App\Services\Util::formatNumber($product->pivot->gram, 2) }} g
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
<th>{{ __('Bestellt am') }}</th>
|
||||
<th>{{ __('Lieferant') }}</th>
|
||||
<th>{{ __('Lagerort') }}</th>
|
||||
<th class="text-right">{{ __('Bestellte Menge') }}</th>
|
||||
<th class="text-right" style="width: 8rem;"></th>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="3" class="text-center text-muted py-3">{{ __('Dieser Rohstoff wird in keiner aktiven Rezeptur verwendet.') }}</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Lieferant') }}</th>
|
||||
<th>{{ __('Lieferzeit') }}</th>
|
||||
<th class="text-right">{{ __('Letzter Einkauf (Netto/kg)') }}</th>
|
||||
<th class="text-right" style="width: 11rem;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($ingredient->suppliers as $supplier)
|
||||
@php
|
||||
$deliveryTime = $ingredient->delivery_time ?: $supplier->delivery_time;
|
||||
$lastPrice = $lastPriceBySupplier[$supplier->id] ?? null;
|
||||
$orderUrl = $supplier->order_url ?: $supplier->url;
|
||||
$orderEmail = $supplier->order_email ?: $supplier->email;
|
||||
@endphp
|
||||
<tr>
|
||||
<td>
|
||||
{{ $supplier->name }}
|
||||
@if($supplier->pivot->preferred)
|
||||
<span class="badge badge-success ml-1">{{ __('bevorzugt') }}</span>
|
||||
@endif
|
||||
@if($supplier->pivot->supplier_sku)
|
||||
<div class="text-muted small">{{ __('Art.-Nr.') }}: {{ $supplier->pivot->supplier_sku }}</div>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $deliveryTime ?: '—' }}</td>
|
||||
<td class="text-right">
|
||||
@if($lastPrice !== null)
|
||||
{{ \App\Services\Util::formatNumber($lastPrice, 2) }} €
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</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>
|
||||
@elseif($orderEmail)
|
||||
<a href="mailto:{{ $orderEmail }}?subject={{ rawurlencode(__('Bestellung') . ': ' . $ingredient->name) }}" class="btn btn-sm btn-dark">{{ __('Per Mail') }}</a>
|
||||
@elseif($orderUrl)
|
||||
<a href="{{ $orderUrl }}" target="_blank" rel="noopener" class="btn btn-sm btn-dark">{{ __('Zum Shop') }}</a>
|
||||
@else
|
||||
<span class="text-muted">—</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted py-3">{{ __('Diesem Rohstoff ist kein Lieferant zugeordnet.') }}</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if(Auth::user()->isAdmin())
|
||||
<div class="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>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($openOrders as $order)
|
||||
<tr>
|
||||
<td>{{ $order->ordered_at?->format('d.m.Y') ?? '—' }}</td>
|
||||
<td>{{ $order->supplier?->name ?? '—' }}</td>
|
||||
<td>{{ $order->location?->name ?? '—' }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($order->ordered_quantity, 0) }} g</td>
|
||||
<td class="text-right">
|
||||
<a href="{{ route('admin.inventory.stock-entries.show', $order) }}" class="btn icon-btn btn-sm btn-primary" title="{{ __('Wareneingang buchen') }}">
|
||||
<span class="far fa-eye"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5"><div class="wawi-empty">{{ __('Keine offenen Bestellungen.') }}</div></td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Bestellt am') }}</th>
|
||||
<th>{{ __('Lieferant') }}</th>
|
||||
<th>{{ __('Lagerort') }}</th>
|
||||
<th class="text-right">{{ __('Bestellte Menge') }}</th>
|
||||
<th class="text-right" style="width: 8rem;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($openOrders as $order)
|
||||
<tr>
|
||||
<td>{{ $order->ordered_at?->format('d.m.Y') ?? '—' }}</td>
|
||||
<td>{{ $order->supplier?->name ?? '—' }}</td>
|
||||
<td>{{ $order->location?->name ?? '—' }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($order->ordered_quantity, 0) }} g</td>
|
||||
<td class="text-right">
|
||||
<a href="{{ route('admin.inventory.stock-entries.show', $order) }}" class="btn icon-btn btn-sm btn-primary" title="{{ __('Wareneingang buchen') }}">
|
||||
<span class="far fa-eye"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted py-3">{{ __('Keine offenen Bestellungen.') }}</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
@if($openOrders->isNotEmpty())
|
||||
<div class="wawi-card__footer text-muted small">
|
||||
{{ __('Offene Bestellungen zählen erst nach gebuchtem Wareneingang zum Bestand.') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@if($openOrders->isNotEmpty())
|
||||
<div class="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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('Charge') }}</th>
|
||||
<th>{{ __('Lieferant') }}</th>
|
||||
<th>{{ __('Lagerort') }}</th>
|
||||
<th>{{ __('MHD') }}</th>
|
||||
<th class="text-right">{{ __('Restbestand') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($charges as $charge)
|
||||
<div class="wawi-card">
|
||||
<div class="wawi-card__header">{{ __('Verfügbare Chargen') }}</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table wawi-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ $charge->batch_number ?: '#' . $charge->id }}</td>
|
||||
<td>{{ $charge->supplier?->name ?? '—' }}</td>
|
||||
<td>{{ $charge->location?->name ?? '—' }}</td>
|
||||
<td>{{ $charge->best_before?->format('d.m.Y') ?? '—' }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($charge->getAttribute('remaining_quantity'), 0) }} g</td>
|
||||
<th>{{ __('Charge') }}</th>
|
||||
<th>{{ __('Lieferant') }}</th>
|
||||
<th>{{ __('Lagerort') }}</th>
|
||||
<th>{{ __('MHD') }}</th>
|
||||
<th class="text-right">{{ __('Restbestand') }}</th>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted py-3">{{ __('Kein Restbestand vorhanden.') }}</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
@if($charges->isNotEmpty() && count($remainingByLocation) > 0)
|
||||
<tfoot>
|
||||
@foreach($locations as $location)
|
||||
@if(isset($remainingByLocation[$location->id]) && $remainingByLocation[$location->id] > 0)
|
||||
<tr>
|
||||
<td colspan="4" class="text-right text-muted">{{ __('Bestand') }} {{ $location->name }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($remainingByLocation[$location->id], 0) }} g</td>
|
||||
</tr>
|
||||
@endif
|
||||
@endforeach
|
||||
</tfoot>
|
||||
@endif
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($charges as $charge)
|
||||
<tr>
|
||||
<td>{{ $charge->batch_number ?: '#' . $charge->id }}</td>
|
||||
<td>{{ $charge->supplier?->name ?? '—' }}</td>
|
||||
<td>{{ $charge->location?->name ?? '—' }}</td>
|
||||
<td>{{ $charge->best_before?->format('d.m.Y') ?? '—' }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($charge->getAttribute('remaining_quantity'), 0) }} g</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5"><div class="wawi-empty">{{ __('Kein Restbestand vorhanden.') }}</div></td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
@if($charges->isNotEmpty() && count($remainingByLocation) > 0)
|
||||
<tfoot>
|
||||
@foreach($locations as $location)
|
||||
@if(isset($remainingByLocation[$location->id]) && $remainingByLocation[$location->id] > 0)
|
||||
<tr>
|
||||
<td colspan="4" class="text-right text-muted">{{ __('Bestand') }} {{ $location->name }}</td>
|
||||
<td class="text-right">{{ \App\Services\Util::formatNumber($remainingByLocation[$location->id], 0) }} g</td>
|
||||
</tr>
|
||||
@endif
|
||||
@endforeach
|
||||
</tfoot>
|
||||
@endif
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue