From f2e99790ccadf2acba257d5f2a09eb407b786474 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Sat, 21 Sep 2024 21:07:18 +0530 Subject: [PATCH] invoice related apis are completed --- resources/PD_Orders/invoiceModel.js | 7 + resources/PD_Orders/pdOrderController.js | 512 ++++++++++++++++++++--- resources/PD_Orders/pdOrderRoute.js | 33 ++ 3 files changed, 485 insertions(+), 67 deletions(-) diff --git a/resources/PD_Orders/invoiceModel.js b/resources/PD_Orders/invoiceModel.js index 77abefb..0c9b26e 100644 --- a/resources/PD_Orders/invoiceModel.js +++ b/resources/PD_Orders/invoiceModel.js @@ -70,4 +70,11 @@ const invoiceSchema = new mongoose.Schema({ 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 Invoice = mongoose.model("Invoice", invoiceSchema); \ No newline at end of file diff --git a/resources/PD_Orders/pdOrderController.js b/resources/PD_Orders/pdOrderController.js index bd9e918..f4960e9 100644 --- a/resources/PD_Orders/pdOrderController.js +++ b/resources/PD_Orders/pdOrderController.js @@ -93,11 +93,9 @@ export const processOrder = async (req, res) => { (i) => i.productId.toString() === item.productId.toString() ); if (orderItem && item.processquantity > orderItem.remainingQuantity) { - return res - .status(400) - .json({ - error: `Product '${item.name}' has more quantity than remaining in the order.`, - }); + return res.status(400).json({ + error: `Product '${item.name}' has more quantity than remaining in the order.`, + }); } } @@ -329,12 +327,10 @@ export const processOrder = async (req, res) => { `, }); - res - .status(200) - .json({ - message: "Order processed and invoice created successfully", - invoiceId: savedInvoice.invoiceId, - }); + res.status(200).json({ + message: "Order processed and invoice created successfully", + invoiceId: savedInvoice.invoiceId, + }); } catch (error) { console.error("Error processing order:", error); res @@ -344,7 +340,7 @@ export const processOrder = async (req, res) => { }; export const cancelOrderController = async (req, res) => { // const { cancellationReason, id: _id } = req.bodyconst - const cancellationReason= req.body.cancellationReason; + const cancellationReason = req.body.cancellationReason; const _id = req.params.id; try { // Find the order by ID @@ -420,7 +416,7 @@ export const cancelOrderController = async (req, res) => { item.price } ₹${ - (item.price * item.GST)/100 + (item.price * item.GST) / 100 } ₹${ itemSubtotal + itemGST @@ -471,7 +467,7 @@ export const cancelOrderController = async (req, res) => { item.price } ₹${ - (item.price * item.GST)/100 + (item.price * item.GST) / 100 } ₹${ itemSubtotal + itemGST @@ -778,7 +774,388 @@ export const getProcessingOrdersAdmin = async (req, res) => { return res.status(500).json({ error: "Internal Server Error" }); } }; +export const getProcessingInvoices = async (req, res) => { + try { + const page = parseInt(req.query.page, 10) || 1; + const limit = parseInt(req.query.limit, 10) || 5; + // Calculate the number of documents to skip + const skip = (page - 1) * limit; + + // Get the total count of 'processing' invoices + const totalInvoices = await Invoice.countDocuments({ + courierStatus: "processing", + }); + + // Fetch the invoices with pagination + const invoices = await Invoice.find({ courierStatus: "processing" }) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit); + // Respond with the invoices and the total count + res.status(200).json({ + totalCount: totalInvoices, + currentPage: page, + totalPages: Math.ceil(totalInvoices / limit), + invoices, + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; +export const getInvoiceDetailsById = async (req, res) => { + const { invoiceId } = req.params; + + try { + // Find the invoice by ID and populate the orderId and addedBy fields + const invoice = await Invoice.findById(invoiceId).populate({ + path: "orderId", + model: "PdOrder", + populate: { + path: "addedBy", + model: "User", + select: "name email phone", // Select only specific fields + }, + }); + + if (!invoice) { + return res.status(404).json({ error: "Invoice not found" }); + } + + res.status(200).json(invoice); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +export const updateCourierStatusToDispatched = async (req, res) => { + const { invoiceId } = req.params; + const { courierName, couriertrackingId } = req.body; + try { + // Find the invoice by ID + const invoice = await Invoice.findById(invoiceId).populate({ + path: "orderId", + populate: { + path: "addedBy", + select: "email", + }, + }); + + if (!invoice) { + return res.status(404).json({ error: "Invoice not found" }); + } + + invoice.courierStatus = "dispatched"; + invoice.courierstatus_timeline.dispatched = new Date(); + invoice.courier_name = courierName; + invoice.courier_tracking_id = couriertrackingId; + + // Save the updated invoice + await invoice.save(); + + const order = invoice.orderId; + const allItemsDispatched = order.orderItem.every( + (item) => item.remainingQuantity === 0 + ); + + if (allItemsDispatched) { + order.status = "dispatched"; + await order.save(); + } + + // Send email to the user + await sendEmail({ + to: `${order?.addedBy?.email}`, // Assuming 'addedBy' references the user who placed the order + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + subject: `Your Order #${order?.uniqueId} is On Its Way!`, + html: ` + 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 NameSKUImageQuantityPriceGST AmountSubTotal
${ + 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 getDispatchedInvoices = async (req, res) => { + try { + 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' invoices + const totalInvoices = await Invoice.countDocuments({ + courierStatus: "dispatched", + }); + + // Fetch the invoices with pagination + const invoices = await Invoice.find({ courierStatus: "dispatched" }) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit); + // Respond with the invoices and the total count + res.status(200).json({ + totalCount: totalInvoices, + currentPage: page, + totalPages: Math.ceil(totalInvoices / limit), + invoices, + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; +export const updateCourierStatusToDelivered = async (req, res) => { + const { invoiceId } = req.params; + + try { + // Find the invoice by ID + const invoice = await Invoice.findById(invoiceId).populate({ + path: "orderId", + populate: { + path: "addedBy", + select: "email", + }, + }); + + if (!invoice) { + return res.status(404).json({ error: "Invoice not found" }); + } + + // Update courier status and timeline + invoice.courierStatus = "delivered"; + invoice.courierstatus_timeline.delivered = new Date(); + + // Save the updated invoice + await invoice.save(); + + const order = invoice.orderId; + const allItemsDelivered = order.orderItem.every( + (item) => item.remainingQuantity === 0 + ); + + if (allItemsDelivered) { + order.status = "delivered"; + await order.save(); + } + + // Format the current date for display + const formattedDate = formatDate(new Date()); + + // Send email to the user + await sendEmail({ + to: `${order?.addedBy?.email}`, // Using 'addedBy' to reference the user's email + from: `${process.env.SEND_EMAIL_FROM}`, // Your verified sender + subject: `Your Order #${order?.uniqueId} Has Been Delivered!`, + html: ` + 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 NameSKUImageQuantityPriceGST AmountSubTotal
${ + 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 getDeliveredInvoices = async (req, res) => { + try { + 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' invoices + const totalInvoices = await Invoice.countDocuments({ + courierStatus: "delivered", + }); + + // Fetch the invoices with pagination + const invoices = await Invoice.find({ courierStatus: "delivered" }) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit); + // Respond with the invoices and the total count + res.status(200).json({ + totalCount: totalInvoices, + currentPage: page, + totalPages: Math.ceil(totalInvoices / limit), + invoices, + }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; export const getDeliveredOrdersAdmin = async (req, res) => { try { // Extract page and limit from query parameters @@ -809,7 +1186,60 @@ export const getDeliveredOrdersAdmin = async (req, res) => { return res.status(500).json({ error: "Internal Server Error" }); } }; +const formatDate = (date) => { + const options = { + weekday: "short", + year: "numeric", + month: "short", + day: "2-digit", + // hour: "2-digit", + // minute: "2-digit", + // hour12: true, + }; + return new Intl.DateTimeFormat("en-US", options).format(new Date(date)); +}; +export const getOrderCounts = async (req, res) => { + try { + // console.log(req.user._id,""); + const userId = req.user._id; + const statusCounts = await PdOrder.aggregate([ + { + $match: { + addedBy: userId, // Only match orders added by the current user + }, + }, + { + $group: { + _id: "$status", // Group by status + count: { $sum: 1 }, // Count the number of orders per status + }, + }, + ]); + + // Restructure the result to make it easier to consume + const result = { + new: 0, + dispatched: 0, + cancelled: 0, + processing: 0, + delivered: 0, + }; + + statusCounts.forEach((status) => { + result[status._id] = status.count; + }); + + res.status(200).json({ counts: result }); + } catch (error) { + console.log(error.message); + res + .status(500) + .json({ message: error?.message || "Something went wrong!" }); + } +}; + +// no need export const updateOrderStatusById = async (req, res) => { try { let body = { status: req.body.status }; @@ -1223,56 +1653,4 @@ export const updateOrderStatusById = async (req, res) => { .status(500) .json({ message: error?.message || "Something went wrong!" }); } -}; -const formatDate = (date) => { - const options = { - weekday: "short", - year: "numeric", - month: "short", - day: "2-digit", - // hour: "2-digit", - // minute: "2-digit", - // hour12: true, - }; - return new Intl.DateTimeFormat("en-US", options).format(new Date(date)); -}; - -export const getOrderCounts = async (req, res) => { - try { - // console.log(req.user._id,""); - const userId = req.user._id; - const statusCounts = await PdOrder.aggregate([ - { - $match: { - addedBy: userId, // Only match orders added by the current user - }, - }, - { - $group: { - _id: "$status", // Group by status - count: { $sum: 1 }, // Count the number of orders per status - }, - }, - ]); - - // Restructure the result to make it easier to consume - const result = { - new: 0, - dispatched: 0, - cancelled: 0, - processing: 0, - delivered: 0, - }; - - statusCounts.forEach((status) => { - result[status._id] = status.count; - }); - - res.status(200).json({ counts: result }); - } catch (error) { - console.log(error.message); - res - .status(500) - .json({ message: error?.message || "Something went wrong!" }); - } -}; +}; \ No newline at end of file diff --git a/resources/PD_Orders/pdOrderRoute.js b/resources/PD_Orders/pdOrderRoute.js index 5784dc6..33d1be8 100644 --- a/resources/PD_Orders/pdOrderRoute.js +++ b/resources/PD_Orders/pdOrderRoute.js @@ -14,6 +14,12 @@ import { processOrder, cancelOrderController, getPlacedPendingOrderAdmin, + getProcessingInvoices, + updateCourierStatusToDispatched, + getInvoiceDetailsById, + getDeliveredInvoices, + getDispatchedInvoices, + updateCourierStatusToDelivered, } from "./pdOrderController.js"; const router = express.Router(); @@ -65,9 +71,36 @@ router router .route("/get-delivered-order-admin") .get(isAuthenticatedUser, authorizeRoles("admin"), getDeliveredOrdersAdmin); +router + .route("/get-processing-invoice-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getProcessingInvoices); +router + .route("/get-dispatched-invoice-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getDispatchedInvoices); +router + .route("/get-delivered-invoice-admin") + .get(isAuthenticatedUser, authorizeRoles("admin"), getDeliveredInvoices); +router + .route("/invoice/details/:invoiceId") + .get(isAuthenticatedUser, authorizeRoles("admin"), getInvoiceDetailsById); +router + .route("/invoice/dispatched/:invoiceId") + .put( + isAuthenticatedUser, + authorizeRoles("admin"), + updateCourierStatusToDispatched + ); +router + .route("/invoice/delivered/:invoiceId") + .put( + isAuthenticatedUser, + authorizeRoles("admin"), + updateCourierStatusToDelivered + ); router .route("/change/status/:id") .patch(isAuthenticatedUser, authorizeRoles("admin"), updateOrderStatusById); + router.route("/get-counts-pdOrders").get(isAuthenticatedUser, getOrderCounts); export default router;