Compare commits
2 Commits
ceb8c97531
...
52b2e40762
| Author | SHA1 | Date | |
|---|---|---|---|
| 52b2e40762 | |||
| 1572456e70 |
2018
ems-service/AllStaff.json
Normal file
2018
ems-service/AllStaff.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@ export const getEmployeeContracts = async (ctx: any) => {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT
|
||||
c.contract_id,
|
||||
e.work_email,
|
||||
c.work_email,
|
||||
c.date_joining,
|
||||
c.probation_days,
|
||||
c.status,
|
||||
|
||||
@ -15,7 +15,7 @@ export const getCompanies = async (ctx: any) => {
|
||||
|
||||
export const getBranches = async (ctx: any) => {
|
||||
try {
|
||||
const [rows] = await pool.query(`SELECT branch_id, name, company_id FROM branches`);
|
||||
const [rows] = await pool.query(`SELECT branch_id, branch_name,code, company_id FROM branches`);
|
||||
ctx.response.status = 200;
|
||||
ctx.response.body = { success: true, data: rows };
|
||||
} catch (error) {
|
||||
@ -37,7 +37,7 @@ export const getDepartments = async (ctx: any) => {
|
||||
|
||||
export const getJobs = async (ctx: any) => {
|
||||
try {
|
||||
const [rows] = await pool.query(`SELECT job_id, title, department_id FROM job_positions`);
|
||||
const [rows] = await pool.query(`SELECT job_id, title, company_id FROM job_positions`);
|
||||
ctx.response.status = 200;
|
||||
ctx.response.body = { success: true, data: rows };
|
||||
} catch (error) {
|
||||
|
||||
@ -95,3 +95,92 @@ export const bulkSeedEmployees = async (ctx: Context) => {
|
||||
connection.release();
|
||||
}
|
||||
};
|
||||
|
||||
export const bulkMapHierarchy = async (ctx: Context) => {
|
||||
const body = ctx.request.hasBody ? await ctx.request.body.json() : {};
|
||||
|
||||
const { employeeHierarchy = [], departmentHierarchy = [] } = body;
|
||||
|
||||
if (employeeHierarchy.length === 0 && departmentHierarchy.length === 0) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { success: false, message: "Provide hierarchy mapping arrays." };
|
||||
return;
|
||||
}
|
||||
|
||||
const connection = await emsPool.getConnection();
|
||||
let contractsUpdated = 0;
|
||||
let departmentsUpdated = 0;
|
||||
|
||||
try {
|
||||
await connection.beginTransaction();
|
||||
|
||||
// 1. Map Employees to their Managers
|
||||
for (const mapping of employeeHierarchy) {
|
||||
const { emp_code, manager_code } = mapping;
|
||||
|
||||
if (!emp_code || !manager_code) continue;
|
||||
|
||||
const [result]: any = await connection.execute(
|
||||
`UPDATE contracts c
|
||||
JOIN employees e ON c.employee_id = e.employee_id
|
||||
JOIN employees m ON m.employee_code = ?
|
||||
SET c.reporting_to_id = m.employee_id
|
||||
WHERE e.employee_code = ? AND c.status = 'ACTIVE'`,
|
||||
[manager_code, emp_code]
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) contractsUpdated++;
|
||||
}
|
||||
|
||||
// 2. Map Departments to their Managers and Parent Departments
|
||||
for (const dept of departmentHierarchy) {
|
||||
const { department_name, manager_code, parent_department_name } = dept;
|
||||
|
||||
if (!department_name) continue;
|
||||
|
||||
// Update Department Manager
|
||||
if (manager_code) {
|
||||
await connection.execute(
|
||||
`UPDATE departments d
|
||||
JOIN employees m ON m.employee_code = ?
|
||||
SET d.manager_id = m.employee_id
|
||||
WHERE d.name = ?`,
|
||||
[manager_code, department_name]
|
||||
);
|
||||
}
|
||||
|
||||
// Update Parent Department Hierarchy
|
||||
if (parent_department_name) {
|
||||
await connection.execute(
|
||||
`UPDATE departments d
|
||||
JOIN departments p ON p.name = ?
|
||||
SET d.parent_id = p.department_id
|
||||
WHERE d.name = ?`,
|
||||
[parent_department_name, department_name]
|
||||
);
|
||||
}
|
||||
departmentsUpdated++;
|
||||
}
|
||||
|
||||
await connection.commit();
|
||||
|
||||
ctx.response.status = 200;
|
||||
ctx.response.body = {
|
||||
success: true,
|
||||
message: "Hierarchy mapping completed successfully.",
|
||||
metrics: {
|
||||
contractsUpdated,
|
||||
departmentsProcessed: departmentsUpdated
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
await connection.rollback();
|
||||
console.error("Hierarchy mapping failed:", error);
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { success: false, error: errorMessage };
|
||||
} finally {
|
||||
connection.release();
|
||||
}
|
||||
};
|
||||
82
ems-service/generate_mapping.ts
Normal file
82
ems-service/generate_mapping.ts
Normal file
@ -0,0 +1,82 @@
|
||||
// generate_mapping.ts
|
||||
import data from "./AllStaff.json" with { type: "json" };
|
||||
|
||||
// 1. Helper to clean up messy department strings
|
||||
const normalizeDept = (dept: string) => {
|
||||
if (dept.toLowerCase() === "finance" || dept === "FInance") return "Finance";
|
||||
if (dept.toLowerCase() === "digital marketing") return "Digital Marketing";
|
||||
return dept.trim();
|
||||
};
|
||||
|
||||
// 2. Helper to determine leadership rank based on designation
|
||||
const getRank = (designation: string) => {
|
||||
const title = designation.toLowerCase();
|
||||
if (title.includes("cfo") || title.includes("vice president") || title.includes("co- founder")) return 100;
|
||||
if (title.includes("senior manager")) return 90;
|
||||
if (title.includes("manager") && !title.includes("assistant")) return 80;
|
||||
if (title.includes("assistant manager")) return 70;
|
||||
if (title.includes("team leader")) return 60;
|
||||
return 0; // Regular employee
|
||||
};
|
||||
|
||||
const departmentsMap = new Map();
|
||||
const employeeHierarchy: any[] = [];
|
||||
const departmentHierarchy: any[] = [];
|
||||
|
||||
// 3. First Pass: Group employees by department and find the leader
|
||||
for (const emp of data) {
|
||||
const deptName = normalizeDept(emp.department);
|
||||
const rank = getRank(emp.designation);
|
||||
|
||||
if (!departmentsMap.has(deptName)) {
|
||||
departmentsMap.set(deptName, { name: deptName, leaderCode: null, highestRank: -1 });
|
||||
}
|
||||
|
||||
const currentDept = departmentsMap.get(deptName);
|
||||
|
||||
// If this employee has a higher rank than the current department leader, replace them
|
||||
if (rank > currentDept.highestRank) {
|
||||
currentDept.leaderCode = emp.emp_code;
|
||||
currentDept.highestRank = rank;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Second Pass: Build the mappings
|
||||
for (const emp of data) {
|
||||
const deptName = normalizeDept(emp.department);
|
||||
const deptInfo = departmentsMap.get(deptName);
|
||||
|
||||
// Only map the employee if they aren't the leader themselves and a leader exists
|
||||
let managerCode = deptInfo.leaderCode;
|
||||
if (managerCode === emp.emp_code) {
|
||||
managerCode = null; // The top boss doesn't report to themselves in this scope
|
||||
}
|
||||
|
||||
employeeHierarchy.push({
|
||||
emp_code: emp.emp_code,
|
||||
manager_code: managerCode
|
||||
});
|
||||
}
|
||||
|
||||
// 5. Build Department Hierarchy
|
||||
for (const [deptName, info] of departmentsMap.entries()) {
|
||||
departmentHierarchy.push({
|
||||
department_name: deptName,
|
||||
manager_code: info.leaderCode,
|
||||
parent_department_name: null // You can manually tweak parent/child relationships later
|
||||
});
|
||||
}
|
||||
|
||||
// 6. Write the final JSON payload
|
||||
const output = {
|
||||
departmentHierarchy,
|
||||
employeeHierarchy
|
||||
};
|
||||
|
||||
await Deno.writeTextFile(
|
||||
"./hierarchy_mapping.json",
|
||||
JSON.stringify(output, null, 2)
|
||||
);
|
||||
|
||||
console.log(`Successfully mapped ${employeeHierarchy.length} employees across ${departmentsMap.size} departments!`);
|
||||
console.log("File saved to ./hierarchy_mapping.json");
|
||||
1094
ems-service/hierarchy_mapping.json
Normal file
1094
ems-service/hierarchy_mapping.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -16,52 +16,73 @@ import {
|
||||
getEmployeeContracts,
|
||||
createContract
|
||||
} from "./controllers/contract.controller.ts";
|
||||
|
||||
import {
|
||||
bulkSeedEmployees
|
||||
} from "./controllers/system.controller.ts"
|
||||
bulkSeedEmployees,
|
||||
bulkMapHierarchy
|
||||
} from "./controllers/system.controller.ts";
|
||||
|
||||
// Shared authentication and authorization middleware
|
||||
import { requireAuth, requireRole, AppRole } from "../shared/auth.ts";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
// --- Employee Core Endpoints ---
|
||||
// ============================================================================
|
||||
// EMPLOYEE CORE DOMAIN
|
||||
// Manages the corporate identity (employees) and personal data (partners).
|
||||
// ============================================================================
|
||||
|
||||
// Retrieve all employees
|
||||
router.get("/api/v1/employees", getEmployees);
|
||||
// Retrieve a list of all active employees. Visible to management and administrators.
|
||||
router.get("/api/v1/employees", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN, AppRole.MANAGER]), getEmployees);
|
||||
|
||||
// Retrieve a single employee's comprehensive profile
|
||||
router.get("/api/v1/employees/:id", getEmployeeById);
|
||||
// Retrieve a single employee's comprehensive profile (identity, address, contract). Accessible to any authenticated user.
|
||||
router.get("/api/v1/employees/:id", requireAuth, getEmployeeById);
|
||||
|
||||
// Create a new employee
|
||||
router.post("/api/v1/employees", createEmployee);
|
||||
// Create a new employee profile and initial contract. Restricted to administrators.
|
||||
router.post("/api/v1/employees", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), createEmployee);
|
||||
|
||||
// Update an existing employee
|
||||
router.put("/api/v1/employees/:id", updateEmployee);
|
||||
// Update an existing employee's personal or address data. Restricted to administrators.
|
||||
router.put("/api/v1/employees/:id", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), updateEmployee);
|
||||
|
||||
// Deactivate an employee
|
||||
router.delete("/api/v1/employees/:id", deleteEmployee);
|
||||
// Soft-delete (deactivate) an employee account. Restricted to administrators.
|
||||
router.delete("/api/v1/employees/:id", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), deleteEmployee);
|
||||
|
||||
// --- Contract Endpoints ---
|
||||
// ============================================================================
|
||||
// CONTRACT & ASSIGNMENT DOMAIN
|
||||
// Manages operational assignments, reporting hierarchies, and job roles.
|
||||
// ============================================================================
|
||||
|
||||
//Retrieve contract history for an individual employee
|
||||
router.get("/api/v1/employees/:id/contracts", getEmployeeContracts);
|
||||
// Retrieve the contract history for an individual employee.
|
||||
router.get("/api/v1/employees/:id/contracts", requireAuth, getEmployeeContracts);
|
||||
|
||||
//Terminate, transition, or execute a new employment agreement.
|
||||
router.post("/api/v1/contracts", createContract);
|
||||
// Expire current active contracts and execute a new employment agreement.
|
||||
router.post("/api/v1/contracts", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), createContract);
|
||||
|
||||
// --- Organizational Lookup Endpoints ---
|
||||
// ============================================================================
|
||||
// ORGANIZATIONAL LOOKUP DOMAIN
|
||||
// Read-only reference endpoints for the frontend UI to populate select options.
|
||||
// ============================================================================
|
||||
|
||||
// Fetch legal entities and parent group structures.
|
||||
router.get("/api/v1/companies", getCompanies);
|
||||
router.get("/api/v1/companies", requireAuth, getCompanies);
|
||||
|
||||
//Fetch structural branch offices
|
||||
router.get("/api/v1/branches", getBranches);
|
||||
// Fetch structural branch offices and physical locations.
|
||||
router.get("/api/v1/branches", requireAuth, getBranches);
|
||||
|
||||
//List corporate departments and organizational chart reporting lines
|
||||
router.get("/api/v1/departments", getDepartments);
|
||||
// List corporate departments and organizational chart reporting lines.
|
||||
router.get("/api/v1/departments", requireAuth, getDepartments);
|
||||
|
||||
// List company designations and employment titles.
|
||||
router.get("/api/v1/jobs", getJobs);
|
||||
router.get("/api/v1/jobs", requireAuth, getJobs);
|
||||
|
||||
router.post("/api/v1/system/seed-employees", bulkSeedEmployees);
|
||||
// ============================================================================
|
||||
// SYSTEM & MIGRATION DOMAIN
|
||||
// High-risk administrative endpoints for bulk data execution.
|
||||
// ============================================================================
|
||||
|
||||
// Process raw bulk data to seed initial employee/partner structures into the DB.
|
||||
router.post("/api/v1/system/seed-employees", requireAuth, requireRole([AppRole.SUPER_ADMIN]), bulkSeedEmployees);
|
||||
|
||||
// Execute mapping script to establish reporting_to_id and manager_id links.
|
||||
router.post("/api/v1/system/map-hierarchy", requireAuth, requireRole([AppRole.SUPER_ADMIN]), bulkMapHierarchy);
|
||||
|
||||
export default router;
|
||||
69
ems-service/routes.ts.bkp
Normal file
69
ems-service/routes.ts.bkp
Normal file
@ -0,0 +1,69 @@
|
||||
import { Router } from "@oak/oak";
|
||||
import {
|
||||
getEmployees,
|
||||
createEmployee,
|
||||
getEmployeeById,
|
||||
updateEmployee,
|
||||
deleteEmployee
|
||||
} from "./controllers/employee.controller.ts";
|
||||
import {
|
||||
getCompanies,
|
||||
getBranches,
|
||||
getDepartments,
|
||||
getJobs
|
||||
} from "./controllers/lookup.controller.ts";
|
||||
import {
|
||||
getEmployeeContracts,
|
||||
createContract
|
||||
} from "./controllers/contract.controller.ts";
|
||||
|
||||
import {
|
||||
bulkSeedEmployees,
|
||||
bulkMapHierarchy
|
||||
} from "./controllers/system.controller.ts"
|
||||
|
||||
const router = new Router();
|
||||
|
||||
// --- Employee Core Endpoints ---
|
||||
|
||||
// Retrieve all employees
|
||||
router.get("/api/v1/employees", getEmployees);
|
||||
|
||||
// Retrieve a single employee's comprehensive profile
|
||||
router.get("/api/v1/employees/:id", getEmployeeById);
|
||||
|
||||
// Create a new employee
|
||||
router.post("/api/v1/employees", createEmployee);
|
||||
|
||||
// Update an existing employee
|
||||
router.put("/api/v1/employees/:id", updateEmployee);
|
||||
|
||||
// Deactivate an employee
|
||||
router.delete("/api/v1/employees/:id", deleteEmployee);
|
||||
|
||||
// --- Contract Endpoints ---
|
||||
|
||||
//Retrieve contract history for an individual employee
|
||||
router.get("/api/v1/employees/:id/contracts", getEmployeeContracts);
|
||||
|
||||
//Terminate, transition, or execute a new employment agreement.
|
||||
router.post("/api/v1/contracts", createContract);
|
||||
|
||||
// --- Organizational Lookup Endpoints ---
|
||||
|
||||
// Fetch legal entities and parent group structures.
|
||||
router.get("/api/v1/companies", getCompanies);
|
||||
|
||||
//Fetch structural branch offices
|
||||
router.get("/api/v1/branches", getBranches);
|
||||
|
||||
//List corporate departments and organizational chart reporting lines
|
||||
router.get("/api/v1/departments", getDepartments);
|
||||
|
||||
//List company designations and employment titles.
|
||||
router.get("/api/v1/jobs", getJobs);
|
||||
|
||||
router.post("/api/v1/system/seed-employees", bulkSeedEmployees);
|
||||
router.post("/api/v1/system/map-hierarchy", bulkMapHierarchy);
|
||||
|
||||
export default router;
|
||||
Loading…
x
Reference in New Issue
Block a user