Warenwirtschaft: AP-09 bis AP-13 (Produktbestand, Set-Produkte, Ausschuss, Konzepte)

- AP-09 Produktbestand inkl. Bewegungshistorie (product_stock_movements, ProductStockService)
- AP-10 Rohstoffbestand-Ansicht je Lager (RawMaterialStockController)
- AP-11 Bestandsschwellen / Out-of-Stock-Handling fuer Produkte und Shop
- AP-12 Ausgang/Ausschuss (stock_disposals, StockDisposalController, InventoryService)
- Set-Produkte (product_set_items) inkl. Aufloesung
- Produktentwicklung & Hinweise-Verwaltung (Notices)
- AP-13 Entwicklungskonzept Shop-Bestandsabzug im Plan dokumentiert
- Feature-Tests fuer neue Module + aktualisierter Entwicklungsplan

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin Adametz 2026-06-03 11:04:22 +00:00
parent 78679e0c55
commit 3ee2d756e9
63 changed files with 5968 additions and 901 deletions

View file

@ -0,0 +1,76 @@
@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>
<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()">
<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">
<thead>
<tr>
<th>{{ __('Datum') }}</th>
<th>{{ __('Art') }}</th>
<th>{{ __('Artikel') }}</th>
<th>{{ __('Charge') }}</th>
<th>{{ __('Lagerort') }}</th>
<th class="text-right">{{ __('Menge') }}</th>
<th>{{ __('Grund') }}</th>
<th>{{ __('Hinweis') }}</th>
<th>{{ __('Mitarbeiter') }}</th>
</tr>
</thead>
<tbody>
@forelse($values as $disposal)
<tr>
<td>{{ $disposal->disposed_at?->format('d.m.Y') }}</td>
<td>
@if ($disposal->isIngredient())
<span class="badge badge-info">{{ __('Rohstoff') }}</span>
@else
<span class="badge badge-secondary">{{ __('Verpackung') }}</span>
@endif
</td>
<td>{{ $disposal->articleName() }}</td>
<td>
@if ($disposal->stockEntry)
{{ $disposal->stockEntry->batch_number ?: '#'.$disposal->stockEntry->id }}
@else
<span class="text-muted"></span>
@endif
</td>
<td>{{ $disposal->location?->name ?? '—' }}</td>
<td class="text-right text-danger font-weight-bold">
{{ \App\Services\Util::formatNumber($disposal->quantity, $disposal->unit === 'piece' ? 0 : 2) }}
{{ $disposal->unit === 'piece' ? __('Stück') : 'g' }}
</td>
<td>{{ $disposal->reason }}</td>
<td class="text-muted">{{ $disposal->note }}</td>
<td>{{ $disposal->user?->getFullName(false) ?: ($disposal->user?->email ?? '—') }}</td>
</tr>
@empty
<tr>
<td colspan="9" class="text-center text-muted py-4">{{ __('Noch keine Ausgänge erfasst.') }}</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection