Sfoglia il codice sorgente

update Overconsumption report

tags/Baseline_30082024_BACKEND_UAT
MSI\derek 1 anno fa
parent
commit
4e2a6df392
4 ha cambiato i file con 65 aggiunte e 44 eliminazioni
  1. +47
    -30
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  2. +15
    -12
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  3. +3
    -2
      src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt
  4. BIN
      src/main/resources/templates/report/AR03_Resource Overconsumption.xlsx

+ 47
- 30
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Vedi File

@@ -1,10 +1,10 @@
package com.ffii.tsms.modules.report.service package com.ffii.tsms.modules.report.service


import com.ffii.core.support.JdbcDao 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.Salary
import com.ffii.tsms.modules.data.entity.Staff import com.ffii.tsms.modules.data.entity.Staff
import com.ffii.tsms.modules.data.entity.Team 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.Invoice
import com.ffii.tsms.modules.project.entity.Project import com.ffii.tsms.modules.project.entity.Project
import com.ffii.tsms.modules.timesheet.entity.Timesheet import com.ffii.tsms.modules.timesheet.entity.Timesheet
@@ -151,13 +151,15 @@ open class ReportService(
fun generateProjectResourceOverconsumptionReport( fun generateProjectResourceOverconsumptionReport(
team: String, team: String,
customer: String, customer: String,
result: List<Map<String, Any>>
result: List<Map<String, Any>>,
lowerLimit: Double
): ByteArray { ): ByteArray {
// Generate the Excel report with query results // Generate the Excel report with query results
val workbook: Workbook = createProjectResourceOverconsumptionReport( val workbook: Workbook = createProjectResourceOverconsumptionReport(
team, team,
customer, customer,
result, result,
lowerLimit,
RESOURCE_OVERCONSUMPTION_REPORT RESOURCE_OVERCONSUMPTION_REPORT
) )
// Write the workbook to a ByteArrayOutputStream // Write the workbook to a ByteArrayOutputStream
@@ -1059,6 +1061,7 @@ open class ReportService(
team: String, team: String,
customer: String, customer: String,
result: List<Map<String, Any>>, result: List<Map<String, Any>>,
lowerLimit: Double,
templatePath: String templatePath: String
):Workbook { ):Workbook {
val resource = ClassPathResource(templatePath) val resource = ClassPathResource(templatePath)
@@ -1089,11 +1092,11 @@ open class ReportService(
rowIndex = 6 rowIndex = 6
columnIndex = 0 columnIndex = 0
result.forEachIndexed { index, obj -> result.forEachIndexed { index, obj ->
tempCell = sheet.getRow(rowIndex).getCell(columnIndex)
tempCell = sheet.getRow(rowIndex).createCell(columnIndex)
tempCell.setCellValue((index + 1).toDouble()) tempCell.setCellValue((index + 1).toDouble())
val keys = obj.keys.toList() val keys = obj.keys.toList()
keys.forEachIndexed { keyIndex, key -> 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]) { when (obj[key]) {
is Double -> tempCell.setCellValue(obj[key] as Double) is Double -> tempCell.setCellValue(obj[key] as Double)
else -> tempCell.setCellValue(obj[key] as String ) else -> tempCell.setCellValue(obj[key] as String )
@@ -1101,8 +1104,21 @@ open class ReportService(
} }
rowIndex++ 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 return workbook
} }
@@ -1256,26 +1272,25 @@ 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 (" val sql = StringBuilder("WITH teamNormalConsumed AS ("
+ " SELECT " + " SELECT "
+ " s.teamId, "
+ " s.teamId, "
+ " pt.project_id, " + " pt.project_id, "
+ " SUM(tns.totalConsumed) AS totalConsumed " + " SUM(tns.totalConsumed) AS totalConsumed "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, " + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId " + " 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 " + " ) 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 " + " SELECT "
+ " p.code, " + " p.code, "
+ " -- p.status, "
+ " p.name, " + " p.name, "
+ " t.code as team, " + " t.code as team, "
+ " c.code as client, " + " c.code as client, "
@@ -1286,8 +1301,8 @@ open class ReportService(
+ " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate, " + " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate, "
+ " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate, " + " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate, "
+ " CASE " + " 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' " + " then 'Potential Overconsumption' "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 " + " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1 " + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1 "
@@ -1311,16 +1326,18 @@ open class ReportService(
sql.append("and c.id = :custId") sql.append("and c.id = :custId")
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) >= 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 -> "" else -> ""
} }
sql.append(statusFilter) sql.append(statusFilter)


+ 15
- 12
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt Vedi File

@@ -115,20 +115,23 @@ class ReportController(
@PostMapping("/ProjectResourceOverconsumptionReport") @PostMapping("/ProjectResourceOverconsumptionReport")
@Throws(ServletRequestBindingException::class, IOException::class) @Throws(ServletRequestBindingException::class, IOException::class)
fun ProjectResourceOverconsumptionReport(@RequestBody @Valid request: ProjectResourceOverconsumptionReport): ResponseEntity<Resource> { fun ProjectResourceOverconsumptionReport(@RequestBody @Valid request: ProjectResourceOverconsumptionReport): ResponseEntity<Resource> {
// val staff = staffRepository.findById(request.id).orElseThrow()
val args: Map<String, Any> = 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<String, Any> = 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<Map<String, Any>> = excelReportService.getProjectResourceOverconsumptionReport(args); val result: List<Map<String, Any>> = 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") // val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
return ResponseEntity.ok() return ResponseEntity.ok()
// .contentType(mediaType) // .contentType(mediaType)


+ 3
- 2
src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt Vedi File

@@ -25,7 +25,8 @@ data class LateStartReportRequest (
val reportDate: LocalDate val reportDate: LocalDate
) )
data class ProjectResourceOverconsumptionReport ( data class ProjectResourceOverconsumptionReport (
val teamId: Long,
val custId: Long,
val teamId: Long?,
val custId: Long?,
val status: String, val status: String,
val lowerLimit: Double
) )

BIN
src/main/resources/templates/report/AR03_Resource Overconsumption.xlsx Vedi File


Caricamento…
Annulla
Salva