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 f66b0b7..68bb5bc 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 @@ -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> + result: List>, + 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>, + 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): List> { 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) diff --git a/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt b/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt index b4a4bee..0037ec6 100644 --- a/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt +++ b/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt @@ -115,20 +115,23 @@ class ReportController( @PostMapping("/ProjectResourceOverconsumptionReport") @Throws(ServletRequestBindingException::class, IOException::class) fun ProjectResourceOverconsumptionReport(@RequestBody @Valid request: ProjectResourceOverconsumptionReport): ResponseEntity { - -// val staff = staffRepository.findById(request.id).orElseThrow() - val args: Map = mutableMapOf( - "teamId" to request.teamId, - "custId" to request.custId, - "status" to request.status + val lowerLimit = request.lowerLimit + var team: String = "All" + var customer: String = "All" + val args: MutableMap = mutableMapOf( + "status" to request.status, + "lowerLimit" to lowerLimit ) - val team: String = teamService.find(request.teamId).orElseThrow().name - val customer: String = customerService.find(request.custId).orElseThrow().name + if (request.teamId != null) { + args["teamId"] = request.teamId + team = teamService.find(request.teamId).orElseThrow().name + } + if (request.custId != null) { + args["custId"] = request.custId + customer = customerService.find(request.custId).orElseThrow().name + } val result: List> = excelReportService.getProjectResourceOverconsumptionReport(args); -// val obj: ProjectResourceReport = mapper.readValue(mapper.writeValueAsBytes(result)) - - - val reportResult: ByteArray = excelReportService.generateProjectResourceOverconsumptionReport(team, customer, result) + val reportResult: ByteArray = excelReportService.generateProjectResourceOverconsumptionReport(team, customer, result, lowerLimit) // val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") return ResponseEntity.ok() // .contentType(mediaType) diff --git a/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt b/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt index 54954c7..d08d3f1 100644 --- a/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt +++ b/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt @@ -25,7 +25,8 @@ data class LateStartReportRequest ( val reportDate: LocalDate ) data class ProjectResourceOverconsumptionReport ( - val teamId: Long, - val custId: Long, + val teamId: Long?, + val custId: Long?, val status: String, + val lowerLimit: Double ) \ No newline at end of file diff --git a/src/main/resources/templates/report/AR03_Resource Overconsumption.xlsx b/src/main/resources/templates/report/AR03_Resource Overconsumption.xlsx index 8ef3799..9d49215 100644 Binary files a/src/main/resources/templates/report/AR03_Resource Overconsumption.xlsx and b/src/main/resources/templates/report/AR03_Resource Overconsumption.xlsx differ