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 fdb23b2..e9c98bc 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 @@ -855,6 +855,46 @@ open class ReportService( combinedResults.forEach { result: String -> if (groupedInvoices.containsKey(result)) { + if (dateType == "Date") { + groupedInvoices[result]!!.forEachIndexed { _, invoice -> + sheet.createRow(rowIndex++).apply { + createCell(0).apply { + setCellValue(result) + } + + createCell(1).apply { + setCellValue(0.0) + cellStyle.dataFormat = accountingStyle + } + + createCell(2).apply { + setCellValue(invoice["paidAmount"] as Double? ?: 0.0) + cellStyle.dataFormat = accountingStyle + } + + createCell(3).apply { + val lastRow = rowIndex - 1 + if (lastRow == 16) { + cellFormula = + "C{currentRow}-B{currentRow}".replace("{currentRow}", rowIndex.toString()) + } else { + cellFormula = + "IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace( + "{currentRow}", + rowIndex.toString() + ).replace("{lastRow}", lastRow.toString()) + } + cellStyle.dataFormat = accountingStyle + } + + createCell(4)?.apply { + setCellValue( + "Invoice Receipt: " + (invoice["invoiceNo"] as String? ?: "N/A").toString() + ) + } + } + } + } else { // groupedInvoices[result]!!.forEachIndexed { _, invoice -> sheet.createRow(rowIndex++).apply { createCell(0).apply { @@ -888,9 +928,16 @@ open class ReportService( createCell(4)?.apply { // setCellValue(invoice["description"].toString()) - setCellValue("Invoice Receipt: " + (groupedInvoices[result]?.map { it["invoiceNo"] }?.joinToString() ?: "N/A").toString()) + val invoiceNos = groupedInvoices[result]?.map { it["invoiceNo"] } + if (invoiceNos?.size != null && invoiceNos.size > 1) { +// setCellValue("Invoice Receipt: " + (groupedInvoices[result]?.map { it["invoiceNo"] }?.joinToString() ?: "N/A").toString()) + setCellValue("Multiple Invoices") + } else { + setCellValue("Invoice Receipt: " + (invoiceNos?.joinToString() ?: "N/A").toString()) + } } // } + } } } @@ -1353,7 +1400,7 @@ open class ReportService( for (i in 0 until rowSize) { tempCell = sheet.getRow(8 + i).createCell(columnIndex + index) tempCell.setCellValue(0.0) - if ( 8+i in holidayIndexList) { + if (8 + i in holidayIndexList) { tempCell.cellStyle = fillOrange } tempCell.cellStyle.dataFormat = accountingStyle @@ -1365,7 +1412,7 @@ open class ReportService( dayInt = temp[index]["date"].toString().toInt() tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex) tempCell.setCellValue((temp[index]["normalConsumed"] as Double) + (temp[index]["otConsumed"] as Double)) - if ( dayInt.plus(7) in holidayIndexList) { + if (dayInt.plus(7) in holidayIndexList) { tempCell.cellStyle = fillOrange tempCell.cellStyle.dataFormat = accountingStyle } @@ -1377,7 +1424,7 @@ open class ReportService( for (i in 0 until rowSize) { tempCell = sheet.getRow(8 + i).createCell(columnIndex) tempCell.setCellValue(0.0) - if ( 8+i in holidayIndexList) { + if (8 + i in holidayIndexList) { tempCell.cellStyle = fillOrange } @@ -1388,7 +1435,7 @@ open class ReportService( dayInt = leave["recordDate"].toString().toInt() tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex) tempCell.setCellValue(leave["leaveHours"] as Double) - if ( dayInt.plus(7) in holidayIndexList) { + if (dayInt.plus(7) in holidayIndexList) { tempCell.cellStyle = fillOrange } @@ -1420,7 +1467,7 @@ open class ReportService( tempCell = sheet.getRow(rowIndex).createCell(columnIndex) tempCell.cellFormula = "SUM(${getColumnAlphabet(2)}${rowIndex + 1}:${getColumnAlphabet(columnIndex - 2)}${rowIndex + 1})" // should columnIndex - 2 - if ( rowIndex in holidayIndexList) { + if (rowIndex in holidayIndexList) { tempCell.cellStyle = fillOrange } @@ -1495,21 +1542,21 @@ open class ReportService( } val cell2 = getCell(2) ?: createCell(2) - cell2.cellFormula = "(B{currentRow}+D{currentRow})-1".replace("{currentRow}", (rowIndex+1).toString()) + cell2.cellFormula = "(B{currentRow}+D{currentRow})-1".replace("{currentRow}", (rowIndex + 1).toString()) CellUtil.setAlignment(cell2, HorizontalAlignment.CENTER) cell2.cellStyle.dataFormat = defaultStyle // getCell(2).cellStyle.dataFormat = accountingStyle - val cell3 = getCell(3)?:createCell(3) + val cell3 = getCell(3) ?: createCell(3) cell3.setCellValue(salary.increment.toDouble()) cell3.cellStyle = fillOrange cell3.cellStyle.dataFormat = defaultStyle CellUtil.setAlignment(cell3, HorizontalAlignment.CENTER) - val cell4 = getCell(4)?:createCell(4) + val cell4 = getCell(4) ?: createCell(4) cell4.cellFormula = - "(((C{currentRow}+B{currentRow})/2)/20)/8".replace("{currentRow}", (rowIndex+1).toString()) + "(((C{currentRow}+B{currentRow})/2)/20)/8".replace("{currentRow}", (rowIndex + 1).toString()) // getCell(4).cellStyle.dataFormat = accountingStyle cell4.cellStyle.dataFormat = accountingStyle CellUtil.setAlignment(cell4, HorizontalAlignment.CENTER) @@ -1979,52 +2026,53 @@ open class ReportService( } open fun getProjectResourceOverconsumptionReport(args: Map): List> { - val sql = StringBuilder("with teamNormalConsumed as (" - + " SELECT" - + " t.projectId," - + " p.teamLead," - + " sum(t.normalConsumed) as normalConsumed," - + " sum(t.otConsumed) as otConsumed," - + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed," - + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * sal.hourlyRate as totalBudget" - + " from timesheet t" - + " left join project p on p.id = t.projectId" - + " left join staff s on s.id = p.teamLead" - + " left join salary sal on sal.salaryPoint = s.salaryId" - + " group by p.teamLead, t.projectId, sal.hourlyRate" - + " )" - + " SELECT" - + " p.id," - + " p.code," - + " p.name," - + " t.code," - + " concat(c.code, ' - ',c.name) as client," - + " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary," - + " p.expectedTotalFee * 0.8 as plannedBudget," - + " COALESCE(tns.totalBudget, 0) as actualConsumedBudget," - + " tns.totalConsumed," - + " COALESCE(p.totalManhour, 0) as plannedManhour," - + " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour," - + " (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) >= :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" - + " then 'Overconsumption'" - + " else 'Within Budget'" - + " END as status" - + " from project p" - + " left join staff s on s.id = p.teamLead" - + " left join team t on t.id = s.teamId" - + " left join customer c on c.id = p.customerId" - + " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id" - + " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint" - + " left join teamNormalConsumed tns on tns.projectId = p.id" - + " WHERE p.deleted = false " - + " and p.status = 'On-going' " + val sql = StringBuilder( + "with teamNormalConsumed as (" + + " SELECT" + + " t.projectId," + + " p.teamLead," + + " sum(t.normalConsumed) as normalConsumed," + + " sum(t.otConsumed) as otConsumed," + + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed," + + " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * sal.hourlyRate as totalBudget" + + " from timesheet t" + + " left join project p on p.id = t.projectId" + + " left join staff s on s.id = p.teamLead" + + " left join salary sal on sal.salaryPoint = s.salaryId" + + " group by p.teamLead, t.projectId, sal.hourlyRate" + + " )" + + " SELECT" + + " p.id," + + " p.code," + + " p.name," + + " t.code," + + " concat(c.code, ' - ',c.name) as client," + + " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary," + + " p.expectedTotalFee * 0.8 as plannedBudget," + + " COALESCE(tns.totalBudget, 0) as actualConsumedBudget," + + " tns.totalConsumed," + + " COALESCE(p.totalManhour, 0) as plannedManhour," + + " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour," + + " (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) >= :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" + + " then 'Overconsumption'" + + " else 'Within Budget'" + + " END as status" + + " from project p" + + " left join staff s on s.id = p.teamLead" + + " left join team t on t.id = s.teamId" + + " left join customer c on c.id = p.customerId" + + " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id" + + " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint" + + " left join teamNormalConsumed tns on tns.projectId = p.id" + + " WHERE p.deleted = false " + + " and p.status = 'On-going' " ) if (args != null) { var statusFilter: String = "" @@ -2041,6 +2089,7 @@ open class ReportService( " 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 " + "All" -> " and " + " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit " + " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit " @@ -2105,17 +2154,17 @@ open class ReportService( + " order by g.code, s.name, cte_ts.recordDate" ) val projectPlanStartEndMonth = projectRepository.findProjectPlanStartEndByIdAndDeletedFalse(projectId) - val compareStartMonth= YearMonth.parse(startMonth) - val compareEndMonth= YearMonth.parse(endMonth) + val compareStartMonth = YearMonth.parse(startMonth) + val compareEndMonth = YearMonth.parse(endMonth) val actualStartMonth: YearMonth - (if (projectPlanStartEndMonth.actualStart == null){ + (if (projectPlanStartEndMonth.actualStart == null) { YearMonth.now().plusMonths(1) - }else{ + } else { YearMonth.from(projectPlanStartEndMonth.actualStart) }).also { actualStartMonth = it } - val queryStartMonth = if(compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth - val queryEndMonth = if(compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth + val queryStartMonth = if (compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth + val queryEndMonth = if (compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth val args = mapOf( "projectId" to projectId, @@ -2251,7 +2300,8 @@ open class ReportService( val manhoursSpentList = getManhoursSpent(projectId, startMonth, endMonth) - val workbook: Workbook = createPandLReportWorkbook(PandL_REPORT, manhoursSpentList, startMonth, endMonth, projectId) + val workbook: Workbook = + createPandLReportWorkbook(PandL_REPORT, manhoursSpentList, startMonth, endMonth, projectId) val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() workbook.write(outputStream) @@ -2274,17 +2324,17 @@ open class ReportService( val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") val projectPlanStartEndMonth = projectRepository.findProjectPlanStartEndByIdAndDeletedFalse(projectId) - val compareStartMonth= YearMonth.parse(startMonth) - val compareEndMonth= YearMonth.parse(endMonth) + val compareStartMonth = YearMonth.parse(startMonth) + val compareEndMonth = YearMonth.parse(endMonth) val actualStartMonth: YearMonth - (if (projectPlanStartEndMonth.actualStart == null){ + (if (projectPlanStartEndMonth.actualStart == null) { YearMonth.now().plusMonths(1) - }else{ + } else { YearMonth.from(projectPlanStartEndMonth.actualStart) }).also { actualStartMonth = it } - val queryStartMonth = if(compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth - val queryEndMonth = if(compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth + val queryStartMonth = if (compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth + val queryEndMonth = if (compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth val monthFormat = DateTimeFormatter.ofPattern("MMM yyyy", Locale.ENGLISH) val startDate = YearMonth.parse(queryStartMonth.toString(), DateTimeFormatter.ofPattern("yyyy-MM")) @@ -2294,7 +2344,7 @@ open class ReportService( val monthRange: MutableList> = ArrayList() var currentDate = startDate - if (currentDate == endDate){ + if (currentDate == endDate) { monthRange.add( mapOf( "display" to YearMonth.of(currentDate.year, currentDate.month).format(monthFormat), @@ -2575,7 +2625,7 @@ open class ReportService( } // CellUtil.setCellStyleProperty(panlCellTitle, "borderBottom", BorderStyle.DOUBLE) // CellUtil.setCellStyleProperty(panlCell, "borderBottom", BorderStyle.DOUBLE) - val profitAndLossRowNum = rowNum+1 + val profitAndLossRowNum = rowNum + 1 rowNum += 1 val uninvoicedAmountRow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum) @@ -2585,7 +2635,7 @@ open class ReportService( } val uninvoicedAmountCell = uninvoicedAmountRow.getCell(1) ?: uninvoicedAmountRow.createCell(1) uninvoicedAmountCell.apply { - setCellValue(if ((info.getValue("expectedTotalFee") as Double) < 0.0 ) 0.0 else info.getValue("expectedTotalFee") as Double) + setCellValue(if ((info.getValue("expectedTotalFee") as Double) < 0.0) 0.0 else info.getValue("expectedTotalFee") as Double) cellStyle.dataFormat = accountingStyle } @@ -2597,7 +2647,7 @@ open class ReportService( } val projectedPnLCell = projectedPnLRow.getCell(1) ?: projectedPnLRow.createCell(1) projectedPnLCell.apply { - cellFormula = "B${profitAndLossRowNum}+B${profitAndLossRowNum+1}" + cellFormula = "B${profitAndLossRowNum}+B${profitAndLossRowNum + 1}" cellStyle.dataFormat = accountingStyle } @@ -3063,7 +3113,8 @@ open class ReportService( } createCell(columnIndex++).apply { - val cellValue = grade.name.trim().substring(0,1) + grade.name.trim().substring(grade.name.length - 1) + " - Cost Adjusted by Salary Point" + val cellValue = grade.name.trim().substring(0, 1) + grade.name.trim() + .substring(grade.name.length - 1) + " - Cost Adjusted by Salary Point" setCellValue(cellValue) val cloneStyle = workbook.createCellStyle() cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)