diff --git a/resources/RetailDistributor/RetailDistributerRoutes.js b/resources/RetailDistributor/RetailDistributerRoutes.js index 402bc62..34a6d71 100644 --- a/resources/RetailDistributor/RetailDistributerRoutes.js +++ b/resources/RetailDistributor/RetailDistributerRoutes.js @@ -16,6 +16,7 @@ import { updateunmapRD, uploadRetaildistributors, updateretaildistributorwithKYC, + generateRetailerReport, } from "./RetailDistributorController.js"; import { isAuthenticatedRD } from "../../middlewares/rdAuth.js"; import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; @@ -25,6 +26,9 @@ const router = express.Router(); router .route("/retaildistributor/upload") .post(isAuthenticatedUser, authorizeRoles("admin"), uploadRetaildistributors); +router + .route("/retaildistributor/download-report") + .get(isAuthenticatedUser, authorizeRoles("admin"), generateRetailerReport); router.route("/rd-login").post(loginRD); router.route("/rd-get-me").get(isAuthenticatedRD, getmyProfileRD); router.post("/forgot-password", forgotPasswordRD); diff --git a/resources/RetailDistributor/RetailDistributorController.js b/resources/RetailDistributor/RetailDistributorController.js index 545e93c..c17bdd8 100644 --- a/resources/RetailDistributor/RetailDistributorController.js +++ b/resources/RetailDistributor/RetailDistributorController.js @@ -1843,3 +1843,105 @@ export const saveFCMTokenForRD = async (req, res) => { res.status(500).send("Internal Server Error"); } }; + +export const generateRetailerReport = async (req, res) => { + try { + // Fetch retailer data and populate required fields + const retailers = await RetailDistributor.find() + .populate("kyc", "trade_name pan_number aadhar_number gst_number state city district address pincode") + .populate("principal_distributer", "name") + .populate("mappedTM", "name") + .populate("mappedSC", "name") + .select("uniqueId name email mobile_number"); + + if (!retailers.length) { + return res.status(404).json({ message: "No retailers found." }); + } + + // Prepare data for the sheet + const retailerData = retailers.map((retailer) => ({ + "Retailer ID": retailer.uniqueId, + "Retailer Name": retailer.name, + Email: retailer.email, + "Mobile Number": retailer.mobile_number, + "Trade Name": retailer.kyc?.trade_name || "", + "PAN Number": retailer.kyc?.pan_number || "", + "Aadhaar Number": retailer.kyc?.aadhar_number || "", + "GST Number": retailer.kyc?.gst_number || "", + State: retailer.kyc?.state || "", + City: retailer.kyc?.city || "", + District: retailer.kyc?.district || "", + Address: retailer.kyc?.address || "", + Pincode: retailer.kyc?.pincode || "", + PrincipalDistributor: retailer.principal_distributer?.name || "N/A", + TerritoryManager: retailer.mappedTM?.name || "N/A", + SalesCoordinator: retailer.mappedSC?.name || "N/A", + })); + + // Define headers for the worksheet + const headers = [ + [ + "Retailer ID", + "Retailer Name", + "Email", + "Mobile Number", + "Trade Name", + "PAN Number", + "Aadhaar Number", + "GST Number", + "State", + "City", + "District", + "Address", + "Pincode", + "Principal Distributor", + "Territory Manager", + "Sales Coordinator", + ], + ]; + + // Create worksheet with headers + const worksheet = XLSX.utils.aoa_to_sheet(headers); + + // Append retailer data below headers + XLSX.utils.sheet_add_json(worksheet, retailerData, { + 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 retailerData) { + 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, "RetailerReport"); + + // 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=RetailerReport.xlsx" + ); + res.send(excelBuffer); + } catch (error) { + console.error("Error generating retailer report:", error); + res.status(500).json({ message: "Failed to generate report" }); + } +}; \ No newline at end of file diff --git a/resources/Task/TaskController.js b/resources/Task/TaskController.js index d7a8f77..8d3c0e2 100644 --- a/resources/Task/TaskController.js +++ b/resources/Task/TaskController.js @@ -385,7 +385,7 @@ export const getTodaysTasks = async (req, res) => { // Find tasks that are due today, with pagination const tasks = await Task.find({ - taskDueDate: { + createdAt: { $gte: startOfToday, $lte: endOfToday, }, @@ -395,7 +395,7 @@ export const getTodaysTasks = async (req, res) => { .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) => { @@ -434,3 +434,127 @@ export const getTodaysTasks = async (req, res) => { 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, + }); + } +}; \ No newline at end of file diff --git a/resources/Task/TaskRoute.js b/resources/Task/TaskRoute.js index 4bc12ab..5ab613e 100644 --- a/resources/Task/TaskRoute.js +++ b/resources/Task/TaskRoute.js @@ -7,6 +7,7 @@ import { getAllTasksByStatus, getTasksByDates, getTodaysTasks, + getTasks, } from "./TaskController.js"; import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; @@ -38,4 +39,10 @@ router.get( authorizeRoles("admin"), getTodaysTasks ); +router.get( + "/tasks", + isAuthenticatedUser, + authorizeRoles("admin"), + getTasks +); export default router; diff --git a/resources/user/userController.js b/resources/user/userController.js index 8ca1434..9018848 100644 --- a/resources/user/userController.js +++ b/resources/user/userController.js @@ -1149,3 +1149,132 @@ export const saveFCMTokenForUser = async (req, res) => { 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" }); + } +}; diff --git a/resources/user/userRoute.js b/resources/user/userRoute.js index 875a6c0..3b6e08c 100644 --- a/resources/user/userRoute.js +++ b/resources/user/userRoute.js @@ -23,6 +23,7 @@ import { getAllPrincipalDistributorbyscId, saveFCMTokenForUser, getAllPD, + generatePrincipalDistributorReport, } from "./userController.js"; import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; @@ -46,6 +47,13 @@ router authorizeRoles("admin"), uploadPrincipaldistributors ); + router + .route("/principaldistributor/download-report") + .get( + isAuthenticatedUser, + authorizeRoles("admin"), + generatePrincipalDistributorReport + ); //mapping start router .route("/admin/users")