visit rd and pd stored in database and pdorder fixed the create order and processing order

This commit is contained in:
Sibunnayak 2024-09-19 12:54:04 +05:30
parent e1906cadbb
commit e53a503ffa
6 changed files with 285 additions and 67 deletions

View 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);

View File

@ -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' });
}
};

View File

@ -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);

View File

@ -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(

View File

@ -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" });
}
};

View File

@ -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 }
);