import Task from "./TaskModel.js"; import crypto from "crypto"; 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 { 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(); // Fetch the Sales Coordinator who is assigned the task const salesCoordinator = await SalesCoOrdinator.findById( task.taskAssignedTo ); if (salesCoordinator) { const fcmToken = salesCoordinator.fcm_token; if (fcmToken) { // Send push notification const message = `Your task "${task.task}" is Pending.`; await sendPushNotification(fcmToken, "Task Status Updated", message); } } } 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("5 9 * * *", 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) => { try { const { task, note, taskPriority, taskDueDate, taskAssignedTo, addedFor, addedForId, tradename, } = 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 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: "New", taskPriority, taskDueDate: dueDate, // Save the date as a Date object taskAssignedTo, taskAssignedBy: req.user._id, addedFor, addedForId, tradename, }); // Fetch the FCM token of the assigned sales coordinator const salesCoordinator = await SalesCoOrdinator.findById(taskAssignedTo); if (!salesCoordinator) { return res.status(404).json({ success: false, message: "Sales Coordinator not found.", }); } const fcmToken = salesCoordinator.fcm_token; if (fcmToken) { // Send push notification const message = `You have been assigned a new task: ${task}`; await sendPushNotification(fcmToken, "New Task Assigned", message); } 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 getTasksByStatus = async (req, res) => { try { await updateOverdueTasks(); 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({ success: true, tasks, }); } catch (error) { res.status(500).json({ success: false, message: error.message, }); } }; export const getStartAndEndOfDay = (date) => { const startOfDay = new Date(date); startOfDay.setUTCHours(0, 0, 0, 0); // Start of the day UTC const endOfDay = new Date(date); endOfDay.setUTCHours(23, 59, 59, 999); // End of the day UTC return { startOfDay, endOfDay }; }; export const getTasksByDates = async (req, res) => { try { await updateOverdueTasks(); // Initialize filter object const filter = {}; console.log(req.userType); // Determine the filter based on user type if (req.userType === "SalesCoOrdinator") { filter.taskAssignedTo = req.user._id; // Use `=` to assign values, and `===` for comparison } else { filter.taskAssignedBy = req.user._id; } // Get the date from the query const { Date: queryDate } = req.query; let taskDate; // If date is provided in query, parse it; otherwise, use today's date if (queryDate) { taskDate = parseDate(queryDate); } else { // Get today's date in UTC taskDate = new Date(); } // Get the start and end of the day in UTC const { startOfDay, endOfDay } = getStartAndEndOfDay(taskDate); // Find tasks for the user, filtered by createdAt within the start and end of the day const tasks = await Task.find({ ...filter, // Use the filter object for querying createdAt: { $gte: startOfDay, $lte: endOfDay }, }) .populate({ path: req.userType === "SalesCoOrdinator" ? "taskAssignedBy" : "taskAssignedTo", // Change path based on user type select: "name mobileNumber email", }) .sort({ createdAt: -1 }); res.status(200).json({ success: true, tasks, }); } catch (error) { res.status(500).json({ success: false, message: error.message, }); } }; export const getAllTasksByStatus = async (req, res) => { try { await updateOverdueTasks(); 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 { await updateOverdueTasks(); 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) => { try { const { taskId } = req.params; 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(); // Fetch the Sales Coordinator who completed the task const salesCoordinator = await SalesCoOrdinator.findById(req.user._id); if (!salesCoordinator) { return res.status(404).json({ success: false, message: "Sales Coordinator not found.", }); } // Fetch the FCM token of the Territory Manager const territoryManager = await TerritoryManager.findById( task.taskAssignedBy ); if (!territoryManager) { return res.status(404).json({ success: false, message: "Territory Manager not found.", }); } const fcmToken = territoryManager.fcm_token; if (fcmToken) { // Send push notification const message = `Task "${task.task}" has been completed by ${salesCoordinator.name}.`; await sendPushNotification(fcmToken, "Task Completed", message); } res.status(200).json({ success: true, message: "Task status updated to Completed.", task, }); } catch (error) { res.status(400).json({ success: false, message: error.message, }); } }; // 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 const tasks = await Task.find({ createdAt: { $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 .exec(); // console.log(tasks); // Populate addedForId conditionally const populatedTasks = await Promise.all( tasks.map(async (task) => { if (task.addedFor === "PrincipalDistributor") { // Populate with PrincipalDistributor await task.populate("addedForId", User); } else if (task.addedFor === "RetailDistributor") { // Populate with RetailDistributor await task.populate("addedForId", RetailDistributor); } return task; // Return the populated task }) ); // 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: populatedTasks, // Paginated and populated 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" }); } }; export const getTasks = async (req, res) => { try { const { page = 1, show = 10, startDate = "", endDate = "", TMname = "", SCname = "", status = "", } = req.query; const limit = parseInt(show); const skip = (parseInt(page) - 1) * limit; const matchStage = {}; // Filter by date range if (startDate && endDate) { const start = new Date(startDate); const end = new Date(endDate); if (start.toDateString() === end.toDateString()) { matchStage.createdAt = { $gte: start, $lt: new Date(start).setDate(start.getDate() + 1), }; } else { matchStage.createdAt = { $gte: start, $lte: new Date(end).setDate(end.getDate() + 1), }; } // matchStage.createdAt = { // $gte: start, // $lte: new Date(end.setDate(end.getDate() + 1)), // }; } else if (startDate && endDate === "") { matchStage.createdAt = { $gte: new Date(startDate), $lte: new Date(), }; } else if (endDate && startDate === "") { matchStage.createdAt = { $lte: new Date(endDate), }; } // Filter by task status if (status) { matchStage.taskStatus = status; } // Aggregation pipeline const tasks = await Task.aggregate([ { $match: matchStage }, { $lookup: { from: "territorymanagers", localField: "taskAssignedBy", foreignField: "_id", as: "taskAssignedBy", }, }, { $lookup: { from: "salescoordinators", localField: "taskAssignedTo", foreignField: "_id", as: "taskAssignedTo", }, }, { $match: { ...(TMname && { "taskAssignedBy.name": { $regex: TMname, $options: "i" }, }), ...(SCname && { "taskAssignedTo.name": { $regex: SCname, $options: "i" }, }), }, }, { $unwind: "$taskAssignedBy" }, { $unwind: "$taskAssignedTo" }, { $sort: { createdAt: -1 } }, { $skip: skip }, { $limit: limit }, { $project: { taskId: 1, task: 1, taskStatus: 1, taskPriority: 1, taskDueDate: 1, note: 1, createdAt: 1, "taskAssignedBy.name": 1, "taskAssignedTo.name": 1, }, }, ]); // Total count const totalTasks = await Task.countDocuments(matchStage); res.status(200).json({ success: true, data: tasks, pagination: { total: totalTasks, page: parseInt(page), show: limit, pages: Math.ceil(totalTasks / limit), }, }); } catch (error) { console.error("Error fetching tasks:", error); res.status(500).json({ success: false, message: "Server error while fetching tasks", error: error.message, }); } };