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 };