All checks were successful
Build And Deploy Main / build-and-deploy (push) Successful in 36s
517 lines
17 KiB
JavaScript
517 lines
17 KiB
JavaScript
const db = require("../models");
|
|
const Employee = db.employee;
|
|
const ExtUserPermission = db.ext_usr_perm;
|
|
const axios = require("axios");
|
|
const https = require("https");
|
|
|
|
var bcrypt = require("bcryptjs");
|
|
const { splitSite } = require("../middlewares");
|
|
const SYSTEM_ACCESS_PERMISSION = "System Access";
|
|
const HR_EMPLOYEE_LIST_ENDPOINT = "https://ws-hr.mayosolution.com/api/integration/employees";
|
|
const HR_INTEGRATION_USERNAME = "vibecodingking";
|
|
const HR_INTEGRATION_PASSWORD = "oAQC483f1jxdJdoJcd0kCAd7C";
|
|
const HR_INSECURE_HTTPS_AGENT = new https.Agent({ rejectUnauthorized: false });
|
|
|
|
const ALL_PERMISSIONS = [
|
|
'Dashboard',
|
|
'Admin View',
|
|
'View_Info Screen',
|
|
'Edit_Info Screen',
|
|
'View_Customer Info _Personal Info',
|
|
'View_Customer Info _Care & Services',
|
|
'View_Customer Info _Medical & Insurance',
|
|
'View_Customer Info _Confidential Details',
|
|
'View_Customer Info _Form Submission',
|
|
'Edit_Customer Info _ Personal Info',
|
|
'Edit_Customer Info _ Care & Services',
|
|
'Edit_Customer Info _ Medical & Insurance',
|
|
'Edit_Customer Info _ Confidential Details',
|
|
'Edit_Customer Info _ Form Submission',
|
|
'Discharge_Customer',
|
|
'Reactivate_Customer',
|
|
'Create_Customer',
|
|
'Export_Customer Report',
|
|
'View _Calendar _Medical Appointment',
|
|
'View _Calendar _Activities',
|
|
'View _Calendar _Attendance Notes',
|
|
'View _Calendar _Meal Plan',
|
|
'View _Calendar _Important Dates',
|
|
'Edit&Create _Calendar _Medical Appointment',
|
|
'Edit&Create _Calendar _Activities',
|
|
'Edit&Create _Calendar _Attendance Notes',
|
|
'Edit&Create _Calendar _Meal Plan',
|
|
'Edit&Create _Calendar _Important Dates',
|
|
'View_Messaging',
|
|
'Sent_Messaging',
|
|
'View_Messaging Template',
|
|
'Create&Edit_Messaging Template',
|
|
'View_Vehicle info_Basic Info',
|
|
'View_Vehicle info_Documents',
|
|
'View_Vehicle info_Repair Records',
|
|
'Edit_Vehicle info_Basic Info',
|
|
'Edit_Vehicle info_Documents',
|
|
'Edit_Vehicle info_Repair Records',
|
|
'Add_New Vehicle',
|
|
'Archive_Vehicle',
|
|
'Export_Vehicle Report',
|
|
'View_Transportation Schedule_Route Overview',
|
|
'Create&Edit_Transportation Schedule',
|
|
'Export_Transportation Schedule Report',
|
|
'View_Route Template',
|
|
'Create&Edit_Route Template',
|
|
'View_Driver Assignment for Appointment',
|
|
'Edit_Driver Assignment for Appointment',
|
|
'isDriver',
|
|
'View_Provider Info',
|
|
'Create & Edit _Provider Info',
|
|
'View_Appointment Request',
|
|
'Edit & Create_Appointment Request',
|
|
'View_Appointment Calendar',
|
|
'Edit & Create_Appointment Calendar',
|
|
'Medical Template',
|
|
'View_Meal Status',
|
|
'Edit_Meal Status',
|
|
'View_Seating Chart',
|
|
'Edit_Seating Chart',
|
|
'Employee page',
|
|
'Set Permission for Employee'
|
|
];
|
|
|
|
const hasAllPermissionsByUsername = (username) => (username || '').toString().trim().toLowerCase() === 'testadmin03';
|
|
// Create and Save a new Employee (driver, distributor, admin)
|
|
exports.createEmployee = (req, res) => {
|
|
// Validate request
|
|
if (!req.body.username) {
|
|
res.status(400).send({ message: "Content can not be empty!" });
|
|
return;
|
|
}
|
|
const site = splitSite.findSiteNumber(req);
|
|
const normalizedUsername = req.body.username || req.body.email || '';
|
|
const requestPermissions = Array.isArray(req.body.permissions) ? req.body.permissions : [];
|
|
// Create a Employee
|
|
const employee = new Employee({
|
|
username: normalizedUsername,
|
|
name_cn: req.body.name_cn || '',
|
|
email: req.body.email || '',
|
|
password: req.body.password ? bcrypt.hashSync(req.body.password, 8) : '',
|
|
roles: req.body.roles || [],
|
|
permissions: hasAllPermissionsByUsername(normalizedUsername) ? ALL_PERMISSIONS : requestPermissions,
|
|
mobile_phone: req.body.mobile_phone || '',
|
|
home_phone: req.body.home_phone || '',
|
|
language: req.body.language || '',
|
|
employment_status: req.body.employment_status || '',
|
|
address: req.body.address || '',
|
|
date_hired: req.body.date_hired || '',
|
|
driver_capacity: req.body.driver_capacity || null,
|
|
create_by: req.body.create_by || '',
|
|
create_date: new Date(),
|
|
edit_by: req.body.edit_by || '',
|
|
edit_date: new Date(),
|
|
note: req.body.note || '',
|
|
tags: req.body.tags || [],
|
|
fetch_route_time: req.body.fetch_route_time || null,
|
|
site,
|
|
// new fields added and legacy fields are used in HR system
|
|
trinet_id: req.body.trinet_id || '',
|
|
name: req.body.name || '',
|
|
firstname: req.body.firstname || '',
|
|
middlename: req.body.middlename || '',
|
|
lastname: req.body.lastname || '',
|
|
preferred_name: req.body.preferred_name || '',
|
|
ssn: req.body.ssn || '',
|
|
marital_status: req.body.marital_status || '',
|
|
gender: req.body.gender || '',
|
|
city: req.body.city || '',
|
|
street_address: req.body.street_address || '',
|
|
state: req.body.state || '',
|
|
zip: req.body.zip || '',
|
|
country: req.body.country || '',
|
|
work_email: req.body.work_email || '',
|
|
personal_email: req.body.personal_email || '',
|
|
phone: req.body.phone || '',
|
|
work_phone: req.body.work_phone || '',
|
|
birth_date: req.body.birth_date || '',
|
|
work_phone_ext: req.body.work_phone_ext || '',
|
|
dietary_restrictions: req.body.dietary_restrictions || '',
|
|
tshirt_size: req.body.tshirt_size || '',
|
|
title: req.body.title || '',
|
|
title_cn: req.body.title_cn || '',
|
|
department: req.body.department || '',
|
|
manager_name: req.body.manager_name || '',
|
|
manager_email: req.body.manager_email || '',
|
|
manager_trinet_id: req.body.manager_trinet_id || '',
|
|
manager_id: req.body.manager_id || '',
|
|
direct_reports: req.body.direct_reports || [],
|
|
current_employment_start_date: req.body.current_employment_start_date || '',
|
|
initial_employment_start_date: req.body.initial_employment_start_date || '',
|
|
status: req.body.status || 'active',
|
|
work_location: req.body.work_location || '',
|
|
employment_type: req.body.employment_type || '',
|
|
employment_start_date: req.body.employment_start_date || '',
|
|
employment_end_date: req.body.employment_end_date || '',
|
|
termination_date: req.body.termination_date || '',
|
|
termination_type: req.body.termination_type || '',
|
|
termination_reason: req.body.termination_reason || '',
|
|
full_time_start_date: req.body.full_time_start_date || '',
|
|
end_date: req.body.end_date || '',
|
|
month_of_service: req.body.month_of_service || null,
|
|
full_years_of_service: req.body.full_years_of_service || null,
|
|
compensation_type: req.body.compensation_type || '',
|
|
compensation: req.body.compensation || null,
|
|
worker_type: req.body.worker_type || '',
|
|
monthly_salary: req.body.monthly_salary || null,
|
|
currency: req.body.currency || '',
|
|
salary: req.body.salary || null,
|
|
hourly_wage: req.body.hourly_wage || null,
|
|
work_site: req.body.work_site || null,
|
|
next_anniversary: req.body.next_anniversary || null,
|
|
job_id: req.body.job_id || '',
|
|
job_title: req.body.job_title || '',
|
|
job_category: req.body.job_category || '',
|
|
add_to_payroll: req.body.add_to_payroll || false,
|
|
collect_tax_information: req.body.collect_tax_information || false,
|
|
fingerprints: req.body.fingerprints || [],
|
|
entry_passcode: req.body.entry_passcode || []
|
|
});
|
|
// Save Employee in the database
|
|
employee
|
|
.save(employee)
|
|
.then(data => {
|
|
res.send(data);
|
|
})
|
|
.catch(err => {
|
|
res.status(500).send({
|
|
message:
|
|
err.message || "Some error occurred while creating the Employee."
|
|
});
|
|
});
|
|
};
|
|
// Retrieve all Employee from the database.
|
|
exports.getAllEmployees = (req, res) => {
|
|
var params = req.query;
|
|
var condition = {};
|
|
condition = splitSite.splitSiteGet(req, condition);
|
|
if (params.status) {
|
|
condition.status = params.status;
|
|
}
|
|
if (params.role) {
|
|
condition.roles = params.role;
|
|
}
|
|
if (params.site) {
|
|
condition.site = params.site;
|
|
}
|
|
Employee.find(condition)
|
|
.then(data => {
|
|
res.send(data);
|
|
})
|
|
.catch(err => {
|
|
res.status(500).send({
|
|
message:
|
|
err.message || "Some error occurred while retrieving employees."
|
|
});
|
|
});
|
|
};
|
|
|
|
// Retrive all Employees without site
|
|
exports.getAllEmployeesAcrossSites = (req, res) => {
|
|
var params = req.query;
|
|
var condition = {};
|
|
if (params.status) {
|
|
condition.status = params.status;
|
|
}
|
|
if (params.role) {
|
|
condition.roles = params.role;
|
|
}
|
|
Employee.find(condition)
|
|
.then(data => {
|
|
res.send(data);
|
|
})
|
|
.catch(err => {
|
|
res.status(500).send({
|
|
message:
|
|
err.message || "Some error occurred while retrieving employees."
|
|
});
|
|
});
|
|
};
|
|
|
|
// Retrieve all Active Employee from the database.
|
|
exports.getAllActiveEmployees = (req, res) => {
|
|
var params = req.query;
|
|
var condition = { status: 'active' };
|
|
condition = splitSite.splitSiteGet(req, condition);
|
|
if (params.roles) {
|
|
condition.roles = params.roles;
|
|
}
|
|
if (params.site) {
|
|
condition.site = params.site;
|
|
}
|
|
Employee.find(condition)
|
|
.then(data => {
|
|
res.send(data);
|
|
})
|
|
.catch(err => {
|
|
res.status(500).send({
|
|
message:
|
|
err.message || "Some error occurred while retrieving employees."
|
|
});
|
|
});
|
|
};
|
|
// Get One Employee by Id
|
|
exports.getEmployee = (req, res) => {
|
|
const id = req.params.id;
|
|
Employee.findById(id)
|
|
.then(data => {
|
|
if (!data)
|
|
res.status(404).send({ message: "Not found Employee with id " + id });
|
|
else res.send(data);
|
|
})
|
|
.catch(err => {
|
|
res
|
|
.status(500)
|
|
.send({ message: "Error retrieving Employee with id=" + id });
|
|
});
|
|
};
|
|
// Update a Employee by the id in the request
|
|
exports.updateEmployee = (req, res) => {
|
|
if (!req.body) {
|
|
return res.status(400).send({
|
|
message: "Data to update can not be empty!"
|
|
});
|
|
}
|
|
const id = req.params.id;
|
|
if (req.body.password) {
|
|
req.body.password = bcrypt.hashSync(req.body.password, 8);
|
|
}
|
|
Employee.findById(id)
|
|
.then((existingEmployee) => {
|
|
if (!existingEmployee) {
|
|
res.status(404).send({
|
|
message: `Cannot update employee with id=${id}. Maybe Employee was not found!`
|
|
});
|
|
return null;
|
|
}
|
|
const nextData = Object.assign({}, req.body);
|
|
const effectiveUsername = nextData.username || existingEmployee.username;
|
|
if (hasAllPermissionsByUsername(effectiveUsername)) {
|
|
nextData.permissions = ALL_PERMISSIONS;
|
|
}
|
|
return Employee.findByIdAndUpdate(id, nextData, { useFindAndModify: false });
|
|
})
|
|
.then((data) => {
|
|
if (!data) return;
|
|
res.send({ success: true, message: "Employee was updated successfully." });
|
|
})
|
|
.catch(err => {
|
|
res.status(500).send({
|
|
success: false,
|
|
message: "Error updating Employee with id=" + id
|
|
});
|
|
});
|
|
};
|
|
// Soft Delete a Employee with the specified id in the request
|
|
exports.deactivateEmployee = (req, res) => {
|
|
|
|
};
|
|
// Delete a Employee by id
|
|
exports.deleteEmployee = (req, res) => {
|
|
|
|
};
|
|
// Get Employees with username or email
|
|
exports.getEmployeesWithUsernameOrEmail = (req, res) => {
|
|
|
|
};
|
|
|
|
exports.getExternalEmployeesList = async (req, res) => {
|
|
const requestOptions = {
|
|
timeout: 15000,
|
|
httpsAgent: HR_INSECURE_HTTPS_AGENT
|
|
};
|
|
const requestedSite = Number(req.query?.site || req.body?.site);
|
|
const hasRequestedSite = Number.isInteger(requestedSite) && requestedSite > 0;
|
|
const requestBodies = hasRequestedSite
|
|
? [
|
|
{
|
|
username: HR_INTEGRATION_USERNAME,
|
|
password: HR_INTEGRATION_PASSWORD,
|
|
site: requestedSite,
|
|
status: "active"
|
|
},
|
|
{
|
|
username: HR_INTEGRATION_USERNAME,
|
|
password: HR_INTEGRATION_PASSWORD,
|
|
site: requestedSite
|
|
}
|
|
]
|
|
: [
|
|
{
|
|
username: HR_INTEGRATION_USERNAME,
|
|
password: HR_INTEGRATION_PASSWORD,
|
|
status: "active"
|
|
},
|
|
{
|
|
username: HR_INTEGRATION_USERNAME,
|
|
password: HR_INTEGRATION_PASSWORD
|
|
}
|
|
];
|
|
const normalizeEmployeesList = (raw) => {
|
|
if (Array.isArray(raw)) return raw;
|
|
if (typeof raw === "string") {
|
|
const trimmed = raw.trim();
|
|
if (!trimmed) return [];
|
|
try {
|
|
const parsed = JSON.parse(trimmed);
|
|
if (Array.isArray(parsed)) return parsed;
|
|
if (Array.isArray(parsed?.data)) return parsed.data;
|
|
if (Array.isArray(parsed?.employees)) return parsed.employees;
|
|
if (Array.isArray(parsed?.result)) return parsed.result;
|
|
if (Array.isArray(parsed?.results)) return parsed.results;
|
|
} catch (_err) {
|
|
return [];
|
|
}
|
|
}
|
|
if (Array.isArray(raw?.data)) return raw.data;
|
|
if (Array.isArray(raw?.employees)) return raw.employees;
|
|
if (Array.isArray(raw?.result)) return raw.result;
|
|
if (Array.isArray(raw?.results)) return raw.results;
|
|
return [];
|
|
};
|
|
|
|
try {
|
|
let bestResponseData = null;
|
|
for (const requestBody of requestBodies) {
|
|
try {
|
|
const response = await axios.post(HR_EMPLOYEE_LIST_ENDPOINT, requestBody, requestOptions);
|
|
const list = normalizeEmployeesList(response?.data);
|
|
bestResponseData = response?.data;
|
|
if (list.length > 0) {
|
|
return res.send(list);
|
|
}
|
|
} catch (_attemptError) {
|
|
// Try next payload variant.
|
|
}
|
|
}
|
|
const emptyList = normalizeEmployeesList(bestResponseData);
|
|
return res.send(emptyList);
|
|
} catch (err) {
|
|
res.status(500).send({
|
|
message: err?.response?.data?.message || err.message || "Failed to fetch employees from HR system."
|
|
});
|
|
}
|
|
};
|
|
|
|
exports.getExternalUserPermission = async (req, res) => {
|
|
try {
|
|
const externalUserId = `${req.query.external_user_id || req.query.externalUserId || ""}`.trim();
|
|
const requestedSite = Number(req.query.site || req.query.allow_site);
|
|
const allowSite = Number.isInteger(requestedSite) && requestedSite > 0 ? requestedSite : splitSite.findSiteNumber(req);
|
|
if (!externalUserId) {
|
|
return res.status(400).send({ message: "external_user_id is required." });
|
|
}
|
|
const config = await ExtUserPermission.findOne({
|
|
external_user_id: externalUserId,
|
|
allow_site: allowSite
|
|
}).sort({ updatedAt: -1 });
|
|
return res.send(config || null);
|
|
} catch (err) {
|
|
return res.status(500).send({
|
|
message: err.message || "Failed to fetch external user permissions."
|
|
});
|
|
}
|
|
};
|
|
|
|
exports.getExternalUserPermissionsList = async (req, res) => {
|
|
try {
|
|
const requestedSite = Number(req.query.site || req.query.allow_site);
|
|
const allowSite = Number.isInteger(requestedSite) && requestedSite > 0 ? requestedSite : splitSite.findSiteNumber(req);
|
|
const records = await ExtUserPermission.find({ allow_site: allowSite }).sort({ username: 1, updatedAt: -1 });
|
|
return res.send(records);
|
|
} catch (err) {
|
|
return res.status(500).send({
|
|
message: err.message || "Failed to fetch external user permissions list."
|
|
});
|
|
}
|
|
};
|
|
|
|
exports.saveExternalUserPermission = async (req, res) => {
|
|
try {
|
|
const externalUserId = `${req.body.external_user_id || req.body.externalUserId || ""}`.trim();
|
|
const allowSite = Number(req.body.allow_site || req.body.allowSite);
|
|
const username = req.body.username || "";
|
|
const name = req.body.name || "";
|
|
const title = req.body.title || "";
|
|
const email = req.body.email || "";
|
|
if (!externalUserId) {
|
|
return res.status(400).send({ message: "external_user_id is required." });
|
|
}
|
|
if (!Number.isInteger(allowSite) || allowSite <= 0) {
|
|
return res.status(400).send({ message: "allow_site must be a positive number." });
|
|
}
|
|
|
|
const rawPermissions = Array.isArray(req.body.permissions) ? req.body.permissions : [];
|
|
const permissions = Array.from(new Set([SYSTEM_ACCESS_PERMISSION, ...rawPermissions.filter(Boolean)]));
|
|
const editorId = req.userId ? `${req.userId}` : "";
|
|
const existing = await ExtUserPermission.findOne({
|
|
external_user_id: externalUserId,
|
|
allow_site: allowSite
|
|
});
|
|
const normalizedTitle = `${title || ""}`.trim();
|
|
const payload = {
|
|
external_user_id: externalUserId,
|
|
allow_site: allowSite,
|
|
username,
|
|
name,
|
|
email,
|
|
permissions,
|
|
edit_by: editorId
|
|
};
|
|
if (normalizedTitle) {
|
|
payload.title = normalizedTitle;
|
|
} else if (existing?.title) {
|
|
// Keep existing title when client does not provide one.
|
|
payload.title = existing.title;
|
|
}
|
|
if (!existing) {
|
|
payload.create_by = editorId;
|
|
}
|
|
|
|
const saved = await ExtUserPermission.findOneAndUpdate(
|
|
{
|
|
external_user_id: externalUserId,
|
|
allow_site: allowSite
|
|
},
|
|
payload,
|
|
{
|
|
upsert: true,
|
|
new: true,
|
|
runValidators: true,
|
|
setDefaultsOnInsert: true
|
|
}
|
|
);
|
|
return res.send(saved);
|
|
} catch (err) {
|
|
return res.status(500).send({
|
|
message: err.message || "Failed to save external user permissions."
|
|
});
|
|
}
|
|
};
|
|
|
|
exports.revokeExternalUserPermission = async (req, res) => {
|
|
try {
|
|
const externalUserId = `${req.query.external_user_id || req.query.externalUserId || req.body.external_user_id || ""}`.trim();
|
|
const requestedSite = Number(req.query.site || req.query.allow_site || req.body.allow_site);
|
|
const allowSite = Number.isInteger(requestedSite) && requestedSite > 0 ? requestedSite : splitSite.findSiteNumber(req);
|
|
if (!externalUserId) {
|
|
return res.status(400).send({ message: "external_user_id is required." });
|
|
}
|
|
await ExtUserPermission.deleteOne({
|
|
external_user_id: externalUserId,
|
|
allow_site: allowSite
|
|
});
|
|
return res.send({ success: true });
|
|
} catch (err) {
|
|
return res.status(500).send({
|
|
message: err.message || "Failed to revoke external user permissions."
|
|
});
|
|
}
|
|
};
|