| @@ -6,5 +6,5 @@ import com.ffii.tsms.modules.data.entity.projections.SalarySearchInfo; | |||
| import java.util.List; | |||
| public interface SalaryRepository extends AbstractRepository<Salary, Long> { | |||
| List<SalarySearchInfo> findSalarySearchInfoBy(); | |||
| List<SalarySearchInfo> findSalarySearchInfoByOrderBySalaryPoint(); | |||
| } | |||
| @@ -21,7 +21,7 @@ open class SalaryService( | |||
| ) : AbstractIdEntityService<Salary, Long, SalaryRepository>(jdbcDao, salaryRepository) { | |||
| open fun allSalarys(): List<SalarySearchInfo>{ | |||
| return salaryRepository.findSalarySearchInfoBy() | |||
| return salaryRepository.findSalarySearchInfoByOrderBySalaryPoint() | |||
| } | |||
| open fun combo(args: Map<String, Any>): List<Map<String, Any>> { | |||
| @@ -51,12 +51,7 @@ open class SalaryService( | |||
| "SET FOREIGN_KEY_CHECKS = 1" | |||
| ) | |||
| val resetAutoIncrement = StringBuilder( | |||
| "ALTER TABLE salary AUTO_INCREMENT = 1" | |||
| ) | |||
| jdbcDao.executeUpdate(disableForeignKeyChecks.toString()) | |||
| jdbcDao.executeUpdate(resetAutoIncrement.toString()) | |||
| jdbcDao.executeUpdate(deleteDataInSalary.toString()) | |||
| val sheet: Sheet = workbook.getSheetAt(0) | |||
| @@ -8,6 +8,7 @@ import jakarta.persistence.JoinColumn | |||
| import jakarta.persistence.ManyToOne | |||
| import jakarta.persistence.Table | |||
| import jakarta.validation.constraints.NotNull | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDate | |||
| @Entity | |||
| @@ -18,7 +19,6 @@ open class Invoice : BaseEntity<Long>(){ | |||
| @Column(name = "invoiceNo", length = 45) | |||
| open var invoiceNo: String? = null | |||
| @NotNull | |||
| @ManyToOne | |||
| @JoinColumn(name = "milestonePaymentId") | |||
| open var milestonePayment: MilestonePayment? = null | |||
| @@ -34,10 +34,40 @@ open class Invoice : BaseEntity<Long>(){ | |||
| @Column(name = "receiptDate") | |||
| open var receiptDate: LocalDate? = null | |||
| @NotNull | |||
| @Column(name = "unpaidAmount") | |||
| open var unpaidAmount: Double? = null | |||
| @Column(name = "paidAmount") | |||
| open var paidAmount: Double? = null | |||
| @Column(name = "projectCode") | |||
| open var projectCode: String? = null | |||
| @Column(name = "projectName") | |||
| open var projectName: String? = null | |||
| @Column(name = "team") | |||
| open var team: String? = null | |||
| @Column(name = "stage") | |||
| open var stage: String? = null | |||
| @Column(name = "paymentMilestone") | |||
| open var paymentMilestone: String? = null | |||
| @Column(name = "paymentMilestoneDate") | |||
| open var paymentMilestoneDate: LocalDate? = null | |||
| @Column(name = "client") | |||
| open var client: String? = null | |||
| @Column(name = "address") | |||
| open var address: String? = null | |||
| @Column(name = "attention") | |||
| open var attention: String? = null | |||
| @NotNull | |||
| @Column(name = "issueAmount") | |||
| open var issueAmount: BigDecimal? = null | |||
| } | |||
| @@ -1,9 +1,13 @@ | |||
| package com.ffii.tsms.modules.project.entity | |||
| import com.ffii.core.support.AbstractRepository | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceInfo | |||
| interface InvoiceRepository : AbstractRepository<Invoice, Long> { | |||
| fun findAllByPaidAmountIsNotNullAndMilestonePaymentIn(milestonePayment: List<MilestonePayment>): List<Invoice> | |||
| fun findInvoiceInfoBy(): List<InvoiceInfo> | |||
| fun findByInvoiceNo(invoiceNo: String): Invoice | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| package com.ffii.tsms.modules.project.entity.projections | |||
| import com.ffii.tsms.modules.project.entity.MilestonePayment | |||
| import org.springframework.beans.factory.annotation.Value | |||
| import org.springframework.cglib.core.Local | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDate | |||
| /** | |||
| * For generating Invoice from Project use | |||
| */ | |||
| interface InvoiceInfo { | |||
| val id: Long? | |||
| val invoiceNo: String? | |||
| val projectName: String? | |||
| val projectCode: String? | |||
| val team: String? | |||
| val stage: String? | |||
| val paymentMilestone: String? | |||
| val paymentMilestoneDate: LocalDate? | |||
| val client: String? | |||
| val address: String? | |||
| val attention: String? | |||
| val invoiceDate: LocalDate? | |||
| val dueDate: LocalDate? | |||
| @get:Value("#{target.issueAmount}") | |||
| val issuedAmount: BigDecimal? | |||
| } | |||
| @@ -2,6 +2,9 @@ package com.ffii.tsms.modules.project.entity.projections | |||
| import org.springframework.beans.factory.annotation.Value | |||
| /** | |||
| * For generating Invoice from Project use | |||
| */ | |||
| interface InvoiceInfoSearchInfo { | |||
| val id: Long? | |||
| @@ -2,7 +2,9 @@ package com.ffii.tsms.modules.project.entity.projections | |||
| import com.ffii.tsms.modules.project.entity.MilestonePayment | |||
| import org.springframework.beans.factory.annotation.Value | |||
| /** | |||
| * For generating Invoice from Project use | |||
| */ | |||
| interface InvoiceSearchInfo { | |||
| val id: Long? | |||
| @@ -1,21 +1,27 @@ | |||
| package com.ffii.tsms.modules.project.service | |||
| import com.ffii.core.support.AbstractBaseEntityService | |||
| import com.ffii.core.support.AbstractIdEntityService | |||
| import com.ffii.core.support.JdbcDao | |||
| import com.ffii.core.utils.ExcelUtils | |||
| import com.ffii.core.utils.PdfUtils | |||
| import com.ffii.tsms.modules.project.entity.* | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceInfo | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoicePDFReq | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | |||
| import com.ffii.tsms.modules.project.entity.projections.ProjectSearchInfo | |||
| import com.ffii.tsms.modules.project.web.models.InvoiceResponse | |||
| import net.sf.jasperreports.engine.JasperCompileManager | |||
| import net.sf.jasperreports.engine.JasperReport | |||
| import org.apache.poi.ss.usermodel.Cell | |||
| import org.apache.poi.ss.usermodel.CellType | |||
| import org.apache.poi.ss.usermodel.Sheet | |||
| import org.apache.poi.ss.usermodel.Workbook | |||
| import org.springframework.core.io.ClassPathResource | |||
| import org.springframework.stereotype.Service | |||
| import org.springframework.transaction.annotation.Transactional | |||
| import java.io.InputStream | |||
| import java.math.BigDecimal | |||
| import java.time.LocalDate | |||
| import java.time.format.DateTimeFormatter | |||
| import java.time.ZoneId | |||
| @Service | |||
| @@ -23,6 +29,7 @@ open class InvoiceService( | |||
| private val invoiceRepository: InvoiceRepository, | |||
| private val milestoneRepository: MilestoneRepository, | |||
| private val milestonePaymentRepository: MilestonePaymentRepository, | |||
| private val projectService: ProjectsService, | |||
| private val jdbcDao: JdbcDao, | |||
| ) : AbstractIdEntityService<Invoice, Long, InvoiceRepository>(jdbcDao, invoiceRepository){ | |||
| @@ -86,6 +93,10 @@ open class InvoiceService( | |||
| return jdbcDao.queryForList(sql.toString(), args) | |||
| } | |||
| open fun getInvoiceByInvoiceNo(invoiceNo: String): Invoice { | |||
| return invoiceRepository.findByInvoiceNo(invoiceNo) | |||
| } | |||
| fun exportPDF(req: InvoicePDFReq): Map<String, Any> { | |||
| // Method implementation | |||
| try { | |||
| @@ -131,4 +142,197 @@ open class InvoiceService( | |||
| ) | |||
| } | |||
| } | |||
| /** | |||
| * return true when the projectCode from invoice issue summary exists | |||
| */ | |||
| open fun checkProjectCode( | |||
| projectCode: String, | |||
| projects: List<ProjectSearchInfo>, | |||
| newProjectCodes: MutableList<String>) | |||
| : List<String>{ | |||
| val existsCodes = projects.map { it.code } | |||
| if (!existsCodes.contains(projectCode) && projectCode.isNotBlank()) { | |||
| newProjectCodes.add(projectCode) | |||
| } | |||
| return newProjectCodes | |||
| } | |||
| /** | |||
| * return the List of existing invoice no | |||
| */ | |||
| open fun checkInvocieNo( | |||
| invoiceNo: String, | |||
| invoices: List<InvoiceInfo>, | |||
| invoicesResult: MutableList<String>, | |||
| checkDuplicate: Boolean | |||
| ): List<String?>{ | |||
| val existingInvoiceNos: List<String?> | |||
| existingInvoiceNos = if(checkDuplicate){ | |||
| invoices.filter { it.invoiceNo == invoiceNo }.map { it.invoiceNo } | |||
| }else{ | |||
| invoices.filter { it.invoiceNo != invoiceNo }.map { it.invoiceNo } | |||
| } | |||
| if (existingInvoiceNos.isNotEmpty()) { | |||
| invoicesResult.add(invoiceNo) | |||
| } | |||
| return invoicesResult | |||
| } | |||
| open fun checkMandatoryField( | |||
| mandatoryColumns: List<Int>, | |||
| sheet: Sheet, | |||
| row: Int, | |||
| emptyRowList: MutableList<Int>, | |||
| extraRow: Int | |||
| ): MutableList<Int>{ | |||
| for (i in 0 until mandatoryColumns.size){ | |||
| // (CellType.NUMERIC, CellType.STRING, CellType.BOOLEAN, CellType.ERROR) | |||
| when(ExcelUtils.getCell(sheet, row, mandatoryColumns[i]).cellType){ | |||
| CellType.NUMERIC -> { | |||
| println("NUMERIC" + ExcelUtils.getCell(sheet, row, mandatoryColumns[i]).numericCellValue) | |||
| if (ExcelUtils.getCell(sheet, row, mandatoryColumns[i]).numericCellValue.isNaN()){ | |||
| emptyRowList.add(row) | |||
| } | |||
| } | |||
| CellType.STRING -> { | |||
| println("STRING" + ExcelUtils.getCell(sheet, row, mandatoryColumns[i]).stringCellValue) | |||
| if(ExcelUtils.getCell(sheet, row, mandatoryColumns[i]).stringCellValue.isBlank()){ | |||
| emptyRowList.add(row) | |||
| } | |||
| } | |||
| CellType.BLANK -> { | |||
| if (row !in emptyRowList){ | |||
| emptyRowList.add(row+extraRow) | |||
| } | |||
| } | |||
| else -> {} | |||
| } | |||
| } | |||
| return emptyRowList | |||
| } | |||
| open fun allInvoice(): List<InvoiceInfo>{ | |||
| return invoiceRepository.findInvoiceInfoBy() | |||
| } | |||
| @Transactional(rollbackFor = [Exception::class]) | |||
| open fun importIssueInvoice(workbook: Workbook?): InvoiceResponse { | |||
| // For checking the existence of imported invoice | |||
| val invoicesResult: MutableList<String> = mutableListOf() | |||
| // For checking existence of projecrt code | |||
| val newProjectCodes: MutableList<String> = mutableListOf() | |||
| // For checking mandatory field in each row | |||
| val emptyRowList: MutableList<Int> = mutableListOf() | |||
| val mandatoryColumns = listOf(0,1,4,5,10,11,12) // Mandatory Field in column 0,1,4,5,10,11,12 | |||
| if (workbook == null) { | |||
| return InvoiceResponse(false, "No Excel import", newProjectCodes, emptyRowList, invoicesResult) // if workbook is null | |||
| } | |||
| val sheet: Sheet = workbook.getSheetAt(0) | |||
| val sheetValues: MutableList<Map<String, Any>> = ArrayList() | |||
| // Get All invoices and Porjects from DB | |||
| val invoices = allInvoice() | |||
| val projects = projectService.allProjects() | |||
| for (i in 2..sheet.lastRowNum){ | |||
| val sheetInvoice = ExcelUtils.getCell(sheet, i, 0).stringCellValue | |||
| val sheetProjectCode = ExcelUtils.getCell(sheet, i, 1).stringCellValue | |||
| checkInvocieNo(sheetInvoice, invoices, invoicesResult, true) | |||
| checkProjectCode(sheetProjectCode, projects, newProjectCodes) | |||
| checkMandatoryField(mandatoryColumns, sheet, i, emptyRowList, 1) | |||
| println("For: $invoicesResult") | |||
| println("For: $newProjectCodes") | |||
| } | |||
| if (invoicesResult.size >= 1 || | |||
| newProjectCodes.size >= 1 || | |||
| emptyRowList.size >= 1 | |||
| ){ | |||
| println("invoicesResult") | |||
| println(invoicesResult) | |||
| return InvoiceResponse(false, "Imported Invoice's format is incorrect", newProjectCodes, emptyRowList, invoicesResult) | |||
| } | |||
| for (i in 2..sheet.lastRowNum){ | |||
| val invoice = Invoice().apply { | |||
| invoiceNo = ExcelUtils.getCell(sheet, i, 0).stringCellValue | |||
| projectCode = ExcelUtils.getCell(sheet, i, 1).stringCellValue | |||
| projectName = ExcelUtils.getCell(sheet, i, 2).stringCellValue | |||
| team = ExcelUtils.getCell(sheet, i, 3).stringCellValue | |||
| stage = ExcelUtils.getCell(sheet, i, 4).numericCellValue.toString() | |||
| paymentMilestone = ExcelUtils.getCell(sheet, i, 5).stringCellValue | |||
| paymentMilestoneDate = ExcelUtils.getCell(sheet, i, 6).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | |||
| client = ExcelUtils.getCell(sheet, i, 7).stringCellValue | |||
| address = ExcelUtils.getCell(sheet, i, 8).stringCellValue | |||
| attention = ExcelUtils.getCell(sheet, i, 9).stringCellValue | |||
| invoiceDate = ExcelUtils.getCell(sheet, i, 10).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | |||
| dueDate = ExcelUtils.getCell(sheet, i, 11).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | |||
| issueAmount = ExcelUtils.getCell(sheet, i, 12).numericCellValue.toBigDecimal() | |||
| } | |||
| saveAndFlush(invoice) | |||
| } | |||
| return InvoiceResponse(true, "OK", newProjectCodes, emptyRowList, invoicesResult) | |||
| } | |||
| @Transactional(rollbackFor = [Exception::class]) | |||
| open fun importReceivedInvoice(workbook: Workbook?): InvoiceResponse { | |||
| // For checking the existence of imported invoice | |||
| val invoicesResult: MutableList<String> = mutableListOf() | |||
| // For checking existence of projecrt code | |||
| val newProjectCodes: MutableList<String> = mutableListOf() | |||
| // For checking mandatory field in each row | |||
| val emptyRowList: MutableList<Int> = mutableListOf() | |||
| val mandatoryColumns = listOf(0,1,4,5) // Mandatory Field in column 0,1,4,5,10,11,12 | |||
| if (workbook == null) { | |||
| return InvoiceResponse(false, "No Excel import", newProjectCodes, emptyRowList, invoicesResult) // if workbook is null | |||
| } | |||
| val sheet: Sheet = workbook.getSheetAt(0) | |||
| val sheetValues: MutableList<Map<String, Any>> = ArrayList() | |||
| // Get All invoices and Porjects from DB | |||
| val invoices = allInvoice() | |||
| val projects = projectService.allProjects() | |||
| for (i in 2..sheet.lastRowNum){ | |||
| val sheetInvoice = ExcelUtils.getCell(sheet, i, 0).stringCellValue | |||
| val sheetProjectCode = ExcelUtils.getCell(sheet, i, 1).stringCellValue | |||
| checkInvocieNo(sheetInvoice, invoices, invoicesResult, false) | |||
| checkProjectCode(sheetProjectCode, projects, newProjectCodes) | |||
| checkMandatoryField(mandatoryColumns, sheet, i, emptyRowList, 1) | |||
| println("For: $invoicesResult") | |||
| println("For: $newProjectCodes") | |||
| } | |||
| if (invoicesResult.size >= 1 || | |||
| newProjectCodes.size >= 1 || | |||
| emptyRowList.size >= 1 | |||
| ){ | |||
| println("duplicateInvoices") | |||
| println(invoicesResult) | |||
| return InvoiceResponse(false, "Imported Invoice's format is incorrect", newProjectCodes, emptyRowList, invoicesResult) | |||
| } | |||
| for (i in 2..sheet.lastRowNum){ | |||
| val invoice = getInvoiceByInvoiceNo(ExcelUtils.getCell(sheet, i, 0).stringCellValue) | |||
| invoice.paidAmount = ExcelUtils.getCell(sheet, i, 5).numericCellValue | |||
| invoice.receiptDate = ExcelUtils.getCell(sheet, i, 4).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | |||
| saveAndFlush(invoice) | |||
| } | |||
| return InvoiceResponse(true, "OK", newProjectCodes, emptyRowList, invoicesResult) | |||
| } | |||
| } | |||
| @@ -1,13 +1,19 @@ | |||
| package com.ffii.tsms.modules.project.web | |||
| import com.ffii.tsms.modules.project.entity.Invoice | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceInfo | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceInfoSearchInfo | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoicePDFReq | |||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | |||
| import com.ffii.tsms.modules.project.service.InvoiceService | |||
| import com.ffii.tsms.modules.project.service.ProjectsService | |||
| import jakarta.servlet.http.HttpServletRequest | |||
| import jakarta.servlet.http.HttpServletResponse | |||
| import net.sf.jasperreports.engine.JasperExportManager | |||
| import net.sf.jasperreports.engine.JasperPrint | |||
| import org.apache.poi.ss.usermodel.Workbook | |||
| import org.apache.poi.xssf.usermodel.XSSFWorkbook | |||
| import org.springframework.http.ResponseEntity | |||
| import org.springframework.web.bind.annotation.GetMapping | |||
| import org.springframework.web.bind.annotation.PathVariable | |||
| import org.springframework.web.bind.annotation.PostMapping | |||
| @@ -15,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestBody | |||
| import org.springframework.web.bind.annotation.RequestMapping | |||
| import org.springframework.web.bind.annotation.RequestParam | |||
| import org.springframework.web.bind.annotation.RestController | |||
| import org.springframework.web.multipart.MultipartHttpServletRequest | |||
| import java.io.OutputStream | |||
| @@ -40,6 +47,11 @@ class InvoiceController( | |||
| return invoiceService.getInvoiceInfoByMilestonePaymentId(id) | |||
| } | |||
| @GetMapping("/getInvoiceByInvoiceNo/{invoiceNo}") | |||
| fun getInvoiceByInvoiceNo(@PathVariable invoiceNo: String): Invoice { | |||
| return invoiceService.getInvoiceByInvoiceNo(invoiceNo) | |||
| } | |||
| @PostMapping("/pdf") | |||
| fun generatePDF(@RequestBody req: InvoicePDFReq, response: HttpServletResponse) { | |||
| response.characterEncoding = "utf-8" | |||
| @@ -57,4 +69,43 @@ class InvoiceController( | |||
| // Any necessary cleanup or additional processing can be done here | |||
| } | |||
| } | |||
| @GetMapping("/v2/allInvoices") | |||
| fun allInvoice_v2(): List<InvoiceInfo> { | |||
| return invoiceService.allInvoice() | |||
| } | |||
| @PostMapping("/import/issued") | |||
| fun importIssuedInvoice(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) | |||
| } | |||
| println("--------- OK -------------") | |||
| return ResponseEntity.ok(invoiceService.importIssueInvoice(workbook)) | |||
| } | |||
| @PostMapping("/import/received") | |||
| fun importReceivedInvoice(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) | |||
| } | |||
| println("--------- OK -------------") | |||
| return ResponseEntity.ok(invoiceService.importReceivedInvoice(workbook)) | |||
| } | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| package com.ffii.tsms.modules.project.web.models | |||
| data class InvoiceResponse ( | |||
| val status: Boolean, | |||
| val message: String, | |||
| val projectList: List<String>, | |||
| val emptyRowList: List<Int>, | |||
| val invoiceList: List<String>, | |||
| ) | |||
| @@ -0,0 +1,4 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset jasonT:modify_id_with_auto_increament | |||
| ALTER TABLE tsmsdb.salary MODIFY COLUMN id int auto_increment NOT NULL; | |||
| @@ -3,4 +3,5 @@ | |||
| -- changeset wayne:salary_data | |||
| UPDATE salary | |||
| SET hourlyRate = (lowerLimit + upperLimit) / 2 / 20 / 8; | |||
| SET hourlyRate = (lowerLimit + upperLimit) / 2 / 20 / 8; | |||
| @@ -0,0 +1,13 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset jasonT:invoice_table | |||
| ALTER TABLE tsmsdb.invoice | |||
| ADD `projectCode` varchar(100) NOT NULL, | |||
| ADD `projectName` varchar(100) DEFAULT NULL, | |||
| ADD `team` varchar(100) DEFAULT NULL, | |||
| ADD `stage` varchar(100) NULL, | |||
| ADD `paymentMilestone` varchar(100) NULL, | |||
| ADD `client` varchar(100) DEFAULT NULL, | |||
| ADD `address` varchar(255) DEFAULT NULL, | |||
| ADD `attention` varchar(100) DEFAULT NULL, | |||
| ADD `issueAmount` decimal(16,2) NOT NULL; | |||
| @@ -0,0 +1,6 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset jasonT:invoice_table_2 | |||
| ALTER TABLE tsmsdb.invoice ADD paymentMilestoneDate DATE NULL AFTER paymentMilestone; | |||
| @@ -0,0 +1,5 @@ | |||
| -- liquibase formatted sql | |||
| -- changeset jasonT:invoice_table_3 | |||
| ALTER TABLE tsmsdb.invoice MODIFY COLUMN milestonePaymentId int NULL; | |||
| ALTER TABLE tsmsdb.invoice MODIFY COLUMN unpaidAmount decimal(16,2) NULL; | |||