diff --git a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt index 558819f..2bc6f45 100644 --- a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt +++ b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt @@ -1839,55 +1839,52 @@ open class ReportService( } open fun getProjectResourceOverconsumptionReport(args: Map): List> { - val sql = StringBuilder( - "WITH teamNormalConsumed AS (" - + " SELECT" - + " tns.project_id," - + " SUM(tns.totalConsumed) AS totalConsumed, " - + " sum(tns.totalBudget) as totalBudget " - + " FROM ( " - + " SELECT" - + " t.staffId," - + " t.projectId AS project_id," - + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, " - + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * min(sal.hourlyRate) as totalBudget " - + " FROM timesheet t" - + " LEFT JOIN staff s ON t.staffId = s.id " - + " left join salary sal on sal.salaryPoint = s.salaryId " - + " GROUP BY t.staffId, t.projectId" - + " ) AS tns" - + " GROUP BY project_id" - + " ) " - + " SELECT " - + " p.code, " - + " p.name, " - + " t.code as team, " - + " 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) >= 1 " - + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1 " - + " then 'Overconsumption' " - + " 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' " - + " else 'Within Budget' " - + " END as status " - + " FROM project p " - + " LEFT JOIN team t ON p.teamLead = t.teamLead " - + " LEFT JOIN staff s ON p.teamLead = s.id " - + " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint " - + " LEFT JOIN customer c ON p.customerId = c.id " - + " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id " - + " left join teamNormalConsumed tns on tns.project_id = 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," + + " 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' " ) if (args != null) { var statusFilter: String = "" @@ -1904,7 +1901,6 @@ open class ReportService( " 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 " - "All" -> " and " + " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit " + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " @@ -2450,7 +2446,7 @@ open class ReportService( } sql.append( - " group by p.code, p.description , c.name, teamLead, p.expectedTotalFee , hourlyRate, s2.name " + " order by p.code" + " group by p.code, p.description , c.name, teamLead, p.expectedTotalFee , hourlyRate, s2.name order by p.code" ) val args = mapOf(