visit rd and pd stored in database and pdorder fixed the create order and processing order
This commit is contained in:
parent
e1906cadbb
commit
e53a503ffa
73
resources/PD_Orders/invoiceModel.js
Normal file
73
resources/PD_Orders/invoiceModel.js
Normal file
@ -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);
|
@ -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' });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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: {
|
||||
@ -93,13 +86,12 @@ const pdOrderSchema = new Schema(
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: ["new", "dispatched", "cancelled", "processing", "delivered"],
|
||||
default: "new",
|
||||
},
|
||||
statusUpdatedAt: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
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);
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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" });
|
||||
}
|
||||
};
|
@ -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 }
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user