Added delete VA and project route, fixed tracker api issues
This commit is contained in:
parent
9c736dea02
commit
39010ee61c
|
|
@ -693,12 +693,12 @@ async function submitForm() {
|
|||
let cardNumber = document.getElementById('card-number');
|
||||
let expiryDate = document.getElementById('expiry-date');
|
||||
let securityCode = document.getElementById('security-code');
|
||||
let cardZip = document.getElementById('card-zip').value;
|
||||
let bankName = document.getElementById('bank-name').value;
|
||||
let accountNumber = document.getElementById('account-number').value;
|
||||
let routingNumber = document.getElementById('routing-number').value;
|
||||
let accountType = document.getElementById('account-type').value;
|
||||
let accountHolderName = document.getElementById('account-holder-name').value;
|
||||
let cardZip = document.getElementById('card-zip');
|
||||
let bankName = document.getElementById('bank-name');
|
||||
let accountNumber = document.getElementById('account-number');
|
||||
let routingNumber = document.getElementById('routing-number');
|
||||
let accountType = document.getElementById('account-type');
|
||||
let accountHolderName = document.getElementById('account-holder-name');
|
||||
|
||||
if(cardNumber.value) {
|
||||
formData.userDetails.cardNumber = cardNumber.value;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
import { ProjectModel, VAModel, ClientModel } from "../../models/vaModel.js";
|
||||
import { Types } from "mongoose";
|
||||
|
||||
/**
|
||||
* Delete project and all related data (without transaction)
|
||||
*/
|
||||
async function delete_project_sf(req, res) {
|
||||
try {
|
||||
const { projectId } = req.params;
|
||||
|
||||
// Validate projectId
|
||||
if (!projectId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Project ID is required"
|
||||
});
|
||||
}
|
||||
|
||||
let projectObjectId;
|
||||
try {
|
||||
projectObjectId = new Types.ObjectId(projectId);
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid Project ID format"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if project exists
|
||||
const existingProject = await ProjectModel.findById(projectObjectId);
|
||||
if (!existingProject) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Project not found"
|
||||
});
|
||||
}
|
||||
|
||||
const clientId = existingProject.clientId;
|
||||
const vaAssigned = existingProject.vaAssigned || [];
|
||||
const projectName = existingProject.name;
|
||||
|
||||
console.log(`Deleting project: ${projectName} (${projectId})`);
|
||||
|
||||
const deletionSummary = {};
|
||||
|
||||
// 1. Remove project from Client's projectIds array
|
||||
if (clientId) {
|
||||
await ClientModel.findByIdAndUpdate(
|
||||
clientId,
|
||||
{ $pull: { projectIds: projectObjectId } }
|
||||
);
|
||||
deletionSummary.clientUpdated = true;
|
||||
console.log(`Removed project from client`);
|
||||
}
|
||||
|
||||
// 2. Remove project from all assigned VAs' projectIds arrays
|
||||
if (vaAssigned.length > 0) {
|
||||
const vaResult = await VAModel.updateMany(
|
||||
{ _id: { $in: vaAssigned } },
|
||||
{ $pull: { projectIds: projectObjectId } }
|
||||
);
|
||||
deletionSummary.vasUpdated = vaResult.modifiedCount;
|
||||
console.log(`Removed project from ${deletionSummary.vasUpdated} VAs`);
|
||||
} else {
|
||||
deletionSummary.vasUpdated = 0;
|
||||
}
|
||||
|
||||
// 6. Finally delete the project
|
||||
await ProjectModel.findByIdAndDelete(projectObjectId);
|
||||
|
||||
console.log(`✅ Successfully deleted project: ${projectName} (${projectId})`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: 'Project and all related data deleted successfully',
|
||||
data: {
|
||||
projectId: projectId,
|
||||
projectName: projectName,
|
||||
clientId: clientId,
|
||||
deletedAt: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("delete_project_sf error:", error);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to delete project",
|
||||
error: process.env.NODE_ENV === 'production'
|
||||
? 'Internal server error'
|
||||
: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { delete_project_sf };
|
||||
|
|
@ -147,7 +147,7 @@ async function va_log_track_work_callback(req, res) {
|
|||
|
||||
const vaId = vaUser._id;
|
||||
const payload = req.body || {};
|
||||
const { projectId, logId, title, appName } = payload;
|
||||
const { projectId, logId, title, appName, url = null } = payload;
|
||||
|
||||
const project = await ProjectModel.findById(projectId);
|
||||
if (!project) return res.status(400).json({ success: false, message: "Project not found" });
|
||||
|
|
@ -190,6 +190,7 @@ async function va_log_track_work_callback(req, res) {
|
|||
logId,
|
||||
title,
|
||||
appName,
|
||||
url,
|
||||
from: Date.now(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
import { VAModel, ClientModel, ProjectModel, LoggedModel, WorkTrackModel } from "../../../models/vaModel.js";
|
||||
|
||||
// Delete VA and all related data
|
||||
async function va_remove_function(req, res) {
|
||||
try {
|
||||
const vaId = req.params.id;
|
||||
|
||||
// Validate VA ID
|
||||
if (!vaId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "VA ID is required"
|
||||
});
|
||||
}
|
||||
|
||||
// Find the VA to get clientIds and projectIds before deletion
|
||||
const vaUser = await VAModel.findById(vaId);
|
||||
if (!vaUser) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "VA not found"
|
||||
});
|
||||
}
|
||||
|
||||
const clientIds = vaUser.clientIds || [];
|
||||
const projectIds = vaUser.projectIds || [];
|
||||
const vaEmail = vaUser.userDetails?.email;
|
||||
|
||||
console.log(`Deleting VA ${vaId} - Affected: ${clientIds.length} clients, ${projectIds.length} projects`);
|
||||
|
||||
// 1. Remove VA from all Clients
|
||||
let clientsUpdated = 0;
|
||||
if (clientIds.length > 0) {
|
||||
const clientResult = await ClientModel.updateMany(
|
||||
{ _id: { $in: clientIds } },
|
||||
{ $pull: { vaIds: vaId } }
|
||||
);
|
||||
clientsUpdated = clientResult.modifiedCount;
|
||||
console.log(`Removed VA from ${clientsUpdated} clients`);
|
||||
}
|
||||
|
||||
// 2. Remove VA from all Projects
|
||||
let projectsUpdated = 0;
|
||||
if (projectIds.length > 0) {
|
||||
const projectResult = await ProjectModel.updateMany(
|
||||
{ _id: { $in: projectIds } },
|
||||
{ $pull: { vaAssigned: vaId } }
|
||||
);
|
||||
projectsUpdated = projectResult.modifiedCount;
|
||||
console.log(`Removed VA from ${projectsUpdated} projects`);
|
||||
}
|
||||
|
||||
// 3. Delete all WorkTrack records for this VA
|
||||
const workTrackResult = await WorkTrackModel.deleteMany({ vaId: vaId });
|
||||
console.log(`Deleted ${workTrackResult.deletedCount} work track records`);
|
||||
|
||||
// 4. Delete all Logged records for this VA
|
||||
const loggedResult = await LoggedModel.deleteMany({ vaId: vaId });
|
||||
console.log(`Deleted ${loggedResult.deletedCount} log records`);
|
||||
|
||||
// 5. Finally delete the VA
|
||||
await VAModel.findByIdAndDelete(vaId);
|
||||
|
||||
console.log(`Successfully deleted VA: ${vaId} and all related data`);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: 'Virtual Assistant and all related data deleted successfully',
|
||||
data: {
|
||||
vaId: vaId,
|
||||
vaName: `${vaUser.userDetails?.firstName || ''} ${vaUser.userDetails?.lastName || ''}`.trim() || 'Unknown VA',
|
||||
vaEmail: vaEmail,
|
||||
clientsUpdated: clientsUpdated,
|
||||
projectsUpdated: projectsUpdated,
|
||||
workTracksDeleted: workTrackResult.deletedCount,
|
||||
logsDeleted: loggedResult.deletedCount,
|
||||
deletedAt: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error deleting VA:', error);
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to delete Virtual Assistant',
|
||||
error: 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { va_remove_function };
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import { LoggedModel, WorkTrackModel, ProjectModel } from "../../../models/vaModel.js";
|
||||
|
||||
// Helper function to get start and end of day
|
||||
// Helper function to get start and end of day in UTC
|
||||
function getDayRange(date = new Date()) {
|
||||
const start = new Date(date);
|
||||
start.setHours(0, 0, 0, 0);
|
||||
start.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
const end = new Date(date);
|
||||
end.setHours(23, 59, 59, 999);
|
||||
end.setUTCHours(23, 59, 59, 999);
|
||||
|
||||
return { start, end };
|
||||
}
|
||||
|
|
@ -25,10 +25,53 @@ function formatHoursToReadable(hours) {
|
|||
}
|
||||
}
|
||||
|
||||
// function to close open work tracks and create logout
|
||||
async function closePreviousSession(vaId, lastLog) {
|
||||
// Helper function to parse ISO time string
|
||||
function parseISOTimeString(timeString) {
|
||||
if (!timeString) return new Date();
|
||||
|
||||
try {
|
||||
const currentTime = new Date();
|
||||
// Parse ISO string directly
|
||||
const parsedDate = new Date(timeString);
|
||||
|
||||
// Check if the date is valid
|
||||
if (isNaN(parsedDate.getTime())) {
|
||||
throw new Error('Invalid date');
|
||||
}
|
||||
parsedDate.setMilliseconds(0);
|
||||
|
||||
console.log("pasdes date" + parsedDate)
|
||||
return parsedDate;
|
||||
} catch (error) {
|
||||
console.error('Error parsing ISO time string:', error);
|
||||
return new Date();
|
||||
}
|
||||
}
|
||||
|
||||
function setMillisecondsToZero(date) {
|
||||
const newDate = new Date(date);
|
||||
newDate.setMilliseconds(0);
|
||||
return newDate;
|
||||
}
|
||||
|
||||
// Helper function to check if time difference is less than threshold
|
||||
function isTimeWithinThreshold(receivedTime, currentTime, thresholdSeconds = 10) {
|
||||
const diffMs = Math.abs(currentTime - receivedTime);
|
||||
const diffSeconds = diffMs / 1000;
|
||||
console.log(`Time comparison - Received: ${receivedTime.toISOString()}, Current: ${currentTime.toISOString()}, Diff: ${diffSeconds}s`);
|
||||
return diffSeconds <= thresholdSeconds;
|
||||
}
|
||||
|
||||
// Helper function to get current UTC time
|
||||
function getCurrentUTCTime() {
|
||||
const now = new Date();
|
||||
now.setMilliseconds(0);
|
||||
return now;
|
||||
}
|
||||
|
||||
// function to close open work tracks and create logout
|
||||
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({
|
||||
|
|
@ -120,18 +163,44 @@ async function closePreviousSession(vaId, lastLog) {
|
|||
async function va_project_log_callback(req, res) {
|
||||
try {
|
||||
const payload = req.body || {};
|
||||
const { vaId, vaEmail, projectId, type } = payload;
|
||||
const { vaId, vaEmail, projectId, type, time: receivedTimeString } = payload;
|
||||
console.log("Received payload:", payload);
|
||||
|
||||
if (!vaId, !vaEmail, !projectId, !type) return res.status(400).send("Missing Props");
|
||||
if (!vaId || !vaEmail || !projectId || !type) {
|
||||
return res.status(400).send("Missing required properties");
|
||||
}
|
||||
|
||||
// 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;
|
||||
console.log(`✅ Using received ISO time: ${receivedTimeString}, difference is within 10 seconds`);
|
||||
console.log(`✅ Parsed as: ${receivedTime.toISOString()}`);
|
||||
} else {
|
||||
logTime = currentTime;
|
||||
console.log(`⚠️ Using current UTC time, received time difference exceeds 10 seconds.`);
|
||||
console.log(`Received: ${receivedTimeString} (parsed: ${receivedTime.toISOString()})`);
|
||||
console.log(`Current: ${currentTime.toISOString()}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`ℹ️ No time received, using current UTC time: ${currentTime.toISOString()}`);
|
||||
}
|
||||
|
||||
const project = await ProjectModel.findById(projectId);
|
||||
if (!project) {
|
||||
return res.status(400).json({ success: false, message: "Project not found" });
|
||||
}
|
||||
|
||||
// For LOGIN requests, check daily hours
|
||||
// For LOGIN requests, check daily hours (using UTC day range)
|
||||
if (type === "LOGIN") {
|
||||
const { start, end } = getDayRange();
|
||||
const { start, end } = getDayRange(); // UTC day range
|
||||
// Get today's total hours
|
||||
const todayWorkEntries = await WorkTrackModel.find({
|
||||
vaId,
|
||||
|
|
@ -163,7 +232,7 @@ async function va_project_log_callback(req, res) {
|
|||
|
||||
// check last workModel login time and make a logout request then create login
|
||||
// console.log(`User already logged in. Closing previous session...`);
|
||||
const closeResult = await closePreviousSession(vaId, lastLog);
|
||||
const closeResult = await closePreviousSession(vaId, lastLog, logTime);
|
||||
|
||||
// console.log(`Previous session closed: ${closeResult.closedWorkTracks} work tracks, logout log: ${closeResult.logoutLogId}`);
|
||||
|
||||
|
|
@ -172,7 +241,8 @@ async function va_project_log_callback(req, res) {
|
|||
loggedType: "LOGIN",
|
||||
vaId,
|
||||
vaEmail,
|
||||
projectId
|
||||
projectId,
|
||||
time: logTime
|
||||
});
|
||||
|
||||
return res.status(201).send({
|
||||
|
|
@ -182,13 +252,21 @@ async function va_project_log_callback(req, res) {
|
|||
previousSession: {
|
||||
closedWorkTracks: closeResult.closedWorkTracks,
|
||||
logoutLogId: closeResult.logoutLogId
|
||||
}
|
||||
},
|
||||
timeUsed: receivedTimeString ? 'received' : 'current',
|
||||
actualTime: createdLog.time
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const createdLog = await LoggedModel.create({ loggedType: type, vaId, vaEmail, projectId })
|
||||
// console.log(createdLog)
|
||||
const createdLog = await LoggedModel.create({
|
||||
loggedType: type,
|
||||
vaId,
|
||||
vaEmail,
|
||||
projectId,
|
||||
time: logTime
|
||||
});
|
||||
|
||||
try {
|
||||
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
|
||||
const sfData = {
|
||||
|
|
@ -218,7 +296,14 @@ 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, SyncSalesforce: result });
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: `${type} Successfully`,
|
||||
logId: createdLog._id,
|
||||
SyncSalesforce: result,
|
||||
timeUsed: receivedTimeString ? 'received' : 'current',
|
||||
actualTime: createdLog.time
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to sync with Salesforce:", error.message);
|
||||
console.log(error)
|
||||
|
|
@ -229,9 +314,8 @@ async function va_project_log_callback(req, res) {
|
|||
throw salesforceError;
|
||||
}
|
||||
|
||||
// return res.status(201).send({ success: true, message: `${type} Successfully`, logId: createdLog._id });
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error in va_project_log_callback:", error);
|
||||
return res.status(500).send("Internal Server Error");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,6 +320,9 @@ const vaWorkTrackSchema = new Schema({
|
|||
appName: {
|
||||
type: String,
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
},
|
||||
from: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { process_reset } from "../../controllers/passwordReset.controllers.js";
|
|||
import { project_add_sf } from "../../controllers/project/projectAddSf.controller.js"
|
||||
import { update_project_sf } from "../../controllers/project/projectUpdateSf.controller.js"
|
||||
import { authValidator } from "../../middlewares/auth.middleware.js";
|
||||
import { delete_project_sf } from "../../controllers/project/deleteProjectSf.controller.js"
|
||||
|
||||
const v1Routes = express.Router()
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ v1Routes.use("/client", clientRoutes);
|
|||
|
||||
v1Routes.post("/project", authValidator, project_add_sf);
|
||||
v1Routes.patch("/project/:projectId", authValidator, update_project_sf);
|
||||
v1Routes.delete("/project/:projectId", authValidator, delete_project_sf);
|
||||
|
||||
v1Routes.post("/password/reset", process_reset);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import { va_verify_email } from "../../controllers/va/vaAuth/vaVerifyEmail.contr
|
|||
import { va_leave_apply_function } from "../../controllers/va/vaLeaves/vaLeavesApply.controller.js";
|
||||
import { va_leave_list_function } from "../../controllers/va/vaLeaves/vaLeaveList.controller.js"
|
||||
import { va_leave_manage_function } from "../../controllers/va/vaLeaves/vaLeaveManage.controller.js";
|
||||
import { va_remove_function } from "../../controllers/va/vaProfile/vaDelete.controller.js";
|
||||
|
||||
vaRoutes.get("/", va_callback); // VA Callback for getting active VA
|
||||
|
||||
|
|
@ -42,6 +43,7 @@ vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update
|
|||
|
||||
|
||||
vaRoutes.get("/:userName", va_username_callback); // VA Username Callback
|
||||
vaRoutes.delete("/:id", va_remove_function); // VA Username Callback
|
||||
|
||||
// timesheet adjustment routes
|
||||
vaRoutes.get("/timesheet/summary/:projectId/:date", authValidator, va_timesheet_summary_function); // Get Timesheet Adjustment for VA on specific project and date
|
||||
|
|
|
|||
Loading…
Reference in New Issue