conflict resolved

This commit is contained in:
ROSHAN GARG 2024-08-30 16:31:30 +05:30
commit afbc17cee7
78 changed files with 146302 additions and 516 deletions

6
app.js
View File

@ -132,6 +132,7 @@ app.use(
//auth //auth
import user from "./resources/user/userRoute.js"; import user from "./resources/user/userRoute.js";
import ProductRouter from "./resources/Products/ProductRoute.js"; import ProductRouter from "./resources/Products/ProductRoute.js";
import ProductManualRouter from "./resources/ProductMannual/ProductManualRoute.js";
//Businesses //Businesses
// import BusinessRoute from "./resources/Businesses/BusinessRoute.js"; // import BusinessRoute from "./resources/Businesses/BusinessRoute.js";
@ -163,7 +164,6 @@ import CurrencyRoute from "./resources/Currency/CurrencyRoute.js";
import ConfigRouter from "./resources/setting/Configration/Config_routes.js"; import ConfigRouter from "./resources/setting/Configration/Config_routes.js";
import TaxRouter from "./resources/Tax/tax_routes.js";
//specialties //specialties
import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js"; import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js";
import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js"; import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js";
@ -200,6 +200,8 @@ app.use("/api/v1", user);
//Product //Product
app.use("/api", ProductRouter); app.use("/api", ProductRouter);
//Product Manual
app.use("/api/productmanual", ProductManualRouter);
//businesses //businesses
// app.use("/api/businesses", BusinessRoute); // app.use("/api/businesses", BusinessRoute);
// Design // Design
@ -239,8 +241,6 @@ app.use("/api/language", LanguageRoute);
//Purpose //Purpose
app.use("/api/purpose", PurposeRoute); app.use("/api/purpose", PurposeRoute);
app.use("/api/business", orderRoute); app.use("/api/business", orderRoute);
//Tax
app.use("/api/tax", TaxRouter);
//Currency Route //Currency Route
app.use("/api/currency", CurrencyRoute); app.use("/api/currency", CurrencyRoute);
//config //config

View File

@ -0,0 +1,56 @@
import jwt from "jsonwebtoken";
import SalesCoOrdinator from "../resources/SalesCoOrdinators/SalesCoOrdinatorModel.js";
import TerritoryManager from "../resources/TerritoryManagers/TerritoryManagerModel.js";
export const isAuthenticated_SC_TM = async (req, res, next) => {
try {
if (!req.headers.authorization) {
return res.status(401).json({
success: false,
message: "Please login to access this resource",
});
}
const getToken = req.headers.authorization;
const token = getToken.slice(7);
const decoded = jwt.verify(token, process.env.JWT_SECRET);
if (!decoded) {
return res.status(400).json({
success: false,
message: "Incorrect token",
});
}
let user = await SalesCoOrdinator.findById(decoded.id);
if (user) {
req.user = user;
req.userType = "SalesCoOrdinator";
} else {
user = await TerritoryManager.findById(decoded.id);
if (user) {
req.user = user;
req.userType = "TerritoryManager";
}
}
if (!user) {
return res.status(401).json({
success: false,
message: "Unauthorized",
});
}
return next();
} catch (error) {
if (error.name === "TokenExpiredError") {
return res.status(401).json({ message: "Token has expired." });
} else if (error.name === "JsonWebTokenError") {
return res.status(401).json({ message: "Invalid token." });
} else {
return res.status(500).json({
message: "An internal error occurred while verifying the token.",
});
}
}
};

22
package-lock.json generated
View File

@ -28,6 +28,7 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"multer-storage-cloudinary": "^4.0.0", "multer-storage-cloudinary": "^4.0.0",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"node-cron": "^3.0.3",
"nodemailer": "^6.9.4", "nodemailer": "^6.9.4",
"nodemon": "^3.0.1", "nodemon": "^3.0.1",
"pm2": "^5.3.1", "pm2": "^5.3.1",
@ -4825,6 +4826,27 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/node-cron": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz",
"integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==",
"license": "ISC",
"dependencies": {
"uuid": "8.3.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/node-cron/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",

View File

@ -33,6 +33,7 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"multer-storage-cloudinary": "^4.0.0", "multer-storage-cloudinary": "^4.0.0",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"node-cron": "^3.0.3",
"nodemailer": "^6.9.4", "nodemailer": "^6.9.4",
"nodemon": "^3.0.1", "nodemon": "^3.0.1",
"pm2": "^5.3.1", "pm2": "^5.3.1",

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/uploads/Add-PD.xlsx Normal file

Binary file not shown.

View File

@ -0,0 +1,232 @@
import ProductManual from "./ProductManualModel.js";
import cloudinary from "../../Utils/cloudinary.js";
import path from "path";
// Create a new product manual
export const createProductManual = async (req, res) => {
const { title } = req.body;
const pdfFile = req.files ? req.files.pdfFile : null;
// Check if title is provided
if (!title) {
return res.status(400).json({
success: false,
message: "Title is required",
});
}
// Check if a file is provided
if (!pdfFile) {
return res.status(400).json({
success: false,
message: "File is required",
});
}
try {
let productManualDetails = null;
let filename = "";
// Upload the file to Cloudinary
if (pdfFile) {
filename = pdfFile.name;
// console.log(pdfFile);
const originalFilename = path.basename(pdfFile.name, path.extname(pdfFile.name));
const result = await cloudinary.v2.uploader.upload(pdfFile.tempFilePath, {
folder: "chemiNova/ProductManuals",
public_id: originalFilename,
});
// console.log(result);
productManualDetails = {
public_id: result.public_id,
url: result.secure_url,
filename: filename,
};
}
// Create the product manual
const productManual = await ProductManual.create({
title,
product_manual: productManualDetails || {}, // Ensure product_manual is an empty object if no file is provided
});
res.status(201).json({
success: true,
productManual,
message: "Product manual created successfully",
});
} catch (error) {
console.error("Error creating product manual:", error);
res.status(500).json({
success: false,
message: error.message || "Internal server error",
});
}
};
// Get all product manuals
export const getAllProductManuals = async (req, res) => {
try {
const PAGE_SIZE = parseInt(req.query.show) || 10;
const page = parseInt(req.query.page) || 1;
const skip = (page - 1) * PAGE_SIZE;
let filter = {};
if (req.query.title) {
filter.title = {
$regex: new RegExp(req.query.title, "i"),
};
}
// Fetch total count of documents
const total = await ProductManual.countDocuments(filter);
// Fetch paginated data
const productManuals = await ProductManual.find(filter)
.limit(PAGE_SIZE)
.skip(skip)
.sort({ createdAt: -1 })
.exec();
// Send response
res.status(200).json({
success: true,
productManuals,
total,
});
} catch (error) {
console.error("Error fetching product manuals:", error);
res.status(500).json({
success: false,
message: error.message || "Internal server error",
});
}
};
// Get a single product manual by ID
export const getSingleProductManual = async (req, res) => {
const { id } = req.params;
try {
const productManual = await ProductManual.findById(id);
if (!productManual) {
return res.status(404).json({
success: false,
message: "Product manual not found",
});
}
res.status(200).json({
success: true,
productManual,
});
} catch (error) {
console.error("Error fetching product manual:", error);
res.status(500).json({
success: false,
message: error.message || "Internal server error",
});
}
};
// Update a product manual
export const updateProductManual = async (req, res) => {
const { id } = req.params;
const { title } = req.body;
try {
const productManual = await ProductManual.findById(id);
if (!productManual) {
return res.status(404).json({
success: false,
message: "Product manual not found",
});
}
let filename = "";
// Check if a new file is provided
if (req.files && req.files.pdfFile) {
// Delete the old file from Cloudinary
if (productManual.product_manual.public_id) {
await cloudinary.v2.uploader.destroy(
productManual.product_manual.public_id,
{
folder: "chemiNova/ProductManuals",
}
);
}
// Upload the new file to Cloudinary
const pdfFile = req.files.pdfFile;
// console.log(pdfFile);
filename = pdfFile.name;
const originalFilename = path.basename(pdfFile.name, path.extname(pdfFile.name));
const result = await cloudinary.v2.uploader.upload(pdfFile.tempFilePath, {
folder: "chemiNova/ProductManuals",
public_id: originalFilename,
});
// console.log(result);
// Update the product manual details
productManual.product_manual = {
public_id: result.public_id,
url: result.secure_url,
filename: filename,
};
}
// Update the title
productManual.title = title || productManual.title;
// console.log(productManual);
await productManual.save();
res.status(200).json({
success: true,
productManual,
message: "Product manual updated successfully",
});
} catch (error) {
console.error("Error updating product manual:", error);
res.status(500).json({
success: false,
message: error.message || "Internal server error",
});
}
};
// Delete a product manual
export const deleteProductManual = async (req, res) => {
const { id } = req.params;
try {
const productManual = await ProductManual.findById(id);
if (!productManual) {
return res.status(404).json({
success: false,
message: "Product manual not found",
});
}
// Delete the file from Cloudinary
if (productManual.product_manual.public_id) {
await cloudinary.v2.uploader.destroy(
productManual.product_manual.public_id,
{
folder: "chemiNova/ProductManuals",
});
}
// Delete the product manual from the database
await ProductManual.findByIdAndDelete(id);
res.status(200).json({
success: true,
message: "Product manual deleted successfully",
});
} catch (error) {
console.error("Error deleting product manual:", error);
res.status(500).json({
success: false,
message: error.message || "Internal server error",
});
}
};

View File

@ -0,0 +1,26 @@
import mongoose from 'mongoose';
const ProductManualSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
product_manual: {
public_id: {
type: String,
},
url: {
type: String,
},
filename: {
type: String,
},
},
},
{ timestamps: true }
);
const ProductManual = mongoose.model('ProductManual', ProductManualSchema);
export default ProductManual;

View File

@ -0,0 +1,39 @@
import express from "express";
import {
createProductManual,
getAllProductManuals,
getSingleProductManual,
updateProductManual,
deleteProductManual,
} from "./ProductManualController.js";
import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js";
import { isAuthenticated_SC_TM } from "../../middlewares/generalAuth.js";
const router = express.Router();
// Route for creating a product manual (Only Admin can create)
router
.route("/create")
.post(isAuthenticatedUser, authorizeRoles("admin"), createProductManual);
router
.route("/")
.get(isAuthenticatedUser, authorizeRoles("admin"), getAllProductManuals);
router.route("/getall").get(isAuthenticatedUser, getAllProductManuals);
router
.route("/:id")
.get(isAuthenticatedUser, authorizeRoles("admin"), getSingleProductManual);
router.route("/getone/:id").get(isAuthenticatedUser, getSingleProductManual);
// Route to update a product manual by ID
router
.route("/update/:id")
.put(isAuthenticatedUser, authorizeRoles("admin"), updateProductManual);
// Route to delete a product manual by ID
router
.route("/delete/:id")
.delete(isAuthenticatedUser, authorizeRoles("admin"), deleteProductManual);
export default router;
// /api/productmanual/update

View File

@ -3,275 +3,12 @@ import cloudinary from "../../Utils/cloudinary.js";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { CategoryModel } from "../Category/CategoryModel.js"; import { CategoryModel } from "../Category/CategoryModel.js";
import { BrandModel } from "../Brands/BrandsModel.js"; import { BrandModel } from "../Brands/BrandsModel.js";
import User from "../user/userModel.js";
import { Tax } from "../Tax/tax_model.js";
import XLSX from "xlsx"; import XLSX from "xlsx";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import mongoose from "mongoose"; import mongoose from "mongoose";
// Function to handle product upload // Function to handle product upload
// export const uploadProducts = async (req, res) => {
// try {
// if (!req.files || !req.files.file) {
// return res.status(400).json({ message: "No file uploaded" });
// }
// const file = req.files.file;
// const filePath = path.join("public", "uploads", file.name);
// // Ensure 'uploads' directory exists
// if (!fs.existsSync(path.dirname(filePath))) {
// fs.mkdirSync(path.dirname(filePath), { recursive: true });
// }
// // Move the file from temp to the uploads directory
// await file.mv(filePath);
// // Process the file
// const fileBuffer = fs.readFileSync(filePath);
// const workbook = XLSX.read(fileBuffer, { type: "buffer" });
// const sheetName = workbook.SheetNames[0];
// const worksheet = workbook.Sheets[sheetName];
// const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
// if (data.length <= 1) {
// return res.status(400).json({ message: "Empty spreadsheet or no data found" });
// }
// const headers = data[0];
// // Map headers from the Excel file to your schema
// const headerMapping = {
// SKU: "SKU",
// "Product Name": "name",
// "Category Name": "category",
// "Brand Name": "brand",
// Price: "price",
// "GST (in %)": "GST",
// "HSN Code": "HSN_Code",
// "Description (Optional)": "description",
// };
// const requiredHeaders = Object.keys(headerMapping);
// if (!requiredHeaders.every((header) => headers.includes(header))) {
// return res.status(400).json({ message: "Missing required columns in spreadsheet" });
// }
// const errors = [];
// const newlyCreated = [];
// const updatedProducts = [];
// for (let i = 1; i < data.length; i++) {
// const row = data[i];
// const item = {};
// headers.forEach((header, index) => {
// if (headerMapping[header]) {
// item[headerMapping[header]] = row[index] !== undefined ? row[index] : "";
// }
// });
// // Initialize error tracking for each item
// const missingFields = new Set();
// const notFoundErrors = new Set();
// let { SKU, name, category, brand, price, GST, HSN_Code, description } = item;
// // Trim leading and trailing spaces from product name and GST
// name = name ? name.trim() : "";
// // Validate required fields
// if (!SKU) missingFields.add("SKU");
// if (!name) missingFields.add("name");
// if (!category) missingFields.add("category");
// if (!brand) missingFields.add("brand");
// if (price === undefined || price === "") missingFields.add("price");
// if (!GST) missingFields.add("GST");
// if (!HSN_Code) missingFields.add("HSN_Code");
// // Validate or create category
// let categoryName = "";
// if (category) {
// let categoryDoc = await CategoryModel.findOne({
// categoryName: { $regex: new RegExp(`^${category.trim()}$`, "i") },
// }).exec();
// if (!categoryDoc) {
// // If category not found, create a new one
// categoryDoc = await CategoryModel.create({
// categoryName: category.trim(),
// addedBy: req.user._id,
// });
// }
// item.category = categoryDoc._id;
// categoryName = categoryDoc.categoryName;
// }
// // Validate or create brand
// let brandName = "";
// if (brand) {
// let brandDoc = await BrandModel.findOne({
// brandName: { $regex: new RegExp(`^${brand.trim()}$`, "i") },
// }).exec();
// if (!brandDoc) {
// // If brand not found, create a new one
// brandDoc = await BrandModel.create({
// brandName: brand.trim(),
// addedBy: req.user._id,
// });
// }
// item.brand = brandDoc._id;
// brandName = brandDoc.brandName;
// }
// // Combine all errors into a single message
// let errorMessage = "";
// if (missingFields.size > 0) {
// errorMessage += `Missing fields: ${Array.from(missingFields).join(", ")}. `;
// }
// if (notFoundErrors.size > 0) {
// errorMessage += `Not found: ${Array.from(notFoundErrors).join(", ")}.`;
// }
// // If there are errors, push them to the errors array
// if (errorMessage.trim()) {
// errors.push({
// SKU: SKU || "N/A",
// productName: name || "N/A",
// category: category || "N/A",
// brand: brand || "N/A",
// GST: GST || "N/A",
// HSN_Code: HSN_Code || "N/A",
// price: price || "N/A",
// message: errorMessage.trim(),
// });
// continue;
// }
// // Ensure fields are set to empty strings if not provided
// description = description !== undefined ? description : "";
// // Check for existing product by SKU
// let existingProduct = await Product.findOne({ SKU }).exec();
// if (existingProduct) {
// // Track changes
// const updatedFields = [];
// let updatedProduct = { ...existingProduct._doc };
// // Fetch existing category name and brand name
// const existingCategory = await CategoryModel.findById(existingProduct.category).exec();
// const existingBrand = await BrandModel.findById(existingProduct.brand).exec();
// // Update product fields if they have changed
// if (name !== existingProduct.name) {
// updatedFields.push("name");
// updatedProduct.name = name;
// }
// if (category && existingProduct.category.toString() !== item.category.toString()) {
// updatedFields.push("category");
// updatedProduct.category = categoryName;
// } else {
// updatedProduct.category = existingCategory ? existingCategory.categoryName : "";
// }
// if (price !== undefined && price !== "" && existingProduct.price !== price) {
// updatedFields.push("price");
// updatedProduct.price = price;
// }
// if (brand && existingProduct.brand.toString() !== item.brand.toString()) {
// updatedFields.push("brand");
// updatedProduct.brand = brandName;
// } else {
// updatedProduct.brand = existingBrand ? existingBrand.brandName : "";
// }
// if (HSN_Code !== existingProduct.HSN_Code) {
// updatedFields.push("HSN_Code");
// updatedProduct.HSN_Code = HSN_Code;
// }
// if (GST !== existingProduct.GST) {
// updatedFields.push("GST");
// updatedProduct.GST = GST;
// }
// if (description !== existingProduct.description) {
// updatedFields.push("description");
// updatedProduct.description = description;
// }
// // Only update if there are changes
// if (updatedFields.length > 0) {
// try {
// await Product.updateOne(
// { SKU: existingProduct.SKU },
// {
// $set: {
// category: item.category || existingProduct.category,
// price: price !== undefined && price !== "" ? price : existingProduct.price,
// GST: GST || existingProduct.GST,
// HSN_Code: HSN_Code || existingProduct.HSN_Code,
// name: name,
// description: description,
// product_Status: item.product_Status || existingProduct.product_Status || "Active",
// },
// }
// );
// updatedProducts.push({
// ...updatedProduct,
// updatedFields: updatedFields.join(", "), // Track updated fields
// });
// } catch (error) {
// errors.push({
// SKU,
// message: "Failed to update product",
// });
// }
// }
// continue;
// }
// // Create new product
// if (item.category && item.brand) {
// const productData = {
// SKU,
// name,
// category: item.category,
// brand: item.brand,
// price,
// GST,
// HSN_Code,
// description: description,
// product_Status: item.product_Status || "Active",
// addedBy: req.user._id,
// };
// try {
// const newProduct = await Product.create(productData);
// newlyCreated.push({
// ...newProduct._doc,
// category: categoryName,
// brand: brandName,
// });
// } catch (error) {
// errors.push({
// SKU,
// message: "Failed to create product",
// });
// }
// }
// }
// fs.unlinkSync(filePath); // Clean up uploaded file
// res.status(201).json({
// message: errors.length > 0 ? "Products processed with errors!" : "Products processed successfully!",
// newlyCreated: newlyCreated,
// updatedProducts: updatedProducts,
// errors,
// });
// } catch (error) {
// console.error("Error:", error);
// res.status(500).json({ message: "Internal server error" });
// }
// };
export const uploadProducts = async (req, res) => { export const uploadProducts = async (req, res) => {
try { try {
if (!req.files || !req.files.file) { if (!req.files || !req.files.file) {

View File

@ -178,6 +178,12 @@ export const getAllSalesCoOrdinator = async (req, res) => {
try { try {
const PAGE_SIZE = parseInt(req.query?.show || "10"); const PAGE_SIZE = parseInt(req.query?.show || "10");
const page = parseInt(req.query?.page || "1") - 1; const page = parseInt(req.query?.page || "1") - 1;
if (!req.user || !req.user._id) {
return res.status(401).json({
success: false,
message: "Please login to a TM account",
});
}
let filter = {}; let filter = {};
if (req.query?.name) { if (req.query?.name) {
filter.name = { filter.name = {
@ -192,6 +198,90 @@ export const getAllSalesCoOrdinator = async (req, res) => {
if (req.query?.isVerified) { if (req.query?.isVerified) {
filter.isVerified = req.query.isVerified === "true"; filter.isVerified = req.query.isVerified === "true";
} }
const total = await SalesCoOrdinator.countDocuments(filter);
const salesCoOrinators = await SalesCoOrdinator.find(filter)
.limit(PAGE_SIZE)
.skip(PAGE_SIZE * page)
.sort({ createdAt: -1 });
return res.status(200).json({
success: true,
total_data: total,
total_pages: Math.ceil(total / PAGE_SIZE),
salesCoOrinators,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message ? error.message : "Something went wrong!",
});
}
};
export const getAllSalesCoOrdinatorforTM_App = async (req, res) => {
try {
const PAGE_SIZE = parseInt(req.query?.show || "10");
const page = parseInt(req.query?.page || "1") - 1;
if (!req.user || !req.user._id) {
return res.status(401).json({
success: false,
message: "Please login to a TM account",
});
}
let filter = {};
if (req.query?.name) {
filter.name = {
$regex: new RegExp(req.query.name, "i"),
};
}
if (req.query?.mobileNumber) {
filter.mobileNumber = {
$regex: new RegExp(req.query.mobileNumber, "i"),
};
}
if (req.query?.isVerified) {
filter.isVerified = req.query.isVerified === "true";
}
// Mandatory filter for mappedby
filter.mappedby = req.user._id;
const total = await SalesCoOrdinator.countDocuments(filter);
const salesCoOrinators = await SalesCoOrdinator.find(filter)
.limit(PAGE_SIZE)
.skip(PAGE_SIZE * page)
.sort({ createdAt: -1 });
return res.status(200).json({
success: true,
total_data: total,
total_pages: Math.ceil(total / PAGE_SIZE),
salesCoOrinators,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message ? error.message : "Something went wrong!",
});
}
};
export const getAllSalesCoOrdinatorbytmId = async (req, res) => {
try {
const PAGE_SIZE = parseInt(req.query?.show || "10");
const page = parseInt(req.query?.page || "1") - 1;
let filter = { mappedby: req.params.id }; // Include the mappedby field in the filter
// Adding optional filters
if (req.query?.name) {
filter.name = {
$regex: new RegExp(req.query.name, "i"),
};
}
if (req.query?.mobileNumber) {
filter.mobileNumber = {
$regex: new RegExp(req.query.mobileNumber, "i"),
};
}
if (req.query?.isVerified) {
filter.isVerified = req.query.isVerified === "true";
}
const total = await SalesCoOrdinator.countDocuments(filter); const total = await SalesCoOrdinator.countDocuments(filter);
const salesCoOrinators = await SalesCoOrdinator.find(filter) const salesCoOrinators = await SalesCoOrdinator.find(filter)
@ -212,7 +302,82 @@ export const getAllSalesCoOrdinator = async (req, res) => {
}); });
} }
}; };
export const mappedbyTM = async (req, res) => {
try {
const { id } = req.params; // SalesCoOrdinator ID from URL parameters
const { mappedby } = req.body; // TerritoryManager ID from request body
// console.log(id, mappedby);
// Validate that the TerritoryManager ID is provided
if (!mappedby) {
return res.status(400).json({
success: false,
message: "Territory Manager ID (mappedby) is required.",
});
}
// Find the SalesCoOrdinator by ID
const salesCoordinator = await SalesCoOrdinator.findById(id);
// If no SalesCoOrdinator is found
if (!salesCoordinator) {
return res.status(404).json({
success: false,
message: "Sales Coordinator not found.",
});
}
// Update the mappedby field
salesCoordinator.mappedby = mappedby;
// Save the updated SalesCoOrdinator
await salesCoordinator.save();
return res.status(200).json({
success: true,
message: "Sales Coordinator successfully updated.",
salesCoordinator,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message ? error.message : "Something went wrong!",
});
}
};
export const unmapSalesCoOrdinator = async (req, res) => {
try {
if (!req.params.id) {
return res.status(400).json({
success: false,
message: "Please provide SalesCoOrdinator ID!",
});
}
const getSalesCoOrdinator = await SalesCoOrdinator.findById(req.params.id);
if (!getSalesCoOrdinator) {
return res.status(404).json({
success: false,
message: "Sales Coordinator not found!",
});
}
// Set mappedby field to null
getSalesCoOrdinator.mappedby = null;
// Save the updated sales coordinator
await getSalesCoOrdinator.save();
res.status(200).json({
success: true,
message: "Sales Coordinator unmapped successfully!",
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message ? error.message : "Something went wrong!",
});
}
};
export const getOneSalesCoOrdinator = async (req, res) => { export const getOneSalesCoOrdinator = async (req, res) => {
try { try {
if (!req.params.id) { if (!req.params.id) {

View File

@ -8,6 +8,11 @@ import crypto from "crypto";
const salescoordinatorSchema = new mongoose.Schema( const salescoordinatorSchema = new mongoose.Schema(
{ {
designation: {
type: String,
required: true,
default: "Sales Coordinator",
},
name: { name: {
type: String, type: String,
required: true, required: true,
@ -54,7 +59,6 @@ const salescoordinatorSchema = new mongoose.Schema(
mappedby: { mappedby: {
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
ref: "TerritoryManager", ref: "TerritoryManager",
required: true,
}, },
}, },
{ timestamps: true } { timestamps: true }

View File

@ -16,6 +16,10 @@ import {
ChangePassword, ChangePassword,
getOneSalesCoOrdinator, getOneSalesCoOrdinator,
logout, logout,
getAllSalesCoOrdinatorbytmId,
mappedbyTM,
unmapSalesCoOrdinator,
getAllSalesCoOrdinatorforTM_App,
} from "./SalesCoOrdinatorController.js"; } from "./SalesCoOrdinatorController.js";
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
@ -35,7 +39,25 @@ router.get(
router.get( router.get(
"/getAll-TM", "/getAll-TM",
isAuthenticatedTerritoryManager, isAuthenticatedTerritoryManager,
getAllSalesCoOrdinator getAllSalesCoOrdinatorforTM_App
);
router.get(
"/getbyTmId/:id",
isAuthenticatedUser,
authorizeRoles("admin"),
getAllSalesCoOrdinatorbytmId
);
router.put(
"/mappedtm/:id",
isAuthenticatedUser,
authorizeRoles("admin"),
mappedbyTM
);
router.delete(
"/unmap/:id",
isAuthenticatedUser,
authorizeRoles("admin"),
unmapSalesCoOrdinator
); );
router.get( router.get(
"/getOne/:id", "/getOne/:id",
@ -75,11 +97,7 @@ router.patch(
authorizeRoles("admin"), authorizeRoles("admin"),
UpdateProfile UpdateProfile
); );
router.patch( router.patch("/profile/update", isAuthenticatedSalesCoOrdinator, UpdateProfile);
"/profile/update",
isAuthenticatedSalesCoOrdinator,
UpdateProfile
);
//change password //change password
router.put( router.put(
"/password/update/:id", "/password/update/:id",

View File

@ -1,6 +1,44 @@
import Task from "./TaskModel.js"; import Task from "./TaskModel.js";
import SalesCoOrdinator from "../SalesCoOrdinators/SalesCoOrdinatorModel.js";
import crypto from "crypto"; import crypto from "crypto";
import cron from "node-cron";
// Function to update task statuses
const updateOverdueTasks = async () => {
try {
const currentDate = new Date();
const currentDateOnly = new Date(currentDate.setHours(0, 0, 0, 0));
// Find tasks where dueDate is before the current date and status is "New"
const overdueTasks = await Task.find({
taskDueDate: { $lt: currentDateOnly },
taskStatus: "New",
});
// Update tasks to "Pending"
for (const task of overdueTasks) {
task.taskStatus = "Pending";
await task.save();
}
console.log('Overdue tasks updated to "Pending".');
} catch (error) {
console.error("Error updating overdue tasks:", error);
}
};
// Schedule the cron job to run daily at midnight
cron.schedule("10 0 * * *", updateOverdueTasks, {
timezone: "Asia/Kolkata",
});
// cron.schedule("30 9 * * *", updateOverdueTasks);
const parseDate = (dateStr) => {
const [day, month, year] = dateStr.split("/").map(Number);
// Create a date object in local timezone
const localDate = new Date(year, month - 1, day);
// Convert to a date with time set to the start of the day in UTC
return new Date(Date.UTC(year, month - 1, day));
};
export const assignTask = async (req, res) => { export const assignTask = async (req, res) => {
try { try {
@ -12,23 +50,47 @@ export const assignTask = async (req, res) => {
taskAssignedTo, taskAssignedTo,
addedFor, addedFor,
addedForId, addedForId,
tradename,
} = req.body; } = req.body;
// console.log(req.body);
// Convert the taskDueDate from DD/MM/YYYY string to Date object
const dueDate = parseDate(taskDueDate);
const currentDate = new Date();
// Set the time of the currentDate to the start of the day for comparison
const currentDateOnly = new Date(
Date.UTC(
currentDate.getUTCFullYear(),
currentDate.getUTCMonth(),
currentDate.getUTCDate()
)
);
// Check if the due date is in the past
if (dueDate < currentDateOnly) {
return res.status(400).json({
success: false,
message: "Due date cannot be earlier than the current date.",
});
}
const currentYear = new Date().getFullYear().toString().slice(-2); const currentYear = new Date().getFullYear().toString().slice(-2);
const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase(); const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase();
const uniqueId = `${currentYear}-${randomChars}`; const uniqueId = `${currentYear}-${randomChars}`;
// Create a new task // Create a new task
const newTask = await Task.create({ const newTask = await Task.create({
taskId: uniqueId, taskId: uniqueId,
task, task,
note, note,
taskStatus: "Pending", taskStatus: "New",
taskPriority, taskPriority,
taskDueDate, taskDueDate: dueDate, // Save the date as a Date object
taskAssignedTo, taskAssignedTo,
taskAssignedBy: req.user._id, // The Territory Manager's ID taskAssignedBy: req.user._id,
addedFor, addedFor,
addedForId, addedForId,
tradename,
}); });
res.status(201).json({ res.status(201).json({
@ -44,28 +106,111 @@ export const assignTask = async (req, res) => {
} }
}; };
export const getTasksForSalesCoordinator = async (req, res) => { export const getTasksByStatus = async (req, res) => {
try { try {
const tasks = await Task.find({ taskAssignedTo: req.user._id }); const { status } = req.params; // This should be "New", "Pending", or "Completed"
// Validate the provided status
if (!["New", "Pending", "Completed"].includes(status)) {
return res.status(400).json({
success: false,
message: "Invalid status type provided.",
});
}
// Find tasks assigned to the user, filtered by status, and sorted by creation date (newest to oldest)
const tasks = await Task.find({
taskAssignedTo: req.user._id,
taskStatus: status,
}).sort({ createdAt: -1 }); // Sort by createdAt in descending order (-1 means newest first)
res.status(200).json({ res.status(200).json({
success: true, success: true,
tasks, tasks,
}); });
} catch (error) { } catch (error) {
res.status(400).json({ res.status(500).json({
success: false, success: false,
message: error.message, message: error.message,
}); });
} }
}; };
export const getAllTasksByStatus = async (req, res) => {
try {
const { status } = req.params; // This should be "New", "Pending", or "Completed"
// Validate the provided status
if (!["New", "Pending", "Completed"].includes(status)) {
return res.status(400).json({
success: false,
message: "Invalid status type provided.",
});
}
// Find tasks assigned to the user, filtered by status, and sorted by creation date (newest to oldest)
const tasks = await Task.find({
taskAssignedBy: req.user._id,
taskStatus: status,
})
.populate({
path: "taskAssignedTo",
select: "name mobileNumber email",
})
.sort({ createdAt: -1 }); // Sort by createdAt in descending order (-1 means newest first)
res.status(200).json({
success: true,
tasks,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
export const getTasksbytask = async (req, res) => {
try {
const { task } = req.params;
// Validate the provided status
if (
![
"Visit RD/PD",
"Update Sales Data",
"Update Inventory Data",
"Collect KYC",
].includes(task)
) {
return res.status(400).json({
success: false,
message: "Invalid task type provided.",
});
}
const tasks = await Task.find({
taskAssignedTo: req.user._id,
task: task,
}).sort({ createdAt: -1 });
res.status(200).json({
success: true,
tasks,
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
export const updateTaskStatus = async (req, res) => { export const updateTaskStatus = async (req, res) => {
try { try {
const { taskId } = req.params; const { taskId } = req.params;
// Find the task to ensure it belongs to the logged-in Sales Coordinator const task = await Task.findOne({
const task = await Task.findOne({ _id: taskId, taskAssignedTo: req.user._id }); _id: taskId,
taskAssignedTo: req.user._id,
});
if (!task) { if (!task) {
return res.status(404).json({ return res.status(404).json({

View File

@ -12,18 +12,19 @@ const TaskSchema = new mongoose.Schema(
task: { task: {
type: String, type: String,
required: true, required: true,
enum: ["Visit Retailers", "Update Sales Data", "Update Inventory Data", "Collect KYC"], // Restrict to specific tasks enum: ["Visit RD/PD", "Update Sales Data", "Update Inventory Data", "Collect KYC"],
}, },
note: { note: {
type: String, type: String,
required: function () { required: function () {
return this.task === "Collect KYC"; return this.task === "Collect KYC" || this.task === "Visit RD/PD";
}, },
}, },
taskStatus: { taskStatus: {
type: String, type: String,
required: true, required: true,
enum: ["Pending", "In Progress", "Completed"], enum: ["New", "Pending", "Completed"],
default: "New",
}, },
taskPriority: { taskPriority: {
type: String, type: String,
@ -31,9 +32,8 @@ const TaskSchema = new mongoose.Schema(
enum: ["Low", "Medium", "High"], enum: ["Low", "Medium", "High"],
}, },
taskDueDate: { taskDueDate: {
type: String, type: Date, // Change to Date
required: true, required: true,
match: /^\d{2}\/\d{2}\/\d{4}$/,
}, },
taskAssignedTo: { taskAssignedTo: {
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
@ -49,14 +49,20 @@ const TaskSchema = new mongoose.Schema(
type: String, type: String,
enum: ['PrincipalDistributor', 'RetailDistributor'], enum: ['PrincipalDistributor', 'RetailDistributor'],
required: function () { required: function () {
return this.task === "Update Inventory Data"; return this.task === "Update Inventory Data" || this.task === "Visit RD/PD";
}, },
}, },
addedForId: { addedForId: {
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
refPath: 'addedFor', refPath: 'addedFor',
required: function () { required: function () {
return this.task === "Update Inventory Data"; return this.task === "Update Inventory Data" || this.task === "Visit RD/PD";
},
},
tradename: {
type: String,
required: function () {
return this.task === "Update Inventory Data" || this.task === "Visit RD/PD";
}, },
}, },
}, },

View File

@ -1,8 +1,10 @@
import express from "express"; import express from "express";
import { import {
assignTask, assignTask,
getTasksForSalesCoordinator, getTasksByStatus,
updateTaskStatus, updateTaskStatus,
getTasksbytask,
getAllTasksByStatus,
} from "./TaskController.js"; } from "./TaskController.js";
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
@ -16,13 +18,23 @@ router.post(
assignTask assignTask
); );
// Route for Sales Coordinator to view their tasks // Route for Sales Coordinator to view their tasks by status
router.get( router.get(
"/tasks", "/tasks/:status",
isAuthenticatedSalesCoOrdinator, isAuthenticatedSalesCoOrdinator,
getTasksForSalesCoordinator getTasksByStatus
); );
router.get(
"/alltasks/:status",
isAuthenticatedTerritoryManager,
getAllTasksByStatus
);
router.get(
"/task/type/:task",
isAuthenticatedSalesCoOrdinator,
getTasksbytask
);
// Route to update task status
router.put( router.put(
"/update-task-status/:taskId", "/update-task-status/:taskId",
isAuthenticatedSalesCoOrdinator, isAuthenticatedSalesCoOrdinator,

View File

@ -1,77 +0,0 @@
import { Tax } from "./tax_model.js";
export const addTax = async (req, res) => {
if (!req.user) {
return res.status(400).json({ message: "User Not Found" });
}
const tax = new Tax({
name: req.body.name,
tax: req.body.tax,
hsn_code: req.body.hsn_code,
});
try {
const data = await tax.save();
res.status(201).json({ message: "Success", data: data });
} catch (error) {
res.status(500).json({ message: error.message ? error.message : "Something went Wrong" });
}
};
export const updateTax = async (req, res) => {
if (!req.user) {
return res.status(400).json({ message: "User Not Found" });
}
const id = req.params.id;
const queryObj = req.body;
try {
const data = await Tax.findByIdAndUpdate(id, queryObj, {
new: true,
});
res.status(200).json({ message: "Success", data: data });
} catch (error) {
res.status(500).json({ message: error.message, message: "failed" });
}
};
export const deleteTax = async (req, res) => {
if (!req.user) {
return res.status(400).json({ message: "User Not Found" });
}
const id = req.params.id;
try {
const data = await Tax.findByIdAndDelete(id);
res.status(200).json({ message: "Success" });
} catch (error) {
res.status(500).json({ message: error.message, message: "failed" });
}
};
export const getTaxes = async (req, res) => {
if (!req.user) {
return res.status(400).json({ message: "User Not Found" });
}
try {
const data = await Tax.find().sort({ createdAt: -1 });
res.status(200).json(data);
} catch (error) {
res.status(500).json({ message: error.message, message: "failed" });
}
};
export const getTax = async (req, res) => {
if (!req.user) {
return res.status(400).json({ message: "User Not Found" });
}
try {
let { id } = req.params;
const tax = await Tax.findById({ _id: id });
return res.status(200).json(tax);
} catch (error) {
return res.status(504).json({
status: "failed",
error,
});
}
};

View File

@ -1,14 +0,0 @@
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const TaxSchema = new Schema(
{
name: String,
hsn_code: Number,
tax: Number,
},
{ timestamps: true }
);
export const Tax = model("Tax", TaxSchema);

View File

@ -1,23 +0,0 @@
import { Router } from "express";
import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js";
import {
addTax,
updateTax,
deleteTax,
getTaxes,
getTax,
} from "./tax_controller.js";
const router = Router();
router
.route("/add_tax")
.post(isAuthenticatedUser, authorizeRoles("admin", "Employee"), addTax);
router
.route("/update_tax/:id")
.patch(isAuthenticatedUser, authorizeRoles("admin", "Employee"), updateTax);
router
.route("/delete_tax/:id")
.delete(isAuthenticatedUser, authorizeRoles("admin", "Employee"), deleteTax);
router.route("/view_tax/:id").get(isAuthenticatedUser, getTax);
router.route("/view_tax").get(isAuthenticatedUser, getTaxes);
export default router;

View File

@ -8,6 +8,11 @@ import crypto from "crypto";
const territorymanagerSchema = new mongoose.Schema( const territorymanagerSchema = new mongoose.Schema(
{ {
designation: {
type: String,
required: true,
default: "Territory Manager",
},
name: { name: {
type: String, type: String,
required: true, required: true,

View File

@ -59,6 +59,278 @@ const generatePassword = (name, email) => {
return password; return password;
}; };
// export const uploadPrincipaldistributors = async (req, res) => {
// try {
// if (!req.files || !req.files.file) {
// return res.status(400).json({ message: "No file uploaded" });
// }
// const file = req.files.file;
// const filePath = path.join("public", "uploads", file.name);
// // Ensure 'uploads' directory exists
// if (!fs.existsSync(path.dirname(filePath))) {
// fs.mkdirSync(path.dirname(filePath), { recursive: true });
// }
// // Move the file from temp to the uploads directory
// await file.mv(filePath);
// // Process the file
// const fileBuffer = fs.readFileSync(filePath);
// const workbook = XLSX.read(fileBuffer, { type: "buffer" });
// const sheetName = workbook.SheetNames[0];
// const worksheet = workbook.Sheets[sheetName];
// const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
// if (data.length <= 1) {
// return res.status(400).json({ message: "Empty spreadsheet or no data found" });
// }
// const headers = data[0];
// // Map headers from the Excel file to your schema
// const headerMapping = {
// "PD ID (From SAP)": "uniqueId",
// "SBU":"SBU",
// "Principal Distributor Name": "name",
// "Email": "email",
// "Phone Number": "phone",
// "PAN Number": "panNumber",
// "Trade Name": "tradeName",
// "GST Number": "gstNumber",
// "State": "state",
// "City": "city",
// "Street": "street",
// "Pincode": "postalCode",
// };
// const requiredHeaders = Object.keys(headerMapping);
// if (!requiredHeaders.every((header) => headers.includes(header))) {
// return res.status(400).json({ message: "Missing required columns in spreadsheet" });
// }
// const errors = [];
// const newlyCreated = [];
// const updatedDistributors = [];
// for (let i = 1; i < data.length; i++) {
// const row = data[i];
// const item = {};
// headers.forEach((header, index) => {
// if (headerMapping[header]) {
// item[headerMapping[header]] = row[index] !== undefined ? row[index] : "";
// }
// });
// // Initialize error tracking for each item
// const missingFields = new Set();
// const validationErrors = new Set();
// // Validate required fields
// if (!item.uniqueId) missingFields.add("uniqueId");
// if(!item.SBU) missingFields.add("SBU");
// if (!item.name) missingFields.add("name");
// if (!item.email) missingFields.add("email");
// if (!item.phone) missingFields.add("phone");
// if (!item.panNumber) missingFields.add("panNumber");
// if (!item.tradeName) missingFields.add("tradeName");
// if (!item.gstNumber) missingFields.add("gstNumber");
// if (!item.state) missingFields.add("state");
// if (!item.city) missingFields.add("city");
// if (!item.street) missingFields.add("street");
// if (!item.postalCode) missingFields.add("postalCode");
// // Check email validity
// if (item.email && !validator.isEmail(item.email)) {
// validationErrors.add("incorrect mail");
// }
// // Validate mobile number
// if (item.phone && !/^\d{10}$/.test(item.phone)) {
// validationErrors.add("Invalid Mobile Number (should be 10 digits)");
// }
// // Check GST, PAN, and postal code validation
// item.panNumber = item.panNumber ? item.panNumber.toUpperCase() : "";
// item.gstNumber = item.gstNumber ? item.gstNumber.toUpperCase() : "";
// // Validate PAN Number
// if (item.panNumber && !/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(item.panNumber)) {
// validationErrors.add("Invalid PAN Number");
// }
// // Validate GST Number
// if (item.gstNumber && !/^(\d{2}[A-Z]{5}\d{4}[A-Z]{1}\d[Z]{1}[A-Z\d]{1})$/.test(item.gstNumber)) {
// validationErrors.add("Invalid GST Number");
// }
// // Validate Postal Code
// if (item.postalCode && !/^\d{6}$/.test(item.postalCode)) {
// validationErrors.add("Invalid Postal Code");
// }
// // Combine all errors into a single message
// let errorMessage = "";
// if (missingFields.size > 0) {
// errorMessage += `Missing fields: ${Array.from(missingFields).join(", ")}. `;
// }
// if (validationErrors.size > 0) {
// errorMessage += `Validation errors: ${Array.from(validationErrors).join(", ")}.`;
// }
// // If there are errors, push them to the errors array
// if (errorMessage.trim()) {
// errors.push({
// uniqueId: item.uniqueId || "N/A",
// SBU:item.SBU || "N/A",
// name: item.name || "N/A",
// email: item.email || "N/A",
// phone: item.phone || "N/A",
// panNumber: item.panNumber || "N/A",
// gstNumber: item.gstNumber || "N/A",
// message: errorMessage.trim(),
// });
// continue;
// }
// // Generate a password
// const password = generatePassword(item.name, item.email);
// item.role = "principal-Distributor";
// // Check for existing user by uniqueId
// let distributor = await User.findOne({ uniqueId: item.uniqueId });
// if (distributor) {
// // Track updated fields
// const updatedFields = [];
// const addressFields = ['panNumber', 'gstNumber', 'state', 'city', 'street', 'tradeName', 'postalCode'];
// const existingAddress = await ShippingAddress.findOne({ user: distributor._id });
// // Check for changes in user details
// let userUpdated = false;
// if (distributor.name !== item.name) {
// updatedFields.push("name");
// distributor.name = item.name;
// userUpdated = true;
// }
// if (distributor.email !== item.email) {
// updatedFields.push("email");
// distributor.email = item.email;
// userUpdated = true;
// }
// if(distributor.SBU !== item.SBU){
// updatedFields.push("SBU");
// distributor.SBU = item.SBU;
// userUpdated = true;
// }
// if (distributor.phone !== item.phone.toString()) {
// updatedFields.push("phone");
// distributor.phone = item.phone;
// userUpdated = true;
// }
// // Update user
// if (userUpdated) {
// await distributor.save();
// }
// // Check for changes in address details
// const addressData = {
// street: item.street,
// city: item.city,
// state: item.state,
// postalCode: item.postalCode.toString(),
// country: "India", // Default country
// panNumber: item.panNumber,
// tradeName: item.tradeName,
// gstNumber: item.gstNumber,
// user: distributor._id,
// };
// let addressUpdated = false;
// if (existingAddress) {
// const addressUpdates = [];
// addressFields.forEach(field => {
// if (existingAddress[field] !== addressData[field]) {
// addressUpdates.push(field);
// addressUpdated = true;
// }
// });
// if (addressUpdated) {
// await ShippingAddress.updateOne({ user: distributor._id }, addressData);
// if (addressUpdates.length > 0) {
// updatedFields.push(`Address fields: ${addressUpdates.join(", ")}`);
// }
// }
// } else {
// // Create new address
// await ShippingAddress.create(addressData);
// updatedFields.push("New address created");
// }
// // Add to updatedDistributors only if there are updated fields
// if (updatedFields.length > 0) {
// updatedDistributors.push({
// ...distributor._doc,
// updatedFields: updatedFields.join(", ")
// });
// }
// } else {
// // Create a new user
// distributor = new User({
// name: item.name,
// SBU:item.SBU,
// email: item.email,
// phone: item.phone,
// password,
// role: item.role,
// uniqueId: item.uniqueId,
// });
// await distributor.save();
// // Send email with the new user details
// await sendEmail({
// to: item.email,
// from: process.env.SEND_EMAIL_FROM,
// subject: `Cheminova Account Created`,
// html: `
// Your Principal Distributor Account is created successfully.
// <br/>Name: <strong>${item.name}</strong><br/>
// <br/>Mobile Number: <strong>${item.phone}</strong><br/>
// <br/>Password: <strong>${password}</strong><br/><br/>
// <a href="${process.env.PD_APP_URL}/login">Click here to login</a><br/><br/>
// If you have not requested this email, please ignore it.
// `,
// });
// newlyCreated.push(distributor._doc);
// }
// }
// fs.unlinkSync(filePath); // Clean up uploaded file
// res.status(201).json({
// message:
// errors.length > 0
// ? "File processed with errors!"
// : "File processed successfully!",
// processedUsers: {
// newlyCreated: newlyCreated.length,
// updatedDistributors: updatedDistributors.length
// },
// errors,
// newlyCreated,
// updatedDistributors
// });
// } catch (error) {
// console.error("Error processing file:", error);
// res.status(500).json({ message: "Error processing file", error: error.message });
// }
// };
export const uploadPrincipaldistributors = async (req, res) => { export const uploadPrincipaldistributors = async (req, res) => {
try { try {
if (!req.files || !req.files.file) { if (!req.files || !req.files.file) {
@ -94,6 +366,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
// Map headers from the Excel file to your schema // Map headers from the Excel file to your schema
const headerMapping = { const headerMapping = {
"PD ID (From SAP)": "uniqueId", "PD ID (From SAP)": "uniqueId",
SBU: "SBU",
"Principal Distributor Name": "name", "Principal Distributor Name": "name",
Email: "email", Email: "email",
"Phone Number": "phone", "Phone Number": "phone",
@ -135,6 +408,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
// Validate required fields // Validate required fields
if (!item.uniqueId) missingFields.add("uniqueId"); if (!item.uniqueId) missingFields.add("uniqueId");
if (!item.SBU) missingFields.add("SBU");
if (!item.name) missingFields.add("name"); if (!item.name) missingFields.add("name");
if (!item.email) missingFields.add("email"); if (!item.email) missingFields.add("email");
if (!item.phone) missingFields.add("phone"); if (!item.phone) missingFields.add("phone");
@ -198,6 +472,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
if (errorMessage.trim()) { if (errorMessage.trim()) {
errors.push({ errors.push({
uniqueId: item.uniqueId || "N/A", uniqueId: item.uniqueId || "N/A",
SBU: item.SBU || "N/A",
name: item.name || "N/A", name: item.name || "N/A",
email: item.email || "N/A", email: item.email || "N/A",
phone: item.phone || "N/A", phone: item.phone || "N/A",
@ -238,6 +513,16 @@ export const uploadPrincipaldistributors = async (req, res) => {
distributor.name = item.name; distributor.name = item.name;
userUpdated = true; userUpdated = true;
} }
if (distributor.email !== item.email) {
updatedFields.push("email");
distributor.email = item.email;
userUpdated = true;
}
if (distributor.SBU !== item.SBU) {
updatedFields.push("SBU");
distributor.SBU = item.SBU;
userUpdated = true;
}
if (distributor.phone !== item.phone.toString()) { if (distributor.phone !== item.phone.toString()) {
updatedFields.push("phone"); updatedFields.push("phone");
distributor.phone = item.phone; distributor.phone = item.phone;
@ -300,6 +585,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
// Create a new user // Create a new user
distributor = new User({ distributor = new User({
name: item.name, name: item.name,
SBU: item.SBU,
email: item.email, email: item.email,
phone: item.phone, phone: item.phone,
password, password,
@ -307,126 +593,57 @@ export const uploadPrincipaldistributors = async (req, res) => {
uniqueId: item.uniqueId, uniqueId: item.uniqueId,
}); });
await distributor.save(); await distributor.save();
// Send email with the new user details
await sendEmail({ await sendEmail({
to: item.email, to: distributor.email,
from: process.env.SEND_EMAIL_FROM, from: process.env.SEND_EMAIL_FROM,
subject: `Cheminova Account Created`, subject: `Cheminova Account Created`,
html: ` html: `
Your Principal Distributor Account is created successfully. Your Principal Distributor Account is created successfully.
<br/>Name: <strong>${item.name}</strong><br/> <br/>Name: <strong>${distributor.name}</strong><br/>
<br/>Mobile Number: <strong>${item.phone}</strong><br/> <br/>Mobile Number: <strong>${distributor.phone}</strong><br/>
<br/>Password: <strong>${password}</strong><br/><br/> <br/>Password: <strong>${password}</strong><br/><br/>
<a href="${process.env.PD_APP_URL}/login">Click here to login</a><br/><br/> <a href="${process.env.PD_APP_URL}/login">Click here to login</a><br/><br/>
If you have not requested this email, please ignore it. If you have not requested this email, please ignore it.
`, `,
}); });
// Now create the address for the new user
const addressData = {
street: item.street,
city: item.city,
state: item.state,
postalCode: item.postalCode.toString(),
country: "India", // Default country
panNumber: item.panNumber,
tradeName: item.tradeName,
gstNumber: item.gstNumber,
user: distributor._id, // Use the saved user's ID
};
const newAddress = await ShippingAddress.create(addressData);
newlyCreated.push(distributor._doc); // Push both the distributor and the addressData to the newlyCreated array
newlyCreated.push({
distributor,
address: newAddress,
});
} }
} }
fs.unlinkSync(filePath); // Clean up uploaded file res.status(200).json({
message: "File processed successfully",
res.status(201).json({
message:
errors.length > 0
? "File processed with errors!"
: "File processed successfully!",
processedUsers: {
newlyCreated: newlyCreated.length,
updatedDistributors: updatedDistributors.length,
},
errors,
newlyCreated, newlyCreated,
updatedDistributors, updatedDistributors,
errors,
}); });
} catch (error) { } catch (error) {
console.error("Error processing file:", error); console.error(error);
res res.status(500).json({ message: "Internal Server Error" });
.status(500)
.json({ message: "Error processing file", error: error.message });
} }
}; };
// 1.Register a User // 1.Register a User
// export const registerUser = async (req, res) => {
// try {
// const { name, email, password, phone, accessTo, role } = req.body;
// // console.log("this is the password ", password, name, req.body);
// let findUser = await User.findOne({ email });
// if (findUser) {
// return res
// .status(400)
// .json({ success: false, message: "User already exists" });
// }
// if (req.files) {
// const files = req.files.avatar;
// const myCloud = await cloudinary.uploader.upload(
// files.tempFilePath,
// {
// folder: "Cheminova/user-image",
// },
// function (error, result) {
// result, error;
// }
// );
// }
// const user = await User.create({
// name,
// email,
// password,
// phone,
// role,
// accessTo,
// // avatar: {
// // public_id: myCloud.public_id,
// // url: myCloud.secure_url,
// // },
// });
// // const emailData = await RegisterEmail.find();
// // let emailSubject = emailData[0]?.subject;
// // let emailDescription = emailData[0]?.description;
// const config = await Config.find();
// let appName = config[0]?.appName;
// // await sendEmail({
// // to: `${email}`, // Change to your recipient
// // from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender
// // subject: `Welcome to Cheminova - Let the Shopping Begin!`,
// // html: ` <h1 style="color: #333; text-align: left; font-family: Arial, sans-serif;">Welcome to ${appName} - Let the Shopping Begin!</h1>
// // <strong style="color: #1b03a3; font-size: 16px"> Hey ${name},</strong>
// // <p style="color: #555; font-size: 15px;">
// // Welcome to Cheminova - Let the Shopping Begin!
// // </p>
// // <br/>
// // <p style="color: #555; font-size: 15px;">You can login into :${role === "Employee" || role === "admin"
// // ? `https://admin.smellika.com/`
// // : `https://smellika.com`
// // } </p>
// // <br/>
// // <p style="color: #555; font-size: 15px;">Below are your login credentials:</p>
// // <p style="color: #555; font-size: 15px;">Email: ${email}</p>
// // <p style="color: #555; font-size: 15px;">Password: ${password}</p>
// // <span style="color: #555; font-size: 13px;">Happy shopping,</span><br/>
// // <span style="color: #555; font-size: 13px;">Team ${appName}</span>`,
// // });
// sendToken(user, 201, res);
// } catch (e) {
// return res.status(400).json({ success: false, message: e.message });
// }
// };
export const registerUser = async (req, res) => { export const registerUser = async (req, res) => {
try { try {
const { name, email, phone, accessTo, role, PD_ID } = req.body; const { name, email, phone, accessTo, role, PD_ID, SBU } = req.body;
// console.log(req.body); // console.log(req.body);
const password = generatePassword(name, email); const password = generatePassword(name, email);
// console.log(password); // console.log(password);
@ -440,7 +657,7 @@ export const registerUser = async (req, res) => {
user.phone = phone; user.phone = phone;
user.role = role; user.role = role;
user.accessTo = accessTo; user.accessTo = accessTo;
user.SBU = SBU;
// Save updates // Save updates
await user.save(); await user.save();
// console.log("finduser", user); // console.log("finduser", user);
@ -455,6 +672,7 @@ export const registerUser = async (req, res) => {
// Create a new user if not found // Create a new user if not found
user = new User({ user = new User({
uniqueId: PD_ID, uniqueId: PD_ID,
SBU,
name, name,
email, email,
password, password,
@ -779,13 +997,132 @@ export const updateProfile = catchAsyncErrors(async (req, res, next) => {
// 9.Get all users(admin) // 9.Get all users(admin)
export const getAllUser = catchAsyncErrors(async (req, res, next) => { export const getAllUser = catchAsyncErrors(async (req, res, next) => {
// Assuming your User model is imported as 'User' // Assuming your User model is imported as 'User'
const users = await User.find({ role: "principal-Distributor" }); const { page = 1, show = 10, name, mobileNumber } = req.query;
// Create a filter object
const filter = { role: "principal-Distributor" };
if (name) {
filter.name = { $regex: name, $options: "i" }; // Case-insensitive regex search
}
if (mobileNumber) {
filter.phone = mobileNumber;
}
// Calculate pagination values
const limit = parseInt(show, 10);
const skip = (parseInt(page, 10) - 1) * limit;
// Count total users matching the filter
// Find users with pagination and filters
const users = await User.find(filter)
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit);
const totalUsers = await User.countDocuments(filter);
res.status(200).json({ res.status(200).json({
success: true, success: true,
users, users,
totalUsers,
}); });
}); });
export const getAllPrincipalDistributorbytmId = catchAsyncErrors(
async (req, res, next) => {
const { page = 1, show = 10, name, mobileNumber } = req.query;
const tmId = req.params.id;
if (!tmId) {
return res
.status(400)
.json({ message: "Please provide Territory Manager ID" });
}
// Create a filter object
const filter = { role: "principal-Distributor" };
if (name) {
filter.name = { $regex: name, $options: "i" }; // Case-insensitive regex search
}
if (mobileNumber) {
filter.phone = mobileNumber;
}
// Filter based on the mapped Territory Manager ID if provided
if (tmId) {
filter.mappedby = tmId;
}
// Calculate pagination values
const limit = parseInt(show, 10);
const skip = (parseInt(page, 10) - 1) * limit;
// Find users with pagination and filters
const users = await User.find(filter)
.sort({ createdAt: -1 })
.skip(skip)
.limit(limit);
// Count total users matching the filter
const totalUsers = await User.countDocuments(filter);
res.status(200).json({
success: true,
principaldistributor: users,
total_data: totalUsers,
page: parseInt(page, 10),
limit,
});
}
);
export const mappedbyTM = catchAsyncErrors(async (req, res, next) => {
const { id } = req.params; // SalesCoOrdinator ID from URL parameters
const { mappedby } = req.body; // TerritoryManager ID from request body
// console.log(id, mappedby);
// Validate that the TerritoryManager ID is provided
if (!mappedby) {
return res.status(400).json({
success: false,
message: "Territory Manager ID (mappedby) is required.",
});
}
const principalDistributor = await User.findById(id);
if (!principalDistributor) {
return res.status(404).json({
success: false,
message: "Principal Distributor not found",
});
}
// Update the mappedby field
principalDistributor.mappedby = mappedby;
await principalDistributor.save();
return res.status(200).json({
success: true,
message: "Principal Distributor updated successfully",
principalDistributor,
});
});
export const unmappedTMinPrincipalDistributor = catchAsyncErrors(
async (req, res, next) => {
const { id } = req.params; // Principal Distributor ID from URL parameters
// console.log(id);
const principalDistributor = await User.findById(id);
if (!principalDistributor) {
return res.status(404).json({
success: false,
message: "Principal Distributor not found",
});
}
// Remove the mappedby field
principalDistributor.mappedby = null;
await principalDistributor.save();
return res.status(200).json({
success: true,
message: "Principal Distributor updated successfully",
principalDistributor,
});
}
);
export const getAllEmployee = catchAsyncErrors(async (req, res, next) => { export const getAllEmployee = catchAsyncErrors(async (req, res, next) => {
// Assuming your User model is imported as 'User' // Assuming your User model is imported as 'User'
const employee = await User.find({ role: "Employee" }); const employee = await User.find({ role: "Employee" });

View File

@ -13,11 +13,14 @@ const userSchema = new mongoose.Schema(
unique: true, unique: true,
required: true, required: true,
}, },
SBU: {
type: String,
required: [true, "Please Enter Your SBU"],
},
name: { name: {
type: String, type: String,
required: [true, "Please Enter Your Name"], required: [true, "Please Enter Your Name"],
maxLength: [30, "Name cannot exceed 30 characters"], maxLength: [30, "Name cannot exceed 30 characters"],
minLength: [4, "Name should have more than 4 characters"],
}, },
email: { email: {
type: String, type: String,
@ -55,6 +58,10 @@ const userSchema = new mongoose.Schema(
type: String, type: String,
default: "user", default: "user",
}, },
mappedby: {
type: mongoose.Schema.Types.ObjectId,
ref: "TerritoryManager",
},
accessTo: {}, accessTo: {},
resetPasswordToken: String, resetPasswordToken: String,
resetPasswordExpire: Date, resetPasswordExpire: Date,

View File

@ -15,6 +15,9 @@ import {
deleteEmployeeById, deleteEmployeeById,
updateEmployeeById, updateEmployeeById,
uploadPrincipaldistributors, uploadPrincipaldistributors,
getAllPrincipalDistributorbytmId,
mappedbyTM,
unmappedTMinPrincipalDistributor,
} from "./userController.js"; } from "./userController.js";
import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js";
@ -32,14 +35,34 @@ router.route("/user/logout").get(logout);
router.route("/user/details").get(isAuthenticatedUser, getUserDetails); router.route("/user/details").get(isAuthenticatedUser, getUserDetails);
router router
.route('/principaldistributor/upload').post( .route("/principaldistributor/upload")
.post(
isAuthenticatedUser, isAuthenticatedUser,
authorizeRoles('admin'), authorizeRoles("admin"),
uploadPrincipaldistributors uploadPrincipaldistributors
); );
router router
.route("/admin/users") .route("/admin/users")
.get(isAuthenticatedUser, authorizeRoles("admin", "Employee"), getAllUser); .get(isAuthenticatedUser, authorizeRoles("admin", "Employee"), getAllUser);
router
.route("/getbyTmId/:id")
.get(
isAuthenticatedUser,
authorizeRoles("admin"),
getAllPrincipalDistributorbytmId
);
router.put(
"/mappedtm/:id",
isAuthenticatedUser,
authorizeRoles("admin"),
mappedbyTM
);
router.patch(
"/unmap/:id",
isAuthenticatedUser,
authorizeRoles("admin"),
unmappedTMinPrincipalDistributor
);
router router
.route("/admin/delete-employee/:id") .route("/admin/delete-employee/:id")
.delete( .delete(