diff --git a/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt b/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt index 0013733..039e152 100644 --- a/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt +++ b/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt @@ -22,6 +22,7 @@ import java.util.* import kotlin.jvm.optionals.getOrNull + @Service open class StaffsService( private val staffRepository: StaffRepository, @@ -426,4 +427,17 @@ open class StaffsService( return jdbcDao.queryForList(sql.toString(), mapOf("ids" to args)) } + + open fun manuelUpdateSalaryEffectiveDate() { + val staffIds = findAll().get().filter { it.departDate == null || it.departDate < LocalDate.now() }.map { it.id } + println(staffIds) + val salaryEffectiveInfo = salaryEffectiveRepository.findSalaryEffectiveInfoByStaffIdInOrderByStaffId(staffIds) + staffIds.forEach { id -> + val filteredSalaryEffective = salaryEffectiveInfo.filter { it.idInStaff == id } + val updatedSalaryEffectiveInfo = filteredSalaryEffective.map { it -> SalaryEffectiveInfo(it.idInStaff, it.salary.salaryPoint.toLong(), it.date ) } + val delSalaryEffectiveInfo = filteredSalaryEffective.map { it.id!! } + salaryEffectiveService.updateSalaryEffective(id!!, updatedSalaryEffectiveInfo.sortedBy { it.date }, delSalaryEffectiveInfo) + } + + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt b/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt index b115d5f..36b3f71 100644 --- a/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt +++ b/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt @@ -78,4 +78,9 @@ class StaffsController(private val staffsService: StaffsService) { fun saveStaff(@Valid @RequestBody newStaff: NewStaffRequest): NewStaffResponse { return staffsService.saveOrUpdate(newStaff) } + + @GetMapping("/manuelUpdate") + fun manualUpdate(){ + staffsService.manuelUpdateSalaryEffectiveDate() + } } \ No newline at end of file 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 2f5428f..5a7689f 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 @@ -193,10 +193,10 @@ open class ReportService( return rowIndex } - fun genFinancialStatusReport(teamLeadId: Long): ByteArray { + fun genFinancialStatusReport(teamLeadId: Long, startMonth: String, endMonth: String): ByteArray { - val financialStatus: List> = getFinancialStatus(teamLeadId) - val manhoursSpent = getManHoursSpentByTeam(teamLeadId) + val financialStatus: List> = getFinancialStatus(teamLeadId, startMonth, endMonth) + val manhoursSpent = getManHoursSpentByTeam(teamLeadId, startMonth, endMonth) val salaryEffectiveMap = getSalaryEffectiveByTeamLead(teamLeadId) val updatedTimesheetData = updateTimesheetDataWithEffectiveSalary(manhoursSpent, salaryEffectiveMap) val projectsExpenditure = calculateProjectExpenditures(updatedTimesheetData) @@ -213,7 +213,7 @@ open class ReportService( } } - val workbook: Workbook = createFinancialStatusReport(FINANCIAL_STATUS_REPORT, updatedList, teamLeadId) + val workbook: Workbook = createFinancialStatusReport(FINANCIAL_STATUS_REPORT, updatedList, teamLeadId, startMonth, endMonth) val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() workbook.write(outputStream) @@ -444,7 +444,9 @@ open class ReportService( private fun createFinancialStatusReport( templatePath: String, projects: List>, - teamLeadId: Long + teamLeadId: Long, + startMonth: String, + endMonth: String, ): Workbook { val resource = ClassPathResource(templatePath) @@ -468,7 +470,7 @@ open class ReportService( val boldFontCellStyle = workbook.createCellStyle() boldFontCellStyle.setFont(boldFont) - var rowNum = 14 + var rowNum = 16 if (projects.isEmpty()) { // Fill the cell in Row 2-12 with thr calculated sum @@ -496,10 +498,15 @@ open class ReportService( setCellValue("$code - $name") } - rowNum = 4 - val row4: Row = sheet.getRow(rowNum) - val row4Cell = row4.createCell(2) - row4Cell.setCellValue(projects.size.toString()) + rowNum = 3 + val row3: Row = sheet.getRow(rowNum) + val row3Cell = row3.createCell(2) + row3Cell.setCellValue("$startMonth - $endMonth") + + rowNum = 4+1 + val row5: Row = sheet.getRow(rowNum) + val row5Cell = row5.createCell(2) + row5Cell.setCellValue(projects.size.toString()) return workbook } @@ -543,9 +550,11 @@ open class ReportService( setCellValue(totalFee) cellStyle.dataFormat = accountingStyle } +// subContractFee is count in fee +// val fee = (item["expectedTotalFee"]?.let { it as Double } ?: 0.0) - (item["subContractFee"]?.let { it as Double } +// ?: 0.0) - val fee = (item["expectedTotalFee"]?.let { it as Double } ?: 0.0) - (item["subContractFee"]?.let { it as Double } - ?: 0.0) + val fee = (item["expectedTotalFee"]?.let { it as Double } ?: 0.0) val budgetCell = row.createCell(8) budgetCell.apply { @@ -759,16 +768,21 @@ open class ReportService( row2Cell.setCellValue("All") } else { row2Cell.apply { - cellFormula = "E15" + cellFormula = "E16" } } - rowNum = 4 + rowNum = 3 + val row3: Row = sheet.getRow(rowNum) + val row3Cell = row3.createCell(2) + row3Cell.setCellValue("$startMonth - $endMonth") + + rowNum = 4+1 val row4: Row = sheet.getRow(rowNum) val row4Cell = row4.createCell(2) row4Cell.setCellValue(projects.size.toString()) - rowNum = 5 + rowNum = 5+1 val row5: Row = sheet.getRow(rowNum) val cell1 = row5.createCell(2) cell1.apply { @@ -776,7 +790,7 @@ open class ReportService( cellStyle.dataFormat = accountingStyle } - rowNum = 6 + rowNum = 6+1 val row6: Row = sheet.getRow(rowNum) val cell2 = row6.createCell(2) cell2.apply { @@ -784,7 +798,7 @@ open class ReportService( cellStyle.dataFormat = accountingStyle } - rowNum = 7 + rowNum = 7+1 val row7: Row = sheet.getRow(rowNum) val cell3 = row7.createCell(2) cell3.apply { @@ -792,7 +806,7 @@ open class ReportService( cellStyle.dataFormat = accountingStyle } - rowNum = 8 + rowNum = 8+1 val row8: Row = sheet.getRow(rowNum) val cell4 = row8.createCell(2) cell4.apply { @@ -800,7 +814,7 @@ open class ReportService( cellStyle.dataFormat = accountingStyle } - rowNum = 9 + rowNum = 9+1 val row9: Row = sheet.getRow(rowNum) val cell5 = row9.createCell(2) cell5.apply { @@ -808,7 +822,7 @@ open class ReportService( cellStyle.dataFormat = accountingStyle } - rowNum = 10 + rowNum = 10+1 val row10: Row = sheet.getRow(rowNum) val cell6 = row10.createCell(2) cell6.apply { @@ -816,7 +830,7 @@ open class ReportService( cellStyle.dataFormat = accountingStyle } - rowNum = 11 + rowNum = 11+1 val row11: Row = sheet.getRow(rowNum) val cell7 = row11.createCell(2) cell7.apply { @@ -2179,7 +2193,16 @@ open class ReportService( } } - open fun getFinancialStatus(teamLeadId: Long?): List> { + open fun getFinancialStatus(teamLeadId: Long?, startMonth: String, endMonth: String): List> { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + val startYearMonth = YearMonth.parse(startMonth) + val startDate = startYearMonth.atDay(1) + val startDateString = startDate.format(formatter) + + val endYearMonth = YearMonth.parse(endMonth) + val endDate = endYearMonth.atEndOfMonth() + val endDateString = endDate.format(formatter) + 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.hourlyRate" @@ -2191,21 +2214,46 @@ open class ReportService( // + " left join team t2 on t2.id = s.teamId" // + " )," " With cte_invoice as (" - + " select p.code, sum(i.issueAmount) as sumIssuedAmount , sum(i.paidAmount) as sumPaidAmount" + + " select p.code, sum(i.issueAmount) as sumIssuedAmount " + + " from invoice i" + + " left join project p on p.code = i.projectCode" + + " where i.deleted = false " + + " and p.deleted = false " + + " and i.invoiceDate >= :startMonth and i.invoiceDate <= :endMonth " + + " group by p.code" + + " )," + + " cte_rinvoice as (" + + " select p.code, sum(i.paidAmount) as sumPaidAmount" + " from invoice i" + " left join project p on p.code = i.projectCode" + " where i.deleted = false " + " and p.deleted = false " + + " and i.receiptDate >= :startMonth and i.receiptDate <= :endMonth " + " group by p.code" + " )," + " cte_expense as ( " + " select IFNULL(sum(pe.amount),0) as amount, pe.projectId " + " from project_expense pe " + " where pe.deleted = false " + + " and pe.issueDate >= :startMonth and pe.issueDate <= :endMonth " + " group by projectId " + + " ), " + + " cte_fee as ( " + + " select IFNULL(sum(mp.amount),0) as expectedTotalFee, p.id as projectId " + + " from milestone_payment mp " + + " left join milestone m on mp.milestoneId = m.id " + + " left join project p on p.id = m.projectId " + + " where p.status = 'On-going' " + + " and p.deleted = false " + + " and coalesce(p.actualStart, p.planStart) <= :endMonth " + + " and mp.`date` >= :startMonth and mp.`date` <= :endMonth " + + " and p.teamLead = :teamLeadId" + + " group by p.id" + " ) " - + " select p.code, p.name, p.description, c.name as client, IFNULL(s2.name, \"N/A\") as subsidiary, concat(t.code, \' - \', t.name) as teamLead, p.planStart , p.planEnd , p.expectedTotalFee, ifnull(p.subContractFee, 0) as subContractFee, " - + " IFNULL(cte_i.sumIssuedAmount, 0) as sumIssuedAmount, IFNULL(cte_i.sumPaidAmount, 0) as sumPaidAmount" + + " select p.code, p.name, p.description, c.name as client, IFNULL(s2.name, \"N/A\") as subsidiary, concat(t.code, \' - \', t.name) as teamLead, " + + " p.planStart , p.planEnd ," + + " IFNULL(cte_f.expectedTotalFee, 0) as expectedTotalFee, ifnull(p.subContractFee, 0) as subContractFee, " + + " IFNULL(cte_i.sumIssuedAmount, 0) as sumIssuedAmount, IFNULL(cte_ri.sumPaidAmount, 0) as sumPaidAmount" + " ,0 as totalCumulativeExpenditure, IFNULL(cte_e.amount,0) as projectExpense " + " from project p" // + " left join cte_timesheet cte_ts on p.code = cte_ts.code" @@ -2214,15 +2262,18 @@ open class ReportService( + " left join subsidiary s2 on s2.id = cs.subsidiaryId " + " left join tsmsdb.team t on t.teamLead = p.teamLead" + " left join cte_invoice cte_i on cte_i.code = p.code" + + " left join cte_rinvoice cte_ri on cte_ri.code = p.code" + " left join cte_expense cte_e on cte_e.projectId = p.id " + + " left join cte_fee cte_f on cte_f.projectId = p.id " + " where p.status = \'On-going\'" + " and p.deleted = false " + + " and coalesce(p.actualStart, p.planStart) <= :endMonth" ) if (teamLeadId!! > 0) { sql.append(" and p.teamLead = :teamLeadId ") } sql.append(" order by p.code") - val args = mapOf("teamLeadId" to teamLeadId) + val args = mapOf("teamLeadId" to teamLeadId, "startMonth" to startDateString, "endMonth" to endDateString ) return jdbcDao.queryForList(sql.toString(), args) } @@ -3437,7 +3488,9 @@ open class ReportService( "clientId" to clientId, "teamLeadId" to teamId ) - val manhoursSpent = getManHoursSpentByTeam(teamId) + val formatter = DateTimeFormatter.ofPattern("yyyy-MM") + + val manhoursSpent = getManHoursSpentByTeam(teamId, "1970-01", LocalDate.now().format(formatter)) val salaryEffectiveMap = getSalaryEffectiveByTeamLead(teamId) val updatedTimesheetData = updateTimesheetDataWithEffectiveSalary(manhoursSpent, salaryEffectiveMap) val projectsExpenditure = calculateProjectExpenditures(updatedTimesheetData) @@ -5223,7 +5276,16 @@ open class ReportService( } // Get all the timesheet data by Team Lead - fun getManHoursSpentByTeam(teamLeadId: Long?): List{ + fun getManHoursSpentByTeam(teamLeadId: Long?, startMonth: String, endMonth: String): List{ + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + val startYearMonth = YearMonth.parse(startMonth) + val startDate = startYearMonth.atDay(1) + val startDateString = startDate.format(formatter) + + val endYearMonth = YearMonth.parse(endMonth) + val endDate = endYearMonth.atEndOfMonth() + val endDateString = endDate.format(formatter) + val sql = StringBuilder( "select coalesce(t.normalConsumed, 0) as normalConsumed, coalesce(t.otConsumed, 0) as otConsumed, t.recordDate, t.staffId, s2.hourlyRate, s2.salaryPoint, p.code, p.planStart, p.planEnd" // For later calculating cross team charge @@ -5236,7 +5298,8 @@ open class ReportService( + " left join project p on t.projectId = p.id" + " left join staff s on t.staffId = s.id" + " left join salary s2 on s.salaryId = s2.salaryPoint" - + " where t.projectId in" + + " where t.recordDate >= :startMonth and t.recordDate <= :endMonth " + + " and t.projectId in" + " (" + " select p.id from project p" + " where p.status = 'On-going'" @@ -5248,7 +5311,7 @@ open class ReportService( sql.append(") order by code, recordDate, staffId; ") - val results = jdbcDao.queryForList(sql.toString(), mapOf("teamLeadId" to teamLeadId)).map { + val results = jdbcDao.queryForList(sql.toString(), mapOf("teamLeadId" to teamLeadId, "startMonth" to startDateString, "endMonth" to endDateString )).map { result -> TimesheetData( result["normalConsumed"] as Double, @@ -5312,7 +5375,8 @@ open class ReportService( // Find the nearest effective date that is less than or equal to the record date val nearestEffectiveSalary = effectiveSalaryList - .filter { YearMonth.from(it.effectiveDate) <= YearMonth.from(timesheetData.recordDate) } +// .filter { YearMonth.from(it.effectiveDate) <= YearMonth.from(timesheetData.recordDate) } + .filter{ it.effectiveDate <= timesheetData.recordDate } .maxByOrNull { it.effectiveDate } if (nearestEffectiveSalary != null) { 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 d8c62e8..2ea4633 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 @@ -74,8 +74,12 @@ class ReportController( @Throws(ServletRequestBindingException::class, IOException::class) fun getFinancialStatusReport(@RequestBody @Valid request: FinancialStatusReportRequest): ResponseEntity { - println(request.teamLeadId) - val reportResult: ByteArray = excelReportService.genFinancialStatusReport(request.teamLeadId) + println("----- start downloading financial status report -----") + println("teamLeadId: ${request.teamLeadId}") + println("startMonth: ${request.startMonth}") + println("endMonth: ${request.endMonth}") + + val reportResult: ByteArray = excelReportService.genFinancialStatusReport(request.teamLeadId, request.startMonth, request.endMonth) return ResponseEntity.ok() .header("filename", "Financial Status Report - " + LocalDate.now() + ".xlsx") @@ -279,10 +283,10 @@ class ReportController( } // For API TESTING - @GetMapping("/financialReport/{id}") - fun getFinancialReport(@PathVariable id: Long): List> { - return excelReportService.getFinancialStatus(id) - } +// @GetMapping("/financialReport/{id}") +// fun getFinancialReport(@PathVariable id: Long): List> { +// return excelReportService.getFinancialStatus(id) +// } // For API TESTING @GetMapping("/manHoursSpent/{id}") 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 a379967..f7a687e 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 @@ -4,7 +4,9 @@ import java.time.LocalDate import java.time.YearMonth data class FinancialStatusReportRequest ( - val teamLeadId: Long + val teamLeadId: Long, + val startMonth: String, + val endMonth: String, ) data class PandLReportRequest ( diff --git a/src/main/resources/templates/report/EX01_Financial Status Report.xlsx b/src/main/resources/templates/report/EX01_Financial Status Report.xlsx index e1dcf08..962ef19 100644 Binary files a/src/main/resources/templates/report/EX01_Financial Status Report.xlsx and b/src/main/resources/templates/report/EX01_Financial Status Report.xlsx differ