Browse Source

Add leave api

tags/Baseline_30082024_BACKEND_UAT
Wayne 1 year ago
parent
commit
eefd10c51f
12 changed files with 148 additions and 6 deletions
  1. +8
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/Leave.kt
  2. +5
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveRepository.kt
  3. +6
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveTypeRepository.kt
  4. +3
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/Timesheet.kt
  5. +72
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt
  6. +1
    -2
      src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt
  7. +25
    -4
      src/main/java/com/ffii/tsms/modules/timesheet/web/TimesheetsController.kt
  8. +7
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/web/models/LeaveEntry.kt
  9. +1
    -0
      src/main/resources/application.yml
  10. +11
    -0
      src/main/resources/db/changelog/changes/20240506_01_wayne/01_leave_type_data.sql
  11. +5
    -0
      src/main/resources/db/changelog/changes/20240506_01_wayne/02_timesheet_remark.sql
  12. +4
    -0
      src/main/resources/db/changelog/changes/20240506_01_wayne/03_leave_update.sql

+ 8
- 0
src/main/java/com/ffii/tsms/modules/timesheet/entity/Leave.kt View File

@@ -4,6 +4,7 @@ import com.ffii.core.entity.BaseEntity
import com.ffii.tsms.modules.data.entity.Staff
import jakarta.persistence.*
import jakarta.validation.constraints.NotNull
import java.time.LocalDate

@Entity
@Table(name = "leave")
@@ -20,4 +21,11 @@ open class Leave : BaseEntity<Long>() {
@ManyToOne
@JoinColumn(name = "leaveTypeId")
open var leaveType: LeaveType? = null

@NotNull
@Column(name = "recordDate")
open var recordDate: LocalDate? = null

@Column(name = "remark")
open var remark: String? = null
}

+ 5
- 0
src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveRepository.kt View File

@@ -1,6 +1,11 @@
package com.ffii.tsms.modules.timesheet.entity;

import com.ffii.core.support.AbstractRepository
import com.ffii.tsms.modules.data.entity.Staff
import java.time.LocalDate

interface LeaveRepository : AbstractRepository<Leave, Long> {
fun findAllByStaff(staff: Staff): List<Leave>

fun deleteAllByStaffAndRecordDate(staff: Staff, recordDate: LocalDate)
}

+ 6
- 0
src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveTypeRepository.kt View File

@@ -0,0 +1,6 @@
package com.ffii.tsms.modules.timesheet.entity;

import com.ffii.core.support.AbstractRepository

interface LeaveTypeRepository : AbstractRepository<LeaveType, Long> {
}

+ 3
- 0
src/main/java/com/ffii/tsms/modules/timesheet/entity/Timesheet.kt View File

@@ -28,4 +28,7 @@ open class Timesheet : BaseEntity<Long>() {
@ManyToOne
@JoinColumn(name = "projectTaskId")
open var projectTask: ProjectTask? = null

@Column(name = "remark")
open var remark: String? = null
}

+ 72
- 0
src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt View File

@@ -0,0 +1,72 @@
package com.ffii.tsms.modules.timesheet.service

import com.ffii.core.exception.BadRequestException
import com.ffii.tsms.modules.data.service.StaffsService
import com.ffii.tsms.modules.timesheet.entity.*
import com.ffii.tsms.modules.timesheet.web.models.LeaveEntry
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.time.format.DateTimeFormatter

@Service
open class LeaveService(
private val leaveRepository: LeaveRepository,
private val leaveTypeRepository: LeaveTypeRepository,
private val staffsService: StaffsService
) {
open fun getLeaveTypes(): List<LeaveType> {
return leaveTypeRepository.findAll()
}

open fun getLeaves(): Map<String, List<LeaveEntry>> {
val currentStaff = staffsService.currentStaff() ?: return emptyMap()
return transformToLeaveEntryMap(leaveRepository.findAllByStaff(currentStaff))
}

@Transactional
open fun saveLeave(recordLeaveEntry: Map<LocalDate, List<LeaveEntry>>): Map<String, List<LeaveEntry>> {
// Need to be associated with a staff
val currentStaff = staffsService.currentStaff() ?: throw BadRequestException()

val leaveTypesMap = getLeaveTypes().associateBy { it.id }

val leaves = recordLeaveEntry.entries.flatMap { (entryDate, leaveEntries) ->
// Replace db leave entries by deleting and then adding back
leaveRepository.deleteAllByStaffAndRecordDate(currentStaff, entryDate)

mergeLeaveEntriesByType(leaveEntries).map { leaveEntry ->
Leave().apply {
this.staff = currentStaff
this.recordDate = entryDate
this.leaveType = leaveTypesMap[leaveEntry.leaveTypeId]
this.leaveHours = leaveEntry.inputHours
}
}
}

val savedLeaves = leaveRepository.saveAll(leaves)

return transformToLeaveEntryMap(savedLeaves)
}

private fun transformToLeaveEntryMap(leaves: List<Leave>): Map<String, List<LeaveEntry>> {
return leaves
.groupBy { leave -> leave.recordDate!!.format(DateTimeFormatter.ISO_LOCAL_DATE) }
.mapValues { (_, leaveEntries) -> leaveEntries.map { leave ->
LeaveEntry(
id = leave.id!!,
inputHours = leave.leaveHours ?: 0.0,
leaveTypeId = leave.leaveType!!.id
)
} }
}

private fun mergeLeaveEntriesByType(entries: List<LeaveEntry>): List<LeaveEntry> {
return entries
.groupBy { leaveEntry -> leaveEntry.leaveTypeId }
.values.map { leaveEntires ->
leaveEntires.reduce { acc, leaveEntry -> acc.copy(inputHours = acc.inputHours + leaveEntry.inputHours) }
}
}
}

+ 1
- 2
src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt View File

@@ -16,7 +16,6 @@ import kotlin.jvm.optionals.getOrDefault
import kotlin.jvm.optionals.getOrNull

@Service

open class TimesheetsService(
private val timesheetRepository: TimesheetRepository,
private val projectTaskRepository: ProjectTaskRepository,
@@ -54,7 +53,7 @@ open class TimesheetsService(

open fun getTimesheet(): Map<String, List<TimeEntry>> {
// Need to be associated with a staff
val currentStaff = staffsService.currentStaff() ?: throw BadRequestException()
val currentStaff = staffsService.currentStaff() ?: return emptyMap()
return transformToTimeEntryMap(timesheetRepository.findAllByStaff(currentStaff))
}



+ 25
- 4
src/main/java/com/ffii/tsms/modules/timesheet/web/TimesheetsController.kt View File

@@ -1,7 +1,10 @@
package com.ffii.tsms.modules.timesheet.web

import com.ffii.core.exception.BadRequestException
import com.ffii.tsms.modules.timesheet.entity.LeaveType
import com.ffii.tsms.modules.timesheet.service.LeaveService
import com.ffii.tsms.modules.timesheet.service.TimesheetsService
import com.ffii.tsms.modules.timesheet.web.models.LeaveEntry
import com.ffii.tsms.modules.timesheet.web.models.TimeEntry
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.GetMapping
@@ -14,21 +17,39 @@ import java.time.format.DateTimeFormatter

@RestController
@RequestMapping("/timesheets")
class TimesheetsController(private val timesheetsService: TimesheetsService) {
class TimesheetsController(private val timesheetsService: TimesheetsService, private val leaveService: LeaveService) {
@PostMapping("/save")
fun newTimesheetEntry(@Valid @RequestBody recordTimesheet: Map<String, List<TimeEntry>>): Map<String, List<TimeEntry>> {
val parseDateTimeResult = kotlin.runCatching {
val parsedEntries = kotlin.runCatching {
recordTimesheet.mapKeys { (dateString) ->
LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE)
}
}
val parsedEntries = parseDateTimeResult.getOrElse { throw BadRequestException() }
}.getOrElse { throw BadRequestException() }

return timesheetsService.saveTimesheet(parsedEntries)
}

@PostMapping("/saveLeave")
fun newLeaveEntry(@Valid @RequestBody recordLeave: Map<String, List<LeaveEntry>>): Map<String, List<LeaveEntry>> {
val parsedEntries = kotlin.runCatching {
recordLeave.mapKeys { (dateString) -> LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE) }
}.getOrElse { throw BadRequestException() }

return leaveService.saveLeave(parsedEntries)
}

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

@GetMapping("/leaves")
fun getLeaveEntry(): Map<String, List<LeaveEntry>> {
return leaveService.getLeaves()
}

@GetMapping("/leaveTypes")
fun leaveTypes(): List<LeaveType> {
return leaveService.getLeaveTypes()
}
}

+ 7
- 0
src/main/java/com/ffii/tsms/modules/timesheet/web/models/LeaveEntry.kt View File

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

data class LeaveEntry(
val id: Long,
val leaveTypeId: Long?,
val inputHours: Double
)

+ 1
- 0
src/main/resources/application.yml View File

@@ -21,6 +21,7 @@ spring:
database-platform: org.hibernate.dialect.MySQL8Dialect
properties:
hibernate:
globally_quoted_identifiers: true
dialect:
storage_engine: innodb



+ 11
- 0
src/main/resources/db/changelog/changes/20240506_01_wayne/01_leave_type_data.sql View File

@@ -0,0 +1,11 @@
-- liquibase formatted sql
-- changeset wayne:leave_type

INSERT
INTO
leave_type
(name)
VALUES
('Annual Leave'),
('Sick Leave'),
('Special Leave');

+ 5
- 0
src/main/resources/db/changelog/changes/20240506_01_wayne/02_timesheet_remark.sql View File

@@ -0,0 +1,5 @@
-- liquibase formatted sql
-- changeset wayne:timesheet_remark

ALTER TABLE timesheet ADD remark VARCHAR(255) NULL;


+ 4
- 0
src/main/resources/db/changelog/changes/20240506_01_wayne/03_leave_update.sql View File

@@ -0,0 +1,4 @@
-- liquibase formatted sql
-- changeset wayne:leave_update

ALTER TABLE `leave` ADD recordDate date NOT NULL, ADD remark VARCHAR(255) NULL;

Loading…
Cancel
Save