Pārlūkot izejas kodu

add: project manhour report

update: order project lists
add_swagger
MSI\derek pirms 9 mēnešiem
vecāks
revīzija
6b811b14df
7 mainītis faili ar 222 papildinājumiem un 44 dzēšanām
  1. +21
    -16
      src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt
  2. +1
    -1
      src/main/java/com/ffii/tsms/modules/project/entity/StaffAllocationRepository.kt
  3. +0
    -24
      src/main/java/com/ffii/tsms/modules/project/service/ProjectsService.kt
  4. +179
    -3
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  5. +16
    -0
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  6. +5
    -0
      src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt
  7. Binārs
      src/main/resources/templates/report/Project Manhour Summary.xlsx

+ 21
- 16
src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt Parādīt failu

@@ -395,7 +395,7 @@ open class DashboardService(

if (viewDashboardAuthority() == "self") {
val teamId = staffsService.currentStaff()?.team?.id
if (teamId != null) {
if (teamId != null && teamId > 0) {
sql.append(" and t.id = $teamId")
}
}
@@ -406,18 +406,18 @@ open class DashboardService(
+ " from teamProject tp"
)

if (viewDashboardAuthority() != "self") {
sql.append(" union all"
+ " select"
+ " null as id,"
+ " null as teamId,"
+ " null as teamLeadId,"
+ " 'All' as teamCode,"
+ " null as teamName,"
+ " sum(tp.projectNo) as projectNo"
+ " from teamProject tp"
+ " order by id")
}
// if (viewDashboardAuthority() != "self") {
// sql.append(" union all"
// + " select"
// + " null as id,"
// + " null as teamId,"
// + " null as teamLeadId,"
// + " 'All' as teamCode,"
// + " null as teamName,"
// + " sum(tp.projectNo) as projectNo"
// + " from teamProject tp"
// + " order by teamCode")
// }

return jdbcDao.queryForList(sql.toString(), args)
}
@@ -487,8 +487,8 @@ open class DashboardService(
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone, taskGroup.expectedStage"
)

if (args["tableSorting"] == "ProjectName") {
sql.append(" ORDER BY p.name ASC")
if (args["tableSorting"] == "ProjectCode") {
sql.append(" ORDER BY p.code ")
} else if (args["tableSorting"] == "PercentageASC") {
sql.append(" ORDER BY coalesce (round(((sum(t.normalConsumed) + sum(t.otConsumed))/p.totalManhour)*100,2),0) asc")
} else if (args["tableSorting"] == "PercentageDESC") {
@@ -579,6 +579,7 @@ open class DashboardService(
+ " where p.teamLead in (:teamIds)"
+ " and p.status not in ('Pending to Start','Completed','Deleted')"
+ " group by p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone, taskGroup.expectedStage"
// + " order by p.code "
)

// if (args["tableSorting"] == "ProjectName") {
@@ -594,6 +595,8 @@ open class DashboardService(
sql.append(" ORDER BY coalesce (round(((sum(t.normalConsumed) + sum(t.otConsumed))/p.totalManhour)*100,2),0) asc")
} else if (args["tableSorting"] == "PercentageDESC") {
sql.append(" ORDER BY coalesce (round(((sum(t.normalConsumed) + sum(t.otConsumed))/p.totalManhour)*100,2),0) desc")
} else {
sql.append(" order by p.code ")
}

return jdbcDao.queryForList(sql.toString(), args)
@@ -1408,6 +1411,7 @@ open class DashboardService(
+ " left join subsidiary s2 on p.customerSubsidiaryId = s2.id"
+ " where p.deleted = 0"
+ " and p.status = 'On-going'"
+ " order by p.code"
)

if (viewDashboardAuthority() == "self") {
@@ -2288,6 +2292,7 @@ open class DashboardService(
sql.append(" and p.teamLead = $teamLeadId")
}
}
sql.append(" order by p.code ")

return jdbcDao.queryForList(sql.toString(), args)
}
@@ -3569,7 +3574,7 @@ open class DashboardService(
+ " left join project_expense pe on pe.projectId = p.id "
+ " where p.status = 'On-going' "
+ (if (args.containsKey("teamId")) "and s.teamId = :teamId" else "")
+ " order by p.id "
+ " order by p.code "
+ " ) result ")
return jdbcDao.queryForList(sql.toString(), args)
}


+ 1
- 1
src/main/java/com/ffii/tsms/modules/project/entity/StaffAllocationRepository.kt Parādīt failu

@@ -7,7 +7,7 @@ import org.springframework.data.jpa.repository.Query
import java.time.LocalDate

interface StaffAllocationRepository : AbstractRepository<StaffAllocation, Long> {
@Query("SELECT sa.project FROM StaffAllocation sa WHERE sa.staff = ?1 AND sa.project.status = 'On-going'")
@Query("SELECT sa.project FROM StaffAllocation sa WHERE sa.staff = ?1 AND sa.project.status = 'On-going' order by sa.project.code")
fun findOnGoingAssignedProjectsByStaff(staff: Staff): List<Project>

fun findByProject(project: Project): List<StaffAllocation>

+ 0
- 24
src/main/java/com/ffii/tsms/modules/project/service/ProjectsService.kt Parādīt failu

@@ -403,30 +403,6 @@ open class ProjectsService(
val nonInvoicedAmount = (project.expectedTotalFee?: 0.0) - invoicedAmount
return InvoiceData(invoicedAmount, nonInvoicedAmount, receivedAmount)
}
// open fun getProjectDashboardDataByProjectId(
// projectId: Long,
// startDate: LocalDate?,
// endDate: LocalDate?,
// ): DashboardData {
// val project = projectRepository.findById(projectId).orElseThrow()
// val manhourExpense = timesheetsService.getManpowerExpenseByProjectId(projectId,startDate,endDate)
// val projectExpense = projectExpenseService.getProjectExpenseByProjectId(projectId,startDate,endDate)
// val invoiceData = getInvoiceDataByProjectId(projectId,startDate,endDate)
// val cumulativeExpenditure = manhourExpense+projectExpense
// val output = DashboardData(
// cumulativeExpenditure,
// manhourExpense,
// projectExpense,
// invoiceData.invoicedAmount,
// invoiceData.nonInvoicedAmount,
// invoiceData.receivedAmount,
// if (invoiceData.invoicedAmount >= manhourExpense+projectExpense) "Positive" else "Negative",
// if (project.expectedTotalFee!! >= cumulativeExpenditure) "Positive" else "Negative",
// if (cumulativeExpenditure > 0.0) invoiceData.invoicedAmount/cumulativeExpenditure else 0.0,
// if (cumulativeExpenditure > 0.0) project.expectedTotalFee!!/cumulativeExpenditure else 0.0
// )
// return output
// }

open fun getProjectDetails(projectId: Long): EditProjectDetails? {
val project = projectRepository.findById(projectId)


+ 179
- 3
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Parādīt failu

@@ -31,6 +31,7 @@ import java.time.format.DateTimeParseException
import java.time.temporal.ChronoUnit
import java.util.*
import java.awt.Color
import java.lang.IllegalArgumentException
import java.math.RoundingMode
import java.time.Year
import javax.swing.plaf.synth.Region
@@ -73,6 +74,7 @@ open class ReportService(
"templates/report/AR06_Project Completion Report with Outstanding Accounts Receivable v02.xlsx"
private val COMPLETION_PROJECT = "templates/report/AR05_Project Completion Report.xlsx"
private val CROSS_TEAM_CHARGE_REPORT = "templates/report/Cross Team Charge Report.xlsx"
private val PROJECT_MANHOUR_SUMMARY = "templates/report/Project Manhour Summary.xlsx"

private fun cellBorderArgs(top: Int, bottom: Int, left: Int, right: Int): MutableMap<String, Any> {
var cellBorderArgs = mutableMapOf<String, Any>()
@@ -243,6 +245,18 @@ open class ReportService(

return outputStream.toByteArray()
}
@Throws(IOException::class)
fun generateProjectManhourSummaryReport(args: Map<String, Any>): ByteArray {
val manhourSummary = getManhourSummary(args)
// Generate the Excel report with query results
val workbook: Workbook = createProjectManhourSummaryReport(args, manhourSummary, PROJECT_MANHOUR_SUMMARY)
// Write the workbook to a ByteArrayOutputStream
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
workbook.write(outputStream)
workbook.close()

return outputStream.toByteArray()
}

@Throws(IOException::class)
fun generateProjectPotentialDelayReport(
@@ -2222,6 +2236,37 @@ open class ReportService(
)
return jdbcDao.queryForList(sql.toString(), args)
}
open fun getManhourSummary(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("select"
+ " DATE_FORMAT(t.recordDate, '%b-%Y') AS recordMonth, "
+ " t.staffId, "
+ " s.name as staff, "
+ " p.id as projectId, "
+ " p.code as projectCode, "
+ " p.name as projectName, "
+ " c.name as client, "
+ " sum(coalesce(t.normalConsumed, 0) + coalesce(t.otConsumed,0)) as consumed "
+ " from timesheet t "
+ " left join team_log tl on tl.staffId = t.staffId and t.recordDate >= tl.`from` and (t.recordDate <= tl.`to` or tl.`to` is null) "
+ " left join staff s on s.id = t.staffId "
+ " left join project p on p.id = t.projectId "
+ " left join customer c on c.id = p.customerId "
+ " where t.deleted = false "
+ " and (tl.teamId is not null and s.teamId is not null) "
+ " and t.projectId is not null "
)
if (args.containsKey(("startDate"))) {
sql.append(" and t.recordDate >= :startDate ")
}
if (args.containsKey(("endDate"))) {
sql.append(" and t.recordDate < :endDate ")
}
if (args.containsKey(("teamId"))) {
sql.append(" and coalesce(tl.teamId, s.teamId) = :teamId ")
}
sql.append(" group by recordMonth, t.staffId, t.projectId, tl.teamId ")
return jdbcDao.queryForList(sql.toString(), args)
}

open fun getTotalConsumed(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
@@ -3573,7 +3618,6 @@ open class ReportService(

return outputStream.toByteArray()
}

open fun getLateStartDetails(
teamId: Long?,
clientId: Long?,
@@ -3628,9 +3672,141 @@ open class ReportService(
)
return jdbcDao.queryForList(sql.toString(), args)
}
// private fun binarySearch(Any[], ) {

// }
fun fontArgs2(sheet: Sheet, fontName: String,isBold: Boolean): MutableMap<String, Any>{
val font = sheet.workbook.createFont()
font.bold = isBold
font.fontName = fontName
val fontArgs = mutableMapOf<String, Any>(
CellUtil.FONT to font.index,
CellUtil.WRAP_TEXT to true,
)
return fontArgs
}
fun dataFormatArgs2(accountingStyle: Short): MutableMap<String, Any> {
val dataFormatArgs = mutableMapOf<String, Any>(
CellUtil.DATA_FORMAT to accountingStyle
)
return dataFormatArgs
}
private fun createProjectManhourSummaryReport(
args: Map<String, Any>,
manhourSummary: List<Map<String, Any>>,
templatePath: String )
: Workbook
{
val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream
val workbook: Workbook = XSSFWorkbook(templateInputStream)
val sheet: Sheet = workbook.getSheetAt(0)
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)")
fun getMonthsBetweenToColumn(start: LocalDate, end: LocalDate, startValue: Int): Map<String, Int> {
// Get the first day of the start month
val startMonth = start.withDayOfMonth(1)
// Generate a map of months between the start date and the day before the end month
return generateSequence(startMonth) { it.plusMonths(1) }
.takeWhile { it.isBefore(end.withDayOfMonth(1)) } // Exclude the end month
// .takeWhile { it.isBefore(endMonth) || it.isEqual(endMonth) }
.mapIndexed { index, month ->
// Format the month as "MMM-yyyy"
val formattedMonth = month.format(DateTimeFormatter.ofPattern("MMM-yyyy"))
// Calculate the value for this month
formattedMonth to (startValue + index)
}
.toMap() // Convert the list of pairs to a map
}
val startDate = LocalDate.parse(args["startDate"].toString())
val endDate = LocalDate.parse(args["endDate"].toString())
val monthList = getMonthsBetweenToColumn(startDate, endDate, 4)
if (monthList.isEmpty()) {
throw IllegalArgumentException("illegal time period")
}
val result = manhourSummary.groupBy { mapOf("staff" to it["staff"], "projectCode" to it["projectCode"], "projectName" to it["projectName"], "client" to it["client"]) }
.map { entry ->
val monthlyConsumption = entry.value.associate { it["recordMonth"] to it["consumed"] }
mapOf("staff" to entry.key["staff"], "projectCode" to entry.key["projectCode"], "projectName" to entry.key["projectName"], "client" to entry.key["client"]) + monthlyConsumption
}
//start from col4
var rowIndex = 1
var columnIndex = 1
var tempRow: Row
var tempCell: Cell
tempRow = getOrCreateRow(sheet, rowIndex)
tempCell = getOrCreateCell(tempRow, columnIndex)
tempCell.setCellValue(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd")).toString())
//write months header
rowIndex = 5
columnIndex = 4
tempRow = getOrCreateRow(sheet, rowIndex)
for (curr in monthList) {
tempCell = getOrCreateCell(tempRow, columnIndex)
alignTopCenter(tempCell)
tempCell.setCellValue(curr.key)
CellUtil.setCellStyleProperties(tempCell,
cellBorderArgs(0,1,0,0)
+ fontArgs2(sheet, "Times New Roman",false))
columnIndex++
}
//write content
rowIndex = 6
for ( curr in result) {
tempRow = getOrCreateRow(sheet, rowIndex)
columnIndex = 0
tempCell = getOrCreateCell(tempRow, columnIndex)
CellUtil.setCellStyleProperties(tempCell,
cellBorderArgs(1,1,1,1)
+ fontArgs2(sheet, "Times New Roman",false))
tempCell.setCellValue(curr["staff"].toString())
columnIndex = 1
tempCell = getOrCreateCell(tempRow, columnIndex)
CellUtil.setCellStyleProperties(tempCell,
cellBorderArgs(1,1,1,1)
+ fontArgs2(sheet, "Times New Roman",false))
tempCell.setCellValue(curr["projectCode"].toString())
columnIndex = 2
tempCell = getOrCreateCell(tempRow, columnIndex)
CellUtil.setCellStyleProperties(tempCell,
cellBorderArgs(1,1,1,1)
+ fontArgs2(sheet, "Times New Roman",false))
tempCell.setCellValue(curr["projectName"].toString())
columnIndex = 3
tempCell = getOrCreateCell(tempRow, columnIndex)
CellUtil.setCellStyleProperties(tempCell,
cellBorderArgs(1,1,1,1)
+ fontArgs2(sheet, "Times New Roman",false))
tempCell.setCellValue(curr["client"].toString())
for ( month in monthList) {
var manhour = 0.0
if (curr.containsKey(month.key.toString())) {
manhour = curr[month.key.toString()] as Double
}
columnIndex = month.value
tempCell = getOrCreateCell(tempRow, columnIndex)
tempCell.setCellValue(manhour)
CellUtil.setCellStyleProperties(tempCell,
cellBorderArgs(1,1,1,1)
+ fontArgs2(sheet, "Times New Roman",false))
}
rowIndex++
}
// total
tempRow = getOrCreateRow(sheet, rowIndex)
columnIndex = monthList.values.firstNotNullOfOrNull { it }!! - 1
tempCell = getOrCreateCell(tempRow, columnIndex)
tempCell.setCellValue("Total:")
CellUtil.setCellStyleProperties(tempCell, fontArgs2(sheet, "Times New Roman",false))
setAlignment(tempCell,"top", "right")
for (curr in monthList) {
columnIndex = curr.value
val columnLetter = CellReference.convertNumToColString(columnIndex)
tempCell = getOrCreateCell(tempRow, columnIndex)
CellUtil.setCellStyleProperties(tempCell,
cellBorderArgs(1,1,1,1)
+ fontArgs2(sheet, "Times New Roman",false))
tempCell.cellFormula = "SUM(${columnLetter}7:$columnLetter$rowIndex)"
}
return workbook
}
private fun generateTeamsInOutMap(
teams: List<Team>,
desiredTeam: MutableList<Team>,


+ 16
- 0
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt Parādīt failu

@@ -307,6 +307,22 @@ class ReportController(
return excelReportService.getCostAndExpense(request.clientId, request.teamId, request.type)
}

@PostMapping("/ProjectManhourSummary")
@Throws(ServletRequestBindingException::class, IOException::class)
fun getProjectManhourSummary(@RequestBody @Valid request: ProjectManhourSummaryRequest): ResponseEntity<Resource> {
println("starting")
val args = mapOf(
"startDate" to request.startDate,
"endDate" to request.endDate,
"teamId" to request.teamId,
)
val reportResult: ByteArray = excelReportService.generateProjectManhourSummaryReport(args)
println("ending")
return ResponseEntity.ok()
.header("filename", "Project Manhour Summary Report - " + LocalDate.now() + ".xlsx")
.body(ByteArrayResource(reportResult))
}

@PostMapping("/costandexpenseReport")
@Throws(ServletRequestBindingException::class, IOException::class)
fun getCostAndExpenseReport(@RequestBody @Valid request: costAndExpenseRequest): ResponseEntity<Resource> {


+ 5
- 0
src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt Parādīt failu

@@ -63,4 +63,9 @@ data class ProjectCompletionReport (
data class CrossTeamChargeReportRequest (
val month: String,
val teamId: String
)
data class ProjectManhourSummaryRequest (
val startDate: LocalDate,
val endDate: LocalDate,
val teamId: Any
)

Binārs
src/main/resources/templates/report/Project Manhour Summary.xlsx Parādīt failu


Notiek ielāde…
Atcelt
Saglabāt