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 currentSite = splitSite.findSiteNumber(req); const requestBodies = [ { username: HR_INTEGRATION_USERNAME, password: HR_INTEGRATION_PASSWORD, site: currentSite, status: "active" }, { username: HR_INTEGRATION_USERNAME, password: HR_INTEGRATION_PASSWORD, site: currentSite }, { 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 (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 { console.log("[HR Integration] Requesting employee list from:", HR_EMPLOYEE_LIST_ENDPOINT, "payload:", requestBody); const response = await axios.post(HR_EMPLOYEE_LIST_ENDPOINT, requestBody, requestOptions); const list = normalizeEmployeesList(response?.data); console.log( "[HR Integration] employee list response meta:", { endpoint: HR_EMPLOYEE_LIST_ENDPOINT, count: list.length, responseType: typeof response?.data } ); console.log("[HR Integration] raw employee list response:", response?.data); bestResponseData = response?.data; if (list.length > 0) { return res.send(list); } } catch (attemptError) { console.log("[HR Integration] employee list attempt failed:", { endpoint: HR_EMPLOYEE_LIST_ENDPOINT, payload: requestBody, status: attemptError?.response?.status, data: attemptError?.response?.data, message: attemptError?.message }); } } const emptyList = normalizeEmployeesList(bestResponseData); console.log("[HR Integration] all attempts completed; returning list length:", emptyList.length); return res.send(emptyList); } catch (err) { console.log("[HR Integration] /employees/list error status:", err?.response?.status); console.log("[HR Integration] /employees/list error data:", err?.response?.data); console.log("[HR Integration] /employees/list error message:", err?.message); 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 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 payload = { external_user_id: externalUserId, allow_site: allowSite, username, name, email, permissions, edit_by: editorId }; 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." }); } };