From 2eae3d38657039858c067d4be3bdf906715cc933 Mon Sep 17 00:00:00 2001 From: "MSI\\2Fi" Date: Tue, 7 May 2024 18:23:46 +0800 Subject: [PATCH] Add HourlyRate in DB import and export Salary Template --- .../ffii/tsms/modules/data/entity/Salary.java | 13 +++++ .../entity/projections/SalarySearchInfo.java | 4 +- .../modules/data/service/SalaryService.kt | 48 +++++++++++++++++- .../tsms/modules/data/web/SalaryController.kt | 47 +++++++++++++++-- .../modules/report/service/ReportService.kt | 28 ++++++++++ .../01_alter_salary_add_hourlyRate.sql | 4 ++ .../templates/report/Salary Template.xlsx | Bin 0 -> 12675 bytes 7 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 src/main/resources/db/changelog/changes/20240507_01_jasonT/01_alter_salary_add_hourlyRate.sql create mode 100644 src/main/resources/templates/report/Salary Template.xlsx diff --git a/src/main/java/com/ffii/tsms/modules/data/entity/Salary.java b/src/main/java/com/ffii/tsms/modules/data/entity/Salary.java index 7e1d35d..6625724 100644 --- a/src/main/java/com/ffii/tsms/modules/data/entity/Salary.java +++ b/src/main/java/com/ffii/tsms/modules/data/entity/Salary.java @@ -6,6 +6,8 @@ import jakarta.persistence.Entity; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; + @Entity @Table(name = "salary") public class Salary extends IdEntity { @@ -25,6 +27,9 @@ public class Salary extends IdEntity { @Column(name = "increment") private Integer increment; + @Column(name = "hourlyRate") + private BigDecimal hourlyRate; + public Integer getIncrement() { return increment; } @@ -56,4 +61,12 @@ public class Salary extends IdEntity { public Integer getSalaryPoint() { return salaryPoint; } + + public BigDecimal getHourlyRate() { + return hourlyRate; + } + + public void setHourlyRate(BigDecimal hourlyRate) { + this.hourlyRate = hourlyRate; + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/data/entity/projections/SalarySearchInfo.java b/src/main/java/com/ffii/tsms/modules/data/entity/projections/SalarySearchInfo.java index a6d3d52..d447a4e 100644 --- a/src/main/java/com/ffii/tsms/modules/data/entity/projections/SalarySearchInfo.java +++ b/src/main/java/com/ffii/tsms/modules/data/entity/projections/SalarySearchInfo.java @@ -1,5 +1,7 @@ package com.ffii.tsms.modules.data.entity.projections; +import java.math.BigDecimal; + /** * Projection for {@link com.ffii.tsms.modules.data.entity.Salary} */ @@ -8,5 +10,5 @@ public interface SalarySearchInfo { Integer getSalaryPoint(); Integer getLowerLimit(); Integer getUpperLimit(); - + BigDecimal getHourlyRate(); } diff --git a/src/main/java/com/ffii/tsms/modules/data/service/SalaryService.kt b/src/main/java/com/ffii/tsms/modules/data/service/SalaryService.kt index b40eebf..37cefce 100644 --- a/src/main/java/com/ffii/tsms/modules/data/service/SalaryService.kt +++ b/src/main/java/com/ffii/tsms/modules/data/service/SalaryService.kt @@ -3,12 +3,15 @@ package com.ffii.tsms.modules.data.service import com.ffii.core.support.AbstractBaseEntityService import com.ffii.core.support.AbstractIdEntityService import com.ffii.core.support.JdbcDao -import com.ffii.tsms.modules.data.entity.Department -import com.ffii.tsms.modules.data.entity.DepartmentRepository +import com.ffii.core.utils.ExcelUtils import com.ffii.tsms.modules.data.entity.Salary import com.ffii.tsms.modules.data.entity.SalaryRepository import com.ffii.tsms.modules.data.entity.projections.SalarySearchInfo +import org.apache.poi.ss.usermodel.Sheet +import org.apache.poi.ss.usermodel.Workbook import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal @Service open class SalaryService( @@ -28,4 +31,45 @@ open class SalaryService( ) return jdbcDao.queryForList(sql.toString(), args) } + + @Transactional(rollbackFor = [Exception::class]) + open fun importFile(workbook: Workbook?): String { + if (workbook == null) { + return "No Excel import" // if workbook is null + } + + val deleteDataInSalary = StringBuilder( + "DELETE FROM salary" + ) + + val disableForeignKeyChecks = StringBuilder( + "SET FOREIGN_KEY_CHECKS = 0" + ) + + val enableForeignKeyChecks = StringBuilder( + "SET FOREIGN_KEY_CHECKS = 1" + ) + + jdbcDao.executeUpdate(disableForeignKeyChecks.toString()) + jdbcDao.executeUpdate(deleteDataInSalary.toString()) + + val sheet: Sheet = workbook.getSheetAt(0) +// val sheetValues: MutableList> = ArrayList() + + for (i in 2..sheet.lastRowNum){ + val salary = Salary().apply { + salaryPoint = ExcelUtils.getIntValue(ExcelUtils.getCell(sheet, i, 0)) + lowerLimit = ExcelUtils.getIntValue(ExcelUtils.getCell(sheet, i, 1)) + upperLimit = ExcelUtils.getIntValue(ExcelUtils.getCell(sheet, i, 2)) + increment = ExcelUtils.getIntValue(ExcelUtils.getCell(sheet, i, 3)) + hourlyRate = ExcelUtils.getDecimalValue(ExcelUtils.getCell(sheet, i, 4)) + } + saveAndFlush(salary) + // println("Lower Limit: " + ExcelUtils.getIntValue(ExcelUtils.getCell(sheet, i, 1))) + } + + jdbcDao.executeUpdate(enableForeignKeyChecks.toString()) + + return "OK" + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/data/web/SalaryController.kt b/src/main/java/com/ffii/tsms/modules/data/web/SalaryController.kt index 65c4feb..85b03f9 100644 --- a/src/main/java/com/ffii/tsms/modules/data/web/SalaryController.kt +++ b/src/main/java/com/ffii/tsms/modules/data/web/SalaryController.kt @@ -5,17 +5,27 @@ import com.ffii.core.utils.CriteriaArgsBuilder import com.ffii.tsms.modules.data.entity.projections.SalarySearchInfo import com.ffii.tsms.modules.data.service.DepartmentService import com.ffii.tsms.modules.data.service.SalaryService +import com.ffii.tsms.modules.report.service.ReportService +import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest import jakarta.servlet.http.HttpServletRequest +import jakarta.validation.Valid +import org.apache.poi.ss.usermodel.Workbook +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import org.springframework.core.io.ByteArrayResource +import org.springframework.core.io.Resource +import org.springframework.http.ResponseEntity import org.springframework.web.bind.ServletRequestBindingException -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* +import org.springframework.web.multipart.MultipartHttpServletRequest +import java.io.IOException +import java.time.LocalDate @RestController @RequestMapping("/salarys") class SalaryController( - private val salaryService: SalaryService + private val salaryService: SalaryService, + private val excelReportService: ReportService ) { @GetMapping @@ -34,4 +44,33 @@ class SalaryController( ) ) } + + @PostMapping("/import") + fun importFile(request: HttpServletRequest): ResponseEntity<*> { + var workbook: Workbook? = null + + try { + val multipartFile = (request as MultipartHttpServletRequest).getFile("multipartFileList") + if (multipartFile != null) { + workbook = XSSFWorkbook(multipartFile.inputStream) + } + } catch (e: Exception) { + println("Excel Wrong") + println(e) + } + + return ResponseEntity.ok(salaryService.importFile(workbook)) + } + + @PostMapping("/export") + @Throws(ServletRequestBindingException::class, IOException::class) + fun exportSalaryList(): ResponseEntity { + + val reportResult: ByteArray = excelReportService.exportSalaryList() +// val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + return ResponseEntity.ok() +// .contentType(mediaType) + .header("filename", "Salary Point List - " + LocalDate.now() + ".xlsx") + .body(ByteArrayResource(reportResult)) + } } \ 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 21bcbeb..a8b74a9 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 @@ -19,6 +19,7 @@ open class ReportService { private val FORMATTED_TODAY = LocalDate.now().format(DATE_FORMATTER) private val PROJECT_CASH_FLOW_REPORT = "templates/report/EX02_Project Cash Flow Report.xlsx" + private val SALART_LIST_TEMPLATE = "templates/report/Salary Template.xlsx" // ==============================|| GENERATE REPORT ||============================== // @Throws(IOException::class) @@ -34,6 +35,19 @@ open class ReportService { return outputStream.toByteArray() } + @Throws(IOException::class) + fun exportSalaryList(): ByteArray { + // Generate the Excel report with query results + val workbook: Workbook = createSalaryList(SALART_LIST_TEMPLATE) + + // Write the workbook to a ByteArrayOutputStream + val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() + workbook.write(outputStream) + workbook.close() + + return outputStream.toByteArray() + } + // ==============================|| CREATE REPORT ||============================== // @Throws(IOException::class) private fun createProjectCashFlowReport( @@ -157,4 +171,18 @@ open class ReportService { return workbook } + + @Throws(IOException::class) + private fun createSalaryList( + templatePath: String, + ): Workbook { + val resource = ClassPathResource(templatePath) + val templateInputStream = resource.inputStream + val workbook: Workbook = XSSFWorkbook(templateInputStream) + + val sheet: Sheet = workbook.getSheetAt(0) + + return workbook + } + } \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/20240507_01_jasonT/01_alter_salary_add_hourlyRate.sql b/src/main/resources/db/changelog/changes/20240507_01_jasonT/01_alter_salary_add_hourlyRate.sql new file mode 100644 index 0000000..f21ce0a --- /dev/null +++ b/src/main/resources/db/changelog/changes/20240507_01_jasonT/01_alter_salary_add_hourlyRate.sql @@ -0,0 +1,4 @@ +-- liquibase formatted sql +-- changeset jasonT:hourlyRate + +ALTER TABLE tsmsdb.salary ADD hourlyRate DECIMAL(5,2) NULL; \ No newline at end of file diff --git a/src/main/resources/templates/report/Salary Template.xlsx b/src/main/resources/templates/report/Salary Template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..882cca1fdca08b1b13946c8ed854f092d7fb9f93 GIT binary patch literal 12675 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 literal 0 HcmV?d00001