|
@@ -1839,55 +1839,52 @@ 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" |
|
|
|
|
|
+ " 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) { |
|
|
if (args != null) { |
|
|
var statusFilter: String = "" |
|
|
var statusFilter: String = "" |
|
@@ -1904,7 +1901,6 @@ open class ReportService( |
|
|
" and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 " + |
|
|
" and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 " + |
|
|
" or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " + |
|
|
" or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " + |
|
|
" and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1 " |
|
|
" and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1 " |
|
|
|
|
|
|
|
|
"All" -> " and " + |
|
|
"All" -> " and " + |
|
|
" (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit " + |
|
|
" (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit " + |
|
|
" or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " |
|
|
" or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " |
|
@@ -2450,7 +2446,7 @@ open class ReportService( |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
sql.append( |
|
|
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( |
|
|
val args = mapOf( |
|
|