File manager - Edit - /home/webapp69.cm.in.th/u69319090028/Shop/app/Services/OrderService.php
Back
<?php /** * OrderService * Handles Multi-Vendor Order creation, customer order history, and status tracking */ require_once __DIR__ . '/../Models/Order.php'; require_once __DIR__ . '/../Models/Cart.php'; require_once __DIR__ . '/../Models/UserAddress.php'; require_once __DIR__ . '/../Models/User.php'; require_once __DIR__ . '/../Models/ActivityLog.php'; class OrderService { private Order $orderModel; private Cart $cartModel; private UserAddress $addressModel; private User $userModel; private ActivityLog $activityLog; public function __construct() { $this->orderModel = new Order(); $this->cartModel = new Cart(); $this->addressModel = new UserAddress(); $this->userModel = new User(); $this->activityLog = new ActivityLog(); } public function getCustomerOrders(int $customerId, ?string $status = null): array { return $this->orderModel->getCustomerOrders($customerId, $status); } public function getOrderDetail(string $orderNo, int $customerId): ?array { $order = $this->orderModel->findOrderByNo($orderNo, $customerId); if (!$order) { return null; } $order['items'] = $this->orderModel->getOrderItems((int)$order['id']); return $order; } /** * Place orders grouped by vendor store from selected cart items */ public function placeOrder(int $customerId, int $addressId, string $paymentMethod = 'PromptPay', ?string $note = null): array { $user = $this->userModel->findById($customerId); $address = $this->addressModel->findUserAddress($addressId, $customerId); if (!$address) { return ['success' => false, 'message' => 'กรุณาเลือกที่อยู่ในการจัดส่งที่ถูกต้อง']; } $cartData = $this->cartModel->getCartItemsGrouped($customerId); if (empty($cartData['stores'])) { return ['success' => false, 'message' => 'ไม่มีสินค้าในรถเข็น']; } // Format delivery address text snapshot $deliveryAddressText = sprintf( "%s | โทร: %s\n%s %s %s %s\nตำบล/แขวง %s, อำเภอ/เขต %s, จังหวัด %s %s", $address['recipient_name'], $address['phone'], $address['house_number'], $address['village'] ? 'หมู่บ้าน ' . $address['village'] : '', $address['alley'] ? 'ซอย ' . $address['alley'] : '', $address['street'] ? 'ถนน ' . $address['street'] : '', $address['sub_district'], $address['district'], $address['province'], $address['postal_code'] ); $createdOrderNos = []; $cartId = (int)$cartData['cart_id']; foreach ($cartData['stores'] as $storeGroup) { // Filter only selected and available items in this store $selectedItems = array_filter($storeGroup['items'], fn($it) => $it['is_selected'] && $it['is_available']); if (empty($selectedItems)) { continue; } $storeSubtotal = 0; foreach ($selectedItems as $it) { $storeSubtotal += (float)$it['price'] * (int)$it['quantity']; } $shippingFee = 50.00; // Flat standard shipping per store order $discount = 0.00; $totalAmount = $storeSubtotal + $shippingFee - $discount; $commissionRate = (float)($storeGroup['commission_rate'] ?? 5.00); $commissionAmount = round(($storeSubtotal * ($commissionRate / 100.0)), 2); $netRevenue = $storeSubtotal - $commissionAmount; $orderNo = $this->orderModel->generateOrderNo(); $orderId = $this->orderModel->createVendorOrder([ 'order_no' => $orderNo, 'customer_id' => $customerId, 'store_id' => (int)$storeGroup['store_id'], 'shipping_address_id' => $addressId, 'customer_name' => $address['recipient_name'], 'customer_phone' => $address['phone'], 'delivery_address' => $deliveryAddressText, 'subtotal' => $storeSubtotal, 'shipping_fee' => $shippingFee, 'discount' => $discount, 'total_amount' => $totalAmount, 'commission_rate' => $commissionRate, 'commission_amount' => $commissionAmount, 'net_revenue' => $netRevenue, 'order_status' => 'pending_payment', 'payment_status' => 'pending', 'payment_method' => $paymentMethod, 'shipping_method' => 'Standard Delivery', 'note' => $note, 'payment_expires_at' => date('Y-m-d H:i:s', time() + 86400), // 24 hours 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], $selectedItems); $createdOrderNos[] = $orderNo; $this->activityLog->record($customerId, 'order_placed', 'order', $orderId, "Placed order {$orderNo}"); } if (empty($createdOrderNos)) { return ['success' => false, 'message' => 'กรุณาเลือกสินค้าอย่างน้อย 1 รายการเพื่อสั่งซื้อ']; } // Clear only selected items $this->cartModel->clearSelected($cartId); return [ 'success' => true, 'message' => 'สั่งซื้อสินค้าเรียบร้อยแล้ว', 'order_nos' => $createdOrderNos, 'primary_order_no' => $createdOrderNos[0] ]; } /** * Cancel an Order by Customer * Handles stock restoration, payment status update, and refund integration */ public function cancelOrder(string $orderNo, int $customerId, string $reason): array { $reason = trim($reason); if (empty($reason)) { return ['success' => false, 'message' => 'กรุณาระบุเหตุผลในการยกเลิกคำสั่งซื้อ']; } return DB::transaction(function($pdo) use ($orderNo, $customerId, $reason) { // 1. Lock and retrieve order $stmt = $pdo->prepare("SELECT * FROM `orders` WHERE `order_no` = :no AND `customer_id` = :cid AND `deleted_at` IS NULL FOR UPDATE"); $stmt->execute([':no' => $orderNo, ':cid' => $customerId]); $order = $stmt->fetch(PDO::FETCH_ASSOC); if (!$order) { return ['success' => false, 'message' => 'ไม่พบคำสั่งซื้อนี้ หรือคุณไม่มีสิทธิ์ยกเลิก']; } $orderId = (int)$order['id']; $currentStatus = $order['order_status']; // 2. Validate Cancellable Status $cancellableStatuses = ['pending_payment', 'awaiting_verification', 'payment_confirmed', 'preparing']; if (!in_array($currentStatus, $cancellableStatuses, true)) { if ($currentStatus === 'cancelled') { return ['success' => false, 'message' => 'คำสั่งซื้อนี้ถูกยกเลิกไปแล้ว']; } return ['success' => false, 'message' => 'ไม่สามารถยกเลิกคำสั่งซื้อในสถานะนี้ได้ (คำสั่งซื้ออยู่ระหว่างจัดส่งหรือดำเนินการเสร็จสิ้นแล้ว)']; } // 3. Restore Stock for each order item $itemsStmt = $pdo->prepare("SELECT * FROM `order_items` WHERE `order_id` = :oid"); $itemsStmt->execute([':oid' => $orderId]); $items = $itemsStmt->fetchAll(PDO::FETCH_ASSOC); $stmtProdStock = $pdo->prepare("UPDATE `products` SET `stock` = `stock` + :qty WHERE `id` = :pid"); $stmtSkuStock = $pdo->prepare("UPDATE `product_skus` SET `stock` = `stock` + :qty WHERE `id` = :skuid"); foreach ($items as $item) { $qty = (int)$item['quantity']; $stmtProdStock->execute([':qty' => $qty, ':pid' => (int)$item['product_id']]); if (!empty($item['sku_id'])) { $stmtSkuStock->execute([':qty' => $qty, ':skuid' => (int)$item['sku_id']]); } } // 4. Payment & Refund Integration $payStmt = $pdo->prepare("SELECT * FROM `payments` WHERE `order_id` = :oid LIMIT 1 FOR UPDATE"); $payStmt->execute([':oid' => $orderId]); $payment = $payStmt->fetch(PDO::FETCH_ASSOC); $cancelNoteText = ($order['note'] ? $order['note'] . "\n" : "") . "[ยกเลิกคำสั่งซื้อเมื่อ " . date('d/m/Y H:i') . "]: " . $reason; $newPaymentStatus = 'cancelled'; $newOrderStatus = 'cancelled'; $isPaid = false; if ($payment) { $paymentId = (int)$payment['id']; $isPaid = in_array($payment['payment_status'], ['confirmed', 'paid'], true) || in_array($currentStatus, ['payment_confirmed', 'preparing'], true); if ($isPaid) { $newPaymentStatus = 'refund_pending'; $newOrderStatus = 'cancelled'; // Insert into refunds table $pdo->prepare("INSERT INTO `refunds` (`order_id`, `customer_id`, `store_id`, `amount`, `reason`, `status`, `created_at`, `updated_at`) VALUES (:oid, :cid, :sid, :amt, :reason, 'pending', NOW(), NOW())") ->execute([ ':oid' => $orderId, ':cid' => $customerId, ':sid' => (int)$order['store_id'], ':amt' => (float)$order['total_amount'], ':reason' => "ลูกค้ายกเลิกคำสั่งซื้อที่ชำระเงินแล้ว: " . $reason ]); // Update payment status $pdo->prepare("UPDATE `payments` SET `payment_status` = 'refund_pending', `cancelled_at` = NOW(), `note` = :note, `updated_at` = NOW() WHERE `id` = :pid") ->execute([':note' => $reason, ':pid' => $paymentId]); } else { $newPaymentStatus = 'cancelled'; $newOrderStatus = 'cancelled'; $pdo->prepare("UPDATE `payments` SET `payment_status` = 'cancelled', `cancelled_at` = NOW(), `note` = :note, `updated_at` = NOW() WHERE `id` = :pid") ->execute([':note' => $reason, ':pid' => $paymentId]); } } // 5. Update Order Status $pdo->prepare("UPDATE `orders` SET `order_status` = :ostatus, `payment_status` = :pstatus, `note` = :note, `updated_at` = NOW() WHERE `id` = :oid") ->execute([ ':ostatus' => $newOrderStatus, ':pstatus' => $newPaymentStatus, ':note' => $cancelNoteText, ':oid' => $orderId ]); // 6. Record Activity Log $this->activityLog->record($customerId, 'order_cancelled', 'order', $orderId, "Customer cancelled order #{$orderNo}. Reason: {$reason}"); // 7. Dispatch Notifications require_once __DIR__ . '/NotificationService.php'; $notifService = new NotificationService(); $notifService->onOrderCancelled($orderId, $orderNo, $reason, $customerId, (int)$order['store_id']); return [ 'success' => true, 'message' => 'ยกเลิกคำสั่งซื้อเรียบร้อยแล้ว' . ($isPaid ? ' (ระบบได้ส่งคำขอคืนเงินเรียบร้อยแล้ว)' : '') ]; }); } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.24 |
proxy
|
phpinfo
|
Settings