1741 lines
68 KiB
JavaScript
1741 lines
68 KiB
JavaScript
import mongoose from "mongoose";
|
|
import { PdOrder } from "./pdOrderModal.js";
|
|
import sendEmail from "../../Utils/sendEmail.js";
|
|
import { Invoice } from "./invoiceModel.js";
|
|
|
|
// Controller for placing an order
|
|
export const createOrder = async (req, res) => {
|
|
try {
|
|
// Destructure the request body
|
|
const {
|
|
paymentMode,
|
|
shipTo,
|
|
billTo,
|
|
orderItems,
|
|
subtotal,
|
|
gstTotal,
|
|
grandTotal,
|
|
} = req.body;
|
|
|
|
// Check for required fields
|
|
if (
|
|
!paymentMode ||
|
|
!shipTo ||
|
|
!billTo ||
|
|
!orderItems ||
|
|
!subtotal ||
|
|
!gstTotal ||
|
|
!grandTotal
|
|
) {
|
|
return res.status(400).json({ error: "Missing required fields" });
|
|
}
|
|
const addedBy = req.user._id;
|
|
// Create a new PdOrder instance
|
|
const newOrder = new PdOrder({
|
|
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,
|
|
});
|
|
|
|
// Save the new order
|
|
const savedOrder = await newOrder.save();
|
|
// console.log(savedOrder);
|
|
return res.status(201).json({
|
|
message: "Order placed successfully",
|
|
placedOrder: savedOrder,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
return res.status(500).json({ error: "Internal server error" });
|
|
}
|
|
};
|
|
// Controller for processing an order and generating invoice
|
|
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 PdOrder.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 Invoice.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 Invoice({
|
|
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) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
|
|
${
|
|
item.image && item.image.length > 0
|
|
? `<img src="${item.image[0]?.url}" alt="${item.name}" style="max-width: 40px; height: auto;">`
|
|
: "No Image"
|
|
}
|
|
</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.processquantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
item.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(item.GST * item.price) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(item.price + (item.GST * item.price) / 100) * item.processquantity
|
|
}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("");
|
|
|
|
const pendingItems = order.orderItem
|
|
.filter((item) => item.remainingQuantity > 0)
|
|
.map(
|
|
(item, index) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
|
|
${
|
|
item.image && item.image.length > 0
|
|
? `<img src="${item.image[0]?.url}" alt="${item.name}" style="max-width: 40px; height: auto;">`
|
|
: "No Image"
|
|
}
|
|
</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.remainingQuantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
item.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(item.GST * item.price) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(item.price + (item.GST * item.price) / 100) * item.remainingQuantity
|
|
}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.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
|
|
? `
|
|
<h3>Exciting news! Your order #${order.uniqueId} has entered the processing phase. We're working hard to ensure everything is perfect for you.</h3>
|
|
<p>Your invoice ID is: <strong>${savedInvoice.invoiceId}</strong></p>
|
|
`
|
|
: `
|
|
<h3>Good news! Some items of your order #${order.uniqueId} have been processed. The remaining items will be processed soon.</h3>
|
|
<p>Your invoice ID is: <strong>${savedInvoice.invoiceId}</strong></p>
|
|
`;
|
|
|
|
await sendEmail({
|
|
to: order.addedBy.email,
|
|
from: process.env.SEND_EMAIL_FROM,
|
|
subject: emailSubject,
|
|
html: `
|
|
<div style="font-family: Arial, sans-serif; color: #333;">
|
|
${emailMessage}
|
|
<strong style="color: #1b03a3; font-size: 16px;">Hi ${
|
|
order.addedBy.name
|
|
},</strong>
|
|
<h4 style="color: #333;">Order Status: ${
|
|
order.status.charAt(0).toUpperCase() + order.status.slice(1)
|
|
}</h4>
|
|
|
|
<h4 style="color: #333;">Processed Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${processedItems}
|
|
<tr>
|
|
<th colspan="6" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${savedInvoice.invoiceAmount.toFixed(
|
|
2
|
|
)}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
${
|
|
pendingItems.length > 0
|
|
? `
|
|
<h4 style="color: #333;">Pending Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${pendingItems}
|
|
<tr>
|
|
<th colspan="6" style="border: 1px solid #555; padding: 2px; text-align: right;">Pending Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${pendingTotalAmount.toFixed(
|
|
2
|
|
)}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
`
|
|
: ""
|
|
}
|
|
<br/>
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">If you have any concerns or further questions, please feel free to reply to this email.</h5>
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
</div>
|
|
`,
|
|
});
|
|
|
|
res.status(200).json({
|
|
message: "Order processed and invoice created successfully",
|
|
invoiceId: savedInvoice.invoiceId,
|
|
});
|
|
} catch (error) {
|
|
console.error("Error processing order:", error);
|
|
res
|
|
.status(500)
|
|
.json({ error: "An error occurred while processing the order" });
|
|
}
|
|
};
|
|
export const cancelOrderController = async (req, res) => {
|
|
// const { cancellationReason, id: _id } = req.bodyconst
|
|
const cancellationReason = req.body.cancellationReason;
|
|
const _id = req.params.id;
|
|
try {
|
|
// Find the order by ID
|
|
const order = await PdOrder.findById(_id)
|
|
.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: `
|
|
<strong style="color: #1b03a3; font-size: 16px">Hi ${order.addedBy.name},</strong>
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">We regret to inform you that your order #${order.uniqueId} has been fully cancelled.</h3>
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Cancellation Reason: ${order.order_Cancelled_Reason}</h4>
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">If you have any concerns or further questions, please feel free to reply to this email.</h5>
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
`,
|
|
});
|
|
} else {
|
|
// If invoices are present
|
|
const invoices = await Invoice.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 `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.SKU
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;"><img src="${itemImage}" alt="${
|
|
item.name
|
|
}" style="max-width: 40px; height: auto;"></td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.processquantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
item.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(item.price * item.GST) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
itemSubtotal + itemGST
|
|
}</td>
|
|
</tr>
|
|
`;
|
|
})
|
|
.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 += `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.SKU
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;"><img src="${itemImage}" alt="${
|
|
item.name
|
|
}" style="max-width: 40px; height: auto;"></td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
item.remainingQuantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
item.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(item.price * item.GST) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
itemSubtotal + itemGST
|
|
}</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
});
|
|
}
|
|
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: `
|
|
<strong style="color: #1b03a3; font-size: 16px">Hi ${
|
|
order.addedBy.name
|
|
},</strong>
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">We would like to inform you that your order #${
|
|
order.uniqueId
|
|
} has been partially cancelled.</h3>
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Cancellation Reason: ${
|
|
order.order_Cancelled_Reason
|
|
}</h4>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Delivered Items:</h4>
|
|
${invoiceDetails
|
|
.map(
|
|
(details) => `
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">Invoice ID: ${
|
|
details.invoiceId
|
|
}</h5>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${details.processedItems}
|
|
<tr>
|
|
<th colspan="6" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${details.totalAmount.toFixed(
|
|
2
|
|
)}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
`
|
|
)
|
|
.join("")}
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Cancelled Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${cancelItems}
|
|
<tr>
|
|
<th colspan="6" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Refund Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${totalCancelAmount.toFixed(
|
|
2
|
|
)}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<br/>
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">If you have any concerns or further questions, please feel free to reply to this email.</h5>
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
`,
|
|
});
|
|
}
|
|
|
|
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" });
|
|
}
|
|
};
|
|
|
|
export const getPlacedOrder = async (req, res) => {
|
|
try {
|
|
// 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;
|
|
|
|
// Get the total count of orders
|
|
const totalOrders = await PdOrder.countDocuments({ addedBy: req.user._id });
|
|
|
|
// Fetch paginated orders
|
|
const plcaedOrders = await PdOrder.find({ addedBy: req.user._id })
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({ path: "orderItem.productId" });
|
|
|
|
if (plcaedOrders.length > 0) {
|
|
return res.status(200).json({ plcaedOrders, totalOrders });
|
|
}
|
|
return res.status(404).json({ return_msg: "Not placed yet" });
|
|
} catch (error) {
|
|
console.error("Error fetching orders:", error);
|
|
return res.status(500).json({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
// Get single placed order api
|
|
|
|
export const getPlacedOrderById = async (req, res) => {
|
|
try {
|
|
const id = req.params.id;
|
|
|
|
if (!mongoose.Types.ObjectId.isValid(id)) {
|
|
return res
|
|
.status(404)
|
|
.json({ return_msg: "Not Valid id to search the doc " });
|
|
}
|
|
const doc = await PdOrder.findById(id)
|
|
.populate({
|
|
path: "orderItem.productId",
|
|
})
|
|
.populate({ path: "addedBy" })
|
|
.populate({ path: "invoices" });
|
|
if (doc) {
|
|
return res
|
|
.status(200)
|
|
.json({ singleOrder: doc, return_msg: "Doc found" });
|
|
}
|
|
return res.status(404).json({ return_msg: "Not Found doc " });
|
|
} catch (error) {
|
|
console.log(error);
|
|
return res.status(500).json({ return_msg: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
export const getPlacedNewOrderAdmin = async (req, res) => {
|
|
try {
|
|
// 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;
|
|
|
|
// Get the total count of new orders
|
|
const totalOrders = await PdOrder.countDocuments({ status: "new" });
|
|
|
|
// Fetch paginated new orders
|
|
const placedOrders = await PdOrder.find({ status: "new" })
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({ path: "orderItem.productId" })
|
|
.populate({ path: "addedBy" });
|
|
|
|
if (placedOrders.length > 0) {
|
|
return res.status(200).json({ placedOrders, totalOrders });
|
|
}
|
|
return res.status(404).json({ return_msg: "No new orders placed yet" });
|
|
} catch (error) {
|
|
console.error("Error fetching new orders:", error);
|
|
return res.status(500).json({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
export const getPlacedPendingOrderAdmin = async (req, res) => {
|
|
try {
|
|
// 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;
|
|
|
|
// Get the total count of pending orders
|
|
const totalOrders = await PdOrder.countDocuments({ status: "pending" });
|
|
|
|
// Fetch paginated new orders
|
|
const placedOrders = await PdOrder.find({ status: "pending" })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({ path: "orderItem.productId" })
|
|
.populate({ path: "invoices" })
|
|
.populate({ path: "addedBy" })
|
|
.sort({ createdAt: -1 });
|
|
|
|
if (placedOrders.length > 0) {
|
|
return res.status(200).json({ placedOrders, totalOrders });
|
|
}
|
|
return res.status(404).json({ return_msg: "No new orders placed yet" });
|
|
} catch (error) {
|
|
console.error("Error fetching new orders:", error);
|
|
return res.status(500).json({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
export const getCancelledOrdersAdmin = async (req, res) => {
|
|
try {
|
|
// 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;
|
|
|
|
// Get the total count of cancelled orders
|
|
const totalOrders = await PdOrder.countDocuments({ status: "cancelled" });
|
|
|
|
// Fetch paginated cancelled orders
|
|
const cancelledOrders = await PdOrder.find({ status: "cancelled" })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({ path: "orderItem.productId" })
|
|
.populate({ path: "invoices" })
|
|
.populate({ path: "addedBy" });
|
|
|
|
if (cancelledOrders.length > 0) {
|
|
return res.status(200).json({ cancelledOrders, totalOrders });
|
|
}
|
|
return res.status(404).json({ return_msg: "No cancelled orders yet" });
|
|
} catch (error) {
|
|
console.error("Error fetching cancelled orders:", error);
|
|
return res.status(500).json({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
export const getDispatchedOrdersAdmin = async (req, res) => {
|
|
try {
|
|
// 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;
|
|
|
|
// Get the total count of dispatched orders
|
|
const totalOrders = await PdOrder.countDocuments({ status: "dispatched" });
|
|
|
|
// Fetch paginated dispatched orders
|
|
const dispatchedOrders = await PdOrder.find({ status: "dispatched" })
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({ path: "orderItem.productId" })
|
|
.populate({ path: "invoices" })
|
|
.populate({ path: "addedBy" });
|
|
|
|
if (dispatchedOrders.length > 0) {
|
|
return res.status(200).json({ dispatchedOrders, totalOrders });
|
|
}
|
|
return res.status(404).json({ return_msg: "No dispatched orders yet" });
|
|
} catch (error) {
|
|
console.error("Error fetching dispatched orders:", error);
|
|
return res.status(500).json({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
export const getProcessingOrdersAdmin = async (req, res) => {
|
|
try {
|
|
// 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;
|
|
|
|
// Get the total count of processing orders
|
|
const totalOrders = await PdOrder.countDocuments({ status: "processing" });
|
|
|
|
// Fetch paginated processing orders
|
|
const processingOrders = await PdOrder.find({ status: "processing" })
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({ path: "orderItem.productId" })
|
|
.populate({ path: "invoices" })
|
|
.populate({ path: "addedBy" });
|
|
|
|
if (processingOrders.length > 0) {
|
|
return res.status(200).json({ processingOrders, totalOrders });
|
|
}
|
|
return res.status(404).json({ return_msg: "No processing orders yet" });
|
|
} catch (error) {
|
|
console.error("Error fetching processing orders:", error);
|
|
return res.status(500).json({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
export const getProcessingInvoices = async (req, res) => {
|
|
try {
|
|
const page = parseInt(req.query.page, 10) || 1;
|
|
const limit = parseInt(req.query.limit, 10) || 5;
|
|
const { invoiceId, orderId } = req.query;
|
|
const skip = (page - 1) * limit;
|
|
|
|
let query = { courierStatus: "processing" };
|
|
|
|
if (invoiceId) {
|
|
query.invoiceId = { $regex: invoiceId, $options: "i" };
|
|
}
|
|
const invoices = await Invoice.find(query)
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({
|
|
path: "orderId",
|
|
select: "uniqueId",
|
|
});
|
|
|
|
if (orderId) {
|
|
const filteredInvoices = invoices.filter(
|
|
(invoice) =>
|
|
invoice.orderId &&
|
|
invoice.orderId.uniqueId &&
|
|
invoice.orderId.uniqueId.toLowerCase().includes(orderId.toLowerCase())
|
|
);
|
|
res.status(200).json({
|
|
totalCount: filteredInvoices.length,
|
|
currentPage: page,
|
|
totalPages: Math.ceil(filteredInvoices.length / limit),
|
|
invoices: filteredInvoices,
|
|
});
|
|
} else {
|
|
res.status(200).json({
|
|
totalCount: invoices.length,
|
|
currentPage: page,
|
|
totalPages: Math.ceil(invoices.length / limit),
|
|
invoices,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
export const getInvoiceDetailsById = async (req, res) => {
|
|
const { invoiceId } = req.params;
|
|
|
|
try {
|
|
// Find the invoice by ID and populate the orderId and addedBy fields
|
|
const invoice = await Invoice.findById(invoiceId).populate({
|
|
path: "orderId",
|
|
model: "PdOrder",
|
|
populate: {
|
|
path: "addedBy",
|
|
model: "User",
|
|
select: "name email phone SBU", // Select only specific fields
|
|
},
|
|
});
|
|
|
|
if (!invoice) {
|
|
return res.status(404).json({ error: "Invoice not found" });
|
|
}
|
|
|
|
res.status(200).json(invoice);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
export const updateCourierStatusToDispatched = async (req, res) => {
|
|
const { invoiceId } = req.params;
|
|
const { courierName, couriertrackingId } = req.body;
|
|
try {
|
|
// Find the invoice by ID
|
|
const invoice = await Invoice.findById(invoiceId).populate({
|
|
path: "orderId",
|
|
populate: {
|
|
path: "addedBy",
|
|
select: "email",
|
|
},
|
|
});
|
|
|
|
if (!invoice) {
|
|
return res.status(404).json({ error: "Invoice not found" });
|
|
}
|
|
|
|
invoice.courierStatus = "dispatched";
|
|
invoice.courierstatus_timeline.dispatched = new Date();
|
|
invoice.courier_name = courierName;
|
|
invoice.courier_tracking_id = couriertrackingId;
|
|
|
|
// Save the updated invoice
|
|
await invoice.save();
|
|
|
|
const order = invoice.orderId;
|
|
const allItemsDispatched = order.orderItem.every(
|
|
(item) => item.remainingQuantity === 0
|
|
);
|
|
|
|
if (allItemsDispatched) {
|
|
order.status = "dispatched";
|
|
await order.save();
|
|
}
|
|
|
|
// Send email to the user
|
|
await sendEmail({
|
|
to: `${order?.addedBy?.email}`, // Assuming 'addedBy' references the user who placed the order
|
|
from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender
|
|
subject: `Your Order #${order?.uniqueId} is On Its Way!`,
|
|
html: `
|
|
<strong style="color: #1b03a3; font-size: 16px">Hi,</strong>
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">Exciting news! Your order #${
|
|
order?.uniqueId
|
|
} has been dispatched and is en route to you. 🚚 Here are the details:</h3>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Courier Name: ${
|
|
invoice?.courier_name || "N/A"
|
|
}</h4>
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Courier Tracking ID: ${
|
|
invoice?.courier_tracking_id || "N/A"
|
|
}</h4>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SKU</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${invoice?.items
|
|
?.map(
|
|
(product, index) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.SKU
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
|
|
${
|
|
product.image && product.image.length > 0
|
|
? `<img src="${product.image[0]?.url}" alt="${product.name}" style="max-width: 40px; height: auto;">`
|
|
: "No Image"
|
|
}
|
|
</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.processquantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
product.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${(
|
|
(product.GST * product.price) /
|
|
100
|
|
).toFixed(2)}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${(
|
|
(product.price + (product.GST * product.price) / 100) *
|
|
product.processquantity
|
|
).toFixed(2)}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("")}
|
|
<tr>
|
|
<th colspan="7" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
invoice.invoiceAmount
|
|
}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">Order Status: Dispatched</h3>
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">If you have any questions or need assistance, feel free to reply to this email.</h4>
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">Thanks for choosing Cheminova! We hope you enjoy your purchase.</h5>
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
`,
|
|
});
|
|
|
|
res.status(200).json({
|
|
message: "Courier status updated to dispatched",
|
|
orderStatus: order.status,
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
export const getDispatchedInvoices = async (req, res) => {
|
|
try {
|
|
const page = parseInt(req.query.page, 10) || 1;
|
|
const limit = parseInt(req.query.limit, 10) || 5;
|
|
const { invoiceId, orderId } = req.query;
|
|
const skip = (page - 1) * limit;
|
|
|
|
let query = { courierStatus: "dispatched" };
|
|
|
|
if (invoiceId) {
|
|
query.invoiceId = { $regex: invoiceId, $options: "i" };
|
|
}
|
|
const invoices = await Invoice.find(query)
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({
|
|
path: "orderId",
|
|
select: "uniqueId",
|
|
});
|
|
|
|
if (orderId) {
|
|
const filteredInvoices = invoices.filter(
|
|
(invoice) =>
|
|
invoice.orderId &&
|
|
invoice.orderId.uniqueId &&
|
|
invoice.orderId.uniqueId.toLowerCase().includes(orderId.toLowerCase())
|
|
);
|
|
res.status(200).json({
|
|
totalCount: filteredInvoices.length,
|
|
currentPage: page,
|
|
totalPages: Math.ceil(filteredInvoices.length / limit),
|
|
invoices: filteredInvoices,
|
|
});
|
|
} else {
|
|
res.status(200).json({
|
|
totalCount: invoices.length,
|
|
currentPage: page,
|
|
totalPages: Math.ceil(invoices.length / limit),
|
|
invoices,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
export const updateCourierStatusToDelivered = async (req, res) => {
|
|
const { invoiceId } = req.params;
|
|
|
|
try {
|
|
// Find the invoice by ID
|
|
const invoice = await Invoice.findById(invoiceId).populate({
|
|
path: "orderId",
|
|
populate: {
|
|
path: "addedBy",
|
|
select: "email",
|
|
},
|
|
});
|
|
|
|
if (!invoice) {
|
|
return res.status(404).json({ error: "Invoice not found" });
|
|
}
|
|
|
|
// Update courier status and timeline
|
|
invoice.courierStatus = "delivered";
|
|
invoice.courierstatus_timeline.delivered = new Date();
|
|
|
|
// Save the updated invoice
|
|
await invoice.save();
|
|
|
|
const order = invoice.orderId;
|
|
const allItemsDelivered = order.orderItem.every(
|
|
(item) => item.remainingQuantity === 0
|
|
);
|
|
|
|
if (allItemsDelivered) {
|
|
order.status = "delivered";
|
|
await order.save();
|
|
}
|
|
|
|
// Format the current date for display
|
|
const formattedDate = formatDate(new Date());
|
|
|
|
// Send email to the user
|
|
await sendEmail({
|
|
to: `${order?.addedBy?.email}`, // Using 'addedBy' to reference the user's email
|
|
from: `${process.env.SEND_EMAIL_FROM}`, // Your verified sender
|
|
subject: `Your Order #${order?.uniqueId} Has Been Delivered!`,
|
|
html: `
|
|
<strong style="color: #1b03a3; font-size: 16px">Hi,</strong>
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">
|
|
Great news! Your order #${
|
|
order?.uniqueId
|
|
} has been successfully delivered to your doorstep. We hope everything is just as you expected!
|
|
</h3>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SKU</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${invoice?.items
|
|
?.map(
|
|
(product, index) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.SKU
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
|
|
${
|
|
product.image && product.image.length > 0
|
|
? `<img src="${product.image[0]?.url}" alt="${product.name}" style="max-width: 40px; height: auto;">`
|
|
: "No Image"
|
|
}
|
|
</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.processquantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
product.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${(
|
|
(product.GST * product.price) /
|
|
100
|
|
).toFixed(2)}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${(
|
|
(product.price + (product.GST * product.price) / 100) *
|
|
product.processquantity
|
|
).toFixed(2)}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("")}
|
|
<tr>
|
|
<th colspan="7" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${invoice.invoiceAmount.toFixed(
|
|
2
|
|
)}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">Delivery Date: ${formattedDate}</h3>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">
|
|
Your satisfaction is our priority, and we'd love to hear about your experience. Please take a moment to share your thoughts by leaving a review. Your feedback is invaluable to us!
|
|
</h4>
|
|
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">
|
|
If you have any questions or concerns about your order, feel free to reply to this email.
|
|
</h5>
|
|
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">
|
|
Thank you for choosing Cheminova! We hope to serve you again soon.
|
|
</h5>
|
|
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
`,
|
|
});
|
|
|
|
res.status(200).json({
|
|
message: "Courier status updated to delivered",
|
|
orderStatus: order.status,
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
export const getDeliveredInvoices = async (req, res) => {
|
|
try {
|
|
const page = parseInt(req.query.page, 10) || 1;
|
|
const limit = parseInt(req.query.limit, 10) || 5;
|
|
const { invoiceId, orderId } = req.query;
|
|
const skip = (page - 1) * limit;
|
|
|
|
let query = { courierStatus: "delivered" };
|
|
|
|
if (invoiceId) {
|
|
query.invoiceId = { $regex: invoiceId, $options: "i" };
|
|
}
|
|
const invoices = await Invoice.find(query)
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({
|
|
path: "orderId",
|
|
select: "uniqueId",
|
|
});
|
|
|
|
if (orderId) {
|
|
const filteredInvoices = invoices.filter(
|
|
(invoice) =>
|
|
invoice.orderId &&
|
|
invoice.orderId.uniqueId &&
|
|
invoice.orderId.uniqueId.toLowerCase().includes(orderId.toLowerCase())
|
|
);
|
|
res.status(200).json({
|
|
totalCount: filteredInvoices.length,
|
|
currentPage: page,
|
|
totalPages: Math.ceil(filteredInvoices.length / limit),
|
|
invoices: filteredInvoices,
|
|
});
|
|
} else {
|
|
res.status(200).json({
|
|
totalCount: invoices.length,
|
|
currentPage: page,
|
|
totalPages: Math.ceil(invoices.length / limit),
|
|
invoices,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
export const getDeliveredOrdersAdmin = async (req, res) => {
|
|
try {
|
|
// 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;
|
|
|
|
// Get the total count of delivered orders
|
|
const totalOrders = await PdOrder.countDocuments({ status: "delivered" });
|
|
|
|
// Fetch paginated delivered orders
|
|
const deliveredOrders = await PdOrder.find({ status: "delivered" })
|
|
.sort({ createdAt: -1 })
|
|
.skip(skip)
|
|
.limit(limit)
|
|
.populate({ path: "orderItem.productId" })
|
|
.populate({ path: "invoices" })
|
|
.populate({ path: "addedBy" });
|
|
|
|
if (deliveredOrders.length > 0) {
|
|
return res.status(200).json({ deliveredOrders, totalOrders });
|
|
}
|
|
return res.status(404).json({ return_msg: "No delivered orders yet" });
|
|
} catch (error) {
|
|
console.error("Error fetching delivered orders:", error);
|
|
return res.status(500).json({ error: "Internal Server Error" });
|
|
}
|
|
};
|
|
const formatDate = (date) => {
|
|
const options = {
|
|
weekday: "short",
|
|
year: "numeric",
|
|
month: "short",
|
|
day: "2-digit",
|
|
// hour: "2-digit",
|
|
// minute: "2-digit",
|
|
// hour12: true,
|
|
};
|
|
return new Intl.DateTimeFormat("en-US", options).format(new Date(date));
|
|
};
|
|
|
|
export const getOrderCounts = async (req, res) => {
|
|
try {
|
|
// console.log(req.user._id,"");
|
|
const userId = req.user._id;
|
|
const statusCounts = await PdOrder.aggregate([
|
|
{
|
|
$match: {
|
|
addedBy: userId, // Only match orders added by the current user
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$status", // Group by status
|
|
count: { $sum: 1 }, // Count the number of orders per status
|
|
},
|
|
},
|
|
]);
|
|
|
|
// Restructure the result to make it easier to consume
|
|
const result = {
|
|
new: 0,
|
|
dispatched: 0,
|
|
cancelled: 0,
|
|
processing: 0,
|
|
delivered: 0,
|
|
};
|
|
|
|
statusCounts.forEach((status) => {
|
|
result[status._id] = status.count;
|
|
});
|
|
|
|
res.status(200).json({ counts: result });
|
|
} catch (error) {
|
|
console.log(error.message);
|
|
res
|
|
.status(500)
|
|
.json({ message: error?.message || "Something went wrong!" });
|
|
}
|
|
};
|
|
|
|
// no need
|
|
export const updateOrderStatusById = async (req, res) => {
|
|
try {
|
|
let body = { status: req.body.status };
|
|
|
|
const currentDate = new Date();
|
|
body["status_timeline." + req.body.status] = currentDate;
|
|
|
|
const order = await PdOrder.findById(req.params.id).populate({
|
|
path: "addedBy",
|
|
select: "name email _id",
|
|
});
|
|
|
|
if (req.body.status === "cancelled") {
|
|
console.log("req came here ");
|
|
body["order_Cancelled_Reason"] = req.body?.ReasonforCancellation;
|
|
body["iscancelled"] = true;
|
|
|
|
const updatedCan = await PdOrder.findByIdAndUpdate(order._id, body);
|
|
|
|
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} Update: Cancellation and Refund Process`,
|
|
html: `
|
|
<strong style="color: #1b03a3; font-size: 16px"> Hi ${
|
|
order?.addedBy.name
|
|
},</strong>
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">We hope this message finds you well. We're writing to inform you that your order ${
|
|
order?.uniqueId
|
|
} has been canceled. We understand that circumstances may change, and we're here to assist you throughout the process.</h3>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Cancellation Reason: ${
|
|
order?.order_Cancelled_Reason || "No specific reason provided."
|
|
}</h4>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Category</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Brand</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${order?.orderItem
|
|
?.map(
|
|
(product, index) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.categoryName
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.brandName
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;"><img src="${
|
|
product?.image[0]?.url
|
|
}" alt="${
|
|
product.name
|
|
}" style="max-width: 40px; height: auto;"></td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.quantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
product.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.GST * product.price) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.price + (product.GST * product.price) / 100) *
|
|
product.quantity
|
|
}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("")}
|
|
<tr>
|
|
<th colspan="8" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount :</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
order?.grandTotal
|
|
}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Refund Information: The amount from your canceled order will be processed for a refund. Please allow up to 7 working days for the amount to be transferred back to your original payment method.</h4>
|
|
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">If you have any concerns or further questions, please feel free to reply to this email. We appreciate your understanding and hope to serve you better in the future.</h5>
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
`,
|
|
});
|
|
|
|
return res.status(200).json({
|
|
status: "ok",
|
|
message: "Order status updated successfully!",
|
|
updatedCan,
|
|
});
|
|
} else if (req.body.status === "processing") {
|
|
const updatedPro = await PdOrder.findByIdAndUpdate(order._id, body);
|
|
await sendEmail({
|
|
to: `${order?.addedBy?.email}`, // Recipient's email address from order model
|
|
from: `${process.env.SEND_EMAIL_FROM}`, // Sender's email address
|
|
subject: `Your Order #${order?.uniqueId} is in Processing!`,
|
|
html: `
|
|
<div style="font-family: Arial, sans-serif; color: #333;">
|
|
<h3 style="color: #333;">Exciting news! Your order #${
|
|
order?.uniqueId
|
|
} has entered the processing phase. Our team is diligently preparing your items for dispatch. Rest assured, we're working hard to ensure everything is perfect for you.</h3>
|
|
<strong style="color: #1b03a3; font-size: 16px;">Hi ${
|
|
order?.addedBy.name
|
|
},</strong>
|
|
<h4 style="color: #333;">Order Status: ${
|
|
order?.status.charAt(0).toUpperCase() + order?.status.slice(1)
|
|
}</h4>
|
|
|
|
|
|
|
|
<h4 style="color: #333;">Order Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${order?.orderItem
|
|
?.map(
|
|
(product, index) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.name
|
|
}</td>
|
|
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
|
|
<img src="${product.image[0]?.url}" alt="${
|
|
product.name
|
|
}" style="max-width: 40px; height: auto;">
|
|
</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.quantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
product.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.GST * product.price) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.price + (product.GST * product.price) / 100) *
|
|
product.quantity
|
|
}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("")}
|
|
<tr>
|
|
<th colspan="7" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
order?.grandTotal
|
|
}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h5 style="color: #333;">We'll send you another email with the tracking details as soon as your order is dispatched. If you have any questions or need assistance, feel free to reply to this email.</h5>
|
|
<h5 style="color: #333;">Thank you for choosing Cheminova!</h5>
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
</div>
|
|
`,
|
|
});
|
|
return res.status(200).json({
|
|
status: "ok",
|
|
message: "Order status updated successfully!",
|
|
updatedPro,
|
|
});
|
|
} else if (req.body.status === "dispatched") {
|
|
body["courier_name"] = req.body.courierName;
|
|
body["courier_tracking_id"] = req.body.TrackingID;
|
|
const disRes = await PdOrder.findByIdAndUpdate(order._id, body);
|
|
await sendEmail({
|
|
to: `${order?.addedBy?.email}`, // Assuming 'addedBy' references the user who placed the order
|
|
from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender
|
|
subject: `Your Order #${order?.uniqueId} is On Its Way!`,
|
|
html: `
|
|
<strong style="color: #1b03a3; font-size: 16px">Hi,</strong>
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">Exciting news! Your order #${
|
|
order?.uniqueId
|
|
} has been dispatched and is en route to you. 🚚 Here are the details:</h3>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Courier Name: ${
|
|
order?.courier_name || "N/A"
|
|
}</h4>
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Courier Tracking ID: ${
|
|
order?.courier_tracking_id || "N/A"
|
|
}</h4>
|
|
|
|
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SKU</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${order?.orderItem
|
|
?.map(
|
|
(product, index) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.SKU
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
|
|
<img src="${product?.image[0]?.url}" alt="${
|
|
product.name
|
|
}" style="max-width: 40px; height: auto;">
|
|
</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.quantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
product.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.GST * product.price) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.price + (product.GST * product.price) / 100) *
|
|
product.quantity
|
|
}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("")}
|
|
<tr>
|
|
<th colspan="7" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
order?.grandTotal
|
|
}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">Order Status: Dispatched</h3>
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">If you have any questions or need assistance, feel free to reply to this email.</h4>
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">Thanks for choosing Cheminova! We hope you enjoy your purchase.</h5>
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
`,
|
|
});
|
|
return res.status(200).json({
|
|
status: "ok",
|
|
message: "Order status updated successfully!",
|
|
disRes,
|
|
});
|
|
} else if (req.body.status === "delivered") {
|
|
body["isDelivered"] = true;
|
|
body["DeliveredDate"] = req.body.DDate;
|
|
const formattedDate = formatDate(req.body.DDate);
|
|
console.log(formattedDate);
|
|
const updatedDeli = await PdOrder.findByIdAndUpdate(order._id, body);
|
|
await sendEmail({
|
|
to: `${order?.addedBy?.email}`, // Using 'addedBy' to reference the user's email
|
|
from: `${process.env.SEND_EMAIL_FROM}`, // Your verified sender
|
|
subject: `Your Order #${order?.uniqueId} Has Been Delivered!`,
|
|
html: `
|
|
<strong style="color: #1b03a3; font-size: 16px">Hi,</strong>
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">
|
|
Great news! Your order #${
|
|
order?.uniqueId
|
|
} has been successfully delivered to your doorstep. We hope everything is just as you expected!
|
|
</h3>
|
|
|
|
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">Items:</h4>
|
|
<table style="border-collapse: collapse; width: 100%;">
|
|
<thead>
|
|
<tr>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SKU</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
|
|
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${order?.orderItem
|
|
?.map(
|
|
(product, index) => `
|
|
<tr>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
index + 1
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.name
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.SKU
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
|
|
<img src="${product?.image[0]?.url}" alt="${
|
|
product.name
|
|
}" style="max-width: 40px; height: auto;">
|
|
</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
|
|
product.quantity
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
product.price
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.GST * product.price) / 100
|
|
}</td>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
(product.price + (product.GST * product.price) / 100) *
|
|
product.quantity
|
|
}</td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("")}
|
|
<tr>
|
|
<th colspan="7" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
|
|
<td style="border: 1px solid #555; padding: 2px; text-align: center;">₹${
|
|
order?.grandTotal
|
|
}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h3 style="color: #333; font-family: Arial, sans-serif;">Delivery Date: ${formattedDate}</h3>
|
|
|
|
<h4 style="color: #333; font-family: Arial, sans-serif;">
|
|
Your satisfaction is our priority, and we'd love to hear about your experience. Please take a moment to share your thoughts by leaving a review. Your feedback is invaluable to us!
|
|
</h4>
|
|
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">
|
|
If you have any questions or concerns about your order, feel free to reply to this email.
|
|
</h5>
|
|
|
|
<h5 style="color: #333; font-family: Arial, sans-serif;">
|
|
Thank you for choosing Cheminova! We hope to serve you again soon.
|
|
</h5>
|
|
|
|
<br/>
|
|
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
|
|
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
|
|
`,
|
|
});
|
|
return res.status(200).json({
|
|
status: "ok",
|
|
message: "Order status updated successfully!",
|
|
updatedDeli,
|
|
});
|
|
} else {
|
|
// await Order.findByIdAndUpdate(order._id, body);
|
|
// console.log(order);
|
|
res
|
|
.status(200)
|
|
.json({ status: "ok", message: "Order status updated successfully!" });
|
|
}
|
|
} catch (error) {
|
|
console.log(error);
|
|
res
|
|
.status(500)
|
|
.json({ message: error?.message || "Something went wrong!" });
|
|
}
|
|
};
|