|
@@ -31,6 +31,7 @@ import java.time.format.DateTimeParseException |
|
|
import java.time.temporal.ChronoUnit |
|
|
import java.time.temporal.ChronoUnit |
|
|
import java.util.* |
|
|
import java.util.* |
|
|
import java.awt.Color |
|
|
import java.awt.Color |
|
|
|
|
|
import java.lang.IllegalArgumentException |
|
|
import java.math.RoundingMode |
|
|
import java.math.RoundingMode |
|
|
import java.time.Year |
|
|
import java.time.Year |
|
|
import javax.swing.plaf.synth.Region |
|
|
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" |
|
|
"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 COMPLETION_PROJECT = "templates/report/AR05_Project Completion Report.xlsx" |
|
|
private val CROSS_TEAM_CHARGE_REPORT = "templates/report/Cross Team Charge 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> { |
|
|
private fun cellBorderArgs(top: Int, bottom: Int, left: Int, right: Int): MutableMap<String, Any> { |
|
|
var cellBorderArgs = mutableMapOf<String, Any>() |
|
|
var cellBorderArgs = mutableMapOf<String, Any>() |
|
@@ -243,6 +245,18 @@ open class ReportService( |
|
|
|
|
|
|
|
|
return outputStream.toByteArray() |
|
|
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) |
|
|
@Throws(IOException::class) |
|
|
fun generateProjectPotentialDelayReport( |
|
|
fun generateProjectPotentialDelayReport( |
|
@@ -2222,6 +2236,37 @@ open class ReportService( |
|
|
) |
|
|
) |
|
|
return jdbcDao.queryForList(sql.toString(), args) |
|
|
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>> { |
|
|
open fun getTotalConsumed(args: Map<String, Any>): List<Map<String, Any>> { |
|
|
val sql = StringBuilder( |
|
|
val sql = StringBuilder( |
|
@@ -3573,7 +3618,6 @@ open class ReportService( |
|
|
|
|
|
|
|
|
return outputStream.toByteArray() |
|
|
return outputStream.toByteArray() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
open fun getLateStartDetails( |
|
|
open fun getLateStartDetails( |
|
|
teamId: Long?, |
|
|
teamId: Long?, |
|
|
clientId: Long?, |
|
|
clientId: Long?, |
|
@@ -3628,9 +3672,141 @@ open class ReportService( |
|
|
) |
|
|
) |
|
|
return jdbcDao.queryForList(sql.toString(), args) |
|
|
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( |
|
|
private fun generateTeamsInOutMap( |
|
|
teams: List<Team>, |
|
|
teams: List<Team>, |
|
|
desiredTeam: MutableList<Team>, |
|
|
desiredTeam: MutableList<Team>, |
|
|