Added leave manage route

This commit is contained in:
Aakash Mittal 2025-11-21 15:40:11 +05:30
parent a48a919b41
commit 5eef945f50
9 changed files with 669 additions and 69 deletions

View File

@ -42,11 +42,49 @@ async function closePreviousSession(vaId, lastLog) {
}); });
// console.log(`Created logout log: ${logoutLog._id}`); // console.log(`Created logout log: ${logoutLog._id}`);
try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
const sfDataSync = {
"_id": logoutLog._id,
"loggedType": "LOGOUT",
"vaId": vaId,
"vaEmail": lastLog.vaEmail,
"projectId": lastLog.projectId,
"time": logoutLog.time,
}
const response = await fetch(salesforceUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Add any required headers for Salesforce authentication
// 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
},
body: JSON.stringify(sfDataSync)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
}
const result = await response.json();
// console.log("✅ Successfully synced VA data to Salesforce:", result);
return { return {
closedWorkTracks: openWorkTracks.length, closedWorkTracks: openWorkTracks.length,
logoutLogId: logoutLog._id logoutLogId: logoutLog._id,
}; SyncSalesforce: result
};
} catch (error) {
console.error("Failed to sync with Salesforce:", error.message);
console.log(error)
// Create a custom error to distinguish Salesforce errors
const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
salesforceError.isSalesforceError = true;
throw salesforceError;
}
} catch (error) { } catch (error) {
console.error("Error closing previous session:", error); console.error("Error closing previous session:", error);

View File

@ -0,0 +1,101 @@
import jwt from "jsonwebtoken";
import { VAModel, LeavesModel } from "../../../models/vaModel.js";
// Get all leave lists applied by VA
async function va_leave_list_function(req, res) {
try {
const accessToken = req.headers.token;
if (!accessToken) {
return res.status(401).json({ success: false, message: "Missing token" });
}
const decoded = jwt.verify(accessToken, process.env.JWT_SECRET_ACCESS_TOKEN);
if (!decoded || !decoded.email) {
return res.status(401).json({ success: false, message: "Invalid token" });
}
const vaEmail = decoded.email;
const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail });
if (!vaUser) {
return res.status(404).json({ success: false, message: "VA not found" });
}
const vaId = vaUser._id;
// Get parameters from route params (they are optional)
const { status, type } = req.params;
// Get sorting from query params (optional)
const {
sortBy = 'createdAt',
sortOrder = 'desc'
} = req.query;
// Build filter object
const filter = { vaId };
// Handle status filter from params
if (status && status !== 'ALL') {
filter.status = status.toUpperCase();
}
// Handle type filter from params (using 'type' instead of 'leaveType')
if (type && type !== 'ALL') {
filter.leaveType = type.toUpperCase();
}
// Validate sort parameters
const validSortFields = ['createdAt', 'dateFrom', 'dateTo', 'status', 'leaveType'];
const sortField = validSortFields.includes(sortBy) ? sortBy : 'createdAt';
const sortDirection = sortOrder.toLowerCase() === 'asc' ? 1 : -1;
// Get all leaves without pagination
const leaves = await LeavesModel.find(filter)
.sort({ [sortField]: sortDirection })
.select('-__v')
.lean();
// Format the response data
const formattedLeaves = leaves.map(leave => ({
leaveId: leave._id,
dateFrom: leave.dateFrom.toISOString().split('T')[0],
dateTo: leave.dateTo.toISOString().split('T')[0],
totalDays: parseInt(leave.days),
leaveType: leave.leaveType,
comment: leave.comment,
status: leave.status,
appliedOn: leave.createdAt,
reviewedAt: leave.reviewedAt || null,
rejectionReason: leave.rejectionReason || null
}));
return res.status(200).json({
success: true,
message: "Leave list retrieved successfully",
data: {
leaves: formattedLeaves,
totalCount: formattedLeaves.length
}
});
} catch (error) {
console.error("Leave list error:", error);
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({ success: false, message: "Invalid token" });
}
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ success: false, message: "Token expired" });
}
return res.status(500).json({
success: false,
message: "Internal Server Error",
error: error.message
});
}
}
export { va_leave_list_function };

View File

@ -0,0 +1,114 @@
import jwt from "jsonwebtoken";
import { VAModel, LeavesModel } from "../../../models/vaModel.js";
// Approve or reject leave
async function va_leave_manage_function(req, res) {
try {
const { leaveId } = req.params;
const { action, rejectionReason } = req.body;
if (!leaveId) {
return res.status(400).json({
success: false,
message: "Leave ID is required"
});
}
if (!action || !['APPROVE', 'REJECT'].includes(action.toUpperCase())) {
return res.status(400).json({
success: false,
message: "Action is required and must be either 'APPROVE' or 'REJECT'"
});
}
// If rejecting, rejectionReason is required
if (action.toUpperCase() === 'REJECT' && !rejectionReason) {
return res.status(400).json({
success: false,
message: "Rejection reason is required when rejecting a leave"
});
}
// Find the leave request
const leaveRequest = await LeavesModel.findById(leaveId);
if (!leaveRequest) {
return res.status(404).json({
success: false,
message: "Leave request not found"
});
}
// Check if leave is already processed
if (leaveRequest.status !== 'PENDING') {
return res.status(400).json({
success: false,
message: `Leave request is already ${leaveRequest.status.toLowerCase()}`
});
}
// Prepare update data
const updateData = {
status: action.toUpperCase() === 'APPROVE' ? 'APPROVED' : 'REJECTED',
reviewedAt: new Date()
};
// Add rejection reason if rejecting
if (action.toUpperCase() === 'REJECT') {
updateData.rejectionReason = rejectionReason;
}
// Update the leave request
const updatedLeave = await LeavesModel.findByIdAndUpdate(
leaveId,
updateData,
{ new: true } // Return updated document
).select('-__v').lean();
// Format the response
const formattedLeave = {
leaveId: updatedLeave._id,
dateFrom: updatedLeave.dateFrom.toISOString().split('T')[0],
dateTo: updatedLeave.dateTo.toISOString().split('T')[0],
totalDays: parseInt(updatedLeave.days),
leaveType: updatedLeave.leaveType,
comment: updatedLeave.comment,
status: updatedLeave.status,
appliedOn: updatedLeave.createdAt,
reviewedAt: updatedLeave.reviewedAt,
rejectionReason: updatedLeave.rejectionReason || null
};
return res.status(200).json({
success: true,
message: `Leave request ${action.toLowerCase()}d successfully`,
data: formattedLeave
});
} catch (error) {
console.error("Leave approve/reject error:", error);
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({ success: false, message: "Invalid token" });
}
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ success: false, message: "Token expired" });
}
if (error.name === 'CastError') {
return res.status(400).json({
success: false,
message: "Invalid leave ID format"
});
}
return res.status(500).json({
success: false,
message: "Internal Server Error",
error: error.message
});
}
}
export { va_leave_manage_function };

View File

@ -1,7 +1,7 @@
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { VAModel, LeavesModel } from "../../../models/vaModel.js"; import { VAModel, LeavesModel } from "../../../models/vaModel.js";
// Adjust missing hours by creating new records // Apply for leave
async function va_leave_apply_function(req, res) { async function va_leave_apply_function(req, res) {
try { try {
const accessToken = req.headers.token; const accessToken = req.headers.token;
@ -22,20 +22,219 @@ async function va_leave_apply_function(req, res) {
} }
const vaId = vaUser._id; const vaId = vaUser._id;
const { dateFrom, dateTo, days, leaveType, comment } = req.body; const { dateFrom, dateTo, leaveType, comment } = req.body;
if (!dateFrom || !dateTo || !days || !leaveType || !comment) { if (!dateFrom || !dateTo || !leaveType || !comment) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "dateFrom, dateTo, days, leaveType, and comment are required" message: "dateFrom, dateTo, leaveType, and comment are required"
}); });
} }
// Validate date formats
let startDate, endDate;
try {
startDate = new Date(dateFrom);
endDate = new Date(dateTo);
// Set to start of day for accurate calculation
startDate.setHours(0, 0, 0, 0);
endDate.setHours(0, 0, 0, 0);
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
return res.status(400).json({
success: false,
message: "Invalid date format. Use valid date format (YYYY-MM-DD)"
});
}
} catch (error) {
return res.status(400).json({
success: false,
message: "Invalid date format"
});
}
// Ensure start date is before or equal to end date
if (startDate > endDate) {
return res.status(400).json({
success: false,
message: "Start date must be before or equal to end date"
});
}
// Ensure dates are not in the past
const today = new Date();
today.setHours(0, 0, 0, 0);
if (startDate < today) {
return res.status(400).json({
success: false,
message: "Cannot apply for leave for past dates"
});
}
// Validate leave type
const validLeaveTypes = ["SICK", "PTO"];
if (!validLeaveTypes.includes(leaveType)) {
return res.status(400).json({
success: false,
message: "Invalid leave type. Must be SICK or PTO"
});
}
// Calculate total days (inclusive of both start and end dates)
const timeDifference = endDate.getTime() - startDate.getTime();
const totalDays = Math.floor(timeDifference / (1000 * 60 * 60 * 24)) + 1; // +1 to include both start and end dates
if (totalDays <= 0) {
return res.status(400).json({
success: false,
message: "Invalid date range"
});
}
// Check for overlapping leave requests
const existingLeave = await LeavesModel.findOne({
vaId,
status: "PENDING",
$or: [
{
dateFrom: { $lte: endDate },
dateTo: { $gte: startDate }
}
]
});
if (existingLeave) {
return res.status(400).json({
success: false,
message: "You already have a pending leave request that overlaps with these dates"
});
}
// Check for overlapping approved leaves
const existingApprovedLeave = await LeavesModel.findOne({
vaId,
status: "APPROVED",
$or: [
{
dateFrom: { $lte: endDate },
dateTo: { $gte: startDate }
}
]
});
if (existingApprovedLeave) {
return res.status(400).json({
success: false,
message: "You already have an approved leave that overlaps with these dates"
});
}
// Create leave request
const leaveRequest = new LeavesModel({
vaId,
dateFrom: startDate,
dateTo: endDate,
days: totalDays.toString(),
leaveType,
comment,
status: "PENDING"
});
await leaveRequest.save();
// try {
// const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/timesheet-adjustment`;
// const sfData = {
// "id": adjustmentRequest._id,
// "va": {
// "id": vaId
// },
// "project": {
// "id": projectId
// },
// "date": adjustmentDate.toISOString().split('T')[0],
// "loginTime": loginDateTime.toISOString(),
// "logoutTime": logoutDateTime.toISOString(),
// "comment": comment,
// "status": "PENDING",
// "rejectionReason": "",
// "adjustedHours": formatTime(adjustedSeconds)
// }
// const response = await fetch(salesforceUrl, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// // Add any required headers for Salesforce authentication
// // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
// },
// body: JSON.stringify({"data": sfData})
// });
// if (!response.ok) {
// const errorText = await response.text();
// throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
// }
// const result = await response.json();
// // console.log("✅ Successfully synced VA data to Salesforce:", result);
// return res.status(201).json({
// success: true,
// message: "Adjustment request submitted successfully",
// data: {
// adjustmentId: adjustmentRequest._id,
// date: adjustmentDate.toISOString().split('T')[0],
// loginTime: loginDateTime.toISOString(),
// logoutTime: logoutDateTime.toISOString(),
// adjustedHours: formatTime(adjustedSeconds),
// comment: comment,
// status: "PENDING"
// },
// SyncSalesforce: result
// });
// } catch (error) {
// console.error("Failed to sync with Salesforce:", error.message);
// console.log(error)
// // Create a custom error to distinguish Salesforce errors
// const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
// salesforceError.isSalesforceError = true;
// throw salesforceError;
// }
return res.status(201).json({
success: true,
message: "Leave request submitted successfully",
data: {
leaveId: leaveRequest._id,
dateFrom: startDate.toISOString().split('T')[0],
dateTo: endDate.toISOString().split('T')[0],
totalDays: totalDays,
leaveType,
comment,
status: "PENDING"
}
});
} catch (error) { } catch (error) {
console.error("Leave apply error:", error); console.error("Leave apply error:", error);
return res.status(500).json({ success: false, message: "Internal Server Error" });
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({ success: false, message: "Invalid token" });
}
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ success: false, message: "Token expired" });
}
return res.status(500).json({
success: false,
message: "Internal Server Error",
error: error.message
});
} }
} }

View File

@ -65,13 +65,52 @@ async function checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, curre
vaEmail, vaEmail,
projectId projectId
}); });
try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
const sfDataSync = {
"_id": logoutLog._id,
"loggedType": "LOGOUT",
"vaId": vaId,
"vaEmail": vaEmail,
"projectId": projectId,
"time": logoutLog.time,
}
const response = await fetch(salesforceUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Add any required headers for Salesforce authentication
// 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
},
body: JSON.stringify(sfDataSync)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
}
const result = await response.json();
// console.log("✅ Successfully synced VA data to Salesforce:", result);
return { return {
shouldLogout: true, shouldLogout: true,
totalTodayHours: totalTodayHours, totalTodayHours: totalTodayHours,
dedicatedHours: project.dedicatedHours, dedicatedHours: project.dedicatedHours,
logoutLogId: logoutLog._id logoutLogId: logoutLog._id,
}; SyncSalesforce: result
};
} catch (error) {
console.error("Failed to sync with Salesforce:", error.message);
console.log(error)
// Create a custom error to distinguish Salesforce errors
const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
salesforceError.isSalesforceError = true;
throw salesforceError;
}
} }
return { return {

View File

@ -66,11 +66,49 @@ async function closePreviousSession(vaId, lastLog) {
}); });
// console.log(`Created logout log: ${logoutLog._id}`); // console.log(`Created logout log: ${logoutLog._id}`);
try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
const sfDataSync = {
"_id": logoutLog._id,
"loggedType": "LOGOUT",
"vaId": vaId,
"vaEmail": lastLog.vaEmail,
"projectId": lastLog.projectId,
"time": logoutLog.time,
}
const response = await fetch(salesforceUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Add any required headers for Salesforce authentication
// 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
},
body: JSON.stringify(sfDataSync)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
}
const result = await response.json();
// console.log("✅ Successfully synced VA data to Salesforce:", result);
return { return {
closedWorkTracks: openWorkTracks.length, closedWorkTracks: openWorkTracks.length,
logoutLogId: logoutLog._id logoutLogId: logoutLog._id,
}; SyncSalesforce: result
};
} catch (error) {
console.error("Failed to sync with Salesforce:", error.message);
console.log(error)
// Create a custom error to distinguish Salesforce errors
const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
salesforceError.isSalesforceError = true;
throw salesforceError;
}
} catch (error) { } catch (error) {
console.error("Error closing previous session:", error); console.error("Error closing previous session:", error);
@ -151,47 +189,47 @@ async function va_project_log_callback(req, res) {
const createdLog = await LoggedModel.create({ loggedType: type, vaId, vaEmail, projectId }) const createdLog = await LoggedModel.create({ loggedType: type, vaId, vaEmail, projectId })
// console.log(createdLog) // console.log(createdLog)
try { // try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; // const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
const sfData = { // const sfData = {
"_id": createdLog._id, // "_id": createdLog._id,
"loggedType": type, // "loggedType": type,
"vaId": vaId, // "vaId": vaId,
"vaEmail": vaEmail, // "vaEmail": vaEmail,
"projectId": projectId, // "projectId": projectId,
"time": createdLog.time, // "time": createdLog.time,
} // }
const response = await fetch(salesforceUrl, { // const response = await fetch(salesforceUrl, {
method: 'POST', // method: 'POST',
headers: { // headers: {
'Content-Type': 'application/json', // 'Content-Type': 'application/json',
// Add any required headers for Salesforce authentication // // Add any required headers for Salesforce authentication
// 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN // // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
}, // },
body: JSON.stringify(sfData) // body: JSON.stringify(sfData)
}); // });
if (!response.ok) { // if (!response.ok) {
const errorText = await response.text(); // const errorText = await response.text();
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); // throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
} // }
const result = await response.json(); // const result = await response.json();
// console.log("✅ Successfully synced VA data to Salesforce:", result); // // console.log("✅ Successfully synced VA data to Salesforce:", result);
res.status(201).json({ success: true, message: `${type} Successfully`, logId: createdLog._id, SyncSalesforce: result }); // res.status(201).json({ success: true, message: `${type} Successfully`, logId: createdLog._id, SyncSalesforce: result });
} catch (error) { // } catch (error) {
console.error("Failed to sync with Salesforce:", error.message); // console.error("Failed to sync with Salesforce:", error.message);
console.log(error) // console.log(error)
// Create a custom error to distinguish Salesforce errors // // Create a custom error to distinguish Salesforce errors
const salesforceError = new Error(`Salesforce sync failed: ${error.message}`); // const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
salesforceError.isSalesforceError = true; // salesforceError.isSalesforceError = true;
throw salesforceError; // throw salesforceError;
} // }
// return res.status(201).send({ success: true, message: `${type} Successfully`, logId: createdLog._id }); return res.status(201).send({ success: true, message: `${type} Successfully`, logId: createdLog._id });
} catch (error) { } catch (error) {
return res.status(500).send("Internal Server Error"); return res.status(500).send("Internal Server Error");

View File

@ -140,19 +140,80 @@ async function va_timesheet_adjustment_function(req, res) {
return `${h}h ${m}m ${s}s`; return `${h}h ${m}m ${s}s`;
}; };
return res.status(201).json({ try {
success: true, const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/timesheet-adjustment`;
message: "Adjustment request submitted successfully", const sfData = {
data: { "id": adjustmentRequest._id,
adjustmentId: adjustmentRequest._id, "va": {
date: adjustmentDate.toISOString().split('T')[0], "id": vaId
loginTime: loginDateTime.toISOString(), },
logoutTime: logoutDateTime.toISOString(), "project": {
adjustedHours: formatTime(adjustedSeconds), "id": projectId
comment: comment, },
status: "PENDING" "date": adjustmentDate.toISOString().split('T')[0],
"loginTime": loginDateTime.toISOString(),
"logoutTime": logoutDateTime.toISOString(),
"comment": comment,
"status": "PENDING",
"rejectionReason": "",
"adjustedHours": formatTime(adjustedSeconds)
} }
});
const response = await fetch(salesforceUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Add any required headers for Salesforce authentication
// 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
},
body: JSON.stringify({"data": sfData})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
}
const result = await response.json();
// console.log("✅ Successfully synced VA data to Salesforce:", result);
return res.status(201).json({
success: true,
message: "Adjustment request submitted successfully",
data: {
adjustmentId: adjustmentRequest._id,
date: adjustmentDate.toISOString().split('T')[0],
loginTime: loginDateTime.toISOString(),
logoutTime: logoutDateTime.toISOString(),
adjustedHours: formatTime(adjustedSeconds),
comment: comment,
status: "PENDING"
},
SyncSalesforce: result
});
} catch (error) {
console.error("Failed to sync with Salesforce:", error.message);
console.log(error)
// Create a custom error to distinguish Salesforce errors
const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
salesforceError.isSalesforceError = true;
throw salesforceError;
}
// return res.status(201).json({
// success: true,
// message: "Adjustment request submitted successfully",
// data: {
// adjustmentId: adjustmentRequest._id,
// date: adjustmentDate.toISOString().split('T')[0],
// loginTime: loginDateTime.toISOString(),
// logoutTime: logoutDateTime.toISOString(),
// adjustedHours: formatTime(adjustedSeconds),
// comment: comment,
// status: "PENDING"
// }
// });
} catch (error) { } catch (error) {
console.error("Timesheet adjustment error:", error); console.error("Timesheet adjustment error:", error);

View File

@ -432,8 +432,7 @@ const leavesSchema = new Schema({
}, },
clientId: { clientId: {
type: Types.ObjectId, type: Types.ObjectId,
ref: "Client", ref: "Client"
required: true
}, },
dateFrom: { dateFrom: {
type: Date, type: Date,

View File

@ -23,6 +23,9 @@ import { va_timesheet_adjustment_function } from "../../controllers/va/vaTimeshe
import { get_adjustment_requests_list } from "../../controllers/va/vaTimesheetAdjustment/vaAllTimesheetAdjustmentReuqest.controller.js" import { get_adjustment_requests_list } from "../../controllers/va/vaTimesheetAdjustment/vaAllTimesheetAdjustmentReuqest.controller.js"
import { va_timesheet_manage_adjustment_request } from "../../controllers/va/vaTimesheetAdjustment/vaManageTimesheetAdjustment.controller.js" import { va_timesheet_manage_adjustment_request } from "../../controllers/va/vaTimesheetAdjustment/vaManageTimesheetAdjustment.controller.js"
import { va_verify_email } from "../../controllers/va/vaAuth/vaVerifyEmail.controller.js"; import { va_verify_email } from "../../controllers/va/vaAuth/vaVerifyEmail.controller.js";
import { va_leave_apply_function } from "../../controllers/va/vaLeaves/vaLeavesApply.controller.js";
import { va_leave_list_function } from "../../controllers/va/vaLeaves/vaLeaveList.controller.js"
import { va_leave_manage_function } from "../../controllers/va/vaLeaves/vaLeaveManage.controller.js";
vaRoutes.get("/", va_callback); // VA Callback for getting active VA vaRoutes.get("/", va_callback); // VA Callback for getting active VA
@ -43,9 +46,17 @@ vaRoutes.get("/:userName", va_username_callback); // VA Username Callback
// timesheet adjustment routes // timesheet adjustment routes
vaRoutes.get("/timesheet/summary/:projectId/:date", authValidator, va_timesheet_summary_function); // Get Timesheet Adjustment for VA on specific project and date vaRoutes.get("/timesheet/summary/:projectId/:date", authValidator, va_timesheet_summary_function); // Get Timesheet Adjustment for VA on specific project and date
vaRoutes.post("/timesheet/adjustment/request", authValidator, va_timesheet_adjustment_function); // Timesheet Adjustment for VA vaRoutes.post("/timesheet/adjustment/request", authValidator, va_timesheet_adjustment_function); // Timesheet Adjustment for VA
vaRoutes.get("/timesheet/adjustments/list/:status?/:projectId?/:vaId?", get_adjustment_requests_list); // show all adjustment list to approve and reject // vaRoutes.get("/timesheet/adjustments/list/:status?/:projectId?/:vaId?", get_adjustment_requests_list); // show all adjustment list to approve and reject
vaRoutes.patch("/timesheet/adjustments/:adjustmentId", va_timesheet_manage_adjustment_request); vaRoutes.patch("/timesheet/adjustments/:adjustmentId", va_timesheet_manage_adjustment_request);
// Leaves routes
vaRoutes.get("/leaves/all", authValidator, va_leave_list_function); // Get all leaves
vaRoutes.get("/leaves/status/:status", authValidator, va_leave_list_function); // Leaves Filter by status
vaRoutes.get("/leaves/type/:leaveType", authValidator, va_leave_list_function); // Filter by leave type
vaRoutes.get("/leaves/status/:status/type/:leaveType", authValidator, va_leave_list_function); // leaves Filter
vaRoutes.post("/leave/request", authValidator, va_leave_apply_function); // Timesheet Adjustment for VA
vaRoutes.patch("/leave/:leaveId", va_leave_manage_function);
// timesheet adjustment routes // timesheet adjustment routes
// Example: GET /va/search?name=john&email=@example.com&from=2025-01-01&to=2025-01-31 (NC) // Example: GET /va/search?name=john&email=@example.com&from=2025-01-01&to=2025-01-31 (NC)