invoice related apis are completed

This commit is contained in:
Sibunnayak 2024-09-21 21:07:18 +05:30
parent 0f2330db13
commit f2e99790cc
3 changed files with 485 additions and 67 deletions

View File

@ -70,4 +70,11 @@ const invoiceSchema = new mongoose.Schema({
delivered: { 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 Invoice = mongoose.model("Invoice", invoiceSchema); export const Invoice = mongoose.model("Invoice", invoiceSchema);

View File

@ -93,9 +93,7 @@ export const processOrder = async (req, res) => {
(i) => i.productId.toString() === item.productId.toString() (i) => i.productId.toString() === item.productId.toString()
); );
if (orderItem && item.processquantity > orderItem.remainingQuantity) { if (orderItem && item.processquantity > orderItem.remainingQuantity) {
return res return res.status(400).json({
.status(400)
.json({
error: `Product '${item.name}' has more quantity than remaining in the order.`, error: `Product '${item.name}' has more quantity than remaining in the order.`,
}); });
} }
@ -329,9 +327,7 @@ export const processOrder = async (req, res) => {
`, `,
}); });
res res.status(200).json({
.status(200)
.json({
message: "Order processed and invoice created successfully", message: "Order processed and invoice created successfully",
invoiceId: savedInvoice.invoiceId, invoiceId: savedInvoice.invoiceId,
}); });
@ -344,7 +340,7 @@ export const processOrder = async (req, res) => {
}; };
export const cancelOrderController = async (req, res) => { export const cancelOrderController = async (req, res) => {
// const { cancellationReason, id: _id } = req.bodyconst // const { cancellationReason, id: _id } = req.bodyconst
const cancellationReason= req.body.cancellationReason; const cancellationReason = req.body.cancellationReason;
const _id = req.params.id; const _id = req.params.id;
try { try {
// Find the order by ID // Find the order by ID
@ -420,7 +416,7 @@ export const cancelOrderController = async (req, res) => {
item.price item.price
}</td> }</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${ <td style="border: 1px solid #555; padding: 2px; text-align: center;">${
(item.price * item.GST)/100 (item.price * item.GST) / 100
}</td> }</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${ <td style="border: 1px solid #555; padding: 2px; text-align: center;">${
itemSubtotal + itemGST itemSubtotal + itemGST
@ -471,7 +467,7 @@ export const cancelOrderController = async (req, res) => {
item.price item.price
}</td> }</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${ <td style="border: 1px solid #555; padding: 2px; text-align: center;">${
(item.price * item.GST)/100 (item.price * item.GST) / 100
}</td> }</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${ <td style="border: 1px solid #555; padding: 2px; text-align: center;">${
itemSubtotal + itemGST itemSubtotal + itemGST
@ -778,7 +774,388 @@ export const getProcessingOrdersAdmin = async (req, res) => {
return res.status(500).json({ error: "Internal Server Error" }); return res.status(500).json({ error: "Internal Server Error" });
} }
}; };
export const getProcessingInvoices = async (req, res) => {
try {
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// 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: `
<strong style="color: #1b03a3; font-size: 16px">Hi,</strong>
<h3 style="color: #333; font-family: Arial, sans-serif;">Exciting news! Your order #${
order?.uniqueId
} has been dispatched and is en route to you. 🚚 Here are the details:</h3>
<h4 style="color: #333; font-family: Arial, sans-serif;">Courier Name: ${
invoice?.courier_name || "N/A"
}</h4>
<h4 style="color: #333; font-family: Arial, sans-serif;">Courier Tracking ID: ${
invoice?.courier_tracking_id || "N/A"
}</h4>
<h4 style="color: #333; font-family: Arial, sans-serif;">Items:</h4>
<table style="border-collapse: collapse; width: 100%;">
<thead>
<tr>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SKU</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
</tr>
</thead>
<tbody>
${invoice?.items
?.map(
(product, index) => `
<tr>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
index + 1
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.name
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.SKU
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
${
product.image && product.image.length > 0
? `<img src="${product.image[0]?.url}" alt="${product.name}" style="max-width: 40px; height: auto;">`
: "No Image"
}
</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.processquantity
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.price
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
((product.GST * product.price) / 100).toFixed(2)
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
((product.price + (product.GST * product.price) / 100) *
product.processquantity).toFixed(2)
}</td>
</tr>
`
)
.join("")}
<tr>
<th colspan="7" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
invoice.invoiceAmount
}</td>
</tr>
</tbody>
</table>
<h3 style="color: #333; font-family: Arial, sans-serif;">Order Status: Dispatched</h3>
<h4 style="color: #333; font-family: Arial, sans-serif;">If you have any questions or need assistance, feel free to reply to this email.</h4>
<h5 style="color: #333; font-family: Arial, sans-serif;">Thanks for choosing Cheminova! We hope you enjoy your purchase.</h5>
<br/>
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
`,
});
res.status(200).json({
message: "Courier status updated to dispatched",
orderStatus: order.status,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
};
export const getDispatchedInvoices = async (req, res) => {
try {
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// 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: `
<strong style="color: #1b03a3; font-size: 16px">Hi,</strong>
<h3 style="color: #333; font-family: Arial, sans-serif;">
Great news! Your order #${
order?.uniqueId
} has been successfully delivered to your doorstep. We hope everything is just as you expected!
</h3>
<h4 style="color: #333; font-family: Arial, sans-serif;">Items:</h4>
<table style="border-collapse: collapse; width: 100%;">
<thead>
<tr>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">S No.</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Product Name</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SKU</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Image</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Quantity</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">Price</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">GST Amount</th>
<th style="border: 1px solid #555; padding: 2px; text-align: center;">SubTotal</th>
</tr>
</thead>
<tbody>
${invoice?.items
?.map(
(product, index) => `
<tr>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
index + 1
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.name
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.SKU
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">
${
product.image && product.image.length > 0
? `<img src="${product.image[0]?.url}" alt="${product.name}" style="max-width: 40px; height: auto;">`
: "No Image"
}
</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.processquantity
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
product.price
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
((product.GST * product.price) / 100).toFixed(2)
}</td>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
((product.price + (product.GST * product.price) / 100) *
product.processquantity).toFixed(2)
}</td>
</tr>
`
)
.join("")}
<tr>
<th colspan="7" style="border: 1px solid #555; padding: 2px; text-align: right;">Total Amount:</th>
<td style="border: 1px solid #555; padding: 2px; text-align: center;">${
invoice.invoiceAmount.toFixed(2)
}</td>
</tr>
</tbody>
</table>
<h3 style="color: #333; font-family: Arial, sans-serif;">Delivery Date: ${formattedDate}</h3>
<h4 style="color: #333; font-family: Arial, sans-serif;">
Your satisfaction is our priority, and we'd love to hear about your experience. Please take a moment to share your thoughts by leaving a review. Your feedback is invaluable to us!
</h4>
<h5 style="color: #333; font-family: Arial, sans-serif;">
If you have any questions or concerns about your order, feel free to reply to this email.
</h5>
<h5 style="color: #333; font-family: Arial, sans-serif;">
Thank you for choosing Cheminova! We hope to serve you again soon.
</h5>
<br/>
<span style="color: #555; font-size: 13px;">Best regards,</span><br/>
<span style="color: #555; font-size: 13px;">Team Cheminova</span>
`,
});
res.status(200).json({
message: "Courier status updated to delivered",
orderStatus: order.status,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
};
export const getDeliveredInvoices = async (req, res) => {
try {
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 5;
// 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) => { export const getDeliveredOrdersAdmin = async (req, res) => {
try { try {
// Extract page and limit from query parameters // 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" }); 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) => { export const updateOrderStatusById = async (req, res) => {
try { try {
let body = { status: req.body.status }; let body = { status: req.body.status };
@ -1224,55 +1654,3 @@ export const updateOrderStatusById = async (req, res) => {
.json({ message: error?.message || "Something went wrong!" }); .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!" });
}
};

View File

@ -14,6 +14,12 @@ import {
processOrder, processOrder,
cancelOrderController, cancelOrderController,
getPlacedPendingOrderAdmin, getPlacedPendingOrderAdmin,
getProcessingInvoices,
updateCourierStatusToDispatched,
getInvoiceDetailsById,
getDeliveredInvoices,
getDispatchedInvoices,
updateCourierStatusToDelivered,
} from "./pdOrderController.js"; } from "./pdOrderController.js";
const router = express.Router(); const router = express.Router();
@ -65,9 +71,36 @@ router
router router
.route("/get-delivered-order-admin") .route("/get-delivered-order-admin")
.get(isAuthenticatedUser, authorizeRoles("admin"), getDeliveredOrdersAdmin); .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 router
.route("/change/status/:id") .route("/change/status/:id")
.patch(isAuthenticatedUser, authorizeRoles("admin"), updateOrderStatusById); .patch(isAuthenticatedUser, authorizeRoles("admin"), updateOrderStatusById);
router.route("/get-counts-pdOrders").get(isAuthenticatedUser, getOrderCounts); router.route("/get-counts-pdOrders").get(isAuthenticatedUser, getOrderCounts);
export default router; export default router;