Browse Source

Merge branch 'master' of https://git.2fi-solutions.com/davidhui/TSMS-backend

tags/Baseline_30082024_BACKEND_UAT
MSI\2Fi 1 year ago
parent
commit
d87520f624
4 changed files with 100 additions and 41 deletions
  1. +79
    -31
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  2. +10
    -8
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  3. +11
    -2
      src/main/java/com/ffii/tsms/modules/timesheet/entity/projections/MonthlyHours.kt
  4. BIN
      src/main/resources/templates/report/AR08_Monthly Work Hours Analysis Report.xlsx

+ 79
- 31
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt View File

@@ -7,6 +7,9 @@ import com.ffii.tsms.modules.project.entity.Invoice
import com.ffii.tsms.modules.project.entity.Project import com.ffii.tsms.modules.project.entity.Project
import com.ffii.tsms.modules.timesheet.entity.Leave import com.ffii.tsms.modules.timesheet.entity.Leave
import com.ffii.tsms.modules.timesheet.entity.Timesheet 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 com.ffii.tsms.modules.timesheet.web.models.LeaveEntry
import org.apache.commons.logging.Log import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory import org.apache.commons.logging.LogFactory
@@ -20,6 +23,7 @@ import org.springframework.stereotype.Service
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.math.BigDecimal import java.math.BigDecimal
import java.sql.Time
import java.time.LocalDate import java.time.LocalDate
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
@@ -121,9 +125,8 @@ open class ReportService(
fun generateStaffMonthlyWorkHourAnalysisReport( fun generateStaffMonthlyWorkHourAnalysisReport(
month: LocalDate, month: LocalDate,
staff: Staff, staff: Staff,
timesheets: List<Timesheet>,
leaves: List<Leave>,
projectList: List<String>
timesheets: List<Map<String, Any>>,
leaves: List<Map<String, Any>>,
): ByteArray { ): ByteArray {
// Generate the Excel report with query results // Generate the Excel report with query results
val workbook: Workbook = createStaffMonthlyWorkHourAnalysisReport( val workbook: Workbook = createStaffMonthlyWorkHourAnalysisReport(
@@ -131,7 +134,6 @@ open class ReportService(
staff, staff,
timesheets, timesheets,
leaves, leaves,
projectList,
MONTHLY_WORK_HOURS_ANALYSIS_REPORT MONTHLY_WORK_HOURS_ANALYSIS_REPORT
) )


@@ -718,21 +720,35 @@ open class ReportService(
private fun createStaffMonthlyWorkHourAnalysisReport( private fun createStaffMonthlyWorkHourAnalysisReport(
month: LocalDate, month: LocalDate,
staff: Staff, staff: Staff,
timesheets: List<Timesheet>,
leaves: List<Leave>,
projectList: List<String>,
timesheets: List<Map<String, Any>>,
leaves: List<Map<String, Any>>,
templatePath: String, templatePath: String,
): Workbook { ): Workbook {
// val yearMonth = YearMonth.of(2022, 5) // May 2022
println("t $timesheets")
println("l $leaves")
println("p $projectList")
var projectList: List<String> = listOf()
println("----timesheets-----")
println(timesheets)
// result = timesheet record mapped
var result: Map<String, Any> = mapOf()
if (timesheets.isNotEmpty()) {
projectList = timesheets.map{ "${it["code"]}\n ${it["name"]}"}.toList()
result = timesheets.groupBy(
{ it["id"].toString() },
{ mapOf(
"date" to it["recordDate"],
"normalConsumed" to it["normalConsumed"],
"otConsumed" to it["otConsumed"],
) }
)
}
println("---result---")
println(result)
println("l $projectList")
val resource = ClassPathResource(templatePath) val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream val templateInputStream = resource.inputStream
val workbook: Workbook = XSSFWorkbook(templateInputStream) val workbook: Workbook = XSSFWorkbook(templateInputStream)


val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)")
val monthStyle = workbook.createDataFormat().getFormat("mmm, yyyy")
val monthStyle = workbook.createDataFormat().getFormat("MMM YYYY")
val dateStyle = workbook.createDataFormat().getFormat("dd/mm/yyyy") val dateStyle = workbook.createDataFormat().getFormat("dd/mm/yyyy")


val boldStyle = workbook.createCellStyle() val boldStyle = workbook.createCellStyle()
@@ -748,8 +764,6 @@ open class ReportService(


val sheet: Sheet = workbook.getSheetAt(0) val sheet: Sheet = workbook.getSheetAt(0)


// sheet.forceFormulaRecalculation = true; //Calculate formulas

var rowIndex = 1 // Assuming the location is in (1,2), which is the report date field var rowIndex = 1 // Assuming the location is in (1,2), which is the report date field
var columnIndex = 1 var columnIndex = 1


@@ -766,7 +780,6 @@ open class ReportService(
rowIndex = 2 rowIndex = 2
sheet.getRow(rowIndex).getCell(columnIndex).apply { sheet.getRow(rowIndex).getCell(columnIndex).apply {
setCellValue(month) setCellValue(month)
// cellStyle.setFont(boldStyle)


cellStyle.dataFormat = monthStyle cellStyle.dataFormat = monthStyle
} }
@@ -801,11 +814,9 @@ open class ReportService(
tempCell.setCellValue(dayInfo.date) tempCell.setCellValue(dayInfo.date)
tempCell.cellStyle = boldStyle tempCell.cellStyle = boldStyle
tempCell.cellStyle.dataFormat = dateStyle tempCell.cellStyle.dataFormat = dateStyle
// cellStyle.alignment = HorizontalAlignment.LEFT
tempCell = sheet.getRow(rowIndex).createCell(1) tempCell = sheet.getRow(rowIndex).createCell(1)
tempCell.setCellValue(dayInfo.weekday) tempCell.setCellValue(dayInfo.weekday)
tempCell.cellStyle = boldStyle tempCell.cellStyle = boldStyle
// cellStyle.alignment = HorizontalAlignment.LEFT
} }


rowIndex += 1 rowIndex += 1
@@ -827,10 +838,11 @@ open class ReportService(
var normalConsumed = 0.0 var normalConsumed = 0.0
var otConsumed = 0.0 var otConsumed = 0.0
var leaveHours = 0.0 var leaveHours = 0.0
// normalConsumed data
if (timesheets.isNotEmpty()) { if (timesheets.isNotEmpty()) {
timesheets.forEach { t -> timesheets.forEach { t ->
normalConsumed += t.normalConsumed!!
otConsumed += t.otConsumed ?: 0.0
normalConsumed += t["normalConsumed"] as Double
otConsumed += t["otConsumed"] as Double
} }
} }
tempCell = sheet.getRow(rowIndex).createCell(2) tempCell = sheet.getRow(rowIndex).createCell(2)
@@ -858,9 +870,10 @@ open class ReportService(
tempCell.cellStyle = boldStyle tempCell.cellStyle = boldStyle
CellUtil.setAlignment(tempCell, HorizontalAlignment.CENTER) CellUtil.setAlignment(tempCell, HorizontalAlignment.CENTER)
sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1))
// cal total leave hour
if (leaves.isNotEmpty()) { if (leaves.isNotEmpty()) {
leaves.forEach { l -> leaves.forEach { l ->
leaveHours += l.leaveHours!!
leaveHours += l["leaveHours"] as Double
} }
} }
tempCell = sheet.getRow(rowIndex).createCell(2) tempCell = sheet.getRow(rowIndex).createCell(2)
@@ -905,28 +918,30 @@ open class ReportService(
tempCell.setCellValue(0.0) tempCell.setCellValue(0.0)
tempCell.cellStyle.dataFormat = accountingStyle tempCell.cellStyle.dataFormat = accountingStyle
} }
timesheets.forEach { timesheet ->
dayInt = timesheet.recordDate!!.dayOfMonth
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex)
tempCell.setCellValue(timesheet.normalConsumed!!)

result.forEach{(id, list) ->
for (i in 0 until id.toInt()) {
val temp: List<Map<String, Any>> = list as List<Map<String, Any>>
temp.forEachIndexed { i, _ ->
dayInt = temp[i]["date"].toString().toInt()
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex)
tempCell.setCellValue(temp[i]["normalConsumed"] as Double)
}
}
} }
columnIndex++ columnIndex++
} }
} }
// dates
// leave hours data
if (leaves.isNotEmpty()) { if (leaves.isNotEmpty()) {
leaves.forEach { leave -> leaves.forEach { leave ->
for (i in 0 until rowSize) { for (i in 0 until rowSize) {
tempCell = sheet.getRow(8 + i).createCell(columnIndex) tempCell = sheet.getRow(8 + i).createCell(columnIndex)
tempCell.setCellValue(0.0) tempCell.setCellValue(0.0)
tempCell.cellStyle.dataFormat = accountingStyle tempCell.cellStyle.dataFormat = accountingStyle

} }
dayInt = leave.recordDate!!.dayOfMonth
dayInt = leave["recordDate"].toString().toInt()
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex) tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex)
tempCell.setCellValue(leave.leaveHours!!)

tempCell.setCellValue(leave["leaveHours"] as Double)
} }
} }
///////////////////////////////////////////////////////// Leave Hours //////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// Leave Hours ////////////////////////////////////////////////////////////////////
@@ -1126,4 +1141,37 @@ open class ReportService(
return jdbcDao.queryForList(sql.toString(), args) return jdbcDao.queryForList(sql.toString(), args)
} }


}
open fun getTimesheet(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
"SELECT"
+ " p.id,"
+ " p.name,"
+ " p.code,"
+ " CAST(DATE_FORMAT(t.recordDate, '%d') AS SIGNED) AS recordDate,"
+ " sum(t.normalConsumed) as normalConsumed,"
+ " IFNULL(sum(t.otConsumed), 0.0) as otConsumed"
+ " from timesheet t"
+ " left join project_task pt on t.projectTaskId = pt.id"
+ " left join project p on p.id = pt.project_id"
+ " where t.staffId = :staffId"
+ " group by p.id, t.recordDate"
+ " order by p.id, t.recordDate"
+ " and t.recordDate BETWEEN :startDate and :endDate"
)
return jdbcDao.queryForList(sql.toString(), args)
}
open fun getLeaves(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
" SELECT "
+ " sum(leaveHours) as leaveHours, "
+ " CAST(DATE_FORMAT(recordDate, '%d') AS SIGNED) AS recordDate "
+ " from `leave` "
+ " where staffId = :staffId "
+ " and recordDate BETWEEN :startDate and :endDate "
+ " group by recordDate "
+ " order by recordDate "
)
return jdbcDao.queryForList(sql.toString(), args)
}

}

+ 10
- 8
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt View File

@@ -11,6 +11,7 @@ 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.StaffMonthlyWorkHourAnalysisReportRequest
import com.ffii.tsms.modules.timesheet.entity.LeaveRepository import com.ffii.tsms.modules.timesheet.entity.LeaveRepository
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository
import com.ffii.tsms.modules.timesheet.entity.projections.ProjectMonthlyHoursWithDate
import jakarta.validation.Valid import jakarta.validation.Valid
import org.springframework.core.io.ByteArrayResource import org.springframework.core.io.ByteArrayResource
import org.springframework.core.io.Resource import org.springframework.core.io.Resource
@@ -78,14 +79,15 @@ class ReportController(
val nextMonth = request.yearMonth.plusMonths(1).atDay(1) val nextMonth = request.yearMonth.plusMonths(1).atDay(1)


val staff = staffRepository.findById(request.id).orElseThrow() val staff = staffRepository.findById(request.id).orElseThrow()
val timesheets = timesheetRepository.findByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth)
val leaves = leaveRepository.findByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth)

val projects = timesheetRepository.findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth)
val projectList: List<String> = projects.map { p -> "${p.projectTask!!.project!!.code}\n ${p.projectTask!!.project!!.name}" }


val reportResult: ByteArray = excelReportService.generateStaffMonthlyWorkHourAnalysisReport(thisMonth, staff, timesheets, leaves, projectList)
val args: Map<String, Any> = mutableMapOf(
"staffId" to request.id,
"startDate" to thisMonth,
"endDate" to nextMonth,
)
val timesheets= excelReportService.getTimesheet(args)
val leaves= excelReportService.getLeaves(args)

val reportResult: ByteArray = excelReportService.generateStaffMonthlyWorkHourAnalysisReport(thisMonth, staff, timesheets, leaves)
// val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") // val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
return ResponseEntity.ok() return ResponseEntity.ok()
// .contentType(mediaType) // .contentType(mediaType)


+ 11
- 2
src/main/java/com/ffii/tsms/modules/timesheet/entity/projections/MonthlyHours.kt View File

@@ -2,7 +2,16 @@ package com.ffii.tsms.modules.timesheet.entity.projections


import java.time.LocalDate import java.time.LocalDate


data class MonthlyHours(
data class MonthlyLeave(
val date: LocalDate, val date: LocalDate,
val nomralConsumed: Number
val leaveHours: Double
) )

data class ProjectMonthlyHoursWithDate(
val id: Long,
val name: String,
val code: String,
val date: LocalDate,
val normalConsumed: Double,
val otConsumed: Double,
)

BIN
src/main/resources/templates/report/AR08_Monthly Work Hours Analysis Report.xlsx View File


Loading…
Cancel
Save