소스 검색

update project potential delay report

tags/Baseline_30082024_BACKEND_UAT
cyril.tsui 1 년 전
부모
커밋
fd0f7fc0cf
3개의 변경된 파일179개의 추가작업 그리고 147개의 파일을 삭제
  1. +172
    -144
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  2. +5
    -3
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  3. +2
    -0
      src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt

+ 172
- 144
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt 파일 보기

@@ -5,6 +5,7 @@ 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.project.entity.GradeAllocation
import com.ffii.tsms.modules.project.entity.Invoice
import com.ffii.tsms.modules.project.entity.Milestone
import com.ffii.tsms.modules.project.entity.Project
@@ -32,6 +33,7 @@ import java.sql.Time
import java.time.LocalDate
import java.time.YearMonth
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.*
import kotlin.jvm.optionals.getOrElse

@@ -56,7 +58,8 @@ open class ReportService(
private val SALART_LIST_TEMPLATE = "templates/report/Salary Template.xlsx"
private val LATE_START_REPORT = "templates/report/AR01_Late Start Report v01.xlsx"
private val RESOURCE_OVERCONSUMPTION_REPORT = "templates/report/AR03_Resource Overconsumption.xlsx"
private val COMPLETE_PROJECT_OUTSTANDING_RECEIVABLE = "templates/report/AR06_Project Completion Report with Outstanding Accounts Receivable v02.xlsx"
private val COMPLETE_PROJECT_OUTSTANDING_RECEIVABLE =
"templates/report/AR06_Project Completion Report with Outstanding Accounts Receivable v02.xlsx"
private val COMPLETION_PROJECT = "templates/report/AR05_Project Completion Report.xlsx"

// ==============================|| GENERATE REPORT ||============================== //
@@ -145,6 +148,8 @@ open class ReportService(
searchedClient: String,
projects: List<Project>,
timesheets: List<Timesheet>,
numberOfDays: Int,
projectCompletion: Int,
): ByteArray {
// Generate the Excel report with query results
val workbook: Workbook = createProjectPotentialDelayReport(
@@ -152,6 +157,8 @@ open class ReportService(
searchedClient,
projects,
timesheets,
numberOfDays,
projectCompletion,
PROJECT_POTENTIAL_DELAY_REPORT
)

@@ -209,6 +216,7 @@ open class ReportService(

return outputStream.toByteArray()
}

@Throws(IOException::class)
fun generateProjectCompletionReport(
args: MutableMap<String, Any>,
@@ -811,6 +819,8 @@ open class ReportService(
searchedClient: String,
projects: List<Project>,
timesheets: List<Timesheet>,
numberOfDays: Int,
projectCompletion: Int,
templatePath: String,
): Workbook {
// please create a new function for each report template
@@ -897,35 +907,36 @@ open class ReportService(
}

project.milestones.forEach { milestone: Milestone ->
logger.info(milestone.id)

val tempRow = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex)
rowIndex++

tempRow.apply {
createCell(7).apply {
setCellValue(milestone.taskGroup?.name ?: "N/A")
}

createCell(8).apply {
setCellValue(milestone.endDate?.format(DATE_FORMATTER) ?: "N/A")
}

createCell(9).apply {
cellStyle.dataFormat = workbook.createDataFormat().getFormat("0.00%")
val manHoursSpent = groupedTimesheets[Pair(project.id, milestone.id)]?.sum() ?: 0.0
val resourceUtilization = manHoursSpent / (milestone.stagePercentAllocation!! / 100 * project.totalManhour!!)
// logger.info(project.name + " : " + milestone.taskGroup?.name + " : " + ChronoUnit.DAYS.between(LocalDate.now(), milestone.endDate))
// logger.info(numberOfDays)
if (ChronoUnit.DAYS.between(LocalDate.now(), milestone.endDate) <= numberOfDays.toLong() && resourceUtilization <= projectCompletion.toDouble() / 100.0) {
val tempRow = sheet.getRow(rowIndex) ?: sheet.createRow(rowIndex)
rowIndex++

tempRow.apply {
createCell(7).apply {
setCellValue(milestone.taskGroup?.name ?: "N/A")
}

if (groupedTimesheets.containsKey(Pair(project.id, milestone.id))) {
val manHoursSpent = groupedTimesheets[Pair(project.id, milestone.id)]!!.sum()
createCell(8).apply {
setCellValue(milestone.endDate?.format(DATE_FORMATTER) ?: "N/A")
}

logger.info("manHoursSpent: $manHoursSpent")
logger.info("milestone.stagePercentAllocation: " + milestone.stagePercentAllocation)
logger.info("project.totalManhour: " + project.totalManhour)
createCell(9).apply {
cellStyle.dataFormat = workbook.createDataFormat().getFormat("0.00%")

val resourceUtilization =
manHoursSpent / (milestone.stagePercentAllocation!! / 100 * project.totalManhour!!)
setCellValue(resourceUtilization ?: 0.0)
} else {
setCellValue(0.0)
// if (groupedTimesheets.containsKey(Pair(project.id, milestone.id))) {
// val manHoursSpent = groupedTimesheets[Pair(project.id, milestone.id)]!!.sum()
//
// val resourceUtilization =
// manHoursSpent / (milestone.stagePercentAllocation!! / 100 * project.totalManhour!!)
setCellValue(resourceUtilization)
// } else {
// setCellValue(0.0)
// }
}
}
}
@@ -1259,6 +1270,7 @@ open class ReportService(

return workbook
}

private fun createProjectCompletionReport(
args: MutableMap<String, Any>,
result: List<Map<String, Any>>,
@@ -1291,10 +1303,11 @@ open class ReportService(
tempCell.setCellValue((index + 1).toDouble())
val keys = obj.keys.toList()
keys.forEachIndexed { keyIndex, key ->
tempCell = sheet.getRow(rowIndex).getCell(columnIndex + keyIndex + 1) ?: sheet.getRow(rowIndex).createCell(columnIndex + keyIndex + 1)
tempCell = sheet.getRow(rowIndex).getCell(columnIndex + keyIndex + 1) ?: sheet.getRow(rowIndex)
.createCell(columnIndex + keyIndex + 1)
when (obj[key]) {
is Double -> tempCell.setCellValue(obj[key] as Double)
else -> tempCell.setCellValue(obj[key] as String )
else -> tempCell.setCellValue(obj[key] as String)
}
}
rowIndex++
@@ -1516,107 +1529,111 @@ open class ReportService(
)
return jdbcDao.queryForList(sql.toString(), args)
}

open fun getProjectCompletionReport(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("select"
+ " result.code, "
+ " result.name, "
+ " result.teamCode, "
+ " result.custCode, " )
val sql = StringBuilder(
"select"
+ " result.code, "
+ " result.name, "
+ " result.teamCode, "
+ " result.custCode, "
)
if (args.get("outstanding") as Boolean) {
sql.append(" result.projectFee - (result.totalBudget - COALESCE(i.issueAmount , 0) + COALESCE(i.issueAmount, 0) - COALESCE(i.paidAmount, 0)) as `Receivable Remained`, ")
}
sql.append(
" DATE_FORMAT(result.actualEnd, '%d/%m/%Y') as actualEnd "
+ " from ( "
+ " SELECT "
+ " pt.project_id, "
+ " min(p.code) as code, "
+ " min(p.name) as name, "
+ " min(t.code) as teamCode, "
+ " min(c.code) as custCode, "
+ " min(p.actualEnd) as actualEnd, "
+ " min(p.expectedTotalFee) as projectFee, "
+ " sum(COALESCE(tns.totalConsumed*sal.hourlyRate, 0)) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " LEFT JOIN team te on s.teamId = te.id "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " order by t.staffId "
+ " ) AS tns "
+ " right join project_task pt ON tns.taskId = pt.id "
+ " left join project p on p.id = pt.project_id "
+ " left JOIN staff s ON p.teamLead = s.id "
+ " left join salary sal on s.salaryId = sal.salaryPoint "
+ " left JOIN team t ON s.teamId = t.id "
+ " left join customer c on c.id = p.customerId "
+ " where p.deleted = false "
+ " and p.status = 'Completed' "
+ " and p.actualEnd BETWEEN :startDate and :endDate "
+ " group by pt.project_id "
+ " ) as result "
+ " left join invoice i on result.code = i.projectCode "
+ " order by result.actualEnd "
+ " from ( "
+ " SELECT "
+ " pt.project_id, "
+ " min(p.code) as code, "
+ " min(p.name) as name, "
+ " min(t.code) as teamCode, "
+ " min(c.code) as custCode, "
+ " min(p.actualEnd) as actualEnd, "
+ " min(p.expectedTotalFee) as projectFee, "
+ " sum(COALESCE(tns.totalConsumed*sal.hourlyRate, 0)) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " LEFT JOIN team te on s.teamId = te.id "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " order by t.staffId "
+ " ) AS tns "
+ " right join project_task pt ON tns.taskId = pt.id "
+ " left join project p on p.id = pt.project_id "
+ " left JOIN staff s ON p.teamLead = s.id "
+ " left join salary sal on s.salaryId = sal.salaryPoint "
+ " left JOIN team t ON s.teamId = t.id "
+ " left join customer c on c.id = p.customerId "
+ " where p.deleted = false "
+ " and p.status = 'Completed' "
+ " and p.actualEnd BETWEEN :startDate and :endDate "
+ " group by pt.project_id "
+ " ) as result "
+ " left join invoice i on result.code = i.projectCode "
+ " order by result.actualEnd "
)

return jdbcDao.queryForList(sql.toString(), args)
}

open fun getProjectResourceOverconsumptionReport(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("WITH teamNormalConsumed AS ("
+ " SELECT "
+ " s.teamId, "
+ " pt.project_id, "
+ " SUM(tns.totalConsumed) AS totalConsumed, "
+ " sum(tns.totalBudget) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * min(sal.hourlyRate) as totalBudget "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " left join salary sal on sal.salaryPoint = s.salaryId "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " ) AS tns "
+ " INNER JOIN project_task pt ON tns.taskId = pt.id "
+ " JOIN staff s ON tns.staffId = s.id "
+ " JOIN team t ON s.teamId = t.id "
+ " GROUP BY teamId, project_id "
+ " ) "
+ " SELECT "
+ " p.code, "
+ " p.name, "
+ " t.code as team, "
+ " c.code as client, "
+ " p.expectedTotalFee * 0.8 as plannedBudget, "
+ " COALESCE(tns.totalBudget, 0) as actualConsumedBudget, "
+ " COALESCE(p.totalManhour, 0) as plannedManhour, "
+ " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour, "
+ " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate, "
+ " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate, "
+ " CASE "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1 "
+ " then 'Potential Overconsumption' "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1 "
+ " then 'Overconsumption' "
+ " else 'Within Budget' "
+ " END as status "
+ " FROM project p "
+ " LEFT JOIN team t ON p.teamLead = t.teamLead "
+ " LEFT JOIN staff s ON p.teamLead = s.id "
+ " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint "
+ " LEFT JOIN customer c ON p.customerId = c.id "
+ " left join teamNormalConsumed tns on tns.project_id = p.id "
+ " WHERE p.deleted = false "
+ " and p.status = 'On-going' "
val sql = StringBuilder(
"WITH teamNormalConsumed AS ("
+ " SELECT "
+ " s.teamId, "
+ " pt.project_id, "
+ " SUM(tns.totalConsumed) AS totalConsumed, "
+ " sum(tns.totalBudget) as totalBudget "
+ " FROM ( "
+ " SELECT "
+ " t.staffId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) as totalConsumed, "
+ " t.projectTaskId AS taskId, "
+ " sum(t.normalConsumed + COALESCE(t.otConsumed, 0)) * min(sal.hourlyRate) as totalBudget "
+ " FROM timesheet t "
+ " LEFT JOIN staff s ON t.staffId = s.id "
+ " left join salary sal on sal.salaryPoint = s.salaryId "
+ " GROUP BY t.staffId, t.projectTaskId "
+ " ) AS tns "
+ " INNER JOIN project_task pt ON tns.taskId = pt.id "
+ " JOIN staff s ON tns.staffId = s.id "
+ " JOIN team t ON s.teamId = t.id "
+ " GROUP BY teamId, project_id "
+ " ) "
+ " SELECT "
+ " p.code, "
+ " p.name, "
+ " t.code as team, "
+ " c.code as client, "
+ " p.expectedTotalFee * 0.8 as plannedBudget, "
+ " COALESCE(tns.totalBudget, 0) as actualConsumedBudget, "
+ " COALESCE(p.totalManhour, 0) as plannedManhour, "
+ " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour, "
+ " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate, "
+ " (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) as manhourConsumptionRate, "
+ " CASE "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit and (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) <= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit and (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) <= 1 "
+ " then 'Potential Overconsumption' "
+ " when (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= 1 "
+ " or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= 1 "
+ " then 'Overconsumption' "
+ " else 'Within Budget' "
+ " END as status "
+ " FROM project p "
+ " LEFT JOIN team t ON p.teamLead = t.teamLead "
+ " LEFT JOIN staff s ON p.teamLead = s.id "
+ " LEFT JOIN salary sa ON s.salaryId = sa.salaryPoint "
+ " LEFT JOIN customer c ON p.customerId = c.id "
+ " left join teamNormalConsumed tns on tns.project_id = p.id "
+ " WHERE p.deleted = false "
+ " and p.status = 'On-going' "
)
if (args != null) {
var statusFilter: String = ""
@@ -2109,10 +2126,10 @@ open class ReportService(
return workbook
}

fun getCostAndExpense(clientId: Long?, teamId: Long?): List<Map<String,Any?>>{
fun getCostAndExpense(clientId: Long?, teamId: Long?): 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,"
+ " 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"
@@ -2134,13 +2151,13 @@ open class ReportService(
+ " left join team t2 on t2.id = s.teamId"
+ " where ISNULL(p.code) = False"
)
if(clientId != null){
if (clientId != null) {
sql.append(
" and c.id = :clientId "
)
}

if(teamId != null){
if (teamId != null) {
sql.append(
" and p.teamLead = :teamId "
)
@@ -2159,9 +2176,9 @@ open class ReportService(
val queryList = jdbcDao.queryForList(sql.toString(), args)
val costAndExpenseList = mutableListOf<Map<String, Any?>>()

for(item in queryList){
for (item in queryList) {
val hourlyRate = (item.getValue("hourlyRate") as BigDecimal).toDouble()
if(item["code"] !in costAndExpenseList){
if (item["code"] !in costAndExpenseList) {
costAndExpenseList.add(
mapOf(
"code" to item["code"],
@@ -2169,22 +2186,27 @@ open class ReportService(
"client" to item["client"],
"teamLead" to item["teamLead"],
"budget" to item["expectedTotalFee"],
"totalManhours" to item["normalConsumed"] as Double + item["otConsumed"] as Double,
"manhourExpenditure" to (hourlyRate * item["normalConsumed"] as Double )
+ (hourlyRate * item["otConsumed"]as Double * otFactor)
"totalManhours" to item["normalConsumed"] as Double + item["otConsumed"] as Double,
"manhourExpenditure" to (hourlyRate * item["normalConsumed"] as Double)
+ (hourlyRate * item["otConsumed"] as Double * otFactor)
)
)
}else{
} else {
val existingMap = costAndExpenseList.find { it.containsValue(item["code"]) }!!
costAndExpenseList[costAndExpenseList.indexOf(existingMap)] = existingMap.toMutableMap().apply {
put("totalManhours", get("manhours") as Double + (item["normalConsumed"] as Double + item["otConsumed"] as Double))
put("manhourExpenditure", get("manhourExpenditure") as Double + ((hourlyRate * item["normalConsumed"] as Double )
+ (hourlyRate * item["otConsumed"]as Double * otFactor)))
put(
"totalManhours",
get("manhours") as Double + (item["normalConsumed"] as Double + item["otConsumed"] as Double)
)
put(
"manhourExpenditure",
get("manhourExpenditure") as Double + ((hourlyRate * item["normalConsumed"] as Double)
+ (hourlyRate * item["otConsumed"] as Double * otFactor))
)
}
}
}
val result = costAndExpenseList.map {
item ->
val result = costAndExpenseList.map { item ->
val budget = (item["budget"] as? Double)?.times(0.8) ?: 0.0
val budgetRemain = budget - (item["manhourExpenditure"] as? Double ?: 0.0)
val remainingPercent = (budgetRemain / budget)
@@ -2202,7 +2224,7 @@ open class ReportService(
teamId: Long?,
clientId: Long?,
budgetPercentage: Double?
): Workbook{
): Workbook {
val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream
val workbook: Workbook = XSSFWorkbook(templateInputStream)
@@ -2220,9 +2242,9 @@ open class ReportService(
rowNum = 2
val row2: Row = sheet.getRow(rowNum)
val row2Cell = row2.getCell(2)
if(teamId == null){
if (teamId == null) {
row2Cell.setCellValue("All")
}else{
} else {
val sql = StringBuilder(
" select t.id, t.code, t.name, concat(t.code, \" - \" ,t.name) as teamLead from team t where t.id = :teamId "
)
@@ -2233,9 +2255,9 @@ open class ReportService(
rowNum = 3
val row3: Row = sheet.getRow(rowNum)
val row3Cell = row3.getCell(2)
if(clientId == null){
if (clientId == null) {
row3Cell.setCellValue("All")
}else{
} else {
val sql = StringBuilder(
" select c.id, c.name from customer c where c.id = :clientId "
)
@@ -2245,15 +2267,15 @@ open class ReportService(


val filterList: List<Map<String, Any?>>
if(budgetPercentage != null){
if (budgetPercentage != null) {
filterList = costAndExpenseList.filter { ((it["budgetPercentage"] as? Double) ?: 0.0) > budgetPercentage }
}else{
} else {
filterList = costAndExpenseList
}


rowNum = 6
for(item in filterList){
for (item in filterList) {
val index = filterList.indexOf(item)
val row: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
val cell = row.getCell(0) ?: row.createCell(0)
@@ -2298,13 +2320,13 @@ open class ReportService(

val cell7 = row.getCell(7) ?: row.createCell(7)
cell7.apply {
cellFormula = "F${rowNum+1}-G${rowNum+1}"
cellFormula = "F${rowNum + 1}-G${rowNum + 1}"
}
CellUtil.setCellStyleProperty(cell7, "dataFormat", accountingStyle)

val cell8 = row.getCell(8) ?: row.createCell(8)
cell8.apply {
cellFormula = "H${rowNum+1}/F${rowNum+1}"
cellFormula = "H${rowNum + 1}/F${rowNum + 1}"
}
CellUtil.setCellStyleProperty(cell8, "dataFormat", percentStyle)

@@ -2314,11 +2336,17 @@ open class ReportService(
return workbook
}

fun genCostAndExpenseReport(request: costAndExpenseRequest): ByteArray{
fun genCostAndExpenseReport(request: costAndExpenseRequest): ByteArray {

val costAndExpenseList = getCostAndExpense(request.clientId, request.teamId)

val workbook: Workbook = createCostAndExpenseWorkbook(COSTANDEXPENSE_REPORT, costAndExpenseList, request.teamId, request.clientId, request.budgetPercentage)
val workbook: Workbook = createCostAndExpenseWorkbook(
COSTANDEXPENSE_REPORT,
costAndExpenseList,
request.teamId,
request.clientId,
request.budgetPercentage
)

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


+ 5
- 3
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt 파일 보기

@@ -39,6 +39,7 @@ import com.ffii.tsms.modules.report.web.model.*
import com.ffii.tsms.modules.timesheet.entity.Timesheet
import org.springframework.data.domain.Example
import org.springframework.data.domain.ExampleMatcher
import java.time.temporal.ChronoUnit

@RestController
@RequestMapping("/reports")
@@ -55,7 +56,8 @@ class ReportController(
private val leaveRepository: LeaveRepository,
private val teamService: TeamService,
private val customerService: CustomerService,
private val invoiceService: InvoiceService) {
private val invoiceService: InvoiceService, private val gradeAllocationRepository: GradeAllocationRepository
) {

@PostMapping("/fetchProjectsFinancialStatusReport")
@Throws(ServletRequestBindingException::class, IOException::class)
@@ -105,10 +107,10 @@ class ReportController(
}, matcher)

val projects = if (team == null) projectRepository.findAll(exampleQuery) else projectRepository.findAll(exampleQuery).filter { it.teamLead == team.staff }

val projectTasks = projectTaskRepository.findAllByProjectIn(projects)
val timesheets = timesheetRepository.findAllByProjectTaskIn(projectTasks)
val reportResult: ByteArray = excelReportService.generateProjectPotentialDelayReport(searchedTeam, searchedClient, projects, timesheets)

val reportResult: ByteArray = excelReportService.generateProjectPotentialDelayReport(searchedTeam, searchedClient, projects, timesheets, request.numberOfDays, request.projectCompletion)
return ResponseEntity.ok()
.header("filename", "Project Potential Delay Report - " + LocalDate.now() + ".xlsx")
.body(ByteArrayResource(reportResult))


+ 2
- 0
src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt 파일 보기

@@ -27,6 +27,8 @@ data class ProjectCashFlowReportRequest (
data class ProjectPotentialDelayReportRequest (
val teamId: String,
val clientId: String,
val numberOfDays: Int,
val projectCompletion: Int,
)

data class StaffMonthlyWorkHourAnalysisReportRequest (


불러오는 중...
취소
저장