gruene-seele/resources/views/admin/inventory/orders/index.blade.php
Kevin Adametz a367d851f6 Warenwirtschaft: AP-33a Bestell-Abwicklung — Nav „Bestellungen" + Fulfillment-Board
Neuer Nav-Punkt „Bestellungen" (Abwicklung + API-Bestellungen als Unterpunkte).
Board für Versand-Mitarbeiter (copyreader): 4 Filter-Kacheln (heute eingegangen /
zu konfektionieren / versandfertig / heute versendet), Packliste je Bestellung
(Sets mit Komponenten), Dokumente (Rechnung/Lieferschein/Kombi-Download über
eigene copyreader-Route, Erstellen ohne Mailversand; API-Bestellungen verlinken
die externe Rechnung), Aktionen Konfektioniert/Versendet (nur vorwärts, Storno
bleibt Verkäufe/admin). Versandfertig-Regel per Kundenentscheid: Rechnung sofort,
Vorkasse nach Zahlungseingang, API immer (ShoppingOrder::isReadyToShip()).
Statuswechsel-Logik in OrderStatusService extrahiert (Verkäufe-Modal + Board
teilen Transaktion/Lock/Margin-Folgen/AP-13-Abzug). Kundenentscheid Punkt 3:
Board-Statuswechsel setzt shipped_status_locked — die Bestell-Detailseiten
ändern den Status dann nicht mehr automatisch (offen → in Bearbeitung).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:30:57 +00:00

237 lines
16 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')
@section('content')
@include('admin.inventory.partials.wawi-ui')
<div class="wawi-page">
<div class="wawi-page-head">
<div>
<h1 class="wawi-page-head__title">{{ __('Bestell-Abwicklung') }}</h1>
<p class="wawi-page-head__subtitle">{{ __('Tagesübersicht: konfektionieren, Dokumente drucken, versenden — versandfertig = bezahlt (Rechnung sofort, Vorkasse nach Zahlungseingang, API immer)') }}</p>
</div>
<div class="wawi-page-head__actions">
<a href="{{ route('admin.inventory.api-orders.index') }}" class="btn btn-outline-secondary btn-sm">
<span class="fas fa-cloud-download-alt mr-1"></span>{{ __('API-Bestellungen') }}
</a>
</div>
</div>
<div class="wawi-stats">
<a class="wawi-stat is-clickable text-reset" href="{{ route('admin.inventory.orders.index', ['filter' => 'today']) }}">
<div class="wawi-stat__icon"><span class="fas fa-inbox"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $counts['today'] }}</div>
<div class="wawi-stat__label">{{ __('Heute eingegangen') }}</div>
</div>
</a>
<a class="wawi-stat wawi-stat--warning is-clickable text-reset" href="{{ route('admin.inventory.orders.index', ['filter' => 'pick']) }}">
<div class="wawi-stat__icon"><span class="fas fa-box-open"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $counts['pick'] }}</div>
<div class="wawi-stat__label">{{ __('Zu konfektionieren') }}</div>
</div>
</a>
<a class="wawi-stat wawi-stat--ok is-clickable text-reset" href="{{ route('admin.inventory.orders.index', ['filter' => 'ready']) }}">
<div class="wawi-stat__icon"><span class="fas fa-truck-loading"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $counts['ready'] }}</div>
<div class="wawi-stat__label">{{ __('Versandfertig') }}</div>
</div>
</a>
<a class="wawi-stat is-clickable text-reset" href="{{ route('admin.inventory.orders.index', ['filter' => 'shipped_today']) }}">
<div class="wawi-stat__icon"><span class="fas fa-shipping-fast"></span></div>
<div class="wawi-stat__body">
<div class="wawi-stat__value">{{ $counts['shipped_today'] }}</div>
<div class="wawi-stat__label">{{ __('Heute versendet') }}</div>
</div>
</a>
</div>
<div class="wawi-card">
<div class="wawi-toolbar">
<div class="d-flex align-items-center" style="gap:.75rem;">
@if ($filter !== '')
<a href="{{ route('admin.inventory.orders.index') }}" class="btn btn-sm btn-outline-secondary">
<span class="fas fa-times mr-1"></span>{{ __('Filter zurücksetzen') }}
</a>
@endif
<input type="search" id="of-search" class="form-control form-control-sm" style="width:240px"
placeholder="{{ __('Suche Bestellnr. / Kunde …') }}">
</div>
</div>
<div class="table-responsive">
<table class="table wawi-table" id="of-table">
<thead>
<tr>
<th>{{ __('Bestellung') }}</th>
<th>{{ __('Datum') }}</th>
<th>{{ __('Kunde') }}</th>
<th class="text-right">{{ __('Positionen') }}</th>
<th class="text-right">{{ __('Gewicht') }}</th>
<th>{{ __('Zahlung') }}</th>
<th>{{ __('Status') }}</th>
<th>{{ __('Dokumente') }}</th>
<th class="text-right">{{ __('Aktionen') }}</th>
</tr>
</thead>
<tbody>
@forelse ($orders as $order)
@php
$customer = $order->shopping_user
? trim(($order->shopping_user->shipping_firstname ?: $order->shopping_user->billing_firstname ?? '').' '.($order->shopping_user->shipping_lastname ?: $order->shopping_user->billing_lastname ?? ''))
: '';
$clearingtype = $order->shopping_payment_last?->clearingtype;
$readyToShip = $order->isReadyToShip();
$itemCount = $order->shopping_order_items->sum('qty');
@endphp
<tr class="of-row" data-search="{{ mb_strtolower($order->id.' '.$customer) }}">
<td>
<a href="{{ route('admin_sales_detail', $order->id) }}">#{{ $order->id }}</a>
@if ($order->isApiOrder())
<span class="wawi-pill" style="background:rgba(24,28,33,.06);color:#6c757d;">API</span>
@elseif ($order->user_shop_id)
<span class="wawi-pill" style="background:rgba(24,28,33,.06);color:#6c757d;">{{ __('Shop') }}</span>
@elseif ($order->promotion_user_id)
<span class="wawi-pill" style="background:rgba(24,28,33,.06);color:#6c757d;">{{ __('Promo') }}</span>
@endif
</td>
<td>{{ $order->created_at->format('d.m.Y H:i') }}</td>
<td>
<span class="wawi-item-name">{{ $customer !== '' ? $customer : '—' }}</span>
@if ($order->shipping_country?->country)
<span class="text-muted small">{{ strtoupper($order->shipping_country->country->code) }}</span>
@endif
</td>
<td class="text-right">
<button type="button" class="btn btn-sm btn-link p-0 js-of-toggle" data-target="of-items-{{ $order->id }}">
{{ $itemCount }} <span class="fas fa-chevron-down small"></span>
</button>
</td>
<td class="text-right">{{ $order->weight ? number_format($order->weight / 1000, 2, ',', '.').' kg' : '—' }}</td>
<td>
@if ($readyToShip)
<span class="wawi-pill wawi-pill--ok">{{ __('versandfertig') }}</span>
@else
<span class="wawi-pill wawi-pill--warning">{{ __('wartet auf Zahlung') }}</span>
@endif
@if ($clearingtype === 'fnc')
<div class="small text-muted">{{ __('Rechnung') }}</div>
@elseif ($clearingtype === 'vor')
<div class="small text-muted">{{ __('Vorkasse') }}</div>
@endif
</td>
<td>
@if ((int) $order->shipped === 0)
<span class="wawi-pill wawi-pill--warning">{{ __('offen') }}</span>
@else
<span class="wawi-pill" style="background:rgba(3,155,229,.12);color:#0277bd;">{{ $order->getShippedType() }}</span>
@endif
</td>
<td>
@if ($order->isApiOrder())
@if ($order->wp_invoice_path)
<a href="{{ $order->wp_invoice_path }}" target="_blank" rel="noopener" title="{{ __('Externe Rechnung (Shop)') }}">
<span class="fas fa-external-link-alt"></span> {{ __('Extern') }}
</a>
@else
<span class="text-muted small">{{ __('extern') }}</span>
@endif
@elseif (\App\Services\Invoice::isInvoice($order))
<a href="{{ route('admin.inventory.orders.document', [$order->id, 'invoice']) }}" target="_blank" title="{{ __('Rechnung') }}" class="mr-1"><span class="far fa-file-alt"></span> {{ __('Re.') }}</a>
<a href="{{ route('admin.inventory.orders.document', [$order->id, 'delivery']) }}" target="_blank" title="{{ __('Lieferschein') }}" class="mr-1"><span class="fas fa-dolly"></span> {{ __('LS') }}</a>
<a href="{{ route('admin.inventory.orders.document', [$order->id, 'combined']) }}" target="_blank" title="{{ __('Rechnung + Lieferschein') }}"><span class="fas fa-print"></span></a>
@else
<form action="{{ route('admin.inventory.orders.documents', $order->id) }}" method="post" class="d-inline">
@csrf
<input type="hidden" name="back_filter" value="{{ $filter }}">
<button type="submit" class="btn btn-sm btn-outline-secondary" title="{{ __('Rechnung + Lieferschein erstellen (ohne E-Mail-Versand)') }}"
onclick="return confirm(@json(__('Rechnung + Lieferschein jetzt erstellen? (Es wird keine E-Mail versendet — Versand wie gewohnt über die Bestell-Details.)')));">
<span class="fas fa-file-medical mr-1"></span>{{ __('erstellen') }}
</button>
</form>
@endif
</td>
<td class="text-right" style="white-space:nowrap;">
@if ((int) $order->shipped === 0)
<form action="{{ route('admin.inventory.orders.status', $order->id) }}" method="post" class="d-inline">
@csrf
<input type="hidden" name="shipped" value="1">
<input type="hidden" name="back_filter" value="{{ $filter }}">
<button type="submit" class="btn btn-sm btn-outline-primary" title="{{ __('Als konfektioniert (in Bearbeitung) markieren') }}">
<span class="fas fa-box mr-1"></span>{{ __('Konfektioniert') }}
</button>
</form>
@endif
@if (in_array((int) $order->shipped, [0, 1], true))
<form action="{{ route('admin.inventory.orders.status', $order->id) }}" method="post" class="d-inline"
onsubmit="return confirm(@json(__('Bestellung als versendet buchen? Der Produktbestand wird abgezogen.')));">
@csrf
<input type="hidden" name="shipped" value="2">
<input type="hidden" name="back_filter" value="{{ $filter }}">
<button type="submit" class="btn btn-sm btn-primary" title="{{ __('Als versendet buchen (Bestandsabzug)') }}">
<span class="fas fa-shipping-fast mr-1"></span>{{ __('Versendet') }}
</button>
</form>
@endif
</td>
</tr>
<tr class="of-items d-none" id="of-items-{{ $order->id }}">
<td colspan="9" class="pl-4" style="background:rgba(24,28,33,.02);">
<strong class="small text-muted">{{ __('Packliste') }}</strong>
<ul class="mb-1 small">
@foreach ($order->shopping_order_items as $item)
<li>
{{ $item->qty }} × {{ $item->product?->name ?? __('(Produkt gelöscht)') }}
@if ($item->product?->is_set)
<span class="wawi-pill" style="background:rgba(24,28,33,.06);color:#6c757d;">SET</span>
<ul class="text-muted">
@foreach ($item->product->setItems as $component)
<li>{{ $item->qty * $component->pivot->quantity }} × {{ $component->name }}</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
</td>
</tr>
@empty
<tr>
<td colspan="9">
<div class="wawi-empty">
<div><span class="fas fa-mug-hot"></span></div>
{{ __('Nichts zu tun — keine Bestellungen in diesem Filter.') }}
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if (($listLimit ?? null) !== null && $orders->count() >= $listLimit)
<p class="text-muted small px-3 pb-2 mb-0">
{{ __('Es werden die neuesten :n Bestellungen angezeigt.', ['n' => $listLimit]) }}
<a href="{{ route('admin.inventory.orders.index', array_filter(['all' => 1, 'filter' => $filter])) }}">{{ __('Alle anzeigen') }}</a>
</p>
@endif
</div>
</div>
<script>
$(function () {
$('.js-of-toggle').on('click', function () {
$('#' + $(this).data('target')).toggleClass('d-none');
});
$('#of-search').on('input', function () {
var term = ($(this).val() || '').toLowerCase().trim();
$('#of-table tbody tr.of-row').each(function () {
var show = term === '' || ($(this).data('search') || '').toString().indexOf(term) !== -1;
$(this).toggle(show);
if (!show) {
$('#of-items-' + $(this).find('a').first().text().replace('#', '')).addClass('d-none');
}
});
});
});
</script>
@endsection