gruene-seele/resources/views/admin/inventory/raw-material-stock/show.blade.php
Kevin Adametz 8808ff538f Warenwirtschaft: F-14 — „Enthalten in“ mit Anteil (%) und Verbrauch je Stück
Nur Hersteller-Rezeptur, alle Phasenzeilen je Produkt summiert, Skalierung
über Füllgewicht und Faktor wie in der Produktion; kein roher Pivot-Wert mehr.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 11:07:22 +00:00

305 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@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.wawi-ui')
<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') }}
@if($ingredient->alt_name)
· {{ __('Alternativer Name') }}: {{ $ingredient->alt_name }}
@endif
</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="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="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)
({{ $daysUntilEmpty }} {{ trans_choice('Tag|Tagen', $daysUntilEmpty) }})
@endif
</div>
</div>
</div>
</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 mr-3">
{{ __('Meldebestand') }}: <strong>{{ \App\Services\Util::formatNumber($ingredient->min_stock_alert, 0) }} g</strong>
</span>
@endif
@if($ingredient->storageLabel())
<span class="text-muted small">
{{ __('Lagerort') }}: <strong>{{ $ingredient->storageLabel() }}</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">{{ __('Anteil (%)') }}</th>
<th class="text-right" title="{{ __('Anteil × Füllgewicht × Überschuss-Faktor — wie in der Produktion gerechnet') }}">{{ __('Verbrauch (g/Stück)') }}</th>
</tr>
</thead>
<tbody>
@forelse($ingredient->products as $product)
@php $usage = $recipeUsage[$product->id] ?? null; @endphp
<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($usage)
{{ \App\Services\Util::formatNumber($usage['percent'], 2) }} %
@else
<span class="text-muted">—</span>
@endif
</td>
<td class="text-right">
@if($usage && $usage['consumption_per_piece'] !== null)
{{ \App\Services\Util::formatNumber($usage['consumption_per_piece'], 2) }} g
<span class="text-muted small">({{ __('netto') }} {{ \App\Services\Util::formatNumber($usage['grams_per_piece'], 2) }} g)</span>
@else
<span class="text-muted" title="{{ __('Kein Füllgewicht hinterlegt') }}">—</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="4"><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>
@if(Auth::user()->isAdmin())
<th class="text-right">{{ __('Letzter Einkauf (Netto/kg)') }}</th>
@endif
<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;
// AP-21: Bestellweg pro INCI-Lieferant — Pivot-URL gesetzt ⇒ Shop, leer ⇒ Mail.
$orderUrl = $supplier->pivot->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>
@if(Auth::user()->isAdmin())
<td class="text-right">
@if($lastPrice !== null)
{{ \App\Services\Util::formatNumber($lastPrice, 2) }} €
@else
<span class="text-muted">—</span>
@endif
</td>
@endif
<td class="text-right">
@if($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>
@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 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>
<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"><div class="wawi-empty">{{ __('Keine offenen Bestellungen.') }}</div></td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($openOrders->isNotEmpty())
<div class="wawi-card__footer text-muted small">
{{ __('Offene Bestellungen zählen erst nach gebuchtem Wareneingang zum Bestand.') }}
</div>
@endif
</div>
<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>
<th>{{ __('Lieferant') }}</th>
<th>{{ __('Lagerort') }}</th>
<th>{{ __('MHD') }}</th>
<th class="text-right">{{ __('Restbestand') }}</th>
</tr>
</thead>
<tbody>
{{-- AP-27: Anzeige je Charge (batch_number) summiert; Einzel-Einkäufe als Unterzeilen --}}
@forelse($chargeGroups as $group)
<tr>
<td>
{{ $group['batch_number'] ?: '#' . $group['entries']->first()->id }}
@if($group['entries']->count() > 1)
<span class="badge badge-light border ml-1">{{ $group['entries']->count() }} {{ __('Einkäufe') }}</span>
@endif
</td>
<td>{{ $group['suppliers']->isNotEmpty() ? $group['suppliers']->implode(', ') : '—' }}</td>
<td>{{ $group['locations']->isNotEmpty() ? $group['locations']->implode(', ') : '—' }}</td>
<td>{{ $group['best_before']?->format('d.m.Y') ?? '—' }}</td>
<td class="text-right">{{ \App\Services\Util::formatNumber($group['remaining'], 0) }} g</td>
</tr>
@if($group['entries']->count() > 1)
@foreach($group['entries'] as $charge)
<tr class="text-muted small">
<td class="pl-4">↳ {{ __('Einkauf vom') }} {{ $charge->received_at?->format('d.m.Y') ?? '—' }}</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>
@endforeach
@endif
@empty
<tr>
<td colspan="5"><div class="wawi-empty">{{ __('Kein Restbestand vorhanden.') }}</div></td>
</tr>
@endforelse
</tbody>
@if($chargeGroups->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