Compare commits
No commits in common. "52b2e40762b4e32577f04fa576c34440e28f1c24" and "ceb8c97531d7320046ae5253fb63022e7a8b289d" have entirely different histories.
52b2e40762
...
ceb8c97531
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(
|
const [rows] = await pool.query(
|
||||||
`SELECT
|
`SELECT
|
||||||
c.contract_id,
|
c.contract_id,
|
||||||
c.work_email,
|
e.work_email,
|
||||||
c.date_joining,
|
c.date_joining,
|
||||||
c.probation_days,
|
c.probation_days,
|
||||||
c.status,
|
c.status,
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export const getCompanies = async (ctx: any) => {
|
|||||||
|
|
||||||
export const getBranches = async (ctx: any) => {
|
export const getBranches = async (ctx: any) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query(`SELECT branch_id, branch_name,code, company_id FROM branches`);
|
const [rows] = await pool.query(`SELECT branch_id, name, company_id FROM branches`);
|
||||||
ctx.response.status = 200;
|
ctx.response.status = 200;
|
||||||
ctx.response.body = { success: true, data: rows };
|
ctx.response.body = { success: true, data: rows };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -37,7 +37,7 @@ export const getDepartments = async (ctx: any) => {
|
|||||||
|
|
||||||
export const getJobs = async (ctx: any) => {
|
export const getJobs = async (ctx: any) => {
|
||||||
try {
|
try {
|
||||||
const [rows] = await pool.query(`SELECT job_id, title, company_id FROM job_positions`);
|
const [rows] = await pool.query(`SELECT job_id, title, department_id FROM job_positions`);
|
||||||
ctx.response.status = 200;
|
ctx.response.status = 200;
|
||||||
ctx.response.body = { success: true, data: rows };
|
ctx.response.body = { success: true, data: rows };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -95,92 +95,3 @@ export const bulkSeedEmployees = async (ctx: Context) => {
|
|||||||
connection.release();
|
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
// 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");
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,11 @@
|
|||||||
import { Router } from "@oak/oak";
|
import { Router } from "@oak/oak";
|
||||||
import {
|
import {
|
||||||
getEmployees,
|
getEmployees,
|
||||||
createEmployee,
|
createEmployee,
|
||||||
getEmployeeById,
|
getEmployeeById,
|
||||||
updateEmployee,
|
updateEmployee,
|
||||||
deleteEmployee
|
deleteEmployee
|
||||||
} from "./controllers/employee.controller.ts";
|
} from "./controllers/employee.controller.ts";
|
||||||
import {
|
import {
|
||||||
getCompanies,
|
getCompanies,
|
||||||
getBranches,
|
getBranches,
|
||||||
@ -16,73 +16,52 @@ import {
|
|||||||
getEmployeeContracts,
|
getEmployeeContracts,
|
||||||
createContract
|
createContract
|
||||||
} from "./controllers/contract.controller.ts";
|
} from "./controllers/contract.controller.ts";
|
||||||
import {
|
|
||||||
bulkSeedEmployees,
|
|
||||||
bulkMapHierarchy
|
|
||||||
} from "./controllers/system.controller.ts";
|
|
||||||
|
|
||||||
// Shared authentication and authorization middleware
|
import {
|
||||||
import { requireAuth, requireRole, AppRole } from "../shared/auth.ts";
|
bulkSeedEmployees
|
||||||
|
} from "./controllers/system.controller.ts"
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
// ============================================================================
|
// --- Employee Core Endpoints ---
|
||||||
// EMPLOYEE CORE DOMAIN
|
|
||||||
// Manages the corporate identity (employees) and personal data (partners).
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// Retrieve a list of all active employees. Visible to management and administrators.
|
// Retrieve all employees
|
||||||
router.get("/api/v1/employees", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN, AppRole.MANAGER]), getEmployees);
|
router.get("/api/v1/employees", getEmployees);
|
||||||
|
|
||||||
// Retrieve a single employee's comprehensive profile (identity, address, contract). Accessible to any authenticated user.
|
// Retrieve a single employee's comprehensive profile
|
||||||
router.get("/api/v1/employees/:id", requireAuth, getEmployeeById);
|
router.get("/api/v1/employees/:id", getEmployeeById);
|
||||||
|
|
||||||
// Create a new employee profile and initial contract. Restricted to administrators.
|
// Create a new employee
|
||||||
router.post("/api/v1/employees", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), createEmployee);
|
router.post("/api/v1/employees", createEmployee);
|
||||||
|
|
||||||
// Update an existing employee's personal or address data. Restricted to administrators.
|
// Update an existing employee
|
||||||
router.put("/api/v1/employees/:id", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), updateEmployee);
|
router.put("/api/v1/employees/:id", updateEmployee);
|
||||||
|
|
||||||
// Soft-delete (deactivate) an employee account. Restricted to administrators.
|
// Deactivate an employee
|
||||||
router.delete("/api/v1/employees/:id", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), deleteEmployee);
|
router.delete("/api/v1/employees/:id", deleteEmployee);
|
||||||
|
|
||||||
// ============================================================================
|
// --- Contract Endpoints ---
|
||||||
// CONTRACT & ASSIGNMENT DOMAIN
|
|
||||||
// Manages operational assignments, reporting hierarchies, and job roles.
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// Retrieve the contract history for an individual employee.
|
//Retrieve contract history for an individual employee
|
||||||
router.get("/api/v1/employees/:id/contracts", requireAuth, getEmployeeContracts);
|
router.get("/api/v1/employees/:id/contracts", getEmployeeContracts);
|
||||||
|
|
||||||
// Expire current active contracts and execute a new employment agreement.
|
//Terminate, transition, or execute a new employment agreement.
|
||||||
router.post("/api/v1/contracts", requireAuth, requireRole([AppRole.SUPER_ADMIN, AppRole.ADMIN]), createContract);
|
router.post("/api/v1/contracts", 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.
|
// Fetch legal entities and parent group structures.
|
||||||
router.get("/api/v1/companies", requireAuth, getCompanies);
|
router.get("/api/v1/companies", getCompanies);
|
||||||
|
|
||||||
// Fetch structural branch offices and physical locations.
|
//Fetch structural branch offices
|
||||||
router.get("/api/v1/branches", requireAuth, getBranches);
|
router.get("/api/v1/branches", getBranches);
|
||||||
|
|
||||||
// List corporate departments and organizational chart reporting lines.
|
//List corporate departments and organizational chart reporting lines
|
||||||
router.get("/api/v1/departments", requireAuth, getDepartments);
|
router.get("/api/v1/departments", getDepartments);
|
||||||
|
|
||||||
// List company designations and employment titles.
|
//List company designations and employment titles.
|
||||||
router.get("/api/v1/jobs", requireAuth, getJobs);
|
router.get("/api/v1/jobs", 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;
|
export default router;
|
||||||
@ -1,69 +0,0 @@
|
|||||||
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