| @@ -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<String, Any>): List<Map<String, Any>> { | |||
| 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<Map<String, Any>> = 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) | |||