import ErrorHander from "../../Utils/errorhander.js"; import catchAsyncErrors from "../../middlewares/catchAsyncErrors.js"; import User from "./userModel.js"; import sendToken from "../../Utils/jwtToken.js"; import sendEmail from "../../Utils/sendEmail.js"; import crypto from "crypto"; import cloudinary from "cloudinary"; import password from "secure-random-password"; import { Order } from "../Orders/orderModel.js"; import { RegisterEmail } from "../EmailCMS/RegisterEmail/registerEmailModal.js"; import { Config } from "../setting/Configration/Config_model.js"; import XLSX from "xlsx"; import fs from "fs"; import path from "path"; import validator from "validator"; import ShippingAddress from "../ShippingAddresses/ShippingAddressModel.js"; import { generatePassword } from "../../Utils/generatepassword.js"; import { PdOrder } from "../PD_Orders/pdOrderModal.js"; import bcrypt from "bcryptjs"; 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]; // Skip the row if it's completely empty if (row.every((cell) => cell === undefined || cell === "")) { continue; } 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 distributorbyid = await User.findOne({ uniqueId: item.uniqueId }); let distributorbymail = await User.findOne({ email: item.email }); // Case 1: Both uniqueId and mobileNumber exist if (distributorbyid && distributorbymail) { if (distributorbyid._id.equals(distributorbymail._id)) { // Track updated fields const updatedFields = []; const addressFields = [ "Name", "phoneNumber", "panNumber", "gstNumber", "state", "city", "street", "tradeName", "postalCode", ]; let existingAddress = await ShippingAddress.findOne({ user: distributorbyid._id, isDefault: true, }).exec(); if (!existingAddress) { existingAddress = await ShippingAddress.findOne({ user: distributorbyid._id, }) .sort({ createdAt: 1 }) // Get the first created address as fallback .exec(); } // Check for changes in user details let userUpdated = false; if (distributorbyid.name !== item.name) { updatedFields.push("name"); distributorbyid.name = item.name; userUpdated = true; } if (distributorbyid.email !== item.email) { updatedFields.push("email"); distributorbyid.email = item.email; userUpdated = true; } if (distributorbyid.SBU !== item.SBU) { updatedFields.push("SBU"); distributorbyid.SBU = item.SBU; userUpdated = true; } if (distributorbyid.phone !== item.phone.toString()) { updatedFields.push("phone"); distributorbyid.phone = item.phone; userUpdated = true; } // Update user if (userUpdated) { await distributorbyid.save(); } // Check for changes in address details const addressData = { Name: item.name, phoneNumber: item.phone.toString().trim(), street: item.street, city: item.city.trim(), state: item.state.trim(), postalCode: item.postalCode.toString(), country: "India", // Default country panNumber: item.panNumber, tradeName: item.tradeName, gstNumber: item.gstNumber, user: distributorbyid._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: distributorbyid._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({ ...distributorbyid._doc, updatedFields: updatedFields.join(", "), }); } } else { // Both exist but refer to different users 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: ` Employee Code (${distributorbyid.uniqueId}) is refer to (${distributorbyid.name}) and Email ID (${distributorbymail.email}) refer to (${distributorbymail.name}) Principal Distributor. Please provide the correct employee code or Email ID.`, }); } } else if (distributorbyid) { // Case 2: uniqueId exists, but Email is new distributorbyid.email = item.email; await distributorbyid.save(); updatedDistributors.push({ ...distributorbyid._doc, updatedFields: "Email", }); } else if (distributorbymail) { // Case 3: Email exists, but uniqueId is new 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: `Email already exists for Employee Code (${distributorbymail.uniqueId})`, }); } else { // Create a new user const 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(); await sendEmail({ to: distributor.email, from: process.env.SEND_EMAIL_FROM, subject: `Welcome to Cheminova - Account Created Successfully`, html: `
Dear ${distributor.name},
We are pleased to inform you that your Principal Distributor account has been successfully created. Please find your account details below:
Name: ${distributor.name}
Mobile Number: ${distributor.phone}
Email: ${distributor.email}
Password: ${distributor.password}
You can log in to your account using the following link:
For convenience, you can also download our mobile app from the following links:
If you have not requested this email or have any concerns, please contact our support team immediately.
Best regards,
Cheminova Support Team
`, }); // Now create the address for the new user const addressData = { Name: item.name, phoneNumber: item.phone, 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 isDefault: true, }; const newAddress = await ShippingAddress.create(addressData); // Push both the distributor and the addressData to the newlyCreated array newlyCreated.push({ distributor, address: newAddress, }); } } if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } res.status(200).json({ message: "File processed successfully", newlyCreated, updatedDistributors, errors, }); } catch (error) { console.error(error); // Clean up uploaded file if any error occurs if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } res.status(500).json({ message: "Internal Server Error" }); } }; // 1.Register a User export const registerUser = async (req, res) => { try { const { name, email, phone, accessTo, role, PD_ID, SBU } = req.body; // console.log(req.body); const password = generatePassword(name, email); // console.log(password); // Check if user already exists let user = await User.findOne({ uniqueId: PD_ID }); // console.log(user); if (user) { // If user exists, update their details if needed user.name = name; // user.password = password; // In a real application, you should hash this user.phone = phone; user.role = role; user.accessTo = accessTo; user.SBU = SBU; // Save updates await user.save(); // console.log("finduser", user); // Respond with success and userId return res.status(200).json({ success: true, message: "User updated successfully", userId: user._id, }); } // Create a new user if not found user = new User({ uniqueId: PD_ID, SBU, name, email, password, phone, role, accessTo, }); // console.log("user",user); // // Generate uniqueId // const currentYear = new Date().getFullYear().toString().slice(-2); // const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase(); // user.uniqueId = `${currentYear}-${randomChars}`; // Save the new user to the database await user.save(); // Send email with the new user details if (user.role === "principal-Distributor") { await sendEmail({ to: email, from: process.env.SEND_EMAIL_FROM, subject: `Welcome to Cheminova - Account Created Successfully`, html: `Dear ${name},
We are pleased to inform you that your Principal Distributor account has been successfully created. Please find your account details below:
Name: ${name}
Mobile Number: ${phone}
Email: ${email}
Password: ${password}
You can log in to your account using the following link:
For convenience, you can also download our mobile app from the following links:
If you have not requested this email or have any concerns, please contact our support team immediately.
Best regards,
Cheminova Support Team
`, }); } // Respond with success and userId res.status(201).json({ success: true, message: "User created successfully", userId: user._id, }); } catch (error) { console.error(error); res.status(400).json({ success: false, message: error.message }); } }; export const UpdateUser = async (req, res) => { try { const { name, email, phone, accessTo, role, PD_ID, SBU } = req.body; const id = req.params.id; // Check if ID is provided and valid if (!id) { return res .status(400) .json({ success: false, message: "User ID is required" }); } // Find the user by ID let user = await User.findById(id); if (!user) { return res .status(404) .json({ success: false, message: "User not found" }); } // Update user details only if provided user.name = name || user.name; user.email = email || user.email; user.phone = phone || user.phone; user.role = role || user.role; user.accessTo = accessTo || user.accessTo; user.SBU = SBU || user.SBU; user.uniqueId = PD_ID || user.uniqueId; // Save the updated user details to the database await user.save(); // If the user is a Principal Distributor, send an email if (user.role === "principal-Distributor" && email) { await sendEmail({ to: email, from: process.env.SEND_EMAIL_FROM, subject: `Welcome to Cheminova - Account Updated Successfully`, html: `Dear ${name || "User"},
We are pleased to inform you that your Principal Distributor account has been successfully updated. Please find your account details below:
Name: ${name || "N/A"}
Mobile Number: ${phone || "N/A"}
Email: ${email || "N/A"}
Your password remains the same as before.
You can log in to your account using the following link:
For convenience, you can also download our mobile app from the following link:
If you did not request this update or have any concerns, please contact our support team immediately.
Best regards,
Cheminova Support Team
`, }); } // Respond with success res.status(200).json({ success: true, message: "User updated successfully", userId: user._id, }); } catch (error) { console.error("Error updating user:", error); res.status(500).json({ success: false, message: "Internal Server Error" }); } }; // 2.Login User export const loginUser = async (req, res, next) => { const { email, password } = req.body; // checking if user has given password and email both try { if (!email || !password) { return res.status(400).json({ message: "Please Enter Email & Password" }); } const user = await User.findOne({ email }).select("+password"); if (!user) { return res.status(400).json({ message: "Invalid Email or Password" }); } const isPasswordMatched = await user.comparePassword(password); if (!isPasswordMatched) { return res.status(400).json({ message: "Invalid Email or Password" }); } sendToken(user, 200, res); } catch (error) { return res .status(500) .json({ message: "Something went wrong!", error: error?.message || "" }); } }; // 3.Logout User export const logout = catchAsyncErrors(async (req, res, next) => { res.cookie("token", null, { expires: new Date(Date.now()), httpOnly: true, }); res.status(200).json({ success: true, message: "Logged Out", }); }); // 4.Forgot Password export const forgotPassword = async (req, res, next) => { const user = await User.findOne({ email: req.body.email }); if (!user) { return res.status(404).json({ message: "User not found" }); } // Get ResetPassword Token const resetToken = user.getResetPasswordToken(); //call function //save database reset token await user.save({ validateBeforeSave: false }); const passwords = password.randomPassword({ length: 12, characters: [ { characters: password.upper, exactly: 1 }, { characters: password.symbols, exactly: 1 }, password.lower, password.digits, ], }); user.password = passwords; await user.save(); // const message = `Your password reset token are :- \n\n ${resetPasswordUrl} \n\nyour new password is:${password}\n\nIf you have not requested this email then, please ignore it.`; try { await sendEmail({ to: `${user.email}`, // Change to your recipient from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender subject: `Cheminova Password Recovery`, html: `your new password is:Dear ${user.name},
Your Principal Distributor account credentials have been updated. Please find your new login details below:
Email: ${user.email}
Password: ${newPassword}
Please use these credentials to log in to your account. For security reasons, it's recommended to change your password after logging in.
To log in via our website, please click here.
To download our mobile app, use the following links:
If you did not request this change, please contact our support team immediately.
Best regards,
Cheminova Support Team
`, }); // console.log(user); res.status(200).json({ success: true, message: `Account credentials sent to ${user.email} successfully.`, }); } catch (error) { console.error("Error resetting password:", error); res.status(500).json({ success: false, message: error.message || "Something went wrong while resetting the password.", }); } }; // 5.Reset Password export const resetPassword = catchAsyncErrors(async (req, res, next) => { // creating token hash const resetPasswordToken = crypto .createHash("sha256") .update(req.params.token) .digest("hex"); const user = await User.findOne({ resetPasswordToken, resetPasswordExpire: { $gt: Date.now() }, }); if (!user) { return next( new ErrorHander( "Reset Password Token is invalid or has been expired", 400 ) ); } //replace previous password if (req.body.password !== req.body.confirmPassword) { return next(new ErrorHander("Password does not password", 400)); } user.password = req.body.password; user.resetPasswordToken = undefined; user.resetPasswordExpire = undefined; await user.save(); sendToken(user, 200, res); }); //6.Get User Detail export const getUserDetails = catchAsyncErrors(async (req, res, next) => { const user = await User.findById(req.user?._id); res.status(200).json({ success: true, user, }); }); // export const getUserDetailsForAdmin = catchAsyncErrors( // async (req, res, next) => { // const user = await User.findById(req.params._id); // res.status(200).json({ // success: true, // user, // }); // } // ); // export const getAllUsers = catchAsyncErrors(async (req, res, next) => { // const users = await User.find().populate("orders"); // Assuming orders are stored in a separate collection and populated in the User model // // Process user data to calculate last purchase date and order count // const usersWithInfo = users.map((user) => { // const lastPurchase = // user.orders.length > 0 // ? user.orders[user.orders.length - 1].createdAt // : null; // const orderCount = user.orders.length; // return { ...user.toJSON(), lastPurchase, orderCount }; // }); // res.status(200).json({ // success: true, // users: usersWithInfo, // }); // }); // 7.Get single user (admin) export const getSingleUser = catchAsyncErrors(async (req, res, next) => { if (!req.params.id) { return next(new ErrorHander(`please send User ID`, 404)); } const user = await User.findById(req.params.id); if (!user) { return next( new ErrorHander(`User does not exist with Id: ${req.params.id}`, 404) ); } res.status(200).json({ success: true, user, }); }); export const getUserOrderForAdmin = async (req, res) => { const id = req.params.id; // console.log(id); try { const order = await Order.find({ user: id, // payment_status: "success", }).sort({ createdAt: -1 }); if (order) { return res.status(200).json({ success: true, order, message: "self Order fetched", }); } } catch (error) { res.status(500).json({ success: false, message: error.message ? error.message : "Something went Wrong", }); } }; // 8.update User password export const updatePassword = catchAsyncErrors(async (req, res, next) => { const user = await User.findById(req.user._id).select("+password"); const isPasswordMatched = await user.comparePassword(req.body.oldPassword); if (!isPasswordMatched) { return res.status(400).json({ message: "Old password is incorrect" }); } if (req.body.newPassword !== req.body.confirmPassword) { return res .status(400) .json({ message: "New Password and Confirm Password do not match" }); } user.password = req.body.newPassword; await user.save(); sendToken(user, 200, res); }); // 9.update User Profile export const updateProfile = catchAsyncErrors(async (req, res, next) => { const newUserData = { name: req.body.name, phone: req.body.phone, email: req.body.email, }; // if (req.files) { // const userImage = req.files?.avatar; // const user = await User.findById(req.user.id); // if (user?.avatar) { // const imageId = user?.avatar?.public_id; // await cloudinary.uploader.destroy(imageId) // } // const myCloud = await cloudinary.v2.uploader.upload(userImage.tempFilePath, // { // folder: "Cheminova/user-image", // }); // newUserData.avatar = { // public_id: myCloud.public_id, // url: myCloud.secure_url, // }; // } const user = await User.findByIdAndUpdate(req.user.id, newUserData, { new: true, runValidators: true, useFindAndModify: false, }); return res.status(200).json({ success: true, user, }); }); // 9.Get all users(admin) export const getAllUser = catchAsyncErrors(async (req, res, next) => { try { const { page = 1, show = 10, name, mobileNumber, SBU } = req.query; const filter = { role: "principal-Distributor" }; if (name) { filter.name = { $regex: name, $options: "i" }; } if (mobileNumber) { filter.phone = mobileNumber; } if (SBU) { filter.SBU = { $regex: SBU, $options: "i" }; } const limit = parseInt(show, 10); const skip = (parseInt(page, 10) - 1) * limit; const users = await User.find(filter) .populate("mappedby", "name") .populate("mappedbySC", "name") .sort({ createdAt: -1 }) .skip(skip) .limit(limit); const totalUsers = await User.countDocuments(filter); res.status(200).json({ success: true, users, total_data: totalUsers, total_pages: Math.ceil(totalUsers / limit), }); } catch (error) { console.error(error); res.status(500).json({ message: "Server Error", error }); } }); export const getAllPD = catchAsyncErrors(async (req, res, next) => { try { const { page = 1, show = 10, name, mobileNumber, SBU } = req.query; const filter = { role: "principal-Distributor" }; if (name) { filter.name = { $regex: name, $options: "i" }; } if (mobileNumber) { filter.phone = mobileNumber; } if (SBU) { filter.SBU = { $regex: SBU, $options: "i" }; } const limit = parseInt(show, 10); const skip = (parseInt(page, 10) - 1) * limit; // Fetch users with the given filter and pagination const users = await User.find(filter) .populate("mappedby", "name") .populate("mappedbySC", "name") .sort({ createdAt: -1 }) .skip(skip) .limit(limit); // Fetch total count of users matching the filter const totalUsers = await User.countDocuments(filter); // Aggregate orders data for each user const orderStats = await PdOrder.aggregate([ { $match: { addedBy: { $in: users.map((user) => user._id) } } }, // Match orders for the listed distributors { $group: { _id: "$addedBy", // Group by addedBy (distributor ID) totalOrders: { $sum: 1 }, // Calculate total number of orders lastOrderDate: { $max: "$createdAt" }, // Get the date of the last order }, }, ]); // Map orders data to corresponding users const usersWithOrderStats = users.map((user) => { const orderData = orderStats.find( (order) => order._id.toString() === user._id.toString() ); return { ...user.toObject(), totalOrders: orderData ? orderData.totalOrders : 0, lastOrderDate: orderData ? orderData.lastOrderDate : null, }; }); res.status(200).json({ success: true, users: usersWithOrderStats, total_data: totalUsers, total_pages: Math.ceil(totalUsers / limit), }); } catch (error) { console.error("Error fetching Principal Distributors:", error); res.status(500).json({ message: "Server Error", error }); } }); 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 getAllPrincipalDistributorbyscId = catchAsyncErrors( async (req, res, next) => { const { page = 1, show = 10, name, mobileNumber } = req.query; const scId = req.params.id; if (!scId) { return res .status(400) .json({ message: "Please provide Sales coordinator 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; } if (scId) { filter.mappedbySC = scId; } // 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 mappedbySC = catchAsyncErrors(async (req, res, next) => { const { id } = req.params; // SalesCoOrdinator ID from URL parameters const { mappedbySC } = req.body; // TerritoryManager ID from request body // console.log(id, mappedbySC); // Validate that the TerritoryManager ID is provided if (!mappedbySC) { return res.status(400).json({ success: false, message: "Sales Coordinator 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 mappedbySC field principalDistributor.mappedbySC = mappedbySC; 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 unmappedSCinPrincipalDistributor = 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 mappedbySC field principalDistributor.mappedbySC = 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) => { // Assuming your User model is imported as 'User' const employee = await User.find({ role: "Employee" }); res.status(200).json({ success: true, employee, }); }); export const deleteEmployeeById = catchAsyncErrors(async (req, res, next) => { // console.log("request came here", req.params); // Extract the employee ID from the request parameters const { id } = req.params; try { // Find the employee by ID and delete it const deletedEmployee = await User.findByIdAndDelete(id); if (!deletedEmployee) { // If the employee with the provided ID is not found, return an error return res.status(404).json({ success: false, message: "Employee not found", }); } // If deletion is successful, return success response res.status(200).json({ success: true, message: "Employee deleted successfully", }); } catch (error) { // Handle any errors that occur during deletion return res.status(500).json({ success: false, message: "Error deleting employee", error: error.message, }); } }); // Update employee // Import necessary modules and set up your User model export const updateEmployeeById = catchAsyncErrors(async (req, res, next) => { // Extract the employee ID from the request parameters const { id } = req.params; try { // Find the employee by ID and update its fields const updatedEmployee = await User.findByIdAndUpdate( id, { $set: req.body }, // Update fields based on the request body { new: true } // Return the updated document ); if (!updatedEmployee) { // If the employee with the provided ID is not found, return an error return res.status(404).json({ success: false, message: "Employee not found", }); } // If update is successful, return success response with updated employee data res.status(200).json({ success: true, message: "Employee updated successfully", employee: updatedEmployee, }); } catch (error) { // Handle any errors that occur during update return res.status(500).json({ success: false, message: "Error updating employee", error: error.message, }); } }); export const saveFCMTokenForUser = async (req, res) => { const { fcmToken } = req.body; const userId = req.user._id; try { // Fetch the current FCM token for the user const user = await User.findById(userId); if (!user) { return res.status(404).send("User not found"); } // Check if the new FCM token is different from the current one if (user.fcm_token && user.fcm_token === fcmToken) { return res.status(200).send("FCM Token is already up to date"); } // Update the FCM token user.fcm_token = fcmToken; await user.save(); res.status(200).send("FCM Token saved successfully"); } catch (error) { console.error("Error saving FCM Token:", error); res.status(500).send("Internal Server Error"); } }; export const generatePrincipalDistributorReport = async (req, res) => { try { // Fetch users with role 'principal-distributor' const distributors = await User.find({ role: "principal-Distributor" }) .select("uniqueId SBU name email phone") .populate("mappedby", "name") // Territory Manager .populate("mappedbySC", "name") // Sales Coordinator .lean(); if (!distributors.length) { return res .status(404) .json({ message: "No principal distributors found." }); } // Prepare data for the sheet const distributorData = []; for (const distributor of distributors) { // Fetch shipping addresses associated with the distributor (both default and non-default) const addresses = await ShippingAddress.find({ user: distributor._id, }).lean(); // Determine the address to use (first default or first address if none default) let selectedAddress = null; // Find the first default address, otherwise use the first address const defaultAddress = addresses.find( (address) => address.isDefault === true ); if (defaultAddress) { selectedAddress = defaultAddress; } else { selectedAddress = addresses[0]; // If no default, take the first address } // If we have a valid address, proceed with adding it to the report if (selectedAddress) { const addressDetails = { "Unique ID": distributor.uniqueId, SBU: distributor.SBU, "Principal Distributor Name": distributor.name, Email: distributor.email, "Mobile Number": distributor.phone, "Trade Name": selectedAddress.tradeName || "N/A", "PAN Number": selectedAddress.panNumber || "N/A", "GST Number": selectedAddress.gstNumber || "N/A", State: selectedAddress.state || "N/A", City: selectedAddress.city || "N/A", Street: selectedAddress.street || "N/A", Pincode: selectedAddress.postalCode || "N/A", "Territory Manager": distributor.mappedby?.name || "N/A", "Sales Coordinator": distributor.mappedbySC?.name || "N/A", }; distributorData.push(addressDetails); } } // Define headers for the worksheet const headers = [ [ "Unique ID", "SBU", "Principal Distributor Name", "Email", "Mobile Number", "Trade Name", "PAN Number", "GST Number", "State", "City", "Street", "Pincode", "Territory Manager", "Sales Coordinator", ], ]; // Create worksheet with headers const worksheet = XLSX.utils.aoa_to_sheet(headers); // Append distributor data below headers XLSX.utils.sheet_add_json(worksheet, distributorData, { skipHeader: true, origin: "A2", }); // Set column widths based on content length const columnWidths = headers[0].map((header, index) => { let maxLength = header.length; for (const row of distributorData) { const cellContent = row[header]?.toString() || ""; maxLength = Math.max(maxLength, cellContent.length); } return { wch: maxLength + 2 }; }); worksheet["!cols"] = columnWidths; // Create workbook and append worksheet const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet( workbook, worksheet, "Principal_Distributor_Report" ); // Write workbook to a buffer const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer", }); // Send Excel file as response res.setHeader( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ); res.setHeader( "Content-Disposition", "attachment; filename=Principal_Distributor_Report.xlsx" ); res.send(excelBuffer); } catch (error) { console.error("Error generating report:", error); res.status(500).json({ message: "Failed to generate report" }); } };