| @@ -2027,55 +2027,43 @@ open class ReportService( | |||||
| } | } | ||||
| open fun getProjectResourceOverconsumptionReport(args: Map<String, Any>): List<Map<String, Any>> { | 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," | |||||
| + " 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("SELECT" | |||||
| + " p.code, " | |||||
| + " p.name, " | |||||
| + " tm.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, " | |||||
| + " sum(t.consumedBudget) as actualConsumedBudget, " | |||||
| + " COALESCE(p.totalManhour, 0) as plannedManhour, " | |||||
| + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as actualConsumedManhour, " | |||||
| + " sum(t.consumedBudget) / p.expectedTotalFee as budgetConsumptionRate, " | |||||
| + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0) as manhourConsumptionRate, " | |||||
| + " case " | |||||
| + " when (sum(t.consumedBudget) / p.expectedTotalFee) >= :lowerLimit and (sum(t.consumedBudget) / p.expectedTotalFee) <= 1 " | |||||
| + " or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) <= 1 " | |||||
| + " then 'Potential Overconsumption' " | |||||
| + " when (sum(t.consumedBudget) / p.expectedTotalFee) >= 1 " | |||||
| + " or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= 1 " | |||||
| + " then 'Overconsumption' " | |||||
| + " else 'Within Budget' " | |||||
| + " END as status " | |||||
| + " from " | |||||
| + " (SELECT " | |||||
| + " t.*, " | |||||
| + " (t.normalConsumed + COALESCE(t.otConsumed, 0)) * sal.hourlyRate as consumedBudget " | |||||
| + " from timesheet t " | |||||
| + " left join staff s on s.id = t.staffId " | |||||
| + " left join salary sal on sal.salaryPoint = s.salaryId ) t " | |||||
| + " left join project p on p.id = t.projectId " | |||||
| + " left join team tm on p.teamLead = tm.teamLead " | |||||
| + " left join customer c on c.id = p.customerId " | |||||
| + " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id " | |||||
| + " WHERE p.deleted = false " | |||||
| + " and p.status = 'On-going' " | |||||
| ) | ) | ||||
| if (args != null) { | |||||
| var statusFilter: String = "" | var statusFilter: String = "" | ||||
| if (args != null) { | |||||
| if (args.containsKey("teamId")) | if (args.containsKey("teamId")) | ||||
| sql.append(" and t.id = :teamId") | sql.append(" and t.id = :teamId") | ||||
| if (args.containsKey("custId")) | if (args.containsKey("custId")) | ||||
| @@ -2084,23 +2072,19 @@ open class ReportService( | |||||
| sql.append(" and ss.id = :subsidiaryId") | sql.append(" and ss.id = :subsidiaryId") | ||||
| if (args.containsKey("status")) | if (args.containsKey("status")) | ||||
| statusFilter = when (args.get("status")) { | statusFilter = when (args.get("status")) { | ||||
| "Potential Overconsumption" -> " and " + | |||||
| " (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 " | |||||
| "All" -> " and " + | |||||
| " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit " + | |||||
| " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " | |||||
| // "Overconsumption" -> " and " + | |||||
| // " ((COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 " + | |||||
| // " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1) " | |||||
| "Potential Overconsumption" -> " having " + | |||||
| " (sum(t.consumedBudget) / p.expectedTotalFee) >= :lowerLimit " + | |||||
| " and (sum(t.consumedBudget) / p.expectedTotalFee) <= 1 " + | |||||
| " or (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " + | |||||
| " and (sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) / COALESCE(p.totalManhour, 0)) <= 1 " | |||||
| "All" -> " having " + | |||||
| " (sum(t.consumedBudget) / p.expectedTotalFee) >= :lowerLimit " + | |||||
| " or (sum(t.consumedBudget) / p.expectedTotalFee) >= :lowerLimit " | |||||
| else -> "" | else -> "" | ||||
| } | } | ||||
| } | |||||
| sql.append(" group by p.code, p.name, tm.code, c.code, c.name, ss.code, ss.name,p.expectedTotalFee, p.totalManhour, p.expectedTotalFee ") | |||||
| sql.append(statusFilter) | sql.append(statusFilter) | ||||
| } | |||||
| return jdbcDao.queryForList(sql.toString(), args) | return jdbcDao.queryForList(sql.toString(), args) | ||||
| } | } | ||||