9 → 6 Spalten (Datum unter Bestellnr., Gewicht in die Packliste, Zahlung in die Status-Spalte); Aktionen-Spalte sticky am rechten Rand (bleibt beim horizontalen Scrollen sichtbar); unterhalb Desktop-Breite Icon-Buttons ohne Beschriftung (Tooltip bleibt). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
253 lines
17 KiB
PHP
253 lines
17 KiB
PHP
@extends('layouts.layout-2')
|
||
|
||
@section('content')
|
||
@include('admin.inventory.partials.wawi-ui')
|
||
|
||
<style>
|
||
/* AP-33a: iPad-Optimierung — Aktionen bleiben beim horizontalen Scrollen sichtbar */
|
||
#of-table th.of-actions,
|
||
#of-table td.of-actions {
|
||
position: sticky;
|
||
right: 0;
|
||
background: #fff;
|
||
box-shadow: -6px 0 6px -6px rgba(24, 28, 33, .15);
|
||
white-space: nowrap;
|
||
}
|
||
#of-table td { vertical-align: middle; }
|
||
@media (max-width: 1199.98px) {
|
||
.of-btn-text { display: none; }
|
||
#of-table .btn .mr-1 { margin-right: 0 !important; }
|
||
}
|
||
</style>
|
||
|
||
<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>{{ __('Kunde') }}</th>
|
||
<th class="text-center">{{ __('Positionen') }}</th>
|
||
<th>{{ __('Status') }}</th>
|
||
<th>{{ __('Dokumente') }}</th>
|
||
<th class="text-right of-actions">{{ __('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
|
||
<div class="small text-muted">{{ $order->created_at->format('d.m.Y H:i') }}</div>
|
||
</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-center">
|
||
<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>
|
||
@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
|
||
<div class="small mt-1">
|
||
@if ($readyToShip)
|
||
<span class="wawi-pill wawi-pill--ok">{{ __('versandfertig') }}</span>
|
||
@else
|
||
<span class="wawi-pill wawi-pill--danger">{{ __('wartet auf Zahlung') }}</span>
|
||
@endif
|
||
@if ($clearingtype === 'fnc')
|
||
<span class="text-muted">{{ __('Rechnung') }}</span>
|
||
@elseif ($clearingtype === 'vor')
|
||
<span class="text-muted">{{ __('Vorkasse') }}</span>
|
||
@endif
|
||
</div>
|
||
</td>
|
||
<td style="white-space:nowrap;">
|
||
@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><span class="of-btn-text">{{ __('erstellen') }}</span>
|
||
</button>
|
||
</form>
|
||
@endif
|
||
</td>
|
||
<td class="text-right of-actions">
|
||
@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><span class="of-btn-text">{{ __('Konfektioniert') }}</span>
|
||
</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><span class="of-btn-text">{{ __('Versendet') }}</span>
|
||
</button>
|
||
</form>
|
||
@endif
|
||
</td>
|
||
</tr>
|
||
<tr class="of-items d-none" id="of-items-{{ $order->id }}">
|
||
<td colspan="6" class="pl-4" style="background:rgba(24,28,33,.02);">
|
||
<strong class="small text-muted">{{ __('Packliste') }}</strong>
|
||
@if ($order->weight)
|
||
<span class="small text-muted">· {{ __('Gewicht') }}: {{ number_format($order->weight / 1000, 2, ',', '.') }} kg</span>
|
||
@endif
|
||
<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="6">
|
||
<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
|