224 lines
8.3 KiB
JavaScript
224 lines
8.3 KiB
JavaScript
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(year, month, day, hours, minutes, seconds);
|
|
const combined = new Date(dateObj);
|
|
combined.setHours(hours, minutes, seconds, 0);
|
|
|
|
return combined;
|
|
};
|
|
|
|
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`;
|
|
};
|
|
|
|
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: "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 }; |