Warenwirtschaft: AP-33a Bestell-Abwicklung — Nav „Bestellungen" + Fulfillment-Board
Neuer Nav-Punkt „Bestellungen" (Abwicklung + API-Bestellungen als Unterpunkte). Board für Versand-Mitarbeiter (copyreader): 4 Filter-Kacheln (heute eingegangen / zu konfektionieren / versandfertig / heute versendet), Packliste je Bestellung (Sets mit Komponenten), Dokumente (Rechnung/Lieferschein/Kombi-Download über eigene copyreader-Route, Erstellen ohne Mailversand; API-Bestellungen verlinken die externe Rechnung), Aktionen Konfektioniert/Versendet (nur vorwärts, Storno bleibt Verkäufe/admin). Versandfertig-Regel per Kundenentscheid: Rechnung sofort, Vorkasse nach Zahlungseingang, API immer (ShoppingOrder::isReadyToShip()). Statuswechsel-Logik in OrderStatusService extrahiert (Verkäufe-Modal + Board teilen Transaktion/Lock/Margin-Folgen/AP-13-Abzug). Kundenentscheid Punkt 3: Board-Statuswechsel setzt shipped_status_locked — die Bestell-Detailseiten ändern den Status dann nicht mehr automatisch (offen → in Bearbeitung). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f1eb5fbc7f
commit
a367d851f6
9 changed files with 818 additions and 71 deletions
87
app/Services/OrderStatusService.php
Normal file
87
app/Services/OrderStatusService.php
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Models\ShoppingOrder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* AP-33a: Zentraler Versand-Statuswechsel einer Bestellung — extrahiert aus
|
||||
* SalesController::store(), damit Verkäufe-Modal und WaWi-Abwicklungs-Board
|
||||
* dieselbe Logik nutzen (Margin-/Provisionsfolgen, Storno-Flag, AP-13-Bestandsabzug).
|
||||
* Läuft transaktional mit lockForUpdate gegen Doppelklick/parallele Tabs.
|
||||
*/
|
||||
class OrderStatusService
|
||||
{
|
||||
public function setShipped(int $orderId, int $shipped, ?int $userId = null, bool $lockStatus = false): ShoppingOrder
|
||||
{
|
||||
return DB::transaction(function () use ($orderId, $shipped, $userId, $lockStatus) {
|
||||
$shopping_order = ShoppingOrder::query()->lockForUpdate()->findOrFail($orderId);
|
||||
$shopping_order->shipped = $shipped;
|
||||
if ($lockStatus) {
|
||||
// Kundenentscheid 16.07.2026 (AP-33): Status aus der WaWi gesetzt ⇒
|
||||
// die Bestell-Detailseite darf ihn nicht mehr automatisch verändern.
|
||||
$shopping_order->shipped_status_locked = true;
|
||||
}
|
||||
$shopping_order->save();
|
||||
// handel Promotion Product and credit by storno
|
||||
Payment::handelUserPromotionOrder($shopping_order);
|
||||
Payment::handelUserShopOrder($shopping_order);
|
||||
|
||||
if ($shopping_order->getAPIShippedType() === 'sent' || $shopping_order->getAPIShippedType() === 'close') {
|
||||
if (! $shopping_order->shipped_at) {
|
||||
$shopping_order->shipped_at = now();
|
||||
$shopping_order->save();
|
||||
// is shipped set pending_to
|
||||
if ($shopping_order->shopping_order_margin) {
|
||||
if ($shopping_order->shopping_order_margin->hasPartnerCommission()) {
|
||||
$days = Setting::getContentBySlug('pending_partner_commissions_in_days');
|
||||
$days = $days ? $days : 20;
|
||||
$partner_commission_pending_to = $shopping_order->shipped_at;
|
||||
$partner_commission_pending_to->addDays($days);
|
||||
$shopping_order->shopping_order_margin->partner_commission_pending_to = $partner_commission_pending_to;
|
||||
$shopping_order->shopping_order_margin->save();
|
||||
} else {
|
||||
$days = Setting::getContentBySlug('pending_order_margins_in_days');
|
||||
$days = $days ? $days : 20;
|
||||
$margin_pending_to = $shopping_order->shipped_at;
|
||||
$margin_pending_to->addDays($days);
|
||||
$shopping_order->shopping_order_margin->margin_pending_to = $margin_pending_to;
|
||||
$shopping_order->shopping_order_margin->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$shopping_order->shipped_at = null;
|
||||
$shopping_order->save();
|
||||
if ($shopping_order->shopping_order_margin) {
|
||||
// zurücksetzen der pending_to
|
||||
$shopping_order->shopping_order_margin->partner_commission_pending_to = null;
|
||||
$shopping_order->shopping_order_margin->margin_pending_to = null;
|
||||
$shopping_order->shopping_order_margin->save();
|
||||
}
|
||||
}
|
||||
if ($shopping_order->getAPIShippedType() === 'cancel') {
|
||||
if ($shopping_order->shopping_order_margin) {
|
||||
$shopping_order->shopping_order_margin->cancellation = true;
|
||||
$shopping_order->shopping_order_margin->partner_commission_pending_to = null;
|
||||
$shopping_order->shopping_order_margin->margin_pending_to = null;
|
||||
$shopping_order->shopping_order_margin->save();
|
||||
}
|
||||
} else {
|
||||
if ($shopping_order->shopping_order_margin && $shopping_order->shopping_order_margin->cancellation) {
|
||||
$shopping_order->shopping_order_margin->cancellation = false;
|
||||
$shopping_order->shopping_order_margin->partner_commission_pending_to = null;
|
||||
$shopping_order->shopping_order_margin->margin_pending_to = null;
|
||||
$shopping_order->shopping_order_margin->save();
|
||||
}
|
||||
}
|
||||
|
||||
// AP-13: Verkaufs-Abgänge (Produktbestand) mit dem neuen Versandstatus abgleichen
|
||||
app(ProductStockService::class)->syncSaleStock($shopping_order, $userId);
|
||||
|
||||
return $shopping_order;
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue