Pārlūkot izejas kodu

Update Mail Service

Update customer max code
Update Last record Report
master
MSI\2Fi pirms 10 mēnešiem
vecāks
revīzija
1ed2cc68b2
10 mainītis faili ar 278 papildinājumiem un 15 dzēšanām
  1. +118
    -4
      src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt
  2. +2
    -2
      src/main/java/com/ffii/tsms/modules/common/mail/web/MailController.kt
  3. +7
    -0
      src/main/java/com/ffii/tsms/modules/common/mail/web/models/WorkHourRecords.kt
  4. +18
    -0
      src/main/java/com/ffii/tsms/modules/data/service/CustomerService.kt
  5. +6
    -0
      src/main/java/com/ffii/tsms/modules/data/web/CustomerController.kt
  6. +2
    -0
      src/main/java/com/ffii/tsms/modules/project/service/InvoiceService.kt
  7. +120
    -6
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  8. +3
    -2
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  9. +2
    -1
      src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt
  10. Binārs
      src/main/resources/templates/report/AR10_Staff Last Record Report.xlsx

+ 118
- 4
src/main/java/com/ffii/tsms/modules/common/mail/service/MailReminderService.kt Parādīt failu

@@ -4,6 +4,7 @@ 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.common.mail.web.models.StaffRecords
import com.ffii.tsms.modules.common.mail.web.models.StaffWithMissingDates
import com.ffii.tsms.modules.common.mail.web.models.WorkHourRecords
import com.ffii.tsms.modules.common.mail.web.models.WorkHourRecordsWithJoinDate
import com.ffii.tsms.modules.data.entity.Staff
@@ -18,6 +19,7 @@ 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.cglib.core.Local
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import java.time.DayOfWeek
@@ -58,7 +60,7 @@ open class MailReminderService(
return false
}
}
private fun createHTMLTable(naughtyList: MutableList<TableRow>): String {
private fun createHTMLTable(naughtyList: MutableList<TableRow>, dateForRed: LocalDate? = LocalDate.of(2024,4,1)): String {
val tableStarter = StringBuilder(" <table width='100%' border='1' align='center'> ")
val header = StringBuilder(
" <tr> "
@@ -69,11 +71,21 @@ open class MailReminderService(
)
tableStarter.append(header)
for (per in naughtyList) {
val missDatesHtml = per.missingDates.joinToString(", "){
localDate ->
val color = if(localDate.isBefore(LocalDate.now().minusMonths(1))){
"red"
}else{
"black"
}
"<span style='color: $color;'>$localDate</span>"
}

tableStarter.append(
" <tr> "
+ " <td>${per.staffId}</td>"
+ " <td>${per.name}</td>"
+ " <td>${per.missingDates.joinToString(", ")}</td>"
+ " <td>${missDatesHtml}</td>"
+ " </tr> "
)
}
@@ -103,7 +115,7 @@ open class MailReminderService(
mailService.send(mailRequestList)
}

@Scheduled(cron = "0 0 6 7 * ?") // (SS/MM/HH/DD/MM/YY)
// @Scheduled(cron = "0 0 6 7 * ?") // (SS/MM/HH/DD/MM/YY)
open fun sendTimesheetToTeamLead7TH() {
logger.info("-----------------------")
logger.info("Scheduled Start: 7th email reminder")
@@ -153,6 +165,7 @@ open class MailReminderService(
key.third // joinDate
)
}
// println("**** timesheetByIdAndRecord: ${timesheetByIdAndRecord.filter { it.staffId == 1L }}")
val goodStaffsList = filteredLastMonthDays.map { date ->
val matchedStaffIds = timesheetByIdAndRecord
.filter {
@@ -196,6 +209,107 @@ open class MailReminderService(
}
}

// Function to generate a sequence of workdays, excluding weekends and public holidays
private fun generateWorkdays(startDate: LocalDate, endDate: LocalDate, publicHolidays: Set<LocalDate>): Sequence<LocalDate> {
return generateSequence(startDate) { date ->
date.plusDays(1).takeIf { it <= endDate }
}.filter { date ->
date.dayOfWeek != DayOfWeek.SATURDAY &&
date.dayOfWeek != DayOfWeek.SUNDAY &&
!publicHolidays.contains(date)
}
}

// Function to find missing workdays for staff
private fun findStaffWithMissingDates(staffRecords: List<StaffRecords>, workdays: Sequence<LocalDate>, staffList: List<Staff>): List<StaffWithMissingDates> {
val staffMap = staffList.associateBy { it.id }
return staffRecords
.groupBy { it.staffId }
.map { (staffId, records) ->
val joinDate = records.firstOrNull()?.joinDate ?: LocalDate.of(1970, 1, 1)
val recordedDates = records.map { it.recordDate }.toSet()
val staffWorkdays = workdays.filter { it >= joinDate }.toList()

val missingDates = staffWorkdays.filterNot { recordedDates.contains(it) }

val staffInfo = staffMap[staffId]

StaffWithMissingDates(
staffId = staffId,
staffIdinCode = staffInfo?.staffId ?: "",
staffName = staffInfo?.name ?: "",
missingDates = missingDates
)
}
.filter { it.missingDates.isNotEmpty() }
}

@Scheduled(cron = "0 0 6 7 * ?") // (SS/MM/HH/DD/MM/YY)
open fun sendMissingTimesheetDateToTeamLead7th(){
logger.info("-----------------------")
logger.info("Scheduled Start: 7th email reminder V2")

val firstDate = LocalDate.of(2024,10,1)
val today = LocalDate.now()

val publicHolidays = holidayService.commonHolidayList().map { it.date }
val companyHolidays = companyHolidayService.allCompanyHolidays().map { it.date }
val allHolidays: Set<LocalDate> = (publicHolidays + companyHolidays).toSet()

val workdays = generateWorkdays(firstDate, today, allHolidays)
println("workdays: ${workdays.joinToString { ", " }}")
//Get all missing timesheet date and group by staff
val args = mutableMapOf(
"from" to firstDate,
"to" to today,
)

val ts = timesheetsService.workHourRecordsWithinRange(args)
val timesheet: List<StaffRecords> = ts.map {
WorkHourRecordsWithJoinDate(
staffId = it["staffId"].toString().toLong(),
recordDate = LocalDate.parse(it["recordDate"].toString()),
hours = it["hours"].toString().toDouble(),
joinDate = it["joinDate"]?.toString()?.let { date -> LocalDate.parse(date) } ?: LocalDate.of(1970, 1, 1)
)
}.groupBy { Triple(it.staffId, it.recordDate, it.joinDate) }
.mapNotNull { (key, records) ->
StaffRecords(
key.second, // recordDate
key.first, // staffId
records.sumOf { it.hours },
key.third // joinDate
)
}

val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull(FULLTIME).filter { it.staffId != "A003" && it.staffId != "A004" && it.staffId != "B011" }.filter{ it.team?.code != "HO"}
val teams = teamRepository.findAll().filter { team -> team.deleted == false }

for (team in teams) {
val teamLead = team.staff
val teamMembers: List<Staff> = staffs.filter { it.team != null && 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.staffId) }
val staffWithMissingDates = findStaffWithMissingDates(filteredTimesheet, workdays, teamMembers)
// println("staffWithMissingDates----: $staffWithMissingDates")
val intro = StringBuilder("${teamLead.name}, Staffs Missing Timesheet in the Table Below: \n")
val tableData = mutableListOf<TableRow>()
staffWithMissingDates.forEach{
val rowData = TableRow(it.staffIdinCode, it.staffName, (it.missingDates).toMutableList())
tableData.add(rowData)
}
val table = createHTMLTable(tableData)
val emailContent = intro
.append(table)

val receiver = listOf(teamLead.email)

createEmailRequest(emailContent.toString(), receiver)
}

}

// Testing function to print the good staff list
open fun test7thStaffList(){
val today = LocalDate.now()
@@ -259,7 +373,7 @@ open class MailReminderService(
Pair(date, matchedStaffIds)
}.sortedBy { it.first }

println(goodStaffsList)
// println(goodStaffsList)
}
}



+ 2
- 2
src/main/java/com/ffii/tsms/modules/common/mail/web/MailController.kt Parādīt failu

@@ -37,7 +37,7 @@ class MailController(
}
@GetMapping("/test7th")
fun test7th() {
mailReminderService.sendTimesheetToTeamLead7TH()
mailReminderService.sendMissingTimesheetDateToTeamLead7th()
}
@GetMapping("/test15th")
fun test15th() {
@@ -46,7 +46,7 @@ class MailController(

@GetMapping("/test7th-staff-list")
fun test7thStaffList(){
mailReminderService.test7thStaffList()
mailReminderService.sendMissingTimesheetDateToTeamLead7th()
}

@GetMapping("/test15th-staff-list")


+ 7
- 0
src/main/java/com/ffii/tsms/modules/common/mail/web/models/WorkHourRecords.kt Parādīt failu

@@ -21,3 +21,10 @@ data class StaffRecords(
val sumOfHours: Double,
val joinDate: LocalDate? = LocalDate.of(1970,1,1)
)

data class StaffWithMissingDates(
val staffId: Long,
val staffIdinCode: String,
val staffName: String,
val missingDates: List<LocalDate>
)

+ 18
- 0
src/main/java/com/ffii/tsms/modules/data/service/CustomerService.kt Parādīt failu

@@ -103,4 +103,22 @@ open class CustomerService(

return SaveCustomerResponse(customer = customer, message = "Success");
}

//Auto generate the latest client code
open fun findMaxCustomerCode(): String{
val sql = StringBuilder("select MAX(code) as maxCode from customer where code regexp 'CT-'")
val maxCode = jdbcDao.queryForString(sql.toString())
val regex = "(\\D+)-(\\d+)".toRegex()
val match = regex.matchEntire(maxCode)

val next = if (match != null) {
val (prefix, numberStr) = match.destructured
val nextNumber = numberStr.toInt() + 1
val padded = nextNumber.toString().padStart(numberStr.length, '0')
"$prefix-$padded"
} else {
maxCode
}
return next
}
}

+ 6
- 0
src/main/java/com/ffii/tsms/modules/data/web/CustomerController.kt Parādīt failu

@@ -8,6 +8,7 @@ import com.ffii.tsms.modules.data.service.CustomerSubsidiaryService
import com.ffii.tsms.modules.data.web.models.CustomerResponse
import com.ffii.tsms.modules.data.web.models.SaveCustomerResponse
import com.ffii.tsms.modules.project.web.models.SaveCustomerRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@@ -56,4 +57,9 @@ class CustomerController(
fun saveCustomer(@Valid @RequestBody saveCustomer: SaveCustomerRequest): SaveCustomerResponse {
return customerService.saveCustomer(saveCustomer)
}

@GetMapping("/getMaxCode")
fun getMaxCustomerCode(): String{
return customerService.findMaxCustomerCode();
}
}

+ 2
- 0
src/main/java/com/ffii/tsms/modules/project/service/InvoiceService.kt Parādīt failu

@@ -609,6 +609,8 @@ open class InvoiceService(
if (workbook == null) {
return InvoiceResponse(false, "No Excel import", ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList()) // if workbook is null
}

// find the imported Invoice Records
val invoiceRecords = repository.findImportInvoiceInfoByAndDeletedFalse()

val importInvoices: MutableList<Map<String, Any>> = mutableListOf();


+ 120
- 6
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Parādīt failu

@@ -1,7 +1,12 @@
package com.ffii.tsms.modules.report.service

import com.ffii.core.support.JdbcDao
import com.ffii.tsms.modules.common.holiday.service.HolidayService
import com.ffii.tsms.modules.common.mail.web.models.StaffRecords
import com.ffii.tsms.modules.common.mail.web.models.StaffWithMissingDates
import com.ffii.tsms.modules.common.mail.web.models.WorkHourRecordsWithJoinDate
import com.ffii.tsms.modules.data.entity.*
import com.ffii.tsms.modules.data.service.CompanyHolidayService
import com.ffii.tsms.modules.data.service.SalaryEffectiveService
import com.ffii.tsms.modules.data.service.SalaryEffectiveService.MonthlyStaffSalaryData
import com.ffii.tsms.modules.data.service.StaffsService
@@ -10,6 +15,7 @@ import com.ffii.tsms.modules.report.web.model.ExportCurrentStaffInfoRequest
import com.ffii.tsms.modules.report.web.model.costAndExpenseRequest
import com.ffii.tsms.modules.timesheet.entity.Timesheet
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository
import com.ffii.tsms.modules.timesheet.service.TimesheetsService
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.apache.poi.ss.usermodel.*
@@ -58,6 +64,10 @@ open class ReportService(
private val teamLogRepository: TeamLogRepository,
private val teamRepository: TeamRepository,
private val staffService: StaffsService,
private val holidayService: HolidayService,
private val companyHolidayService: CompanyHolidayService,
private val timesheetsService: TimesheetsService,
private val staffRepository: StaffRepository
) {
private val logger: Log = LogFactory.getLog(javaClass)
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd")
@@ -3478,6 +3488,7 @@ open class ReportService(
if (teamId != null) {
sql.append(
" and p.teamLead = :teamLeadId "
+ " and p.deleted = false "
)
}

@@ -5729,6 +5740,16 @@ open class ReportService(
val team: String,
val lastRecordDate: String,
)

data class StaffLastRecordDataV2(
val staffId: Long,
val staffIdinCode: String,
val staffName: String,
val email: String,
val team: String,
val missingDates: List<LocalDate>,
val lastRecordDate: LocalDate
)
private fun getStaffLastRecordDateByDate(date: LocalDate): List<StaffLastRecordData>{
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val sql = StringBuilder(
@@ -5760,7 +5781,7 @@ open class ReportService(
val currentDate = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant())
departDate.after(currentDate)
} else {
false // Exclude entries with null departDate
true // Include entries with null departDate, null = not departed
}
}
.map {
@@ -5777,16 +5798,100 @@ open class ReportService(
return results
}

// Function to generate a sequence of workdays, excluding weekends and public holidays
private fun generateWorkdays(startDate: LocalDate, endDate: LocalDate, publicHolidays: Set<LocalDate>): Sequence<LocalDate> {
return generateSequence(startDate) { date ->
date.plusDays(1).takeIf { it <= endDate }
}.filter { date ->
date.dayOfWeek != DayOfWeek.SATURDAY &&
date.dayOfWeek != DayOfWeek.SUNDAY &&
!publicHolidays.contains(date)
}
}

// Function to find missing workdays for staff
private fun findStaffWithMissingDates(staffRecords: List<StaffRecords>, workdays: Sequence<LocalDate>, staffList: List<Staff>): List<StaffLastRecordDataV2> {
val staffMap = staffList.associateBy { it.id }
return staffRecords
.groupBy { it.staffId }
.map { (staffId, records) ->
val joinDate = records.firstOrNull()?.joinDate ?: LocalDate.of(1970, 1, 1)
val recordedDates = records.map { it.recordDate }.toSet()
val staffWorkdays = workdays.filter { it >= joinDate }.toList()

val missingDates = staffWorkdays.filterNot { recordedDates.contains(it) }
val workDates = staffWorkdays.filter { recordedDates.contains(it) }

val staffInfo = staffMap[staffId]

StaffLastRecordDataV2(
staffId = staffId,
staffIdinCode = staffInfo?.staffId ?: "",
staffName = staffInfo?.name ?: "",
email = staffInfo?.email ?: "",
team = staffInfo?.team?.code ?: "",
missingDates = missingDates,
lastRecordDate = workDates.last()
)
}
.filter { it.missingDates.isNotEmpty() }
}
private fun getStaffLastRecordDateByDateWithDayRange(date: LocalDate, dayRange: Long): List<StaffLastRecordDataV2>{
logger.info("-----------------------")
logger.info("Report Start: Staff Last Record Date By Date With DayRange")

val today = date
val firstDate = today.minusDays(dayRange)

val publicHolidays = holidayService.commonHolidayList().map { it.date }
val companyHolidays = companyHolidayService.allCompanyHolidays().map { it.date }
val allHolidays: Set<LocalDate> = (publicHolidays + companyHolidays).toSet()

val workdays = generateWorkdays(firstDate, today, allHolidays)
//Get all missing timesheet date and group by staff
val args = mutableMapOf(
"from" to firstDate,
"to" to today,
)
logger.info(args)
val ts = timesheetsService.workHourRecordsWithinRange(args)
val timesheet: List<StaffRecords> = ts.map {
WorkHourRecordsWithJoinDate(
staffId = it["staffId"].toString().toLong(),
recordDate = LocalDate.parse(it["recordDate"].toString()),
hours = it["hours"].toString().toDouble(),
joinDate = it["joinDate"]?.toString()?.let { date -> LocalDate.parse(date) } ?: LocalDate.of(1970, 1, 1)
)
}.groupBy { Triple(it.staffId, it.recordDate, it.joinDate) }
.mapNotNull { (key, records) ->
StaffRecords(
key.second, // recordDate
key.first, // staffId
records.sumOf { it.hours },
key.third // joinDate
)
}

val staffs = staffRepository.findAllByEmployTypeAndDeletedFalseAndDepartDateIsNull("FT").filter { it.staffId != "A003" && it.staffId != "A004" && it.staffId != "B011" }.filter{ it.team?.code != "HO"}
// val teams = teamRepository.findAll().filter { team -> team.deleted == false }
val filteredTimesheet = timesheet.filter { staffs.map { it.id!! }.sorted().contains(it.staffId) }
val staffWithMissingDates = findStaffWithMissingDates(filteredTimesheet, workdays, staffs)
// logger.info(staffWithMissingDates.sortedBy { it.team })
return staffWithMissingDates.sortedWith(compareBy({ it.team }, { it.staffId }))
}

@Throws(IOException::class)
private fun createLastRecordReportWorkbook(
date: LocalDate,
dayRange: Long,
templatePath: String,
): Workbook{
val resource = ClassPathResource(templatePath)
val templateInputStream = resource.inputStream
val workbook: Workbook = XSSFWorkbook(templateInputStream)

val result = getStaffLastRecordDateByDate(date)
// val result = getStaffLastRecordDateByDate(date)
val result = getStaffLastRecordDateByDateWithDayRange(date, dayRange)
println(result)

var sheet: Sheet = workbook.getSheetAt(0)
@@ -5812,7 +5917,9 @@ open class ReportService(

rowIndex = 2
sheet.getRow(rowIndex).getCell(columnIndex+1).apply {
setCellValue(date.format(DATE_FORMATTER))
val startDate = date.minusDays(dayRange).format(DATE_FORMATTER)
val endDate = date.format(DATE_FORMATTER)
setCellValue("$startDate - $endDate ($dayRange)")
}

rowIndex = 3
@@ -5830,9 +5937,10 @@ open class ReportService(
val emailCell = row.createCell(2)
val teamCell = row.createCell(3)
val lastRecordDateCell = row.createCell(4)
val numOfMissingDateCell = row.createCell(5)

staffIdCell.apply {
setCellValue(it.staffId)
setCellValue(it.staffIdinCode)
cellStyle =blackFontStyle
}

@@ -5852,7 +5960,12 @@ open class ReportService(
}

lastRecordDateCell.apply {
setCellValue(it.lastRecordDate)
setCellValue(it.lastRecordDate.toString())
cellStyle =blackFontStyle
}

numOfMissingDateCell.apply {
setCellValue(it.missingDates.size.toDouble()) // count total number of missing date in list
cellStyle =blackFontStyle
}

@@ -5864,8 +5977,9 @@ open class ReportService(

fun genLastRecordReport(
date: LocalDate,
dayRange: Long
): ByteArray{
val workbook = createLastRecordReportWorkbook(date, LAST_RECORD_REPORT)
val workbook = createLastRecordReportWorkbook(date, dayRange, LAST_RECORD_REPORT)

val outputStream: ByteArrayOutputStream = ByteArrayOutputStream()
workbook.write(outputStream)


+ 3
- 2
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt Parādīt failu

@@ -387,16 +387,17 @@ class ReportController(
val reportResult: ByteArray = excelReportService.genProjectMonthlyReport(request.yearMonth, request.projectId, request.holidays)

return ResponseEntity.ok()
.header("filename", "Project Monthly Report - " + LocalDate.now() + ".xlsx")
.header("filename", "Project Monthly daily Summary Report - " + LocalDate.now() + ".xlsx")
.body(ByteArrayResource(reportResult))
}

@PostMapping("/gen-staff-last-record-report")
fun genLastRecordReport(@RequestBody @Valid request: LastRecordReportRequest): ResponseEntity<Resource>{
println("================= ${request.dateString}")
println("================= ${request.dayRange}")
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val date = LocalDate.parse(request.dateString, formatter)
val reportResult: ByteArray = excelReportService.genLastRecordReport(date)
val reportResult: ByteArray = excelReportService.genLastRecordReport(date, request.dayRange)

return ResponseEntity.ok()
.header("filename", "Staff Last Record Report - " + LocalDate.now() + ".xlsx")


+ 2
- 1
src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt Parādīt failu

@@ -79,7 +79,8 @@ data class ProjectMonthlyRequest (
)

data class LastRecordReportRequest (
val dateString: String
val dateString: String,
val dayRange: Long
)

data class ExportCurrentStaffInfoRequest (


Binārs
src/main/resources/templates/report/AR10_Staff Last Record Report.xlsx Parādīt failu


Notiek ielāde…
Atcelt
Saglabāt