From 7e169d79734cc39f8d5026d86b96240cf24240e2 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Mon, 22 Jul 2024 20:20:49 +0530 Subject: [PATCH] attendance and leave api created successfully --- app.js | 4 + resources/Attendance/AttendanceController.js | 361 +++++++------------ resources/Attendance/AttendanceModel.js | 33 +- resources/Leaves/LeaveController.js | 250 +++++++++++++ resources/Leaves/LeaveModel.js | 43 +++ resources/Leaves/LeaveRoute.js | 60 +++ 6 files changed, 495 insertions(+), 256 deletions(-) create mode 100644 resources/Leaves/LeaveController.js create mode 100644 resources/Leaves/LeaveModel.js create mode 100644 resources/Leaves/LeaveRoute.js diff --git a/app.js b/app.js index 46fb2b8..9cc6e6e 100644 --- a/app.js +++ b/app.js @@ -171,6 +171,8 @@ import CouponRoute from "./resources/Affiliate&Coupon/Coupon/CouponRoute.js"; //support Ticket // attandance import attendance from "./resources/Attendance/AttandanceRoute.js" +//leave +import leave from "./resources/Leaves/LeaveRoute.js"; app.use("/api/v1", user); //Product @@ -228,6 +230,8 @@ app.use("/api/v1/coupon", CouponRoute); app.use("/api/v1/blog", BlogRoute); //attendance app.use("/api/v1", attendance); +//leave +app.use("/api/v1", leave); //config specialty // app.use("/api/config/specialty", SpecialtiesRouter); diff --git a/resources/Attendance/AttendanceController.js b/resources/Attendance/AttendanceController.js index 4feddb6..c016460 100644 --- a/resources/Attendance/AttendanceController.js +++ b/resources/Attendance/AttendanceController.js @@ -1,229 +1,113 @@ -// import { Attendance } from "./AttandanceModel.js"; +import { Attendance } from "./AttendanceModel.js"; +// Mark attendance // export const markAttendance = async (req, res) => { // try { // const { date, time, location, notes } = req.body; -// const salesCoordinatorId = req.user._id; +// const userId = req.user._id; +// const userType = req.userType; -// // Get the start and end of the day to check if the attendance is already marked -// const startOfDay = new Date(date); -// startOfDay.setHours(0, 0, 0, 0); - -// const endOfDay = new Date(date); -// endOfDay.setHours(23, 59, 59, 999); - -// // Check if the attendance record exists for today -// const existingAttendance = await AttendanceSalesCoOrdinator.findOne({ -// salesCoordinator: salesCoordinatorId, -// "records.date": { $gte: startOfDay, $lte: endOfDay }, -// }); - -// if (existingAttendance) { +// // Parse the input date +// const [year, month, day] = date.split("/"); +// const parsedDate = new Date(Date.UTC(year, month - 1, day)); +// const today = new Date(); +// today.setUTCHours(0, 0, 0, 0); +// // Check if the date in req.body is not today's date +// if (parsedDate.getTime() !== today.getTime()) { // return res.status(400).json({ // success: false, -// message: "Attendance for today is already marked.", +// message: `Leave can only be marked for today's date: ${ +// today.toISOString().split("T")[0] +// }`, // }); // } +// // Get the start and end of the day to check if the leave is already marked +// const startOfDay = new Date(parsedDate); +// const endOfDay = new Date(parsedDate); +// endOfDay.setUTCHours(23, 59, 59, 999); -// // Check if attendance record exists for the sales coordinator -// let attendance = await AttendanceSalesCoOrdinator.findOne({ -// salesCoordinator: salesCoordinatorId, +// // Check if the attendance record exists for today +// const existingAttendance = await Attendance.findOne({ +// userId, +// userType, +// records: { +// $elemMatch: { +// date: { $gte: startOfDay, $lte: endOfDay }, +// }, +// }, // }); +// if (existingAttendance) { +// return res.status(400).json({ +// success: false, +// message: "Attendance for today is already marked.", +// }); +// } + +// // Check if attendance record exists for the user +// let attendance = await Attendance.findOne({ userId, userType }); + // if (!attendance) { // // Create a new attendance record if it doesn't exist -// attendance = new AttendanceSalesCoOrdinator({ -// salesCoordinator: salesCoordinatorId, +// attendance = new Attendance({ +// userId, +// userType, // records: [], // }); // } // // Add the new attendance record to the array -// attendance.records.push({ date, time, location, notes }); +// attendance.records.push({ date: startOfDay, time, location, notes }); // await attendance.save(); // res.status(201).json({ // success: true, // message: "Attendance marked successfully", -// record: { date, time, location, notes }, +// record: { date: today, time, location, notes }, // }); // } catch (error) { +// console.error("Error marking attendance:", error); // res.status(500).json({ // success: false, // message: error.message || "Something went wrong", // }); // } // }; - -// export const getAttendanceBySalesCoordinator = async (req, res) => { -// try { -// const salesCoordinatorId = req.user._id; - -// const attendance = await AttendanceSalesCoOrdinator.findOne({ -// salesCoordinator: salesCoordinatorId, -// }).populate("salesCoordinator", "name email"); - -// if (!attendance) { -// return res.status(404).json({ -// success: false, -// message: "No attendance records found for this sales coordinator.", -// }); -// } - -// res.status(200).json({ -// success: true, -// attendance, -// }); -// } catch (error) { -// res.status(500).json({ -// success: false, -// message: error.message || "Something went wrong", -// }); -// } -// }; -// // admin -// export const AdmingetAttendanceBySalesCoordinator = async (req, res) => { -// try { -// const { id } = req.params; -// const { page = 1, show = 10 } = req.query; -// const limit = parseInt(show); -// const skip = (page - 1) * limit; - -// // Find attendance records for the given sales coordinator -// const attendanceDoc = await AttendanceSalesCoOrdinator.findOne({ -// salesCoordinator: id, -// }).populate("salesCoordinator", "name email"); - -// if (!attendanceDoc) { -// return res.status(404).json({ -// success: false, -// message: "No attendance records found for this sales coordinator.", -// }); -// } - -// // Pagination and slicing records -// const totalData = attendanceDoc.records.length; -// const paginatedRecords = attendanceDoc.records.slice(skip, skip + limit); - -// res.status(200).json({ -// success: true, -// salesCoordinator: attendanceDoc.salesCoordinator, -// attendance: paginatedRecords, -// total_data: totalData, -// }); -// } catch (error) { -// res.status(500).json({ -// success: false, -// message: error.message || "Something went wrong", -// }); -// } -// }; - -// export const getTodayAttendance = async (req, res) => { -// try { -// const today = new Date(); -// today.setHours(0, 0, 0, 0); - -// const startOfDay = new Date(today); -// const endOfDay = new Date(today); -// endOfDay.setHours(23, 59, 59, 999); - -// const { page = 1, show = 10 } = req.query; -// const limit = parseInt(show); -// const skip = (page - 1) * limit; - -// const attendances = await AttendanceSalesCoOrdinator.aggregate([ -// { $unwind: '$records' }, -// { -// $match: { -// 'records.date': { $gte: startOfDay, $lte: endOfDay } -// } -// }, -// { -// $lookup: { -// from: 'salescoordinators', -// localField: 'salesCoordinator', -// foreignField: '_id', -// as: 'salesCoordinator' -// } -// }, -// { $unwind: '$salesCoordinator' }, -// { -// $facet: { -// totalData: [{ $count: 'count' }], -// attendance: [ -// { $skip: skip }, -// { $limit: limit }, -// { -// $project: { -// salesCoordinator: { -// id: '$salesCoordinator._id', -// name: '$salesCoordinator.name', -// email: '$salesCoordinator.email' -// }, -// date: '$records.date', -// time: '$records.time', -// location: '$records.location', -// note: '$records.notes' -// } -// } -// ] -// } -// } -// ]); - -// const totalData = attendances[0].totalData[0] ? attendances[0].totalData[0].count : 0; - -// res.status(200).json({ -// success: true, -// attendance: attendances[0].attendance, -// total_data: totalData -// }); -// } catch (error) { -// res.status(500).json({ -// success: false, -// message: error.message || 'Something went wrong' -// }); -// } -// }; - -import { Attendance } from "./AttendanceModel.js"; - -// Mark attendance export const markAttendance = async (req, res) => { try { const { date, time, location, notes } = req.body; const userId = req.user._id; const userType = req.userType; - // Get today's date + // Parse the input date + const [year, month, day] = date.split("/"); + const parsedDate = new Date(Date.UTC(year, month - 1, day)); const today = new Date(); - today.setHours(0, 0, 0, 0); + today.setUTCHours(0, 0, 0, 0); // Check if the date in req.body is not today's date - if (!isSameDay(new Date(date), today)) { + if (parsedDate.getTime() !== today.getTime()) { return res.status(400).json({ success: false, - message: `Attendance can only be marked for today's date: ${today.toLocaleDateString()}`, + message: `Attendance can only be marked for today's date: ${ + today.toISOString().split("T")[0] + }`, }); } // Get the start and end of the day to check if the attendance is already marked - const startOfDay = new Date(date); - startOfDay.setHours(0, 0, 0, 0); - - const endOfDay = new Date(date); - endOfDay.setHours(23, 59, 59, 999); + const startOfDay = new Date(parsedDate); + const endOfDay = new Date(parsedDate); + endOfDay.setUTCHours(23, 59, 59, 999); // Check if the attendance record exists for today const existingAttendance = await Attendance.findOne({ userId, userType, - records: { - $elemMatch: { - date: { $gte: startOfDay, $lte: endOfDay } - } - } + 'records.date': { + $gte: startOfDay, + $lte: endOfDay, + }, }); if (existingAttendance) { @@ -246,15 +130,16 @@ export const markAttendance = async (req, res) => { } // Add the new attendance record to the array - attendance.records.push({ date, time, location, notes }); + attendance.records.push({ date: startOfDay, time, location, notes }); await attendance.save(); res.status(201).json({ success: true, message: "Attendance marked successfully", - record: { date, time, location, notes }, + record: { date: startOfDay, time, location, notes }, }); } catch (error) { + console.error("Error marking attendance:", error); res.status(500).json({ success: false, message: error.message || "Something went wrong", @@ -262,14 +147,6 @@ export const markAttendance = async (req, res) => { } }; -// Helper function to check if two dates are on the same day -function isSameDay(date1, date2) { - return ( - date1.getFullYear() === date2.getFullYear() && - date1.getMonth() === date2.getMonth() && - date1.getDate() === date2.getDate() - ); -} // Get attendance by user ID export const getAttendanceByUser = async (req, res) => { @@ -340,66 +217,102 @@ export const AdmingetAttendanceByUser = async (req, res) => { // Get today's attendance for admin export const getTodayAttendance = async (req, res) => { try { + // Step 1: Get today's date in UTC and set the time to start and end of the day const today = new Date(); - today.setHours(0, 0, 0, 0); - - const startOfDay = new Date(today); - const endOfDay = new Date(today); - endOfDay.setHours(23, 59, 59, 999); + const todayStartUTC = new Date( + Date.UTC( + today.getUTCFullYear(), + today.getUTCMonth(), + today.getUTCDate(), + 0, + 0, + 0 + ) + ); + const tomorrowStartUTC = new Date(todayStartUTC); + tomorrowStartUTC.setUTCDate(todayStartUTC.getUTCDate() + 1); // Start of the next day + // Step 2: Parse query parameters const { page = 1, show = 10 } = req.query; - const limit = parseInt(show); + const limit = parseInt(show, 10); const skip = (page - 1) * limit; + // Step 3: Aggregate query to get today's attendance records const attendances = await Attendance.aggregate([ - { $unwind: '$records' }, + { $unwind: "$records" }, { $match: { - 'records.date': { $gte: startOfDay, $lte: endOfDay } - } + "records.date": { + $gte: todayStartUTC, + $lt: tomorrowStartUTC, + }, + }, }, { $lookup: { - from: 'users', - localField: 'userId', - foreignField: '_id', - as: 'user' - } + from: "salescoordinators", + localField: "userId", + foreignField: "_id", + as: "salesCoOrdinatorUser", + }, }, - { $unwind: '$user' }, { - $facet: { - totalData: [{ $count: 'count' }], - attendance: [ - { $skip: skip }, - { $limit: limit }, - { - $project: { - user: { - id: '$user._id', - name: '$user.name', - email: '$user.email', - userType: '$userType' - }, - date: '$records.date', - time: '$records.time', - location: '$records.location', - notes: '$records.notes', - } - } - ] - } - } + $lookup: { + from: "territorymanagers", + localField: "userId", + foreignField: "_id", + as: "territoryManagerUser", + }, + }, + { + $addFields: { + user: { + $cond: { + if: { $eq: ["$userType", "SalesCoOrdinator"] }, + then: { $arrayElemAt: ["$salesCoOrdinatorUser", 0] }, + else: { $arrayElemAt: ["$territoryManagerUser", 0] }, + }, + }, + }, + }, + { + $project: { + salesCoOrdinatorUser: 0, + territoryManagerUser: 0, + }, + }, ]); + const totalData = attendances.length; + const paginatedattendance = attendances.slice(skip, skip + limit); + // Step 6: Format the response + const formattedattendance = paginatedattendance.map((record) => ({ + user: record.user + ? { + id: record.user._id, + name: record.user.name, + email: record.user.email, + mobileNumber: record.user.mobileNumber, + userType: record.userType, + uniqueId: record.user.uniqueId, + } + : null, + date: record.records.date, + time: record.records.time, + location: record.records.location, + reason: record.records.reason, + leaveType: record.records.leaveType, + })); - const totalData = attendances[0].totalData[0]?.count || 0; - + // Send response res.status(200).json({ success: true, total_data: totalData, - attendance: attendances[0].attendance + attendance: formattedattendance, + page: parseInt(page, 10), + limit: limit, }); } catch (error) { + console.error("Error fetching today’s attendance:", error); res.status(500).json({ success: false, message: error.message || "Something went wrong", diff --git a/resources/Attendance/AttendanceModel.js b/resources/Attendance/AttendanceModel.js index c2a1cb0..ec7e7fb 100644 --- a/resources/Attendance/AttendanceModel.js +++ b/resources/Attendance/AttendanceModel.js @@ -1,34 +1,3 @@ -// import mongoose from 'mongoose'; - -// const attendanceRecordSchema = new mongoose.Schema({ -// date: { -// type: Date, -// required: true, -// }, -// time: { -// type: String, -// required: true, -// }, -// location: { -// type: String, -// required: true, -// }, -// notes: { -// type: String, -// }, -// }); - -// const attendanceSchema = new mongoose.Schema({ -// salesCoordinator: { -// type: mongoose.Schema.Types.ObjectId, -// ref: 'SalesCoOrdinator', -// required: true, -// unique: true, -// }, -// records: [attendanceRecordSchema], -// }, { timestamps: true, versionKey: false }); - -// export const AttendanceSalesCoOrdinator = mongoose.model('Attendance', attendanceSchema); import mongoose from 'mongoose'; // Define attendance record schema @@ -60,7 +29,7 @@ const attendanceSchema = new mongoose.Schema({ userType: { type: String, required: true, - enum: ['SalesCoOrdinator', 'TerritoryManager'], // Specify allowed user types + enum: ['SalesCoOrdinator', 'TerritoryManager'], }, records: [attendanceRecordSchema], }, { timestamps: true, versionKey: false }); diff --git a/resources/Leaves/LeaveController.js b/resources/Leaves/LeaveController.js new file mode 100644 index 0000000..2cea40e --- /dev/null +++ b/resources/Leaves/LeaveController.js @@ -0,0 +1,250 @@ +import { Leave } from "./LeaveModel.js"; + +// Mark leave +export const markLeave = async (req, res) => { + try { + const { date, time, location, reason, leaveType } = req.body; + const userId = req.user._id; + const userType = req.userType; + + // Parse the input date + const [year, month, day] = date.split("/"); + const parsedDate = new Date(Date.UTC(year, month - 1, day)); + const today = new Date(); + today.setUTCHours(0, 0, 0, 0); + + // Check if the date in req.body is not today's date + if (parsedDate.getTime() !== today.getTime()) { + return res.status(400).json({ + success: false, + message: `Leave can only be marked for today's date: ${ + today.toISOString().split("T")[0] + }`, + }); + } + + // Get the start and end of the day to check if the leave is already marked + const startOfDay = new Date(parsedDate); + const endOfDay = new Date(parsedDate); + endOfDay.setUTCHours(23, 59, 59, 999); + + // Check if the leave record exists for today + const existingLeave = await Leave.findOne({ + userId, + userType, + records: { + $elemMatch: { + date: { $gte: startOfDay, $lte: endOfDay }, + }, + }, + }); + + if (existingLeave) { + return res.status(400).json({ + success: false, + message: "Leave for today is already marked.", + }); + } + + // Check if leave record exists for the user + let leave = await Leave.findOne({ userId, userType }); + + if (!leave) { + // Create a new leave record if it doesn't exist + leave = new Leave({ + userId, + userType, + records: [], + }); + } + + // Add the new leave record to the array + leave.records.push({ date: startOfDay, time, location, reason, leaveType }); + await leave.save(); + + res.status(201).json({ + success: true, + message: "Leave marked successfully", + record: { date: startOfDay, time, location, reason, leaveType }, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message || "Something went wrong", + }); + } +}; + +// Helper function to check if two dates are on the same day +function isSameDay(date1, date2) { + return ( + date1.getFullYear() === date2.getFullYear() && + date1.getMonth() === date2.getMonth() && + date1.getDate() === date2.getDate() + ); +} + +// Get leave by user ID +export const getLeaveByUser = async (req, res) => { + try { + const userId = req.user._id; + + const leave = await Leave.findOne({ + userId, + }).populate("userId", "name email"); + + if (!leave) { + return res.status(404).json({ + success: false, + message: "No leave records found for this user.", + }); + } + + res.status(200).json({ + success: true, + leave, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message || "Something went wrong", + }); + } +}; + +// Admin route to get leave by user ID +export const AdmingetLeaveByUser = async (req, res) => { + try { + const { id } = req.params; + const { page = 1, show = 10 } = req.query; + const limit = parseInt(show); + const skip = (page - 1) * limit; + + // Find leave records for the given user + const leaveDoc = await Leave.findOne({ + userId: id, + }).populate("userId", "name email"); + + if (!leaveDoc) { + return res.status(404).json({ + success: false, + message: "No leave records found for this user.", + }); + } + + // Pagination and slicing records + const totalData = leaveDoc.records.length; + const paginatedRecords = leaveDoc.records.slice(skip, skip + limit); + + res.status(200).json({ + success: true, + user: leaveDoc.userId, + leave: paginatedRecords, + total_data: totalData, + }); + } catch (error) { + res.status500().json({ + success: false, + message: error.message || "Something went wrong", + }); + } +}; + +export const getTodayLeave = async (req, res) => { + try { + // Step 1: Get today's date in UTC and set the time to 00:00:00.000 + const today = new Date(); + const todayStartUTC = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate(), 0, 0, 0)); + const tomorrowStartUTC = new Date(todayStartUTC); + tomorrowStartUTC.setUTCDate(todayStartUTC.getUTCDate() + 1); // Start of the next day + + // Step 2: Parse query parameters + const { page = 1, show = 10 } = req.query; + const limit = parseInt(show, 10); + const skip = (page - 1) * limit; + + // Step 3: Aggregate query to get today's leave records + const leavesWithUserLookup = await Leave.aggregate([ + { $unwind: '$records' }, + { + $match: { + 'records.date': { + $gte: todayStartUTC, + $lt: tomorrowStartUTC + } + } + }, + { + $lookup: { + from: 'salescoordinators', + localField: 'userId', + foreignField: '_id', + as: 'salesCoOrdinatorUser' + } + }, + { + $lookup: { + from: 'territorymanagers', + localField: 'userId', + foreignField: '_id', + as: 'territoryManagerUser' + } + }, + { + $addFields: { + user: { + $cond: { + if: { $eq: ["$userType", "SalesCoOrdinator"] }, + then: { $arrayElemAt: ["$salesCoOrdinatorUser", 0] }, + else: { $arrayElemAt: ["$territoryManagerUser", 0] } + } + } + } + }, + { + $project: { + salesCoOrdinatorUser: 0, + territoryManagerUser: 0 + } + } + ]); + + // Step 4: Calculate total data + const totalData = leavesWithUserLookup.length; + + // Step 5: Implement pagination + const paginatedLeaves = leavesWithUserLookup.slice(skip, skip + limit); + + // Step 6: Format the response + const formattedLeaves = paginatedLeaves.map(record => ({ + user: record.user ? { + id: record.user._id, + name: record.user.name, + email: record.user.email, + mobileNumber: record.user.mobileNumber, + userType: record.userType, + uniqueId: record.user.uniqueId, + } : null, + date: record.records.date, + time: record.records.time, + location: record.records.location, + reason: record.records.reason, + leaveType: record.records.leaveType, + })); + + // Send response + res.status(200).json({ + success: true, + total_data: totalData, + leave: formattedLeaves, + page: parseInt(page, 10), + limit: limit, + }); + } catch (error) { + console.error("Error fetching today’s leave:", error); + res.status(500).json({ + success: false, + message: error.message || "Something went wrong", + }); + } +}; diff --git a/resources/Leaves/LeaveModel.js b/resources/Leaves/LeaveModel.js new file mode 100644 index 0000000..6a84dbb --- /dev/null +++ b/resources/Leaves/LeaveModel.js @@ -0,0 +1,43 @@ +import mongoose from 'mongoose'; + +// Define leave record schema +const leaveRecordSchema = new mongoose.Schema({ + date: { + type: Date, + required: true, + }, + time: { + type: String, + required: true, + }, + location: { + type: String, + required: true, + }, + reason: { + type: String, + required: true, + }, + leaveType: { + type: String, + required: true, + enum: ['Sick Leave', 'Casual Leave'], + }, +}); + +// Define main leave schema +const leaveSchema = new mongoose.Schema({ + userId: { + type: mongoose.Schema.Types.ObjectId, + refPath: 'userType', + required: true, + }, + userType: { + type: String, + required: true, + enum: ['SalesCoOrdinator', 'TerritoryManager'], + }, + records: [leaveRecordSchema], +}, { timestamps: true, versionKey: false }); + +export const Leave = mongoose.model('Leave', leaveSchema); diff --git a/resources/Leaves/LeaveRoute.js b/resources/Leaves/LeaveRoute.js new file mode 100644 index 0000000..9e7ac69 --- /dev/null +++ b/resources/Leaves/LeaveRoute.js @@ -0,0 +1,60 @@ +import express from "express"; +import { + markLeave, + getLeaveByUser, + getTodayLeave, + AdmingetLeaveByUser, +} from "./LeaveController.js"; +import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; +// import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; +import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; + +const router = express.Router(); + +// Place more specific routes first + +// Route to get today's leave for admin +router.get( + "/leave/today", + isAuthenticatedUser, + authorizeRoles("admin"), + getTodayLeave +); + +// Route to mark leave for Sales Coordinators +router.post( + "/markleave/salescoordinator", + isAuthenticatedSalesCoOrdinator, + markLeave +); + +// Route to mark leave for Territory Managers +router.post( + "/markleave/territorymanager", + // isAuthenticatedTerritoryManager, + markLeave +); + +// Route to get leave for the logged-in sales coordinator +router.get( + "/leave/salescoordinator", + isAuthenticatedSalesCoOrdinator, + getLeaveByUser +); + +// Route to get leave for the logged-in territory manager +router.get( + "/leave/territorymanager", + // isAuthenticatedTerritoryManager, + getLeaveByUser +); + +// Admin route to get leave by user ID +router.get( + "/leave/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + AdmingetLeaveByUser +); + +export default router;