diff --git a/app.js b/app.js index b5caf77..77f5d9e 100644 --- a/app.js +++ b/app.js @@ -188,7 +188,7 @@ import leave from "./resources/Leaves/LeaveRoute.js"; import notification from "./resources/Notification/notificationRoute.js" //Inventory import InventoryRoute from "./resources/Inventory/InventoryRoute.js"; - +import TaskRoute from "./resources/Task/TaskRoute.js"; app.use("/api/v1", user); //Product @@ -259,6 +259,9 @@ app.use("/api",notification) //Inventory app.use("/api/inventory", InventoryRoute); +//Task +app.use("/api/task", TaskRoute); + //config specialty // app.use("/api/config/specialty", SpecialtiesRouter); //specialties diff --git a/resources/SalesCoOrdinators/SalesCoOrdinatorController.js b/resources/SalesCoOrdinators/SalesCoOrdinatorController.js index 368c17e..4dbdd3a 100644 --- a/resources/SalesCoOrdinators/SalesCoOrdinatorController.js +++ b/resources/SalesCoOrdinators/SalesCoOrdinatorController.js @@ -7,7 +7,8 @@ import password from "secure-random-password"; import catchAsyncErrors from "../../middlewares/catchAsyncErrors.js"; export const register = async (req, res) => { - let { name, email, countryCode, mobileNumber } = req.body; + let { name, email, countryCode, mobileNumber,territoryManager } = req.body; + // console.log(req.body); countryCode = countryCode?.trim(); mobileNumber = mobileNumber?.trim(); const fullMobileNumber = `${countryCode}${mobileNumber}`; @@ -30,6 +31,7 @@ export const register = async (req, res) => { if (salesCoordinator) { salesCoordinator.otp = otp; salesCoordinator.otpExpires = otpExpires; + salesCoordinator.mappedby = territoryManager; } else { salesCoordinator = new SalesCoOrdinator({ name, @@ -37,6 +39,7 @@ export const register = async (req, res) => { mobileNumber: fullMobileNumber, otp, otpExpires, + mappedby: territoryManager, }); } // Generate uniqueId if not already present diff --git a/resources/SalesCoOrdinators/SalesCoOrdinatorModel.js b/resources/SalesCoOrdinators/SalesCoOrdinatorModel.js index b2a001b..5bdf601 100644 --- a/resources/SalesCoOrdinators/SalesCoOrdinatorModel.js +++ b/resources/SalesCoOrdinators/SalesCoOrdinatorModel.js @@ -51,6 +51,11 @@ const salescoordinatorSchema = new mongoose.Schema( type: String, unique: true, }, + mappedby: { + type: mongoose.Schema.Types.ObjectId, + ref: "TerritoryManager", + required: true, + }, }, { timestamps: true } ); diff --git a/resources/SalesCoOrdinators/SalesCoOrdinatorRoute.js b/resources/SalesCoOrdinators/SalesCoOrdinatorRoute.js index 190fadf..b46d5cb 100644 --- a/resources/SalesCoOrdinators/SalesCoOrdinatorRoute.js +++ b/resources/SalesCoOrdinators/SalesCoOrdinatorRoute.js @@ -18,6 +18,7 @@ import { logout, } from "./SalesCoOrdinatorController.js"; import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; +import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; router.post("/register", register); @@ -31,6 +32,11 @@ router.get( authorizeRoles("admin"), getAllSalesCoOrdinator ); +router.get( + "/getAll-TM", + isAuthenticatedTerritoryManager, + getAllSalesCoOrdinator +); router.get( "/getOne/:id", isAuthenticatedUser, diff --git a/resources/Task/TaskController.js b/resources/Task/TaskController.js new file mode 100644 index 0000000..e04a838 --- /dev/null +++ b/resources/Task/TaskController.js @@ -0,0 +1,92 @@ +import Task from "./TaskModel.js"; +import SalesCoOrdinator from "../SalesCoOrdinators/SalesCoOrdinatorModel.js"; +import crypto from "crypto"; + +export const assignTask = async (req, res) => { + try { + const { + task, + note, + taskPriority, + taskDueDate, + taskAssignedTo, + addedFor, + addedForId, + } = req.body; + // console.log(req.body); + const currentYear = new Date().getFullYear().toString().slice(-2); + const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase(); + const uniqueId = `${currentYear}-${randomChars}`; + // Create a new task + const newTask = await Task.create({ + taskId: uniqueId, + task, + note, + taskStatus: "Pending", + taskPriority, + taskDueDate, + taskAssignedTo, + taskAssignedBy: req.user._id, // The Territory Manager's ID + addedFor, + addedForId, + }); + + res.status(201).json({ + success: true, + message: "Task assigned successfully", + task: newTask, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: error.message, + }); + } +}; + +export const getTasksForSalesCoordinator = async (req, res) => { + try { + const tasks = await Task.find({ taskAssignedTo: req.user._id }); + + res.status(200).json({ + success: true, + tasks, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: error.message, + }); + } +}; + +export const updateTaskStatus = async (req, res) => { + try { + const { taskId } = req.params; + + // Find the task to ensure it belongs to the logged-in Sales Coordinator + const task = await Task.findOne({ _id: taskId, taskAssignedTo: req.user._id }); + + if (!task) { + return res.status(404).json({ + success: false, + message: "Task not found or you're not authorized to update this task.", + }); + } + + // Update the task status to "Completed" + task.taskStatus = "Completed"; + await task.save(); + + res.status(200).json({ + success: true, + message: "Task status updated to Completed.", + task, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: error.message, + }); + } +}; \ No newline at end of file diff --git a/resources/Task/TaskModel.js b/resources/Task/TaskModel.js new file mode 100644 index 0000000..50748ce --- /dev/null +++ b/resources/Task/TaskModel.js @@ -0,0 +1,68 @@ +import dotenv from "dotenv"; +dotenv.config(); +import mongoose from "mongoose"; + +const TaskSchema = new mongoose.Schema( + { + taskId: { + type: String, + unique: true, + required: true, + }, + task: { + type: String, + required: true, + enum: ["Visit Retailers", "Update Sales Data", "Update Inventory Data", "Collect KYC"], // Restrict to specific tasks + }, + note: { + type: String, + required: function () { + return this.task === "Collect KYC"; + }, + }, + taskStatus: { + type: String, + required: true, + enum: ["Pending", "In Progress", "Completed"], + }, + taskPriority: { + type: String, + required: true, + enum: ["Low", "Medium", "High"], + }, + taskDueDate: { + type: String, + required: true, + match: /^\d{2}\/\d{2}\/\d{4}$/, + }, + taskAssignedTo: { + type: mongoose.Schema.Types.ObjectId, + ref: "SalesCoOrdinator", + required: true, + }, + taskAssignedBy: { + type: mongoose.Schema.Types.ObjectId, + ref: "TerritoryManager", + required: true, + }, + addedFor: { + type: String, + enum: ['PrincipalDistributor', 'RetailDistributor'], + required: function () { + return this.task === "Update Inventory Data"; + }, + }, + addedForId: { + type: mongoose.Schema.Types.ObjectId, + refPath: 'addedFor', + required: function () { + return this.task === "Update Inventory Data"; + }, + }, + }, + { timestamps: true } +); + +const Task = mongoose.model("Task", TaskSchema); + +export default Task; diff --git a/resources/Task/TaskRoute.js b/resources/Task/TaskRoute.js new file mode 100644 index 0000000..f080dec --- /dev/null +++ b/resources/Task/TaskRoute.js @@ -0,0 +1,32 @@ +import express from "express"; +import { + assignTask, + getTasksForSalesCoordinator, + updateTaskStatus, +} from "./TaskController.js"; +import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; +import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; + +const router = express.Router(); + +// Route for Territory Manager to assign a task +router.post( + "/assign-task", + isAuthenticatedTerritoryManager, + assignTask +); + +// Route for Sales Coordinator to view their tasks +router.get( + "/tasks", + isAuthenticatedSalesCoOrdinator, + getTasksForSalesCoordinator +); + +router.put( + "/update-task-status/:taskId", + isAuthenticatedSalesCoOrdinator, + updateTaskStatus +); + +export default router;