(Feat-Fix): Lots of fixes done, reporting system fixed, stricter types
This commit is contained in:
@@ -1,22 +1,30 @@
|
||||
import { Router } from "@oak/oak";
|
||||
import { db } from "../config/database.ts";
|
||||
import { authenticateToken, authorize, getCurrentUser } from "../middleware/auth.ts";
|
||||
import type { WorkAllocation } from "../types/index.ts";
|
||||
import {
|
||||
authenticateToken,
|
||||
authorize,
|
||||
getCurrentUser,
|
||||
} from "../middleware/auth.ts";
|
||||
import type { JWTPayload, WorkAllocation } from "../types/index.ts";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
// Get completed work allocations for reporting (with optional filters)
|
||||
router.get("/completed-allocations", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const currentUser = getCurrentUser(ctx);
|
||||
const params = ctx.request.url.searchParams;
|
||||
const startDate = params.get("startDate");
|
||||
const endDate = params.get("endDate");
|
||||
const departmentId = params.get("departmentId");
|
||||
const contractorId = params.get("contractorId");
|
||||
const employeeId = params.get("employeeId");
|
||||
|
||||
let query = `
|
||||
router.get(
|
||||
"/completed-allocations",
|
||||
authenticateToken,
|
||||
authorize("Supervisor", "SuperAdmin"),
|
||||
async (ctx) => {
|
||||
try {
|
||||
const currentUser: JWTPayload = getCurrentUser(ctx);
|
||||
const params: URLSearchParams = ctx.request.url.searchParams;
|
||||
const startDate: string | null = params.get("startDate");
|
||||
const endDate: string | null = params.get("endDate");
|
||||
const departmentId: string | null = params.get("departmentId");
|
||||
const contractorId: string | null = params.get("contractorId");
|
||||
const employeeId: string | null = params.get("employeeId");
|
||||
|
||||
let query = `
|
||||
SELECT wa.*,
|
||||
e.name as employee_name, e.username as employee_username,
|
||||
e.phone_number as employee_phone,
|
||||
@@ -33,95 +41,110 @@ router.get("/completed-allocations", authenticateToken, authorize("Supervisor",
|
||||
LEFT JOIN departments d ON e.department_id = d.id
|
||||
WHERE wa.status = 'Completed'
|
||||
`;
|
||||
const queryParams: unknown[] = [];
|
||||
|
||||
// Role-based filtering - Supervisors can only see their department
|
||||
if (currentUser.role === "Supervisor") {
|
||||
query += " AND e.department_id = ?";
|
||||
queryParams.push(currentUser.departmentId);
|
||||
}
|
||||
|
||||
// Date range filter
|
||||
if (startDate) {
|
||||
query += " AND wa.completion_date >= ?";
|
||||
queryParams.push(startDate);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
query += " AND wa.completion_date <= ?";
|
||||
queryParams.push(endDate);
|
||||
}
|
||||
|
||||
// Department filter (for SuperAdmin)
|
||||
if (departmentId && currentUser.role === "SuperAdmin") {
|
||||
query += " AND e.department_id = ?";
|
||||
queryParams.push(departmentId);
|
||||
}
|
||||
|
||||
// Contractor filter
|
||||
if (contractorId) {
|
||||
query += " AND wa.contractor_id = ?";
|
||||
queryParams.push(contractorId);
|
||||
}
|
||||
|
||||
// Employee filter
|
||||
if (employeeId) {
|
||||
query += " AND wa.employee_id = ?";
|
||||
queryParams.push(employeeId);
|
||||
}
|
||||
|
||||
query += " ORDER BY wa.completion_date DESC, wa.created_at DESC";
|
||||
|
||||
const allocations = await db.query<WorkAllocation[]>(query, queryParams);
|
||||
|
||||
// Calculate summary stats
|
||||
const totalAllocations = allocations.length;
|
||||
const totalAmount = allocations.reduce((sum, a) => sum + (parseFloat(String(a.total_amount)) || parseFloat(String(a.rate)) || 0), 0);
|
||||
const totalUnits = allocations.reduce((sum, a) => sum + (parseFloat(String(a.units)) || 0), 0);
|
||||
|
||||
ctx.response.body = {
|
||||
allocations,
|
||||
summary: {
|
||||
totalAllocations,
|
||||
totalAmount: totalAmount.toFixed(2),
|
||||
totalUnits: totalUnits.toFixed(2),
|
||||
const queryParams: unknown[] = [];
|
||||
|
||||
// Role-based filtering - Supervisors can only see their department
|
||||
if (currentUser.role === "Supervisor") {
|
||||
query += " AND e.department_id = ?";
|
||||
queryParams.push(currentUser.departmentId);
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Get completed allocations report error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
|
||||
// Date range filter
|
||||
if (startDate) {
|
||||
query += " AND wa.completion_date >= ?";
|
||||
queryParams.push(startDate);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
query += " AND wa.completion_date <= ?";
|
||||
queryParams.push(endDate);
|
||||
}
|
||||
|
||||
// Department filter (for SuperAdmin)
|
||||
if (departmentId && currentUser.role === "SuperAdmin") {
|
||||
query += " AND e.department_id = ?";
|
||||
queryParams.push(departmentId);
|
||||
}
|
||||
|
||||
// Contractor filter
|
||||
if (contractorId) {
|
||||
query += " AND wa.contractor_id = ?";
|
||||
queryParams.push(contractorId);
|
||||
}
|
||||
|
||||
// Employee filter
|
||||
if (employeeId) {
|
||||
query += " AND wa.employee_id = ?";
|
||||
queryParams.push(employeeId);
|
||||
}
|
||||
|
||||
query += " ORDER BY wa.completion_date DESC, wa.created_at DESC";
|
||||
|
||||
const allocations = await db.query<WorkAllocation[]>(query, queryParams);
|
||||
|
||||
// Calculate summary stats
|
||||
const totalAllocations = allocations.length;
|
||||
const totalAmount = allocations.reduce(
|
||||
(sum, a) =>
|
||||
sum +
|
||||
(parseFloat(String(a.total_amount)) || parseFloat(String(a.rate)) ||
|
||||
0),
|
||||
0,
|
||||
);
|
||||
const totalUnits = allocations.reduce(
|
||||
(sum, a) => sum + (parseFloat(String(a.units)) || 0),
|
||||
0,
|
||||
);
|
||||
|
||||
ctx.response.body = {
|
||||
allocations,
|
||||
summary: {
|
||||
totalAllocations,
|
||||
totalAmount: totalAmount.toFixed(2),
|
||||
totalUnits: totalUnits.toFixed(2),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Get completed allocations report error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Get summary statistics for completed work
|
||||
router.get("/summary", authenticateToken, authorize("Supervisor", "SuperAdmin"), async (ctx) => {
|
||||
try {
|
||||
const currentUser = getCurrentUser(ctx);
|
||||
const params = ctx.request.url.searchParams;
|
||||
const startDate = params.get("startDate");
|
||||
const endDate = params.get("endDate");
|
||||
|
||||
let departmentFilter = "";
|
||||
const queryParams: unknown[] = [];
|
||||
|
||||
if (currentUser.role === "Supervisor") {
|
||||
departmentFilter = " AND e.department_id = ?";
|
||||
queryParams.push(currentUser.departmentId);
|
||||
}
|
||||
|
||||
let dateFilter = "";
|
||||
if (startDate) {
|
||||
dateFilter += " AND wa.completion_date >= ?";
|
||||
queryParams.push(startDate);
|
||||
}
|
||||
if (endDate) {
|
||||
dateFilter += " AND wa.completion_date <= ?";
|
||||
queryParams.push(endDate);
|
||||
}
|
||||
|
||||
// Get summary by contractor
|
||||
const byContractor = await db.query<any[]>(`
|
||||
router.get(
|
||||
"/summary",
|
||||
authenticateToken,
|
||||
authorize("Supervisor", "SuperAdmin"),
|
||||
async (ctx) => {
|
||||
try {
|
||||
const currentUser: JWTPayload = getCurrentUser(ctx);
|
||||
const params: URLSearchParams = ctx.request.url.searchParams;
|
||||
const startDate: string | null = params.get("startDate");
|
||||
const endDate: string | null = params.get("endDate");
|
||||
|
||||
let departmentFilter = "";
|
||||
const queryParams: unknown[] = [];
|
||||
|
||||
if (currentUser.role === "Supervisor") {
|
||||
departmentFilter = " AND e.department_id = ?";
|
||||
queryParams.push(currentUser.departmentId);
|
||||
}
|
||||
|
||||
let dateFilter = "";
|
||||
if (startDate) {
|
||||
dateFilter += " AND wa.completion_date >= ?";
|
||||
queryParams.push(startDate);
|
||||
}
|
||||
if (endDate) {
|
||||
dateFilter += " AND wa.completion_date <= ?";
|
||||
queryParams.push(endDate);
|
||||
}
|
||||
|
||||
// Get summary by contractor
|
||||
const byContractor = await db.query<any[]>(
|
||||
`
|
||||
SELECT
|
||||
c.id as contractor_id,
|
||||
c.name as contractor_name,
|
||||
@@ -134,10 +157,13 @@ router.get("/summary", authenticateToken, authorize("Supervisor", "SuperAdmin"),
|
||||
WHERE wa.status = 'Completed' ${departmentFilter} ${dateFilter}
|
||||
GROUP BY c.id, c.name
|
||||
ORDER BY total_amount DESC
|
||||
`, queryParams);
|
||||
|
||||
// Get summary by sub-department
|
||||
const bySubDepartment = await db.query<any[]>(`
|
||||
`,
|
||||
queryParams,
|
||||
);
|
||||
|
||||
// Get summary by sub-department
|
||||
const bySubDepartment = await db.query<any[]>(
|
||||
`
|
||||
SELECT
|
||||
sd.id as sub_department_id,
|
||||
sd.name as sub_department_name,
|
||||
@@ -152,10 +178,13 @@ router.get("/summary", authenticateToken, authorize("Supervisor", "SuperAdmin"),
|
||||
WHERE wa.status = 'Completed' ${departmentFilter} ${dateFilter}
|
||||
GROUP BY sd.id, sd.name, d.name
|
||||
ORDER BY total_amount DESC
|
||||
`, queryParams);
|
||||
|
||||
// Get summary by activity type
|
||||
const byActivity = await db.query<any[]>(`
|
||||
`,
|
||||
queryParams,
|
||||
);
|
||||
|
||||
// Get summary by activity type
|
||||
const byActivity = await db.query<any[]>(
|
||||
`
|
||||
SELECT
|
||||
COALESCE(wa.activity, 'Standard') as activity,
|
||||
COUNT(*) as total_allocations,
|
||||
@@ -166,18 +195,21 @@ router.get("/summary", authenticateToken, authorize("Supervisor", "SuperAdmin"),
|
||||
WHERE wa.status = 'Completed' ${departmentFilter} ${dateFilter}
|
||||
GROUP BY wa.activity
|
||||
ORDER BY total_amount DESC
|
||||
`, queryParams);
|
||||
|
||||
ctx.response.body = {
|
||||
byContractor,
|
||||
bySubDepartment,
|
||||
byActivity,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Get report summary error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
});
|
||||
`,
|
||||
queryParams,
|
||||
);
|
||||
|
||||
ctx.response.body = {
|
||||
byContractor,
|
||||
bySubDepartment,
|
||||
byActivity,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Get report summary error:", error);
|
||||
ctx.response.status = 500;
|
||||
ctx.response.body = { error: "Internal server error" };
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user