import mongoose from "mongoose"; import sendEmail from "../../Utils/sendEmail.js"; import RetailDistributor from "../RetailDistributor/RetailDistributorModel.js"; import { InvoiceRd } from "./invoiceModalRD.js"; import { RdOrder } from "./rdOrderModal.js"; import { PDStock } from "../Stock/PdStockModel.js"; import { createKYC } from "../../Utils/rejectKyc.js"; import { Notification } from "../Notification/notificationModal.js"; import { sendPushNotification } from "../../Utils/sendPushNotification.js"; import { RDStock } from "../Stock/RdStockModel.js"; // Controller to create a new order by RD export const createOrderRD = async (req, res) => { try { const { paymentMode, shipTo, billTo, orderItems, subtotal, gstTotal, grandTotal, } = req.body; const rdId = req.user._id; console.log(rdId); // Fetch the Retail Distributor (RD) to find the associated Principal Distributor (PD) const rd = await RetailDistributor.findById(rdId).populate( "principal_distributer" ); if (!rd) { return res.status(404).json({ message: "Retail Distributor not found" }); } if (!rd.principal_distributer) { return res.status(422).json({ msg: "You are not assoicated with any Pd please contact support team", }); } const pdId = rd.principal_distributer._id; if (pdId) { await createKYC( rd.principal_distributer._id, "New order placed ", `New Order placed.` ); await Notification.create({ title: "New order placed ", msg: `New order placed `, added_for: rd.principal_distributer._id, }); } const newOrder = new RdOrder({ paymentMode, shipTo, billTo, orderItem: orderItems.map((item) => ({ productId: item._id, SKU: item.SKU, name: item.name, categoryName: item.category.categoryName, // Store category name brandName: item.brand.brandName, // Store brand name price: item.price, GST: item.GST, HSN_Code: item.HSN_Code, description: item.description, image: item.brand?.image?.length > 0 ? item.brand.image[0] : item.image, quantity: item.count, remainingQuantity: item.count, })), subtotal, gstTotal, grandTotal, addedBy: rdId, // The RD who placed the order (Retail Distributor) pd: pdId, // Reference to the PD associated with the RD }); await newOrder.save(); res .status(201) .json({ message: "Order placed successfully", order: newOrder }); } catch (error) { console.log(error.message); res.status(500).json({ message: "Server error", error }); } }; export const getPlacedOrdersForRD = async (req, res) => { try { const rdId = req.user?._id; // Assuming the Retail Distributor's ID is obtained from the authenticated request if (!rdId) { return res.status(401).json({ message: "Unauthorized access" }); } // Extract page and limit from query parameters, with default values const page = parseInt(req.query.page, 10) || 1; const limit = parseInt(req.query.limit, 10) || 5; // Calculate how many documents to skip for pagination const skip = (page - 1) * limit; // Fetch total count of orders for this RD (for pagination purposes) const totalOrders = await RdOrder.countDocuments({ addedBy: rdId }); // Fetch orders for the logged-in RD const placedOrders = await RdOrder.find({ addedBy: rdId }) .sort({ createdAt: -1 }) // Sort by creation date, newest first .skip(skip) // Skip documents for pagination .limit(limit); // Limit number of documents returned if (!placedOrders || placedOrders.length === 0) { return res .status(404) .json({ message: "No orders found for this Retail Distributor" }); } // Send the paginated order list and total count of orders res.status(200).json({ placedOrders, totalOrders }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error", error }); } }; export const getSinglePlacedOrderForRD = async (req, res) => { try { const rdId = req.user?._id; if (!rdId) { return res.status(401).json({ message: "Unauthorized access" }); } // Assuming the Retail Distributor's ID is obtained from the authenticated request const orderId = req.params.id; // Assuming the order ID is passed in the URL as a parameter if (!rdId) { return res.status(401).json({ message: "Unauthorized access" }); } if (!orderId) { return res.status(400).json({ message: "Order ID is required" }); } // Fetch the specific order for the logged-in RD const order = await RdOrder.findOne({ _id: orderId, addedBy: rdId }); if (!order) { return res .status(404) .json({ message: "Order not found for this Retail Distributor" }); } // Send the single order document res.status(200).json({ singleOrder: order }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error", error }); } }; export const 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 RdOrder.findById(id) .populate({ path: "orderItem.productId", populate: { path: "brand", }, }) .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 getPlacedOrdersForPD = async (req, res) => { try { const pdId = req.user?._id; if (!pdId) { return res.status(401).json({ return_message: "Unauthorized access " }); } // Extract page and limit from query parameters const page = parseInt(req.query.page, 10) || 1; const limit = parseInt(req.query.limit, 10) || 5; // Calculate the number of documents to skip const skip = (page - 1) * limit; const totalOrders = await RdOrder.countDocuments({ pd: pdId }); // Fetch all orders where the PD is associated with the order const plcaedOrders = await RdOrder.find({ pd: pdId }) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); if (!plcaedOrders || plcaedOrders.length === 0) { return res .status(404) .json({ message: "No orders found for this Principal Distributor" }); } res.status(200).json({ plcaedOrders, totalOrders }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error", error }); } }; export const getSinglePlacedOrderForPD = async (req, res) => { try { const pdId = req.user?._id; // Assuming the Principal Distributor's ID is obtained from the authenticated request const orderId = req.params.id; // Assuming the order ID is passed in the URL as a parameter if (!pdId) { return res.status(401).json({ message: "Unauthorized access" }); } if (!orderId) { return res.status(400).json({ message: "Order ID is required" }); } // Fetch the specific order for the logged-in PD const order = await RdOrder.findOne({ _id: orderId, pd: pdId }) .populate({ path: "addedBy" }) .populate({ path: "invoices" }); if (!order) { return res .status(404) .json({ message: "Order not found for this Principal Distributor" }); } // Send the single order document res.status(200).json({ singleOrder: order }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error", error }); } }; export const getNewOrders = async (req, res) => { try { const pdId = req.user?._id; if (!pdId) { return res.status(401).json({ return_message: "Unauthorized access " }); } // Extract page and limit from query parameters const page = parseInt(req.query.page, 10) || 1; const limit = parseInt(req.query.limit, 10) || 5; // Calculate the number of documents to skip const skip = (page - 1) * limit; const totalOrders = await RdOrder.countDocuments({ pd: pdId, status: "new", }); // Fetch all orders where the PD is associated with the order const plcaedOrders = await RdOrder.find({ pd: pdId, status: "new" }) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); if (!plcaedOrders || plcaedOrders.length === 0) { return res .status(404) .json({ message: "No orders found for this Principal Distributor" }); } res.status(200).json({ plcaedOrders, totalOrders }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error", error }); } }; export const getPendignOrders = async (req, res) => { try { const pdId = req.user?._id; if (!pdId) { return res.status(401).json({ return_message: "Unauthorized access " }); } // Extract page and limit from query parameters const page = parseInt(req.query.page, 10) || 1; const limit = parseInt(req.query.limit, 10) || 5; // Calculate the number of documents to skip const skip = (page - 1) * limit; const totalOrders = await RdOrder.countDocuments({ pd: pdId, status: "new", }); // Fetch all orders where the PD is associated with the order const plcaedOrders = await RdOrder.find({ pd: pdId, status: "pending" }) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); if (!plcaedOrders || plcaedOrders.length === 0) { return res .status(404) .json({ message: "No orders found for this Principal Distributor" }); } res.status(200).json({ plcaedOrders, totalOrders }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error", error }); } }; export const getCancelledOrders = async (req, res) => { try { const pdId = req.user?._id; if (!pdId) { return res.status(401).json({ return_message: "Unauthorized access " }); } // Extract page and limit from query parameters const page = parseInt(req.query.page, 10) || 1; const limit = parseInt(req.query.limit, 10) || 5; // Calculate the number of documents to skip const skip = (page - 1) * limit; const totalOrders = await RdOrder.countDocuments({ pd: pdId, status: "new", }); // Fetch all orders where the PD is associated with the order const plcaedOrders = await RdOrder.find({ pd: pdId, status: "cancelled" }) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); if (!plcaedOrders || plcaedOrders.length === 0) { return res .status(404) .json({ message: "No orders found for this Principal Distributor" }); } res.status(200).json({ plcaedOrders, totalOrders }); } catch (error) { console.error(error); res.status(500).json({ message: "Server error", error }); } }; export const processOrder = async (req, res) => { try { const { orderId, invoiceItems } = req.body; if (!orderId || !invoiceItems || !Array.isArray(invoiceItems)) { return res .status(400) .json({ error: "Missing required fields or invalid data" }); } // Find the order by ID const order = await RdOrder.findById(orderId).populate("addedBy"); if (!order) { return res.status(404).json({ error: "Order not found" }); } // Validate quantities const exceededItems = invoiceItems .filter((item) => { const orderItem = order.orderItem.find( (i) => i.productId.toString() === item.productId.toString() ); return orderItem && item.processquantity > orderItem.remainingQuantity; }) .map((item) => item.name); if (exceededItems.length > 0) { return res.status(400).json({ error: `The following items have more quantity than remaining in the order: ${exceededItems.join( ", " )}`, }); } // 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 // Stock calculation part const pdStock = await PDStock.findOne({ userId: order.pd }); if (!pdStock) { return res.status(404).json({ error: "Stock not available" }); } // Update stock and validate const updatedInvoiceItems = invoiceItems.filter((item) => { // Find the product in the stock const productInStock = pdStock.products.find( (p) => p.productid.toString() === item.productId.toString() ); // If the product exists in stock if (productInStock) { // Check if the processquantity is less than or equal to available stock if (item.processquantity <= productInStock.Stock) { // Deduct the quantity from the stock productInStock.Stock -= item.processquantity; productInStock.liquidation += item.processquantity; // Add the item to updatedInvoiceItems (since stock is sufficient) return true; } } // If no stock or insufficient stock, don't add the item return false; }); // If no items are left after stock validation, return an error if (updatedInvoiceItems.length === 0) { return res .status(400) .json({ error: "No stock available for the requested items." }); } // Save updated stock await pdStock.save(); // Generate unique invoice number const existingInvoices = await InvoiceRd.find({ orderId }); const invoiceNumber = existingInvoices.length + 1; const invoiceId = `ch/${order.uniqueId}/${updatedInvoiceItems.length}/${invoiceNumber}`; // Calculate subtotal, gstTotal, and invoiceAmount for processed items let subtotal = 0; let gstTotal = 0; let invoiceAmount = 0; updatedInvoiceItems.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: updatedInvoiceItems, 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 = updatedInvoiceItems.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"; await sendPushNotification( order.addedBy?.fcm_token, "Exciting news", "Your order got processed" ); await Notification.create({ title: "Exciting news", msg: `Your order got processed`, added_for: order.addedBy?._id, }); // All items are fully processed } else { order.status = "pending"; await sendPushNotification( order.addedBy?.fcm_token, "Exciting news", "Some of your items got in processing " ); await Notification.create({ title: "Exciting news", msg: `Some of your items got in processing `, added_for: order.addedBy?._id, }); // 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 = updatedInvoiceItems .map( (item, index) => ` ${ index + 1 } ${ item.name } ${ item.image && item.image.length > 0 ? `${item.name}` : "No Image" } ${ item.processquantity } ₹${ item.price } ₹${ (item.GST * item.price) / 100 } ₹${ (item.price + (item.GST * item.price) / 100) * item.processquantity } ` ) .join(""); const pendingItems = order.orderItem .filter((item) => item.remainingQuantity > 0) .map( (item, index) => ` ${ index + 1 } ${ item.name } ${ item.image && item.image.length > 0 ? `${item.name}` : "No Image" } ${ item.remainingQuantity } ₹${ item.price } ₹${ (item.GST * item.price) / 100 } ₹${ (item.price + (item.GST * item.price) / 100) * item.remainingQuantity } ` ) .join(""); // Dynamic email subject and message based on the order status const emailSubject = allItemsProcessed ? `Your Order #${order.uniqueId} is in Processing!` : `Your Order #${order.uniqueId} is Partially Processed!`; const emailMessage = allItemsProcessed ? `

Exciting news! Your order #${order.uniqueId} has entered the processing phase. We're working hard to ensure everything is perfect for you.

Your invoice ID is: ${savedInvoice.invoiceId}

` : `

Good news! Some items of your order #${order.uniqueId} have been processed. The remaining items will be processed soon.

Your invoice ID is: ${savedInvoice.invoiceId}

`; await sendEmail({ to: order.addedBy.email, from: process.env.SEND_EMAIL_FROM, subject: emailSubject, html: `
${emailMessage} Hi ${ order.addedBy.name },

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

Processed Items:

${processedItems}
S No. Product Name Image Quantity Price GST Amount SubTotal
Total Amount: ₹${savedInvoice.invoiceAmount.toFixed( 2 )}
${ pendingItems.length > 0 ? `

Pending Items:

${pendingItems}
S No. Product Name Image Quantity Price GST Amount SubTotal
Pending Amount: ₹${pendingTotalAmount.toFixed( 2 )}
` : "" }
If you have any concerns or further questions, please feel free to reply to this email.

Best regards,
Team Cheminova
`, }); res.status(200).json({ message: "Order processed and invoice created successfully", invoiceId: savedInvoice.invoiceId, }); } catch (error) { res .status(500) .json({ error: "An error occurred while processing the order" }); } }; // get invoice by id export const getProcessingInvoicesForPd = async (req, res) => { console.log("req , cames"); try { // Ensure this is passed in your route, or retrieve it from the logged-in user const page = parseInt(req.query.page, 10) || 1; // Default page is 1 const limit = parseInt(req.query.limit, 10) || 5; // Default limit is 5 const skip = (page - 1) * limit; const { invoiceId, orderId } = req.query; // Search parameters const pdId = req.user._id; if (!pdId) { return res.status(400).json({ message: "PD ID is required" }); } // Build the base query for fetching processing invoices let query = { courierStatus: "processing" }; // If invoiceId is provided, add regex search for invoiceId if (invoiceId) { query.invoiceId = { $regex: invoiceId, $options: "i" }; // Case-insensitive search } // Fetch the invoices matching the query, along with pagination and population const invoices = await InvoiceRd.find(query) .skip(skip) .limit(limit) .populate({ path: "orderId", match: { pd: pdId }, // Match the associated PD in the RdOrder model select: "uniqueId", // Only select uniqueId from the order }) .sort({ "courierstatus_timeline.processing": -1 }); // Sort by processing date, newest first console.log(invoices); // Filter invoices where the orderId exists and matches the search (if orderId is provided) let filteredInvoices = invoices.filter( (invoice) => invoice.orderId !== null ); if (orderId) { filteredInvoices = filteredInvoices.filter( (invoice) => invoice.orderId && invoice.orderId.uniqueId && invoice.orderId.uniqueId.toLowerCase().includes(orderId.toLowerCase()) ); } // Pagination metadata const totalCount = filteredInvoices.length; const totalPages = Math.ceil(totalCount / limit); // Send the filtered invoices with pagination details res.status(200).json({ totalCount, currentPage: page, totalPages, invoices: filteredInvoices, }); } catch (error) { console.error(error); res.status(500).json({ error: error.message }); } }; export const getInvoiceDetailsByIdForPD = async (req, res) => { const { invoiceId } = req.params; try { // Find the invoice by ID and populate the orderId and addedBy fields const invoice = await InvoiceRd.findById(invoiceId).populate({ path: "orderId", model: "RdOrder", populate: { path: "addedBy", model: "RetailDistributor", select: "name email mobile_number fcm_token ", }, }); 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 updateCourierStatusToDispatchedForPD = async (req, res) => { const { invoiceId } = req.params; const { courierName, couriertrackingId } = req.body; try { // Find the invoice by ID const invoice = await InvoiceRd.findById(invoiceId).populate({ path: "orderId", populate: { path: "addedBy", select: "email fcm_token", }, }); 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(); } await sendPushNotification( order.addedBy?.fcm_token, "Exciting news", "Order dispatched " ); await Notification.create({ title: "Exciting news", msg: `Order dispatched`, added_for: order.addedBy?._id, }); // 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: ` Hi,

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

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

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

Items:

${invoice?.items ?.map( (product, index) => ` ` ) .join("")}
S No. Product Name SKU Image Quantity Price GST Amount SubTotal
${ index + 1 } ${ product.name } ${ product.SKU } ${ product.image && product.image.length > 0 ? `${product.name}` : "No Image" } ${ product.processquantity } ₹${ product.price } ₹${( (product.GST * product.price) / 100 ).toFixed(2)} ₹${( (product.price + (product.GST * product.price) / 100) * product.processquantity ).toFixed(2)}
Total Amount: ₹${ invoice.invoiceAmount }

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 `, }); res.status(200).json({ message: "Courier status updated to dispatched", orderStatus: order.status, }); } catch (error) { res.status(500).json({ error: error.message }); } }; export const updateCourierStatusToDeliveredForPD = async (req, res) => { const { invoiceId } = req.params; try { // Find the invoice by ID const invoice = await InvoiceRd.findById(invoiceId).populate({ path: "orderId", populate: { path: "addedBy", select: "email fcm_token", }, }); 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(); } // Get the userId from the order's addedBy const userId = order?.addedBy?._id; await sendPushNotification( order?.addedBy?.fcm_token, "Exciting news", "Order Delivered " ); await Notification.create({ title: "Exciting news", msg: `Order Delivered `, added_for: order?.addedBy?._id, }); if (!userId) { return res.status(400).json({ error: "User not found for the order" }); } // Check if RDStock exists for the user let rdStock = await RDStock.findOne({ userId }); if (!rdStock) { // If no stock record exists, create a new one rdStock = new RDStock({ userId, products: [], // Initialize with empty products array }); } // Iterate over each item in the invoice for (let item of invoice.items) { const { productId, processquantity } = item; // Check if the product already exists in the PDStock for the user const existingProduct = rdStock.products.find( (p) => p.productid.toString() === productId.toString() ); if (existingProduct) { // If the product exists, update the stock by adding the processquantity existingProduct.Stock += processquantity; } else { // If the product doesn't exist, add a new entry for the product rdStock.products.push({ productid: productId, Stock: processquantity, }); } } // Save the updated PDStock await rdStock.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: ` Hi,

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

Items:

${invoice?.items ?.map( (product, index) => ` ` ) .join("")}
S No. Product Name SKU Image Quantity Price GST Amount SubTotal
${ index + 1 } ${ product.name } ${ product.SKU } ${ product.image && product.image.length > 0 ? `${product.name}` : "No Image" } ${ product.processquantity } ₹${ product.price } ₹${( (product.GST * product.price) / 100 ).toFixed(2)} ₹${( (product.price + (product.GST * product.price) / 100) * product.processquantity ).toFixed(2)}
Total Amount: ₹${invoice.invoiceAmount.toFixed( 2 )}

Delivery Date: ${formattedDate}

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 `, }); res.status(200).json({ message: "Courier status updated to delivered", orderStatus: order.status, }); } catch (error) { res.status(500).json({ error: error.message }); } }; export const getDispatchedInvoicesForPd = async (req, res) => { try { const pdId = req.user._id; // Ensure this is passed in your route, or retrieve it from the logged-in user const page = parseInt(req.query.page, 10) || 1; // Default page is 1 const limit = parseInt(req.query.limit, 10) || 5; // Default limit is 5 const skip = (page - 1) * limit; const { invoiceId, orderId } = req.query; // Search parameters if (!pdId) { return res.status(400).json({ message: "PD ID is required" }); } // Build the base query for fetching processing invoices let query = { courierStatus: "dispatched" }; // If invoiceId is provided, add regex search for invoiceId if (invoiceId) { query.invoiceId = { $regex: invoiceId, $options: "i" }; // Case-insensitive search } // Fetch the invoices matching the query, along with pagination and population const invoices = await InvoiceRd.find(query) .skip(skip) .limit(limit) .populate({ path: "orderId", match: { pd: pdId }, // Match the associated PD in the RdOrder model select: "uniqueId", // Only select uniqueId from the order }) .sort({ "courierstatus_timeline.processing": -1 }); // Sort by processing date, newest first // Filter invoices where the orderId exists and matches the search (if orderId is provided) let filteredInvoices = invoices.filter( (invoice) => invoice.orderId !== null ); if (orderId) { filteredInvoices = filteredInvoices.filter( (invoice) => invoice.orderId && invoice.orderId.uniqueId && invoice.orderId.uniqueId.toLowerCase().includes(orderId.toLowerCase()) ); } // Pagination metadata const totalCount = filteredInvoices.length; const totalPages = Math.ceil(totalCount / limit); // Send the filtered invoices with pagination details res.status(200).json({ totalCount, currentPage: page, totalPages, invoices: filteredInvoices, }); } catch (error) { console.error(error); res.status(500).json({ error: error.message }); } }; export const getDeliveredInvoicesForPd = async (req, res) => { try { const pdId = req.user._id; // Ensure this is passed in your route, or retrieve it from the logged-in user const page = parseInt(req.query.page, 10) || 1; // Default page is 1 const limit = parseInt(req.query.limit, 10) || 5; // Default limit is 5 const skip = (page - 1) * limit; const { invoiceId, orderId } = req.query; // Search parameters if (!pdId) { return res.status(400).json({ message: "PD ID is required" }); } // Build the base query for fetching processing invoices let query = { courierStatus: "delivered" }; // If invoiceId is provided, add regex search for invoiceId if (invoiceId) { query.invoiceId = { $regex: invoiceId, $options: "i" }; // Case-insensitive search } // Fetch the invoices matching the query, along with pagination and population const invoices = await InvoiceRd.find(query) .skip(skip) .limit(limit) .populate({ path: "orderId", match: { pd: pdId }, // Match the associated PD in the RdOrder model select: "uniqueId", // Only select uniqueId from the order }) .sort({ "courierstatus_timeline.processing": -1 }); // Sort by processing date, newest first // Filter invoices where the orderId exists and matches the search (if orderId is provided) let filteredInvoices = invoices.filter( (invoice) => invoice.orderId !== null ); if (orderId) { filteredInvoices = filteredInvoices.filter( (invoice) => invoice.orderId && invoice.orderId.uniqueId && invoice.orderId.uniqueId.toLowerCase().includes(orderId.toLowerCase()) ); } // Pagination metadata const totalCount = filteredInvoices.length; const totalPages = Math.ceil(totalCount / limit); // Send the filtered invoices with pagination details res.status(200).json({ totalCount, currentPage: page, totalPages, invoices: filteredInvoices, }); } catch (error) { console.error(error); res.status(500).json({ error: error.message }); } }; // 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 sendPushNotification( order.addedBy?.fcm_token, "Sorry! order cancelled fully.", `Order has been cancelled due to ${cancellationReason}` ); await Notification.create({ title: "Sorry! order cancelled fully.", msg: `Order dispatched`, added_for: order.addedBy?._id, }); await sendEmail({ to: `${order.addedBy.email}`, // Change to your recipient from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender subject: `Order #${order.uniqueId} Cancellation Confirmation`, html: ` Hi ${order.addedBy.name},

We regret to inform you that your order #${order.uniqueId} has been fully cancelled.

Cancellation Reason: ${order.order_Cancelled_Reason}

If you have any concerns or further questions, please feel free to reply to this email.

Best regards,
Team Cheminova `, }); } else { // If invoices are present const invoices = await InvoiceRd.find({ _id: { $in: order.invoices } }); // Collect all invoice data const invoiceDetails = invoices.map((invoice) => { let subtotal = 0; let gstTotal = 0; let totalAmount = 0; const processedItems = invoice.items .map((item) => { const itemSubtotal = item.price * item.processquantity; const itemGST = ((item.price * item.GST) / 100) * item.processquantity; subtotal += itemSubtotal; gstTotal += itemGST; totalAmount += itemSubtotal + itemGST; const itemImage = item.image && Array.isArray(item.image) ? item.image[0]?.url : ""; return ` ${ item.SKU } ${ item.name } ${
              item.name
            } ${ item.processquantity } ₹${ item.price } ₹${ (item.price * item.GST) / 100 } ₹${ itemSubtotal + itemGST } `; }) .join(""); return { invoiceId: invoice.invoiceId, processedItems, totalAmount, }; }); // Collect all delivered and remaining items let cancelItems = ""; let totalCancelAmount = 0; if (Array.isArray(order.orderItem)) { order.orderItem.forEach((item) => { if (item.remainingQuantity > 0) { const itemSubtotal = item.price * item.remainingQuantity; const itemGST = ((item.price * item.GST) / 100) * item.remainingQuantity; totalCancelAmount += itemSubtotal + itemGST; const itemImage = item.image && Array.isArray(item.image) ? item.image[0]?.url : ""; cancelItems += ` ${ item.SKU } ${ item.name } ${
              item.name
            } ${ item.remainingQuantity } ₹${ item.price } ₹${ (item.price * item.GST) / 100 } ₹${ itemSubtotal + itemGST } `; } }); } await sendPushNotification( order.addedBy?.fcm_token, "Sorry! order cancelled partially.", `Order has been cancelled due to ${ cancellationReason ? cancellationReason : "some reason" }` ); await Notification.create({ title: "Sorry! order cancelled fully.", msg: `Order dispatched`, added_for: order.addedBy?._id, }); await sendEmail({ to: `${order.addedBy.email}`, // Change to your recipient from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender subject: `Order #${order.uniqueId} is Partially Cancelled!`, html: ` Hi ${ order.addedBy.name },

We would like to inform you that your order #${ order.uniqueId } has been partially cancelled.

Cancellation Reason: ${ order.order_Cancelled_Reason }

Delivered Items:

${invoiceDetails .map( (details) => `
Invoice ID: ${ details.invoiceId }
${details.processedItems}
S No. Product Name Image Quantity Price GST Amount SubTotal
Total Amount: ₹${details.totalAmount.toFixed( 2 )}
` ) .join("")}

Cancelled Items:

${cancelItems}
S No. Product Name Image Quantity Price GST Amount SubTotal
Total Refund Amount: ₹${totalCancelAmount.toFixed( 2 )}

If you have any concerns or further questions, please feel free to reply to this email.

Best regards,
Team Cheminova `, }); } res.status(200).json({ message: "Order cancelled successfully" }); } catch (error) { console.error("Error cancelling order:", error); res .status(500) .json({ message: "An error occurred while cancelling the order" }); } }; 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 getAllOrdersByDistributor = async (req, res) => { const { distributorId } = req.params; const { orderId, status } = req.query; // Added status to query parameters // console.log(distributorId, orderId, status); // Handle pagination parameters const page = parseInt(req.query.page, 10) || 1; const limit = parseInt(req.query.limit, 10) || 5; const skip = (page - 1) * limit; // console.log(distributorId, orderId, status, page, limit, skip); try { let orders; // Search by orderId if (orderId) { const id = new RegExp(orderId, "i"); orders = await RdOrder.find({ addedBy: distributorId, uniqueId: { $regex: id }, }) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); // console.log(orders); // Return empty array if no orders are found if (!orders || orders.length === 0) { return res.status(200).json([]); } // Get total count for pagination const totalOrders = await RdOrder.countDocuments({ addedBy: distributorId, uniqueId: { $regex: id }, }); // Send response with pagination info return res.status(200).json({ totalOrders, totalPages: Math.ceil(totalOrders / limit), currentPage: page, orders, }); } // Search by status else if (status) { // Create a regex for case-insensitive search const regex = new RegExp(status, "i"); orders = await RdOrder.find({ addedBy: distributorId, status: { $regex: regex }, }) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); // console.log(orders); // Return empty array if no orders are found if (!orders || orders.length === 0) { return res.status(200).json([]); } // Get total count for pagination const totalOrders = await RdOrder.countDocuments({ addedBy: distributorId, status: { $regex: regex }, }); // console.log(totalOrders); // Send response with pagination info return res.status(200).json({ totalOrders, totalPages: Math.ceil(totalOrders / limit), currentPage: page, orders, }); } // Default behavior to return all orders else { orders = await RdOrder.find({ addedBy: distributorId }) .sort({ createdAt: -1 }) .skip(skip) .limit(limit); // Return empty array if no orders are found if (!orders || orders.length === 0) { return res.status(200).json([]); } // Get total count for pagination const totalOrders = await RdOrder.countDocuments({ addedBy: distributorId, }); // Send response with pagination info return res.status(200).json({ totalOrders, totalPages: Math.ceil(totalOrders / limit), currentPage: page, orders, }); } } catch (error) { console.error("Error fetching orders:", error); res.status(500).json({ message: "Server error", error }); } }; export const gettotalorderandvalueofrd = async (req, res) => { const { distributorId } = req.params; try { const orders = await RdOrder.find({ addedBy: distributorId }).sort({ createdAt: -1, }); const totalOrders = orders.length; const totalValue = orders .reduce((acc, order) => acc + order.grandTotal, 0) .toFixed(2); // Get the date of the last order const lastPurchaseOrderDate = totalOrders > 0 ? orders[0].createdAt : null; res.status(200).json({ totalOrders, totalValue: parseFloat(totalValue), lastPurchaseOrderDate, }); } catch (error) { console.error("Error fetching orders:", error); res.status(500).json({ message: "Server error", error }); } }; export const getOrderCounts = async (req, res) => { try { // console.log(req.user._id,""); const userId = req.user._id; const statusCounts = await RdOrder.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!" }); } };