import { Router } from "@oak/oak"; import { db } from "../config/database.ts"; import { authenticateToken, authorize, getCurrentUser } from "../middleware/auth.ts"; import { sanitizeInput } from "../middleware/security.ts"; const router = new Router(); // Standard Rate interface interface StandardRate { id: number; sub_department_id: number | null; activity: string | null; rate: number; effective_date: Date; created_by: number; created_at: Date; sub_department_name?: string; department_name?: string; department_id?: number; created_by_name?: string; } // Get all standard rates (default rates for comparison) router.get("/", authenticateToken, async (ctx) => { try { const currentUser = getCurrentUser(ctx); const params = ctx.request.url.searchParams; const departmentId = params.get("departmentId"); const subDepartmentId = params.get("subDepartmentId"); const activity = params.get("activity"); let query = ` SELECT sr.*, sd.name as sub_department_name, d.name as department_name, d.id as department_id, u.name as created_by_name FROM standard_rates sr LEFT JOIN sub_departments sd ON sr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id LEFT JOIN users u ON sr.created_by = u.id WHERE 1=1 `; const queryParams: unknown[] = []; // Supervisors can only see rates for their department if (currentUser.role === "Supervisor") { query += " AND d.id = ?"; queryParams.push(currentUser.departmentId); } if (departmentId) { query += " AND d.id = ?"; queryParams.push(departmentId); } if (subDepartmentId) { query += " AND sr.sub_department_id = ?"; queryParams.push(subDepartmentId); } if (activity) { query += " AND sr.activity = ?"; queryParams.push(activity); } query += " ORDER BY sr.effective_date DESC, sr.created_at DESC"; const rates = await db.query(query, queryParams); ctx.response.body = rates; } catch (error) { console.error("Get standard rates error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Get all rates (contractor + standard) for SuperAdmin - all departments, sorted by date router.get("/all-rates", authenticateToken, authorize("SuperAdmin"), async (ctx) => { try { const params = ctx.request.url.searchParams; const departmentId = params.get("departmentId"); const startDate = params.get("startDate"); const endDate = params.get("endDate"); // Get contractor rates let contractorQuery = ` SELECT cr.id, 'contractor' as rate_type, cr.contractor_id, u.name as contractor_name, cr.sub_department_id, sd.name as sub_department_name, d.id as department_id, d.name as department_name, cr.activity, cr.rate, cr.effective_date, cr.created_at, NULL as created_by, NULL as created_by_name FROM contractor_rates cr JOIN users u ON cr.contractor_id = u.id LEFT JOIN sub_departments sd ON cr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id WHERE 1=1 `; const contractorParams: unknown[] = []; if (departmentId) { contractorQuery += " AND d.id = ?"; contractorParams.push(departmentId); } if (startDate) { contractorQuery += " AND cr.effective_date >= ?"; contractorParams.push(startDate); } if (endDate) { contractorQuery += " AND cr.effective_date <= ?"; contractorParams.push(endDate); } // Get standard rates let standardQuery = ` SELECT sr.id, 'standard' as rate_type, NULL as contractor_id, NULL as contractor_name, sr.sub_department_id, sd.name as sub_department_name, d.id as department_id, d.name as department_name, sr.activity, sr.rate, sr.effective_date, sr.created_at, sr.created_by, u.name as created_by_name FROM standard_rates sr LEFT JOIN sub_departments sd ON sr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id LEFT JOIN users u ON sr.created_by = u.id WHERE 1=1 `; const standardParams: unknown[] = []; if (departmentId) { standardQuery += " AND d.id = ?"; standardParams.push(departmentId); } if (startDate) { standardQuery += " AND sr.effective_date >= ?"; standardParams.push(startDate); } if (endDate) { standardQuery += " AND sr.effective_date <= ?"; standardParams.push(endDate); } const contractorRates = await db.query(contractorQuery, contractorParams); const standardRates = await db.query(standardQuery, standardParams); // Combine and sort by date const allRates = [...contractorRates, ...standardRates].sort((a, b) => { const dateA = new Date(a.effective_date).getTime(); const dateB = new Date(b.effective_date).getTime(); return dateB - dateA; // Descending order }); ctx.response.body = { allRates, summary: { totalContractorRates: contractorRates.length, totalStandardRates: standardRates.length, totalRates: allRates.length, } }; } catch (error) { console.error("Get all rates error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Compare contractor rates with standard rates router.get("/compare", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const params = ctx.request.url.searchParams; const contractorId = params.get("contractorId"); const subDepartmentId = params.get("subDepartmentId"); let departmentFilter = ""; const queryParams: unknown[] = []; if (currentUser.role === "Supervisor") { departmentFilter = " AND d.id = ?"; queryParams.push(currentUser.departmentId); } // Get standard rates let standardQuery = ` SELECT sr.*, sd.name as sub_department_name, d.name as department_name, d.id as department_id FROM standard_rates sr LEFT JOIN sub_departments sd ON sr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id WHERE 1=1 ${departmentFilter} `; if (subDepartmentId) { standardQuery += " AND sr.sub_department_id = ?"; queryParams.push(subDepartmentId); } standardQuery += " ORDER BY sr.effective_date DESC"; const standardRates = await db.query(standardQuery, queryParams); // Get contractor rates for comparison let contractorQuery = ` SELECT cr.*, u.name as contractor_name, sd.name as sub_department_name, d.name as department_name, d.id as department_id FROM contractor_rates cr JOIN users u ON cr.contractor_id = u.id LEFT JOIN sub_departments sd ON cr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id WHERE 1=1 `; const contractorParams: unknown[] = []; if (currentUser.role === "Supervisor") { contractorQuery += " AND d.id = ?"; contractorParams.push(currentUser.departmentId); } if (contractorId) { contractorQuery += " AND cr.contractor_id = ?"; contractorParams.push(contractorId); } if (subDepartmentId) { contractorQuery += " AND cr.sub_department_id = ?"; contractorParams.push(subDepartmentId); } contractorQuery += " ORDER BY cr.effective_date DESC"; const contractorRates = await db.query(contractorQuery, contractorParams); // Build comparison data const comparisons = contractorRates.map(cr => { // Find matching standard rate const matchingStandard = standardRates.find(sr => sr.sub_department_id === cr.sub_department_id && sr.activity === cr.activity ); const standardRate = matchingStandard?.rate || 0; const contractorRate = cr.rate || 0; const difference = contractorRate - standardRate; const percentageDiff = standardRate > 0 ? ((difference / standardRate) * 100).toFixed(2) : null; return { ...cr, standard_rate: standardRate, difference, percentage_difference: percentageDiff, is_above_standard: difference > 0, is_below_standard: difference < 0, }; }); ctx.response.body = { standardRates, contractorRates, comparisons, }; } catch (error) { console.error("Compare rates error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Create standard rate (Supervisor or SuperAdmin) router.post("/", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const body = await ctx.request.body.json() as { subDepartmentId?: number; activity?: string; rate: number; effectiveDate: string; }; const { subDepartmentId, activity, rate, effectiveDate } = body; if (!rate || !effectiveDate) { ctx.response.status = 400; ctx.response.body = { error: "Missing required fields (rate, effectiveDate)" }; return; } // Verify sub-department belongs to supervisor's department if supervisor if (subDepartmentId && currentUser.role === "Supervisor") { const subDepts = await db.query( "SELECT sd.* FROM sub_departments sd JOIN departments d ON sd.department_id = d.id WHERE sd.id = ? AND d.id = ?", [subDepartmentId, currentUser.departmentId] ); if (subDepts.length === 0) { ctx.response.status = 403; ctx.response.body = { error: "Sub-department not in your department" }; return; } } const sanitizedActivity = activity ? sanitizeInput(activity) : null; const result = await db.execute( "INSERT INTO standard_rates (sub_department_id, activity, rate, effective_date, created_by) VALUES (?, ?, ?, ?, ?)", [subDepartmentId || null, sanitizedActivity, rate, effectiveDate, currentUser.id] ); const newRate = await db.query( `SELECT sr.*, sd.name as sub_department_name, d.name as department_name, u.name as created_by_name FROM standard_rates sr LEFT JOIN sub_departments sd ON sr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id LEFT JOIN users u ON sr.created_by = u.id WHERE sr.id = ?`, [result.insertId] ); ctx.response.status = 201; ctx.response.body = newRate[0]; } catch (error) { console.error("Create standard rate error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Update standard rate router.put("/:id", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const rateId = ctx.params.id; const body = await ctx.request.body.json() as { rate?: number; activity?: string; effectiveDate?: string }; const { rate, activity, effectiveDate } = body; // Verify rate exists and user has access let query = ` SELECT sr.*, d.id as department_id FROM standard_rates sr LEFT JOIN sub_departments sd ON sr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id WHERE sr.id = ? `; const params: unknown[] = [rateId]; const existing = await db.query(query, params); if (existing.length === 0) { ctx.response.status = 404; ctx.response.body = { error: "Standard rate not found" }; return; } // Supervisors can only update rates in their department if (currentUser.role === "Supervisor" && existing[0].department_id !== currentUser.departmentId) { ctx.response.status = 403; ctx.response.body = { error: "Access denied - rate not in your department" }; return; } const updates: string[] = []; const updateParams: unknown[] = []; if (rate !== undefined) { updates.push("rate = ?"); updateParams.push(rate); } if (activity !== undefined) { updates.push("activity = ?"); updateParams.push(sanitizeInput(activity)); } if (effectiveDate !== undefined) { updates.push("effective_date = ?"); updateParams.push(effectiveDate); } if (updates.length === 0) { ctx.response.status = 400; ctx.response.body = { error: "No fields to update" }; return; } updateParams.push(rateId); await db.execute( `UPDATE standard_rates SET ${updates.join(", ")} WHERE id = ?`, updateParams ); const updatedRate = await db.query( `SELECT sr.*, sd.name as sub_department_name, d.name as department_name, u.name as created_by_name FROM standard_rates sr LEFT JOIN sub_departments sd ON sr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id LEFT JOIN users u ON sr.created_by = u.id WHERE sr.id = ?`, [rateId] ); ctx.response.body = updatedRate[0]; } catch (error) { console.error("Update standard rate error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); // Delete standard rate router.delete("/:id", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => { try { const currentUser = getCurrentUser(ctx); const rateId = ctx.params.id; // Verify rate exists and user has access const existing = await db.query( `SELECT sr.*, d.id as department_id FROM standard_rates sr LEFT JOIN sub_departments sd ON sr.sub_department_id = sd.id LEFT JOIN departments d ON sd.department_id = d.id WHERE sr.id = ?`, [rateId] ); if (existing.length === 0) { ctx.response.status = 404; ctx.response.body = { error: "Standard rate not found" }; return; } // Supervisors can only delete rates in their department if (currentUser.role === "Supervisor" && existing[0].department_id !== currentUser.departmentId) { ctx.response.status = 403; ctx.response.body = { error: "Access denied - rate not in your department" }; return; } await db.execute("DELETE FROM standard_rates WHERE id = ?", [rateId]); ctx.response.body = { message: "Standard rate deleted successfully" }; } catch (error) { console.error("Delete standard rate error:", error); ctx.response.status = 500; ctx.response.body = { error: "Internal server error" }; } }); export default router;