| @@ -2026,53 +2026,51 @@ open class ReportService( | |||
| } | |||
| open fun getProjectResourceOverconsumptionReport(args: Map<String, Any>): List<Map<String, Any>> { | |||
| val sql = StringBuilder( | |||
| "with teamNormalConsumed as (" | |||
| + " SELECT" | |||
| + " t.projectId," | |||
| + " p.teamLead," | |||
| + " sum(t.normalConsumed) as normalConsumed," | |||
| + " sum(t.otConsumed) as otConsumed," | |||
| + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed," | |||
| + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * sal.hourlyRate as totalBudget" | |||
| + " from timesheet t" | |||
| + " left join project p on p.id = t.projectId" | |||
| + " left join staff s on s.id = p.teamLead" | |||
| + " left join salary sal on sal.salaryPoint = s.salaryId" | |||
| + " group by p.teamLead, t.projectId, sal.hourlyRate" | |||
| + " )" | |||
| + " SELECT" | |||
| + " p.id," | |||
| + " p.code," | |||
| + " p.name," | |||
| + " t.code," | |||
| + " concat(c.code, ' - ',c.name) as client," | |||
| + " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary," | |||
| + " p.expectedTotalFee * 0.8 as plannedBudget," | |||
| + " COALESCE(tns.totalBudget, 0) as actualConsumedBudget," | |||
| + " tns.totalConsumed," | |||
| + " COALESCE(p.totalManhour, 0) as plannedManhour," | |||
| + " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour," | |||
| + " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate," | |||
| + " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate," | |||
| + " CASE" | |||
| + " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1" | |||
| + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1" | |||
| + " then 'Potential Overconsumption'" | |||
| + " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1" | |||
| + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1" | |||
| + " then 'Overconsumption'" | |||
| + " else 'Within Budget'" | |||
| + " END as status" | |||
| + " from project p" | |||
| + " left join staff s on s.id = p.teamLead" | |||
| + " left join team t on t.id = s.teamId" | |||
| + " left join customer c on c.id = p.customerId" | |||
| + " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id" | |||
| + " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint" | |||
| + " left join teamNormalConsumed tns on tns.projectId = p.id" | |||
| + " WHERE p.deleted = false " | |||
| + " and p.status = 'On-going' " | |||
| val sql = StringBuilder("with teamNormalConsumed as (" | |||
| + " SELECT" | |||
| + " t.projectId," | |||
| + " p.teamLead," | |||
| + " sum(t.normalConsumed) as normalConsumed," | |||
| + " sum(t.otConsumed) as otConsumed," | |||
| + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed," | |||
| + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * sal.hourlyRate as totalBudget" | |||
| + " from timesheet t" | |||
| + " left join project p on p.id = t.projectId" | |||
| + " left join staff s on s.id = p.teamLead" | |||
| + " left join salary sal on sal.salaryPoint = s.salaryId" | |||
| + " group by p.teamLead, t.projectId, sal.hourlyRate" | |||
| + " )" | |||
| + " SELECT" | |||
| + " p.id," | |||
| + " p.code," | |||
| + " p.name," | |||
| + " t.code," | |||
| + " concat(c.code, ' - ',c.name) as client," | |||
| + " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary," | |||
| + " p.expectedTotalFee * 0.8 as plannedBudget," | |||
| + " COALESCE(tns.totalBudget, 0) as actualConsumedBudget," | |||
| + " COALESCE(p.totalManhour, 0) as plannedManhour," | |||
| + " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour," | |||
| + " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate," | |||
| + " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate," | |||
| + " CASE" | |||
| + " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1" | |||
| + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1" | |||
| + " then 'Potential Overconsumption'" | |||
| + " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1" | |||
| + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1" | |||
| + " then 'Overconsumption'" | |||
| + " else 'Within Budget'" | |||
| + " END as status" | |||
| + " from project p" | |||
| + " left join staff s on s.id = p.teamLead" | |||
| + " left join team t on t.id = s.teamId" | |||
| + " left join customer c on c.id = p.customerId" | |||
| + " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id" | |||
| + " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint" | |||
| + " left join teamNormalConsumed tns on tns.projectId = p.id" | |||
| + " WHERE p.deleted = false " | |||
| + " and p.status = 'On-going' " | |||
| ) | |||
| if (args != null) { | |||
| var statusFilter: String = "" | |||