Ver a proveniência

update scheduled email & reports update

tags/Baseline_30082024_BACKEND_UAT
MSI\derek há 1 ano
ascendente
cometimento
05b1e2e5c7
7 ficheiros alterados com 450 adições e 110 eliminações
  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 Ver ficheiro

@@ -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<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 HOLIDAY_URL = "https://www.1823.gov.hk/common/ical/tc.json"

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()
.url(HOLIDAY_URL)
.build()

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



+ 354
- 62
src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt Ver ficheiro

@@ -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<LocalDate>,
)
@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<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 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<MailRequest>()
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<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 Ver ficheiro

@@ -17,11 +17,15 @@ public interface StaffRepository extends AbstractRepository<Staff, Long> {
List<StaffSearchInfo> findAllStaffSearchInfoByIdIn(List<Serializable> ids);
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<Staff> findByUserId(@Param("userId") Long userId);
Optional<List<Staff>> findAllByTeamIdAndDeletedFalse(Long id);

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

Optional<List<Staff>> findAllByDeletedFalse();

Optional<Staff> findIdAndNameByUserIdAndDeletedFalse(Long id);


+ 13
- 0
src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt Ver ficheiro

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

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 Ver ficheiro

@@ -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<StaffSearchInfo> {
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")
// fun list(): List<Staff> {
// return staffsService.getTeamLeads()


+ 43
- 43
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Ver ficheiro

@@ -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<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>> {
val sql = StringBuilder(


+ 3
- 0
src/main/java/com/ffii/tsms/modules/timesheet/entity/TimesheetRepository.kt Ver ficheiro

@@ -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<Timesheet, Long> {
@@ -26,6 +27,8 @@ interface TimesheetRepository : AbstractRepository<Timesheet, Long> {

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>

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


Carregando…
Cancelar
Guardar