Ver a proveniência

Import Issued Invoice Related

tags/Baseline_30082024_BACKEND_UAT
MSI\2Fi há 1 ano
ascendente
cometimento
3cc8387c33
15 ficheiros alterados com 384 adições e 15 eliminações
  1. +1
    -1
      src/main/java/com/ffii/tsms/modules/data/entity/SalaryRepository.java
  2. +1
    -6
      src/main/java/com/ffii/tsms/modules/data/service/SalaryService.kt
  3. +32
    -2
      src/main/java/com/ffii/tsms/modules/project/entity/Invoice.kt
  4. +4
    -0
      src/main/java/com/ffii/tsms/modules/project/entity/InvoiceRepository.kt
  5. +42
    -0
      src/main/java/com/ffii/tsms/modules/project/entity/projections/InvoiceInfo.kt
  6. +3
    -0
      src/main/java/com/ffii/tsms/modules/project/entity/projections/InvoiceInfoSearchInfo.kt
  7. +3
    -1
      src/main/java/com/ffii/tsms/modules/project/entity/projections/InvoiceSearchInfo.kt
  8. +208
    -4
      src/main/java/com/ffii/tsms/modules/project/service/InvoiceService.kt
  9. +51
    -0
      src/main/java/com/ffii/tsms/modules/project/web/InvoiceController.kt
  10. +9
    -0
      src/main/java/com/ffii/tsms/modules/project/web/models/InvoiceResponse.kt
  11. +4
    -0
      src/main/resources/db/changelog/changes/20240318_01_wayne/06_revert_salary_id_with_auto_increment.sql
  12. +2
    -1
      src/main/resources/db/changelog/changes/20240507_01_jasonT/04_salary_data.sql
  13. +13
    -0
      src/main/resources/db/changelog/changes/20240507_01_jasonT/05_alter_invoice.sql
  14. +6
    -0
      src/main/resources/db/changelog/changes/20240507_01_jasonT/06_alter_invoice_2.sql
  15. +5
    -0
      src/main/resources/db/changelog/changes/20240507_01_jasonT/07_alter_invoice_3.sql

+ 1
- 1
src/main/java/com/ffii/tsms/modules/data/entity/SalaryRepository.java Ver ficheiro

@@ -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();
}

+ 1
- 6
src/main/java/com/ffii/tsms/modules/data/service/SalaryService.kt Ver ficheiro

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


+ 32
- 2
src/main/java/com/ffii/tsms/modules/project/entity/Invoice.kt Ver ficheiro

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

+ 4
- 0
src/main/java/com/ffii/tsms/modules/project/entity/InvoiceRepository.kt Ver ficheiro

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

+ 42
- 0
src/main/java/com/ffii/tsms/modules/project/entity/projections/InvoiceInfo.kt Ver ficheiro

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

}

+ 3
- 0
src/main/java/com/ffii/tsms/modules/project/entity/projections/InvoiceInfoSearchInfo.kt Ver ficheiro

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



+ 3
- 1
src/main/java/com/ffii/tsms/modules/project/entity/projections/InvoiceSearchInfo.kt Ver ficheiro

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



+ 208
- 4
src/main/java/com/ffii/tsms/modules/project/service/InvoiceService.kt Ver ficheiro

@@ -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)
}
}

+ 51
- 0
src/main/java/com/ffii/tsms/modules/project/web/InvoiceController.kt Ver ficheiro

@@ -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))
}
}

+ 9
- 0
src/main/java/com/ffii/tsms/modules/project/web/models/InvoiceResponse.kt Ver ficheiro

@@ -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>,
)

+ 4
- 0
src/main/resources/db/changelog/changes/20240318_01_wayne/06_revert_salary_id_with_auto_increment.sql Ver ficheiro

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

+ 2
- 1
src/main/resources/db/changelog/changes/20240507_01_jasonT/04_salary_data.sql Ver ficheiro

@@ -3,4 +3,5 @@
-- changeset wayne:salary_data

UPDATE salary
SET hourlyRate = (lowerLimit + upperLimit) / 2 / 20 / 8;
SET hourlyRate = (lowerLimit + upperLimit) / 2 / 20 / 8;


+ 13
- 0
src/main/resources/db/changelog/changes/20240507_01_jasonT/05_alter_invoice.sql Ver ficheiro

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

+ 6
- 0
src/main/resources/db/changelog/changes/20240507_01_jasonT/06_alter_invoice_2.sql Ver ficheiro

@@ -0,0 +1,6 @@
-- liquibase formatted sql
-- changeset jasonT:invoice_table_2

ALTER TABLE tsmsdb.invoice ADD paymentMilestoneDate DATE NULL AFTER paymentMilestone;



+ 5
- 0
src/main/resources/db/changelog/changes/20240507_01_jasonT/07_alter_invoice_3.sql Ver ficheiro

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

Carregando…
Cancelar
Guardar