(Feat-Fix): Lots of fixes done, reporting system fixed, stricter types
This commit is contained in:
@@ -1,20 +1,30 @@
|
||||
import { Router } from "@oak/oak";
|
||||
import { Router, type RouterContext, type State } from "@oak/oak";
|
||||
import { db } from "../config/database.ts";
|
||||
import { authenticateToken, authorize, getCurrentUser } from "../middleware/auth.ts";
|
||||
import type { EmployeeSwap, CreateSwapRequest, User } from "../types/index.ts";
|
||||
import {
|
||||
authenticateToken,
|
||||
authorize,
|
||||
getCurrentUser,
|
||||
} from "../middleware/auth.ts";
|
||||
import type { CreateSwapRequest, EmployeeSwap, User } from "../types/index.ts";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
// Get all employee swaps (SuperAdmin only)
|
||||
router.get("/", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const params = ctx.request.url.searchParams;
|
||||
const status = params.get("status");
|
||||
const employeeId = params.get("employeeId");
|
||||
const startDate = params.get("startDate");
|
||||
const endDate = params.get("endDate");
|
||||
|
||||
let query = `
|
||||
router.get(
|
||||
"/",
|
||||
authenticateToken,
|
||||
authorize("SuperAdmin"),
|
||||
async (
|
||||
ctx: RouterContext<"/", Record<string | number, string | undefined>, State>,
|
||||
) => {
|
||||
try {
|
||||
const params: URLSearchParams = ctx.request.url.searchParams;
|
||||
const status: string | null = params.get("status");
|
||||
const employeeId: string | null = params.get("employeeId");
|
||||
const startDate: string | null = params.get("startDate");
|
||||
const endDate: string | null = params.get("endDate");
|
||||
|
||||
let query = `
|
||||
SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
@@ -31,46 +41,57 @@ router.get("/", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE 1=1
|
||||
`;
|
||||
const queryParams: unknown[] = [];
|
||||
|
||||
if (status) {
|
||||
query += " AND es.status = ?";
|
||||
queryParams.push(status);
|
||||
const queryParams: unknown[] = [];
|
||||
|
||||
if (status) {
|
||||
query += " AND es.status = ?";
|
||||
queryParams.push(status);
|
||||
}
|
||||
|
||||
if (employeeId) {
|
||||
query += " AND es.employee_id = ?";
|
||||
queryParams.push(employeeId);
|
||||
}
|
||||
|
||||
if (startDate) {
|
||||
query += " AND es.swap_date >= ?";
|
||||
queryParams.push(startDate);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
query += " AND es.swap_date <= ?";
|
||||
queryParams.push(endDate);
|
||||
}
|
||||
|
||||
query += " ORDER BY es.created_at DESC";
|
||||
|
||||
const swaps = await db.query<EmployeeSwap[]>(query, queryParams);
|
||||
ctx.response.body = swaps;
|
||||
} catch (error) {
|
||||
console.error("Get employee swaps error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
|
||||
if (employeeId) {
|
||||
query += " AND es.employee_id = ?";
|
||||
queryParams.push(employeeId);
|
||||
}
|
||||
|
||||
if (startDate) {
|
||||
query += " AND es.swap_date >= ?";
|
||||
queryParams.push(startDate);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
query += " AND es.swap_date <= ?";
|
||||
queryParams.push(endDate);
|
||||
}
|
||||
|
||||
query += " ORDER BY es.created_at DESC";
|
||||
|
||||
const swaps = await db.query<EmployeeSwap[]>(query, queryParams);
|
||||
ctx.response.body = swaps;
|
||||
} catch (error) {
|
||||
console.error("Get employee swaps error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Get swap by ID
|
||||
router.get("/:id", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const swapId = ctx.params.id;
|
||||
|
||||
const swaps = await db.query<EmployeeSwap[]>(
|
||||
`SELECT es.*,
|
||||
router.get(
|
||||
"/:id",
|
||||
authenticateToken,
|
||||
authorize("SuperAdmin"),
|
||||
async (
|
||||
ctx: RouterContext<
|
||||
"/:id",
|
||||
{ id: string } & Record<string | number, string | undefined>,
|
||||
State
|
||||
>,
|
||||
) => {
|
||||
try {
|
||||
const swapId = ctx.params.id;
|
||||
|
||||
const swaps = await db.query<EmployeeSwap[]>(
|
||||
`SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
td.name as target_department_name,
|
||||
@@ -85,45 +106,49 @@ router.get("/:id", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE es.id = ?`,
|
||||
[swapId]
|
||||
);
|
||||
|
||||
if (swaps.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Swap record not found" };
|
||||
return;
|
||||
[swapId],
|
||||
);
|
||||
|
||||
if (swaps.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Swap record not found" };
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.response.body = swaps[0];
|
||||
} catch (error) {
|
||||
console.error("Get swap error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
|
||||
ctx.response.body = swaps[0];
|
||||
} catch (error) {
|
||||
console.error("Get swap error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Create new employee swap (SuperAdmin only)
|
||||
router.post("/", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const currentUser = getCurrentUser(ctx);
|
||||
const body = await ctx.request.body.json() as CreateSwapRequest;
|
||||
const {
|
||||
employeeId,
|
||||
targetDepartmentId,
|
||||
targetContractorId,
|
||||
swapReason,
|
||||
reasonDetails,
|
||||
const {
|
||||
employeeId,
|
||||
targetDepartmentId,
|
||||
targetContractorId,
|
||||
swapReason,
|
||||
reasonDetails,
|
||||
workCompletionPercentage,
|
||||
swapDate
|
||||
swapDate,
|
||||
} = body;
|
||||
|
||||
|
||||
// Validate required fields
|
||||
if (!employeeId || !targetDepartmentId || !swapReason || !swapDate) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Employee ID, target department, swap reason, and swap date are required" };
|
||||
ctx.response.body = {
|
||||
error:
|
||||
"Employee ID, target department, swap reason, and swap date are required",
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Validate swap reason
|
||||
const validReasons = ["LeftWork", "Sick", "FinishedEarly", "Other"];
|
||||
if (!validReasons.includes(swapReason)) {
|
||||
@@ -131,87 +156,108 @@ router.post("/", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
ctx.response.body = { error: "Invalid swap reason" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get employee's current department and contractor
|
||||
const employees = await db.query<User[]>(
|
||||
"SELECT * FROM users WHERE id = ? AND role = 'Employee'",
|
||||
[employeeId]
|
||||
[employeeId],
|
||||
);
|
||||
|
||||
|
||||
if (employees.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Employee not found" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const employee = employees[0];
|
||||
|
||||
|
||||
if (!employee.department_id) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Employee has no current department" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check if there's already an active swap for this employee
|
||||
const activeSwaps = await db.query<EmployeeSwap[]>(
|
||||
"SELECT * FROM employee_swaps WHERE employee_id = ? AND status = 'Active'",
|
||||
[employeeId]
|
||||
[employeeId],
|
||||
);
|
||||
|
||||
|
||||
if (activeSwaps.length > 0) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.body = { error: "Employee already has an active swap. Complete or cancel it first." };
|
||||
ctx.response.body = {
|
||||
error:
|
||||
"Employee already has an active swap. Complete or cancel it first.",
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the swap record
|
||||
const result = await db.execute(
|
||||
`INSERT INTO employee_swaps
|
||||
(employee_id, original_department_id, target_department_id, original_contractor_id, target_contractor_id,
|
||||
swap_reason, reason_details, work_completion_percentage, swap_date, swapped_by, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'Active')`,
|
||||
[
|
||||
employeeId,
|
||||
employee.department_id,
|
||||
targetDepartmentId,
|
||||
employee.contractor_id || null,
|
||||
targetContractorId || null,
|
||||
swapReason,
|
||||
reasonDetails || null,
|
||||
workCompletionPercentage || 0,
|
||||
swapDate,
|
||||
currentUser.id
|
||||
]
|
||||
);
|
||||
|
||||
// Update the employee's department and contractor
|
||||
await db.execute(
|
||||
"UPDATE users SET department_id = ?, contractor_id = ? WHERE id = ?",
|
||||
[targetDepartmentId, targetContractorId || null, employeeId]
|
||||
);
|
||||
|
||||
// Fetch the created swap
|
||||
const newSwap = await db.query<EmployeeSwap[]>(
|
||||
`SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
td.name as target_department_name,
|
||||
oc.name as original_contractor_name,
|
||||
tc.name as target_contractor_name,
|
||||
sb.name as swapped_by_name
|
||||
FROM employee_swaps es
|
||||
JOIN users e ON es.employee_id = e.id
|
||||
JOIN departments od ON es.original_department_id = od.id
|
||||
JOIN departments td ON es.target_department_id = td.id
|
||||
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE es.id = ?`,
|
||||
[result.insertId]
|
||||
);
|
||||
|
||||
|
||||
// Use transaction to ensure both operations succeed or fail together
|
||||
const newSwap = await db.transaction(async (connection) => {
|
||||
// Create the swap record
|
||||
const [insertResult] = await connection.execute(
|
||||
`INSERT INTO employee_swaps
|
||||
(employee_id, original_department_id, target_department_id, original_contractor_id, target_contractor_id,
|
||||
swap_reason, reason_details, work_completion_percentage, swap_date, swapped_by, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'Active')`,
|
||||
[
|
||||
employeeId,
|
||||
employee.department_id,
|
||||
targetDepartmentId,
|
||||
employee.contractor_id || null,
|
||||
targetContractorId || null,
|
||||
swapReason,
|
||||
reasonDetails || null,
|
||||
workCompletionPercentage || 0,
|
||||
swapDate,
|
||||
currentUser.id,
|
||||
],
|
||||
);
|
||||
|
||||
const swapInsertId = (insertResult as { insertId: number }).insertId;
|
||||
|
||||
if (!swapInsertId) {
|
||||
throw new Error("Failed to create swap record");
|
||||
}
|
||||
|
||||
// Update the employee's department and contractor
|
||||
const [updateResult] = await connection.execute(
|
||||
"UPDATE users SET department_id = ?, contractor_id = ? WHERE id = ?",
|
||||
[targetDepartmentId, targetContractorId || null, employeeId],
|
||||
);
|
||||
|
||||
const affectedRows =
|
||||
(updateResult as { affectedRows: number }).affectedRows;
|
||||
|
||||
if (affectedRows === 0) {
|
||||
throw new Error("Failed to update employee department");
|
||||
}
|
||||
|
||||
// Fetch the created swap
|
||||
const [swapRows] = await connection.query(
|
||||
`SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
td.name as target_department_name,
|
||||
oc.name as original_contractor_name,
|
||||
tc.name as target_contractor_name,
|
||||
sb.name as swapped_by_name
|
||||
FROM employee_swaps es
|
||||
JOIN users e ON es.employee_id = e.id
|
||||
JOIN departments od ON es.original_department_id = od.id
|
||||
JOIN departments td ON es.target_department_id = td.id
|
||||
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE es.id = ?`,
|
||||
[swapInsertId],
|
||||
);
|
||||
|
||||
return (swapRows as EmployeeSwap[])[0];
|
||||
});
|
||||
|
||||
ctx.response.status = 201;
|
||||
ctx.response.body = newSwap[0];
|
||||
ctx.response.body = newSwap;
|
||||
} catch (error) {
|
||||
console.error("Create swap error:", error);
|
||||
ctx.response.status = 500;
|
||||
@@ -220,121 +266,149 @@ router.post("/", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
});
|
||||
|
||||
// Complete a swap (return employee to original department)
|
||||
router.put("/:id/complete", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const swapId = ctx.params.id;
|
||||
|
||||
// Get the swap record
|
||||
const swaps = await db.query<EmployeeSwap[]>(
|
||||
"SELECT * FROM employee_swaps WHERE id = ? AND status = 'Active'",
|
||||
[swapId]
|
||||
);
|
||||
|
||||
if (swaps.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Active swap not found" };
|
||||
return;
|
||||
router.put(
|
||||
"/:id/complete",
|
||||
authenticateToken,
|
||||
authorize("SuperAdmin"),
|
||||
async (ctx) => {
|
||||
try {
|
||||
const swapId = ctx.params.id;
|
||||
|
||||
// Get the swap record
|
||||
const swaps = await db.query<EmployeeSwap[]>(
|
||||
"SELECT * FROM employee_swaps WHERE id = ? AND status = 'Active'",
|
||||
[swapId],
|
||||
);
|
||||
|
||||
if (swaps.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Active swap not found" };
|
||||
return;
|
||||
}
|
||||
|
||||
const swap = swaps[0];
|
||||
|
||||
// Use transaction to ensure both operations succeed or fail together
|
||||
const updatedSwap = await db.transaction(async (connection) => {
|
||||
// Return employee to original department and contractor
|
||||
await connection.execute(
|
||||
"UPDATE users SET department_id = ?, contractor_id = ? WHERE id = ?",
|
||||
[
|
||||
swap.original_department_id,
|
||||
swap.original_contractor_id,
|
||||
swap.employee_id,
|
||||
],
|
||||
);
|
||||
|
||||
// Mark swap as completed
|
||||
await connection.execute(
|
||||
"UPDATE employee_swaps SET status = 'Completed', completed_at = NOW() WHERE id = ?",
|
||||
[swapId],
|
||||
);
|
||||
|
||||
// Fetch updated swap
|
||||
const [swapRows] = await connection.query(
|
||||
`SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
td.name as target_department_name,
|
||||
oc.name as original_contractor_name,
|
||||
tc.name as target_contractor_name,
|
||||
sb.name as swapped_by_name
|
||||
FROM employee_swaps es
|
||||
JOIN users e ON es.employee_id = e.id
|
||||
JOIN departments od ON es.original_department_id = od.id
|
||||
JOIN departments td ON es.target_department_id = td.id
|
||||
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE es.id = ?`,
|
||||
[swapId],
|
||||
);
|
||||
|
||||
return (swapRows as EmployeeSwap[])[0];
|
||||
});
|
||||
|
||||
ctx.response.body = updatedSwap;
|
||||
} catch (error) {
|
||||
console.error("Complete swap error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
|
||||
const swap = swaps[0];
|
||||
|
||||
// Return employee to original department and contractor
|
||||
await db.execute(
|
||||
"UPDATE users SET department_id = ?, contractor_id = ? WHERE id = ?",
|
||||
[swap.original_department_id, swap.original_contractor_id, swap.employee_id]
|
||||
);
|
||||
|
||||
// Mark swap as completed
|
||||
await db.execute(
|
||||
"UPDATE employee_swaps SET status = 'Completed', completed_at = NOW() WHERE id = ?",
|
||||
[swapId]
|
||||
);
|
||||
|
||||
// Fetch updated swap
|
||||
const updatedSwap = await db.query<EmployeeSwap[]>(
|
||||
`SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
td.name as target_department_name,
|
||||
oc.name as original_contractor_name,
|
||||
tc.name as target_contractor_name,
|
||||
sb.name as swapped_by_name
|
||||
FROM employee_swaps es
|
||||
JOIN users e ON es.employee_id = e.id
|
||||
JOIN departments od ON es.original_department_id = od.id
|
||||
JOIN departments td ON es.target_department_id = td.id
|
||||
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE es.id = ?`,
|
||||
[swapId]
|
||||
);
|
||||
|
||||
ctx.response.body = updatedSwap[0];
|
||||
} catch (error) {
|
||||
console.error("Complete swap error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// Cancel a swap (return employee to original department)
|
||||
router.put("/:id/cancel", authenticateToken, authorize("SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const swapId = ctx.params.id;
|
||||
|
||||
// Get the swap record
|
||||
const swaps = await db.query<EmployeeSwap[]>(
|
||||
"SELECT * FROM employee_swaps WHERE id = ? AND status = 'Active'",
|
||||
[swapId]
|
||||
);
|
||||
|
||||
if (swaps.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Active swap not found" };
|
||||
return;
|
||||
router.put(
|
||||
"/:id/cancel",
|
||||
authenticateToken,
|
||||
authorize("SuperAdmin"),
|
||||
async (ctx) => {
|
||||
try {
|
||||
const swapId = ctx.params.id;
|
||||
|
||||
// Get the swap record
|
||||
const swaps = await db.query<EmployeeSwap[]>(
|
||||
"SELECT * FROM employee_swaps WHERE id = ? AND status = 'Active'",
|
||||
[swapId],
|
||||
);
|
||||
|
||||
if (swaps.length === 0) {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = { error: "Active swap not found" };
|
||||
return;
|
||||
}
|
||||
|
||||
const swap = swaps[0];
|
||||
|
||||
// Use transaction to ensure both operations succeed or fail together
|
||||
const updatedSwap = await db.transaction(async (connection) => {
|
||||
// Return employee to original department and contractor
|
||||
await connection.execute(
|
||||
"UPDATE users SET department_id = ?, contractor_id = ? WHERE id = ?",
|
||||
[
|
||||
swap.original_department_id,
|
||||
swap.original_contractor_id,
|
||||
swap.employee_id,
|
||||
],
|
||||
);
|
||||
|
||||
// Mark swap as cancelled
|
||||
await connection.execute(
|
||||
"UPDATE employee_swaps SET status = 'Cancelled', completed_at = NOW() WHERE id = ?",
|
||||
[swapId],
|
||||
);
|
||||
|
||||
// Fetch updated swap
|
||||
const [swapRows] = await connection.query(
|
||||
`SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
td.name as target_department_name,
|
||||
oc.name as original_contractor_name,
|
||||
tc.name as target_contractor_name,
|
||||
sb.name as swapped_by_name
|
||||
FROM employee_swaps es
|
||||
JOIN users e ON es.employee_id = e.id
|
||||
JOIN departments od ON es.original_department_id = od.id
|
||||
JOIN departments td ON es.target_department_id = td.id
|
||||
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE es.id = ?`,
|
||||
[swapId],
|
||||
);
|
||||
|
||||
return (swapRows as EmployeeSwap[])[0];
|
||||
});
|
||||
|
||||
ctx.response.body = updatedSwap;
|
||||
} catch (error) {
|
||||
console.error("Cancel swap error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
|
||||
const swap = swaps[0];
|
||||
|
||||
// Return employee to original department and contractor
|
||||
await db.execute(
|
||||
"UPDATE users SET department_id = ?, contractor_id = ? WHERE id = ?",
|
||||
[swap.original_department_id, swap.original_contractor_id, swap.employee_id]
|
||||
);
|
||||
|
||||
// Mark swap as cancelled
|
||||
await db.execute(
|
||||
"UPDATE employee_swaps SET status = 'Cancelled', completed_at = NOW() WHERE id = ?",
|
||||
[swapId]
|
||||
);
|
||||
|
||||
// Fetch updated swap
|
||||
const updatedSwap = await db.query<EmployeeSwap[]>(
|
||||
`SELECT es.*,
|
||||
e.name as employee_name,
|
||||
od.name as original_department_name,
|
||||
td.name as target_department_name,
|
||||
oc.name as original_contractor_name,
|
||||
tc.name as target_contractor_name,
|
||||
sb.name as swapped_by_name
|
||||
FROM employee_swaps es
|
||||
JOIN users e ON es.employee_id = e.id
|
||||
JOIN departments od ON es.original_department_id = od.id
|
||||
JOIN departments td ON es.target_department_id = td.id
|
||||
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||
JOIN users sb ON es.swapped_by = sb.id
|
||||
WHERE es.id = ?`,
|
||||
[swapId]
|
||||
);
|
||||
|
||||
ctx.response.body = updatedSwap[0];
|
||||
} catch (error) {
|
||||
console.error("Cancel swap error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user