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

PANDL Report

tags/Baseline_30082024_BACKEND_UAT
MSI\2Fi 1 год назад
Родитель
Сommit
bfe70fbc8c
4 измененных файлов: 483 добавлений и 13 удалений
  1. +455
    -7
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  2. +21
    -6
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  3. +7
    -0
      src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt
  4. Двоичные данные
      src/main/resources/templates/report/AR07_Project P&L Report v02.xlsx

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

@@ -1,18 +1,13 @@
package com.ffii.tsms.modules.report.service

import com.ffii.core.support.JdbcDao
import com.ffii.tsms.modules.data.entity.Customer
import com.ffii.tsms.modules.data.entity.Salary
import com.ffii.tsms.modules.data.entity.Staff
import com.ffii.tsms.modules.data.entity.Team
import com.ffii.tsms.modules.data.entity.Customer
import com.ffii.tsms.modules.project.entity.Invoice
import com.ffii.tsms.modules.project.entity.Project
import com.ffii.tsms.modules.timesheet.entity.Leave
import com.ffii.tsms.modules.timesheet.entity.Timesheet
import com.ffii.tsms.modules.timesheet.entity.projections.MonthlyLeave
import com.ffii.tsms.modules.timesheet.entity.projections.ProjectMonthlyHoursWithDate
import com.ffii.tsms.modules.timesheet.entity.projections.TimesheetHours
import com.ffii.tsms.modules.timesheet.web.models.LeaveEntry
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.apache.poi.ss.usermodel.*
@@ -25,8 +20,8 @@ import org.springframework.stereotype.Service
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.math.BigDecimal
import java.sql.Time
import java.time.LocalDate
import java.time.YearMonth
import java.time.format.DateTimeFormatter
import java.util.*

@@ -40,6 +35,7 @@ open class ReportService(
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd")
private val FORMATTED_TODAY = LocalDate.now().format(DATE_FORMATTER)

private val PandL_REPORT = "templates/report/AR07_Project P&L Report v02.xlsx"
private val FINANCIAL_STATUS_REPORT = "templates/report/EX01_Financial Status Report.xlsx"
private val PROJECT_CASH_FLOW_REPORT = "templates/report/EX02_Project Cash Flow Report.xlsx"
private val MONTHLY_WORK_HOURS_ANALYSIS_REPORT = "templates/report/AR08_Monthly Work Hours Analysis Report.xlsx"
@@ -1177,4 +1173,456 @@ open class ReportService(
return jdbcDao.queryForList(sql.toString(), args)
}

open fun getManhoursSpent(projectId: Long, startMonth: String, endMonth: String): List<Map<String, Any>>{
val sql = StringBuilder(
" with cte_timesheet as ("
+ " Select p.code, s.name as staff, IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.salaryPoint, s2.hourlyRate, t.staffId,"
+ " t.recordDate"
+ " from timesheet t"
+ " left join project_task pt on pt.id = t.projectTaskId"
+ " left join project p ON p.id = pt.project_id"
+ " left join staff s on s.id = t.staffId"
+ " left join salary s2 on s.salaryId = s2.salaryPoint"
+ " left join team t2 on t2.id = s.teamId"
+ " ),"
+ " 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 as client, concat(t.code, \" - \", t.name) as teamLead,"
+ " IFNULL(cte_ts.normalConsumed, 0) as normalConsumed, IFNULL(cte_ts.otConsumed, 0) as otConsumed, DATE_FORMAT(cte_ts.recordDate, '%Y-%m') as recordDate, "
+ " IFNULL(cte_ts.salaryPoint, 0) as salaryPoint, "
+ " IFNULL(cte_ts.hourlyRate, 0) as hourlyRate, IFNULL(cte_i.sumIssuedAmount, 0) as sumIssuedAmount, IFNULL(cte_i.sumPaidAmount, 0) as sumPaidAmount,"
+ " s.name, s.staffId, g.code as gradeCode, g.name as gradeName, t2.code as teamCode, t2.name as teamName"
+ " from project p"
+ " left join cte_timesheet cte_ts on p.code = cte_ts.code"
+ " left join customer c on c.id = p.customerId"
+ " left join tsmsdb.team t on t.teamLead = p.teamLead"
+ " left join cte_invoice cte_i on cte_i.code = p.code"
+ " left join staff s on s.id = cte_ts.staffId"
+ " left join grade g on g.id = s.gradeId"
+ " left join team t2 on t2.id = s.teamId"
+ " where p.deleted = false"
+ " and (DATE_FORMAT(cte_ts.recordDate, \'%Y-%m\') >= :startMonth and DATE_FORMAT(cte_ts.recordDate, \'%Y-%m\') <= :endMonth)"
+ " and p.id = :projectId"
+ " order by g.code, s.name, cte_ts.recordDate"
)

val args = mapOf(
"projectId" to projectId,
"startMonth" to startMonth,
"endMonth" to endMonth,
)

val manHoursSpent = jdbcDao.queryForList(sql.toString(), args)

val projectCodeSql = StringBuilder(
"select p.code, p.description from project p where p.deleted = false and p.id = :projectId"
)

val args2 = mapOf(
"projectId" to projectId,
)

val projectsCode = jdbcDao.queryForMap(projectCodeSql.toString(), args).get()

val otFactor = BigDecimal(1).toDouble()

var tempList = mutableListOf<Map<String, Any>>()

val info: MutableMap<String, Any?> = mutableMapOf(
"reportGenerationDate" to LocalDate.now().toString(),
"startMonth" to startMonth,
"endMonth" to endMonth,
"code" to projectsCode["code"],
"description" to projectsCode["description"],
)

val staffInfoList = mutableListOf<Map<String, Any>>()
println("manHoursSpent------- ${manHoursSpent}")
for(item in manHoursSpent){
if(info["teamLead"] != item.getValue("teamLead")){
info["teamLead"] = item.getValue("teamLead")
}
if(info["client"] != item.getValue("client")){
info["client"] = item.getValue("client")
}
if(info["code"] != item.getValue("code")){
info["code"] = item.getValue("code")
}
if(info["code"] == item["code"] && "paidAmount" !in info){
info["paidAmount"] = item.getValue("sumPaidAmount")
}
if(info["description"] != item.getValue("description")){
info["description"] = item.getValue("description")
}
val hourlyRate = (item.getValue("hourlyRate") as BigDecimal).toDouble()
if(!staffInfoList.any { it["staffId"] == item["staffId"] && it["name"] == item["name"]}){
staffInfoList.add(
mapOf(
"staffId" to item.getValue("staffId"),
"name" to item.getValue("name"),
"team" to "${item.getValue("teamCode")} - ${item.getValue("teamName")}",
"grade" to item.getValue("gradeCode"),
"salaryPoint" to item.getValue("salaryPoint"),
"hourlyRate" to hourlyRate,
"hourlySpent" to mutableListOf(
mapOf(
"recordDate" to item.getValue("recordDate"),
"normalConsumed" to item.getValue("normalConsumed"),
"otConsumed" to item.getValue("otConsumed"),
"totalManHours" to item.getValue("normalConsumed") as Double + item.getValue("otConsumed") as Double,
"manhourExpenditure" to (hourlyRate * item.getValue("normalConsumed") as Double )
+ (hourlyRate * item.getValue("otConsumed") as Double * otFactor)
)
)
)
)
}else{
val existingMap = staffInfoList.find { it.containsValue(item.getValue("staffId")) }!!
val updatedMap = existingMap.toMutableMap()
val hourlySpentList = updatedMap["hourlySpent"] as MutableList<Map<String, Any>>
val existingRecord = hourlySpentList.find { it["recordDate"] == item.getValue("recordDate") }
if (existingRecord != null) {
val existingIndex = hourlySpentList.indexOf(existingRecord)
val updatedRecord = existingRecord.toMutableMap()
updatedRecord["normalConsumed"] = (updatedRecord["normalConsumed"] as Double) + item.getValue("normalConsumed") as Double
updatedRecord["otConsumed"] = (updatedRecord["otConsumed"] as Double) + item.getValue("otConsumed") as Double
updatedRecord["totalManHours"] = (updatedRecord["totalManHours"] as Double) + item.getValue("normalConsumed") as Double + item.getValue("otConsumed") as Double
updatedRecord["manhourExpenditure"] = (updatedRecord["manhourExpenditure"] as Double) + (hourlyRate * item.getValue("normalConsumed") as Double) + (hourlyRate * item.getValue("otConsumed") as Double * otFactor)
hourlySpentList[existingIndex] = updatedRecord
} else {
hourlySpentList.add(
mapOf(
"recordDate" to item.getValue("recordDate"),
"normalConsumed" to item.getValue("normalConsumed"),
"otConsumed" to item.getValue("otConsumed"),
"totalManHours" to item.getValue("normalConsumed") as Double + item.getValue("otConsumed") as Double,
"manhourExpenditure" to (hourlyRate * item.getValue("normalConsumed") as Double)
+ (hourlyRate * item.getValue("otConsumed") as Double * otFactor)
)
)
}
updatedMap["hourlySpent"] = hourlySpentList
val updatedIndex = staffInfoList.indexOf(existingMap)
staffInfoList[updatedIndex] = updatedMap
}
}
println("staffInfoList----------------- $staffInfoList")
println("info----------------- $info")

tempList.add(mapOf("info" to info))
tempList.add(mapOf("staffInfoList" to staffInfoList))

println("Only Staff Info List --------------------- ${ tempList.first() { it.containsKey("staffInfoList") }["staffInfoList"]}")
println("tempList----------------- $tempList")

return tempList
}

fun genPandLReport(projectId: Long, startMonth: String, endMonth: String): ByteArray {
val manhoursSpentList = getManhoursSpent(projectId, startMonth, endMonth)

val workbook: Workbook = createPandLReportWorkbook(PandL_REPORT, manhoursSpentList, startMonth, endMonth)

val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
workbook.write(outputStream)
workbook.close()

return outputStream.toByteArray()
}

fun createPandLReportWorkbook(
templatePath: String,
manhoursSpent: List<Map<String, Any>>,
startMonth: String,
endMonth: String
): Workbook{
val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream
val workbook: Workbook = XSSFWorkbook(templateInputStream)

val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)")

val startDate = YearMonth.parse(startMonth, DateTimeFormatter.ofPattern("yyyy-MM"))
val convertStartMonth = YearMonth.of(startDate.year, startDate.month)
val endDate = YearMonth.parse(endMonth, DateTimeFormatter.ofPattern("yyyy-MM"))
val convertEndMonth = YearMonth.of(endDate.year, endDate.month)

val monthRange: MutableList<Map<String, Any>> = ArrayList()
var currentDate = startDate
while (!currentDate.isAfter(endDate)) {
monthRange.add(
mapOf(
"display" to currentDate.month.name.substring(0, 3),
"date" to currentDate
)
)
currentDate = currentDate.plusMonths(1)
}

val monthFormat = DateTimeFormatter.ofPattern("MMM yyyy", Locale.ENGLISH)

val info:Map<String, Any> = manhoursSpent.first() { it.containsKey("info") }["info"] as Map<String, Any>
val staffInfoList: List<Map<String, Any>> = manhoursSpent.first() { it.containsKey("staffInfoList") }["staffInfoList"] as List<Map<String, Any>>

if (staffInfoList.isEmpty()){
val sheet = workbook.getSheetAt(0)
var rowNum = 0
rowNum = 1
val row1: Row = sheet.getRow(rowNum)
val row1Cell = row1.getCell(1)
row1Cell.setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")).toString())

rowNum = 2
val row2: Row = sheet.getRow(rowNum)
val row2Cell = row2.getCell(1)
row2Cell.setCellValue("${convertStartMonth.format(monthFormat)} - ${convertEndMonth.format(monthFormat)}")

rowNum = 3
val row3: Row = sheet.getRow(rowNum)
val row3Cell = row3.getCell(1)
row3Cell.setCellValue(info.getValue("code").toString())

rowNum = 4
val row4: Row = sheet.getRow(rowNum)
val row4Cell = row4.getCell(1)
row4Cell.setCellValue(info.getValue("description").toString())

rowNum = 5
val row5: Row = sheet.getRow(rowNum)
val row5Cell = row5.getCell(1)
row5Cell.setCellValue("-")

rowNum = 6
val row6: Row = sheet.getRow(rowNum)
val row6Cell = row6.getCell(1)
row6Cell.setCellValue("-")

return workbook
}

val sheet = workbook.getSheetAt(0)
var rowNum = 0
rowNum = 1
val row1: Row = sheet.getRow(rowNum)
val row1Cell = row1.getCell(1)
row1Cell.setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")).toString())

rowNum = 2
val row2: Row = sheet.getRow(rowNum)
val row2Cell = row2.getCell(1)
row2Cell.setCellValue("${convertStartMonth.format(monthFormat)} - ${convertEndMonth.format(monthFormat)}")

rowNum = 3
val row3: Row = sheet.getRow(rowNum)
val row3Cell = row3.getCell(1)
row3Cell.setCellValue(info.getValue("code").toString())

rowNum = 4
val row4: Row = sheet.getRow(rowNum)
val row4Cell = row4.getCell(1)
row4Cell.setCellValue(info.getValue("description").toString())

rowNum = 5
val row5: Row = sheet.getRow(rowNum)
val row5Cell = row5.getCell(1)
row5Cell.setCellValue(info.getValue("teamLead").toString())

rowNum = 6
val row6: Row = sheet.getRow(rowNum)
val row6Cell = row6.getCell(1)
row6Cell.setCellValue(info.getValue("client").toString())

rowNum = 9
val row9: Row = sheet.getRow(rowNum)
val row9Cell = row9.getCell(3)
row9Cell.setCellValue("${convertStartMonth.format(monthFormat)} - ${convertEndMonth.format(monthFormat)}")

rowNum = 10
for(staff in staffInfoList){
// val row: Row = sheet.getRow(rowNum++) ?: sheet.createRow(rowNum++)
val row: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val staffCell = row.getCell(0) ?: row.createCell(0)
staffCell.apply {
setCellValue("${staff.getValue("staffId")} - ${staff.getValue("name")}")
}

val gradeCell = row.getCell(1) ?: row.createCell(1)
gradeCell.apply {
setCellValue("G${staff.getValue("grade")}")
}
CellUtil.setAlignment(gradeCell, HorizontalAlignment.CENTER);

val salaryPointCell = row.getCell(2) ?: row.createCell(2)
salaryPointCell.apply {
setCellValue((staff.getValue("salaryPoint") as Long).toDouble())
}
CellUtil.setAlignment(salaryPointCell, HorizontalAlignment.CENTER);

sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, 3, 4))
val hourlyRateCell = row.getCell(3) ?: row.createCell(3)
hourlyRateCell.apply {
setCellValue((staff.getValue("hourlyRate") as Double))
}
CellUtil.setAlignment(hourlyRateCell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(hourlyRateCell, VerticalAlignment.CENTER);

sheet.setRowBreak(rowNum++);
}
rowNum += 2
val titleRow = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
sheet.addMergedRegion(CellRangeAddress(rowNum, rowNum, 0, monthRange.size+3))
val titleCell = titleRow.getCell(0) ?: titleRow.createCell(0)
titleCell.apply {
setCellValue("Manhours Spent per Month")
}
CellUtil.setAlignment(titleCell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(titleCell, VerticalAlignment.CENTER);


rowNum += 1
val manhoursSpentRow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val staffCell = manhoursSpentRow.getCell(0) ?: manhoursSpentRow.createCell(0)
staffCell.apply {
setCellValue("Staff No. and Name")
}
CellUtil.setAlignment(staffCell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(staffCell, VerticalAlignment.CENTER);
CellUtil.setCellStyleProperty(staffCell,"borderBottom", BorderStyle.THIN)

val teamCell = manhoursSpentRow.getCell(1) ?: manhoursSpentRow.createCell(1)
teamCell.apply {
setCellValue("Team")
}
CellUtil.setAlignment(teamCell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(teamCell, VerticalAlignment.CENTER);
CellUtil.setCellStyleProperty(teamCell,"borderBottom", BorderStyle.THIN)

for ((column, month) in monthRange.withIndex()){
val row: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val monthCell = row.getCell(2+column) ?: row.createCell(2+column)
monthCell.apply {
setCellValue(month.getValue("display").toString())
}
CellUtil.setAlignment(monthCell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(monthCell, VerticalAlignment.CENTER);
CellUtil.setCellStyleProperty(monthCell,"borderBottom", BorderStyle.THIN)
}
val monthColumnEnd = monthRange.size
val row: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val manhourCell = row.getCell(2+monthColumnEnd) ?: row.createCell(2+monthColumnEnd)
manhourCell.apply {
setCellValue("Manhour Sub-total")
}
CellUtil.setAlignment(manhourCell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(manhourCell, VerticalAlignment.CENTER);
CellUtil.setCellStyleProperty(manhourCell, CellUtil.WRAP_TEXT, true)
CellUtil.setCellStyleProperty(manhourCell,"borderBottom", BorderStyle.THIN)

val manhourECell = row.getCell(2+monthColumnEnd+1) ?: row.createCell(2+monthColumnEnd+1)
manhourECell.apply {
setCellValue("Manhour Expenditure (HKD)")
}
CellUtil.setAlignment(manhourECell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(manhourECell, VerticalAlignment.CENTER);
CellUtil.setCellStyleProperty(manhourECell, CellUtil.WRAP_TEXT, true)
CellUtil.setCellStyleProperty(manhourECell,"borderBottom", BorderStyle.THIN)
sheet.setColumnWidth(2+monthColumnEnd+1, 20*256)


// Manhour Spent LOOP
println("monthRange--------------- ${monthRange}\n")
rowNum += 1
for(staff in staffInfoList){
val row: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val staffCell = row.getCell(0) ?: row.createCell(0)
var manhourExpenditure = 0.0
staffCell.apply {
setCellValue("${staff.getValue("staffId")} - ${staff.getValue("name")}")
}

val teamCell = row.getCell(1) ?: row.createCell(1)
teamCell.apply {
setCellValue(staff.getValue("team").toString())
}
CellUtil.setCellStyleProperty(teamCell, CellUtil.WRAP_TEXT, true)

for(i in 0 until monthRange.size){
val cell = row.getCell(i+2) ?: row.createCell(i+2)
cell.setCellValue(0.0)
for(hourlySpent in staff.getValue("hourlySpent") as List<Map<String, Any>>){
cell.apply {
if (hourlySpent.getValue("recordDate") == monthRange[i].getValue("date").toString()) {
setCellValue(hourlySpent.getValue("totalManHours") as Double)
manhourExpenditure += hourlySpent.getValue("manhourExpenditure") as Double
}
cellStyle.dataFormat = accountingStyle
}
}
CellUtil.setAlignment(cell, HorizontalAlignment.CENTER);
CellUtil.setVerticalAlignment(cell, VerticalAlignment.CENTER);
}

val manhourCell = row.getCell(2+monthRange.size) ?: row.createCell(2+monthRange.size)
manhourCell.apply {
cellFormula="SUM(C${rowNum+1}:${getColumnAlphabet(1+monthRange.size)}${rowNum+1})"
cellStyle.dataFormat = accountingStyle
}

val manhourECell = row.getCell(3+monthRange.size) ?: row.createCell(3+monthRange.size)
manhourECell.apply {
setCellValue(manhourExpenditure)
cellStyle.dataFormat = accountingStyle
}
sheet.setRowBreak(rowNum++);
}

rowNum += 3
val totalRevenueRow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val totalRevenueTitleCell = totalRevenueRow.getCell(0) ?: totalRevenueRow.createCell(0)
totalRevenueTitleCell.apply {
setCellValue("Total Revenue (HKD)")
}
val totalRevenueCell = totalRevenueRow.getCell(1) ?: totalRevenueRow.createCell(1)
totalRevenueCell.apply {
setCellValue((info.getValue("paidAmount") as BigDecimal).toDouble())
cellStyle.dataFormat = accountingStyle
}

rowNum += 1
val totalManhourERow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val startRow = 15+staffInfoList.size
val lastColumnIndex = getColumnAlphabet(3+monthRange.size)
val totalManhourETitleCell = totalManhourERow.getCell(0) ?: totalManhourERow.createCell(0)
totalManhourETitleCell.apply {
setCellValue("Total Manhour Expenditure (HKD)")
}
val totalManhourECell = totalManhourERow.getCell(1) ?: totalManhourERow.createCell(1)
totalManhourECell.apply {
cellFormula = "SUM(${lastColumnIndex}${startRow}:${lastColumnIndex}${startRow+staffInfoList.size})"
cellStyle.dataFormat = accountingStyle
}
CellUtil.setCellStyleProperty(totalManhourETitleCell,"borderBottom", BorderStyle.THIN)
CellUtil.setCellStyleProperty(totalManhourECell,"borderBottom", BorderStyle.THIN)

rowNum += 1
val pandlRow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val panlCellTitle = pandlRow.getCell(0) ?: pandlRow.createCell(0)
panlCellTitle.apply {
setCellValue("Profit / (Loss)")
}
val panlCell = pandlRow.getCell(1) ?: pandlRow.createCell(1)
panlCell.apply {
cellFormula = "B${rowNum-1}-B${rowNum}"
cellStyle.dataFormat = accountingStyle
}
CellUtil.setCellStyleProperty(panlCellTitle,"borderBottom", BorderStyle.DOUBLE)
CellUtil.setCellStyleProperty(panlCell,"borderBottom", BorderStyle.DOUBLE)

return workbook
}

}

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

@@ -6,10 +6,6 @@ import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo
import com.ffii.tsms.modules.project.entity.*
import com.ffii.tsms.modules.report.service.ReportService
import com.ffii.tsms.modules.project.service.InvoiceService
import com.ffii.tsms.modules.report.web.model.FinancialStatusReportRequest
import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest
import com.ffii.tsms.modules.report.web.model.StaffMonthlyWorkHourAnalysisReportRequest
import com.ffii.tsms.modules.report.web.model.LateStartReportRequest
import com.ffii.tsms.modules.project.entity.ProjectRepository
import com.ffii.tsms.modules.timesheet.entity.LeaveRepository
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository
@@ -38,6 +34,7 @@ import com.ffii.tsms.modules.data.entity.CustomerRepository
import org.springframework.data.jpa.repository.JpaRepository
import com.ffii.tsms.modules.data.entity.Customer
import com.ffii.tsms.modules.project.entity.Project
import com.ffii.tsms.modules.report.web.model.*

@RestController
@RequestMapping("/reports")
@@ -165,11 +162,29 @@ fun downloadLateStartReport(@RequestBody @Valid request: LateStartReportRequest)
.contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
.body(ByteArrayResource(reportResult))
}

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

// For API TESTING
@GetMapping("/manHoursSpent/{id}")
fun getManhoursSpent(@PathVariable id: Long): List<Map<String, Any>> {
return excelReportService.getManhoursSpent(id, "2024-05", "2025-05")
}

// P&L Report
@PostMapping("/projectpandlreport")
@Throws(ServletRequestBindingException::class, IOException::class)
fun getProjectPandLReport(@RequestBody @Valid request: PandLReportRequest): ResponseEntity<Resource> {
println(request)
val reportResult: ByteArray = excelReportService.genPandLReport(request.projectId, request.startMonth, request.endMonth)

return ResponseEntity.ok()
.header("filename", "Project P&L Report - " + LocalDate.now() + ".xlsx")
.body(ByteArrayResource(reportResult))
}

}

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

@@ -6,6 +6,13 @@ import java.time.YearMonth
data class FinancialStatusReportRequest (
val teamLeadId: Long
)

data class PandLReportRequest (
val projectId: Long,
val startMonth: String,
val endMonth: String,
)

data class ProjectCashFlowReportRequest (
val projectId: Long,
val dateType: String, // "Date", "Month"


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


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