From 7088673d7caf0727fd4908f2c5009701e3bb1288 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Mon, 13 Jan 2025 16:48:50 +0800 Subject: [PATCH] update report --- .../modules/report/service/ReportService.kt | 121 +++++++++++++++--- .../report/Project Manhour Summary.xlsx | Bin 9498 -> 10453 bytes 2 files changed, 106 insertions(+), 15 deletions(-) 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 ed60147..368c9a8 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 @@ -55,7 +55,8 @@ open class ReportService( private val salaryEffectiveRepository: SalaryEffectiveRepository, private val salaryRepository: SalaryRepository, private val timesheetRepository: TimesheetRepository, - private val teamLogRepository: TeamLogRepository + private val teamLogRepository: TeamLogRepository, + private val teamRepository: TeamRepository ) { private val logger: Log = LogFactory.getLog(javaClass) private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd") @@ -3712,6 +3713,7 @@ open class ReportService( val templateInputStream = resource.inputStream val workbook: Workbook = XSSFWorkbook(templateInputStream) val sheet: Sheet = workbook.getSheetAt(0) + val sheet2: Sheet = workbook.getSheetAt(1) val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") fun getMonthsBetweenToColumn(start: LocalDate, end: LocalDate, startValue: Int): Map { // Get the first day of the start month @@ -3730,6 +3732,7 @@ open class ReportService( } val startDate = LocalDate.parse(args["startDate"].toString()) val endDate = LocalDate.parse(args["endDate"].toString()) + val team = teamRepository.findById(args["teamId"].toString().toLong()).orElseThrow().name val monthList = getMonthsBetweenToColumn(startDate, endDate, 4) if (monthList.isEmpty()) { throw IllegalArgumentException("illegal time period") @@ -3742,37 +3745,69 @@ open class ReportService( "client" to entry.key["client"]) + monthlyConsumption } .sortedBy { it["projectCode"] as String } + + val result2 = manhourSummary.groupBy { mapOf("staff" to it["staff"], "projectCode" to it["projectCode"], "projectName" to it["projectName"], "client" to it["client"]) } + .map { entry -> + val monthlyConsumption = entry.value.associate { it["recordMonth"] to it["consumed"] } + mapOf( + "staff" to entry.key["staff"], + "projectCode" to entry.key["projectCode"], + "projectName" to entry.key["projectName"], + "client" to entry.key["client"]) + monthlyConsumption + } //start from col4 - var rowIndex = 1 - var columnIndex = 1 var tempRow: Row + var tempRow2: Row var tempCell: Cell + var tempCell2: Cell + var columnIndex = 1 + var rowIndex = 1 // generation time tempRow = getOrCreateRow(sheet, rowIndex) + tempRow2 = getOrCreateRow(sheet2, rowIndex) tempCell = getOrCreateCell(tempRow, columnIndex) + tempCell2 = getOrCreateCell(tempRow2, columnIndex) tempCell.setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")).toString()) - //write months header + tempCell2.setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")).toString()) + rowIndex = 2 // Team + tempRow = getOrCreateRow(sheet, rowIndex) + tempRow2 = getOrCreateRow(sheet2, rowIndex) + tempCell = getOrCreateCell(tempRow, columnIndex) + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + tempCell.setCellValue(team) + tempCell2.setCellValue(team) + rowIndex = 3 // report period + tempRow = getOrCreateRow(sheet, rowIndex) + tempRow2 = getOrCreateRow(sheet2, rowIndex) + tempCell = getOrCreateCell(tempRow, columnIndex) + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + tempCell.setCellValue("${args["startDate"].toString()} - ${args["endDate"].toString()}") + tempCell2.setCellValue("${args["startDate"].toString()} - ${args["endDate"].toString()}") + // write months header rowIndex = 5 columnIndex = 3 tempRow = getOrCreateRow(sheet, rowIndex) + tempRow2 = getOrCreateRow(sheet2, rowIndex) for (curr in monthList) { tempCell = getOrCreateCell(tempRow, columnIndex) + tempCell2 = getOrCreateCell(tempRow2, columnIndex + 1) alignTopCenter(tempCell) + alignTopCenter(tempCell2) tempCell.setCellValue(curr.key) + tempCell2.setCellValue(curr.key) CellUtil.setCellStyleProperties(tempCell, cellBorderArgs(0,1,0,0) + fontArgs2(sheet, "Times New Roman",false)) + CellUtil.setCellStyleProperties(tempCell2, + cellBorderArgs(0,1,0,0) + + fontArgs2(sheet2, "Times New Roman",false)) columnIndex++ } - //write content + // write content + // sheet 1 rowIndex = 6 - for ( curr in result) { + columnIndex = 0 + for (curr in result) { tempRow = getOrCreateRow(sheet, rowIndex) -// columnIndex = 0 -// tempCell = getOrCreateCell(tempRow, columnIndex) -// CellUtil.setCellStyleProperties(tempCell, -// cellBorderArgs(1,1,1,1) -// + fontArgs2(sheet, "Times New Roman",false)) -// tempCell.setCellValue(curr["staff"].toString()) columnIndex = 0 tempCell = getOrCreateCell(tempRow, columnIndex) CellUtil.setCellStyleProperties(tempCell, @@ -3793,9 +3828,6 @@ open class ReportService( tempCell.setCellValue(curr["client"].toString()) for ( month in monthList) { var manhour = 0.0 -// println("curr-rM ${curr["recordMonth"]}") -// println("curr ${curr}") -// println("month $month") if (curr.containsKey(month.key.toString())) { manhour = curr[month.key.toString()] as Double } @@ -3824,6 +3856,65 @@ open class ReportService( + fontArgs2(sheet, "Times New Roman",false)) tempCell.cellFormula = "SUM(${columnLetter}7:$columnLetter$rowIndex)" } + // sheet 2 + rowIndex = 6 + columnIndex = 0 + for (curr in result2) { + tempRow2 = getOrCreateRow(sheet2, rowIndex) + columnIndex = 0 + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + CellUtil.setCellStyleProperties(tempCell2, + cellBorderArgs(1,1,1,1) + + fontArgs2(sheet2, "Times New Roman",false)) + tempCell2.setCellValue(curr["staff"].toString()) + columnIndex = 1 + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + CellUtil.setCellStyleProperties(tempCell2, + cellBorderArgs(1,1,1,1) + + fontArgs2(sheet2, "Times New Roman",false)) + tempCell2.setCellValue(curr["projectCode"].toString()) + columnIndex = 2 + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + CellUtil.setCellStyleProperties(tempCell2, + cellBorderArgs(1,1,1,1) + + fontArgs2(sheet2, "Times New Roman",false)) + tempCell2.setCellValue(curr["projectName"].toString()) + columnIndex = 3 + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + CellUtil.setCellStyleProperties(tempCell2, + cellBorderArgs(1,1,1,1) + + fontArgs2(sheet2, "Times New Roman",false)) + tempCell2.setCellValue(curr["client"].toString()) + for ( month in monthList) { + var manhour = 0.0 + if (curr.containsKey(month.key.toString())) { + manhour = curr[month.key.toString()] as Double + } + columnIndex = month.value + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + tempCell2.setCellValue(manhour) + CellUtil.setCellStyleProperties(tempCell2, + cellBorderArgs(1,1,1,1) + + fontArgs2(sheet2, "Times New Roman",false)) + } + rowIndex++ + } + // total + tempRow2 = getOrCreateRow(sheet2, rowIndex) + columnIndex = monthList.values.firstNotNullOfOrNull { it }!! - 1 + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + tempCell2.setCellValue("Total:") + CellUtil.setCellStyleProperties(tempCell2, fontArgs2(sheet2, "Times New Roman",false)) + setAlignment(tempCell2,"top", "right") + for (curr in monthList) { + columnIndex = curr.value + val columnLetter = CellReference.convertNumToColString(columnIndex) + tempCell2 = getOrCreateCell(tempRow2, columnIndex) + CellUtil.setCellStyleProperties(tempCell2, + cellBorderArgs(1,1,1,1) + + fontArgs2(sheet2, "Times New Roman",false)) + tempCell2.cellFormula = "SUM(${columnLetter}7:$columnLetter$rowIndex)" + } return workbook } private fun generateTeamsInOutMap( diff --git a/src/main/resources/templates/report/Project Manhour Summary.xlsx b/src/main/resources/templates/report/Project Manhour Summary.xlsx index 55f5356cc07c7369b12c80e25ee1c2bad06904d7..02dd0fb6d563cdc8c72ce8b597ba7766cf14f6b8 100644 GIT binary patch delta 4361 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 delta 3578 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 O42V3t9V7au_5T2N