@@ -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; |