|
|
@@ -12,11 +12,13 @@ import org.apache.commons.logging.LogFactory |
|
|
|
import org.apache.poi.ss.usermodel.* |
|
|
|
import org.apache.poi.ss.util.CellAddress |
|
|
|
import org.apache.poi.ss.util.CellRangeAddress |
|
|
|
import org.apache.poi.ss.util.CellUtil |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook |
|
|
|
import org.springframework.core.io.ClassPathResource |
|
|
|
import org.springframework.stereotype.Service |
|
|
|
import java.io.ByteArrayOutputStream |
|
|
|
import java.io.IOException |
|
|
|
import java.math.BigDecimal |
|
|
|
import java.time.LocalDate |
|
|
|
import java.time.format.DateTimeFormatter |
|
|
|
import java.util.* |
|
|
@@ -40,7 +42,54 @@ open class ReportService ( |
|
|
|
// ==============================|| GENERATE REPORT ||============================== // |
|
|
|
|
|
|
|
fun genFinancialStatusReport(projectId: Long): ByteArray { |
|
|
|
val workbook: Workbook = createFinancialStatusReport(FINANCIAL_STATUS_REPORT, projectId) |
|
|
|
|
|
|
|
val financialStatus: List<Map<String, Any>> = getFinancialStatus(projectId) |
|
|
|
|
|
|
|
val otFactor = BigDecimal(1) |
|
|
|
|
|
|
|
val tempList = mutableListOf<Map<String, Any>>() |
|
|
|
|
|
|
|
for (item in financialStatus){ |
|
|
|
val normalConsumed = item.getValue("normalConsumed") as Double |
|
|
|
val hourlyRate = item.getValue("hourlyRate") as BigDecimal |
|
|
|
println("normalConsumed------------- $normalConsumed") |
|
|
|
println("hourlyRate------------- $hourlyRate") |
|
|
|
val manHourRate = normalConsumed.toBigDecimal().multiply(hourlyRate) |
|
|
|
println("manHourRate------------ $manHourRate") |
|
|
|
|
|
|
|
val otConsumed = item.getValue("otConsumed") as Double |
|
|
|
val manOtHourRate = otConsumed.toBigDecimal().multiply(hourlyRate).multiply(otFactor) |
|
|
|
|
|
|
|
if(!tempList.any{ it.containsValue(item.getValue("code"))}){ |
|
|
|
|
|
|
|
tempList.add(mapOf( |
|
|
|
"code" to item.getValue("code"), |
|
|
|
"description" to item.getValue("description"), |
|
|
|
"client" to item.getValue("client"), |
|
|
|
"teamLead" to item.getValue("teamLead"), |
|
|
|
"planStart" to item.getValue("planStart"), |
|
|
|
"planEnd" to item.getValue("planEnd"), |
|
|
|
"expectedTotalFee" to item.getValue("expectedTotalFee"), |
|
|
|
"normalConsumed" to manHourRate, |
|
|
|
"otConsumed" to manOtHourRate, |
|
|
|
"issuedAmount" to item.getValue("sumIssuedAmount"), |
|
|
|
"paidAmount" to item.getValue("sumPaidAmount"), |
|
|
|
)) |
|
|
|
}else{ |
|
|
|
// Find the existing Map in the tempList that has the same "code" value |
|
|
|
val existingMap = tempList.find { it.containsValue(item.getValue("code")) }!! |
|
|
|
|
|
|
|
// Update the existing Map with the new manHourRate and manOtHourRate values |
|
|
|
tempList[tempList.indexOf(existingMap)] = existingMap.toMutableMap().apply { |
|
|
|
put("normalConsumed", (get("normalConsumed") as BigDecimal).add(manHourRate)) |
|
|
|
put("otConsumed", (get("otConsumed") as BigDecimal).add(manOtHourRate)) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
println("tempList---------------------- $tempList") |
|
|
|
|
|
|
|
val workbook: Workbook = createFinancialStatusReport(FINANCIAL_STATUS_REPORT, tempList) |
|
|
|
|
|
|
|
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() |
|
|
|
workbook.write(outputStream) |
|
|
@@ -101,12 +150,265 @@ open class ReportService ( |
|
|
|
// EX01 Financial Report |
|
|
|
private fun createFinancialStatusReport( |
|
|
|
templatePath: String, |
|
|
|
projectId: Long, |
|
|
|
projects: List<Map<String, Any>>, |
|
|
|
) : Workbook { |
|
|
|
|
|
|
|
val resource = ClassPathResource(templatePath) |
|
|
|
val templateInputStream = resource.inputStream |
|
|
|
val workbook: Workbook = XSSFWorkbook(templateInputStream) |
|
|
|
|
|
|
|
val accountingStyle = workbook.createDataFormat() .getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") |
|
|
|
|
|
|
|
val sheet = workbook.getSheetAt(0) |
|
|
|
|
|
|
|
val boldFont = sheet.workbook.createFont() |
|
|
|
boldFont.bold = true |
|
|
|
|
|
|
|
val boldFontCellStyle = workbook.createCellStyle() |
|
|
|
boldFontCellStyle.setFont(boldFont) |
|
|
|
|
|
|
|
|
|
|
|
var rowNum = 14 |
|
|
|
|
|
|
|
for(item in projects){ |
|
|
|
val row: Row = sheet.createRow(rowNum++) |
|
|
|
|
|
|
|
val codeCell = row.createCell(0) |
|
|
|
codeCell.setCellValue(if (item["code"] != null) item.getValue("code").toString() else "N/A") |
|
|
|
|
|
|
|
val descriptionCell = row.createCell(1) |
|
|
|
descriptionCell.setCellValue(if (item["description"] != null) item.getValue("description").toString() else "N/A") |
|
|
|
|
|
|
|
val clientCell = row.createCell(2) |
|
|
|
clientCell.setCellValue(if (item["client"] != null) item.getValue("client").toString() else "N/A") |
|
|
|
|
|
|
|
val teamLeadCell = row.createCell(3) |
|
|
|
teamLeadCell.setCellValue(if (item["teamLead"] != null) item.getValue("teamLead").toString() else "N/A") |
|
|
|
|
|
|
|
val startDateCell = row.createCell(4) |
|
|
|
startDateCell.setCellValue(if (item["planStart"] != null) item.getValue("planStart").toString() else "N/A") |
|
|
|
|
|
|
|
val endDateCell = row.createCell(5) |
|
|
|
endDateCell.setCellValue(if (item["planEnd"] != null) item.getValue("planEnd").toString() else "N/A") |
|
|
|
|
|
|
|
val totalFeeCell = row.createCell(6) |
|
|
|
val totalFee = item["expectedTotalFee"]?.let { it as Double } ?: 0.0 |
|
|
|
totalFeeCell.apply { |
|
|
|
setCellValue(totalFee) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val budgetCell = row.createCell(7) |
|
|
|
budgetCell.apply { |
|
|
|
cellFormula = "G${rowNum} * 80%" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
val cumExpenditureCell = row.createCell(8) |
|
|
|
val normalConsumed = item["normalConsumed"]?.let { it as BigDecimal } ?: BigDecimal(0) |
|
|
|
val otConsumed = item["otConsumed"]?.let { it as BigDecimal } ?: BigDecimal(0) |
|
|
|
val cumExpenditure = normalConsumed.add(otConsumed) |
|
|
|
cumExpenditureCell.apply{ |
|
|
|
setCellValue(cumExpenditure.toDouble()) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
val budgetVCell = row.createCell(9) |
|
|
|
budgetVCell.apply { |
|
|
|
cellFormula = "H${rowNum} - I${rowNum}" |
|
|
|
cellStyle = boldFontCellStyle |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
val issuedCell = row.createCell(10) |
|
|
|
val issuedAmount = item["issuedAmount"]?.let { it as BigDecimal } ?: BigDecimal(0) |
|
|
|
issuedCell.apply { |
|
|
|
setCellValue(issuedAmount.toDouble()) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
val uninvoiceCell = row.createCell(11) |
|
|
|
uninvoiceCell.apply { |
|
|
|
cellFormula = " IF(H${rowNum}<I${rowNum},H${rowNum}-K${rowNum},I${rowNum}-K${rowNum}) " |
|
|
|
cellStyle = boldFontCellStyle |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
val cpiCell = row.createCell(12) |
|
|
|
cpiCell.apply { |
|
|
|
cellFormula = "K${rowNum}/I${rowNum}" |
|
|
|
} |
|
|
|
|
|
|
|
val receivedAmountCell = row.createCell(13) |
|
|
|
val paidAmount = item["paidAmount"]?.let { it as BigDecimal } ?: BigDecimal(0) |
|
|
|
receivedAmountCell.apply{ |
|
|
|
setCellValue(paidAmount.toDouble()) |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val unsettledAmountCell = row.createCell(14) |
|
|
|
unsettledAmountCell.apply { |
|
|
|
cellFormula = "K${rowNum}-N${rowNum}" |
|
|
|
cellStyle = boldFontCellStyle |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
val lastRowNum = rowNum + 1 |
|
|
|
|
|
|
|
val row: Row = sheet.createRow(rowNum) |
|
|
|
for(i in 0..4){ |
|
|
|
val cell = row.createCell(i) |
|
|
|
CellUtil.setCellStyleProperty(cell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(cell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
} |
|
|
|
|
|
|
|
val subTotalCell = row.createCell(5) |
|
|
|
subTotalCell.apply { |
|
|
|
setCellValue("Sub-total:") |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(subTotalCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(subTotalCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
|
|
|
|
val sumTotalFeeCell = row.createCell(6) |
|
|
|
sumTotalFeeCell.apply { |
|
|
|
cellFormula = "SUM(G15:G${rowNum})" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumTotalFeeCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumTotalFeeCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val sumBudgetCell = row.createCell(7) |
|
|
|
sumBudgetCell.apply { |
|
|
|
cellFormula = "SUM(H15:H${rowNum})" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumBudgetCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumBudgetCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val sumCumExpenditureCell = row.createCell(8) |
|
|
|
sumCumExpenditureCell.apply { |
|
|
|
cellFormula = "SUM(I15:I${rowNum})" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumCumExpenditureCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumCumExpenditureCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val sumBudgetVCell = row.createCell(9) |
|
|
|
sumBudgetVCell.apply { |
|
|
|
cellFormula = "SUM(J15:J${rowNum})" |
|
|
|
cellStyle = boldFontCellStyle |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumBudgetVCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumBudgetVCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val sumIInvoiceCell = row.createCell(10) |
|
|
|
sumIInvoiceCell.apply { |
|
|
|
cellFormula = "SUM(K15:K${rowNum})" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumIInvoiceCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumIInvoiceCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val sumUInvoiceCell = row.createCell(11) |
|
|
|
sumUInvoiceCell.apply { |
|
|
|
cellFormula = "SUM(L15:L${rowNum})" |
|
|
|
cellStyle = boldFontCellStyle |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumUInvoiceCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumUInvoiceCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val lastCpiCell = row.createCell(12) |
|
|
|
CellUtil.setCellStyleProperty(lastCpiCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(lastCpiCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val sumRAmountCell = row.createCell(13) |
|
|
|
sumRAmountCell.apply { |
|
|
|
cellFormula = "SUM(N15:N${rowNum})" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumRAmountCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumRAmountCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
val sumUnSettleCell = row.createCell(14) |
|
|
|
sumUnSettleCell.apply { |
|
|
|
cellFormula = "SUM(O15:O${rowNum})" |
|
|
|
cellStyle = boldFontCellStyle |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
CellUtil.setCellStyleProperty(sumUnSettleCell,"borderTop", BorderStyle.THIN) |
|
|
|
CellUtil.setCellStyleProperty(sumUnSettleCell,"borderBottom", BorderStyle.DOUBLE) |
|
|
|
|
|
|
|
rowNum = 1 |
|
|
|
val row1: Row = sheet.getRow(rowNum) |
|
|
|
val genDateCell = row1.createCell(2) |
|
|
|
genDateCell.setCellValue(LocalDate.now().toString()) |
|
|
|
|
|
|
|
rowNum = 2 |
|
|
|
|
|
|
|
rowNum = 4 |
|
|
|
|
|
|
|
rowNum = 5 |
|
|
|
val row5: Row = sheet.getRow(rowNum) |
|
|
|
val cell1 = row5.createCell(2) |
|
|
|
cell1.apply { |
|
|
|
cellFormula = "H${lastRowNum}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
rowNum = 6 |
|
|
|
val row6: Row = sheet.getRow(rowNum) |
|
|
|
val cell2 = row6.createCell(2) |
|
|
|
cell2.apply { |
|
|
|
cellFormula = "I${lastRowNum}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
rowNum = 7 |
|
|
|
val row7: Row = sheet.getRow(rowNum) |
|
|
|
val cell3 = row7.createCell(2) |
|
|
|
cell3.apply { |
|
|
|
cellFormula = "J${lastRowNum}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
rowNum = 8 |
|
|
|
val row8: Row = sheet.getRow(rowNum) |
|
|
|
val cell4 = row8.createCell(2) |
|
|
|
cell4.apply { |
|
|
|
cellFormula = "K${lastRowNum}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
rowNum = 9 |
|
|
|
val row9: Row = sheet.getRow(rowNum) |
|
|
|
val cell5 = row9.createCell(2) |
|
|
|
cell5.apply { |
|
|
|
cellFormula = "L${lastRowNum}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
rowNum = 10 |
|
|
|
val row10: Row = sheet.getRow(rowNum) |
|
|
|
val cell6 = row10.createCell(2) |
|
|
|
cell6.apply { |
|
|
|
cellFormula = "N${lastRowNum}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
rowNum = 11 |
|
|
|
val row11: Row = sheet.getRow(rowNum) |
|
|
|
val cell7 = row11.createCell(2) |
|
|
|
cell7.apply { |
|
|
|
cellFormula = "O${lastRowNum}" |
|
|
|
cellStyle.dataFormat = accountingStyle |
|
|
|
} |
|
|
|
|
|
|
|
return workbook |
|
|
|
} |
|
|
|
|
|
|
@@ -658,14 +960,14 @@ open class ReportService ( |
|
|
|
|
|
|
|
open fun getFinancialStatus(projectId: Long?): List<Map<String, Any>> { |
|
|
|
val sql = StringBuilder( |
|
|
|
"with cte_invoice as (select p.code, sum(i.issueAmount) as sumIssuedAmount , sum(i.paidAmount) as sumPaidAmount" |
|
|
|
+ "from invoice i" |
|
|
|
+ "left join project p on p.code = i.projectCode" |
|
|
|
+ "group by p.code" |
|
|
|
"with cte_invoice as (select p.code, sum(i.issueAmount) as sumIssuedAmount , sum(i.paidAmount) as sumPaidAmount " |
|
|
|
+ " from invoice i" |
|
|
|
+ " left join project p on p.code = i.projectCode" |
|
|
|
+ " group by p.code" |
|
|
|
+ ")" |
|
|
|
+ " Select p.code, p.description, c.name, t2.name, p.planStart , p.planEnd , p.expectedTotalFee ," |
|
|
|
+ " s.name , IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.hourlyRate," |
|
|
|
+ " cte_i.sumIssuedAmount, cte_i.sumPaidAmount" |
|
|
|
+ " Select p.code, p.description, c.name as client, t2.name as teamLead, p.planStart , p.planEnd , p.expectedTotalFee ," |
|
|
|
+ " s.name as staff , IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.hourlyRate," |
|
|
|
+ " IFNULL(cte_i.sumIssuedAmount, 0) as sumIssuedAmount, IFNULL(cte_i.sumPaidAmount, 0) as sumPaidAmount " |
|
|
|
+ " from timesheet t" |
|
|
|
+ " left join project_task pt on pt.id = t.projectTaskId" |
|
|
|
+ " left join project p ON p.id = pt.project_id" |
|
|
|