Va and project List API update
This commit is contained in:
parent
59561f211d
commit
00cb793a7c
|
|
@ -6,7 +6,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "nodemon --inspect src/server.js",
|
"start": "nodemon --inspect src/server.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"deploy": "curl -X POST http://35.162.222.18:9000/hooks/deploy-va-middleware",
|
"deploy": "curl -X POST http://35.95.209.161:9000/hooks/deploy-va-middleware",
|
||||||
"build": "npx webpack --mode production",
|
"build": "npx webpack --mode production",
|
||||||
"dev": "npx webpack --mode development"
|
"dev": "npx webpack --mode development"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import { updateEntityRelationships } from "../../helper/updateEntityRelationship
|
||||||
// Register a new VA
|
// Register a new VA
|
||||||
async function project_add_sf(req, res) {
|
async function project_add_sf(req, res) {
|
||||||
try {
|
try {
|
||||||
const payload = req.body|| {};
|
const payload = req.body || {};
|
||||||
const { name, hourlyRate, clientId, vaAssigned, dedicatedHours, vaWorkingScheduleStart, vaWorkingScheduleEnd, isFlexibleAllowedPt, vaWorkingDelayAllowedStart, vaWorkingDelayAllowedEnd } = payload;
|
const { name, hourlyRate, clientId, vaAssigned, dedicatedHours, vaWorkingScheduleStart, vaWorkingScheduleEnd, isFlexibleAllowed, vaWorkingDelayAllowedStart, vaWorkingDelayAllowedEnd } = payload;
|
||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
if (!name) {
|
if (!name) {
|
||||||
|
|
@ -34,46 +34,55 @@ async function project_add_sf(req, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and convert vaAssigned to array of ObjectIds
|
// Validate and convert vaAssigned to array of ObjectIds
|
||||||
let vaAssignedArray = [];
|
// let vaAssignedArray = [];
|
||||||
if (vaAssigned) {
|
// if (vaAssigned) {
|
||||||
if (Array.isArray(vaAssigned)) {
|
// if (Array.isArray(vaAssigned)) {
|
||||||
try {
|
// try {
|
||||||
vaAssignedArray = vaAssigned.map(vaId => new Types.ObjectId(vaId));
|
// vaAssignedArray = vaAssigned.map(vaId => {
|
||||||
} catch (error) {
|
// if(typeof vaId === 'object' && "isFlexibleAllowed" in vaId){
|
||||||
return res.status(400).json({
|
// // Handle Error form SF Side for now - That vaId is coming as object
|
||||||
success: false,
|
// return new Types.ObjectId(vaId.vaId)
|
||||||
message: "Invalid VA ID format in vaAssigned array"
|
// } else {
|
||||||
});
|
// return new Types.ObjectId(vaId)
|
||||||
}
|
// }
|
||||||
} else if (typeof vaAssigned === 'string') {
|
// });
|
||||||
// Handle single VA ID as string
|
// } catch (error) {
|
||||||
try {
|
// return res.status(400).json({
|
||||||
vaAssignedArray = [new Types.ObjectId(vaAssigned)];
|
// success: false,
|
||||||
} catch (error) {
|
// message: "Invalid VA ID format in vaAssigned array"
|
||||||
return res.status(400).json({
|
// });
|
||||||
success: false,
|
// }
|
||||||
message: "Invalid VA ID format"
|
// } else if (typeof vaAssigned === 'string') {
|
||||||
});
|
// // Handle single VA ID as string
|
||||||
}
|
// try {
|
||||||
}
|
// vaAssignedArray = [new Types.ObjectId(vaAssigned)];
|
||||||
}
|
// } catch (error) {
|
||||||
|
// return res.status(400).json({
|
||||||
|
// success: false,
|
||||||
|
// message: "Invalid VA ID format"
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// sanitize/construct allowed fields
|
// sanitize/construct allowed fields
|
||||||
const toCreate = {
|
const toCreate = {
|
||||||
name: name.trim(),
|
name: name.trim(),
|
||||||
hourlyRate: hourlyRate || '0',
|
hourlyRate: hourlyRate || '0',
|
||||||
clientId: clientObjectId,
|
clientId: clientObjectId,
|
||||||
vaAssigned: vaAssignedArray,
|
vaAssigned: vaAssigned,
|
||||||
dedicatedHours: dedicatedHours ? parseFloat(dedicatedHours) : 0,
|
dedicatedHours: dedicatedHours ? parseFloat(dedicatedHours) : 0,
|
||||||
vaWorkingScheduleStart: vaWorkingScheduleStart || null,
|
vaWorkingScheduleStart: vaWorkingScheduleStart || null,
|
||||||
vaWorkingScheduleEnd: vaWorkingScheduleEnd || null,
|
vaWorkingScheduleEnd: vaWorkingScheduleEnd || null,
|
||||||
isFlexibleAllowedPt: isFlexibleAllowedPt || true,
|
isFlexibleAllowed: isFlexibleAllowed || true,
|
||||||
vaWorkingDelayAllowedStart: vaWorkingDelayAllowedStart || null,
|
vaWorkingDelayAllowedStart: vaWorkingDelayAllowedStart || null,
|
||||||
vaWorkingDelayAllowedEnd: vaWorkingDelayAllowedEnd || null
|
vaWorkingDelayAllowedEnd: vaWorkingDelayAllowedEnd || null
|
||||||
};
|
};
|
||||||
|
|
||||||
const created = await ProjectModel.create(toCreate);
|
const created = await ProjectModel.create(toCreate);
|
||||||
|
|
||||||
|
const vaAssignedArray = vaAssigned ? vaAssigned.map(vaDtl => vaDtl.vaId) : [];
|
||||||
|
|
||||||
const relationshipUpdate = await updateEntityRelationships({
|
const relationshipUpdate = await updateEntityRelationships({
|
||||||
project: {
|
project: {
|
||||||
projectId: created._id,
|
projectId: created._id,
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ async function update_project_sf(req, res) {
|
||||||
vaAssigned,
|
vaAssigned,
|
||||||
dedicatedHours,
|
dedicatedHours,
|
||||||
status,
|
status,
|
||||||
totalLoggedHours ,
|
totalLoggedHours,
|
||||||
vaWorkingScheduleStart,
|
vaWorkingScheduleStart,
|
||||||
vaWorkingScheduleEnd,
|
vaWorkingScheduleEnd,
|
||||||
isFlexibleAllowedPt,
|
isFlexibleAllowed,
|
||||||
vaWorkingDelayAllowedStart,
|
vaWorkingDelayAllowedStart,
|
||||||
vaWorkingDelayAllowedEnd
|
vaWorkingDelayAllowedEnd
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
@ -102,8 +102,8 @@ async function update_project_sf(req, res) {
|
||||||
updateData.vaWorkingScheduleEnd = vaWorkingScheduleEnd || null;
|
updateData.vaWorkingScheduleEnd = vaWorkingScheduleEnd || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFlexibleAllowedPt !== undefined) {
|
if (isFlexibleAllowed !== undefined) {
|
||||||
updateData.isFlexibleAllowedPt = isFlexibleAllowedPt;
|
updateData.isFlexibleAllowed = isFlexibleAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vaWorkingDelayAllowedStart !== undefined) {
|
if (vaWorkingDelayAllowedStart !== undefined) {
|
||||||
|
|
@ -114,15 +114,26 @@ async function update_project_sf(req, res) {
|
||||||
updateData.vaWorkingDelayAllowedEnd = vaWorkingDelayAllowedEnd || null;
|
updateData.vaWorkingDelayAllowedEnd = vaWorkingDelayAllowedEnd || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vaAssigned !== undefined && vaAssigned !== null && Array.isArray(vaAssigned)) {
|
||||||
|
updateData.vaAssigned = vaAssigned;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle VA assignments - this requires relationship updates
|
// Handle VA assignments - this requires relationship updates
|
||||||
if (vaAssigned !== undefined) {
|
if (vaAssigned !== undefined) {
|
||||||
let newVaAssignedArray = [];
|
let newVaAssignedArray = updateData.vaAssigned.map(vaDtl => vaDtl.vaId);
|
||||||
|
|
||||||
// Parse and validate VA IDs
|
// Parse and validate VA IDs
|
||||||
if (vaAssigned && vaAssigned.length > 0) {
|
if (vaAssigned && vaAssigned.length > 0) {
|
||||||
if (Array.isArray(vaAssigned)) {
|
if (Array.isArray(vaAssigned)) {
|
||||||
try {
|
try {
|
||||||
newVaAssignedArray = vaAssigned.map(vaId => new Types.ObjectId(vaId));
|
newVaAssignedArray = vaAssigned.map(vaId => {
|
||||||
|
if (typeof vaId === 'object' && "isFlexibleAllowed" in vaId) {
|
||||||
|
// Handle Error form SF Side for now - That vaId is coming as object
|
||||||
|
return new Types.ObjectId(vaId.vaId)
|
||||||
|
} else {
|
||||||
|
return new Types.ObjectId(vaId)
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
|
|
@ -141,7 +152,7 @@ async function update_project_sf(req, res) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData.vaAssigned = newVaAssignedArray;
|
// updateData.vaAssigned = newVaAssignedArray;
|
||||||
|
|
||||||
// Calculate VAs to add and remove for relationship updates
|
// Calculate VAs to add and remove for relationship updates
|
||||||
const currentVaIds = existingProject.vaAssigned.map(va => va.toString());
|
const currentVaIds = existingProject.vaAssigned.map(va => va.toString());
|
||||||
|
|
@ -187,7 +198,7 @@ async function update_project_sf(req, res) {
|
||||||
updateData,
|
updateData,
|
||||||
{ new: true, runValidators: true }
|
{ new: true, runValidators: true }
|
||||||
).populate('clientId', 'userDetails.name userDetails.email')
|
).populate('clientId', 'userDetails.name userDetails.email')
|
||||||
.populate('vaAssigned', 'userDetails.firstName userDetails.lastName userDetails.email');
|
.populate('vaAssigned', 'userDetails.firstName userDetails.lastName userDetails.email');
|
||||||
|
|
||||||
// If VA assignments were changed but relationship update failed, log warning
|
// If VA assignments were changed but relationship update failed, log warning
|
||||||
if (vaAssigned !== undefined) {
|
if (vaAssigned !== undefined) {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ async function va_remove_function(req, res) {
|
||||||
if (projectIds.length > 0) {
|
if (projectIds.length > 0) {
|
||||||
const projectResult = await ProjectModel.updateMany(
|
const projectResult = await ProjectModel.updateMany(
|
||||||
{ _id: { $in: projectIds } },
|
{ _id: { $in: projectIds } },
|
||||||
{ $pull: { vaAssigned: vaId } }
|
{ $pull: { "vaAssigned.vaId": vaId } }
|
||||||
);
|
);
|
||||||
projectsUpdated = projectResult.modifiedCount;
|
projectsUpdated = projectResult.modifiedCount;
|
||||||
console.log(`Removed VA from ${projectsUpdated} projects`);
|
console.log(`Removed VA from ${projectsUpdated} projects`);
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ function getCurrentUTCTime() {
|
||||||
// console.log(`Schedule Check - Current: ${currentMinutes}, Start: ${scheduleStartMinutes}, End: ${scheduleEndMinutes}, Allowed Start: ${allowedStartMinutes}, Allowed End: ${allowedEndMinutes}`);
|
// console.log(`Schedule Check - Current: ${currentMinutes}, Start: ${scheduleStartMinutes}, End: ${scheduleEndMinutes}, Allowed Start: ${allowedStartMinutes}, Allowed End: ${allowedEndMinutes}`);
|
||||||
|
|
||||||
// // Check for PARTTIME with flexible schedule
|
// // Check for PARTTIME with flexible schedule
|
||||||
// if (vaStatus === "PARTTIME" && project.isFlexibleAllowedPt) {
|
// if (vaStatus === "PARTTIME" && project.isFlexibleAllowed) {
|
||||||
// return { isCompliant: true, warnings };
|
// return { isCompliant: true, warnings };
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ async function va_projects_log_list_function(req, res) {
|
||||||
|
|
||||||
console.log("VA ID:", vaId.toString());
|
console.log("VA ID:", vaId.toString());
|
||||||
|
|
||||||
projectList = await ProjectModel.find({ vaAssigned: vaUser._id });
|
projectList = await ProjectModel.find({ "vaAssigned.vaId": vaUser._id });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!projectList.length) {
|
if (!projectList.length) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ async function va_timesheet_adjustment_function(req, res) {
|
||||||
// Validate project exists and VA is assigned to it
|
// Validate project exists and VA is assigned to it
|
||||||
const project = await ProjectModel.findOne({
|
const project = await ProjectModel.findOne({
|
||||||
_id: projectId,
|
_id: projectId,
|
||||||
vaAssigned: vaId
|
"vaAssigned.vaId": vaId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!project) {
|
if (!project) {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ async function va_timesheet_summary_function(req, res) {
|
||||||
// Validate project exists and VA is assigned to it
|
// Validate project exists and VA is assigned to it
|
||||||
const project = await ProjectModel.findOne({
|
const project = await ProjectModel.findOne({
|
||||||
_id: projectId,
|
_id: projectId,
|
||||||
vaAssigned: vaId
|
"vaAssigned.vaId": vaId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!project) {
|
if (!project) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ async function va_tips_manage_function(req, res) {
|
||||||
clientId,
|
clientId,
|
||||||
status: "Unbilled",
|
status: "Unbilled",
|
||||||
amount: parseFloat(amount).toFixed(2),
|
amount: parseFloat(amount).toFixed(2),
|
||||||
description,
|
description: description || "",
|
||||||
vaName: vaUser.userDetails.firstName ? vaUser.userDetails.firstName : "" + " " + vaUser.userDetails.lastName ? vaUser.userDetails.lastName : "",
|
vaName: vaUser.userDetails.firstName ? vaUser.userDetails.firstName : "" + " " + vaUser.userDetails.lastName ? vaUser.userDetails.lastName : "",
|
||||||
vaId,
|
vaId,
|
||||||
};
|
};
|
||||||
|
|
@ -34,7 +34,7 @@ async function va_tips_manage_function(req, res) {
|
||||||
"vaId": createdTip.vaId.toString(),
|
"vaId": createdTip.vaId.toString(),
|
||||||
"status": "Unbilled",
|
"status": "Unbilled",
|
||||||
"clientId": createdTip.clientId.toString(),
|
"clientId": createdTip.clientId.toString(),
|
||||||
"comment": createdTip.description,
|
"comment": createdTip.description || "",
|
||||||
"tipAmount": Number(parseFloat(createdTip.amount).toFixed(2)), // Convert to cents
|
"tipAmount": Number(parseFloat(createdTip.amount).toFixed(2)), // Convert to cents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ async function updateEntityRelationships(data) {
|
||||||
if (vaId && Array.isArray(vaId)) {
|
if (vaId && Array.isArray(vaId)) {
|
||||||
await ProjectModel.findByIdAndUpdate(
|
await ProjectModel.findByIdAndUpdate(
|
||||||
projectId,
|
projectId,
|
||||||
{ $addToSet: { vaAssigned: { $each: vaId } } }
|
{ $addToSet: { "vaAssigned.vaId": { $each: vaId } } }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ async function updateEntityRelationships(data) {
|
||||||
{ _id: { $in: projectId } },
|
{ _id: { $in: projectId } },
|
||||||
{
|
{
|
||||||
$addToSet: {
|
$addToSet: {
|
||||||
vaAssigned: vaId
|
"vaAssigned.vaId": vaId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -285,7 +285,7 @@ async function removeEntityRelationships(data) {
|
||||||
if (projectId && Array.isArray(projectId)) {
|
if (projectId && Array.isArray(projectId)) {
|
||||||
await ProjectModel.updateMany(
|
await ProjectModel.updateMany(
|
||||||
{ _id: { $in: projectId } },
|
{ _id: { $in: projectId } },
|
||||||
{ $pull: { vaAssigned: vaId } }
|
{ $pull: { "vaAssigned.vaId": vaId } }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,16 @@ const projectSchema = new Schema({
|
||||||
},
|
},
|
||||||
vaAssigned: [
|
vaAssigned: [
|
||||||
{
|
{
|
||||||
type: Types.ObjectId,
|
_id: false,
|
||||||
ref: "VA",
|
vaId: {
|
||||||
},
|
type: Types.ObjectId,
|
||||||
|
ref: "VA",
|
||||||
|
},
|
||||||
|
isFlexibleAllowed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
dedicatedHours: {
|
dedicatedHours: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
@ -110,7 +117,7 @@ const projectSchema = new Schema({
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
isFlexibleAllowedPt: {
|
isFlexibleAllowed: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
|
@ -363,36 +370,36 @@ const vaWorkTrackSchema = new Schema({
|
||||||
)
|
)
|
||||||
|
|
||||||
const passwordResetSchema = new mongoose.Schema({
|
const passwordResetSchema = new mongoose.Schema({
|
||||||
email: {
|
email: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
lowercase: true,
|
lowercase: true,
|
||||||
index: true
|
index: true
|
||||||
},
|
},
|
||||||
token: {
|
token: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
unique: true,
|
unique: true,
|
||||||
index: true
|
index: true
|
||||||
},
|
},
|
||||||
userType: {
|
userType: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
enum: ['va', 'client'],
|
enum: ['va', 'client'],
|
||||||
index: true
|
index: true
|
||||||
},
|
},
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
type: Date,
|
type: Date,
|
||||||
required: true,
|
required: true,
|
||||||
index: true,
|
index: true,
|
||||||
expires: 3600 // TTL index for 1 hour
|
expires: 3600 // TTL index for 1 hour
|
||||||
},
|
},
|
||||||
used: {
|
used: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
timestamps: true
|
timestamps: true
|
||||||
});
|
});
|
||||||
|
|
||||||
passwordResetSchema.index({ email: 1, used: 1 });
|
passwordResetSchema.index({ email: 1, used: 1 });
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ io.on("connection", (socket) => {
|
||||||
const projectVAIds = new Set();
|
const projectVAIds = new Set();
|
||||||
|
|
||||||
const populated = await ProjectModel.findById(projectId).populate("vaAssigned");
|
const populated = await ProjectModel.findById(projectId).populate("vaAssigned");
|
||||||
populated.vaAssigned.forEach((va) => projectVAIds.add(va._id.toString()));
|
populated.vaAssigned.forEach((va) => projectVAIds.add(va.vaId.toString()));
|
||||||
|
|
||||||
socket.data.allowedVAs = Array.from(projectVAIds);
|
socket.data.allowedVAs = Array.from(projectVAIds);
|
||||||
console.log(`Client ${clientId} can view VAs:`, socket.data.allowedVAs);
|
console.log(`Client ${clientId} can view VAs:`, socket.data.allowedVAs);
|
||||||
|
|
@ -498,6 +498,14 @@ io.on("connection", (socket) => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get("/heath", () => {
|
||||||
|
if(Math.round(Math.random()) === 1) {
|
||||||
|
return res.status(200).json({"success": true, "message": "Server is Healthy"})
|
||||||
|
} else {
|
||||||
|
return res.status(403).json({"success": false, "message": "Server is not Healthy"})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
res.status(404).render("not_found", {
|
res.status(404).render("not_found", {
|
||||||
title: "404 - Page Not Found"
|
title: "404 - Page Not Found"
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Tip Description</label>
|
<label class="form-label">Tip Description</label>
|
||||||
<textarea name="vaTipDescription" class="form-input" id="vaTipDescription"></textarea>
|
<textarea required="true" name="vaTipDescription" class="form-input" id="vaTipDescription"></textarea>
|
||||||
<div class="error-message" id="vaTipDescriptionError"></div>
|
<div class="error-message" id="vaTipDescriptionError"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue