diff --git a/src/main/java/com/ffii/tsms/modules/common/holiday/service/HolidayService.kt b/src/main/java/com/ffii/tsms/modules/common/holiday/service/HolidayService.kt index 375d868..5f8f695 100644 --- a/src/main/java/com/ffii/tsms/modules/common/holiday/service/HolidayService.kt +++ b/src/main/java/com/ffii/tsms/modules/common/holiday/service/HolidayService.kt @@ -3,29 +3,45 @@ package com.ffii.tsms.modules.common.holiday.service import com.ffii.tsms.modules.common.holiday.models.CommonHolidayResponse import com.ffii.tsms.modules.common.holiday.models.VCalendarRequest import com.squareup.moshi.Moshi -import com.squareup.moshi.adapter import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import okhttp3.Request import okhttp3.OkHttpClient -import okhttp3.ResponseBody +import okhttp3.Request import org.apache.commons.logging.Log import org.apache.commons.logging.LogFactory import org.springframework.stereotype.Service import java.io.IOException import java.time.LocalDate import java.time.format.DateTimeFormatter +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager + @Service open class HolidayService() { + private val trustAllCerts = arrayOf(object : X509TrustManager { + override fun checkClientTrusted(chain: Array, authType: String) {} + override fun checkServerTrusted(chain: Array, authType: String) {} + override fun getAcceptedIssuers(): Array { + return arrayOf() + } + }) + private var sslContext: SSLContext = SSLContext.getInstance("SSL").apply { + init(null, trustAllCerts, java.security.SecureRandom()) + } +// newClient.newCall(request).execute() private val logger: Log = LogFactory.getLog(javaClass) private val HOLIDAY_URL = "https://www.1823.gov.hk/common/ical/tc.json" open fun commonHolidayList(): List { - val client = OkHttpClient() + val newBuilder = OkHttpClient.Builder() + newBuilder.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager) + newBuilder.hostnameVerifier { _, _ -> true } + + val client = newBuilder.build() val request = Request.Builder() .url(HOLIDAY_URL) .build() - client.newCall(request).execute().use { response -> if (!response.isSuccessful) throw IOException("Unexpected code: $response") diff --git a/src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt b/src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt index 1d25bc0..73f8f68 100644 --- a/src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt +++ b/src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt @@ -1,117 +1,409 @@ package com.ffii.tsms.modules.common.mail.service -import com.ffii.tsms.modules.common.SecurityUtils import com.ffii.tsms.modules.common.SettingNames import com.ffii.tsms.modules.common.holiday.service.HolidayService import com.ffii.tsms.modules.common.mail.pojo.MailRequest +import com.ffii.tsms.modules.data.entity.Staff import com.ffii.tsms.modules.data.entity.StaffRepository +import com.ffii.tsms.modules.data.entity.TeamRepository +import com.ffii.tsms.modules.data.service.CompanyHolidayService import com.ffii.tsms.modules.data.service.StaffsService -import com.ffii.tsms.modules.project.web.models.MilestoneInfo import com.ffii.tsms.modules.settings.service.SettingsService 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 jakarta.mail.internet.InternetAddress import org.apache.commons.logging.Log import org.apache.commons.logging.LogFactory +import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Service import java.time.DayOfWeek import java.time.LocalDate +import java.time.YearMonth import java.time.format.DateTimeFormatter -import java.time.temporal.TemporalAdjuster -import java.time.temporal.TemporalAdjusters +data class TableRow( + val staffId: String, + val name: String, + val missingDates: MutableList, +) @Service open class MailReminderService( val mailService: MailService, val userService: UserService, val settingsService: SettingsService, val holidayService: HolidayService, - val timesheetsService: TimesheetsService, val timesheetRepository: TimesheetRepository, val staffsService: StaffsService, val staffRepository: StaffRepository, + val companyHolidayService: CompanyHolidayService, + val teamRepository: TeamRepository ) { protected val logger: Log = LogFactory.getLog(javaClass) private val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") - // @Scheduled(cron = "0 0 6 * * ?") // Runs at 06:00 AM every day - open fun sendTimesheetReminder() { - val inputDate = LocalDate.now().minusDays(4) - val holidayList = holidayService.commonHolidayList().map { it.date } - val dayOfWeek = inputDate.dayOfWeek - - if (!holidayList.contains(inputDate) && dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) { - // Timesheet Data - val timesheet = timesheetRepository.findAllByDeletedFalseAndRecordDate(inputDate) - .groupBy { it.staff?.id } - .mapValues {(_, t) -> - t.map { (it.normalConsumed ?: 0.0) } - } - - // Staff Data - val staffs = staffRepository.findAllByDeletedFalse().orElseThrow() - - val toList = mutableListOf() - staffs.forEach { staff -> - val sum = timesheet[staff.id]?.sum() ?: 0.0 + private fun isSettingsConfigValid(): Boolean { + try { + val username = settingsService.findByName(SettingNames.MAIL_SMTP_USERNAME).orElseThrow().value + val pw = settingsService.findByName(SettingNames.MAIL_SMTP_USERNAME).orElseThrow().value + return !username.isNullOrEmpty() && !pw.isNullOrEmpty() - if (sum < 8) { - toList += staff.email - } - } - - val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value - val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value - val cc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_CC).orElseThrow().value.split(",") - val bcc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_BCC).orElseThrow().value.split(",") - val mailRequest = MailRequest.Builder() - .subject(subject) - .template("mail/TimesheetNotification") - .args( - mapOf( - Pair("date", inputDate.format(dateFormat)), - Pair("template", template) - ) - ) -// .addTo(InternetAddress("cyril.tsui@2fi-solutions.com.hk")) - .addTo(toList) - .addCc(cc) - .addBcc(bcc) - .build() - - val mailRequestList = mutableListOf() - mailRequestList += mailRequest - mailService.send(mailRequestList) + } catch (e: Error) { + return false } } + private fun createHTMLTable(naughtyList: MutableList): String { + val tableStarter = StringBuilder(" ") + val header = StringBuilder( + " " + + " " + + " " + + " " + + " " + ) + tableStarter.append(header) + for (per in naughtyList) { + tableStarter.append( + " " + + " " + + " " + + " " + + " " + ) + } + val footer = StringBuilder("
StaffIdNameMissing Dates
${per.staffId}${per.name}${per.missingDates.joinToString(", ")}
") + return tableStarter.toString() + } - open fun sendTimesheetReminderTest() { - val inputDate = LocalDate.now().minusDays(4).format(dateFormat) - + private fun createEmailRequest(content: String, emailTo: List) { val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value val cc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_CC).orElseThrow().value.split(",") - logger.info(cc) val bcc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_BCC).orElseThrow().value.split(",") val mailRequest = MailRequest.Builder() .subject(subject) -// .template("mail/TimesheetNotification") .templateContent(template) .args( mapOf( - Pair("date", inputDate), -// Pair("template", template) + Pair("date", content), ) ) - .addTo(InternetAddress(SecurityUtils.getUser().orElseThrow().email)) + .addTo(emailTo) .addCc(cc) .addBcc(bcc) .build() - val mailRequestList = mutableListOf() mailRequestList += mailRequest mailService.send(mailRequestList) } + + @Scheduled(cron = "0 0 6 7 * ?") // (SS/MM/HH/DD/MM/YY) + open fun sendTimesheetToTeamLead7TH() { + if (!isSettingsConfigValid()) return + val today = LocalDate.now() + val dayOfWeek = today.dayOfWeek + + val holidayList = holidayService.commonHolidayList().map { it.date } + val companyHolidayList = companyHolidayService.allCompanyHolidays().map { it.date } + val allHolidaysList: List = (holidayList + companyHolidayList).toSet().toList() + //check if today is holiday + if (allHolidaysList.contains(today) || dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) return + // get working days from last month + val lastMonth = today.minusMonths(1) + val yearMonth = YearMonth.from(lastMonth) + val daysInLastMonth = yearMonth.lengthOfMonth() + val lastMonthDays: List = (1..daysInLastMonth).map { LocalDate.of(yearMonth.year, yearMonth.month, it) } + val filteredLastMonthDays = lastMonthDays.filter { + it !in allHolidaysList && it.dayOfWeek != DayOfWeek.SATURDAY && it.dayOfWeek != DayOfWeek.SUNDAY + } + + val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(filteredLastMonthDays.first(),filteredLastMonthDays.last()) + val staffs = staffRepository.findAllByEmployTypeAndDeletedFalse("Full Time") + val teams = teamRepository.findAll().filter { team -> team.deleted == false } + + for (team in teams) { +// if (team.id?.toInt() != 5) continue // remove this when finishes + val teamLead = team.staff + val teamMembers: List = staffs.filter { it.team.id == team.id } + if (teamMembers.isEmpty()) continue + val teamMembersIds: List = 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(staffId ?: 0) + } + val goodStaffsList = filteredLastMonthDays.map { day -> + timesheetByIdAndRecord.find { + it.first == day + } ?: Pair(day, mutableListOf()) + }.sortedBy { it.first } + // creating the email + val intro = StringBuilder("${teamLead.name}, Staffs Missing Timesheet in the Table Below: \n") + val tableData = mutableListOf() + teamMembers.forEach { + var isNaughty = false + val missingDates = mutableListOf() + goodStaffsList.forEach { (key, value) -> + if (!value.contains(it.id!!)) { + isNaughty = true + missingDates.add(key!!) + } + } + if (!isNaughty) return@forEach + val rowData = TableRow(it.staffId, it.name, missingDates) + tableData.add(rowData) + } + val table = createHTMLTable(tableData) + val emailContent = intro + .append(table) + + val receiver = listOf(teamLead.email) + createEmailRequest(emailContent.toString(), receiver) + } + } + +// @Scheduled(cron = "0 51 14 * * ?") // (SS/MM/HH/DD/MM/YY) + @Scheduled(cron = "0 0 6 15 * ?") // (SS/MM/HH/DD/MM/YY) + open fun sendTimesheetToTeamLead15TH() { + if (!isSettingsConfigValid()) return + + val today = LocalDate.now() // should always be 15 + val firstDay = today.withDayOfMonth(1) + val holidayList = holidayService.commonHolidayList().map { it.date } + val companyHolidayList = companyHolidayService.allCompanyHolidays().map { it.date } + val allHolidaysList: List = (holidayList + companyHolidayList).toSet().toList() + + //get data + val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(firstDay, today) + val staffs = staffRepository.findAllByEmployTypeAndDeletedFalse("Full Time") + val teams = teamRepository.findAll().filter { team -> team.deleted == false } + + val dateList = generateSequence(firstDay) { it.plusDays(1) } + .takeWhile { it <= today } + .toList() + val filteredDatesList = dateList.filter { + it !in allHolidaysList && it.dayOfWeek != DayOfWeek.SATURDAY && it.dayOfWeek != DayOfWeek.SUNDAY + } + + //loop each team + for (team in teams) { +// if (team.id?.toInt() != 5) continue // just for testing with fewer records, remove this when finishes + val teamLead = team.staff + val teamMembers: List = staffs.filter { it.team.id == team.id } + if (teamMembers.isEmpty()) continue + val teamMembersIds: List = teamMembers.map { it.id }.sorted() + // 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(staffId ?: 0) + } + // change the date list with desired time range + val goodStaffsList = filteredDatesList.map { day -> + timesheetByIdAndRecord.find { + it.first == day + } ?: Pair(day, mutableListOf()) + }.sortedBy { it.first } + // creating the email content + val intro = StringBuilder("${teamLead.name}, Staffs Missing Timesheet in the Table Below: ($firstDay-$today) \n") + val tableData = mutableListOf() + teamMembers.forEach { + var isNaughty = false + val missingDates = mutableListOf() + goodStaffsList.forEach { (key, value) -> + if (!value.contains(it.id!!)) { + isNaughty = true + missingDates.add(key!!) + } + } + if (!isNaughty) return@forEach + val rowData = TableRow(it.staffId, it.name, missingDates) + tableData.add(rowData) + } + val table = createHTMLTable(tableData) // custom fn creating the html table + val emailContent = intro + .append(table) + + val receiver = listOf(teamLead.email) + createEmailRequest(emailContent.toString(), receiver) + } + } + @Scheduled(cron = "0 0 6 * * ?") // (SS/MM/HH/DD/MM/YY) - Runs at 06:00 AM every day + open fun sendTimesheetReminder() { + if (!isSettingsConfigValid()) return + + val today = LocalDate.now() + val dayOfWeek = today.dayOfWeek + + val holidayList = holidayService.commonHolidayList().map { it.date } + val companyHolidayList = companyHolidayService.allCompanyHolidays().map { it.date } + val allHolidaysList: List = (holidayList + companyHolidayList).toSet().toList() + + if (!allHolidaysList.contains(today) && dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) { + // get working day + var daysChecking: Int = 0 + var fourDaysBefore: LocalDate = today + var sevenDaysBefore: LocalDate = today + var daysBefore: LocalDate = today.minusDays(1) + + val workingDaysList = mutableListOf() + + while (true) { + if (!allHolidaysList.contains(daysBefore) && daysBefore.dayOfWeek != DayOfWeek.SATURDAY && daysBefore.dayOfWeek != DayOfWeek.SUNDAY) { + daysChecking++ + if (daysChecking in 4..7) { + workingDaysList.add(daysBefore) + } + if (daysChecking == 4) { + fourDaysBefore = daysBefore + } else if (daysChecking == 7) { + sevenDaysBefore = daysBefore + break + } + } + daysBefore = daysBefore.minusDays(1) + } + + val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(sevenDaysBefore, fourDaysBefore) + val staffs = staffRepository.findAllByEmployTypeAndDeletedFalse("Full Times") // Full Time? FT? etc + val staffIds: List = 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(staffId ?: 0) + } + val goodStaffsList = workingDaysList.map { it -> + val key = it.toString() + timesheetByIdAndRecord.find { + it.first == key + }?: Pair(key, mutableListOf()) + }.sortedBy { it.first } + + // change this list with the staffs that need checking + staffIds.forEach { id -> + var isNaughty: Boolean = false + val message = StringBuilder("Enter timesheet for:") + goodStaffsList.forEach { (key, value) -> + if (!value.contains(id)) { + isNaughty = true + message.append("\n [$key] ") + } + } + println(message) + if (!isNaughty) return@forEach + val emailAddress = staffRepository.findById(id).get().email + val receiver = listOf(emailAddress) + createEmailRequest(message.toString(), receiver) + } + } + } + + open fun sendTimesheetReminderTest() { + val today = LocalDate.now() + val dayOfWeek = today.dayOfWeek + + val holidayList = holidayService.commonHolidayList().map { it.date } + val companyHolidayList = companyHolidayService.allCompanyHolidays().map { it.date } + val allHolidaysList: List = (holidayList + companyHolidayList).toSet().toList() + + if (!allHolidaysList.contains(today) && dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) { + // get working day + var daysChecking: Int = 0 + var fourDaysBefore: LocalDate = today + var sevenDaysBefore: LocalDate = today + var daysBefore: LocalDate = today.minusDays(1) + + val workingDaysList = mutableListOf() + + while (true) { + if (!allHolidaysList.contains(daysBefore) && daysBefore.dayOfWeek != DayOfWeek.SATURDAY && daysBefore.dayOfWeek != DayOfWeek.SUNDAY) { + daysChecking++ + if (daysChecking in 4..7) { + workingDaysList.add(daysBefore) + } + if (daysChecking == 4) { + fourDaysBefore = daysBefore + } else if (daysChecking == 7) { + sevenDaysBefore = daysBefore + break + } + } + daysBefore = daysBefore.minusDays(1) + } + + val timesheet = timesheetRepository.findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(sevenDaysBefore,fourDaysBefore) + // just getting my own staff record + // change it back later + val staffs = staffRepository.findAllByEmployTypeAndDeletedFalse("Full Times") + val staffIds: List = 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(staffId ?: 0) + } + val goodStaffsList = workingDaysList.map { it -> + val key = it.toString() + timesheetByIdAndRecord.find { + it.first == key + } ?: Pair(key, mutableListOf()) + }.sortedBy { it.first } + + val fullLog = StringBuilder("") + // change here with the actual list of staffIds + staffIds.forEach { id -> + var isNaughty: Boolean = false + val message = StringBuilder("Enter timesheet for:") +// val tableHtml = StringBuilder( +// "" +// + "" +// + "" +// + "" +// + "" +// ) + goodStaffsList.forEach { (key, value) -> + if (!value.contains(id)) { + isNaughty = true + message.append("\n [$key] ") + } + } + println(message) + fullLog.append("\n $id, $message") + println("about to return") + if (!isNaughty) return@forEach + println("no return") + val emailAddress = staffRepository.findById(id).get().email +// println(emailAddress) + val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value + val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value + val cc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_CC).orElseThrow().value.split(",") + val bcc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_BCC).orElseThrow().value.split(",") + val mailRequest = MailRequest.Builder() + .subject(subject) + .templateContent(template) +// .template("mail/TimesheetNotification") + .args( + mapOf( + Pair("date", message), +// Pair("template", template) + ) + ) +// .addTo(InternetAddress(emailAddress)) + .addTo(InternetAddress("derek.chan@2fi-solutions.com.hk")) +// .addTo(toList) + .addCc(cc) + .addBcc(bcc) + .build() + + val mailRequestList = mutableListOf() + mailRequestList += mailRequest + mailService.send(mailRequestList) + } + println(fullLog) + } + + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java b/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java index 0559e1c..890f3d5 100644 --- a/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java +++ b/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java @@ -17,11 +17,15 @@ public interface StaffRepository extends AbstractRepository { List findAllStaffSearchInfoByIdIn(List ids); Optional findByStaffId(@Param("staffId") String staffId); + List findTeamLeadByIdIn(List ids); +// String findEmailById(Long id); Optional findStaffSearchInfoById(@Param("id") Long id); Optional findByUserId(@Param("userId") Long userId); Optional> findAllByTeamIdAndDeletedFalse(Long id); + List findAllByEmployTypeAndDeletedFalse(@Param("employType") String employType); + Optional> findAllByDeletedFalse(); Optional findIdAndNameByUserIdAndDeletedFalse(Long id); diff --git a/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt b/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt index c962145..f16765f 100644 --- a/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt +++ b/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt @@ -304,4 +304,17 @@ open class StaffsService( userRepository.saveAll(users) } + + open fun staffInvolvedProjects(args: Map): List> { + val sql = StringBuilder("select distinct" + + " p.id, " + + " p.code, " + + " p.name, " + + " p.status " + + " from timesheet t " + + " inner join project p on p.id = t.projectId " + + " where t.staffId = :id " + ) + return jdbcDao.queryForList(sql.toString(), args) + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt b/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt index 10ddc0c..703d545 100644 --- a/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt +++ b/src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt @@ -10,6 +10,7 @@ import com.ffii.tsms.modules.data.service.StaffsService import com.ffii.tsms.modules.data.web.models.NewStaffRequest import jakarta.servlet.http.HttpServletRequest import jakarta.validation.Valid +import liquibase.hub.model.Project import org.springframework.http.HttpStatus import org.springframework.web.bind.ServletRequestBindingException import org.springframework.web.bind.annotation.* @@ -31,6 +32,17 @@ class StaffsController(private val staffsService: StaffsService) { fun StaffWithoutTeam(): List { return staffsService.StaffWithoutTeam() } + + @GetMapping("/staff-projects/{id}") + fun staffProjects(@PathVariable id: Long): List> { + val args = mutableMapOf("id" to id) + return staffsService.staffInvolvedProjects(args) + } + +// @GetMapping("/staff-projects") +// fun staffProject(): List { +// return projectService.StaffWithoutTeam() +// } // @GetMapping("/list") // fun list(): List { // return staffsService.getTeamLeads() diff --git a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt index bf2bebc..1e0f2a5 100644 --- a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt +++ b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt @@ -2068,54 +2068,54 @@ open class ReportService( + " and p.actualEnd BETWEEN :startDate and :endDate " + " group by pt.project_id " + " ) as result " - + " left join invoice i on result.code = i.projectCode " + + " left join invoice i on result.code = i.projectCode and i.deleted = false" + " order by result.actualEnd " ) return jdbcDao.queryForList(sql.toString(), args) } - - open fun getProjectResourceOverconsumptionReport_projectInfo(args: Map): List> { - val sql = StringBuilder("SELECT" - + " t.projectId as id, " - + " p.code as projectCode, " - + " p.name as projectName, " - + " te.code as team, " - + " CONCAT( c.code, ' - ' ,c.name ) as client, " - + " COALESCE(concat(sub.code, ' - ', sub.name), 'N/A') as subsidaiary, " - + " (p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8 as plannedBudget " - + " from timesheet t " - + " left join project p on p.id = t.projectId " - + " left join team te on te.teamLead = p.teamLead " - + " left join customer c ON p.customerId = c.id " - + " left join subsidiary sub on sub.id = p.customerSubsidiaryId " - + " where t.projectid is not NULL " - + " and p.deleted = false " - + " and p.status = 'On-going' " - ) - if (args != null) { - if (args.containsKey("teamId")) - sql.append(" and tm.id = :teamId") - if (args.containsKey("custId")) - sql.append(" and c.id = :custId") - if (args.containsKey("subsidiaryId")) - sql.append(" and ss.id = :subsidiaryId") - } - sql.append(" group by t.projectId; ") - return jdbcDao.queryForList(sql.toString(), args) - } - - open fun getProjectResourceOverconsumptionReport_timesheetInfo(): List> { - val sql = StringBuilder("SELECT" - + " t.*, " - + " sal.hourlyRate " - + " from timesheet t " - + " left join staff s on s.id = t.staffId " - + " left join salary sal on sal.salaryPoint = s.salaryId " - + " where t.deleted = false " - ) - return jdbcDao.queryForList(sql.toString()) - } +// +// open fun getProjectResourceOverconsumptionReport_projectInfo(args: Map): List> { +// val sql = StringBuilder("SELECT" +// + " t.projectId as id, " +// + " p.code as projectCode, " +// + " p.name as projectName, " +// + " te.code as team, " +// + " CONCAT( c.code, ' - ' ,c.name ) as client, " +// + " COALESCE(concat(sub.code, ' - ', sub.name), 'N/A') as subsidaiary, " +// + " (p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8 as plannedBudget " +// + " from timesheet t " +// + " left join project p on p.id = t.projectId " +// + " left join team te on te.teamLead = p.teamLead " +// + " left join customer c ON p.customerId = c.id " +// + " left join subsidiary sub on sub.id = p.customerSubsidiaryId " +// + " where t.projectid is not NULL " +// + " and p.deleted = false " +// + " and p.status = 'On-going' " +// ) +// if (args != null) { +// if (args.containsKey("teamId")) +// sql.append(" and tm.id = :teamId") +// if (args.containsKey("custId")) +// sql.append(" and c.id = :custId") +// if (args.containsKey("subsidiaryId")) +// sql.append(" and ss.id = :subsidiaryId") +// } +// sql.append(" group by t.projectId; ") +// return jdbcDao.queryForList(sql.toString(), args) +// } +// +// open fun getProjectResourceOverconsumptionReport_timesheetInfo(): List> { +// val sql = StringBuilder("SELECT" +// + " t.*, " +// + " sal.hourlyRate " +// + " from timesheet t " +// + " left join staff s on s.id = t.staffId " +// + " left join salary sal on sal.salaryPoint = s.salaryId " +// + " where t.deleted = false " +// ) +// return jdbcDao.queryForList(sql.toString()) +// } open fun getProjectResourceOverconsumptionReport(args: Map): List> { val sql = StringBuilder( diff --git a/src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt b/src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt index 653e803..0e0caef 100644 --- a/src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt +++ b/src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt @@ -6,6 +6,7 @@ import com.ffii.tsms.modules.project.entity.Project import com.ffii.tsms.modules.timesheet.entity.projections.TimesheetHours import com.ffii.tsms.modules.project.entity.ProjectTask import org.springframework.data.jpa.repository.Query +import java.io.Serializable import java.time.LocalDate interface TimesheetRepository : AbstractRepository { @@ -26,6 +27,8 @@ interface TimesheetRepository : AbstractRepository { fun findByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List + fun findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(start: LocalDate, end: LocalDate): List + fun findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List @Query("SELECT MIN(t.recordDate) AS recordDate FROM Timesheet t WHERE t.project.id = ?1")
Product Name Count