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 e0ad756..075384c 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 @@ -4,6 +4,7 @@ import com.ffii.tsms.modules.data.entity.Salary import com.ffii.tsms.modules.data.entity.Staff import com.ffii.tsms.modules.project.entity.Invoice import com.ffii.tsms.modules.project.entity.Project +import com.ffii.tsms.modules.timesheet.entity.Leave import com.ffii.tsms.modules.timesheet.entity.Timesheet import org.apache.poi.ss.usermodel.* import org.apache.poi.ss.util.CellRangeAddress @@ -41,9 +42,9 @@ open class ReportService { } @Throws(IOException::class) - fun generateStaffMonthlyWorkHourAnalysisReport(month: LocalDate, staff: Staff, timesheets: List, projectList: List): ByteArray { + fun generateStaffMonthlyWorkHourAnalysisReport(month: LocalDate, staff: Staff, timesheets: List, leaves: List, projectList: List): ByteArray { // Generate the Excel report with query results - val workbook: Workbook = createStaffMonthlyWorkHourAnalysisReport(month, staff, timesheets, projectList, MONTHLY_WORK_HOURS_ANALYSIS_REPORT) + val workbook: Workbook = createStaffMonthlyWorkHourAnalysisReport(month, staff, timesheets, leaves, projectList, MONTHLY_WORK_HOURS_ANALYSIS_REPORT) // Write the workbook to a ByteArrayOutputStream val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() @@ -251,6 +252,7 @@ open class ReportService { month: LocalDate, staff: Staff, timesheets: List, + leaves: List, projectList: List, templatePath: String, ): Workbook { @@ -261,6 +263,7 @@ open class ReportService { val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") val monthStyle = workbook.createDataFormat().getFormat("mmm, yyyy") + val dateStyle = workbook.createDataFormat().getFormat("dd/mm/yyyy") val daysOfMonth = (1..month.lengthOfMonth()).map { day -> val date = month.withDayOfMonth(day) @@ -281,6 +284,7 @@ open class ReportService { var rowSize = 0 var columnSize = 0 + var dayInt = 0 // tempCell = tempRow.createCell(columnIndex) sheet.getRow(rowIndex).getCell(columnIndex).apply { setCellValue(FORMATTED_TODAY) @@ -310,13 +314,12 @@ open class ReportService { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// rowIndex = 7 - println(daysOfMonth) daysOfMonth.forEach { dayInfo -> rowIndex++ rowSize++ - println(dayInfo) sheet.getRow(rowIndex).getCell(0).apply { setCellValue(dayInfo.date) + cellStyle.dataFormat = dateStyle cellStyle.setFont(boldFont) } sheet.getRow(rowIndex).getCell(1).apply { @@ -329,6 +332,8 @@ open class ReportService { sheet.getRow(rowIndex).getCell(0).apply { setCellValue("Sub-total") cellStyle.setFont(boldFont) +// cellStyle.borderTop = BorderStyle.THIN +// cellStyle.borderBottom = BorderStyle.DOUBLE cellStyle.alignment = HorizontalAlignment.CENTER } sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) @@ -339,22 +344,63 @@ open class ReportService { // cellStyle.setFont(boldFont) cellStyle.alignment = HorizontalAlignment.CENTER } + var normalConsumed = 0.0 + var otConsumed = 0.0 + var leaveHours = 0.0 + if (timesheets.isNotEmpty()) { + timesheets.forEach { t -> + normalConsumed += t.normalConsumed!! + otConsumed += t.otConsumed!! + } + } + sheet.getRow(rowIndex).getCell(2).apply { + setCellValue(normalConsumed) + cellStyle.alignment = HorizontalAlignment.CENTER + cellStyle.dataFormat = accountingStyle + } sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) // rowIndex += 1 sheet.getRow(rowIndex).getCell(0).apply { setCellValue("Total Other Hours [B]") - sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) cellStyle.setFont(boldFont) cellStyle.alignment = HorizontalAlignment.CENTER } + sheet.getRow(rowIndex).getCell(2).apply { + setCellValue(otConsumed) + cellStyle.alignment = HorizontalAlignment.CENTER + cellStyle.dataFormat = accountingStyle + } + + sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) + rowIndex += 1 + sheet.getRow(rowIndex).getCell(0).apply { + setCellValue("Total Leave Hours") + cellStyle.alignment = HorizontalAlignment.CENTER + } + if (leaves.isNotEmpty()) { + leaves.forEach { l -> + leaveHours += l.leaveHours!! + } + } + sheet.getRow(rowIndex).getCell(2).apply { + setCellValue(leaveHours) + cellStyle.dataFormat = accountingStyle + } rowIndex += 1 sheet.getRow(rowIndex).getCell(0).apply { setCellValue("Total Spent Manhours [A+B]") -// cellStyle.setFont(boldFont) + cellStyle.setFont(boldFont) cellStyle.alignment = HorizontalAlignment.CENTER - cellStyle.borderBottom = BorderStyle.DOUBLE +// cellStyle.borderTop = BorderStyle.THIN +// cellStyle.borderBottom = BorderStyle.DOUBLE + } + sheet.getRow(rowIndex).getCell(2).apply { + cellFormula = "C${rowIndex-2}+C${rowIndex-1}" + cellStyle.dataFormat = accountingStyle +// cellStyle.borderTop = BorderStyle.THIN +// cellStyle.borderBottom = BorderStyle.DOUBLE } sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -364,6 +410,26 @@ open class ReportService { sheet.getRow(7).getCell(columnIndex + index).apply { setCellValue(title) } + } + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if (timesheets.isNotEmpty()) { + projectList.forEach { _ -> + timesheets.forEach { timesheet -> + dayInt = timesheet.recordDate!!.dayOfMonth + sheet.getRow(dayInt.plus(7)).getCell(columnIndex).apply { + setCellValue(timesheet.normalConsumed!!) + } + } + columnIndex++ + } + } + if (leaves.isNotEmpty()) { + leaves.forEach { leave -> + dayInt = leave.recordDate!!.dayOfMonth + sheet.getRow(dayInt.plus(7)).getCell(columnIndex).apply { + setCellValue(leave.leaveHours!!) + } + } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sheet.getRow(rowIndex).apply { @@ -377,47 +443,49 @@ open class ReportService { } sheet.addMergedRegion(CellRangeAddress(6,6 , 2, columnIndex)) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - for (i in 2 ..columnIndex) { + for (i in 2 until columnIndex) { for (k in 0 until rowSize) { sheet.getRow(8+k).getCell(i).apply { - setCellValue(" - ") + setCellValue(0.0) cellStyle.dataFormat = accountingStyle } } } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - if (timesheets.isNotEmpty()) { - projectList.forEachIndexed { i, _ -> - timesheets.forEach { timesheet -> - val dayInt = timesheet.recordDate!!.dayOfMonth - sheet.getRow(dayInt.plus(7)).getCell(columnIndex + i).apply { - setCellValue(timesheet.normalConsumed!!) - } - } - } - } - rowIndex = 8 -// println("rowSize is: $rowSize") - if (sheet.getRow(rowIndex - 1).getCell(2).stringCellValue == "Leave Hours") { + if (sheet.getRow(rowIndex - 1).getCell(2).stringCellValue != "Leave Hours") { + // cal daily spent manhour for (i in 0 until rowSize) { val cell = sheet.getRow(rowIndex)?.getCell(columnIndex) ?: sheet.getRow(rowIndex + i)?.createCell(columnIndex) cell?.cellFormula = "SUM(${getColumnAlphabet(2)}${rowIndex+1}:${getColumnAlphabet(columnIndex)}${rowIndex+1})" // should columnIndex - 2 rowIndex++ -// cell?.setCellValue("testing") -// rowIndex++ } -// println(columnSize) + // cal subtotal for (i in 0 until columnSize) { // minus last col val cell = sheet.getRow(rowIndex)?.getCell(2) ?: sheet.getRow(rowIndex)?.createCell(2) cell?.cellFormula = "SUM(${getColumnAlphabet(2)}${rowIndex}:${getColumnAlphabet(columnIndex + i)}${rowIndex})" cell?.cellStyle?.dataFormat = accountingStyle cell?.cellStyle?.setFont(boldFont) -// cell?.cellStyle.borderBottom = border +// cell?.cellStyle?.borderTop = BorderStyle.THIN +// cell?.cellStyle?.borderBottom = BorderStyle.DOUBLE + } + } else { // just for preview when no data + // cal daily spent manhour + for (i in 0 until rowSize) { + val cell = sheet.getRow(rowIndex)?.getCell(columnIndex) ?: sheet.getRow(rowIndex + i)?.createCell(columnIndex) + cell?.setCellValue("daily spent manhour") + rowIndex++ + } + // cal subtotal + for (i in 0 until columnSize) { // minus last col + val cell = sheet.getRow(rowIndex)?.getCell(2) ?: sheet.getRow(rowIndex)?.createCell(2) + cell?.setCellValue("testing subtotal") + cell?.cellStyle?.dataFormat = accountingStyle + cell?.cellStyle?.setFont(boldFont) +// cell?.cellStyle?.borderTop = BorderStyle.THIN +// cell?.cellStyle?.borderBottom = BorderStyle.DOUBLE } } - return workbook } 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 0edaee4..3fa4166 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 @@ -7,6 +7,7 @@ import com.ffii.tsms.modules.report.service.ReportService import com.ffii.tsms.modules.project.service.InvoiceService import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest import com.ffii.tsms.modules.report.web.model.StaffMonthlyWorkHourAnalysisReportRequest +import com.ffii.tsms.modules.timesheet.entity.LeaveRepository import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository import jakarta.validation.Valid import org.springframework.core.io.ByteArrayResource @@ -33,6 +34,7 @@ class ReportController( private val timesheetRepository: TimesheetRepository, private val projectTaskRepository: ProjectTaskRepository, private val staffRepository: StaffRepository, + private val leaveRepository: LeaveRepository, private val invoiceService: InvoiceService) { @PostMapping("/ProjectCashFlowReport") @@ -60,11 +62,12 @@ class ReportController( val staff = staffRepository.findById(request.id).orElseThrow() val timesheets = timesheetRepository.findByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth) + val leaves = leaveRepository.findByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth) val projects = timesheetRepository.findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth) val projectList: List = projects.map { p -> "${p.projectTask!!.project!!.code}\n ${p.projectTask!!.project!!.name}" } - val reportResult: ByteArray = excelReportService.generateStaffMonthlyWorkHourAnalysisReport(thisMonth, staff, timesheets, projectList) + val reportResult: ByteArray = excelReportService.generateStaffMonthlyWorkHourAnalysisReport(thisMonth, staff, timesheets, leaves, projectList) // 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/timesheet/entity/LeaveRepository.kt b/src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveRepository.kt index 7c5a2de..1911771 100644 --- a/src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveRepository.kt +++ b/src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveRepository.kt @@ -8,4 +8,6 @@ interface LeaveRepository : AbstractRepository { fun findAllByStaff(staff: Staff): List fun deleteAllByStaffAndRecordDate(staff: Staff, recordDate: LocalDate) + + fun findByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List } \ No newline at end of file