Initial commit
This commit is contained in:
212
server/controllers/orderController.js
Normal file
212
server/controllers/orderController.js
Normal file
@@ -0,0 +1,212 @@
|
||||
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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user