| @@ -1404,50 +1404,54 @@ open class ReportService( | |||||
| return workbook | return workbook | ||||
| } | } | ||||
| //createLateStartReport | |||||
| private fun createLateStartReport( | |||||
| team: List<Team>, | |||||
| customer: List<Customer>, | |||||
| templatePath: String, | |||||
| teamId: Long, | |||||
| clientId: Long, | |||||
| lateStartData: List<Map<String, Any>> | |||||
| ): Workbook { | |||||
| val resource = ClassPathResource(templatePath) | |||||
| val templateInputStream = resource.inputStream | |||||
| val workbook: Workbook = XSSFWorkbook(templateInputStream) | |||||
| val sheet = workbook.getSheetAt(0) | |||||
| val evaluator: FormulaEvaluator = workbook.creationHelper.createFormulaEvaluator() | |||||
| // Apply standard and custom fonts | |||||
| applyStandardAndCustomFonts(workbook, sheet) | |||||
| // Formatting the date in cell C2 | |||||
| setDateInCellC2(workbook, sheet) | |||||
| setTeamAndClientIds(sheet, team, teamId, customer, clientId) | |||||
| // Set data and conditional formatting in columns | |||||
| setDataAndConditionalFormatting(workbook, sheet, lateStartData, evaluator) | |||||
| // Auto-size columns to fit content | |||||
| autoSizeColumns(sheet) | |||||
| return workbook | |||||
| } | |||||
| //createLateStartReport | |||||
| private fun createLateStartReport( | |||||
| team: List<Team>, | |||||
| customer: List<Customer>, | |||||
| templatePath: String, | |||||
| teamId: Long, | |||||
| clientId: Long, | |||||
| lateStartData: List<Map<String, Any>> | |||||
| ): Workbook { | |||||
| val resource = ClassPathResource(templatePath) | |||||
| val templateInputStream = resource.inputStream | |||||
| val workbook: Workbook = XSSFWorkbook(templateInputStream) | |||||
| val sheet = workbook.getSheetAt(0) | |||||
| val evaluator: FormulaEvaluator = workbook.creationHelper.createFormulaEvaluator() | |||||
| // Apply standard and custom fonts | |||||
| applyStandardAndCustomFonts(workbook, sheet) | |||||
| // Set the current date in cell C2 | |||||
| setDateInCellC2(workbook, sheet) | |||||
| // Populate team and client information based on IDs | |||||
| setTeamAndClientIds(sheet, team, teamId, customer, clientId) | |||||
| // Process late start data, and apply data and conditional formatting to the sheet | |||||
| setDataAndConditionalFormatting(workbook, sheet, lateStartData, evaluator) | |||||
| // Automatically adjust column widths to fit content | |||||
| autoSizeColumns(sheet) | |||||
| return workbook | |||||
| } | |||||
| // Configures standard and custom fonts within the workbook and applies specific styles to cells. | |||||
| private fun applyStandardAndCustomFonts(workbook: Workbook, sheet: Sheet) { | private fun applyStandardAndCustomFonts(workbook: Workbook, sheet: Sheet) { | ||||
| // Create and configure a bold Times New Roman font for general use | |||||
| val timesNewRoman = workbook.createFont().apply { | val timesNewRoman = workbook.createFont().apply { | ||||
| fontName = "Times New Roman" | fontName = "Times New Roman" | ||||
| fontHeightInPoints = 12 | fontHeightInPoints = 12 | ||||
| bold = true | bold = true | ||||
| } | } | ||||
| // Create and configure a larger, bold Times New Roman font for prominent headers | |||||
| val timesNewRomanLarge = workbook.createFont().apply { | val timesNewRomanLarge = workbook.createFont().apply { | ||||
| fontName = "Times New Roman" | fontName = "Times New Roman" | ||||
| fontHeightInPoints = 16 | fontHeightInPoints = 16 | ||||
| bold = true | bold = true | ||||
| } | } | ||||
| // Apply custom font size for A1 | |||||
| // Apply the larger custom font size to cell A1 for header emphasis | |||||
| val rowA1 = sheet.getRow(0) ?: sheet.createRow(0) | val rowA1 = sheet.getRow(0) ?: sheet.createRow(0) | ||||
| val cellA1 = rowA1.getCell(0) ?: rowA1.createCell(0) | val cellA1 = rowA1.getCell(0) ?: rowA1.createCell(0) | ||||
| val cellStyleA1 = workbook.createCellStyle().apply { | val cellStyleA1 = workbook.createCellStyle().apply { | ||||
| @@ -1455,7 +1459,8 @@ private fun createLateStartReport( | |||||
| setFont(timesNewRomanLarge) | setFont(timesNewRomanLarge) | ||||
| } | } | ||||
| cellA1.cellStyle = cellStyleA1 | cellA1.cellStyle = cellStyleA1 | ||||
| // Apply styles from A2 to A4 (bold) | |||||
| // Apply standard bold font to cells A2 to A4 | |||||
| val boldCellStyle = workbook.createCellStyle().apply { setFont(timesNewRoman) } | val boldCellStyle = workbook.createCellStyle().apply { setFont(timesNewRoman) } | ||||
| listOf(1, 2, 3).forEach { rowIndex -> | listOf(1, 2, 3).forEach { rowIndex -> | ||||
| val row = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex) | val row = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex) | ||||
| @@ -1463,39 +1468,42 @@ private fun createLateStartReport( | |||||
| cell.cellStyle = boldCellStyle | cell.cellStyle = boldCellStyle | ||||
| } | } | ||||
| // Apply styles from A6 to J6 (bold, bottom border, center alignment) | |||||
| // Apply a bold, bottom-bordered, center-aligned style to header cells from A6 to J6 | |||||
| val styleA6ToJ6 = workbook.createCellStyle().apply { | val styleA6ToJ6 = workbook.createCellStyle().apply { | ||||
| setFont(timesNewRoman) // Use the standard bold font for consistency | setFont(timesNewRoman) // Use the standard bold font for consistency | ||||
| alignment = HorizontalAlignment.CENTER | alignment = HorizontalAlignment.CENTER | ||||
| borderBottom = BorderStyle.THIN | borderBottom = BorderStyle.THIN | ||||
| } | } | ||||
| // Apply styleA6ToJ6 to cells A6 to J6 | |||||
| val rowA6 = sheet.getRow(5) ?: sheet.createRow(5) | val rowA6 = sheet.getRow(5) ?: sheet.createRow(5) | ||||
| (0..9).forEach { colIndex -> | (0..9).forEach { colIndex -> | ||||
| val cell = rowA6.getCell(colIndex) ?: rowA6.createCell(colIndex) | val cell = rowA6.getCell(colIndex) ?: rowA6.createCell(colIndex) | ||||
| cell.cellStyle = styleA6ToJ6 | cell.cellStyle = styleA6ToJ6 | ||||
| } | } | ||||
| } | } | ||||
| // Sets the current data in the Excel sheet at cell C2 using a specified date format | |||||
| private fun setDateInCellC2(workbook: Workbook, sheet: Sheet) { | private fun setDateInCellC2(workbook: Workbook, sheet: Sheet) { | ||||
| // Format the current date to "yyyy/MM/dd" and set it to cell C2 | |||||
| val formattedToday = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) | val formattedToday = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")) | ||||
| val dateCell = sheet.getRow(1)?.getCell(2) ?: sheet.getRow(1).createCell(2) | val dateCell = sheet.getRow(1)?.getCell(2) ?: sheet.getRow(1).createCell(2) | ||||
| dateCell.setCellValue(formattedToday) | dateCell.setCellValue(formattedToday) | ||||
| } | } | ||||
| // Populates the Excel sheet with late start data and applies conditional formatting based on criteria. | |||||
| private fun setDataAndConditionalFormatting( | private fun setDataAndConditionalFormatting( | ||||
| workbook: Workbook, | workbook: Workbook, | ||||
| sheet: Sheet, | sheet: Sheet, | ||||
| lateStartData: List<Map<String, Any>>, | lateStartData: List<Map<String, Any>>, | ||||
| evaluator: FormulaEvaluator | evaluator: FormulaEvaluator | ||||
| ) { | ) { | ||||
| var dataRowIndex = 6 // Starting from row 7 | |||||
| var dataRowIndex = 6 // Start populating data from row index 7 | |||||
| lateStartData.forEachIndexed { index, data -> | lateStartData.forEachIndexed { index, data -> | ||||
| val row = sheet.getRow(dataRowIndex) ?: sheet.createRow(dataRowIndex) | val row = sheet.getRow(dataRowIndex) ?: sheet.createRow(dataRowIndex) | ||||
| populateDataRow(workbook, sheet, row, data, index) | populateDataRow(workbook, sheet, row, data, index) | ||||
| dataRowIndex++ | dataRowIndex++ | ||||
| } | } | ||||
| // Update conditional formatting to apply to the new range of column J | |||||
| // Update and apply conditional formatting to highlight specific values in column J | |||||
| val sheetCF = sheet.sheetConditionalFormatting | val sheetCF = sheet.sheetConditionalFormatting | ||||
| val rule1 = sheetCF.createConditionalFormattingRule("J7 < 60") | val rule1 = sheetCF.createConditionalFormattingRule("J7 < 60") | ||||
| val pattern1 = rule1.createPatternFormatting().apply { | val pattern1 = rule1.createPatternFormatting().apply { | ||||
| @@ -1510,34 +1518,37 @@ private fun createLateStartReport( | |||||
| val region = CellRangeAddress(dataRowIndex - lateStartData.size, dataRowIndex - 1, 9, 9) // Column J | val region = CellRangeAddress(dataRowIndex - lateStartData.size, dataRowIndex - 1, 9, 9) // Column J | ||||
| sheetCF.addConditionalFormatting(arrayOf(region), arrayOf(rule1, rule2)) | sheetCF.addConditionalFormatting(arrayOf(region), arrayOf(rule1, rule2)) | ||||
| } | } | ||||
| // Sets team and client IDs in the sheet to manage data visibility based on the selected team and client | |||||
| private fun setTeamAndClientIds(sheet: Sheet, team: List<Team>, teamId: Long, customer: List<Customer>, clientId: Long) { | private fun setTeamAndClientIds(sheet: Sheet, team: List<Team>, teamId: Long, customer: List<Customer>, clientId: Long) { | ||||
| // Set the 'teamId' value in cell C3 | |||||
| // Insert the team name or "All" if no specific team is selected in cell C3 | |||||
| val teamIdRow = sheet.getRow(2) ?: sheet.createRow(2) | val teamIdRow = sheet.getRow(2) ?: sheet.createRow(2) | ||||
| val teamIdCell = teamIdRow.getCell(2) ?: teamIdRow.createCell(2) | val teamIdCell = teamIdRow.getCell(2) ?: teamIdRow.createCell(2) | ||||
| teamIdCell.setCellValue(if (teamId == 0L) "All" else team.getOrNull(0)?.name ?: "Unknown") | teamIdCell.setCellValue(if (teamId == 0L) "All" else team.getOrNull(0)?.name ?: "Unknown") | ||||
| // Set the 'clientId' value in cell C4 | |||||
| // Insert the client name or "All" if no specific client is selecred in cell C4 | |||||
| val clientIdRow = sheet.getRow(3) ?: sheet.createRow(3) | val clientIdRow = sheet.getRow(3) ?: sheet.createRow(3) | ||||
| val clientIdCell = clientIdRow.getCell(2) ?: clientIdRow.createCell(2) | val clientIdCell = clientIdRow.getCell(2) ?: clientIdRow.createCell(2) | ||||
| clientIdCell.setCellValue(if (clientId == 0L) "All" else customer.getOrNull(0)?.name ?: "Unknown") | clientIdCell.setCellValue(if (clientId == 0L) "All" else customer.getOrNull(0)?.name ?: "Unknown") | ||||
| } | } | ||||
| // Populates a single row with data from a map and applies formatting and formulas | |||||
| private fun populateDataRow(workbook: Workbook, sheet: Sheet, row: Row, data: Map<String, Any>, index: Int) { | private fun populateDataRow(workbook: Workbook, sheet: Sheet, row: Row, data: Map<String, Any>, index: Int) { | ||||
| val dateFormat = workbook.creationHelper.createDataFormat().getFormat("yyyy/MM/dd") | val dateFormat = workbook.creationHelper.createDataFormat().getFormat("yyyy/MM/dd") | ||||
| // Define the font for general use (if not already defined elsewhere) | |||||
| // Define a font for general use throughout the row | |||||
| val generalFont = workbook.createFont().apply { | val generalFont = workbook.createFont().apply { | ||||
| fontName = "Times New Roman" | fontName = "Times New Roman" | ||||
| fontHeightInPoints = 12 // Standard size | |||||
| fontHeightInPoints = 12 // Standard size is 11 | |||||
| } | } | ||||
| // Create and apply a date format style for date cells | |||||
| val dateCellStyle = workbook.createCellStyle().apply { | val dateCellStyle = workbook.createCellStyle().apply { | ||||
| dataFormat = dateFormat | dataFormat = dateFormat | ||||
| setFont(generalFont) // Apply the font here along with the date format | |||||
| setFont(generalFont) // Maintain consistency in font usage | |||||
| } | } | ||||
| // Create a style for center alignment for column G | |||||
| // Style for center alignment, used in calcalated columns | |||||
| val centerAlignedStyle = workbook.createCellStyle().apply { | val centerAlignedStyle = workbook.createCellStyle().apply { | ||||
| alignment = HorizontalAlignment.CENTER | alignment = HorizontalAlignment.CENTER | ||||
| setFont(generalFont) // Maintain font consistency | |||||
| setFont(generalFont) // Ensure font consistency | |||||
| } | } | ||||
| // Column A: Sequential ID | // Column A: Sequential ID | ||||
| @@ -1561,9 +1572,6 @@ private fun createLateStartReport( | |||||
| customerNameCell.setCellValue(data["customer_name"] as String) | customerNameCell.setCellValue(data["customer_name"] as String) | ||||
| // // Column F: Project Plan Start Date | // // Column F: Project Plan Start Date | ||||
| // val projectPlanStartCell = row.createCell(5) | |||||
| // projectPlanStartCell.setCellValue(data["project_plan_start"].toString()) | |||||
| // Parse the project start date and apply the date format | // Parse the project start date and apply the date format | ||||
| val projectPlanStartCell = row.createCell(5) | val projectPlanStartCell = row.createCell(5) | ||||
| try { | try { | ||||
| @@ -1583,7 +1591,7 @@ private fun createLateStartReport( | |||||
| val taskGroupNameCell = row.createCell(7) | val taskGroupNameCell = row.createCell(7) | ||||
| taskGroupNameCell.setCellValue(data["task_group_name"] as String) | taskGroupNameCell.setCellValue(data["task_group_name"] as String) | ||||
| // // Column I: Milestone End Date | |||||
| // Column I: Milestone End Date | |||||
| val dateCell = row.createCell(8) // Milestone end date in column I | val dateCell = row.createCell(8) // Milestone end date in column I | ||||
| try { | try { | ||||
| val date = LocalDate.parse(data["milestone_end_date"].toString()) | val date = LocalDate.parse(data["milestone_end_date"].toString()) | ||||
| @@ -1593,7 +1601,7 @@ private fun createLateStartReport( | |||||
| } | } | ||||
| dateCell.cellStyle = dateCellStyle | dateCell.cellStyle = dateCellStyle | ||||
| // Get the current date and parse the milestone end date | |||||
| // Calculate the days between current date and milestone end date, and display in column J | |||||
| val today = LocalDate.now() | val today = LocalDate.now() | ||||
| val endDate = LocalDate.parse(data["milestone_end_date"].toString(), DateTimeFormatter.ISO_LOCAL_DATE) | val endDate = LocalDate.parse(data["milestone_end_date"].toString(), DateTimeFormatter.ISO_LOCAL_DATE) | ||||
| val daysBetween = ChronoUnit.DAYS.between(today, endDate).toInt() // Calculate the difference in days | val daysBetween = ChronoUnit.DAYS.between(today, endDate).toInt() // Calculate the difference in days | ||||
| @@ -1601,12 +1609,12 @@ private fun createLateStartReport( | |||||
| val daysDifferenceCellJ = row.createCell(9) | val daysDifferenceCellJ = row.createCell(9) | ||||
| daysDifferenceCellJ.setCellValue(daysBetween.toDouble()) // Set as double for consistency | daysDifferenceCellJ.setCellValue(daysBetween.toDouble()) // Set as double for consistency | ||||
| daysDifferenceCellJ.cellStyle = centerAlignedStyle | daysDifferenceCellJ.cellStyle = centerAlignedStyle | ||||
| } | } | ||||
| // Automatically adjusts the width if all columns in the sheet to fit their content optimally. | |||||
| private fun autoSizeColumns(sheet: Sheet) { | private fun autoSizeColumns(sheet: Sheet) { | ||||
| for (colIndex in 0..9) { | |||||
| sheet.autoSizeColumn(colIndex) | |||||
| for (colIndex in 0..9) { // Iterate through columns A to J | |||||
| sheet.autoSizeColumn(colIndex) // Auto-size each column based on its content | |||||
| } | } | ||||
| } | } | ||||