diff --git a/public/uploads/Add-SC.xlsx b/public/uploads/Add-SC.xlsx index 744e40a..eab142e 100644 Binary files a/public/uploads/Add-SC.xlsx and b/public/uploads/Add-SC.xlsx differ diff --git a/public/uploads/Add-TM.xlsx b/public/uploads/Add-TM.xlsx index 49e6ae6..1e7d554 100644 Binary files a/public/uploads/Add-TM.xlsx and b/public/uploads/Add-TM.xlsx differ diff --git a/resources/SalesCoOrdinators/SalesCoOrdinatorController.js b/resources/SalesCoOrdinators/SalesCoOrdinatorController.js index bef5b45..965df20 100644 --- a/resources/SalesCoOrdinators/SalesCoOrdinatorController.js +++ b/resources/SalesCoOrdinators/SalesCoOrdinatorController.js @@ -47,6 +47,7 @@ export const uploadSalesCoordinators = async (req, res) => { // Map headers from the Excel file to your schema const headerMapping = { + "Employee Code": "uniqueId", "Sales Coordinator Name": "name", Email: "email", "Phone Number": "mobileNumber", @@ -67,7 +68,7 @@ export const uploadSalesCoordinators = async (req, res) => { 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 === "")) { + if (row.every((cell) => cell === undefined || cell === "")) { continue; } const item = {}; @@ -84,6 +85,7 @@ export const uploadSalesCoordinators = async (req, res) => { const validationErrors = new Set(); // Validate required fields + if (!item.uniqueId) missingFields.add("Employee Code"); if (!item.name) missingFields.add("name"); if (!item.email) missingFields.add("email"); if (!item.mobileNumber) missingFields.add("mobileNumber"); @@ -93,9 +95,33 @@ export const uploadSalesCoordinators = async (req, res) => { validationErrors.add("incorrect mail"); } - // Validate mobile number - if (item.mobileNumber && !/^\d{10}$/.test(item.mobileNumber)) { - validationErrors.add("Invalid Mobile Number (should be 10 digits)"); + // Normalize the mobileNumber + if (item.mobileNumber) { + item.mobileNumber = item.mobileNumber.toString().trim(); + + // Check if it already has +91 + if (item.mobileNumber.startsWith("+91")) { + // If it has +91, remove it for validation + const strippedNumber = item.mobileNumber.substring(3); + + // Validate that the remaining number is 10 digits + if (/^\d{10}$/.test(strippedNumber)) { + // Keep the mobile number with +91 for storage + item.mobileNumber = `+91${strippedNumber}`; + } else { + validationErrors.add( + "Invalid Mobile Number (should be 10 digits after +91)" + ); + } + } else { + // If not prefixed with +91, check if it is exactly 10 digits + if (/^\d{10}$/.test(item.mobileNumber)) { + // Add +91 for storage + item.mobileNumber = `+91${item.mobileNumber}`; + } else { + validationErrors.add("Invalid Mobile Number (should be 10 digits)"); + } + } } // Combine all errors into a single message @@ -114,6 +140,7 @@ export const uploadSalesCoordinators = async (req, res) => { // If there are errors, push them to the errors array if (errorMessage.trim()) { errors.push({ + uniqueId: item.uniqueId || "N/A", name: item.name || "N/A", email: item.email || "N/A", phone: item.mobileNumber || "N/A", @@ -126,37 +153,75 @@ export const uploadSalesCoordinators = async (req, res) => { const password = generatePassword(item.name, item.email); // Check for existing user by uniqueId - let salesCoordinator = await SalesCoOrdinator.findOne({ - email: item.email, + let salesCoordinatorByUniqueId = await SalesCoOrdinator.findOne({ + uniqueId: item.uniqueId, }); - if (salesCoordinator) { - // Track updated fields - const updatedFields = []; + // Search for sales coordinator by mobile number + let salesCoordinatorByMobileNumber = await SalesCoOrdinator.findOne({ + $or: [ + { mobileNumber: item.mobileNumber }, // Check stored mobile number with +91 + { mobileNumber: item.mobileNumber.substring(3) }, // Check 10-digit number (remove +91) + ], + }); - // Check for changes in user details - let territoryManagerUpdated = false; - for (let field in item) { - const currentValue = salesCoordinator[field]?.toString(); - const newValue = item[field]?.toString(); + // Case 1: Both uniqueId and mobileNumber exist + if (salesCoordinatorByUniqueId && salesCoordinatorByMobileNumber) { + if ( + salesCoordinatorByUniqueId._id.equals( + salesCoordinatorByMobileNumber._id + ) + ) { + // Both match and are the same person, proceed to update + let salescoordinatorUpdated = false; - if (currentValue !== newValue) { - updatedFields.push(field); - salesCoordinator[field] = item[field]; - territoryManagerUpdated = true; + for (let field in item) { + const currentValue = salesCoordinatorByUniqueId[field]?.toString(); + const newValue = item[field]?.toString(); + + if (currentValue !== newValue) { + salesCoordinatorByUniqueId[field] = item[field]; + salescoordinatorUpdated = true; + } } - } - if (territoryManagerUpdated) { - await salesCoordinator.save(); - updatedsalesCoordinators.push({ - ...salesCoordinator._doc, - updatedFields: updatedFields.join(", "), + if (salescoordinatorUpdated) { + await salesCoordinatorByUniqueId.save(); + updatedsalesCoordinators.push({ + ...salesCoordinatorByUniqueId._doc, + updatedFields: updatedFields.join(", "), + }); + } + } else { + // Both exist but refer to different users + errors.push({ + uniqueId: item.uniqueId, + name: item.name, + email: item.email, + phone: item.mobileNumber, + message: ` Employee Code (${salesCoordinatorByUniqueId.uniqueId}) is refer to (${salesCoordinatorByUniqueId.name}) and Mobile Number (${salesCoordinatorByMobileNumber.mobileNumber}) refer to (${salesCoordinatorByMobileNumber.name}) Sales Coordinator. Please provide the correct employee code or mobile number.`, }); } + } else if (salesCoordinatorByUniqueId) { + // Case 2: uniqueId exists, but mobileNumber is new + salesCoordinatorByUniqueId.mobileNumber = item.mobileNumber; // Update mobile number + await salesCoordinatorByUniqueId.save(); + updatedsalesCoordinators.push({ + ...salesCoordinatorByUniqueId._doc, + updatedFields: "mobileNumber", + }); + } else if (salesCoordinatorByMobileNumber) { + // Case 3: mobileNumber exists but uniqueId is new + errors.push({ + uniqueId: item.uniqueId, + name: item.name, + email: item.email, + phone: item.mobileNumber, + message: `Mobile number already exists for ${salesCoordinatorByMobileNumber.name} user.`, + }); } else { - // Create a new salesCoordinator - salesCoordinator = new SalesCoOrdinator({ + // Case 4: Both uniqueId and mobileNumber are new, create a new salesCoordinator + const salesCoordinator = new SalesCoOrdinator({ ...item, password, isVerified: true, @@ -168,9 +233,9 @@ export const uploadSalesCoordinators = async (req, res) => { from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender subject: `Cheminova Account Created`, html: `Your Sales Coordinator Account is created successfully. -
name is: ${item?.name}
-
MobileNumber is: ${item?.mobileNumber}
-
password is: ${password}

If you have not requested this email, please ignore it.`, +
Name: ${item?.name}
+
Mobile Number: ${item?.mobileNumber}
+
Password: ${password}

If you have not requested this email, please ignore it.`, }); newlyCreated.push({ salesCoordinator }); } @@ -188,7 +253,8 @@ export const uploadSalesCoordinators = async (req, res) => { } }; export const register = async (req, res) => { - let { name, email, countryCode, mobileNumber, territoryManager } = req.body; + let { name, email, countryCode, mobileNumber, territoryManager, uniqueId } = + req.body; // console.log(req.body); countryCode = countryCode?.trim(); mobileNumber = mobileNumber?.trim(); @@ -196,6 +262,7 @@ export const register = async (req, res) => { try { let salesCoordinator = await SalesCoOrdinator.findOne({ + uniqueId, mobileNumber: fullMobileNumber, }); @@ -221,14 +288,9 @@ export const register = async (req, res) => { otp, otpExpires, mappedby: territoryManager, + uniqueId, }); } - // Generate uniqueId if not already present - if (!salesCoordinator.uniqueId) { - const currentYear = new Date().getFullYear().toString().slice(-2); - const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase(); - salesCoordinator.uniqueId = `${currentYear}-${randomChars}`; - } await salesCoordinator.save(); // await sendOtp( // fullMobileNumber, diff --git a/resources/Task/TaskController.js b/resources/Task/TaskController.js index 21f10af..a2c6f89 100644 --- a/resources/Task/TaskController.js +++ b/resources/Task/TaskController.js @@ -4,6 +4,8 @@ import cron from "node-cron"; import { sendPushNotification } from "../../Utils/sendPushNotification.js"; import SalesCoOrdinator from "../SalesCoOrdinators/SalesCoOrdinatorModel.js"; import TerritoryManager from "../TerritoryManagers/TerritoryManagerModel.js"; +import User from "../user/userModel.js"; +import RetailDistributor from "../RetailDistributor/RetailDistributorModel.js"; // Function to update task statuses export const updateOverdueTasks = async () => { try { @@ -362,3 +364,69 @@ export const updateTaskStatus = async (req, res) => { }); } }; + +// Controller to get today's tasks with pagination and conditional population +export const getTodaysTasks = async (req, res) => { + try { + // Get the current page and items per page (defaults to page 1, 10 items per page) + const currentPage = parseInt(req.query.page) || 1; + const itemsPerPage = parseInt(req.query.show) || 10; + + // Get today's date at midnight + const startOfToday = new Date(); + startOfToday.setHours(0, 0, 0, 0); // Set time to 00:00:00 + + // Get the end of today at 23:59:59 + const endOfToday = new Date(); + endOfToday.setHours(23, 59, 59, 999); // Set time to 23:59:59 + + // Calculate the number of items to skip + const skip = (currentPage - 1) * itemsPerPage; + + // Find tasks that are due today, with pagination + let tasksQuery = Task.find({ + taskDueDate: { + $gte: startOfToday, + $lte: endOfToday, + }, + }) + .populate('taskAssignedTo') // Optional: populate assigned coordinator details + .populate('taskAssignedBy') // Optional: populate assigned manager details + .skip(skip) // Skip documents for pagination + .limit(itemsPerPage); // Limit the number of documents + + // Modify the population based on the `addedFor` field value + tasksQuery = tasksQuery.populate({ + path: 'addedForId', + model: function (doc) { + return doc.addedFor === 'PrincipalDistributor' ? 'User' : 'RetailDistributor'; + }, + }); + + // Execute the query + const tasks = await tasksQuery.exec(); + + // Count the total number of tasks for pagination metadata + const totalTasks = await Task.countDocuments({ + taskDueDate: { + $gte: startOfToday, + $lte: endOfToday, + }, + }); + + // Calculate total pages + const totalPages = Math.ceil(totalTasks / itemsPerPage); + + // Send paginated tasks in response + res.status(200).json({ + tasks, // Paginated tasks + currentPage, // Current page number + itemsPerPage, // Number of tasks per page + totalTasks, // Total number of tasks + totalPages, // Total number of pages + }); + } catch (error) { + console.error('Error fetching today\'s tasks with pagination:', error); + res.status(500).json({ message: 'Failed to retrieve tasks for today' }); + } +}; \ No newline at end of file diff --git a/resources/Task/TaskRoute.js b/resources/Task/TaskRoute.js index d654595..4bc12ab 100644 --- a/resources/Task/TaskRoute.js +++ b/resources/Task/TaskRoute.js @@ -6,11 +6,12 @@ import { getTasksbytask, getAllTasksByStatus, getTasksByDates, + getTodaysTasks, } from "./TaskController.js"; import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; import { isAuthenticated_SC_TM } from "../../middlewares/generalAuth.js"; - +import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; const router = express.Router(); // Route for Territory Manager to assign a task @@ -31,5 +32,10 @@ router.put( isAuthenticatedSalesCoOrdinator, updateTaskStatus ); - +router.get( + "/today", + isAuthenticatedUser, + authorizeRoles("admin"), + getTodaysTasks +); export default router; diff --git a/resources/TerritoryManagers/TerritoryManagerController.js b/resources/TerritoryManagers/TerritoryManagerController.js index 3194761..e083a5e 100644 --- a/resources/TerritoryManagers/TerritoryManagerController.js +++ b/resources/TerritoryManagers/TerritoryManagerController.js @@ -47,6 +47,7 @@ export const uploadTerritoryManagers = async (req, res) => { // Map headers from the Excel file to your schema const headerMapping = { + "Employee Code": "uniqueId", "Territory Manager Name": "name", Email: "email", "Phone Number": "mobileNumber", @@ -84,6 +85,7 @@ export const uploadTerritoryManagers = async (req, res) => { const validationErrors = new Set(); // Validate required fields + if (!item.uniqueId) missingFields.add("Employee Code"); if (!item.name) missingFields.add("name"); if (!item.email) missingFields.add("email"); if (!item.mobileNumber) missingFields.add("mobileNumber"); @@ -93,9 +95,33 @@ export const uploadTerritoryManagers = async (req, res) => { validationErrors.add("incorrect mail"); } - // Validate mobile number - if (item.mobileNumber && !/^\d{10}$/.test(item.mobileNumber)) { - validationErrors.add("Invalid Mobile Number (should be 10 digits)"); + // Normalize the mobileNumber + if (item.mobileNumber) { + item.mobileNumber = item.mobileNumber.toString().trim(); + + // Check if it already has +91 + if (item.mobileNumber.startsWith("+91")) { + // If it has +91, remove it for validation + const strippedNumber = item.mobileNumber.substring(3); + + // Validate that the remaining number is 10 digits + if (/^\d{10}$/.test(strippedNumber)) { + // Keep the mobile number with +91 for storage + item.mobileNumber = `+91${strippedNumber}`; + } else { + validationErrors.add( + "Invalid Mobile Number (should be 10 digits after +91)" + ); + } + } else { + // If not prefixed with +91, check if it is exactly 10 digits + if (/^\d{10}$/.test(item.mobileNumber)) { + // Add +91 for storage + item.mobileNumber = `+91${item.mobileNumber}`; + } else { + validationErrors.add("Invalid Mobile Number (should be 10 digits)"); + } + } } // Combine all errors into a single message @@ -114,6 +140,7 @@ export const uploadTerritoryManagers = async (req, res) => { // If there are errors, push them to the errors array if (errorMessage.trim()) { errors.push({ + uniqueId: item.uniqueId || "N/A", name: item.name || "N/A", email: item.email || "N/A", phone: item.mobileNumber || "N/A", @@ -126,37 +153,73 @@ export const uploadTerritoryManagers = async (req, res) => { const password = generatePassword(item.name, item.email); // Check for existing user by uniqueId - let territoryManager = await TerritoryManager.findOne({ - email: item.email, + let territotymanagerByUniqueId = await TerritoryManager.findOne({ + uniqueId: item.uniqueId, }); + // Search for Territory Manager by mobile number + let territorymanagerByMobileNumber = await TerritoryManager.findOne({ + $or: [ + { mobileNumber: item.mobileNumber }, // Check stored mobile number with +91 + { mobileNumber: item.mobileNumber.substring(3) }, // Check 10-digit number (remove +91) + ], + }); + // Case 1: Both uniqueId and mobileNumber exist + if (territotymanagerByUniqueId && territorymanagerByMobileNumber) { + if ( + territotymanagerByUniqueId._id.equals( + territorymanagerByMobileNumber._id + ) + ) { + // Both match and are the same person, proceed to update + let territorymanagerUpdated = false; - if (territoryManager) { - // Track updated fields - const updatedFields = []; + for (let field in item) { + const currentValue = territotymanagerByUniqueId[field]?.toString(); + const newValue = item[field]?.toString(); - // Check for changes in user details - let territoryManagerUpdated = false; - for (let field in item) { - const currentValue = territoryManager[field]?.toString(); - const newValue = item[field]?.toString(); - - if (currentValue !== newValue) { - updatedFields.push(field); - territoryManager[field] = item[field]; - territoryManagerUpdated = true; + if (currentValue !== newValue) { + territotymanagerByUniqueId[field] = item[field]; + territorymanagerUpdated = true; + } } - } - if (territoryManagerUpdated) { - await territoryManager.save(); - updatedtrritoryManagers.push({ - ...territoryManager._doc, - updatedFields: updatedFields.join(", "), + if (territorymanagerUpdated) { + await territotymanagerByUniqueId.save(); + updatedtrritoryManagers.push({ + ...territotymanagerByUniqueId._doc, + updatedFields: updatedFields.join(", "), + }); + } + } else { + // Both exist but refer to different users + errors.push({ + uniqueId: item.uniqueId, + name: item.name, + email: item.email, + phone: item.mobileNumber, + message: ` Employee Code (${territotymanagerByUniqueId.uniqueId}) is refer to (${territotymanagerByUniqueId.name}) and Mobile Number (${territorymanagerByMobileNumber.mobileNumber}) refer to (${territorymanagerByMobileNumber.name}) Territory Manager. Please provide the correct employee code or mobile number.`, }); } + } else if (territotymanagerByUniqueId) { + // Case 2: uniqueId exists, but mobileNumber is new + territotymanagerByUniqueId.mobileNumber = item.mobileNumber; // Update mobile number + await territotymanagerByUniqueId.save(); + updatedtrritoryManagers.push({ + ...territotymanagerByUniqueId._doc, + updatedFields: "mobileNumber", + }); + } else if (territorymanagerByMobileNumber) { + // Case 3: mobileNumber exists but uniqueId is new + errors.push({ + uniqueId: item.uniqueId, + name: item.name, + email: item.email, + phone: item.mobileNumber, + message: `Mobile number already exists for ${territorymanagerByMobileNumber.name} user.`, + }); } else { - // Create a new territoryManager - territoryManager = new TerritoryManager({ + // Case 4: Both uniqueId and mobileNumber are new, create a new salesCoordinator + const territoryManager = new TerritoryManager({ ...item, password, isVerified: true, @@ -188,13 +251,14 @@ export const uploadTerritoryManagers = async (req, res) => { } }; export const register = async (req, res) => { - let { name, email, countryCode, mobileNumber } = req.body; + let { name, email, countryCode, mobileNumber, uniqueId } = req.body; countryCode = countryCode?.trim(); mobileNumber = mobileNumber?.trim(); const fullMobileNumber = `${countryCode}${mobileNumber}`; try { let territoryManager = await TerritoryManager.findOne({ + uniqueId, mobileNumber: fullMobileNumber, }); @@ -218,14 +282,9 @@ export const register = async (req, res) => { mobileNumber: fullMobileNumber, otp, otpExpires, + uniqueId, }); } - // Generate uniqueId if not already present - if (!territoryManager.uniqueId) { - const currentYear = new Date().getFullYear().toString().slice(-2); - const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase(); - territoryManager.uniqueId = `${currentYear}-${randomChars}`; - } await territoryManager.save(); // await sendOtp( // fullMobileNumber, @@ -390,7 +449,32 @@ export const getAllTerritoryManager = async (req, res) => { }); } }; +//for dropdown +export const getAllTerritoryManagerdropdown = async (req, res) => { + try { + let filter = {}; + if (req.query?.name) { + filter.name = { + $regex: new RegExp(req.query.name, "i"), + }; + } + const total = await TerritoryManager.countDocuments(filter); + const territoryManager = await TerritoryManager.find(filter).sort({ + createdAt: -1, + }); + return res.status(200).json({ + success: true, + total_data: total, + territoryManager, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went wrong!", + }); + } +}; export const getOneTerritoryManager = async (req, res) => { try { if (!req.params.id) { diff --git a/resources/TerritoryManagers/TerritoryManagerRoute.js b/resources/TerritoryManagers/TerritoryManagerRoute.js index 8c24a17..ded5955 100644 --- a/resources/TerritoryManagers/TerritoryManagerRoute.js +++ b/resources/TerritoryManagers/TerritoryManagerRoute.js @@ -17,6 +17,7 @@ import { getOneTerritoryManager, logout, uploadTerritoryManagers, + getAllTerritoryManagerdropdown, } from "./TerritoryManagerController.js"; import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; @@ -35,6 +36,12 @@ router.get( authorizeRoles("admin"), getAllTerritoryManager ); +router.get( + "/getAll-dropdown", + isAuthenticatedUser, + authorizeRoles("admin"), + getAllTerritoryManagerdropdown +); router.get( "/getOne/:id", isAuthenticatedUser, diff --git a/resources/user/userModel.js b/resources/user/userModel.js index e19c597..d9ce78c 100644 --- a/resources/user/userModel.js +++ b/resources/user/userModel.js @@ -43,7 +43,6 @@ const userSchema = new mongoose.Schema( email: { type: String, required: [true, "Please Enter Your Email"], - unique: true, validate: [validator.isEmail, "Please Enter a valid Email"], }, phone: {