|
|
@@ -1,10 +1,10 @@ |
|
|
|
package com.ffii.tsms.modules.report.service |
|
|
|
|
|
|
|
import com.ffii.core.support.JdbcDao |
|
|
|
import com.ffii.tsms.modules.data.entity.Customer |
|
|
|
import com.ffii.tsms.modules.data.entity.Salary |
|
|
|
import com.ffii.tsms.modules.data.entity.Staff |
|
|
|
import com.ffii.tsms.modules.data.entity.Team |
|
|
|
import com.ffii.tsms.modules.data.entity.Customer |
|
|
|
import com.ffii.tsms.modules.project.entity.Invoice |
|
|
|
import com.ffii.tsms.modules.project.entity.Project |
|
|
|
import com.ffii.tsms.modules.timesheet.entity.Timesheet |
|
|
@@ -151,13 +151,15 @@ open class ReportService( |
|
|
|
fun generateProjectResourceOverconsumptionReport( |
|
|
|
team: String, |
|
|
|
customer: String, |
|
|
|
result: List<Map<String, Any>> |
|
|
|
result: List<Map<String, Any>>, |
|
|
|
lowerLimit: Double |
|
|
|
): ByteArray { |
|
|
|
// Generate the Excel report with query results |
|
|
|
val workbook: Workbook = createProjectResourceOverconsumptionReport( |
|
|
|
team, |
|
|
|
customer, |
|
|
|
result, |
|
|
|
lowerLimit, |
|
|
|
RESOURCE_OVERCONSUMPTION_REPORT |
|
|
|
) |
|
|
|
// Write the workbook to a ByteArrayOutputStream |
|
|
@@ -1059,6 +1061,7 @@ open class ReportService( |
|
|
|
team: String, |
|
|
|
customer: String, |
|
|
|
result: List<Map<String, Any>>, |
|
|
|
lowerLimit: Double, |
|
|
|
templatePath: String |
|
|
|
):Workbook { |
|
|
|
val resource = ClassPathResource(templatePath) |
|
|
@@ -1089,11 +1092,11 @@ open class ReportService( |
|
|
|
rowIndex = 6 |
|
|
|
columnIndex = 0 |
|
|
|
result.forEachIndexed { index, obj -> |
|
|
|
tempCell = sheet.getRow(rowIndex).getCell(columnIndex) |
|
|
|
tempCell = sheet.getRow(rowIndex).createCell(columnIndex) |
|
|
|
tempCell.setCellValue((index + 1).toDouble()) |
|
|
|
val keys = obj.keys.toList() |
|
|
|
keys.forEachIndexed { keyIndex, key -> |
|
|
|
tempCell = sheet.getRow(rowIndex).getCell(columnIndex + keyIndex + 1) |
|
|
|
tempCell = sheet.getRow(rowIndex).getCell(columnIndex + keyIndex + 1) ?: sheet.getRow(rowIndex).createCell(columnIndex + keyIndex + 1) |
|
|
|
when (obj[key]) { |
|
|
|
is Double -> tempCell.setCellValue(obj[key] as Double) |
|
|
|
else -> tempCell.setCellValue(obj[key] as String ) |
|
|
@@ -1101,8 +1104,21 @@ open class ReportService( |
|
|
|
} |
|
|
|
rowIndex++ |
|
|
|
} |
|
|
|
// tempCell = sheet.getRow(rowIndex).getCell(columnIndex) |
|
|
|
// tempCell.setCellValue() |
|
|
|
|
|
|
|
val sheetCF = sheet.sheetConditionalFormatting |
|
|
|
val rule1 = sheetCF.createConditionalFormattingRule("AND(J7 >= $lowerLimit, J7 <= 1)") |
|
|
|
val rule2 = sheetCF.createConditionalFormattingRule("J7 > 1") |
|
|
|
var fillOrange = rule1.createPatternFormatting() |
|
|
|
fillOrange.setFillBackgroundColor(IndexedColors.LIGHT_ORANGE.index); |
|
|
|
fillOrange.setFillPattern(PatternFormatting.SOLID_FOREGROUND) |
|
|
|
|
|
|
|
var fillRed = rule2.createPatternFormatting() |
|
|
|
fillRed.setFillBackgroundColor(IndexedColors.ROSE.index); |
|
|
|
fillRed.setFillPattern(PatternFormatting.SOLID_FOREGROUND) |
|
|
|
|
|
|
|
val cfRules = arrayOf(rule1, rule2) |
|
|
|
val regions = arrayOf(CellRangeAddress.valueOf("\$J7:\$K${rowIndex+1}")) |
|
|
|
sheetCF.addConditionalFormatting(regions, cfRules); |
|
|
|
|
|
|
|
return workbook |
|
|
|
} |
|
|
@@ -1256,26 +1272,25 @@ open class ReportService( |
|
|
|
open fun getProjectResourceOverconsumptionReport(args: Map<String, Any>): List<Map<String, Any>> { |
|
|
|
val sql = StringBuilder("WITH teamNormalConsumed AS (" |
|
|
|
+ " SELECT " |
|
|
|
+ " s.teamId, " |
|
|
|
+ " s.teamId, " |
|
|
|
+ " pt.project_id, " |
|
|
|
+ " SUM(tns.totalConsumed) AS totalConsumed " |
|
|
|
+ " FROM ( " |
|
|
|
+ " SELECT " |
|
|
|
+ " t.staffId, " |
|
|
|
+ " FROM ( " |
|
|
|
+ " SELECT " |
|
|
|
+ " t.staffId, " |
|
|
|
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, " |
|
|
|
+ " t.projectTaskId AS taskId " |
|
|
|
+ " FROM timesheet t " |
|
|
|
+ " LEFT JOIN staff s ON t.staffId = s.id " |
|
|
|
+ " GROUP BY t.staffId, t.projectTaskId " |
|
|
|
+ " FROM timesheet t " |
|
|
|
+ " LEFT JOIN staff s ON t.staffId = s.id " |
|
|
|
+ " GROUP BY t.staffId, t.projectTaskId " |
|
|
|
+ " ) AS tns " |
|
|
|
+ " INNER JOIN project_task pt ON tns.taskId = pt.id " |
|
|
|
+ " JOIN staff s ON tns.staffId = s.id " |
|
|
|
+ " JOIN team t ON s.teamId = t.id " |
|
|
|
+ " GROUP BY teamId, project_id " |
|
|
|
+ " INNER JOIN project_task pt ON tns.taskId = pt.id " |
|
|
|
+ " JOIN staff s ON tns.staffId = s.id " |
|
|
|
+ " JOIN team t ON s.teamId = t.id " |
|
|
|
+ " GROUP BY teamId, project_id " |
|
|
|
+ " ) " |
|
|
|
+ " SELECT " |
|
|
|
+ " p.code, " |
|
|
|
+ " -- p.status, " |
|
|
|
+ " p.name, " |
|
|
|
+ " t.code as team, " |
|
|
|
+ " c.code as client, " |
|
|
@@ -1286,8 +1301,8 @@ open class ReportService( |
|
|
|
+ " (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) >= 0.9 and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 " |
|
|
|
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 0.9 and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1 " |
|
|
|
+ " 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 " |
|
|
@@ -1311,16 +1326,18 @@ open class ReportService( |
|
|
|
sql.append("and c.id = :custId") |
|
|
|
if (args.containsKey("status")) |
|
|
|
statusFilter = when (args.get("status")) { |
|
|
|
"Potential Overconsumption" -> " ( and " + |
|
|
|
"((COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 0.9 " + |
|
|
|
"and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 " + |
|
|
|
"or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 0.9 " + |
|
|
|
"and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1)" + |
|
|
|
" ) " |
|
|
|
"Overconsumption" -> " ( and " + |
|
|
|
"((COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 " + |
|
|
|
"or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1)" + |
|
|
|
" ) " |
|
|
|
"Potential Overconsumption" -> "and " + |
|
|
|
" ((COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 0.9 " + |
|
|
|
" and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 " + |
|
|
|
" or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 0.9 " + |
|
|
|
" and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1)" |
|
|
|
"Overconsumption" -> "and " + |
|
|
|
" ((COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 " + |
|
|
|
" or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1) " |
|
|
|
"Within Budget" -> "and " + |
|
|
|
" ((COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) < 0.9 " + |
|
|
|
" and " + |
|
|
|
" (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 0.9 " |
|
|
|
else -> "" |
|
|
|
} |
|
|
|
sql.append(statusFilter) |
|
|
|