- shopping_orders.shipped_at (+ Herkunft live/dhl/geschaetzt): gesetzt im Model, sobald der Versandstatus auf versendet wechselt, egal an welcher Stelle der Status gesetzt wird. sales:backfill-shipped-at traegt Altbestellungen aus dem DHL-Label nach oder schaetzt. - SalesVolumeService rechnet Absatzmengen je Produkt wahlweise nach Versand (inkl. externer/API-Bestellungen, Stornos im Monat des Stornobelegs) oder nach Rechnung. Sets werden fuer die zweite Liste in Einzelartikel aufgeloest. - Admin-Seite: Umschalter Versand/Rechnung, Datenqualitaet (Anteil geschaetzter Versanddaten), Export Produkte und Einzelartikel. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
194 lines
9.3 KiB
PHP
194 lines
9.3 KiB
PHP
@extends('layouts.layout-2')
|
|
|
|
@section('content')
|
|
|
|
@if ($errors->any())
|
|
<div class="row">
|
|
<div class="col-sm-12">
|
|
<div class="alert alert-danger">
|
|
<ul>
|
|
@foreach ($errors->all() as $error)
|
|
<li>{{ $error }}</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="card">
|
|
<h5 class="card-header">
|
|
{{ __('navigation.sales_volumes') }} {{ __('navigation.products') }} – {{ $basis_label }}
|
|
</h5>
|
|
<div class="card-body">
|
|
{!! Form::open(['action' => route('admin_payments_sales_volumes_download'), 'class' => '']) !!}
|
|
{!! Form::hidden('key', 'value') !!}
|
|
<button type="submit" name="action" value="export" class="btn btn-sm btn-primary mb-2"><i class="ion ion-md-download"></i> Export Produkte als xls</button>
|
|
|
|
|
|
<button type="submit" name="action" value="export_articles" class="btn btn-sm btn-primary mb-2"><i class="ion ion-md-download"></i> Export Einzelartikel als xls</button>
|
|
|
|
|
|
<button type="submit" name="action" value="exportfull_paid_invoice" class="btn btn-sm btn-primary mb-2"><i class="ion ion-md-download"></i> Export vollständige Liste als xls</button>
|
|
|
|
<hr>
|
|
<div class="form-row align-items-center px-0 pb-2 pt-0">
|
|
<div class="col-12 col-sm-4 col-md-4 col-lg-4 mb-1">
|
|
<select class="custom-select" id="payment_sales_vol_filter_basis" name="payment_sales_vol_filter_basis">
|
|
@foreach($filter_basis as $key=>$value)
|
|
<option value="{{$key}}" @if($basis === $key) selected @endif>{{$value}}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
|
|
<select class="custom-select on_change_select_filter" name="payment_sales_vol_filter_month">
|
|
@foreach($filter_months as $key=>$value)
|
|
<option value="{{$key}}" @if(session('payment_sales_vol_filter_month') == $key) selected @endif>{{$value}}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
|
|
<select class="custom-select on_change_select_filter" name="payment_sales_vol_filter_year">
|
|
@foreach($filter_years as $key=>$value)
|
|
<option value="{{$value}}" @if(session('payment_sales_vol_filter_year') == $value) selected @endif>{{$value}}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
</div>
|
|
{!! Form::close() !!}
|
|
|
|
@if($basis === \App\Services\SalesVolumeService::BASIS_SHIPPING)
|
|
<div class="alert alert-light border small mb-3">
|
|
<i class="fa fa-truck text-muted"></i>
|
|
Gezählt wird, was im gewählten Monat versendet wurde (Warenausgang), inklusive der externen/API-Bestellungen.
|
|
Stornos und Retouren werden im Monat der Stornorechnung abgezogen.
|
|
<br>
|
|
<strong>Datengrundlage:</strong> {{ $quality['orders'] }} versendete Bestellungen im Monat,
|
|
davon {{ $quality['estimated'] }} mit geschätztem Versanddatum ({{ $quality['estimated_percent'] }} %).
|
|
@if($quality['estimated_percent'] > 0)
|
|
Geschätzt wird das Versanddatum bei Bestellungen ohne DHL-Label – also aus der Zeit vor der DHL-Anbindung (produktiv ab 02/2026)
|
|
oder bei anderem Versandweg. Grundlage ist dann die letzte Änderung der Bestellung.
|
|
@endif
|
|
@if($quality['without_date'] > 0)
|
|
<br><span class="text-danger">{{ $quality['without_date'] }} versendete Bestellungen haben noch kein Versanddatum – <code>php artisan sales:backfill-shipped-at --fix</code> ausführen.</span>
|
|
@endif
|
|
</div>
|
|
@else
|
|
<div class="alert alert-light border small mb-3">
|
|
<i class="fa fa-file-invoice text-muted"></i>
|
|
Gezählt wird, was im gewählten Monat fakturiert wurde (Rechnungsdatum = Zahlungseingang).
|
|
Externe/API-Bestellungen erscheinen hier erst über die bezahlte Sammelrechnung des Partners – teils im Folgemonat.
|
|
Stornorechnungen werden abgezogen.
|
|
</div>
|
|
@endif
|
|
|
|
<h6 class="mt-3">Absatz je verkauftem Artikel <small class="text-muted">(Sets zählen als ein Set)</small></h6>
|
|
<div class="card mb-4">
|
|
<div class="card-datatable table-responsive">
|
|
<table class="table table-striped table-bordered" id="datatable-sales-volume">
|
|
|
|
<thead>
|
|
<tr>
|
|
<th>{{__('#') }}</th>
|
|
<th>{{__('tables.product')}}</th>
|
|
<th>{{__('tables.article_no')}}</th>
|
|
<th>{{__('tables.quantity')}}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<h6>Absatz je Einzelartikel <small class="text-muted">(Sets/Kits in ihre hinterlegten Anlagen aufgelöst)</small></h6>
|
|
<div class="card">
|
|
<div class="card-datatable table-responsive">
|
|
<table class="table table-striped table-bordered" id="datatable-sales-volume-articles">
|
|
|
|
<thead>
|
|
<tr>
|
|
<th>{{__('#') }}</th>
|
|
<th>{{__('tables.product')}}</th>
|
|
<th>{{__('tables.article_no')}}</th>
|
|
<th>{{__('tables.quantity')}}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<script>
|
|
|
|
$( document ).ready(function() {
|
|
|
|
function filterData(d) {
|
|
d.payment_sales_vol_filter_month = $('select[name=payment_sales_vol_filter_month]').val();
|
|
d.payment_sales_vol_filter_year = $('select[name=payment_sales_vol_filter_year]').val();
|
|
d.payment_sales_vol_filter_basis = $('select[name=payment_sales_vol_filter_basis]').val();
|
|
}
|
|
|
|
var tableOptions = {
|
|
"processing": true,
|
|
"serverSide": true,
|
|
"stateSave": true,
|
|
"searching": false,
|
|
"order": [[0, "asc" ]],
|
|
"columns": [
|
|
{ data: 'id', orderable: true, searchable: false },
|
|
{ data: 'name', name: 'name', orderable: true, searchable: false },
|
|
{ data: 'number', name: 'number', orderable: true, searchable: false },
|
|
{ data: 'value', name: 'value', orderable: true, searchable: false },
|
|
],
|
|
"bLengthChange": false,
|
|
"iDisplayLength": 100,
|
|
"language": {
|
|
"url": "/js/datatables-{{ \App::getLocale() }}.json"
|
|
}
|
|
};
|
|
|
|
var oTable = $('#datatable-sales-volume').DataTable($.extend({}, tableOptions, {
|
|
ajax: {
|
|
url: '{!! route('admin_payments_sales_volumes_datatable') !!}',
|
|
data: function(d) {
|
|
filterData(d);
|
|
d.list = 'products';
|
|
}
|
|
}
|
|
}));
|
|
|
|
var oTableArticles = $('#datatable-sales-volume-articles').DataTable($.extend({}, tableOptions, {
|
|
ajax: {
|
|
url: '{!! route('admin_payments_sales_volumes_datatable') !!}',
|
|
data: function(d) {
|
|
filterData(d);
|
|
d.list = 'articles';
|
|
}
|
|
}
|
|
}));
|
|
|
|
$('#payment_sales_vol_filter_basis').on('change', function(){
|
|
window.location = '{!! route('admin_payments_sales_volumes') !!}?payment_sales_vol_filter_basis=' + $(this).val();
|
|
});
|
|
|
|
$('select.on_change_select_filter').on('change', function(){
|
|
oTable.draw();
|
|
oTableArticles.draw();
|
|
});
|
|
|
|
$('input.on_keyup_input_filter').on('keyup', function(){
|
|
oTable.draw();
|
|
oTableArticles.draw();
|
|
});
|
|
|
|
|
|
});
|
|
</script>
|
|
@endsection
|