const Order = require('../models/Order'); const Product = require('../models/Product'); const InventoryHistory = require('../models/InventoryHistory'); const Cart = require('../models/Cart'); const { sendEmail } = require('../utils/emailSender'); const { successResponse, errorResponse } = require('../utils/apiResponse'); // @desc Create new order // @route POST /api/orders // @access Private const createOrder = async (req, res, next) => { try { const { orderItems, shippingAddress, paymentMethod, itemsPrice, taxPrice, shippingPrice, discountAmount, totalAmount } = req.body; if (!orderItems || orderItems.length === 0) { return errorResponse(res, 'No order items provided', [], 400); } // 1. Check stock and reduce inventory for (const item of orderItems) { const product = await Product.findById(item.product); if (!product) { return errorResponse(res, `Product ${item.name} not found`, [], 400); } if (product.stock < item.quantity) { return errorResponse(res, `Insufficient stock for product ${product.name}`, [], 400); } // Record stock deduction const oldStock = product.stock; product.stock -= item.quantity; product.salesCount += item.quantity; await product.save(); await InventoryHistory.create({ product: product._id, sku: product.sku, changeType: 'Order Placed', quantityChanged: -item.quantity, previousStock: oldStock, newStock: product.stock, reason: `Order placed by user ${req.user.name}`, performedBy: req.user._id }); } // 2. Generate unique order number const orderNumber = `GTR-${Math.floor(100000 + Math.random() * 900000)}`; // 3. Save Order const order = new Order({ orderNumber, user: req.user._id, orderItems, shippingAddress, paymentMethod, itemsPrice, taxPrice, shippingPrice, discountAmount, totalAmount, isPaid: paymentMethod === 'Stripe', // Auto mark paid for simulated stripe paidAt: paymentMethod === 'Stripe' ? Date.now() : null, orderStatus: paymentMethod === 'Stripe' ? 'Payment Confirmed' : 'Pending', statusHistory: [ { status: paymentMethod === 'Stripe' ? 'Payment Confirmed' : 'Pending', note: `Order created via ${paymentMethod}` } ] }); const createdOrder = await order.save(); // 4. Clear user cart await Cart.findOneAndDelete({ user: req.user._id }); // 5. Send order confirmation email sendEmail({ templateName: 'Order Confirmation', to: req.user.email, variables: { customerName: req.user.name, orderId: createdOrder.orderNumber, orderTotal: `$${createdOrder.totalAmount.toFixed(2)}` } }); return successResponse(res, 'Order created successfully', createdOrder, 201); } catch (error) { next(error); } }; // @desc Get logged in user orders // @route GET /api/orders/my-orders // @access Private const getMyOrders = async (req, res, next) => { try { const orders = await Order.find({ user: req.user._id }).sort({ createdAt: -1 }); return successResponse(res, 'User orders retrieved', orders); } catch (error) { next(error); } }; // @desc Get order by ID // @route GET /api/orders/:id // @access Private const getOrderById = async (req, res, next) => { try { const order = await Order.findById(req.params.id).populate('user', 'name email phone'); if (!order) { return errorResponse(res, 'Order not found', [], 404); } // Check authorization: customer can only view own order unless Admin if (order.user._id.toString() !== req.user._id.toString() && req.user.role !== 'Admin') { return errorResponse(res, 'Not authorized to view this order', [], 403); } return successResponse(res, 'Order retrieved', order); } catch (error) { next(error); } }; // @desc Get all orders (Admin) // @route GET /api/orders // @access Private/Admin const getAllOrders = async (req, res, next) => { try { const { status } = req.query; const filter = {}; if (status) filter.orderStatus = status; const orders = await Order.find(filter) .populate('user', 'name email') .sort({ createdAt: -1 }); return successResponse(res, 'All orders retrieved', orders); } catch (error) { next(error); } }; // @desc Update order status & shipping info (Admin) // @route PUT /api/orders/:id/status // @access Private/Admin const updateOrderStatus = async (req, res, next) => { try { const { orderStatus, trackingNumber, shippingProvider, note } = req.body; const order = await Order.findById(req.params.id).populate('user', 'name email'); if (!order) { return errorResponse(res, 'Order not found', [], 404); } if (orderStatus) { order.orderStatus = orderStatus; order.statusHistory.push({ status: orderStatus, note: note || `Status updated to ${orderStatus}` }); } if (trackingNumber) order.trackingNumber = trackingNumber; if (shippingProvider) order.shippingProvider = shippingProvider; if (orderStatus === 'Delivered') { order.isDelivered = true; order.deliveredAt = Date.now(); } const updatedOrder = await order.save(); // Trigger status change email if (['Shipped', 'Delivered', 'Cancelled'].includes(orderStatus)) { sendEmail({ templateName: orderStatus === 'Shipped' ? 'Order Shipped' : 'Order Confirmation', to: order.user.email, variables: { customerName: order.user.name, orderId: order.orderNumber, trackingNumber: order.trackingNumber || 'N/A' } }); } return successResponse(res, 'Order status updated', updatedOrder); } catch (error) { next(error); } }; module.exports = { createOrder, getMyOrders, getOrderById, getAllOrders, updateOrderStatus };