import sendEmail from "../../Utils/sendEmail.js";
import RetailDistributor from "../RetailDistributor/RetailDistributorModel.js";
import { InvoiceRd } from "./invoiceModalRD.js";
import { RdOrder } from "./rdOrderModal.js";
// Controller to create a new order by RD
export const createOrderRD = async (req, res) => {
try {
const {
paymentMode,
shipTo,
billTo,
orderItems,
subtotal,
gstTotal,
grandTotal,
} = req.body;
const rdId = req.user._id;
// Fetch the Retail Distributor (RD) to find the associated Principal Distributor (PD)
const rd = await RetailDistributor.findById(rdId).populate(
"principal_distributer"
);
if (!rd) {
return res.status(404).json({ message: "Retail Distributor not found" });
}
const pdId = rd.principal_distributer._id; // Get the associated PD
// Create the order
const newOrder = new RdOrder({
paymentMode,
shipTo,
billTo,
orderItem: orderItems.map((item) => ({
productId: item._id,
SKU: item.SKU,
name: item.name,
categoryName: item.category.categoryName, // Store category name
brandName: item.brand.brandName, // Store brand name
price: item.price,
GST: item.GST,
HSN_Code: item.HSN_Code,
description: item.description,
image: item.image,
quantity: item.count,
remainingQuantity: item.count,
})),
subtotal,
gstTotal,
grandTotal,
addedBy: rdId, // The RD who placed the order (Retail Distributor)
pd: pdId, // Reference to the PD associated with the RD
});
await newOrder.save();
res
.status(201)
.json({ message: "Order placed successfully", order: newOrder });
} catch (error) {
res.status(500).json({ message: "Server error", error });
}
};
export const getPlacedOrdersForRD = async (req, res) => {
try {
const rdId = req.user?._id; // Assuming the Retail Distributor's ID is obtained from the authenticated request
if (!rdId) {
return res.status(401).json({ message: "Unauthorized access" });
}
// Extract page and limit from query parameters, with default values
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// Calculate how many documents to skip for pagination
const skip = (page - 1) * limit;
// Fetch total count of orders for this RD (for pagination purposes)
const totalOrders = await RdOrder.countDocuments({ addedBy: rdId });
// Fetch orders for the logged-in RD
const placedOrders = await RdOrder.find({ addedBy: rdId })
.sort({ createdAt: -1 }) // Sort by creation date, newest first
.skip(skip) // Skip documents for pagination
.limit(limit); // Limit number of documents returned
if (!placedOrders || placedOrders.length === 0) {
return res
.status(404)
.json({ message: "No orders found for this Retail Distributor" });
}
// Send the paginated order list and total count of orders
res.status(200).json({ placedOrders, totalOrders });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error", error });
}
};
export const getSinglePlacedOrderForRD = async (req, res) => {
try {
const rdId = req.user?._id;
if (!rdId) {
return res.status(401).json({ message: "Unauthorized access" });
} // Assuming the Retail Distributor's ID is obtained from the authenticated request
const orderId = req.params.id; // Assuming the order ID is passed in the URL as a parameter
if (!rdId) {
return res.status(401).json({ message: "Unauthorized access" });
}
if (!orderId) {
return res.status(400).json({ message: "Order ID is required" });
}
// Fetch the specific order for the logged-in RD
const order = await RdOrder.findOne({ _id: orderId, addedBy: rdId });
if (!order) {
return res
.status(404)
.json({ message: "Order not found for this Retail Distributor" });
}
// Send the single order document
res.status(200).json({ singleOrder: order });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error", error });
}
};
export const getPlacedOrdersForPD = async (req, res) => {
try {
const pdId = req.user?._id;
if (!pdId) {
return res.status(401).json({ return_message: "Unauthorized access " });
}
// Extract page and limit from query parameters
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// Calculate the number of documents to skip
const skip = (page - 1) * limit;
const totalOrders = await RdOrder.countDocuments({ pd: pdId });
// Fetch all orders where the PD is associated with the order
const plcaedOrders = await RdOrder.find({ pd: pdId })
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit);
if (!plcaedOrders || plcaedOrders.length === 0) {
return res
.status(404)
.json({ message: "No orders found for this Principal Distributor" });
}
res.status(200).json({ plcaedOrders, totalOrders });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error", error });
}
};
export const getSinglePlacedOrderForPD = async (req, res) => {
try {
const pdId = req.user?._id; // Assuming the Principal Distributor's ID is obtained from the authenticated request
const orderId = req.params.id; // Assuming the order ID is passed in the URL as a parameter
if (!pdId) {
return res.status(401).json({ message: "Unauthorized access" });
}
if (!orderId) {
return res.status(400).json({ message: "Order ID is required" });
}
// Fetch the specific order for the logged-in PD
const order = await RdOrder.findOne({ _id: orderId, pd: pdId })
.populate({ path: "addedBy" })
.populate({ path: "invoices" });
if (!order) {
return res
.status(404)
.json({ message: "Order not found for this Principal Distributor" });
}
// Send the single order document
res.status(200).json({ singleOrder: order });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error", error });
}
};
export const getNewOrders = async (req, res) => {
try {
const pdId = req.user?._id;
if (!pdId) {
return res.status(401).json({ return_message: "Unauthorized access " });
}
// Extract page and limit from query parameters
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// Calculate the number of documents to skip
const skip = (page - 1) * limit;
const totalOrders = await RdOrder.countDocuments({
pd: pdId,
status: "new",
});
// Fetch all orders where the PD is associated with the order
const plcaedOrders = await RdOrder.find({ pd: pdId, status: "new" })
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit);
if (!plcaedOrders || plcaedOrders.length === 0) {
return res
.status(404)
.json({ message: "No orders found for this Principal Distributor" });
}
res.status(200).json({ plcaedOrders, totalOrders });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error", error });
}
};
export const getPendignOrders = async (req, res) => {
try {
const pdId = req.user?._id;
if (!pdId) {
return res.status(401).json({ return_message: "Unauthorized access " });
}
// Extract page and limit from query parameters
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// Calculate the number of documents to skip
const skip = (page - 1) * limit;
const totalOrders = await RdOrder.countDocuments({
pd: pdId,
status: "new",
});
// Fetch all orders where the PD is associated with the order
const plcaedOrders = await RdOrder.find({ pd: pdId, status: "pending" })
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit);
if (!plcaedOrders || plcaedOrders.length === 0) {
return res
.status(404)
.json({ message: "No orders found for this Principal Distributor" });
}
res.status(200).json({ plcaedOrders, totalOrders });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error", error });
}
};
export const getCancelledOrders = async (req, res) => {
try {
const pdId = req.user?._id;
if (!pdId) {
return res.status(401).json({ return_message: "Unauthorized access " });
}
// Extract page and limit from query parameters
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// Calculate the number of documents to skip
const skip = (page - 1) * limit;
const totalOrders = await RdOrder.countDocuments({
pd: pdId,
status: "new",
});
// Fetch all orders where the PD is associated with the order
const plcaedOrders = await RdOrder.find({ pd: pdId, status: "cancelled" })
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit);
if (!plcaedOrders || plcaedOrders.length === 0) {
return res
.status(404)
.json({ message: "No orders found for this Principal Distributor" });
}
res.status(200).json({ plcaedOrders, totalOrders });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server error", error });
}
};
export const processOrder = async (req, res) => {
try {
const { orderId, invoiceItems } = req.body;
if (!orderId || !invoiceItems || !Array.isArray(invoiceItems)) {
return res
.status(400)
.json({ error: "Missing required fields or invalid data" });
}
// Find the order by ID
const order = await RdOrder.findById(orderId).populate("addedBy");
if (!order) {
return res.status(404).json({ error: "Order not found" });
}
// Validate quantities
const exceededItems = [];
// Check each item in invoiceItems for quantity limits
for (const item of invoiceItems) {
const orderItem = order.orderItem.find(
(i) => i.productId.toString() === item.productId.toString()
);
// If processquantity exceeds remainingQuantity, add the item name to exceededItems
if (orderItem && item.processquantity > orderItem.remainingQuantity) {
exceededItems.push(item.name);
}
}
// If there are any exceeded items, return an error with their names
if (exceededItems.length > 0) {
return res.status(400).json({
error: `The following items have more quantity than remaining in the order: ${exceededItems.join(
", "
)}`,
});
}
// Continue with the rest of the logic if no quantity limits are exceeded
// Generate unique invoice number
const existingInvoices = await InvoiceRd.find({ orderId });
const invoiceNumber = existingInvoices.length + 1;
const invoiceId = `ch/${order.uniqueId}/${invoiceItems.length}/${invoiceNumber}`;
// Calculate subtotal, gstTotal, and invoiceAmount for processed items
let subtotal = 0;
let gstTotal = 0;
let invoiceAmount = 0;
invoiceItems.forEach((item) => {
const itemSubtotal = item.price * item.processquantity;
const itemGST = ((item.price * item.GST) / 100) * item.processquantity;
subtotal += itemSubtotal;
gstTotal += itemGST;
invoiceAmount += itemSubtotal + itemGST;
});
// Create a new invoice
const newInvoice = new InvoiceRd({
invoiceId,
orderId,
items: invoiceItems,
subtotal,
gstTotal,
invoiceAmount,
});
// Save the invoice
const savedInvoice = await newInvoice.save();
// Update the order's order items with the remaining quantity
let allItemsProcessed = true; // Flag to check if all items are processed
order.orderItem.forEach((item) => {
const invoicedItem = invoiceItems.find(
(i) => i.productId.toString() === item.productId.toString()
);
// If the item was invoiced, update the remaining quantity
if (invoicedItem) {
// Deduct the processed quantity from remaining quantity
item.remainingQuantity -= invoicedItem.processquantity;
// Ensure remaining quantity does not go negative
if (item.remainingQuantity < 0) {
item.remainingQuantity = 0;
}
}
// Check if the remaining quantity is greater than 0, even for items not invoiced
if (item.remainingQuantity > 0) {
allItemsProcessed = false;
}
});
// Calculate total amount for pending items
let pendingTotalAmount = 0;
order.orderItem.forEach((item) => {
if (item.remainingQuantity > 0) {
const itemPendingSubtotal = item.price * item.remainingQuantity;
const itemPendingGST =
((item.price * item.GST) / 100) * item.remainingQuantity;
pendingTotalAmount += itemPendingSubtotal + itemPendingGST;
}
});
// Only update order status if all items have been fully processed
if (allItemsProcessed) {
order.status = "processing"; // All items are fully processed
} else {
order.status = "pending"; // There are still remaining quantities
}
// Add the invoice to the order
order.invoices.push(savedInvoice._id);
// Save the updated order
await order.save();
// Prepare the email content
const processedItems = invoiceItems
.map(
(item, index) => `
${
index + 1
} |
${
item.name
} |
${
item.image && item.image.length > 0
? ` `
: "No Image"
}
|
${
item.processquantity
} |
₹${
item.price
} |
₹${
(item.GST * item.price) / 100
} |
₹${
(item.price + (item.GST * item.price) / 100) * item.processquantity
} |
`
)
.join("");
const pendingItems = order.orderItem
.filter((item) => item.remainingQuantity > 0)
.map(
(item, index) => `
${
index + 1
} |
${
item.name
} |
${
item.image && item.image.length > 0
? ` `
: "No Image"
}
|
${
item.remainingQuantity
} |
₹${
item.price
} |
₹${
(item.GST * item.price) / 100
} |
₹${
(item.price + (item.GST * item.price) / 100) * item.remainingQuantity
} |
`
)
.join("");
// Dynamic email subject and message based on the order status
const emailSubject = allItemsProcessed
? `Your Order #${order.uniqueId} is in Processing!`
: `Your Order #${order.uniqueId} is Partially Processed!`;
const emailMessage = allItemsProcessed
? `
Exciting news! Your order #${order.uniqueId} has entered the processing phase. We're working hard to ensure everything is perfect for you.
Your invoice ID is: ${savedInvoice.invoiceId}
`
: `
Good news! Some items of your order #${order.uniqueId} have been processed. The remaining items will be processed soon.
Your invoice ID is: ${savedInvoice.invoiceId}
`;
await sendEmail({
to: order.addedBy.email,
from: process.env.SEND_EMAIL_FROM,
subject: emailSubject,
html: `
${emailMessage}
Hi ${
order.addedBy.name
},
Order Status: ${
order.status.charAt(0).toUpperCase() + order.status.slice(1)
}
Processed Items:
S No. |
Product Name |
Image |
Quantity |
Price |
GST Amount |
SubTotal |
${processedItems}
Total Amount: |
₹${savedInvoice.invoiceAmount.toFixed(
2
)} |
${
pendingItems.length > 0
? `
Pending Items:
S No. |
Product Name |
Image |
Quantity |
Price |
GST Amount |
SubTotal |
${pendingItems}
Pending Amount: |
₹${pendingTotalAmount.toFixed(
2
)} |
`
: ""
}
If you have any concerns or further questions, please feel free to reply to this email.
Best regards,
Team Cheminova
`,
});
res.status(200).json({
message: "Order processed and invoice created successfully",
invoiceId: savedInvoice.invoiceId,
});
} catch (error) {
res
.status(500)
.json({ error: "An error occurred while processing the order" });
}
};
// cancell order
export const cancelOrderController = async (req, res) => {
// const { cancellationReason, id: _id } = req.bodyconst
const pdId = req.user?._id;
const cancellationReason = req.body.cancellationReason;
const _id = req.params.id;
try {
// Find the order by ID
const order = await RdOrder.findOne({ _id, pd: pdId })
.populate("invoices")
.populate("addedBy");
if (!order) {
return res.status(404).json({ message: "Order not found" });
}
// Update the order status to 'cancelled'
order.status = "cancelled";
order.iscancelled = true;
order.order_Cancelled_Reason =
cancellationReason || "No specific reason provided.";
await order.save();
if (order.invoices.length === 0) {
// If no invoices are associated with the order
await sendEmail({
to: `${order.addedBy.email}`, // Change to your recipient
from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender
subject: `Order #${order.uniqueId} Cancellation Confirmation`,
html: `
Hi ${order.addedBy.name},
We regret to inform you that your order #${order.uniqueId} has been fully cancelled.
Cancellation Reason: ${order.order_Cancelled_Reason}
If you have any concerns or further questions, please feel free to reply to this email.
Best regards,
Team Cheminova
`,
});
} else {
// If invoices are present
const invoices = await InvoiceRd.find({ _id: { $in: order.invoices } });
// Collect all invoice data
const invoiceDetails = invoices.map((invoice) => {
let subtotal = 0;
let gstTotal = 0;
let totalAmount = 0;
const processedItems = invoice.items
.map((item) => {
const itemSubtotal = item.price * item.processquantity;
const itemGST =
((item.price * item.GST) / 100) * item.processquantity;
subtotal += itemSubtotal;
gstTotal += itemGST;
totalAmount += itemSubtotal + itemGST;
const itemImage =
item.image && Array.isArray(item.image) ? item.image[0]?.url : "";
return `
${
item.SKU
} |
${
item.name
} |
 |
${
item.processquantity
} |
₹${
item.price
} |
₹${
(item.price * item.GST) / 100
} |
₹${
itemSubtotal + itemGST
} |
`;
})
.join("");
return {
invoiceId: invoice.invoiceId,
processedItems,
totalAmount,
};
});
// Collect all delivered and remaining items
let cancelItems = "";
let totalCancelAmount = 0;
if (Array.isArray(order.orderItem)) {
order.orderItem.forEach((item) => {
if (item.remainingQuantity > 0) {
const itemSubtotal = item.price * item.remainingQuantity;
const itemGST =
((item.price * item.GST) / 100) * item.remainingQuantity;
totalCancelAmount += itemSubtotal + itemGST;
const itemImage =
item.image && Array.isArray(item.image) ? item.image[0]?.url : "";
cancelItems += `
${
item.SKU
} |
${
item.name
} |
 |
${
item.remainingQuantity
} |
₹${
item.price
} |
₹${
(item.price * item.GST) / 100
} |
₹${
itemSubtotal + itemGST
} |
`;
}
});
}
await sendEmail({
to: `${order.addedBy.email}`, // Change to your recipient
from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender
subject: `Order #${order.uniqueId} is Partially Cancelled!`,
html: `
Hi ${
order.addedBy.name
},
We would like to inform you that your order #${
order.uniqueId
} has been partially cancelled.
Cancellation Reason: ${
order.order_Cancelled_Reason
}
Delivered Items:
${invoiceDetails
.map(
(details) => `
Invoice ID: ${
details.invoiceId
}
S No. |
Product Name |
Image |
Quantity |
Price |
GST Amount |
SubTotal |
${details.processedItems}
Total Amount: |
₹${details.totalAmount.toFixed(
2
)} |
`
)
.join("")}
Cancelled Items:
S No. |
Product Name |
Image |
Quantity |
Price |
GST Amount |
SubTotal |
${cancelItems}
Total Refund Amount: |
₹${totalCancelAmount.toFixed(
2
)} |
If you have any concerns or further questions, please feel free to reply to this email.
Best regards,
Team Cheminova
`,
});
}
res.status(200).json({ message: "Order cancelled successfully" });
} catch (error) {
console.error("Error cancelling order:", error);
res
.status(500)
.json({ message: "An error occurred while cancelling the order" });
}
};