Absatzmengen: nach Warenausgang (Versanddatum) oder nach Rechnung auswertbar

- 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>
This commit is contained in:
Kevin 2026-09-23 15:42:39 +00:00
parent a21bebc932
commit d01b9bca3a
7 changed files with 1139 additions and 432 deletions

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* Absatzmengen nach Versand: `shopping_orders.shipped` ist nur ein Status ohne
* Zeitstempel, deshalb war eine Auswertung "was ist in Monat X rausgegangen"
* bisher nicht moeglich. `shipped_at` haelt den Versandzeitpunkt fest,
* `shipped_at_source` dokumentiert, woher er stammt (DHL-Label, Live-Erfassung
* oder Schaetzung aus dem Backfill).
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('shopping_orders', function (Blueprint $table) {
if (! Schema::hasColumn('shopping_orders', 'shipped_at')) {
$table->timestamp('shipped_at')->nullable()->index()->after('tracking');
}
if (! Schema::hasColumn('shopping_orders', 'shipped_at_source')) {
$table->string('shipped_at_source', 20)->nullable()->after('shipped_at');
}
});
}
public function down(): void
{
Schema::table('shopping_orders', function (Blueprint $table) {
if (Schema::hasColumn('shopping_orders', 'shipped_at_source')) {
$table->dropColumn('shipped_at_source');
}
if (Schema::hasColumn('shopping_orders', 'shipped_at')) {
$table->dropColumn('shipped_at');
}
});
}
};