ソースを参照

update scheduled email & reports update

tags/Baseline_30082024_BACKEND_UAT
MSI\derek 1年前
コミット
05b1e2e5c7
7個のファイルの変更450行の追加110行の削除
  1. +21
    -5
      src/main/java/com/ffii/tsms/modules/common/holiday/service/HolidayService.kt
  2. +354
    -62
      src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt
  3. +4
    -0
      src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java
  4. +13
    -0
      src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt
  5. +12
    -0
      src/main/java/com/ffii/tsms/modules/data/web/StaffsController.kt
  6. +43
    -43
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  7. +3
    -0
      src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt

+ 21
- 5
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.CommonHolidayResponse
import com.ffii.tsms.modules.common.holiday.models.VCalendarRequest import com.ffii.tsms.modules.common.holiday.models.VCalendarRequest
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import okhttp3.Request
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.ResponseBody
import okhttp3.Request
import org.apache.commons.logging.Log import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory import org.apache.commons.logging.LogFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.IOException import java.io.IOException
import java.time.LocalDate import java.time.LocalDate
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager



@Service @Service
open class HolidayService() { open class HolidayService() {
private val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {}
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
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 logger: Log = LogFactory.getLog(javaClass)
private val HOLIDAY_URL = "https://www.1823.gov.hk/common/ical/tc.json" private val HOLIDAY_URL = "https://www.1823.gov.hk/common/ical/tc.json"


open fun commonHolidayList(): List<CommonHolidayResponse> { open fun commonHolidayList(): List<CommonHolidayResponse> {
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() val request = Request.Builder()
.url(HOLIDAY_URL) .url(HOLIDAY_URL)
.build() .build()

client.newCall(request).execute().use { response -> client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code: $response") if (!response.isSuccessful) throw IOException("Unexpected code: $response")




+ 354
- 62
src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt ファイルの表示

@@ -1,117 +1,409 @@
package com.ffii.tsms.modules.common.mail.service 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.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.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.service.CompanyHolidayService
import com.ffii.tsms.modules.data.service.StaffsService 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.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
import org.apache.commons.logging.LogFactory import org.apache.commons.logging.LogFactory
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.DayOfWeek import java.time.DayOfWeek
import java.time.LocalDate import java.time.LocalDate
import java.time.YearMonth
import java.time.format.DateTimeFormatter 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<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,
val companyHolidayService: CompanyHolidayService,
val teamRepository: TeamRepository
) { ) {
protected val logger: Log = LogFactory.getLog(javaClass) protected val logger: Log = LogFactory.getLog(javaClass)


private val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") 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<String>()
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("[email protected]"))
.addTo(toList)
.addCc(cc)
.addBcc(bcc)
.build()

val mailRequestList = mutableListOf<MailRequest>()
mailRequestList += mailRequest
mailService.send(mailRequestList)
} catch (e: Error) {
return false
} }
} }
private fun createHTMLTable(naughtyList: MutableList<TableRow>): String {
val tableStarter = StringBuilder(" <table width='100%' border='1' align='center'> ")
val header = StringBuilder(
" <tr> "
+ " <th>StaffId</th> "
+ " <th>Name</th> "
+ " <th>Missing Dates</th> "
+ " </tr> "
)
tableStarter.append(header)
for (per in naughtyList) {
tableStarter.append(
" <tr> "
+ " <td>${per.staffId}</td>"
+ " <td>${per.name}</td>"
+ " <td>${per.missingDates.joinToString(", ")}</td>"
+ " </tr> "
)
}
val footer = StringBuilder(" </table> ")
return tableStarter.toString()
}


open fun sendTimesheetReminderTest() {
val inputDate = LocalDate.now().minusDays(4).format(dateFormat)

private fun createEmailRequest(content: String, emailTo: List<String>) {
val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value val subject = settingsService.findByName(SettingNames.TIMESHEET_MAIL_SUBJECT).orElseThrow().value
val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value val template = settingsService.findByName(SettingNames.TIMESHEET_MAIL_TEMPLATE).orElseThrow().value
val cc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_CC).orElseThrow().value.split(",") 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 bcc = settingsService.findByName(SettingNames.TIMESHEET_MAIL_BCC).orElseThrow().value.split(",")
val mailRequest = MailRequest.Builder() val mailRequest = MailRequest.Builder()
.subject(subject) .subject(subject)
// .template("mail/TimesheetNotification")
.templateContent(template) .templateContent(template)
.args( .args(
mapOf( mapOf(
Pair("date", inputDate),
// Pair("template", template)
Pair("date", content),
) )
) )
.addTo(InternetAddress(SecurityUtils.getUser().orElseThrow().email))
.addTo(emailTo)
.addCc(cc) .addCc(cc)
.addBcc(bcc) .addBcc(bcc)
.build() .build()

val mailRequestList = mutableListOf<MailRequest>() val mailRequestList = mutableListOf<MailRequest>()
mailRequestList += mailRequest mailRequestList += mailRequest
mailService.send(mailRequestList) 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<LocalDate> = (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<LocalDate> = (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<Staff> = staffs.filter { it.team.id == team.id }
if (teamMembers.isEmpty()) continue
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 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<TableRow>()
teamMembers.forEach {
var isNaughty = false
val missingDates = mutableListOf<LocalDate>()
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<LocalDate> = (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<Staff> = staffs.filter { it.team.id == team.id }
if (teamMembers.isEmpty()) continue
val teamMembersIds: List<Long?> = 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<Long>(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<TableRow>()
teamMembers.forEach {
var isNaughty = false
val missingDates = mutableListOf<LocalDate>()
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<LocalDate> = (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<LocalDate>()

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<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 }

// 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<LocalDate> = (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<LocalDate>()

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<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 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(
// "<table width='100%' border='1' align='center'>"
// + "<tr align='center'>"
// + "<td><b>Product Name <b></td>"
// + "<td><b>Count<b></td>"
// + "</tr>"
// )
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("[email protected]"))
// .addTo(toList)
.addCc(cc)
.addBcc(bcc)
.build()

val mailRequestList = mutableListOf<MailRequest>()
mailRequestList += mailRequest
mailService.send(mailRequestList)
}
println(fullLog)
}

}
} }

+ 4
- 0
src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java ファイルの表示

@@ -17,11 +17,15 @@ public interface StaffRepository extends AbstractRepository<Staff, Long> {
List<StaffSearchInfo> findAllStaffSearchInfoByIdIn(List<Serializable> ids); List<StaffSearchInfo> findAllStaffSearchInfoByIdIn(List<Serializable> ids);
Optional<Staff> findByStaffId(@Param("staffId") String staffId); Optional<Staff> findByStaffId(@Param("staffId") String staffId);


List<Staff> findTeamLeadByIdIn(List<Long> ids);
// String findEmailById(Long id);
Optional<StaffSearchInfo> findStaffSearchInfoById(@Param("id") Long id); Optional<StaffSearchInfo> findStaffSearchInfoById(@Param("id") Long id);


Optional<Staff> findByUserId(@Param("userId") Long userId); Optional<Staff> findByUserId(@Param("userId") Long userId);
Optional<List<Staff>> findAllByTeamIdAndDeletedFalse(Long id); Optional<List<Staff>> findAllByTeamIdAndDeletedFalse(Long id);


List<Staff> findAllByEmployTypeAndDeletedFalse(@Param("employType") String employType);

Optional<List<Staff>> findAllByDeletedFalse(); Optional<List<Staff>> findAllByDeletedFalse();


Optional<Staff> findIdAndNameByUserIdAndDeletedFalse(Long id); Optional<Staff> findIdAndNameByUserIdAndDeletedFalse(Long id);


+ 13
- 0
src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt ファイルの表示

@@ -304,4 +304,17 @@ open class StaffsService(


userRepository.saveAll(users) userRepository.saveAll(users)
} }

open fun staffInvolvedProjects(args: Map<String, Any>): List<Map<String, Any>> {
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)
}
} }

+ 12
- 0
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 com.ffii.tsms.modules.data.web.models.NewStaffRequest
import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid import jakarta.validation.Valid
import liquibase.hub.model.Project
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
import org.springframework.web.bind.ServletRequestBindingException import org.springframework.web.bind.ServletRequestBindingException
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
@@ -31,6 +32,17 @@ class StaffsController(private val staffsService: StaffsService) {
fun StaffWithoutTeam(): List<StaffSearchInfo> { fun StaffWithoutTeam(): List<StaffSearchInfo> {
return staffsService.StaffWithoutTeam() return staffsService.StaffWithoutTeam()
} }

@GetMapping("/staff-projects/{id}")
fun staffProjects(@PathVariable id: Long): List<Map<String, Any>> {
val args = mutableMapOf<String, Long>("id" to id)
return staffsService.staffInvolvedProjects(args)
}

// @GetMapping("/staff-projects")
// fun staffProject(): List<Project> {
// return projectService.StaffWithoutTeam()
// }
// @GetMapping("/list") // @GetMapping("/list")
// fun list(): List<Staff> { // fun list(): List<Staff> {
// return staffsService.getTeamLeads() // return staffsService.getTeamLeads()


+ 43
- 43
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 " + " and p.actualEnd BETWEEN :startDate and :endDate "
+ " group by pt.project_id " + " group by pt.project_id "
+ " ) as result " + " ) 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 " + " order by result.actualEnd "
) )


return jdbcDao.queryForList(sql.toString(), args) return jdbcDao.queryForList(sql.toString(), args)
} }
open fun getProjectResourceOverconsumptionReport_projectInfo(args: Map<String, Any>): List<Map<String, Any>> {
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<Map<String, Any>> {
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<String, Any>): List<Map<String, Any>> {
// 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<Map<String, Any>> {
// 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<String, Any>): List<Map<String, Any>> { open fun getProjectResourceOverconsumptionReport(args: Map<String, Any>): List<Map<String, Any>> {
val sql = StringBuilder( val sql = StringBuilder(


+ 3
- 0
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.timesheet.entity.projections.TimesheetHours
import com.ffii.tsms.modules.project.entity.ProjectTask import com.ffii.tsms.modules.project.entity.ProjectTask
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
import java.io.Serializable
import java.time.LocalDate import java.time.LocalDate


interface TimesheetRepository : AbstractRepository<Timesheet, Long> { interface TimesheetRepository : AbstractRepository<Timesheet, Long> {
@@ -26,6 +27,8 @@ interface TimesheetRepository : AbstractRepository<Timesheet, Long> {


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


fun findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(start: LocalDate, end: LocalDate): List<Timesheet>

fun findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Timesheet> fun findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff: Staff, start: LocalDate, end: LocalDate): List<Timesheet>


@Query("SELECT MIN(t.recordDate) AS recordDate FROM Timesheet t WHERE t.project.id = ?1") @Query("SELECT MIN(t.recordDate) AS recordDate FROM Timesheet t WHERE t.project.id = ?1")


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