Warenwirtschaft: Anforderungsrunde 12.06. — Plan V5.0 + AP-26/AP-25/AP-22
Neue Anforderungen (docs/) interpretiert und als Entwicklungsplan V5.0 (AP-20 bis AP-28) aufgenommen; erste drei Pakete umgesetzt: AP-26 Ausschuss-Gründe konfigurierbar: - Stammdaten-Tabelle disposal_reasons + CRUD unter Einstellungen → Allgemein - StockDisposalController liest aktive DB-Gründe statt hartkodierter Liste - Seeder übernimmt die bisherigen 6 Gründe idempotent AP-25 Lieferbestand — Datum statt Tage: - "Nicht vorrätig" wird über Datepicker "Wieder lieferbar ab" gepflegt; Resttage-Hinweis zählt täglich automatisch herunter - Interne Bestellliste wieder kaufbar: Hinweis erscheint zusätzlich zu den Mengen-Buttons (VP entscheidet selbst) AP-22 Produktbestand-Erweiterungen: - Default-Sortierung nach Dringlichkeit, Status-Kopf toggelt - Alle vier Status-Kacheln als Filter klickbar - Neue Spalte "Verbrauch/Monat" (Ø Abgänge der letzten 6 Monate) - Produkt-Flag "Im Produktbestand anzeigen" (products.show_in_product_stock) Tests: 77 grün (DisposalReasonSettings 8, ProductOutOfStock 8, ProductStock 13 + Regression). Hinweise-Doku + Plan-Protokoll fortgeschrieben; nächster Schritt laut Plan: AP-21 (INCI-Erweiterungen). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a8f6fef38e
commit
e53201f229
32 changed files with 1377 additions and 94 deletions
|
|
@ -24,14 +24,14 @@
|
|||
</div>
|
||||
|
||||
<div class="wawi-stats">
|
||||
<div class="wawi-stat" data-filter="all">
|
||||
<div class="wawi-stat is-clickable" 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 wawi-stat--ok is-clickable" data-filter="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>
|
||||
|
|
@ -75,7 +75,10 @@
|
|||
<th style="width:3.5rem"></th>
|
||||
<th>{{ __('Name') }}</th>
|
||||
<th class="text-right">{{ __('Bestand') }}</th>
|
||||
<th>{{ __('Status') }}</th>
|
||||
<th class="text-right" title="{{ __('Durchschnitt der letzten 6 Monate') }}">{{ __('Verbrauch/Monat') }}</th>
|
||||
<th id="ps-sort-status" style="cursor:pointer" title="{{ __('Nach Dringlichkeit sortieren') }}">
|
||||
{{ __('Status') }} <span class="fas fa-sort text-muted small"></span>
|
||||
</th>
|
||||
<th class="text-right" style="width:16rem">{{ __('Aktion') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -86,7 +89,7 @@
|
|||
$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'] }}">
|
||||
<tr class="ps-row {{ $rowClass }}" data-name="{{ Str::lower($product->name) }} {{ Str::lower($product->number ?? '') }}" data-status="{{ $row['status'] }}" data-rank="{{ ['critical' => 0, 'warning' => 1, 'ok' => 2][$row['status']] }}">
|
||||
<td>
|
||||
@if ($product->images->count())
|
||||
<img class="wawi-thumb" alt=""
|
||||
|
|
@ -104,6 +107,13 @@
|
|||
<td class="text-right {{ $stockClass }}">
|
||||
{{ \App\Services\Util::formatNumber($row['stock'], 0) }} <span class="text-muted">{{ __('Stück') }}</span>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
@if ($row['monthly_consumption'] > 0)
|
||||
{{ \App\Services\Util::formatNumber($row['monthly_consumption'], 1) }} <span class="text-muted">{{ __('Stück') }}</span>
|
||||
@else
|
||||
<span class="text-muted">–</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if ($row['status'] === 'critical')
|
||||
<span class="wawi-pill wawi-pill--danger">{{ __('Kritisch') }}</span>
|
||||
|
|
@ -131,7 +141,7 @@
|
|||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<td colspan="6">
|
||||
<div class="wawi-empty">
|
||||
<div><span class="fas fa-boxes-stacked"></span></div>
|
||||
{{ __('Keine Produkte vorhanden.') }}
|
||||
|
|
@ -188,6 +198,7 @@
|
|||
<script>
|
||||
$(function () {
|
||||
var $rows = $('#ps-table tbody tr.ps-row');
|
||||
var statusFilter = null; // null = alle; sonst 'ok' | 'warning' | 'critical'
|
||||
|
||||
function applyFilter() {
|
||||
var term = ($('#ps-search').val() || '').toLowerCase().trim();
|
||||
|
|
@ -197,23 +208,39 @@
|
|||
var matchesTerm = term === '' || ($row.data('name') || '').toString().indexOf(term) !== -1;
|
||||
var status = $row.data('status');
|
||||
var matchesCritical = !onlyCritical || (status === 'critical' || status === 'warning');
|
||||
$row.toggle(matchesTerm && matchesCritical);
|
||||
var matchesStatus = statusFilter === null || status === statusFilter;
|
||||
$row.toggle(matchesTerm && matchesCritical && matchesStatus);
|
||||
});
|
||||
}
|
||||
|
||||
$('#ps-search').on('keyup', applyFilter);
|
||||
$('#ps-only-critical').on('change', applyFilter);
|
||||
|
||||
// AP-22: alle vier Kacheln filtern (Produkte = Filter aufheben, OK/Niedrig/Kritisch = nur dieser Status).
|
||||
$('.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();
|
||||
$('#ps-only-critical').prop('checked', false);
|
||||
if (filter === 'all' || wasActive) {
|
||||
statusFilter = null;
|
||||
} else {
|
||||
statusFilter = filter;
|
||||
$this.addClass('is-active');
|
||||
}
|
||||
applyFilter();
|
||||
});
|
||||
|
||||
// AP-22: Klick auf „Status" sortiert nach Dringlichkeit (Default bereits dringlichste oben; Klick toggelt).
|
||||
var statusSortAsc = true; // Server liefert bereits kritisch → ok
|
||||
$('#ps-sort-status').on('click', function () {
|
||||
statusSortAsc = !statusSortAsc;
|
||||
var sorted = $rows.get().sort(function (a, b) {
|
||||
var diff = ($(a).data('rank') - $(b).data('rank'));
|
||||
return statusSortAsc ? diff : -diff;
|
||||
});
|
||||
$('#ps-table tbody').append(sorted);
|
||||
});
|
||||
|
||||
var moveTemplate = "{{ route('admin.inventory.product-stock.movement', ['product' => '__ID__']) }}";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue