diff --git a/src/controllers/va/vaProjects/vaProjectLog.controller.js b/src/controllers/va/vaProjects/vaProjectLog.controller.js index da0de73..4582d3b 100644 --- a/src/controllers/va/vaProjects/vaProjectLog.controller.js +++ b/src/controllers/va/vaProjects/vaProjectLog.controller.js @@ -1,3 +1,4 @@ +import mongoose from "mongoose"; import { LoggedModel, WorkTrackModel, ProjectModel, VAModel } from "../../../models/vaModel.js"; import authFetch from "../../../utils/authFetch.js"; import getDayNumber from "../../../utils/getDayNumber.js"; @@ -6,10 +7,10 @@ import getDayNumber from "../../../utils/getDayNumber.js"; function getDayRange(date = new Date()) { const start = new Date(date); start.setUTCHours(0, 0, 0, 0); - + const end = new Date(date); end.setUTCHours(23, 59, 59, 999); - + return { start, end }; } @@ -17,7 +18,7 @@ function formatHoursToReadable(hours) { const wholeHours = Math.floor(hours); const decimalMinutes = (hours - wholeHours) * 60; const wholeMinutes = Math.round(decimalMinutes); - + if (wholeHours > 0 && wholeMinutes > 0) { return `${wholeHours}h ${wholeMinutes}m`; } else if (wholeHours > 0) { @@ -30,11 +31,11 @@ function formatHoursToReadable(hours) { // Helper function to parse ISO time string function parseISOTimeString(timeString) { if (!timeString) return new Date(); - + try { // Parse ISO string directly const parsedDate = new Date(timeString); - + // Check if the date is valid if (isNaN(parsedDate.getTime())) { throw new Error('Invalid date'); @@ -66,84 +67,84 @@ function getCurrentUTCTime() { // function parseTimeToMinutes(timeString) { // if (!timeString) return null; - + // const [hours, minutes] = timeString.split(':').map(Number); // return hours * 60 + (minutes || 0); // } // function parseDelayToMinutes(delayString) { // if (!delayString) return 0; - + // if (delayString.includes('h')) { // const hours = parseInt(delayString.replace('h', '').trim()); // return hours * 60; // } else if (delayString.includes('m')) { // return parseInt(delayString.replace('m', '').trim()); // } - + // return parseInt(delayString) || 0; // Default to minutes if no unit specified // } // function checkScheduleCompliance(currentTime, project, vaStatus, type) { // const warnings = []; - + // // If project doesn't have schedule or VA is PARTTIME with flexible allowed, no schedule restrictions // if (!project.vaWorkingScheduleStart || !project.vaWorkingScheduleEnd) { // return { isCompliant: true, warnings }; // } - + // const currentDate = new Date(currentTime); // const currentMinutes = currentDate.getUTCHours() * 60 + currentDate.getUTCMinutes(); - + // const scheduleStartMinutes = parseTimeToMinutes(project.vaWorkingScheduleStart); // const scheduleEndMinutes = parseTimeToMinutes(project.vaWorkingScheduleEnd); // const allowedStartDelay = parseDelayToMinutes(project.vaWorkingDelayAllowedStart); // const allowedEndDelay = parseDelayToMinutes(project.vaWorkingDelayAllowedEnd); - + // // Calculate allowed time ranges // const allowedStartMinutes = scheduleStartMinutes - allowedStartDelay; // const allowedEndMinutes = scheduleEndMinutes + allowedEndDelay; - + // console.log(`Schedule Check - Current: ${currentMinutes}, Start: ${scheduleStartMinutes}, End: ${scheduleEndMinutes}, Allowed Start: ${allowedStartMinutes}, Allowed End: ${allowedEndMinutes}`); - + // // Check for PARTTIME with flexible schedule // if (vaStatus === "PARTTIME" && project.isFlexibleAllowed) { // return { isCompliant: true, warnings }; // } - + // // For all other cases, check schedule compliance // if (type === "LOGIN") { // // Check if login is too early // if (currentMinutes < allowedStartMinutes) { // warnings.push("early_login"); // } - + // // Check if login is after shift end // if (currentMinutes > scheduleEndMinutes) { // warnings.push("working_after_shift_end"); // } - + // // Check if login is within allowed range // // const isWithinAllowedRange = currentMinutes >= allowedStartMinutes && currentMinutes <= allowedEndMinutes; - + // return { // isCompliant: true, // Allow login regardless of time // warnings // }; - + // } else if (type === "LOGOUT") { // // Check if logout is too late // if (currentMinutes > allowedEndMinutes) { // warnings.push("late_logout"); // } - + // // For logout, we're generally more permissive - allow logout even if late // return { // isCompliant: true, // Allow logout regardless of time // warnings // }; // } - + // return { isCompliant: true, warnings }; // } @@ -151,7 +152,7 @@ function getCurrentUTCTime() { async function closePreviousSession(vaId, lastLog, logTime = new Date()) { try { const currentTime = logTime; - + // Find all open work tracks for this VA and project (where to is not set or totalTime is 0) const openWorkTracks = await WorkTrackModel.find({ vaId, @@ -162,22 +163,22 @@ async function closePreviousSession(vaId, lastLog, logTime = new Date()) { { to: null } ] }); - + // console.log(`Found ${openWorkTracks.length} open work tracks to close`); - + // Close all open work tracks for (const workTrack of openWorkTracks) { const fromTime = new Date(workTrack.from); const totalTimeMs = currentTime - fromTime; - + // Update the work track with end time and total time workTrack.to = currentTime; workTrack.totalTime = totalTimeMs; await workTrack.save(); - + // console.log(`Closed work track ${workTrack._id} with duration: ${totalTimeMs}ms`); } - + // Create a logout log entry const logoutLog = await LoggedModel.create({ loggedType: "LOGOUT", @@ -186,18 +187,18 @@ async function closePreviousSession(vaId, lastLog, logTime = new Date()) { projectId: lastLog.projectId, time: currentTime // Use same timestamp for consistent logging }); - + // 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, - } + "_id": logoutLog._id, + "loggedType": "LOGOUT", + "vaId": vaId, + "vaEmail": lastLog.vaEmail, + "projectId": lastLog.projectId, + "time": logoutLog.time, + } const response = await authFetch(salesforceUrl, { method: 'POST', @@ -208,7 +209,7 @@ async function closePreviousSession(vaId, lastLog, logTime = new Date()) { }, body: JSON.stringify(sfDataSync) }); - + if (!response.ok) { const errorText = await response.text(); throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); @@ -216,7 +217,7 @@ async function closePreviousSession(vaId, lastLog, logTime = new Date()) { const result = await response.json(); // console.log("✅ Successfully synced VA data to Salesforce:", result); - + return { closedWorkTracks: openWorkTracks.length, logoutLogId: logoutLog._id, @@ -225,13 +226,13 @@ async function closePreviousSession(vaId, lastLog, logTime = new Date()) { } 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) { console.error("Error closing previous session:", error); throw error; @@ -244,7 +245,7 @@ async function va_project_log_callback(req, res) { const payload = req.body || {}; const { vaId, vaEmail, projectId, type, time: receivedTimeString } = payload; // console.log("Received payload:", payload); - + if (!vaId || !vaEmail || !projectId || !type) { return res.status(400).send("Missing required properties"); } @@ -252,11 +253,11 @@ async function va_project_log_callback(req, res) { // Determine the log time based on received time (both in UTC) const currentTime = getCurrentUTCTime(); // Current UTC time let logTime = currentTime; - + if (receivedTimeString) { // Parse the received ISO time string const receivedTime = parseISOTimeString(receivedTimeString); - + // Check if the difference is less than 10 seconds if (isTimeWithinThreshold(receivedTime, currentTime, 10)) { logTime = receivedTime; @@ -296,7 +297,7 @@ async function va_project_log_callback(req, res) { const { logId } = payload; const workTrackData = await WorkTrackModel.find({ - logId: logId + logId: new mongoose.Types.ObjectId(logId) }); // console.log(`Found work track data for logout:`, workTrackData); @@ -311,23 +312,23 @@ async function va_project_log_callback(req, res) { if (updatedLogs.length > 0) { const lastLogIndex = updatedLogs.length - 1; const lastLogEntry = updatedLogs[lastLogIndex]; - + // Update the last log's 'to' time and recalculate its totalTime const lastLogFromTime = new Date(lastLogEntry.from); const lastLogTotalTime = Math.abs(toLogTime - lastLogFromTime); - + updatedLogs[lastLogIndex] = { ...lastLogEntry, to: toLogTime, totalTime: lastLogTotalTime }; - + // console.log(`Updated last log in array - from: ${lastLogFromTime.toISOString()}, to: ${toLogTime.toISOString()}, totalTime: ${lastLogTotalTime}ms`); } const updateResult = await WorkTrackModel.updateOne( { _id: workTrackData[0]._id }, - { + { $set: { to: toLogTime, totalTime: totalTimeMs, @@ -356,10 +357,10 @@ async function va_project_log_callback(req, res) { } // Create the logout log entry - const createdLog = await LoggedModel.create({ - loggedType: type, - vaId, - vaEmail, + const createdLog = await LoggedModel.create({ + loggedType: type, + vaId, + vaEmail, projectId, time: logTime }); @@ -367,13 +368,13 @@ async function va_project_log_callback(req, res) { try { const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; const sfData = { - "_id": createdLog._id, - "loggedType": type, - "vaId": vaId, - "vaEmail": vaEmail, - "projectId": projectId, - "time": createdLog.time, - } + "_id": createdLog._id, + "loggedType": type, + "vaId": vaId, + "vaEmail": vaEmail, + "projectId": projectId, + "time": createdLog.time, + } const response = await authFetch(salesforceUrl, { method: 'POST', @@ -382,18 +383,18 @@ async function va_project_log_callback(req, res) { }, body: JSON.stringify(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(); - - return res.status(201).json({ - success: true, - message: `${type} Successfully`, - logId: createdLog._id, + + return res.status(201).json({ + success: true, + message: `${type} Successfully`, + logId: createdLog._id, SyncSalesforce: result, timeUsed: receivedTimeString ? 'received' : 'current', actualTime: createdLog.time, @@ -401,11 +402,11 @@ async function va_project_log_callback(req, res) { } catch (error) { console.error("Failed to sync with Salesforce:", error.message); console.log(error) - + // Still return success since the logout was recorded locally - return res.status(201).json({ - success: true, - message: `${type} Successfully (Salesforce sync failed)`, + return res.status(201).json({ + success: true, + message: `${type} Successfully (Salesforce sync failed)`, logId: createdLog._id, timeUsed: receivedTimeString ? 'received' : 'current', actualTime: createdLog.time, @@ -423,15 +424,15 @@ async function va_project_log_callback(req, res) { projectId, from: { $gte: start, $lte: end } }); - + const totalTodayMs = todayWorkEntries.reduce((total, entry) => { return total + (entry.totalTime || 0); }, 0); - + const totalTodayHours = totalTodayMs / (1000 * 60 * 60); const dayNumber = getDayNumber(logTime); - + // Check if daily hours are already completed if (project.dedicatedHours[dayNumber] > 0 && totalTodayHours >= project.dedicatedHours[dayNumber]) { return res.status(400).json({ @@ -456,10 +457,10 @@ async function va_project_log_callback(req, res) { // console.log(`Previous session closed: ${closeResult.closedWorkTracks} work tracks, logout log: ${closeResult.logoutLogId}`); // Now proceed to create new login - const createdLog = await LoggedModel.create({ - loggedType: "LOGIN", - vaId, - vaEmail, + const createdLog = await LoggedModel.create({ + loggedType: "LOGIN", + vaId, + vaEmail, projectId, time: logTime }); @@ -467,13 +468,13 @@ async function va_project_log_callback(req, res) { try { const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; const sfData = { - "_id": createdLog._id, - "loggedType": "LOGIN", - "vaId": vaId, - "vaEmail": vaEmail, - "projectId": projectId, - "time": createdLog.time, - } + "_id": createdLog._id, + "loggedType": "LOGIN", + "vaId": vaId, + "vaEmail": vaEmail, + "projectId": projectId, + "time": createdLog.time, + } const response = await authFetch(salesforceUrl, { method: 'POST', @@ -484,7 +485,7 @@ async function va_project_log_callback(req, res) { }, body: JSON.stringify(sfData) }); - + if (!response.ok) { const errorText = await response.text(); throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); @@ -492,11 +493,11 @@ async function va_project_log_callback(req, res) { const result = await response.json(); // console.log("✅ Successfully synced VA data to Salesforce:", result); - - return res.status(201).json({ - success: true, - message: `${type} Successfully`, - logId: createdLog._id, + + return res.status(201).json({ + success: true, + message: `${type} Successfully`, + logId: createdLog._id, SyncSalesforce: result, previousSession: { closedWorkTracks: closeResult.closedWorkTracks, @@ -509,7 +510,7 @@ async function va_project_log_callback(req, res) { } 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; @@ -518,10 +519,10 @@ async function va_project_log_callback(req, res) { } } - const createdLog = await LoggedModel.create({ - loggedType: type, - vaId, - vaEmail, + const createdLog = await LoggedModel.create({ + loggedType: type, + vaId, + vaEmail, projectId, time: logTime }); @@ -529,13 +530,13 @@ async function va_project_log_callback(req, res) { try { const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; const sfData = { - "_id": createdLog._id, - "loggedType": type, - "vaId": vaId, - "vaEmail": vaEmail, - "projectId": projectId, - "time": createdLog.time, - } + "_id": createdLog._id, + "loggedType": type, + "vaId": vaId, + "vaEmail": vaEmail, + "projectId": projectId, + "time": createdLog.time, + } const response = await authFetch(salesforceUrl, { method: 'POST', @@ -546,7 +547,7 @@ async function va_project_log_callback(req, res) { }, body: JSON.stringify(sfData) }); - + if (!response.ok) { const errorText = await response.text(); throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); @@ -554,11 +555,11 @@ async function va_project_log_callback(req, res) { const result = await response.json(); // console.log("✅ Successfully synced VA data to Salesforce:", result); - - res.status(201).json({ - success: true, - message: `${type} Successfully`, - logId: createdLog._id, + + res.status(201).json({ + success: true, + message: `${type} Successfully`, + logId: createdLog._id, SyncSalesforce: result, timeUsed: receivedTimeString ? 'received' : 'current', actualTime: createdLog.time, @@ -567,7 +568,7 @@ async function va_project_log_callback(req, res) { } 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;