From d980a5df9c921c2a059de18203e84ff80a328433 Mon Sep 17 00:00:00 2001 From: "MSI\\2Fi" Date: Wed, 5 Jun 2024 16:15:47 +0800 Subject: [PATCH] costandexpense Report: Add subsidiary for searching --- .../modules/report/service/ReportService.kt | 70 ++++++++++++------ .../modules/report/web/ReportController.kt | 2 +- .../modules/report/web/model/ReportRequest.kt | 1 + .../AR04_Cost and Expense Report v02.xlsx | Bin 12689 -> 12808 bytes 4 files changed, 49 insertions(+), 24 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 748735a..cb678cb 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 @@ -2339,7 +2339,7 @@ open class ReportService( return workbook } - fun getCostAndExpense(clientId: Long?, teamId: Long?): List>{ + fun getCostAndExpense(clientId: Long?, teamId: Long?, type: String): List>{ val sql = StringBuilder( " with cte_timesheet as ( " + " Select p.code, s.name as staff, IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.salaryPoint, s2.hourlyRate, t.staffId," @@ -2351,13 +2351,15 @@ open class ReportService( + " left join salary s2 on s.salaryId = s2.salaryPoint" + " left join team t2 on t2.id = s.teamId" + " )" - + " select p.code, p.description, c.name as client, concat(t.code, \' - \', t.name) as teamLead, p.expectedTotalFee," + + " select p.code, p.description, c.name as client, IFNULL(s2.name, \'NA\') as subsidiary, concat(t.code, \' - \', t.name) as teamLead, p.expectedTotalFee," + " SUM(IFNULL(cte_ts.normalConsumed, 0)) as normalConsumed," + " SUM(IFNULL(cte_ts.otConsumed, 0)) as otConsumed," + " IFNULL(cte_ts.hourlyRate, 0) as hourlyRate" + " from project p" + " left join cte_timesheet cte_ts on p.code = cte_ts.code" + " left join customer c on c.id = p.customerId" + + " left join customer_subsidiary cs on cs.id = p.customerSubsidiaryId" + + " left join subsidiary s2 on s2.id = cs.subsidiaryId " + " left join tsmsdb.team t on t.teamLead = p.teamLead" + " left join staff s on s.id = cte_ts.staffId" + " left join grade g on g.id = s.gradeId" @@ -2365,9 +2367,16 @@ open class ReportService( + " where ISNULL(p.code) = False" ) if(clientId != null){ - sql.append( - " and c.id = :clientId " - ) + if(type == "client"){ + sql.append( + " and c.id = :clientId " + ) + } + if(type == "subsidiary"){ + sql.append( + " and s2.id = :clientId " + ) + } } if(teamId != null){ @@ -2377,7 +2386,7 @@ open class ReportService( } sql.append( - " group by p.code, p.description , c.name, teamLead, p.expectedTotalFee , hourlyRate" + " order by p.code" + " group by p.code, p.description , c.name, teamLead, p.expectedTotalFee , hourlyRate, s2.name " + " order by p.code" ) val args = mapOf( @@ -2397,6 +2406,7 @@ open class ReportService( "code" to item["code"], "description" to item["description"], "client" to item["client"], + "subsidiary" to item["subsidiary"], "teamLead" to item["teamLead"], "budget" to item["expectedTotalFee"], "totalManhours" to item["normalConsumed"] as Double + item["otConsumed"] as Double, @@ -2431,7 +2441,8 @@ open class ReportService( costAndExpenseList: List>, teamId: Long?, clientId: Long?, - budgetPercentage: Double? + budgetPercentage: Double?, + type: String ): Workbook{ val resource = ClassPathResource(templatePath) val templateInputStream = resource.inputStream @@ -2466,9 +2477,17 @@ open class ReportService( if(clientId == null){ row3Cell.setCellValue("All") }else{ - val sql = StringBuilder( - " select c.id, c.name from customer c where c.id = :clientId " - ) + val sql= StringBuilder() + if(type == "client"){ + sql.append( + " select c.id, c.name from customer c where c.id = :clientId " + ) + } + if(type == "subsidiary"){ + sql.append( + " select s.id, s.name from subsidiary s where s.id = :clientId " + ) + } val client = jdbcDao.queryForMap(sql.toString(), mapOf("clientId" to clientId)).get() row3Cell.setCellValue(client["name"] as String) } @@ -2476,7 +2495,7 @@ open class ReportService( val filterList: List> if(budgetPercentage != null){ - filterList = costAndExpenseList.filter { ((it["budgetPercentage"] as? Double) ?: 0.0) > budgetPercentage } + filterList = costAndExpenseList.filter { ((it["budgetPercentage"] as? Double) ?: 0.0) <= budgetPercentage } }else{ filterList = costAndExpenseList } @@ -2512,31 +2531,36 @@ open class ReportService( } val cell5 = row.getCell(5) ?: row.createCell(5) - val budget = item["budget"] as Double * 0.8 cell5.apply { - setCellValue(budget) -// cellStyle.dataFormat = accountingStyle + setCellValue(item["subsidiary"].toString()) } - CellUtil.setCellStyleProperty(cell5, "dataFormat", accountingStyle) val cell6 = row.getCell(6) ?: row.createCell(6) - val manHoutsSpentCost = item["manhourExpenditure"] as Double + val budget = item["budget"] as Double * 0.8 cell6.apply { - setCellValue(manHoutsSpentCost) + setCellValue(budget) +// cellStyle.dataFormat = accountingStyle } - CellUtil.setCellStyleProperty(cell6, "dataFormat", accountingStyle) + CellUtil.setCellStyleProperty(cell5, "dataFormat", accountingStyle) val cell7 = row.getCell(7) ?: row.createCell(7) + val manHoutsSpentCost = item["manhourExpenditure"] as Double cell7.apply { - cellFormula = "F${rowNum+1}-G${rowNum+1}" + setCellValue(manHoutsSpentCost) } CellUtil.setCellStyleProperty(cell7, "dataFormat", accountingStyle) val cell8 = row.getCell(8) ?: row.createCell(8) cell8.apply { - cellFormula = "H${rowNum+1}/F${rowNum+1}" + cellFormula = "G${rowNum+1}-H${rowNum+1}" + } + CellUtil.setCellStyleProperty(cell8, "dataFormat", accountingStyle) + + val cell9 = row.getCell(9) ?: row.createCell(9) + cell9.apply { + cellFormula = "I${rowNum+1}/G${rowNum+1}" } - CellUtil.setCellStyleProperty(cell8, "dataFormat", percentStyle) + CellUtil.setCellStyleProperty(cell9, "dataFormat", percentStyle) sheet.setRowBreak(rowNum++); } @@ -2546,9 +2570,9 @@ open class ReportService( fun genCostAndExpenseReport(request: costAndExpenseRequest): ByteArray{ - val costAndExpenseList = getCostAndExpense(request.clientId, request.teamId) + val costAndExpenseList = getCostAndExpense(request.clientId, request.teamId, request.type) - val workbook: Workbook = createCostAndExpenseWorkbook(COSTANDEXPENSE_REPORT, costAndExpenseList, request.teamId, request.clientId, request.budgetPercentage) + val workbook: Workbook = createCostAndExpenseWorkbook(COSTANDEXPENSE_REPORT, costAndExpenseList, request.teamId, request.clientId, request.budgetPercentage, request.type) val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() workbook.write(outputStream) 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 563801f..f84307e 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 @@ -286,7 +286,7 @@ class ReportController( @GetMapping("/costNExpenses/{status}") fun getManhoursSpent(@RequestBody @Valid request: costAndExpenseRequest): List> { - return excelReportService.getCostAndExpense(request.clientId, request.teamId) + return excelReportService.getCostAndExpense(request.clientId, request.teamId, request.type) } @PostMapping("/costandexpenseReport") diff --git a/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt b/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt index c56925d..1233753 100644 --- a/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt +++ b/src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt @@ -17,6 +17,7 @@ data class costAndExpenseRequest ( val teamId: Long?, val clientId: Long?, val budgetPercentage: Double?, + val type: String, ) data class ProjectCashFlowReportRequest ( diff --git a/src/main/resources/templates/report/AR04_Cost and Expense Report v02.xlsx b/src/main/resources/templates/report/AR04_Cost and Expense Report v02.xlsx index a8aecebf09dce1486c46280b9b5d99d028819f57..8f971d5d354e78f2f97e801e1a5f0ab30751ad09 100644 GIT binary patch delta 4902 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 Cf#zWV