Compare commits
No commits in common. "f405476976385bccb2406593a54bcaaa0a3dcb12" and "a2bcc9feecac86c79f1a9257ce671602d8776ef1" have entirely different histories.
f405476976
...
a2bcc9feec
@ -12,7 +12,7 @@ export const isAuthenticatedRD = async (req, res, next) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const getToken = req.headers;
|
const getToken = req.headers;
|
||||||
|
console.log(getToken);
|
||||||
// console.log(getToken);
|
// console.log(getToken);
|
||||||
//remove Bearer from token
|
//remove Bearer from token
|
||||||
const fronttoken = getToken.authorization.slice(7);
|
const fronttoken = getToken.authorization.slice(7);
|
||||||
@ -24,7 +24,7 @@ export const isAuthenticatedRD = async (req, res, next) => {
|
|||||||
message: "incorrect token",
|
message: "incorrect token",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.log(frontdecoded);
|
||||||
const fuser = await RetailDistributor.findById(frontdecoded._id);
|
const fuser = await RetailDistributor.findById(frontdecoded._id);
|
||||||
// console.log(fuser);
|
// console.log(fuser);
|
||||||
req.user = fuser;
|
req.user = fuser;
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
import mongoose, { Schema } from "mongoose";
|
|
||||||
|
|
||||||
const orderItemSchema = new Schema({
|
|
||||||
productId: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
ref: "Product",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
SKU: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
categoryName: {
|
|
||||||
type: String, // Directly store category name
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
brandName: {
|
|
||||||
type: String, // Directly store brand name
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
price: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
GST: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
HSN_Code: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
processquantity: {
|
|
||||||
//updated quantity
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const invoiceSchema = new mongoose.Schema({
|
|
||||||
invoiceId: { type: String, required: true, unique: true },
|
|
||||||
orderId: {
|
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
|
||||||
ref: "RdOrder",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
items: [orderItemSchema],
|
|
||||||
subtotal: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
gstTotal: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
invoiceAmount: { type: Number, required: true },
|
|
||||||
courier_name: { type: String },
|
|
||||||
courier_tracking_id: { type: String },
|
|
||||||
courierStatus: {
|
|
||||||
type: String,
|
|
||||||
enum: ["processing", "dispatched", "delivered"],
|
|
||||||
default: "processing",
|
|
||||||
},
|
|
||||||
courierstatus_timeline: {
|
|
||||||
processing: { type: Date },
|
|
||||||
dispatched: { type: Date },
|
|
||||||
delivered: { type: Date },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// Middleware to set the processing date only when the invoice is created
|
|
||||||
invoiceSchema.pre("save", function (next) {
|
|
||||||
if (this.isNew && !this.courierstatus_timeline.processing) {
|
|
||||||
this.courierstatus_timeline.processing = new Date();
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
export const InvoiceRd = mongoose.model("InvoiceRd", invoiceSchema);
|
|
@ -1,6 +1,4 @@
|
|||||||
import sendEmail from "../../Utils/sendEmail.js";
|
|
||||||
import RetailDistributor from "../RetailDistributor/RetailDistributorModel.js";
|
import RetailDistributor from "../RetailDistributor/RetailDistributorModel.js";
|
||||||
import { InvoiceRd } from "./invoiceModalRD.js";
|
|
||||||
import { RdOrder } from "./rdOrderModal.js";
|
import { RdOrder } from "./rdOrderModal.js";
|
||||||
|
|
||||||
// Controller to create a new order by RD
|
// Controller to create a new order by RD
|
||||||
@ -47,7 +45,6 @@ export const createOrderRD = async (req, res) => {
|
|||||||
description: item.description,
|
description: item.description,
|
||||||
image: item.image,
|
image: item.image,
|
||||||
quantity: item.count,
|
quantity: item.count,
|
||||||
remainingQuantity: item.count,
|
|
||||||
})),
|
})),
|
||||||
subtotal,
|
subtotal,
|
||||||
gstTotal,
|
gstTotal,
|
||||||
@ -65,7 +62,6 @@ export const createOrderRD = async (req, res) => {
|
|||||||
res.status(500).json({ message: "Server error", error });
|
res.status(500).json({ message: "Server error", error });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPlacedOrdersForRD = async (req, res) => {
|
export const getPlacedOrdersForRD = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const rdId = req.user?._id; // Assuming the Retail Distributor's ID is obtained from the authenticated request
|
const rdId = req.user?._id; // Assuming the Retail Distributor's ID is obtained from the authenticated request
|
||||||
@ -181,9 +177,7 @@ export const getSinglePlacedOrderForPD = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the specific order for the logged-in PD
|
// Fetch the specific order for the logged-in PD
|
||||||
const order = await RdOrder.findOne({ _id: orderId, pd: pdId })
|
const order = await RdOrder.findOne({ _id: orderId, pd: pdId });
|
||||||
.populate({ path: "addedBy" })
|
|
||||||
.populate({ path: "invoices" });
|
|
||||||
|
|
||||||
if (!order) {
|
if (!order) {
|
||||||
return res
|
return res
|
||||||
@ -198,637 +192,3 @@ export const getSinglePlacedOrderForPD = async (req, res) => {
|
|||||||
res.status(500).json({ message: "Server 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) => `
|
|
||||||
<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) {
|
|
||||||
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: `
|
|
||||||
<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 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 `
|
|
||||||
<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" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
@ -49,10 +49,18 @@ const orderItemSchema = new Schema({
|
|||||||
required: true,
|
required: true,
|
||||||
default: 1,
|
default: 1,
|
||||||
},
|
},
|
||||||
remainingQuantity: {
|
});
|
||||||
type: Number,
|
|
||||||
|
const StatusHistorySchema = new mongoose.Schema({
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
enum: ["new", "dispatched", "cancelled", "processing", "delivered"], // Ensure this matches your status enum
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
timestamp: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const rdOrderSchema = new Schema(
|
const rdOrderSchema = new Schema(
|
||||||
@ -85,19 +93,9 @@ const rdOrderSchema = new Schema(
|
|||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: [
|
enum: ["new", "dispatched", "cancelled", "processing", "delivered"],
|
||||||
"new",
|
|
||||||
"pending",
|
|
||||||
"processing",
|
|
||||||
"dispatched",
|
|
||||||
"cancelled",
|
|
||||||
"delivered",
|
|
||||||
],
|
|
||||||
default: "new",
|
default: "new",
|
||||||
},
|
},
|
||||||
invoices: [
|
|
||||||
{ type: mongoose.Schema.Types.ObjectId, ref: "InvoiceRd" }, // Similar to PdOrder invoices
|
|
||||||
],
|
|
||||||
statusUpdatedAt: {
|
statusUpdatedAt: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: Date.now,
|
default: Date.now,
|
||||||
@ -117,16 +115,15 @@ const rdOrderSchema = new Schema(
|
|||||||
ref: "User", // Reference to the PD associated with the RD
|
ref: "User", // Reference to the PD associated with the RD
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
// status_timeline: {
|
status_timeline: {
|
||||||
// new: { type: Date },
|
new: { type: Date },
|
||||||
// paid: { type: Date },
|
paid: { type: Date },
|
||||||
// processing: { type: Date },
|
processing: { type: Date },
|
||||||
// dispatched: { type: Date },
|
dispatched: { type: Date },
|
||||||
// delivered: { type: Date },
|
delivered: { type: Date },
|
||||||
// cancelled: { type: Date },
|
cancelled: { type: Date },
|
||||||
// returned: { type: Date },
|
returned: { type: Date },
|
||||||
// },
|
},
|
||||||
|
|
||||||
iscancelled: {
|
iscancelled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
@ -138,21 +135,21 @@ const rdOrderSchema = new Schema(
|
|||||||
courier_tracking_id: { type: String },
|
courier_tracking_id: { type: String },
|
||||||
isDelivered: { type: Boolean, required: true, default: false },
|
isDelivered: { type: Boolean, required: true, default: false },
|
||||||
DeliveredDate: { type: String, default: "" },
|
DeliveredDate: { type: String, default: "" },
|
||||||
// To store the status history
|
statusHistory: [StatusHistorySchema], // Add this field to store the status history
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Middleware to generate uniqueId and update statusHistory
|
// Middleware to update the statusUpdatedAt field whenever status changes
|
||||||
rdOrderSchema.pre("save", function (next) {
|
rdOrderSchema.pre("save", function (next) {
|
||||||
if (this.isNew) {
|
if (this.isModified("status")) {
|
||||||
const year = new Date().getFullYear().toString().slice(-2); // Get the last 2 digits of the year
|
this.statusUpdatedAt = Date.now();
|
||||||
const orderItemCount = this.orderItem.length; // Count the number of order items
|
// Add the new status and timestamp to statusHistory
|
||||||
const unique6CharId = nanoid(6); // Generate a 6-character unique ID
|
this.statusHistory.push({
|
||||||
|
status: this.status,
|
||||||
this.uniqueId = `${year}-${orderItemCount}-${unique6CharId}`;
|
timestamp: this.statusUpdatedAt,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import {
|
import {
|
||||||
cancelOrderController,
|
|
||||||
createOrderRD,
|
createOrderRD,
|
||||||
getCancelledOrders,
|
|
||||||
getNewOrders,
|
|
||||||
getPendignOrders,
|
|
||||||
getPlacedOrdersForPD,
|
getPlacedOrdersForPD,
|
||||||
getPlacedOrdersForRD,
|
getPlacedOrdersForRD,
|
||||||
getSinglePlacedOrderForPD,
|
getSinglePlacedOrderForPD,
|
||||||
getSinglePlacedOrderForRD,
|
getSinglePlacedOrderForRD,
|
||||||
processOrder,
|
|
||||||
} from "./rdOrderController.js";
|
} from "./rdOrderController.js";
|
||||||
import { isAuthenticatedRD } from "../../middlewares/rdAuth.js";
|
import { isAuthenticatedRD } from "../../middlewares/rdAuth.js";
|
||||||
import { isAuthenticatedUser } from "../../middlewares/auth.js";
|
import { isAuthenticatedUser } from "../../middlewares/auth.js";
|
||||||
@ -27,19 +22,7 @@ router
|
|||||||
.get(isAuthenticatedUser, getPlacedOrdersForPD);
|
.get(isAuthenticatedUser, getPlacedOrdersForPD);
|
||||||
|
|
||||||
router
|
router
|
||||||
.route("/pd-get-single-place-order/:id")
|
.route("/pd-get-all-place-order/:id")
|
||||||
.get(isAuthenticatedUser, getSinglePlacedOrderForPD);
|
.get(isAuthenticatedUser, getSinglePlacedOrderForPD);
|
||||||
|
|
||||||
router.route("/pd-process-order").post(isAuthenticatedUser, processOrder);
|
|
||||||
router.route("/pd-get-new-orders").get(isAuthenticatedUser, getNewOrders);
|
|
||||||
router
|
|
||||||
.route("/pd-get-pending-orders")
|
|
||||||
.get(isAuthenticatedUser, getPendignOrders);
|
|
||||||
router
|
|
||||||
.route("/pd-get-cancelled-orders")
|
|
||||||
.get(isAuthenticatedUser, getCancelledOrders);
|
|
||||||
router
|
|
||||||
.route("/pd-cancel-order/:id")
|
|
||||||
.put(isAuthenticatedUser, cancelOrderController);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
Loading…
Reference in New Issue
Block a user