Просмотр исходного кода

Financial Report Status

tags/Baseline_30082024_BACKEND_UAT
MSI\2Fi 1 год назад
Родитель
Сommit
c393a86921
4 измененных файлов: 332 добавлений и 23 удалений
  1. +14
    -14
      src/main/java/com/ffii/tsms/modules/data/entity/projections/FinancialStatusReportInfo.kt
  2. +311
    -9
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  3. +7
    -0
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  4. Двоичные данные
      src/main/resources/templates/report/EX01_Financial Status Report.xlsx

+ 14
- 14
src/main/java/com/ffii/tsms/modules/data/entity/projections/FinancialStatusReportInfo.kt Просмотреть файл

@@ -3,18 +3,18 @@ package com.ffii.tsms.modules.data.entity.projections
import java.math.BigDecimal
import java.time.LocalDate

interface FinancialStatusReportInfo {
val code: String
val description: String
val client: String
val teamLead: String
val planStart: LocalDate
val planEnd: LocalDate
val expectedTotalFee: BigDecimal
val staff: String
val normalConsumed: BigDecimal
val otConsumed: BigDecimal
val hourlyRate: BigDecimal
val sumIssuedAmount: BigDecimal
data class FinancialStatusReportInfo(
val code: String,
val description: String,
val client: String,
val teamLead: String,
val planStart: LocalDate,
val planEnd: LocalDate,
val expectedTotalFee: BigDecimal,
val staff: String,
val normalConsumed: BigDecimal,
val otConsumed: BigDecimal,
val hourlyRate: BigDecimal,
val sumIssuedAmount: BigDecimal,
val sumPaidAmount: BigDecimal
}
)

+ 311
- 9
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Просмотреть файл

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


+ 7
- 0
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt Просмотреть файл

@@ -1,6 +1,7 @@
package com.ffii.tsms.modules.report.web

import com.ffii.tsms.modules.data.entity.StaffRepository
import com.ffii.tsms.modules.data.entity.projections.FinancialStatusReportInfo
import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo
import com.ffii.tsms.modules.project.entity.*
import com.ffii.tsms.modules.report.service.ReportService
@@ -110,4 +111,10 @@ class ReportController(
.body(ByteArrayResource(reportBytes))
}

@GetMapping("/financialReport/{id}")
fun getFinancialReport(@PathVariable id: Long): List<Map<String, Any>> {
println(excelReportService.genFinancialStatusReport(id))
return excelReportService.getFinancialStatus(id)
}

}

Двоичные данные
src/main/resources/templates/report/EX01_Financial Status Report.xlsx Просмотреть файл


Загрузка…
Отмена
Сохранить