Initial commit: WitShop Laravel Online-Shop
- Laravel 11, Blade, Alpine.js, Tailwind CSS - Produkt- & Kategorieverwaltung mit Varianten - Checkout (Vorkasse + Rechnung), Session-Warenkorb - Admin-Backend (Dashboard, Bestellungen, Kunden, CMS) - DSGVO-konform: Cookie-Consent, Impressum, Widerruf, AGB - PDF-Rechnung via dompdf, SMTP-Mailversand - Kundenverwaltung mit manueller Freischaltung Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Mail\OrderConfirmationMail;
|
||||
use App\Mail\OrderShippedMail;
|
||||
use App\Models\Order;
|
||||
use App\Models\OrderAddress;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\ProductVariant;
|
||||
use App\Models\ShippingRate;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class OrderService
|
||||
{
|
||||
public function __construct(private CartService $cart) {}
|
||||
|
||||
public function createFromSession(array $addressData, string $paymentMethod): Order
|
||||
{
|
||||
$items = $this->cart->get();
|
||||
$subtotal = $this->cart->subtotal();
|
||||
|
||||
$shippingRate = ShippingRate::forCart($subtotal);
|
||||
$shippingCost = $shippingRate ? $shippingRate->getCostForTotal($subtotal) : 0;
|
||||
|
||||
$taxTotal = array_sum(array_map(function ($item) {
|
||||
$net = $item['price'] / (1 + $item['tax_rate'] / 100);
|
||||
return round(($item['price'] - $net) * $item['qty'], 2);
|
||||
}, $items));
|
||||
|
||||
$order = Order::create([
|
||||
'order_number' => Order::generateOrderNumber(),
|
||||
'user_id' => auth()->id(),
|
||||
'guest_email' => auth()->check() ? null : ($addressData['email'] ?? null),
|
||||
'status' => 'new',
|
||||
'payment_method' => $paymentMethod,
|
||||
'subtotal' => $subtotal,
|
||||
'tax_total' => $taxTotal,
|
||||
'shipping_cost' => $shippingCost,
|
||||
'total' => $subtotal + $shippingCost,
|
||||
]);
|
||||
|
||||
foreach ($items as $item) {
|
||||
OrderItem::create([
|
||||
'order_id' => $order->id,
|
||||
'product_variant_id'=> $item['variant_id'],
|
||||
'product_name' => $item['product_name'],
|
||||
'variant_label' => $item['variant_label'],
|
||||
'quantity' => $item['qty'],
|
||||
'unit_price' => $item['price'],
|
||||
'tax_rate' => $item['tax_rate'],
|
||||
]);
|
||||
// Bestand reduzieren — nur wenn track_stock aktiv
|
||||
$variant = ProductVariant::with('product')->find($item['variant_id']);
|
||||
if ($variant && $variant->product->track_stock) {
|
||||
$variant->decrement('stock', $item['qty']);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (['billing', 'shipping'] as $type) {
|
||||
$src = $addressData[$type] ?? $addressData['billing'];
|
||||
OrderAddress::create(array_merge($src, ['order_id' => $order->id, 'type' => $type]));
|
||||
}
|
||||
|
||||
$this->cart->clear();
|
||||
|
||||
Mail::to($order->guest_email ?? $order->user?->email)->send(new OrderConfirmationMail($order));
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
public function updateStatus(Order $order, string $newStatus, ?string $trackingCode = null): void
|
||||
{
|
||||
$order->status = $newStatus;
|
||||
|
||||
if ($newStatus === 'payment_received' && !$order->paid_at) {
|
||||
$order->paid_at = now();
|
||||
}
|
||||
|
||||
if ($trackingCode) {
|
||||
$order->tracking_code = $trackingCode;
|
||||
}
|
||||
|
||||
$order->save();
|
||||
|
||||
if ($newStatus === 'shipped') {
|
||||
$email = $order->guest_email ?? $order->user?->email;
|
||||
if ($email) {
|
||||
Mail::to($email)->send(new OrderShippedMail($order));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user