- Stats\Sales: alle Summenzeilen (Angezeigt/Ausgeblendet/Gesamt/Rabatte/Netto) aus price_net × qty der Positionen statt aus subtotal_full/discount des Bestellkopfs; WooCommerce-Importe (txaction extern) hatten dort NULL und zählten seit 06/2026 mit 0 € (Aug 2026: 449 € statt 3.318 € angezeigt) - Kompensationsprodukte (comp > 0) bleiben in der Menge, neue Spalten „davon Kompensation“ (aktuell + Vorjahr) und Summenzeile 9993 - Gelöschte Produkte bleiben im Umsatz sichtbar (withTrashed, „(gelöscht)“) - WP-Import setzt subtotal_full und discount jetzt wie der Checkout - Migration füllt subtotal_full/discount bestehender Extern-Bestellungen nach - Excel-Export um die beiden Kompensationsspalten erweitert - Test tests/Feature/StatsSalesVolumeTest.php (Konsistenz, Filter, Jahr) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
151 lines
7.6 KiB
PHP
Executable file
151 lines
7.6 KiB
PHP
Executable file
@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.products') }} {{ __('navigation.sales_volumes') }}
|
||
</h5>
|
||
<div class="card-body">
|
||
{!! Form::open(['action' => route('admin_stats_sales_volumes_download'), 'class' => 'form-horizontal']) !!}
|
||
{!! Form::hidden('key', 'value') !!}
|
||
<button type="submit" name="action" value="export" class="btn btn-md btn-primary mb-2"><i class="ion ion-md-download"></i> Export als xls</button>
|
||
<hr>
|
||
<div class="form-row align-items-center px-0 pb-2 pt-0">
|
||
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
|
||
<select class="custom-select on_change_select_filter" name="product_sales_vol_filter_month">
|
||
@foreach($filter_months as $key=>$value)
|
||
<option value="{{$key}}" @if(session('product_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="product_sales_vol_filter_year">
|
||
@foreach($filter_years as $key=>$value)
|
||
<option value="{{$value}}" @if(session('product_sales_vol_filter_year') == $value) selected @endif>{{$value}}</option>
|
||
@endforeach
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-row align-items-center px-0 pb-2 pt-0">
|
||
<div class="col-12 col-sm-6 col-md-4 col-lg-4 mb-1">
|
||
<select class="selectpicker" name="product_sales_vol_filter_products[]" id="product_sales_vol_filter_products" data-style="btn-light" data-live-search="true" multiple>
|
||
<option value="0" selected>Produkte ausschließen</option>
|
||
@foreach($filter_products as $key=>$value)
|
||
<option value="{{$key}}" @if(in_array($key, session('product_sales_vol_filter_products'))) selected @endif>{{$value}}</option>
|
||
@endforeach
|
||
</select> <br>
|
||
<em class="">Die Produktliste erst nach der Filtern von Monat und Jahr aktualisieren</em>
|
||
</div>
|
||
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mt-1 mb-3">
|
||
<button type="submit" name="action" value="filter" class="btn btn-md btn-primary mb-4"><i class="ion ion-md-refresh"></i> Filtern</button>
|
||
</div>
|
||
</div>
|
||
{!! Form::close() !!}
|
||
|
||
<div class="card">
|
||
<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>
|
||
<th>davon Kompensation</th>
|
||
<th>{{__('tables.total')}} netto €</th>
|
||
<th>Vorjahr {{__('tables.quantity')}}</th>
|
||
<th>Vorjahr davon Kompensation</th>
|
||
<th>Vorjahr {{__('tables.total')}} netto €</th>
|
||
<th>{{__('Einzelrabatt')}}</th>
|
||
<th>{{__('Einzelrabatt %')}}</th>
|
||
<th>{{__('VP Einzelrabatt in %')}}</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<br>
|
||
<div class="alert alert-info m-2">
|
||
Alle Umsätze werden aus den Bestellpositionen (Netto-Einzelpreis × Menge) berechnet: „Angezeigter Umsatz“ + „Ausgeblendeter Umsatz“ = „Gesamter Umsatz“. Bei "Gesamter Umsatz", "Gesamte Rabatte", "Gesamt netto" sind die ausgeschlossenen Produkte einberechnet, da Rabatte auf Warenkorb-Ebene verrechnet werden und nicht auf Produkt-Ebene.<br>
|
||
Kompensationsprodukte (0 € Umsatz) sind in der Menge enthalten und werden in „davon Kompensation“ separat ausgewiesen.
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script>
|
||
|
||
$( document ).ready(function() {
|
||
|
||
|
||
var oTable = $('#datatable-sales-volume').DataTable({
|
||
"processing": true,
|
||
"serverSide": true,
|
||
"stateSave": true,
|
||
"searching": false,
|
||
ajax: {
|
||
url: '{!! route('admin_stats_sales_volumes_datatable') !!}',
|
||
data: function(d) {
|
||
d.product_sales_vol_filter_month = $('select[name=product_sales_vol_filter_month]').val();
|
||
d.product_sales_vol_filter_year = $('select[name=product_sales_vol_filter_year]').val();
|
||
}
|
||
},
|
||
"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: 'qty', name: 'qty', orderable: true, searchable: false },
|
||
{ data: 'comp_qty', name: 'comp_qty', orderable: true, searchable: false },
|
||
{ data: 'total', name: 'total', orderable: true, searchable: false },
|
||
{ data: 'pre_qty', name: 'pre_qty', orderable: true, searchable: false },
|
||
{ data: 'pre_comp_qty', name: 'pre_comp_qty', orderable: true, searchable: false },
|
||
{ data: 'pre_total', name: 'pre_total', orderable: true, searchable: false },
|
||
{ data: 'single_commission', name: 'single_commission', orderable: true, searchable: false },
|
||
{ data: 'value_commission', name: 'value_commission', orderable: true, searchable: false },
|
||
{ data: 'partner_commission', name: 'partner_commission', orderable: true, searchable: false },
|
||
|
||
|
||
],
|
||
"bLengthChange": false,
|
||
"iDisplayLength": 100,
|
||
"language": {
|
||
"url": "/js/datatables-{{ \App::getLocale() }}.json"
|
||
}
|
||
});
|
||
|
||
$('select.on_change_select_filter').on('change', function(){
|
||
// oTable.draw();
|
||
});
|
||
|
||
$('input.on_keyup_input_filter').on('keyup', function(){
|
||
// oTable.draw();
|
||
});
|
||
|
||
|
||
|
||
|
||
|
||
});
|
||
</script>
|
||
@endsection
|