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:
parent
a21bebc932
commit
d01b9bca3a
7 changed files with 1139 additions and 432 deletions
|
|
@ -142,7 +142,7 @@ class ShoppingOrder extends Model
|
|||
|
||||
use SoftDeletes;
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
protected $dates = ['deleted_at', 'shipped_at'];
|
||||
|
||||
protected $fillable = [
|
||||
'shopping_user_id',
|
||||
|
|
@ -177,6 +177,8 @@ class ShoppingOrder extends Model
|
|||
'customer_order_source_comment',
|
||||
'shipped',
|
||||
'tracking',
|
||||
'shipped_at',
|
||||
'shipped_at_source',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
|
@ -187,6 +189,7 @@ class ShoppingOrder extends Model
|
|||
'is_abo' => 'bool',
|
||||
'recovery_user_abo_id' => 'int',
|
||||
'points' => 'float',
|
||||
'shipped_at' => 'datetime',
|
||||
];
|
||||
|
||||
public const CUSTOMER_ORDER_SOURCE_OPTIONS = [
|
||||
|
|
@ -199,6 +202,49 @@ class ShoppingOrder extends Model
|
|||
'other' => 'Sonstiges',
|
||||
];
|
||||
|
||||
/**
|
||||
* Versandstatus, die als "ist rausgegangen" gelten ({@see self::$shippedTypes}).
|
||||
*/
|
||||
public const SHIPPED_STATES = [2, 3, 4];
|
||||
|
||||
public const SHIPPED_AT_SOURCE_LIVE = 'live';
|
||||
|
||||
public const SHIPPED_AT_SOURCE_DHL = 'dhl';
|
||||
|
||||
public const SHIPPED_AT_SOURCE_ESTIMATED = 'geschaetzt';
|
||||
|
||||
/**
|
||||
* Setzt den Versandzeitpunkt, sobald der Versandstatus auf "versendet" wechselt.
|
||||
* Der Hook sitzt bewusst im Model, weil der Status an mehreren Stellen gesetzt
|
||||
* wird (Admin-Liste, Abo-Fulfillment, API) und keine davon uebersehen werden darf.
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (self $order): void {
|
||||
if ($order->shipped_at !== null) {
|
||||
return;
|
||||
}
|
||||
if (! in_array((int) $order->shipped, self::SHIPPED_STATES, true)) {
|
||||
return;
|
||||
}
|
||||
if ($order->exists && ! $order->isDirty('shipped')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order->shipped_at = now();
|
||||
$order->shipped_at_source = self::SHIPPED_AT_SOURCE_LIVE;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bestellungen, die im angegebenen Zeitraum versendet wurden.
|
||||
*/
|
||||
public function scopeShippedBetween(Builder $query, Carbon $from, Carbon $to): Builder
|
||||
{
|
||||
return $query->whereIn('shipped', self::SHIPPED_STATES)
|
||||
->whereBetween('shipped_at', [$from, $to]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue