Added time adjustment api
This commit is contained in:
parent
7fac56b5ec
commit
dd118a2359
|
|
@ -40,7 +40,7 @@ async function va_login_callback(req, res) {
|
|||
name: user.userDetails.firstName,
|
||||
},
|
||||
process.env.JWT_SECRET_ACCESS_TOKEN || "DocTrackerSecretKey",
|
||||
{ expiresIn: "24h" }
|
||||
// { expiresIn: "24h" }
|
||||
);
|
||||
|
||||
// Generate JWT refreshToken with 24h expiry
|
||||
|
|
@ -51,7 +51,7 @@ async function va_login_callback(req, res) {
|
|||
name: user.userDetails.firstName,
|
||||
},
|
||||
process.env.JWT_SECRET_REFRESH_TOKEN || "DocTrackerSecretKeyRefreshToken",
|
||||
{ expiresIn: "30d" }
|
||||
// { expiresIn: "30d" }
|
||||
);
|
||||
|
||||
await VAModel.updateOne({ "userDetails.email": userDetails.email }, {
|
||||
|
|
|
|||
|
|
@ -1,151 +0,0 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
import { VAModel, ProjectModel, LoggedModel } from "../../../models/vaModel.js";
|
||||
|
||||
// Adjust missing hours by creating new records
|
||||
async function va_timesheet_adjustment_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;
|
||||
const { projectId, date } = req.params;
|
||||
const { loginTime, logoutTime, reason } = req.body;
|
||||
|
||||
if (!projectId || !date) {
|
||||
return res.status(400).json({ success: false, message: "Project ID and date are required" });
|
||||
}
|
||||
|
||||
if (!loginTime || !logoutTime) {
|
||||
return res.status(400).json({ success: false, message: "Login time and logout time are required" });
|
||||
}
|
||||
|
||||
// Validate project exists and VA is assigned to it
|
||||
const project = await ProjectModel.findOne({
|
||||
_id: projectId,
|
||||
vaAssigned: vaId
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
return res.status(404).json({ success: false, message: "Project not found or VA not assigned to this project" });
|
||||
}
|
||||
|
||||
// Parse and validate times
|
||||
const adjustmentDate = new Date(date);
|
||||
const loginDateTime = new Date(loginTime);
|
||||
const logoutDateTime = new Date(logoutTime);
|
||||
|
||||
// Ensure times are within the target date
|
||||
const startOfDay = new Date(adjustmentDate);
|
||||
startOfDay.setHours(0, 0, 0, 0);
|
||||
|
||||
const endOfDay = new Date(adjustmentDate);
|
||||
endOfDay.setHours(23, 59, 59, 999);
|
||||
|
||||
if (loginDateTime < startOfDay || loginDateTime > endOfDay ||
|
||||
logoutDateTime < startOfDay || logoutDateTime > endOfDay) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Login and logout times must be within the specified date"
|
||||
});
|
||||
}
|
||||
|
||||
if (loginDateTime >= logoutDateTime) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Login time must be before logout time"
|
||||
});
|
||||
}
|
||||
|
||||
// Check for existing logs that might conflict with this adjustment
|
||||
const existingLogs = await LoggedModel.find({
|
||||
vaId,
|
||||
projectId,
|
||||
time: {
|
||||
$gte: loginDateTime,
|
||||
$lte: logoutDateTime
|
||||
}
|
||||
});
|
||||
|
||||
if (existingLogs.length > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "There are existing logs in the specified time range. Please choose a different time range."
|
||||
});
|
||||
}
|
||||
|
||||
// Create adjustment logs
|
||||
const loginLog = new LoggedModel({
|
||||
loggedType: "LOGIN",
|
||||
time: loginDateTime,
|
||||
vaId,
|
||||
vaEmail,
|
||||
projectId,
|
||||
isAdjustment: true,
|
||||
adjustmentReason: reason
|
||||
});
|
||||
|
||||
const logoutLog = new LoggedModel({
|
||||
loggedType: "LOGOUT",
|
||||
time: logoutDateTime,
|
||||
vaId,
|
||||
vaEmail,
|
||||
projectId,
|
||||
isAdjustment: true,
|
||||
adjustmentReason: reason
|
||||
});
|
||||
|
||||
// Save both logs in transaction
|
||||
await loginLog.save();
|
||||
await logoutLog.save();
|
||||
|
||||
// Calculate adjusted hours
|
||||
const adjustedSeconds = (logoutDateTime - loginDateTime) / 1000;
|
||||
const formatTime = (seconds) => {
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const response = {
|
||||
success: true,
|
||||
message: "Timesheet adjustment completed successfully",
|
||||
data: {
|
||||
adjustment: {
|
||||
date: adjustmentDate.toISOString().split('T')[0],
|
||||
loginTime: loginDateTime.toISOString(),
|
||||
logoutTime: logoutDateTime.toISOString(),
|
||||
adjustedHours: formatTime(adjustedSeconds),
|
||||
adjustedHoursDecimal: (adjustedSeconds / 3600).toFixed(2),
|
||||
reason: reason
|
||||
},
|
||||
logs: {
|
||||
loginLogId: loginLog._id,
|
||||
logoutLogId: logoutLog._id
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return res.status(201).json(response);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Timesheet adjustment error:", error);
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
|
||||
export { va_timesheet_adjustment_function };
|
||||
|
|
@ -34,7 +34,7 @@ async function va_profile_callback(req, res) {
|
|||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "VA profile",
|
||||
data: vaUser
|
||||
data: vaUser.userDetails
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
import { LoggedModel, WorkTrackModel, VAModel } from "../../../models/vaModel.js";
|
||||
|
||||
function formatHMS(ms) {
|
||||
|
|
@ -10,6 +11,23 @@ function formatHMS(ms) {
|
|||
|
||||
async function va_projects_report_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;
|
||||
const { projectId, from, to } = req.params;
|
||||
|
||||
if (!projectId) {
|
||||
|
|
@ -25,6 +43,7 @@ async function va_projects_report_function(req, res) {
|
|||
// Get all VA login/logout logs for this project in range
|
||||
const logs = await LoggedModel.find({
|
||||
projectId,
|
||||
vaId,
|
||||
time: { $gte: fromDate, $lte: toDate }
|
||||
}).sort({ time: 1 }).lean();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
import { AdjustedHoursModel } from "../../../models/vaModel.js";
|
||||
|
||||
// Get all adjustment requests (with filtering options)
|
||||
async function get_adjustment_requests_list(req, res) {
|
||||
try {
|
||||
const { status, projectId, vaId } = req.params;
|
||||
|
||||
// Build filter object
|
||||
const filter = {};
|
||||
if (status) filter.status = status;
|
||||
if (projectId) filter.projectId = projectId;
|
||||
if (vaId) filter.vaId = vaId;
|
||||
|
||||
const adjustments = await AdjustedHoursModel.find(filter)
|
||||
.sort({ createdAt: -1 })
|
||||
.populate('vaId', 'userDetails.name userDetails.email')
|
||||
.populate('projectId', 'name')
|
||||
.lean();
|
||||
|
||||
// Format response
|
||||
const formattedData = adjustments.map(adj => ({
|
||||
id: adj._id,
|
||||
va: {
|
||||
id: adj.vaId._id,
|
||||
name: adj.vaId.userDetails?.name,
|
||||
email: adj.vaId.userDetails?.email
|
||||
},
|
||||
project: {
|
||||
id: adj.projectId._id,
|
||||
name: adj.projectId.name
|
||||
},
|
||||
date: adj.date,
|
||||
loginTime: adj.loginTime,
|
||||
logoutTime: adj.logoutTime,
|
||||
comment: adj.comment,
|
||||
status: adj.status,
|
||||
requestedAt: adj.createdAt,
|
||||
reviewedAt: adj.reviewedAt,
|
||||
rejectionReason: adj.rejectionReason,
|
||||
adjustedHours: formatTime((new Date(adj.logoutTime) - new Date(adj.loginTime)) / 1000)
|
||||
}));
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
data: formattedData,
|
||||
pagination: {
|
||||
currentPage: adjustments.page,
|
||||
totalPages: adjustments.totalPages,
|
||||
totalItems: adjustments.totalDocs,
|
||||
hasNext: adjustments.hasNextPage,
|
||||
hasPrev: adjustments.hasPrevPage
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Get adjustment requests error:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal Server Error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(seconds) {
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${h}h ${m}m ${s}s`;
|
||||
}
|
||||
|
||||
export { get_adjustment_requests_list }
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
import { AdjustedHoursModel, LoggedModel } from "../../../models/vaModel.js";
|
||||
|
||||
// Approve or reject adjustment request
|
||||
async function va_timesheet_manage_adjustment_request(req, res) {
|
||||
try {
|
||||
const { adjustmentId } = req.params;
|
||||
const { action, rejectionReason } = req.body; // action: "approve" or "reject"
|
||||
|
||||
if (!adjustmentId || !action) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Adjustment ID and action are required"
|
||||
});
|
||||
}
|
||||
|
||||
// Find adjustment request
|
||||
const adjustment = await AdjustedHoursModel.findById(adjustmentId)
|
||||
.populate('vaId', 'userDetails.email');
|
||||
|
||||
if (!adjustment) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Adjustment request not found"
|
||||
});
|
||||
}
|
||||
|
||||
if (adjustment.status !== "PENDING") {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Adjustment request has already been processed"
|
||||
});
|
||||
}
|
||||
|
||||
if (action === "approve") {
|
||||
// Create new log entries in LoggedModel
|
||||
const loginLog = new LoggedModel({
|
||||
loggedType: "LOGIN",
|
||||
time: adjustment.loginTime,
|
||||
vaId: adjustment.vaId._id,
|
||||
vaEmail: adjustment.vaId.userDetails.email,
|
||||
projectId: adjustment.projectId,
|
||||
isAdjustment: true,
|
||||
adjustmentId: adjustment._id
|
||||
});
|
||||
|
||||
const logoutLog = new LoggedModel({
|
||||
loggedType: "LOGOUT",
|
||||
time: adjustment.logoutTime,
|
||||
vaId: adjustment.vaId._id,
|
||||
vaEmail: adjustment.vaId.userDetails.email,
|
||||
projectId: adjustment.projectId,
|
||||
isAdjustment: true,
|
||||
adjustmentId: adjustment._id
|
||||
});
|
||||
|
||||
await loginLog.save();
|
||||
await logoutLog.save();
|
||||
|
||||
// Update adjustment record
|
||||
adjustment.status = "APPROVED";
|
||||
adjustment.reviewedAt = new Date();
|
||||
adjustment.adjustedLogs = [
|
||||
{ loginLogId: loginLog._id },
|
||||
{ logoutLogId: logoutLog._id }
|
||||
];
|
||||
|
||||
await adjustment.save();
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Adjustment request approved successfully",
|
||||
data: {
|
||||
adjustmentId: adjustment._id,
|
||||
status: "APPROVED",
|
||||
loginLogId: loginLog._id,
|
||||
logoutLogId: logoutLog._id,
|
||||
adjustedHours: formatTime((adjustment.logoutTime - adjustment.loginTime) / 1000)
|
||||
}
|
||||
});
|
||||
|
||||
} else if (action === "reject") {
|
||||
// Update adjustment record as rejected
|
||||
adjustment.status = "REJECTED";
|
||||
adjustment.reviewedAt = new Date();
|
||||
adjustment.rejectionReason = rejectionReason || "No reason provided";
|
||||
|
||||
await adjustment.save();
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Adjustment request rejected",
|
||||
data: {
|
||||
adjustmentId: adjustment._id,
|
||||
status: "REJECTED",
|
||||
rejectionReason: adjustment.rejectionReason
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid action. Use 'approve' or 'reject'"
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Manage adjustment error:", error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal Server Error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(seconds) {
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${h}h ${m}m ${s}s`;
|
||||
}
|
||||
|
||||
export { va_timesheet_manage_adjustment_request }
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
import { VAModel, ProjectModel, AdjustedHoursModel } from "../../../models/vaModel.js";
|
||||
|
||||
// Adjust missing hours by creating new records
|
||||
async function va_timesheet_adjustment_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;
|
||||
const { projectId, date, loginTime, logoutTime, comment } = req.body;
|
||||
|
||||
if (!projectId || !date || !loginTime || !logoutTime || !comment) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Project ID, date, loginTime, logoutTime, and comment are required"
|
||||
});
|
||||
}
|
||||
|
||||
// Validate project exists and VA is assigned to it
|
||||
const project = await ProjectModel.findOne({
|
||||
_id: projectId,
|
||||
vaAssigned: vaId
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
return res.status(404).json({ success: false, message: "Project not found or VA not assigned to this project" });
|
||||
}
|
||||
|
||||
let adjustmentDate;
|
||||
try {
|
||||
adjustmentDate = new Date(date);
|
||||
if (isNaN(adjustmentDate.getTime())) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid date format. Use YYYY-MM-DD"
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid date format"
|
||||
});
|
||||
}
|
||||
|
||||
const combineDateTime = (dateObj, timeStr) => {
|
||||
const [hours, minutes, seconds] = timeStr.split(':').map(Number);
|
||||
|
||||
// Extract date components from the Date object
|
||||
const year = dateObj.getFullYear();
|
||||
const month = dateObj.getMonth();
|
||||
const day = dateObj.getDate();
|
||||
|
||||
// Create UTC date directly
|
||||
return new Date(Date.UTC(year, month, day, hours, minutes, seconds));
|
||||
};
|
||||
|
||||
const loginDateTime = combineDateTime(adjustmentDate, loginTime);
|
||||
const logoutDateTime = combineDateTime(adjustmentDate, logoutTime);
|
||||
|
||||
if (isNaN(adjustmentDate.getTime()) || isNaN(loginDateTime.getTime()) || isNaN(logoutDateTime.getTime())) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid date format"
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure login time is before logout time
|
||||
if (loginDateTime >= logoutDateTime) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Login time must be before logout time"
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure times are within the target date
|
||||
const startOfDay = new Date(adjustmentDate);
|
||||
startOfDay.setHours(0, 0, 0, 0);
|
||||
|
||||
const endOfDay = new Date(adjustmentDate);
|
||||
endOfDay.setHours(23, 59, 59, 999);
|
||||
|
||||
if (loginDateTime < startOfDay || loginDateTime > endOfDay ||
|
||||
logoutDateTime < startOfDay || logoutDateTime > endOfDay) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Login and logout times must be within the specified date"
|
||||
});
|
||||
}
|
||||
|
||||
const existingAdjustment = await AdjustedHoursModel.findOne({
|
||||
vaId,
|
||||
projectId,
|
||||
date: startOfDay,
|
||||
status: "PENDING"
|
||||
});
|
||||
|
||||
if (existingAdjustment) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "You already have a pending adjustment request for this date and project"
|
||||
});
|
||||
}
|
||||
|
||||
const adjustmentRequest = new AdjustedHoursModel({
|
||||
vaId,
|
||||
projectId,
|
||||
date: startOfDay,
|
||||
loginTime: loginDateTime,
|
||||
logoutTime: logoutDateTime,
|
||||
comment,
|
||||
status: "PENDING"
|
||||
});
|
||||
|
||||
await adjustmentRequest.save();
|
||||
|
||||
const adjustedSeconds = (logoutDateTime - loginDateTime) / 1000;
|
||||
const formatTime = (seconds) => {
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds % 3600) / 60);
|
||||
const s = Math.floor(seconds % 60);
|
||||
return `${h}h ${m}m ${s}s`;
|
||||
};
|
||||
|
||||
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) {
|
||||
console.error("Timesheet adjustment error:", error);
|
||||
return res.status(500).json({ success: false, message: "Internal Server Error" });
|
||||
}
|
||||
}
|
||||
|
||||
export { va_timesheet_adjustment_function };
|
||||
|
|
@ -86,7 +86,7 @@ async function va_timesheet_summary_function(req, res) {
|
|||
if (!firstLoginTime || logTime < firstLoginTime) {
|
||||
firstLoginTime = logTime;
|
||||
}
|
||||
} else if (log.loggedType === "LOGOUT" && lastLogin) {
|
||||
} else if (log.loggedType === "LOGOUT") {
|
||||
if (loginStack.length > 0) {
|
||||
// Pop the last login from stack
|
||||
const lastLogin = loginStack.pop();
|
||||
|
|
@ -157,7 +157,6 @@ async function va_timesheet_summary_function(req, res) {
|
|||
const response = {
|
||||
success: true,
|
||||
data: {
|
||||
date: targetDate.toISOString().split('T')[0],
|
||||
project: {
|
||||
id: project._id,
|
||||
name: project.name,
|
||||
|
|
@ -171,10 +170,10 @@ async function va_timesheet_summary_function(req, res) {
|
|||
summary: {
|
||||
totalWorkedHours: formatTime(totalWorkSeconds),
|
||||
totalWorkedHoursDetailed: formatTimeDetailed(totalWorkSeconds),
|
||||
totalWorkedHoursDecimal: totalWorkedHours.toFixed(4),
|
||||
totalWorkedHoursDecimal: totalWorkedHours.toFixed(2),
|
||||
totalWorkedMinutes: (totalWorkSeconds / 60).toFixed(2),
|
||||
dedicatedHours: dedicatedHours,
|
||||
pendingHours: pendingHours.toFixed(4),
|
||||
pendingHours: pendingHours.toFixed(2),
|
||||
firstLoginTime: firstLoginTime ? firstLoginTime.toISOString() : null,
|
||||
lastLogoutTime: lastLogoutTime ? lastLogoutTime.toISOString() : null
|
||||
},
|
||||
|
|
@ -358,6 +358,57 @@ passwordResetSchema.index({ email: 1, used: 1 });
|
|||
passwordResetSchema.index({ token: 1, used: 1, expiresAt: 1 });
|
||||
passwordResetSchema.index({ userType: 1, email: 1 });
|
||||
|
||||
|
||||
const adjustedHoursSchema = new Schema({
|
||||
vaId: {
|
||||
type: Types.ObjectId,
|
||||
ref: "VA",
|
||||
required: true
|
||||
},
|
||||
projectId: {
|
||||
type: Types.ObjectId,
|
||||
ref: "Project",
|
||||
required: true
|
||||
},
|
||||
date: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
loginTime: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
logoutTime: {
|
||||
type: Date,
|
||||
required: true
|
||||
},
|
||||
comment: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: ["PENDING", "APPROVED", "REJECTED"],
|
||||
default: "PENDING"
|
||||
},
|
||||
reviewedAt: {
|
||||
type: Date
|
||||
},
|
||||
rejectionReason: {
|
||||
type: String // If rejected, store the reason
|
||||
},
|
||||
adjustedLogs: [{
|
||||
loginLogId: {
|
||||
type: Types.ObjectId,
|
||||
ref: "Logged"
|
||||
},
|
||||
logoutLogId: {
|
||||
type: Types.ObjectId,
|
||||
ref: "Logged"
|
||||
}
|
||||
}]
|
||||
}, { timestamps: true });
|
||||
|
||||
/* ---------------------- Model Exports ---------------------- */
|
||||
const VAModel = model("VA", vaSchema);
|
||||
const ClientModel = model("Client", clientSchema);
|
||||
|
|
@ -365,5 +416,6 @@ const ProjectModel = model("Project", projectSchema);
|
|||
const LoggedModel = model("TrackLog", loggedSchema);
|
||||
const WorkTrackModel = model("TrackWork", vaWorkTrackSchema);
|
||||
const PasswordResetModel = model("PasswordReset", passwordResetSchema);
|
||||
const AdjustedHoursModel = model("AdjustedHours", adjustedHoursSchema);
|
||||
|
||||
export { VAModel, ClientModel, ProjectModel, LoggedModel, WorkTrackModel, PasswordResetModel };
|
||||
export { VAModel, ClientModel, ProjectModel, LoggedModel, WorkTrackModel, PasswordResetModel, AdjustedHoursModel };
|
||||
|
|
@ -18,8 +18,10 @@ import { va_log_work_report_callback } from "../../controllers/va/vaLog/vaLogWor
|
|||
import { va_projects_report_function } from "../../controllers/va/vaProjects/vaProjectReport.controller.js";
|
||||
import { va_projects_log_list_function } from "../../controllers/va/vaProjects/vaProjectLogList.controller.js";
|
||||
import { va_request_password_reset } from "../../controllers/passwordReset.controllers.js";
|
||||
import { va_timesheet_summary_function } from "../../controllers/va/vaLog/vaTimesheetAdjustmentSummary.controller.js";
|
||||
import { va_timesheet_adjustment_function } from "../../controllers/va/vaLog/vaTimesheetAdjustment.controller.js";
|
||||
import { va_timesheet_summary_function } from "../../controllers/va/vaTimesheetAdjustment/vaTimesheetAdjustmentSummary.controller.js";
|
||||
import { va_timesheet_adjustment_function } from "../../controllers/va/vaTimesheetAdjustment/vaTimesheetAdjustment.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"
|
||||
|
||||
vaRoutes.get("/", va_callback); // VA Callback for getting active VA
|
||||
|
||||
|
|
@ -36,9 +38,13 @@ vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update
|
|||
|
||||
vaRoutes.get("/:userName", va_username_callback); // VA Username Callback (NC)
|
||||
|
||||
|
||||
// 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.post("/timesheet/adjustment/:projectId/:date", authValidator, va_timesheet_adjustment_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.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);
|
||||
|
||||
// timesheet adjustment routes
|
||||
|
||||
// Example: GET /va/search?name=john&email=@example.com&from=2025-01-01&to=2025-01-31 (NC)
|
||||
vaRoutes.get("/search", va_filter_search);
|
||||
|
|
|
|||
Loading…
Reference in New Issue