Browse Source

update email: checking leave + consumed ?>= 8

add_swagger
MSI\derek 11 months ago
parent
commit
62577c9b1c
4 changed files with 172 additions and 39 deletions
  1. +113
    -37
      src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt
  2. +9
    -0
      src/main/java/com/ffii/tsms/modules/common/mail/web/models/WorkHourRecords.kt
  3. +2
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/LeaveRepository.kt
  4. +48
    -2
      src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt

+ 113
- 37
src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt View File

@@ -3,6 +3,7 @@ package com.ffii.tsms.modules.common.mail.service
import com.ffii.tsms.modules.common.SettingNames import com.ffii.tsms.modules.common.SettingNames
import com.ffii.tsms.modules.common.holiday.service.HolidayService import com.ffii.tsms.modules.common.holiday.service.HolidayService
import com.ffii.tsms.modules.common.mail.pojo.MailRequest import com.ffii.tsms.modules.common.mail.pojo.MailRequest
import com.ffii.tsms.modules.common.mail.web.models.WorkHourRecords
import com.ffii.tsms.modules.data.entity.Staff import com.ffii.tsms.modules.data.entity.Staff
import com.ffii.tsms.modules.data.entity.StaffRepository import com.ffii.tsms.modules.data.entity.StaffRepository
import com.ffii.tsms.modules.data.entity.TeamRepository import com.ffii.tsms.modules.data.entity.TeamRepository
@@ -10,6 +11,7 @@ import com.ffii.tsms.modules.data.service.CompanyHolidayService
import com.ffii.tsms.modules.data.service.StaffsService import com.ffii.tsms.modules.data.service.StaffsService
import com.ffii.tsms.modules.settings.service.SettingsService import com.ffii.tsms.modules.settings.service.SettingsService
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository
import com.ffii.tsms.modules.timesheet.service.TimesheetsService
import com.ffii.tsms.modules.user.service.UserService import com.ffii.tsms.modules.user.service.UserService
import jakarta.mail.internet.InternetAddress import jakarta.mail.internet.InternetAddress
import org.apache.commons.logging.Log import org.apache.commons.logging.Log
@@ -26,12 +28,14 @@ data class TableRow(
val name: String, val name: String,
val missingDates: MutableList<LocalDate>, val missingDates: MutableList<LocalDate>,
) )

@Service @Service
open class MailReminderService( open class MailReminderService(
val mailService: MailService, val mailService: MailService,
val userService: UserService, val userService: UserService,
val settingsService: SettingsService, val settingsService: SettingsService,
val holidayService: HolidayService, val holidayService: HolidayService,
val timesheetsService: TimesheetsService,
val timesheetRepository: TimesheetRepository, val timesheetRepository: TimesheetRepository,
val staffsService: StaffsService, val staffsService: StaffsService,
val staffRepository: StaffRepository, val staffRepository: StaffRepository,
@@ -117,8 +121,19 @@ open class MailReminderService(
val filteredLastMonthDays = lastMonthDays.filter { val filteredLastMonthDays = lastMonthDays.filter {
it !in allHolidaysList && it.dayOfWeek != DayOfWeek.SATURDAY && it.dayOfWeek != DayOfWeek.SUNDAY it !in allHolidaysList && it.dayOfWeek != DayOfWeek.SATURDAY && it.dayOfWeek != DayOfWeek.SUNDAY
} }

val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(filteredLastMonthDays.first(),filteredLastMonthDays.last())
val args = mutableMapOf(
"from" to filteredLastMonthDays.first(),
"to" to filteredLastMonthDays.last(),
)
val ts = timesheetsService.workHourRecordsWithinRange(args)
val timesheet: List<WorkHourRecords> = ts.map {
WorkHourRecords(
staffId = it["staffId"].toString().toLong(),
recordDate = LocalDate.parse(it["recordDate"].toString()),
hours = it["hours"].toString().toDouble()
)
}
// val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(filteredLastMonthDays.first(),filteredLastMonthDays.last())
val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull(FULLTIME) val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull(FULLTIME)
val teams = teamRepository.findAll().filter { team -> team.deleted == false } val teams = teamRepository.findAll().filter { team -> team.deleted == false }


@@ -128,17 +143,36 @@ open class MailReminderService(
val teamMembers: List<Staff> = staffs.filter { it.team != null && it.team.id == team.id } val teamMembers: List<Staff> = staffs.filter { it.team != null && it.team.id == team.id }
if (teamMembers.isEmpty()) continue if (teamMembers.isEmpty()) continue
val teamMembersIds: List<Long?> = teamMembers.map { it.id }.sorted() val teamMembersIds: List<Long?> = teamMembers.map { it.id }.sorted()
val filteredTimesheet = timesheet.filter { teamMembersIds.contains(it.staff?.id) }
val timesheetByIdAndRecord = filteredTimesheet.groupBy { it.staff?.id to it.recordDate }
.map { (key, _) ->
val (staffId, recordDate) = key
recordDate to mutableListOf<Long>(staffId ?: 0)
// val filteredTimesheet = timesheet.filter { teamMembersIds.contains(it.staff?.id) }
val filteredTimesheet = timesheet.filter { teamMembersIds.contains(it.staffId) }
// val timesheetByIdAndRecord = filteredTimesheet.groupBy { it.staff?.id to it.recordDate }
// .map { (key, _) ->
// val (staffId, recordDate) = key
// recordDate to mutableListOf<Long>(staffId ?: 0)
// }
println(filteredTimesheet.filter { it.staffId == 4.toLong() })
val timesheetByIdAndRecord = filteredTimesheet.groupBy { it.staffId to it.recordDate }
.mapNotNull { (key, records) ->
Triple(
key.second,
key.first,
records.sumOf { it.hours }
)
} }
val goodStaffsList = filteredLastMonthDays.map { day ->
timesheetByIdAndRecord.find {
it.first == day
} ?: Pair(day, mutableListOf())
}.sortedBy { it.first }
// val goodStaffsList = filteredLastMonthDays.map { day ->
// timesheetByIdAndRecord.find {
// it.first == day
// } ?: Pair(day, mutableListOf())
// }.sortedBy { it.first }

val goodStaffsList = filteredLastMonthDays.map { date ->
val matchedStaffIds = timesheetByIdAndRecord
.filter { it.first == date && it.third >= 8 }
.map { it.second } // Extracting the second element (staffId)
// Returning a Pair of the date and the list of matched staff IDs
Pair(date, matchedStaffIds)
}.sortedBy { it.first } // Sort by date
// return
// creating the email // creating the email
val intro = StringBuilder("${teamLead.name}, Staffs Missing Timesheet in the Table Below: \n") val intro = StringBuilder("${teamLead.name}, Staffs Missing Timesheet in the Table Below: \n")
val tableData = mutableListOf<TableRow>() val tableData = mutableListOf<TableRow>()
@@ -148,7 +182,7 @@ open class MailReminderService(
goodStaffsList.forEach { (key, value) -> goodStaffsList.forEach { (key, value) ->
if (!value.contains(it.id!!)) { if (!value.contains(it.id!!)) {
isNaughty = true isNaughty = true
missingDates.add(key!!)
missingDates.add(key)
} }
} }
if (!isNaughty) return@forEach if (!isNaughty) return@forEach
@@ -176,7 +210,19 @@ open class MailReminderService(
val allHolidaysList: List<LocalDate> = (holidayList + companyHolidayList).toSet().toList() val allHolidaysList: List<LocalDate> = (holidayList + companyHolidayList).toSet().toList()


//get data //get data
val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(firstDay, today)
val args = mutableMapOf(
"from" to firstDay,
"to" to today,
)
val ts = timesheetsService.workHourRecordsWithinRange(args)
val timesheet: List<WorkHourRecords> = ts.map {
WorkHourRecords(
staffId = it["staffId"].toString().toLong(),
recordDate = LocalDate.parse(it["recordDate"].toString()),
hours = it["hours"].toString().toDouble()
)
}
// val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(firstDay, today)
val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull(FULLTIME) val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull(FULLTIME)
val teams = teamRepository.findAll().filter { team -> team.deleted == false } val teams = teamRepository.findAll().filter { team -> team.deleted == false }


@@ -195,18 +241,24 @@ open class MailReminderService(
if (teamMembers.isEmpty()) continue if (teamMembers.isEmpty()) continue
val teamMembersIds: List<Long?> = teamMembers.map { it.id }.sorted() val teamMembersIds: List<Long?> = teamMembers.map { it.id }.sorted()
// getting the naughty list // getting the naughty list
val filteredTimesheet = timesheet.filter { teamMembersIds.contains(it.staff?.id) } // filter team members' timesheet
val timesheetByIdAndRecord = filteredTimesheet.groupBy { it.staff?.id to it.recordDate }
.map { (key, _) ->
val (staffId, recordDate) = key
recordDate to mutableListOf<Long>(staffId ?: 0)
val filteredTimesheet = timesheet.filter { teamMembersIds.contains(it.staffId) } // filter team members' timesheet
val timesheetByIdAndRecord = filteredTimesheet.groupBy { it.staffId to it.recordDate }
.mapNotNull { (key, records) ->
Triple(
key.second,
key.first,
records.sumOf { it.hours }
)
} }
// change the date list with desired time range // change the date list with desired time range
val goodStaffsList = filteredDatesList.map { day ->
timesheetByIdAndRecord.find {
it.first == day
} ?: Pair(day, mutableListOf())
val goodStaffsList = filteredDatesList.map { date ->
val matchedStaffIds = timesheetByIdAndRecord
.filter { it.first == date && it.third >= 8 }
.map { it.second } // Extracting the second element (staffId)
// Returning a Pair of the date and the list of matched staff IDs
Pair(date, matchedStaffIds)
}.sortedBy { it.first } }.sortedBy { it.first }

// creating the email content // creating the email content
val intro = StringBuilder("${teamLead.name}, Staffs Missing Timesheet in the Table Below: ($firstDay-$today) \n") val intro = StringBuilder("${teamLead.name}, Staffs Missing Timesheet in the Table Below: ($firstDay-$today) \n")
val tableData = mutableListOf<TableRow>() val tableData = mutableListOf<TableRow>()
@@ -216,7 +268,7 @@ open class MailReminderService(
goodStaffsList.forEach { (key, value) -> goodStaffsList.forEach { (key, value) ->
if (!value.contains(it.id!!)) { if (!value.contains(it.id!!)) {
isNaughty = true isNaughty = true
missingDates.add(key!!)
missingDates.add(key)
} }
} }
if (!isNaughty) return@forEach if (!isNaughty) return@forEach
@@ -267,22 +319,46 @@ open class MailReminderService(
daysBefore = daysBefore.minusDays(1) daysBefore = daysBefore.minusDays(1)
} }


val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(sevenDaysBefore, fourDaysBefore)
val args = mutableMapOf(
"from" to sevenDaysBefore,
"to" to fourDaysBefore,
)

val ts = timesheetsService.workHourRecordsWithinRange(args)
val timesheet: List<WorkHourRecords> = ts.map {
WorkHourRecords(
staffId = it["staffId"].toString().toLong(),
recordDate = LocalDate.parse(it["recordDate"].toString()),
hours = it["hours"].toString().toDouble()
)
}
// val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(sevenDaysBefore, fourDaysBefore)
val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull(FULLTIME) // FT? FT? etc val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull(FULLTIME) // FT? FT? etc
val staffIds: List<Long> = staffs.map { it.id as Long } val staffIds: List<Long> = staffs.map { it.id as Long }


val timesheetByIdAndRecord = timesheet.groupBy { it.staff?.id to it.recordDate }
.map { (key, _) ->
val (staffId, recordDate) = key
"$recordDate" to mutableListOf<Long>(staffId ?: 0)
}
val goodStaffsList = workingDaysList.map { it ->
val key = it.toString()
timesheetByIdAndRecord.find {
it.first == key
}?: Pair(key, mutableListOf())
}.sortedBy { it.first }

// val timesheetByIdAndRecord = timesheet.groupBy { it.staff?.id to it.recordDate }
// .map { (key, _) ->
// val (staffId, recordDate) = key
// "$recordDate" to mutableListOf<Long>(staffId ?: 0)
// }
val timesheetByIdAndRecord = timesheet.groupBy {
it.staffId to it.recordDate
}.mapNotNull { (key, records) ->
Triple(
key.second,
key.first,
records.sumOf { it.hours }
)
}
val goodStaffsList = workingDaysList.map { date ->
val matchedStaffIds = timesheetByIdAndRecord
.filter { it.first == date && it.third >= 8 }
.map { it.second } // Extracting the second element (staffId)
// Returning a Pair of the date and the list of matched staff IDs
Pair(date, matchedStaffIds)
}.sortedBy { it.first } // Sort by date
println("goodStaffsList")
println(goodStaffsList)
// change this list with the staffs that need checking // change this list with the staffs that need checking
staffIds.forEach { id -> staffIds.forEach { id ->
var isNaughty: Boolean = false var isNaughty: Boolean = false


+ 9
- 0
src/main/java/com/ffii/tsms/modules/common/mail/web/models/WorkHourRecords.kt View File

@@ -0,0 +1,9 @@
package com.ffii.tsms.modules.common.mail.web.models

import java.time.LocalDate

data class WorkHourRecords(
val staffId: Long,
val recordDate: LocalDate,
val hours: Double,
)

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

@@ -10,4 +10,6 @@ interface LeaveRepository : AbstractRepository<Leave, Long> {
fun deleteAllByStaffAndRecordDate(staff: Staff, recordDate: LocalDate) fun deleteAllByStaffAndRecordDate(staff: Staff, recordDate: LocalDate)


fun findByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Leave> fun findByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Leave>
fun findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(start: LocalDate, end: LocalDate): List<Leave>

} }

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

@@ -1,6 +1,9 @@
package com.ffii.tsms.modules.timesheet.service package com.ffii.tsms.modules.timesheet.service


import com.ffii.core.exception.BadRequestException import com.ffii.core.exception.BadRequestException
import com.ffii.core.support.AbstractBaseEntityService
import com.ffii.core.support.JdbcDao
import com.ffii.tsms.modules.data.entity.Staff
import com.ffii.tsms.modules.data.entity.StaffRepository import com.ffii.tsms.modules.data.entity.StaffRepository
import com.ffii.tsms.modules.data.service.StaffsService import com.ffii.tsms.modules.data.service.StaffsService
import com.ffii.tsms.modules.data.service.TeamService import com.ffii.tsms.modules.data.service.TeamService
@@ -28,8 +31,10 @@ open class TimesheetsService(
private val projectRepository: ProjectRepository, private val projectRepository: ProjectRepository,
private val taskRepository: TaskRepository, private val taskRepository: TaskRepository,
private val staffsService: StaffsService, private val staffsService: StaffsService,
private val teamService: TeamService, private val staffRepository: StaffRepository, private val leaveRepository: LeaveRepository
) {
private val teamService: TeamService,
private val staffRepository: StaffRepository, private val leaveRepository: LeaveRepository,
private val jdbcDao: JdbcDao,
) : AbstractBaseEntityService<Timesheet, Long, TimesheetRepository>(jdbcDao, timesheetRepository) {
@Transactional @Transactional
open fun saveTimesheet(recordTimeEntry: Map<LocalDate, List<TimeEntry>>): Map<String, List<TimeEntry>> { open fun saveTimesheet(recordTimeEntry: Map<LocalDate, List<TimeEntry>>): Map<String, List<TimeEntry>> {
// Need to be associated with a staff // Need to be associated with a staff
@@ -378,4 +383,45 @@ open class TimesheetsService(


return "Rearrange success" return "Rearrange success"
} }
open fun workHourRecordsWithinRange(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder("WITH ts_cte AS ("
+ " SELECT "
+ " t.recordDate, "
+ " t.staffId, "
+ " SUM(coalesce(t.normalConsumed, 0)) + SUM(COALESCE(t.otConsumed, 0)) AS hours "
+ " , 'normal' as tp"
+ " FROM timesheet t "
+ " where t.deleted = false "
+ " GROUP BY t.staffId, t.recordDate "
+ " ), "
+ " l_cte AS ( "
+ " SELECT "
+ " l.recordDate, "
+ " l.staffId, "
+ " SUM(COALESCE(l.leaveHours, 0)) AS hours "
+ " ,'ot' as tp "
+ " FROM `leave` l "
+ " where l.deleted = false "
+ " GROUP BY l.staffId, l.recordDate "
+ " ) "
+ " select "
+ " recordDate, "
+ " staffId, "
+ " hours "
+ " ,tp "
+ " from ( "
+ " SELECT "
+ " * "
+ " FROM ts_cte tc "
+ " union "
+ " SELECT "
+ " * "
+ " FROM l_cte lc "
+ " ) ut "
)
if (args.containsKey("from") && args.containsKey("to"))
sql.append(" where recordDate BETWEEN :from AND :to ");
return jdbcDao.queryForList(sql.toString(), args)
}

} }

Loading…
Cancel
Save