From e53a503ffa0f2886596a29a6aef9ec7268df006f Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Thu, 19 Sep 2024 12:54:04 +0530 Subject: [PATCH] visit rd and pd stored in database and pdorder fixed the create order and processing order --- resources/PD_Orders/invoiceModel.js | 73 ++++++++++++ resources/PD_Orders/pdOrderController.js | 113 +++++++++++++++++-- resources/PD_Orders/pdOrderModal.js | 58 ++++------ resources/PD_Orders/pdOrderRoute.js | 5 + resources/VisitRD&PD/VisitRD&PDController.js | 74 +++++++++--- resources/VisitRD&PD/VisitRD&PDModel.js | 29 ++++- 6 files changed, 285 insertions(+), 67 deletions(-) create mode 100644 resources/PD_Orders/invoiceModel.js diff --git a/resources/PD_Orders/invoiceModel.js b/resources/PD_Orders/invoiceModel.js new file mode 100644 index 0000000..441d4ef --- /dev/null +++ b/resources/PD_Orders/invoiceModel.js @@ -0,0 +1,73 @@ +import mongoose, { Schema } from "mongoose"; + +const orderItemSchema = new Schema({ + productId: { + type: Schema.Types.ObjectId, + ref: "Product", + required: true, + }, + SKU: { + type: String, + required: true, + }, + name: { + type: String, + required: true, + }, + categoryName: { + type: String, // Directly store category name + required: true, + }, + brandName: { + type: String, // Directly store brand name + required: true, + }, + price: { + type: Number, + required: true, + }, + GST: { + type: Number, + required: true, + }, + HSN_Code: { + type: Number, + required: true, + }, + quantity: { + type: Number, + required: true, + default: 1, + }, +}); +const invoiceSchema = new mongoose.Schema({ + invoiceId: { type: String, required: true, unique: true }, + orderId: { + type: mongoose.Schema.Types.ObjectId, + ref: "PdOrder", + required: true, + }, + items: [orderItemSchema], + subtotal: { + type: Number, + required: true, + }, + gstTotal: { + type: Number, + required: true, + }, + invoiceAmount: { type: Number, required: true }, + courier_name: { type: String }, + courier_tracking_id: { type: String }, + courierStatus: { + type: String, + enum: ["processing", "dispatched", "delivered"], + default: "processing", + }, + courierstatus_timeline: { + processing: { type: Date }, + dispatched: { type: Date }, + delivered: { type: Date }, + }, +}); +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 335d0d7..89699fa 100644 --- a/resources/PD_Orders/pdOrderController.js +++ b/resources/PD_Orders/pdOrderController.js @@ -1,11 +1,12 @@ -// Adjust the path to where your PdOrder model is located - import mongoose from "mongoose"; import { PdOrder } from "./pdOrderModal.js"; import sendEmail from "../../Utils/sendEmail.js"; +import { Invoice } from "./invoiceModel.js"; +// Controller for placing an order export const createOrder = async (req, res) => { try { + // Destructure the request body const { paymentMode, shipTo, @@ -16,6 +17,7 @@ export const createOrder = async (req, res) => { grandTotal, } = req.body; + // Check for required fields if ( !paymentMode || !shipTo || @@ -25,11 +27,10 @@ export const createOrder = async (req, res) => { !gstTotal || !grandTotal ) { - return res.status(400).json({ error: "All fields are required." }); + return res.status(400).json({ error: "Missing required fields" }); } - - // Create the order const addedBy = req.user._id; + // Create a new PdOrder instance const newOrder = new PdOrder({ paymentMode, shipTo, @@ -48,6 +49,7 @@ export const createOrder = async (req, res) => { description: item.description, image: item.image, quantity: item.count, + remainingQuantity: item.count, })), subtotal, gstTotal, @@ -55,14 +57,103 @@ export const createOrder = async (req, res) => { addedBy, }); - // Save the order to the database + // Save the new order const savedOrder = await newOrder.save(); - - // Return the created order as a response - return res.status(201).json({ placedOrder: savedOrder }); + // console.log(savedOrder); + return res.status(201).json({ + message: "Order placed successfully", + placedOrder: savedOrder, + }); } catch (error) { - console.error("Error creating order:", error); - return res.status(500).json({ error: "Internal Server Error" }); + console.error(error); + return res.status(500).json({ error: "Internal server error" }); + } +}; +// Controller for processing an order and generating invoice +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 PdOrder.findById(orderId); + + if (!order) { + return res.status(404).json({ error: 'Order not found' }); + } + + // Validate quantities + for (const item of invoiceItems) { + const orderItem = order.orderItem.find(i => i.productId.toString() === item.productId.toString()); + if (orderItem && item.quantity > orderItem.remainingQuantity) { + return res.status(400).json({ error: `Product '${item.name}' has more quantity than remaining in the order.` }); + } + } + + // Generate unique invoice number + const existingInvoices = await Invoice.find({ orderId }); + const invoiceNumber = existingInvoices.length + 1; + const invoiceId = `ch/${order.uniqueId}/${invoiceItems.length}/${invoiceNumber}`; + + // Calculate subtotal, gstTotal, and invoiceAmount + let subtotal = 0; + let gstTotal = 0; + let invoiceAmount = 0; + + invoiceItems.forEach(item => { + const itemSubtotal = item.price * item.quantity; + const itemGST = (item.price * item.GST) / 100 * item.quantity; + + subtotal += itemSubtotal; + gstTotal += itemGST; + invoiceAmount += itemSubtotal + itemGST; + }); + + // Create a new invoice + const newInvoice = new Invoice({ + invoiceId, + orderId, + items: invoiceItems, + subtotal, + gstTotal, + invoiceAmount, + }); + + // Save the invoice + const savedInvoice = await newInvoice.save(); + + // Update the order's order items with the remaining quantity + let allItemsProcessed = true; + order.orderItem.forEach(item => { + const invoicedItem = invoiceItems.find(i => i.productId.toString() === item.productId.toString()); + if (invoicedItem) { + item.remainingQuantity -= invoicedItem.quantity; + if (item.remainingQuantity < 0) { + item.remainingQuantity = 0; // Ensure it does not go negative + } + if (item.remainingQuantity > 0) { + allItemsProcessed = false; + } + } + }); + + // Update the order status + order.status = allItemsProcessed ? 'processing' : 'pending'; + + // Save the updated order + await order.save(); + + return res.status(201).json({ + message: 'Invoice created and order processed successfully', + invoice: savedInvoice, + order: order, + }); + } catch (error) { + console.error(error); + return res.status(500).json({ error: 'Internal server error' }); } }; diff --git a/resources/PD_Orders/pdOrderModal.js b/resources/PD_Orders/pdOrderModal.js index 62212aa..d1ae373 100644 --- a/resources/PD_Orders/pdOrderModal.js +++ b/resources/PD_Orders/pdOrderModal.js @@ -49,20 +49,13 @@ const orderItemSchema = new Schema({ required: true, default: 1, }, -}); - -const StatusHistorySchema = new mongoose.Schema({ - status: { - type: String, - enum: ["new", "dispatched", "cancelled", "processing", "delivered"], // Ensure this matches your status enum + remainingQuantity: { + type: Number, required: true, }, - timestamp: { - type: Date, - default: Date.now, - }, }); + const pdOrderSchema = new Schema( { paymentMode: { @@ -91,15 +84,14 @@ const pdOrderSchema = new Schema( type: Number, required: true, }, - status: { - type: String, - enum: ["new", "dispatched", "cancelled", "processing", "delivered"], - default: "new", - }, - statusUpdatedAt: { - type: Date, - default: Date.now, + status: { + type: String, + enum: ['new','pending','processing','dispatched', 'cancelled', 'delivered'], + default: 'new' }, + invoices: [ + { type: mongoose.Schema.Types.ObjectId, ref: 'Invoice' } + ], uniqueId: { type: String, unique: true, @@ -110,15 +102,6 @@ const pdOrderSchema = new Schema( ref: "User", required: true, }, - status_timeline: { - new: { type: Date }, - paid: { type: Date }, - processing: { type: Date }, - dispatched: { type: Date }, - delivered: { type: Date }, - cancelled: { type: Date }, - returned: { type: Date }, - }, iscancelled: { type: Boolean, default: false, @@ -126,26 +109,23 @@ const pdOrderSchema = new Schema( order_Cancelled_Reason: { type: String, }, - courier_name: { type: String }, - courier_tracking_id: { type: String }, isDelivered: { type: Boolean, required: true, default: false }, DeliveredDate: { type: String, default: "" }, - statusHistory: [StatusHistorySchema], // Add this field to store the status history }, { timestamps: true } ); -// Middleware to update the statusUpdatedAt field whenever status changes -pdOrderSchema.pre("save", function (next) { - if (this.isModified("status")) { - this.statusUpdatedAt = Date.now(); - // Add the new status and timestamp to statusHistory - this.statusHistory.push({ - status: this.status, - timestamp: this.statusUpdatedAt, - }); +// Middleware to generate uniqueId before saving the document +pdOrderSchema.pre('save', async function(next) { + if (this.isNew) { + const year = new Date().getFullYear().toString().slice(-2); // Get the last 2 digits of the year + const orderItemCount = this.orderItem.length; // Count the number of order items + const unique6CharId = nanoid(6); // Generate a 6-character unique ID + + this.uniqueId = `${year}-${orderItemCount}-${unique6CharId}`; } next(); }); export const PdOrder = mongoose.model("PdOrder", pdOrderSchema); + diff --git a/resources/PD_Orders/pdOrderRoute.js b/resources/PD_Orders/pdOrderRoute.js index 513383b..df87553 100644 --- a/resources/PD_Orders/pdOrderRoute.js +++ b/resources/PD_Orders/pdOrderRoute.js @@ -11,6 +11,7 @@ import { getPlacedOrderById, getProcessingOrdersAdmin, updateOrderStatusById, + processOrder, } from "./pdOrderController.js"; const router = express.Router(); @@ -22,6 +23,10 @@ router authorizeRoles("principal-Distributor"), createOrder ); +// Define the route for processing an order +router + .route("/processing-order") + .post(isAuthenticatedUser, authorizeRoles("admin"), processOrder); router .route("/get-placed-order-pd") .get( diff --git a/resources/VisitRD&PD/VisitRD&PDController.js b/resources/VisitRD&PD/VisitRD&PDController.js index db6a046..0f417a8 100644 --- a/resources/VisitRD&PD/VisitRD&PDController.js +++ b/resources/VisitRD&PD/VisitRD&PDController.js @@ -1,20 +1,55 @@ -import VisitRDandPD from './VisitRD&PDModel.js'; +import VisitRDandPD from "./VisitRD&PDModel.js"; const parseDate = (dateStr) => { const [day, month, year] = dateStr.split("/").map(Number); - // Create a UTC date object to ensure the correct date is stored return new Date(Date.UTC(year, month - 1, day)); }; + // Controller for creating a visit record export const createVisit = async (req, res) => { try { const visitBy = req.user._id; const visitUserType = req.userType; - const { addedFor, addedForId, tradename, visitDate, note } = req.body; - if (!addedFor || !addedForId || !tradename || !visitDate) { - return res.status(400).json({ message: 'All fields are required' }); + const { + addedFor, + addedForId, + tradename, + visitDate, + visitTime, + meetingsummary, + followup, + visitpurpose, + nextvisitdate, + } = req.body; + if ( + !addedFor || + !addedForId || + !tradename || + !visitDate || + !meetingsummary + ) { + return res.status(400).json({ message: "All fields are required" }); } + const parsedDate = parseDate(visitDate); + const nextvisit = parseDate(nextvisitdate); + let documents = []; + if (req.files && req.files.documents) { + const documentFiles = Array.isArray(req.files.documents) + ? req.files.documents + : [req.files.documents]; + for (const file of documentFiles) { + const result = await cloudinary.v2.uploader.upload(file.tempFilePath, { + folder: "chemiNova/visitdistributors", + }); + + documents.push({ + public_id: result.public_id, + url: result.secure_url, + }); + } + } + // Create a new visit record const newVisit = new VisitRDandPD({ visitBy, @@ -23,16 +58,23 @@ export const createVisit = async (req, res) => { addedForId, tradename, visitDate: parsedDate, - note, + visitTime, + visitpurpose, + meetingsummary, + followup, + nextvisitdate: nextvisit, + documents, }); // Save the visit record to the database const savedVisit = await newVisit.save(); - return res.status(201).json({ message: 'Visit done successfully', visit: savedVisit }); + return res + .status(201) + .json({ message: "Visit created successfully", visit: savedVisit }); } catch (error) { console.error(error); - return res.status(500).json({ message: 'Server error' }); + return res.status(500).json({ message: "Server error" }); } }; @@ -42,23 +84,27 @@ export const getVisitsByDistributor = async (req, res) => { const { distributorId, distributorType } = req.params; // Validate distributor type - if (!['RetailDistributor', 'PrincipalDistributor'].includes(distributorType)) { - return res.status(400).json({ message: 'Invalid distributor type' }); + if ( + !["RetailDistributor", "PrincipalDistributor"].includes(distributorType) + ) { + return res.status(400).json({ message: "Invalid distributor type" }); } // Find all visits for the specified distributor const visits = await VisitRDandPD.find({ addedFor: distributorType, addedForId: distributorId, - }).populate('visitBy', 'name email'); // Populating visitBy with user details + }).populate("visitBy", "name email"); // Populating visitBy with user details if (!visits.length) { - return res.status(404).json({ message: 'No visits found for this distributor' }); + return res + .status(404) + .json({ message: "No visits found for this distributor" }); } return res.status(200).json({ visits }); } catch (error) { console.error(error); - return res.status(500).json({ message: 'Server error' }); + return res.status(500).json({ message: "Server error" }); } -}; \ No newline at end of file +}; diff --git a/resources/VisitRD&PD/VisitRD&PDModel.js b/resources/VisitRD&PD/VisitRD&PDModel.js index c57fa22..e0f38a8 100644 --- a/resources/VisitRD&PD/VisitRD&PDModel.js +++ b/resources/VisitRD&PD/VisitRD&PDModel.js @@ -6,7 +6,7 @@ const VisitSchema = new mongoose.Schema( { visitBy: { type: mongoose.Schema.Types.ObjectId, - refPath: "userType", + refPath: "visitUserType", // Corrected refPath required: true, }, visitUserType: { @@ -21,7 +21,7 @@ const VisitSchema = new mongoose.Schema( }, addedForId: { type: mongoose.Schema.Types.ObjectId, - refPath: "addedFor", + refPath: "addedFor", // Corrected refPath required: true, }, tradename: { @@ -32,9 +32,32 @@ const VisitSchema = new mongoose.Schema( type: Date, required: true, }, - note: { + visitTime: { + type: String, + required: true, + }, + visitpurpose: { + type: String, + required: true, + }, + meetingsummary: { + type: String, + required: true, + }, + followup: { type: String, }, + nextvisitdate: { + type: Date, + }, + documents: [{ + public_id: { + type: String, + }, + url: { + type: String, + }, + }], }, { timestamps: true } );