222 lines
5.9 KiB
JavaScript
222 lines
5.9 KiB
JavaScript
const Order = require('../models/orderModel');
|
|
const Cart = require('../models/cartModel');
|
|
const Product = require('../models/productModel');
|
|
|
|
// @desc Create new order from cart
|
|
// @route POST /api/orders
|
|
// @access Private
|
|
exports.createOrder = async (req, res) => {
|
|
try {
|
|
const { deliveryAddress, paymentMethod, notes } = req.body;
|
|
|
|
// Get user's cart
|
|
const cart = await Cart.findOne({ user: req.user.id });
|
|
if (!cart || cart.items.length === 0) {
|
|
return res.status(400).json({ message: 'Cart is empty' });
|
|
}
|
|
|
|
// Validate cart items and calculate total
|
|
let total = 0;
|
|
const orderItems = [];
|
|
|
|
for (const item of cart.items) {
|
|
const product = await Product.findById(item.product);
|
|
if (!product) {
|
|
return res.status(404).json({ message: `Product ${item.product} not found` });
|
|
}
|
|
|
|
if (product.stock < item.quantity) {
|
|
return res.status(400).json({ message: `Insufficient stock for ${product.name}` });
|
|
}
|
|
|
|
orderItems.push({
|
|
productId: product._id,
|
|
name: product.name,
|
|
price: product.price,
|
|
quantity: item.quantity
|
|
});
|
|
|
|
total += product.price * item.quantity;
|
|
|
|
// Reduce stock
|
|
product.stock -= item.quantity;
|
|
await product.save();
|
|
}
|
|
|
|
// Create order
|
|
const order = await Order.create({
|
|
userId: req.user.id,
|
|
items: orderItems,
|
|
total,
|
|
deliveryAddress,
|
|
paymentMethod: paymentMethod || 'cash',
|
|
notes: notes || ''
|
|
});
|
|
|
|
// Clear cart
|
|
cart.items = [];
|
|
await cart.save();
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: 'Order created successfully',
|
|
order: order.getPublicData()
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
};
|
|
|
|
// @desc Get user's orders
|
|
// @route GET /api/orders
|
|
// @access Private
|
|
exports.getUserOrders = async (req, res) => {
|
|
try {
|
|
const orders = await Order.find({ userId: req.user.id });
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
count: orders.length,
|
|
orders: orders.map(order => order.getPublicData())
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
};
|
|
|
|
// @desc Get single order
|
|
// @route GET /api/orders/:id
|
|
// @access Private
|
|
exports.getOrder = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const order = await Order.findById(id);
|
|
if (!order) {
|
|
return res.status(404).json({ message: 'Order not found' });
|
|
}
|
|
|
|
// Check if user owns this order or is admin
|
|
if (order.userId !== req.user.id && req.user.role !== 'admin') {
|
|
return res.status(403).json({ message: 'Not authorized to view this order' });
|
|
}
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
order: order.getPublicData()
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
};
|
|
|
|
// @desc Update order status (Admin only)
|
|
// @route PUT /api/orders/:id/status
|
|
// @access Private/Admin
|
|
exports.updateOrderStatus = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { status } = req.body;
|
|
|
|
if (!status) {
|
|
return res.status(400).json({ message: 'Status is required' });
|
|
}
|
|
|
|
const validStatuses = ['pending', 'confirmed', 'preparing', 'ready', 'delivered', 'cancelled'];
|
|
if (!validStatuses.includes(status)) {
|
|
return res.status(400).json({ message: 'Invalid status' });
|
|
}
|
|
|
|
const order = await Order.findById(id);
|
|
if (!order) {
|
|
return res.status(404).json({ message: 'Order not found' });
|
|
}
|
|
|
|
order.status = status;
|
|
await order.save();
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Order status updated successfully',
|
|
order: order.getPublicData()
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
};
|
|
|
|
// @desc Cancel order
|
|
// @route PUT /api/orders/:id/cancel
|
|
// @access Private
|
|
exports.cancelOrder = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const order = await Order.findById(id);
|
|
if (!order) {
|
|
return res.status(404).json({ message: 'Order not found' });
|
|
}
|
|
|
|
// Check if user owns this order
|
|
if (order.userId !== req.user.id) {
|
|
return res.status(403).json({ message: 'Not authorized to cancel this order' });
|
|
}
|
|
|
|
// Only allow cancellation if order is pending or confirmed
|
|
if (!['pending', 'confirmed'].includes(order.status)) {
|
|
return res.status(400).json({ message: 'Order cannot be cancelled at this stage' });
|
|
}
|
|
|
|
// Restore product stock
|
|
for (const item of order.items) {
|
|
const product = await Product.findById(item.productId);
|
|
if (product) {
|
|
product.stock += item.quantity;
|
|
await product.save();
|
|
}
|
|
}
|
|
|
|
order.status = 'cancelled';
|
|
await order.save();
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Order cancelled successfully',
|
|
order: order.getPublicData()
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
};
|
|
|
|
// @desc Get all orders (Admin only)
|
|
// @route GET /api/orders/admin/all
|
|
// @access Private/Admin
|
|
exports.getAllOrders = async (req, res) => {
|
|
try {
|
|
const { status, page = 1, limit = 10 } = req.query;
|
|
|
|
let query = {};
|
|
if (status) {
|
|
query.status = status;
|
|
}
|
|
|
|
const orders = await Order.find(query);
|
|
|
|
// Simple pagination
|
|
const startIndex = (page - 1) * limit;
|
|
const endIndex = page * limit;
|
|
const paginatedOrders = orders.slice(startIndex, endIndex);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
count: paginatedOrders.length,
|
|
total: orders.length,
|
|
page: parseInt(page),
|
|
pages: Math.ceil(orders.length / limit),
|
|
orders: paginatedOrders.map(order => order.getPublicData())
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, message: error.message });
|
|
}
|
|
}; |