Added days wise dedicated hours in projects
This commit is contained in:
parent
5f043ed80a
commit
46f2d7377f
|
|
@ -1,5 +1,6 @@
|
|||
import { VAModel, LoggedModel, ClientModel, ProjectModel } from "../../../models/vaModel.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
||||
|
||||
// Getting client dashboard data - all VAs, projects, and active VAs
|
||||
async function client_dashboard(req, res) {
|
||||
|
|
@ -314,9 +315,11 @@ async function client_dashboard(req, res) {
|
|||
};
|
||||
});
|
||||
|
||||
const dayNumber = getDayNumber(logTime);
|
||||
|
||||
// Calculate dashboard statistics
|
||||
const totalHoursLogged = clientProjects.reduce((sum, p) => sum + (p.totalLoggedHours || 0), 0);
|
||||
const totalDedicatedHours = clientProjects.reduce((sum, p) => sum + (p.dedicatedHours || 0), 0);
|
||||
const totalDedicatedHours = clientProjects.reduce((sum, p) => sum + (p.dedicatedHours[dayNumber] || 0), 0);
|
||||
|
||||
const currentlyActiveVAs = allVAsFormatted.filter(va => va.isCurrentlyActive);
|
||||
const inactiveVAs = allVAsFormatted.filter(va => !va.isCurrentlyActive);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { ClientModel, ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
||||
|
||||
function formatHoursToReadable(hours) {
|
||||
const wholeHours = Math.floor(hours);
|
||||
|
|
@ -112,13 +113,15 @@ async function client_dashboard_projects(req, res) {
|
|||
const validVADetails = assignedVADetails.filter(Boolean);
|
||||
const activeVAsCount = validVADetails.filter(va => va.isActive).length;
|
||||
|
||||
const dayNumber = getDayNumber();
|
||||
|
||||
return {
|
||||
id: project._id,
|
||||
name: project.name,
|
||||
hourlyRate: project.hourlyRate,
|
||||
vaAssigned: validVADetails.length,
|
||||
activeVAs: activeVAsCount,
|
||||
dedicatedHours: formatHoursToReadable(project.dedicatedHours) || "0h",
|
||||
dedicatedHours: formatHoursToReadable(project.dedicatedHours[dayNumber]) || "0h",
|
||||
status: project.status || '',
|
||||
assignedVADetails: validVADetails
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { ClientModel, VAModel } from "../../models/vaModel.js";
|
||||
import { va_timesheet_adjustment_function } from "../va/vaTimesheetAdjustment/vaTimesheetAdjustment.controller.js";
|
||||
|
||||
// Login VA
|
||||
async function va_login_client(req, res) {
|
||||
|
|
|
|||
|
|
@ -4,99 +4,99 @@ import { VAModel, LoggedModel, WorkTrackModel } from "../../../models/vaModel.js
|
|||
import authFetch from "../../../utils/authFetch.js";
|
||||
|
||||
// function to close open work tracks and create logout
|
||||
async function closePreviousSession(vaId, lastLog) {
|
||||
try {
|
||||
const currentTime = new Date();
|
||||
// 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,
|
||||
logId: lastLog._id,
|
||||
$or: [
|
||||
{ to: { $exists: false } },
|
||||
{ totalTime: 0 },
|
||||
{ to: null }
|
||||
]
|
||||
});
|
||||
// async function closePreviousSession(vaId, lastLog) {
|
||||
// try {
|
||||
// const currentTime = new Date();
|
||||
// // 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,
|
||||
// logId: lastLog._id,
|
||||
// $or: [
|
||||
// { to: { $exists: false } },
|
||||
// { totalTime: 0 },
|
||||
// { to: null }
|
||||
// ]
|
||||
// });
|
||||
|
||||
// console.log(`Found ${openWorkTracks.length} open work tracks to close`);
|
||||
// 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;
|
||||
// // 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();
|
||||
// // 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`);
|
||||
}
|
||||
// // console.log(`Closed work track ${workTrack._id} with duration: ${totalTimeMs}ms`);
|
||||
// }
|
||||
|
||||
// Create a logout log entry
|
||||
const logoutLog = await LoggedModel.create({
|
||||
loggedType: "LOGOUT",
|
||||
vaId,
|
||||
vaEmail: lastLog.vaEmail, // Use email from previous log
|
||||
projectId: lastLog.projectId,
|
||||
time: currentTime // Use same timestamp for consistent logging
|
||||
});
|
||||
// // Create a logout log entry
|
||||
// const logoutLog = await LoggedModel.create({
|
||||
// loggedType: "LOGOUT",
|
||||
// vaId,
|
||||
// vaEmail: lastLog.vaEmail, // Use email from previous log
|
||||
// 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,
|
||||
}
|
||||
// // 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 authFetch(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)
|
||||
});
|
||||
// const response = await authFetch(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}`);
|
||||
}
|
||||
// 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);
|
||||
// const result = await response.json();
|
||||
// // console.log("✅ Successfully synced VA data to Salesforce:", result);
|
||||
|
||||
return {
|
||||
closedWorkTracks: openWorkTracks.length,
|
||||
logoutLogId: logoutLog._id,
|
||||
SyncSalesforce: result
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to sync with Salesforce:", error.message);
|
||||
console.log(error)
|
||||
// return {
|
||||
// closedWorkTracks: openWorkTracks.length,
|
||||
// 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;
|
||||
}
|
||||
// // Create a custom error to distinguish Salesforce errors
|
||||
// const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
|
||||
// salesforceError.isSalesforceError = true;
|
||||
// throw salesforceError;
|
||||
// }
|
||||
|
||||
// return {
|
||||
// closedWorkTracks: openWorkTracks.length,
|
||||
// logoutLogId: logoutLog._id,
|
||||
// };
|
||||
// // return {
|
||||
// // closedWorkTracks: openWorkTracks.length,
|
||||
// // logoutLogId: logoutLog._id,
|
||||
// // };
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error closing previous session:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// } catch (error) {
|
||||
// console.error("Error closing previous session:", error);
|
||||
// throw error;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Login VA
|
||||
async function va_login_callback(req, res) {
|
||||
|
|
@ -132,17 +132,17 @@ async function va_login_callback(req, res) {
|
|||
// also on main login when user try to login into app then logout project and workModel track then allow login on same timestamp
|
||||
|
||||
// Check if VA has any active sessions (LOGIN without LOGOUT)
|
||||
const activeSessions = await LoggedModel.find({
|
||||
vaId: user._id
|
||||
}).sort({ time: -1 });
|
||||
// const activeSessions = await LoggedModel.find({
|
||||
// vaId: user._id
|
||||
// }).sort({ time: -1 });
|
||||
|
||||
let closeResult = [];
|
||||
// let closeResult = [];
|
||||
|
||||
if (activeSessions.length > 0) {
|
||||
// Get the latest login to check if there's no corresponding logout
|
||||
const latestLog = activeSessions[0];
|
||||
closeResult = await closePreviousSession(user._id, latestLog);
|
||||
}
|
||||
// if (activeSessions.length > 0) {
|
||||
// // Get the latest login to check if there's no corresponding logout
|
||||
// const latestLog = activeSessions[0];
|
||||
// closeResult = await closePreviousSession(user._id, latestLog);
|
||||
// }
|
||||
|
||||
// Generate JWT accessToken with 24h expiry
|
||||
const accessToken = jwt.sign(
|
||||
|
|
@ -172,23 +172,23 @@ async function va_login_callback(req, res) {
|
|||
|
||||
let responseData = [];
|
||||
|
||||
if (closeResult) {
|
||||
responseData = {
|
||||
success: true,
|
||||
message: "Login Successfull",
|
||||
accessToken,
|
||||
refreshToken,
|
||||
user: {
|
||||
id: user._id,
|
||||
name: user.userDetails.firstName,
|
||||
email: user.userDetails.email,
|
||||
},
|
||||
previousSession: {
|
||||
closedWorkTracks: closeResult.closedWorkTracks,
|
||||
logoutLogId: closeResult.logoutLogId
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if (closeResult) {
|
||||
// responseData = {
|
||||
// success: true,
|
||||
// message: "Login Successfull",
|
||||
// accessToken,
|
||||
// refreshToken,
|
||||
// user: {
|
||||
// id: user._id,
|
||||
// name: user.userDetails.firstName,
|
||||
// email: user.userDetails.email,
|
||||
// },
|
||||
// previousSession: {
|
||||
// closedWorkTracks: closeResult.closedWorkTracks,
|
||||
// logoutLogId: closeResult.logoutLogId
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
responseData = {
|
||||
success: true,
|
||||
message: "Login Successfull",
|
||||
|
|
@ -200,7 +200,7 @@ async function va_login_callback(req, res) {
|
|||
email: user.userDetails.email,
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
// Return accessToken and user info
|
||||
return res.status(200).json(responseData);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { LoggedModel, WorkTrackModel, ProjectModel, VAModel } from "../../../models/vaModel.js";
|
||||
import authFetch from "../../../utils/authFetch.js";
|
||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
||||
|
||||
// Helper function to get start and end of day in UTC
|
||||
function getDayRange(date = new Date()) {
|
||||
|
|
@ -428,12 +429,14 @@ async function va_project_log_callback(req, res) {
|
|||
}, 0);
|
||||
|
||||
const totalTodayHours = totalTodayMs / (1000 * 60 * 60);
|
||||
|
||||
const dayNumber = getDayNumber(logTime);
|
||||
|
||||
// Check if daily hours are already completed
|
||||
if (project.dedicatedHours > 0 && totalTodayHours >= project.dedicatedHours) {
|
||||
if (project.dedicatedHours[dayNumber] > 0 && totalTodayHours >= project.dedicatedHours[dayNumber]) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `Cannot login - daily hours already completed (${formatHoursToReadable(totalTodayHours)} / ${formatHoursToReadable(project.dedicatedHours)})`,
|
||||
message: `Cannot login - daily hours already completed (${formatHoursToReadable(totalTodayHours)} / ${formatHoursToReadable(project.dedicatedHours[dayNumber])})`,
|
||||
// warnings: scheduleCheck.warnings
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
import { ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
|
||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
||||
|
||||
function formatHMS(ms) {
|
||||
const totalSeconds = Math.floor(ms / 1000);
|
||||
|
|
@ -110,13 +111,15 @@ async function va_projects_log_list_function(req, res) {
|
|||
const lastLog = logs[logs.length - 1];
|
||||
const activityStatus = lastLog?.loggedType === "LOGIN" ? "ACTIVE" : "AWAY";
|
||||
|
||||
const dayNumber = getDayNumber();
|
||||
|
||||
finalReport.push({
|
||||
projectId: project._id,
|
||||
projectName: project.name,
|
||||
activityStatus,
|
||||
totalWorkTime: formatHMS(totalWorkMs),
|
||||
totalWorkMs,
|
||||
dedicatedHours: formatHoursToReadable(project?.dedicatedHours),
|
||||
dedicatedHours: formatHoursToReadable(project?.dedicatedHours[dayNumber]) || "0h",
|
||||
totalLogs: logs.length,
|
||||
status: project?.status
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
import { VAModel, ProjectModel, LoggedModel } from "../../../models/vaModel.js";
|
||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
||||
|
||||
async function va_timesheet_summary_function(req, res) {
|
||||
try {
|
||||
|
|
@ -162,8 +163,10 @@ async function va_timesheet_summary_function(req, res) {
|
|||
}
|
||||
};
|
||||
|
||||
const dayNumber = getDayNumber();
|
||||
|
||||
// Calculate dedicated hours and pending hours
|
||||
const dedicatedHours = project.dedicatedHours || 0;
|
||||
const dedicatedHours = project.dedicatedHours[dayNumber] || 0;
|
||||
const totalWorkedHours = totalWorkSeconds / 3600; // Convert to hours
|
||||
const pendingHours = Math.max(0, dedicatedHours - totalWorkedHours);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
function getDayNumber(date = new Date()) {
|
||||
const today = new Date(date);
|
||||
return today.getDay(); // getDay() already returns 0-6 for Sunday-Saturday
|
||||
}
|
||||
|
||||
export default getDayNumber;
|
||||
Loading…
Reference in New Issue