diff --git a/resources/PD_Orders/pdOrderController.js b/resources/PD_Orders/pdOrderController.js index 50c5860..64a5575 100644 --- a/resources/PD_Orders/pdOrderController.js +++ b/resources/PD_Orders/pdOrderController.js @@ -2,6 +2,7 @@ import mongoose from "mongoose"; import { PdOrder } from "./pdOrderModal.js"; +import sendEmail from "../../Utils/sendEmail.js"; export const createOrder = async (req, res) => { try { @@ -35,6 +36,17 @@ export const createOrder = async (req, res) => { 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, })), subtotal, @@ -88,14 +100,17 @@ export const getPlacedOrder = async (req, res) => { 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", - }); + const doc = await PdOrder.findById(id) + .populate({ + path: "orderItem.productId", + }) + .populate({ path: "addedBy" }); if (doc) { return res .status(200) @@ -108,24 +123,571 @@ export const getPlacedOrderById = async (req, res) => { } }; -export const getPlacedOrderAdmin = async (req, res) => { +export const getPlacedNewOrderAdmin = async (req, res) => { try { - const plcaedOrders = await PdOrder.find() - .sort({ - createdAt: -1, - }) - .populate({ - path: "orderItem.productId", - }) - .populate({ - path: "addedBy", - }); - if (plcaedOrders) { - return res.status(200).json({ plcaedOrders }); + // 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: "Not placed yet " }); + return res.status(404).json({ return_msg: "No new orders placed yet" }); } catch (error) { - console.error("Error creating order:", error); + console.error("Error fetching new 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: "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 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" }) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit) + .populate({ path: "orderItem.productId" }) + .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 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: "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 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: "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" }); + } +}; + +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: ` + Hi ${ + order?.addedBy.name + }, +

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.

+ +

Cancellation Reason: ${ + order?.order_Cancelled_Reason || "No specific reason provided." + }

+ +

Items:

+ + + + + + + + + + + + + + + + ${order?.orderItem + ?.map( + (product, index) => ` + + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameCategoryBrandImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${ + product.categoryName + }${ + product.brandName + }${
+                    product.name
+                  }${ + product.quantity + }₹${ + product.price + }₹${ + (product.GST * product.price) / 100 + }₹${ + (product.price + (product.GST * product.price) / 100) * + product.quantity + }
Total Amount :₹${ + order?.grandTotal + }
+ +

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.

+ +
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.
+
+ Best regards,
+ Team Cheminova + `, + }); + + 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: ` +
+

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.

+ Hi ${ + order?.addedBy.name + }, +

Order Status: ${ + order?.status.charAt(0).toUpperCase() + order?.status.slice(1) + }

+ + + +

Order Items:

+ + + + + + + + + + + + + + + ${order?.orderItem + ?.map( + (product, index) => ` + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameVariantImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${ + product.variant_Name || "N/A" + } + ${
+                    product.name
+                  } + ${ + product.quantity + }₹${ + product.price + }₹${ + (product.GST * product.price) / 100 + }₹${ + (product.price + (product.GST * product.price) / 100) * + product.quantity + }
Total Amount:₹${ + order?.grandTotal + }
+ +
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.
+
Thank you for choosing Cheminova!
+
+ Best regards,
+ Team Cheminova +
+ `, + }); + 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: ` + Hi, +

Exciting news! Your order #${ + order?.uniqueId + } has been dispatched and is en route to you. 🚚 Here are the details:

+ +

Courier Name: ${ + order?.courier_name || "N/A" + }

+

Courier Tracking ID: ${ + order?.courier_tracking_id || "N/A" + }

+ + + +

Items:

+ + + + + + + + + + + + + + + ${order?.orderItem + ?.map( + (product, index) => ` + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameSKUImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${ + product.SKU + } + ${
+                    product.name
+                  } + ${ + product.quantity + }₹${ + product.price + }₹${ + (product.GST * product.price) / 100 + }₹${ + (product.price + (product.GST * product.price) / 100) * + product.quantity + }
Total Amount:₹${ + order?.grandTotal + }
+ +

Order Status: Dispatched

+

If you have any questions or need assistance, feel free to reply to this email.

+
Thanks for choosing Cheminova! We hope you enjoy your purchase.
+
+ Best regards,
+ Team Cheminova + `, + }); + 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 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: ` + Hi, +

+ Great news! Your order #${ + order?.uniqueId + } has been successfully delivered to your doorstep. We hope everything is just as you expected! +

+ + + +

Items:

+ + + + + + + + + + + + + + + ${order?.orderItem + ?.map( + (product, index) => ` + + + + + + + + + + + ` + ) + .join("")} + + + + + +
S No.Product NameSKUImageQuantityPriceGST AmountSubTotal
${ + index + 1 + }${ + product.name + }${ + product.SKU + } + ${
+                    product.name
+                  } + ${ + product.quantity + }₹${ + product.price + }₹${ + (product.GST * product.price) / 100 + }₹${ + (product.price + (product.GST * product.price) / 100) * + product.quantity + }
Total Amount:₹${ + order?.grandTotal + }
+ +

Delivery Date: ${ + req.body.DDate + }

+ +

+ 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! +

+ +
+ If you have any questions or concerns about your order, feel free to reply to this email. +
+ +
+ Thank you for choosing Cheminova! We hope to serve you again soon. +
+ +
+ Best regards,
+ Team Cheminova + `, + }); + 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!" }); + } +}; diff --git a/resources/PD_Orders/pdOrderModal.js b/resources/PD_Orders/pdOrderModal.js index c4ac396..62212aa 100644 --- a/resources/PD_Orders/pdOrderModal.js +++ b/resources/PD_Orders/pdOrderModal.js @@ -1,23 +1,60 @@ import mongoose, { Schema } from "mongoose"; import { nanoid } from "nanoid"; // To generate unique 6-char IDs -const OrderItemSchema = new mongoose.Schema({ +const orderItemSchema = new Schema({ productId: { - type: mongoose.Schema.Types.ObjectId, + 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, + }, + description: { + type: String, + }, + image: [ + { + public_id: String, + url: String, + }, + ], quantity: { type: Number, required: true, - min: 1, + default: 1, }, }); const StatusHistorySchema = new mongoose.Schema({ status: { type: String, - enum: ["new", "dispatched", "paid", "process"], // Ensure this matches your status enum + enum: ["new", "dispatched", "cancelled", "processing", "delivered"], // Ensure this matches your status enum required: true, }, timestamp: { @@ -41,7 +78,7 @@ const pdOrderSchema = new Schema( type: String, required: true, }, - orderItem: [OrderItemSchema], + orderItem: [orderItemSchema], subtotal: { type: Number, required: true, @@ -56,7 +93,7 @@ const pdOrderSchema = new Schema( }, status: { type: String, - enum: ["new", "dispatched", "paid", "processing"], + enum: ["new", "dispatched", "cancelled", "processing", "delivered"], default: "new", }, statusUpdatedAt: { diff --git a/resources/PD_Orders/pdOrderRoute.js b/resources/PD_Orders/pdOrderRoute.js index da19842..a10710c 100644 --- a/resources/PD_Orders/pdOrderRoute.js +++ b/resources/PD_Orders/pdOrderRoute.js @@ -2,9 +2,14 @@ import express from "express"; import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; import { createOrder, + getCancelledOrdersAdmin, + getDeliveredOrdersAdmin, + getDispatchedOrdersAdmin, + getPlacedNewOrderAdmin, getPlacedOrder, - getPlacedOrderAdmin, getPlacedOrderById, + getProcessingOrdersAdmin, + updateOrderStatusById, } from "./pdOrderController.js"; const router = express.Router(); @@ -23,9 +28,26 @@ router authorizeRoles("principal-Distributor"), getPlacedOrder ); -router.route("/get-single-placed-order-pd/:id").get(getPlacedOrderById); router - .route("/get-placed-order-admin") - .get(isAuthenticatedUser, authorizeRoles("admin"), getPlacedOrderAdmin); + .route("/get-single-placed-order-pd/:id") + .get(isAuthenticatedUser, getPlacedOrderById); +router + .route("/get-new-order-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getPlacedNewOrderAdmin); +router + .route("/get-dispatched-order-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getDispatchedOrdersAdmin); +router + .route("/get-cancelled-order-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getCancelledOrdersAdmin); +router + .route("/get-processing-order-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getProcessingOrdersAdmin); +router + .route("/get-delivered-order-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getDeliveredOrdersAdmin); +router + .route("/change/status/:id") + .patch(isAuthenticatedUser, authorizeRoles("admin"), updateOrderStatusById); export default router; diff --git a/resources/user/userController.js b/resources/user/userController.js index 1c700d2..3bffbb6 100644 --- a/resources/user/userController.js +++ b/resources/user/userController.js @@ -1029,8 +1029,10 @@ export const getAllPrincipalDistributorbytmId = catchAsyncErrors( async (req, res, next) => { const { page = 1, show = 10, name, mobileNumber } = req.query; const tmId = req.params.id; - if(!tmId){ - return res.status(400).json({ message: "Please provide Territory Manager ID" }); + if (!tmId) { + return res + .status(400) + .json({ message: "Please provide Territory Manager ID" }); } // Create a filter object const filter = { role: "principal-Distributor" };