ソースを参照

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

tags/Baseline_30082024_BACKEND_UAT
MSI\2Fi 1年前
コミット
3a4dc62b67
8個のファイルの変更325行の追加45行の削除
  1. +58
    -2
      src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt
  2. +16
    -0
      src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt
  3. +143
    -41
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  4. +29
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/web/TimesheetsController.kt
  5. +15
    -1
      src/main/java/com/ffii/tsms/modules/timesheet/web/models/LeaveEntry.kt
  6. +15
    -1
      src/main/java/com/ffii/tsms/modules/timesheet/web/models/TimeEntry.kt
  7. +49
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/web/models/TimeLeaveEntry.kt
  8. バイナリ
      src/main/resources/templates/report/Cross Team Charge Report.xlsx

+ 58
- 2
src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt ファイルの表示

@@ -463,6 +463,55 @@ open class DashboardService(
return jdbcDao.queryForList(sql.toString(), args)
}

fun searchTeamConsumptionColorOrder(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
"select"
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id,"
+ " p.id as id,"
+ " p.id as projectId,"
+ " p.code as projectCode,"
+ " p.name as projectName,"
+ " te.code as team,"
+ " s.name as teamLead,"
+ " GROUP_CONCAT(DISTINCT tg.name ORDER BY tg.name) as expectedStage,"
+ " p.totalManhour as budgetedManhour,"
+ " COALESCE (sum(t.normalConsumed) + sum(t.otConsumed),0) as spentManhour,"
+ " COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) as remainedManhour,"
+ " coalesce (round(((sum(t.normalConsumed) + sum(t.otConsumed))/p.totalManhour)*100,2),0) as manhourConsumptionPercentage,"
+ " COALESCE (DATE_FORMAT(milestonePayment.comingPaymentMilestone, '%Y-%m-%d'),'NA') as comingPaymentMilestone,"
+ " case"
+ " when COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) > 0 then 0"
+ " when COALESCE (p.totalManhour - sum(t.normalConsumed) - sum(t.otConsumed),0) <= 0 then 1"
+ " end as alert"
+ " from project p"
+ " left join project_task pt on p.id = pt.project_id"
+ " left join timesheet t on pt.id = t.projectTaskId"
+ " left join team te on p.teamLead = te.teamLead"
+ " left join staff s on te.teamLead = s.id"
+ " left join milestone m on p.id = m.projectId and curdate() >= m.startDate and curdate() <= m.endDate"
+ " left join task_group tg on m.taskGroupId = tg.id"
+ " left join ("
+ " SELECT pid, MIN(comingPaymentMilestone) AS comingPaymentMilestone"
+ " FROM ("
+ " SELECT p.id AS pid, mp.date AS comingPaymentMilestone"
+ " FROM project p"
+ " LEFT JOIN milestone m ON p.id = m.projectId"
+ " LEFT JOIN milestone_payment mp ON m.id = mp.milestoneId"
+ " WHERE p.teamLead in (:teamIds)"
+ " AND p.status NOT IN ('Pending to Start', 'Completed', 'Deleted')"
+ " AND mp.date >= CURDATE()"
+ " ) AS subquery"
+ " GROUP BY pid"
+ " ORDER BY comingPaymentMilestone ASC"
+ " ) milestonePayment on milestonePayment.pid = p.id"
+ " 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"
+ " ORDER BY te.code ASC"
)
return jdbcDao.queryForList(sql.toString(), args)
}

fun searchFinancialSummaryCard(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder(
"select"
@@ -1668,7 +1717,11 @@ open class DashboardService(
+ " SELECT 5 as num"
+ " ) numbers"
+ " WHERE"
+ " DATE(:startdate + INTERVAL numbers.num DAY) BETWEEN :startdate AND DATE_ADD(:startdate, INTERVAL 6 DAY)"
+ " DATE(:startdate + INTERVAL numbers.num DAY) BETWEEN :startdate AND"
+ " case"
+ " when curdate() < DATE_ADD(:startdate, INTERVAL 6 DAY) then curdate()"
+ " else DATE_ADD(:startdate, INTERVAL 6 DAY)"
+ " end"
+ " AND DAYOFWEEK(DATE(:startdate + INTERVAL numbers.num DAY)) BETWEEN 2 AND 6"
+ " AND DATE(:startdate + INTERVAL numbers.num DAY) not in (select ch.date from company_holiday ch where ch.deleted = 0)"
+ " AND DATE(:startdate + INTERVAL numbers.num DAY) not in (:publicHolidayList)"
@@ -1747,7 +1800,10 @@ open class DashboardService(
+ " WHERE"
+ " DATE(:startdate + INTERVAL numbers.num DAY) BETWEEN"
+ " :startdate AND"
+ " LAST_DAY(:startdate)"
+ " case"
+ " when month(:startdate) >= month(curdate()) then curdate()"
+ " else LAST_DAY(:startdate)"
+ " end"
+ " AND DAYOFWEEK(DATE(:startdate + INTERVAL numbers.num DAY)) BETWEEN 2 AND 6"
+ " AND DATE(:startdate + INTERVAL numbers.num DAY) not in (select ch.date from company_holiday ch where ch.deleted = 0)"
+ " AND DATE(:startdate + INTERVAL numbers.num DAY) not in (:publicHolidayList)"


+ 16
- 0
src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt ファイルの表示

@@ -117,6 +117,22 @@ class DashboardController(
result = dashboardService.searchTeamConsumption(args)
return result
}
@GetMapping("/searchTeamConsumptionColorOrder")
fun searchTeamConsumptionColorOrder(request: HttpServletRequest?): List<Map<String, Any>> {
val args = mutableMapOf<String, Any>()
val teamIdList = request?.getParameter("teamIdList")
val tableSorting = request?.getParameter("tableSorting")
val teamIds = teamIdList?.split(",")?.map { it.toInt() }?.toList()
var result: List<Map<String, Any>> = emptyList()
if (teamIds != null) {
args["teamIds"] = teamIds
}
if (tableSorting != null) {
args["tableSorting"] = tableSorting
}
result = dashboardService.searchTeamConsumptionColorOrder(args)
return result
}
@GetMapping("/searchFinancialSummaryCard")
fun searchFinancialSummaryCard(request: HttpServletRequest?): List<Map<String, Any>> {
val authority = dashboardService.viewDashboardAuthority()


+ 143
- 41
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt ファイルの表示

@@ -855,6 +855,46 @@ open class ReportService(
combinedResults.forEach { result: String ->

if (groupedInvoices.containsKey(result)) {
if (dateType == "Date") {
groupedInvoices[result]!!.forEachIndexed { _, invoice ->
sheet.createRow(rowIndex++).apply {
createCell(0).apply {
setCellValue(result)
}

createCell(1).apply {
setCellValue(0.0)
cellStyle.dataFormat = accountingStyle
}

createCell(2).apply {
setCellValue(invoice["paidAmount"] as Double? ?: 0.0)
cellStyle.dataFormat = accountingStyle
}

createCell(3).apply {
val lastRow = rowIndex - 1
if (lastRow == 16) {
cellFormula =
"C{currentRow}-B{currentRow}".replace("{currentRow}", rowIndex.toString())
} else {
cellFormula =
"IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace(
"{currentRow}",
rowIndex.toString()
).replace("{lastRow}", lastRow.toString())
}
cellStyle.dataFormat = accountingStyle
}

createCell(4)?.apply {
setCellValue(
"Invoice Receipt: " + (invoice["invoiceNo"] as String? ?: "N/A").toString()
)
}
}
}
} else {
// groupedInvoices[result]!!.forEachIndexed { _, invoice ->
sheet.createRow(rowIndex++).apply {
createCell(0).apply {
@@ -888,9 +928,16 @@ open class ReportService(

createCell(4)?.apply {
// setCellValue(invoice["description"].toString())
setCellValue("Invoice Receipt: " + (groupedInvoices[result]?.map { it["invoiceNo"] }?.joinToString() ?: "N/A").toString())
val invoiceNos = groupedInvoices[result]?.map { it["invoiceNo"] }
if (invoiceNos?.size != null && invoiceNos.size > 1) {
// setCellValue("Invoice Receipt: " + (groupedInvoices[result]?.map { it["invoiceNo"] }?.joinToString() ?: "N/A").toString())
setCellValue("Multiple Invoices")
} else {
setCellValue("Invoice Receipt: " + (invoiceNos?.joinToString() ?: "N/A").toString())
}
}
// }
}
}
}

@@ -1353,7 +1400,7 @@ open class ReportService(
for (i in 0 until rowSize) {
tempCell = sheet.getRow(8 + i).createCell(columnIndex + index)
tempCell.setCellValue(0.0)
if ( 8+i in holidayIndexList) {
if (8 + i in holidayIndexList) {
tempCell.cellStyle = fillOrange
}
tempCell.cellStyle.dataFormat = accountingStyle
@@ -1365,7 +1412,7 @@ open class ReportService(
dayInt = temp[index]["date"].toString().toInt()
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex)
tempCell.setCellValue((temp[index]["normalConsumed"] as Double) + (temp[index]["otConsumed"] as Double))
if ( dayInt.plus(7) in holidayIndexList) {
if (dayInt.plus(7) in holidayIndexList) {
tempCell.cellStyle = fillOrange
tempCell.cellStyle.dataFormat = accountingStyle
}
@@ -1377,7 +1424,7 @@ open class ReportService(
for (i in 0 until rowSize) {
tempCell = sheet.getRow(8 + i).createCell(columnIndex)
tempCell.setCellValue(0.0)
if ( 8+i in holidayIndexList) {
if (8 + i in holidayIndexList) {
tempCell.cellStyle = fillOrange

}
@@ -1388,7 +1435,7 @@ open class ReportService(
dayInt = leave["recordDate"].toString().toInt()
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex)
tempCell.setCellValue(leave["leaveHours"] as Double)
if ( dayInt.plus(7) in holidayIndexList) {
if (dayInt.plus(7) in holidayIndexList) {
tempCell.cellStyle = fillOrange

}
@@ -1420,7 +1467,7 @@ open class ReportService(
tempCell = sheet.getRow(rowIndex).createCell(columnIndex)
tempCell.cellFormula =
"SUM(${getColumnAlphabet(2)}${rowIndex + 1}:${getColumnAlphabet(columnIndex - 2)}${rowIndex + 1})" // should columnIndex - 2
if ( rowIndex in holidayIndexList) {
if (rowIndex in holidayIndexList) {
tempCell.cellStyle = fillOrange

}
@@ -1495,21 +1542,21 @@ open class ReportService(
}

val cell2 = getCell(2) ?: createCell(2)
cell2.cellFormula = "(B{currentRow}+D{currentRow})-1".replace("{currentRow}", (rowIndex+1).toString())
cell2.cellFormula = "(B{currentRow}+D{currentRow})-1".replace("{currentRow}", (rowIndex + 1).toString())
CellUtil.setAlignment(cell2, HorizontalAlignment.CENTER)
cell2.cellStyle.dataFormat = defaultStyle
// getCell(2).cellStyle.dataFormat = accountingStyle

val cell3 = getCell(3)?:createCell(3)
val cell3 = getCell(3) ?: createCell(3)
cell3.setCellValue(salary.increment.toDouble())
cell3.cellStyle = fillOrange
cell3.cellStyle.dataFormat = defaultStyle
CellUtil.setAlignment(cell3, HorizontalAlignment.CENTER)


val cell4 = getCell(4)?:createCell(4)
val cell4 = getCell(4) ?: createCell(4)
cell4.cellFormula =
"(((C{currentRow}+B{currentRow})/2)/20)/8".replace("{currentRow}", (rowIndex+1).toString())
"(((C{currentRow}+B{currentRow})/2)/20)/8".replace("{currentRow}", (rowIndex + 1).toString())
// getCell(4).cellStyle.dataFormat = accountingStyle
cell4.cellStyle.dataFormat = accountingStyle
CellUtil.setAlignment(cell4, HorizontalAlignment.CENTER)
@@ -2002,7 +2049,6 @@ open class ReportService(
+ " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary,"
+ " p.expectedTotalFee * 0.8 as plannedBudget,"
+ " COALESCE(tns.totalBudget, 0) as actualConsumedBudget,"
+ " tns.totalConsumed,"
+ " COALESCE(p.totalManhour, 0) as plannedManhour,"
+ " COALESCE(tns.totalConsumed, 0) as actualConsumedManhour,"
+ " (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) as budgetConsumptionRate,"
@@ -2041,6 +2087,7 @@ open class ReportService(
" 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 "

"All" -> " and " +
" (COALESCE((tns.totalConsumed * sa.hourlyRate), 0) / p.expectedTotalFee) >= :lowerLimit " +
" or (COALESCE(tns.totalConsumed, 0) / COALESCE(p.totalManhour, 0)) >= :lowerLimit "
@@ -2105,17 +2152,17 @@ open class ReportService(
+ " order by g.code, s.name, cte_ts.recordDate"
)
val projectPlanStartEndMonth = projectRepository.findProjectPlanStartEndByIdAndDeletedFalse(projectId)
val compareStartMonth= YearMonth.parse(startMonth)
val compareEndMonth= YearMonth.parse(endMonth)
val compareStartMonth = YearMonth.parse(startMonth)
val compareEndMonth = YearMonth.parse(endMonth)
val actualStartMonth: YearMonth
(if (projectPlanStartEndMonth.actualStart == null){
(if (projectPlanStartEndMonth.actualStart == null) {
YearMonth.now().plusMonths(1)
}else{
} else {
YearMonth.from(projectPlanStartEndMonth.actualStart)
}).also { actualStartMonth = it }

val queryStartMonth = if(compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth
val queryEndMonth = if(compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth
val queryStartMonth = if (compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth
val queryEndMonth = if (compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth

val args = mapOf(
"projectId" to projectId,
@@ -2263,7 +2310,8 @@ open class ReportService(

val manhoursSpentList = getManhoursSpent(projectId, startMonth, endMonth)

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

val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
workbook.write(outputStream)
@@ -2332,17 +2380,17 @@ open class ReportService(
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)")

val projectPlanStartEndMonth = projectRepository.findProjectPlanStartEndByIdAndDeletedFalse(projectId)
val compareStartMonth= YearMonth.parse(startMonth)
val compareEndMonth= YearMonth.parse(endMonth)
val compareStartMonth = YearMonth.parse(startMonth)
val compareEndMonth = YearMonth.parse(endMonth)
val actualStartMonth: YearMonth
(if (projectPlanStartEndMonth.actualStart == null){
(if (projectPlanStartEndMonth.actualStart == null) {
YearMonth.now().plusMonths(1)
}else{
} else {
YearMonth.from(projectPlanStartEndMonth.actualStart)
}).also { actualStartMonth = it }

val queryStartMonth = if(compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth
val queryEndMonth = if(compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth
val queryStartMonth = if (compareStartMonth.isAfter(actualStartMonth)) compareStartMonth else actualStartMonth
val queryEndMonth = if (compareEndMonth.isAfter(YearMonth.now())) YearMonth.now() else compareEndMonth

val monthFormat = DateTimeFormatter.ofPattern("MMM yyyy", Locale.ENGLISH)
val startDate = YearMonth.parse(queryStartMonth.toString(), DateTimeFormatter.ofPattern("yyyy-MM"))
@@ -2353,7 +2401,7 @@ open class ReportService(
val monthRange: MutableList<Map<String, Any>> = ArrayList()

var currentDate = startDate
if (currentDate == endDate){
if (currentDate == endDate) {
monthRange.add(
mapOf(
"display" to YearMonth.of(currentDate.year, currentDate.month).format(monthFormat),
@@ -2638,7 +2686,7 @@ open class ReportService(
}
// CellUtil.setCellStyleProperty(panlCellTitle, "borderBottom", BorderStyle.DOUBLE)
// CellUtil.setCellStyleProperty(panlCell, "borderBottom", BorderStyle.DOUBLE)
val profitAndLossRowNum = rowNum+1
val profitAndLossRowNum = rowNum + 1

rowNum += 1
val uninvoicedAmountRow: Row = sheet.getRow(rowNum) ?: sheet.createRow(rowNum)
@@ -2648,7 +2696,7 @@ open class ReportService(
}
val uninvoicedAmountCell = uninvoicedAmountRow.getCell(1) ?: uninvoicedAmountRow.createCell(1)
uninvoicedAmountCell.apply {
setCellValue(if ((info.getValue("expectedTotalFee") as Double) < 0.0 ) 0.0 else info.getValue("expectedTotalFee") as Double)
setCellValue(if ((info.getValue("expectedTotalFee") as Double) < 0.0) 0.0 else info.getValue("expectedTotalFee") as Double)
cellStyle.dataFormat = accountingStyle
}

@@ -2660,7 +2708,7 @@ open class ReportService(
}
val projectedPnLCell = projectedPnLRow.getCell(1) ?: projectedPnLRow.createCell(1)
projectedPnLCell.apply {
cellFormula = "B${profitAndLossRowNum}+B${profitAndLossRowNum+1}"
cellFormula = "B${profitAndLossRowNum}+B${profitAndLossRowNum + 1}"
cellStyle.dataFormat = accountingStyle
}

@@ -3038,10 +3086,11 @@ open class ReportService(
}

// if (timesheets.isNotEmpty()) {
val combinedTeamCodeColNumber = grades.size
val combinedTeamCodeColNumber = grades.size * 2
val sortedGrades = grades.sortedBy { it.id }
val sortedTeams = teams.sortedBy { it.id }

logger.info(timesheets.filter { it.project?.teamLead?.team?.id != it.staff?.team?.id }.size)
val groupedTimesheets = timesheets
.filter { it.project?.teamLead?.team?.id != it.staff?.team?.id }
.groupBy { timesheetEntry ->
@@ -3095,8 +3144,12 @@ open class ReportService(

createCell(1).apply {
setCellValue(team.code)
cellStyle = normalFontWithBorderStyle
CellUtil.setAlignment(this, HorizontalAlignment.CENTER)

val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
alignment = HorizontalAlignment.CENTER
}
}

}
@@ -3113,8 +3166,22 @@ open class ReportService(
sortedGrades.forEach { grade: Grade ->
createCell(columnIndex++).apply {
setCellValue(grade.name)
cellStyle = boldFontWithBorderStyle
CellUtil.setAlignment(this, HorizontalAlignment.CENTER)
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cellStyle = cloneStyle.apply {
alignment = HorizontalAlignment.CENTER
}
}

createCell(columnIndex++).apply {
val cellValue = grade.name.trim().substring(0, 1) + grade.name.trim()
.substring(grade.name.length - 1) + " - Cost Adjusted by Salary Point"
setCellValue(cellValue)
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cellStyle = cloneStyle.apply {
alignment = HorizontalAlignment.CENTER
}
}
}

@@ -3139,8 +3206,11 @@ open class ReportService(
columnIndex = 0
createCell(columnIndex++).apply {
setCellValue(chargedTeam.code)
cellStyle = normalFontWithBorderStyle
CellUtil.setAlignment(this, HorizontalAlignment.CENTER)
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
alignment = HorizontalAlignment.CENTER
}
}

var totalSalary = 0.0
@@ -3159,22 +3229,47 @@ open class ReportService(
grade.id
)]?.sumOf { it.getValue("salary") } ?: 0.0

cellStyle = normalFontWithBorderStyle
cellStyle.dataFormat = accountingStyle
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
}
}

createCell(columnIndex++).apply {
setCellValue(
groupedTimesheets[Triple(
team.id,
chargedTeam.id,
grade.id
)]?.sumOf { it.getValue("manHour") * it.getValue("salary") } ?: 0.0)

val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(normalFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
}
}
}

createCell(columnIndex++).apply {
val lastCellLetter = CellReference.convertNumToColString(this.columnIndex - 1)
cellFormula = "sum(B${this.rowIndex + 1}:${lastCellLetter}${this.rowIndex + 1})"
cellStyle = boldFontWithBorderStyle
cellStyle.dataFormat = accountingStyle

val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
}
}

createCell(columnIndex).apply {
setCellValue(totalSalary)
cellStyle = boldFontWithBorderStyle
cellStyle.dataFormat = accountingStyle
val cloneStyle = workbook.createCellStyle()
cloneStyle.cloneStyleFrom(boldFontWithBorderStyle)
cellStyle = cloneStyle.apply {
dataFormat = accountingStyle
}
}
}
}
@@ -3195,6 +3290,13 @@ open class ReportService(
cellStyle = normalFontWithBorderStyle
cellStyle.dataFormat = accountingStyle
}

createCell(columnIndex++).apply {
val currentCellLetter = CellReference.convertNumToColString(this.columnIndex)
cellFormula = "sum(${currentCellLetter}${startRow}:${currentCellLetter}${endRow})"
cellStyle = normalFontWithBorderStyle
cellStyle.dataFormat = accountingStyle
}
}

createCell(columnIndex++).apply {


+ 29
- 0
src/main/java/com/ffii/tsms/modules/timesheet/web/TimesheetsController.kt ファイルの表示

@@ -43,6 +43,35 @@ class TimesheetsController(private val timesheetsService: TimesheetsService, pri
return leaveService.saveLeave(parsedEntries)
}

@PostMapping("saveTimeLeave")
fun newTimeLeave(@Valid @RequestBody recordTimeLeave: Map<String, List<TimeLeaveEntry>>): Map<String, List<TimeLeaveEntry>> {
val parsedEntries = kotlin.runCatching {
recordTimeLeave.mapKeys { (dateString) -> LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE) }
}.getOrElse { throw BadRequestException() }

val timesheets = parsedEntries.mapValues { (_, entries) -> entries.mapNotNull { timeLeaveEntry -> timeLeaveEntry.toTimeEntry() } }
val leaves = parsedEntries.mapValues { (_, entries) -> entries.mapNotNull { timeLeaveEntry -> timeLeaveEntry.toLeaveEntry() } }

val savedTimesheets = timesheetsService.saveTimesheet(timesheets)
val savedLeaves = leaveService.saveLeave(leaves)

val newMap = mutableMapOf<String, MutableList<TimeLeaveEntry>>()
savedTimesheets.forEach { (date, entries) ->
if (!newMap.containsKey(date)) {
newMap[date] = mutableListOf()
}
newMap[date]!!.addAll(entries.map { e -> e.toTimeLeaveEntry() })
}
savedLeaves.forEach { (date, entries) ->
if (!newMap.containsKey(date)) {
newMap[date] = mutableListOf()
}
newMap[date]!!.addAll(entries.map { e -> e.toTimeLeaveEntry() })
}

return newMap
}

@GetMapping
fun getTimesheetEntry(): Map<String, List<TimeEntry>> {
return timesheetsService.getTimesheet()


+ 15
- 1
src/main/java/com/ffii/tsms/modules/timesheet/web/models/LeaveEntry.kt ファイルの表示

@@ -5,4 +5,18 @@ data class LeaveEntry(
val leaveTypeId: Long?,
val inputHours: Double,
val remark: String?
)
) {
fun toTimeLeaveEntry(): TimeLeaveEntry {
return TimeLeaveEntry(
type = "leaveEntry",
id = this.id,
leaveTypeId = this.leaveTypeId,
inputHours = this.inputHours,
remark = this.remark,
projectId = null,
taskGroupId = null,
taskId = null,
otHours = null,
)
}
}

+ 15
- 1
src/main/java/com/ffii/tsms/modules/timesheet/web/models/TimeEntry.kt ファイルの表示

@@ -9,4 +9,18 @@ data class TimeEntry(
val inputHours: Double?,
val otHours: Double?,
val remark: String?
)
) {
fun toTimeLeaveEntry(): TimeLeaveEntry {
return TimeLeaveEntry(
type = "timeEntry",
id = this.id,
projectId = this.projectId,
taskGroupId = this.taskGroupId,
taskId = this.taskId,
inputHours = this.inputHours,
otHours = this.otHours,
remark = this.remark,
leaveTypeId = null
)
}
}

+ 49
- 0
src/main/java/com/ffii/tsms/modules/timesheet/web/models/TimeLeaveEntry.kt ファイルの表示

@@ -0,0 +1,49 @@
package com.ffii.tsms.modules.timesheet.web.models

import jakarta.validation.constraints.AssertTrue

data class TimeLeaveEntry(
val id: Long,
val type: String,
val projectId: Long?,
val leaveTypeId: Long?,
val taskGroupId: Long?,
val taskId: Long?,
val inputHours: Double?,
val otHours: Double?,
val remark: String?,
) {
@AssertTrue
private fun isValid(): Boolean {
return type == "timeEntry" || (type == "leaveEntry" && leaveTypeId != null)
}

fun toTimeEntry(): TimeEntry? {
if (type == "leaveEntry") {
return null
}

return TimeEntry(
id,
projectId,
taskGroupId,
taskId,
inputHours,
otHours,
remark,
)
}

fun toLeaveEntry(): LeaveEntry? {
if (type == "timeEntry" || inputHours == null) {
return null
}

return LeaveEntry(
id,
leaveTypeId,
inputHours,
remark,
)
}
}

バイナリ
src/main/resources/templates/report/Cross Team Charge Report.xlsx ファイルの表示


読み込み中…
キャンセル
保存