@@ -1,6 +1,11 @@ | |||
package com.ffii.tsms.modules.data.entity; | |||
import com.ffii.core.support.AbstractRepository; | |||
import org.springframework.data.repository.query.Param; | |||
import java.util.List; | |||
public interface StaffSkillsetRepository extends AbstractRepository<StaffSkillset, Long> { | |||
List<StaffSkillset> findByStaff(@Param("staff") Staff staff); | |||
} |
@@ -7,4 +7,6 @@ import java.util.List; | |||
public interface TeamRepository extends AbstractRepository<Team, Long> { | |||
List<Team> findByDeletedFalse(); | |||
Team findByStaff(Staff staff); | |||
} |
@@ -10,6 +10,7 @@ import com.ffii.tsms.modules.data.web.models.SaveCustomerResponse | |||
import com.ffii.tsms.modules.project.web.models.SaveCustomerRequest | |||
import org.springframework.beans.BeanUtils | |||
import org.springframework.stereotype.Service | |||
import java.math.BigDecimal | |||
import java.util.Optional | |||
@Service | |||
@@ -23,9 +24,7 @@ open class DashboardService( | |||
fun CustomerSubsidiary(args: Map<String, Any>): List<Map<String, Any>> { | |||
val sql = StringBuilder("select" | |||
+ " row_number()OVER (" | |||
+ " ORDER BY c.id" | |||
+ " ) as id," | |||
+ " ROW_NUMBER() OVER (ORDER BY c.id, c.name, c.code, c.address, c.district, c.brNo, c.typeId, s.id, s.name, s.code, s.address, s.district, s.brNo, s.typeId) AS RowNum," | |||
+ " c.id as customerId," | |||
+ " c.name as customerName," | |||
+ " c.code as customerCode," | |||
@@ -45,6 +44,7 @@ open class DashboardService( | |||
+ " left join project p on c.id = p.customerId" | |||
+ " left join subsidiary s on p.customerSubsidiaryId = s.id" | |||
+ " where c.deleted = 0" | |||
+ " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")" | |||
) | |||
if (args != null) { | |||
if (args.containsKey("customerName")) | |||
@@ -58,6 +58,7 @@ open class DashboardService( | |||
fun searchCustomerSubsidiaryProject(args: Map<String, Any>): List<Map<String, Any>> { | |||
val sql = StringBuilder("select" | |||
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id," | |||
+ " p.id as id," | |||
+ " p.id as projectId," | |||
+ " p.code as projectCode," | |||
@@ -91,6 +92,7 @@ open class DashboardService( | |||
+ " ) milestonePayment on 1=1" | |||
+ " where p.customerId = :customerId" | |||
+ " and p.customerSubsidiaryId = :subsidiaryId" | |||
+ " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")" | |||
+ " group by p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone" | |||
) | |||
@@ -99,6 +101,7 @@ open class DashboardService( | |||
fun searchCustomerNonSubsidiaryProject(args: Map<String, Any>): List<Map<String, Any>> { | |||
val sql = StringBuilder("select" | |||
+ " ROW_NUMBER() OVER (ORDER BY p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone) AS id," | |||
+ " p.id as id," | |||
+ " p.id as projectId," | |||
+ " p.code as projectCode," | |||
@@ -132,11 +135,94 @@ open class DashboardService( | |||
+ " ) milestonePayment on 1=1" | |||
+ " where p.customerId = :customerId" | |||
+ " and isNull(p.customerSubsidiaryId)" | |||
+ " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")" | |||
+ " group by p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone" | |||
) | |||
return jdbcDao.queryForList(sql.toString(), args) | |||
} | |||
open fun getFinancialStatus(): List<Map<String, Any>> { | |||
val sql = StringBuilder( | |||
" with cte_timesheet as (" | |||
+ " Select p.code, s.name as staff, IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.hourlyRate" | |||
+ " from timesheet t" | |||
+ " left join project_task pt on pt.id = t.projectTaskId" | |||
+ " left join project p ON p.id = pt.project_id" | |||
+ " left join staff s on s.id = t.staffId" | |||
+ " left join salary s2 on s.salaryId = s2.salaryPoint" | |||
+ " left join team t2 on t2.id = s.teamId" | |||
+ " )," | |||
+ " cte_invoice as (" | |||
+ " select p.code, sum(i.issueAmount) as sumIssuedAmount , sum(i.paidAmount) as sumPaidAmount" | |||
+ " from invoice i" | |||
+ " left join project p on p.code = i.projectCode" | |||
+ " group by p.code" | |||
+ " )" | |||
+ " select p.code, p.description, c.name as client, concat(t.code, \' - \', t.name) as teamLead, p.planStart , p.planEnd , p.expectedTotalFee," | |||
+ " IFNULL(cte_ts.normalConsumed, 0) as normalConsumed, IFNULL(cte_ts.otConsumed, 0) as otConsumed," | |||
+ " IFNULL(cte_ts.hourlyRate, 0) as hourlyRate, IFNULL(cte_i.sumIssuedAmount, 0) as sumIssuedAmount, IFNULL(cte_i.sumPaidAmount, 0) as sumPaidAmount" | |||
+ " from project p" | |||
+ " left join cte_timesheet cte_ts on p.code = cte_ts.code" | |||
+ " left join customer c on c.id = p.customerId" | |||
+ " left join tsmsdb.team t on t.teamLead = p.teamLead" | |||
+ " left join cte_invoice cte_i on cte_i.code = p.code" | |||
+ " where p.status = \'On-going\'" | |||
) | |||
sql.append(" order by p.code") | |||
return jdbcDao.queryForList(sql.toString()) | |||
} | |||
fun searchFinancialSummary(): List<Map<String, Any>> { | |||
val financialStatus: List<Map<String, Any>> = getFinancialStatus() | |||
val otFactor = BigDecimal(1) | |||
val tempList = mutableListOf<Map<String, Any>>() | |||
for (item in financialStatus) { | |||
val normalConsumed = item.getValue("normalConsumed") as Double | |||
val hourlyRate = item.getValue("hourlyRate") as BigDecimal | |||
// println("normalConsumed------------- $normalConsumed") | |||
// println("hourlyRate------------- $hourlyRate") | |||
val manHourRate = normalConsumed.toBigDecimal().multiply(hourlyRate) | |||
// println("manHourRate------------ $manHourRate") | |||
val otConsumed = item.getValue("otConsumed") as Double | |||
val manOtHourRate = otConsumed.toBigDecimal().multiply(hourlyRate).multiply(otFactor) | |||
if (!tempList.any { it.containsValue(item.getValue("code")) }) { | |||
tempList.add( | |||
mapOf( | |||
"code" to item.getValue("code"), | |||
"description" to item.getValue("description"), | |||
"client" to item.getValue("client"), | |||
"teamLead" to item.getValue("teamLead"), | |||
"planStart" to item.getValue("planStart"), | |||
"planEnd" to item.getValue("planEnd"), | |||
"expectedTotalFee" to item.getValue("expectedTotalFee"), | |||
"normalConsumed" to manHourRate, | |||
"otConsumed" to manOtHourRate, | |||
"issuedAmount" to item.getValue("sumIssuedAmount"), | |||
"paidAmount" to item.getValue("sumPaidAmount"), | |||
) | |||
) | |||
} else { | |||
// Find the existing Map in the tempList that has the same "code" value | |||
val existingMap = tempList.find { it.containsValue(item.getValue("code")) }!! | |||
// Update the existing Map with the new manHourRate and manOtHourRate values | |||
tempList[tempList.indexOf(existingMap)] = existingMap.toMutableMap().apply { | |||
put("normalConsumed", (get("normalConsumed") as BigDecimal).add(manHourRate)) | |||
put("otConsumed", (get("otConsumed") as BigDecimal).add(manOtHourRate)) | |||
} | |||
} | |||
} | |||
return tempList | |||
} | |||
} | |||
@@ -95,7 +95,6 @@ open class StaffsService( | |||
@Transactional(rollbackFor = [Exception::class]) | |||
open fun saveStaff(req: NewStaffRequest): Staff { | |||
// if (req.staffId) | |||
val checkStaffIdList: List<StaffSearchInfo> = staffRepository.findStaffSearchInfoByAndDeletedFalse() | |||
checkStaffIdList.forEach{ s -> | |||
if (s.staffId == req.staffId) { | |||
@@ -108,7 +107,6 @@ open class StaffsService( | |||
val grade = if (req.gradeId != null && req.gradeId > 0L) gradeRepository.findById(req.gradeId).orElseThrow() else null | |||
val team = if (req.teamId != null && req.teamId > 0L) teamRepository.findById(req.teamId).orElseThrow() else null | |||
val salary = salaryRepository.findBySalaryPoint(req.salaryId).orElseThrow() | |||
// val salaryEffective = salaryEffectiveRepository.findById(req.salaryEffId).orElseThrow() | |||
val department = departmentRepository.findById(req.departmentId).orElseThrow() | |||
val user = userRepository.saveAndFlush( | |||
@@ -117,7 +115,6 @@ open class StaffsService( | |||
password = passwordEncoder.encode("mms1234") | |||
name = req.name | |||
phone1 = req.phone1 | |||
// phone2 = req.phone2 ?: null | |||
email = req.email ?: null | |||
} | |||
) | |||
@@ -142,7 +139,6 @@ open class StaffsService( | |||
this.company = company | |||
this.grade = grade | |||
this.team = team | |||
// this.skill = skill | |||
this.salary = salary | |||
this.department = department | |||
} | |||
@@ -158,31 +154,31 @@ open class StaffsService( | |||
staffSkillsetRepository.save(ss) | |||
} | |||
} | |||
// val skillBatchInsertValues: MutableList<MutableMap<String, Any>> | |||
// if (!req.skillSetId.isNullOrEmpty()) { | |||
// skillBatchInsertValues = req.skillSetId.stream() | |||
// .map { skillId -> mutableMapOf<String, Any>(("staffId" to staff.id) as Pair<String, Any>, "skillId" to skillId) } | |||
// .collect(Collectors.toList()) | |||
// jdbcDao.batchUpdate( | |||
// "INSERT IGNORE INTO staff_skillset (staffId, skillId)" | |||
// + " VALUES (:staffId, :skillId)", | |||
// skillBatchInsertValues); | |||
// } | |||
salaryEffectiveService.saveSalaryEffective(staff.id!!, salary.id!!) | |||
return staff | |||
} | |||
@Transactional(rollbackFor = [Exception::class]) | |||
open fun updateStaff(req: NewStaffRequest, staff: Staff): Staff { | |||
val args = java.util.Map.of<String, Any>("staffId", staff.id) | |||
if(!req.skillSetId.isNullOrEmpty()) { | |||
// remove all skills of the staff | |||
jdbcDao.executeUpdate("DELETE FROM staff_skillset WHERE staffId = :staffId", args); | |||
// add skiils | |||
for (skillId in req.skillSetId) { | |||
val skill = skillRepository.findById(skillId).orElseThrow() | |||
val ss = StaffSkillset().apply { | |||
this.staff = staff | |||
this.skill = skill | |||
} | |||
staffSkillsetRepository.save(ss) | |||
} | |||
} | |||
val currentPosition = positionRepository.findById(req.currentPositionId).orElseThrow() | |||
val joinPosition = positionRepository.findById(req.joinPositionId).orElseThrow() | |||
val company = companyRepository.findById(req.companyId).orElseThrow() | |||
val grade = if (req.gradeId != null && req.gradeId > 0L) gradeRepository.findById(req.gradeId).orElseThrow() else null | |||
val team = if (req.teamId != null && req.teamId > 0L) teamRepository.findById(req.teamId).orElseThrow() else null | |||
// val skill = if (req.skillSetId != null && req.skillSetId > 0L) skillRepository.findById(req.skillSetId).orElseThrow() else null | |||
val salary = salaryRepository.findById(req.salaryId).orElseThrow() | |||
// val salaryEffective = salaryEffectiveRepository.findById(req.salaryEffId).orElseThrow() | |||
val department = departmentRepository.findById(req.departmentId).orElseThrow() | |||
staff.apply { | |||
@@ -203,7 +199,6 @@ open class StaffsService( | |||
this.company = company | |||
this.grade = grade | |||
this.team = team | |||
// this.skill = skill | |||
this.salary = salary | |||
this.department = department | |||
} | |||
@@ -162,4 +162,18 @@ open class TeamService( | |||
) | |||
return jdbcDao.queryForList(sql.toString(), args) | |||
} | |||
open fun combo2(): List<Map<String, Any>> { | |||
val sql = StringBuilder("select" | |||
+ " t.teamLead as id," | |||
+ " t.name, t.code " | |||
+ " from team t" | |||
+ " where t.deleted = false " | |||
) | |||
return jdbcDao.queryForList(sql.toString()) | |||
} | |||
open fun getMyTeamForStaff(staff: Staff): Team? { | |||
return teamRepository.findByStaff(staff) | |||
} | |||
} |
@@ -69,4 +69,9 @@ class DashboardController( | |||
} | |||
return result | |||
} | |||
@GetMapping("/searchFinancialSummary") | |||
fun searchFinancialSummary(): List<Map<String, Any>>{ | |||
return dashboardService.searchFinancialSummary() | |||
} | |||
} |
@@ -53,4 +53,10 @@ class TeamController(private val teamService: TeamService) { | |||
) | |||
) | |||
} | |||
@GetMapping("/combo2") | |||
@Throws(ServletRequestBindingException::class) | |||
fun combo2(): List<Map<String, Any>> { | |||
return teamService.combo2() | |||
} | |||
} |
@@ -9,6 +9,9 @@ import com.ffii.tsms.modules.project.entity.Invoice | |||
import com.ffii.tsms.modules.project.entity.Project | |||
import com.ffii.tsms.modules.timesheet.entity.Leave | |||
import com.ffii.tsms.modules.timesheet.entity.Timesheet | |||
import com.ffii.tsms.modules.timesheet.entity.projections.MonthlyLeave | |||
import com.ffii.tsms.modules.timesheet.entity.projections.ProjectMonthlyHoursWithDate | |||
import com.ffii.tsms.modules.timesheet.entity.projections.TimesheetHours | |||
import com.ffii.tsms.modules.timesheet.web.models.LeaveEntry | |||
import org.apache.commons.logging.Log | |||
import org.apache.commons.logging.LogFactory | |||
@@ -22,6 +25,7 @@ import org.springframework.stereotype.Service | |||
import java.io.ByteArrayOutputStream | |||
import java.io.IOException | |||
import java.math.BigDecimal | |||
import java.sql.Time | |||
import java.time.LocalDate | |||
import java.time.format.DateTimeFormatter | |||
import java.util.* | |||
@@ -44,9 +48,9 @@ open class ReportService( | |||
// ==============================|| GENERATE REPORT ||============================== // | |||
fun genFinancialStatusReport(projectId: Long): ByteArray { | |||
fun genFinancialStatusReport(teamLeadId: Long): ByteArray { | |||
val financialStatus: List<Map<String, Any>> = getFinancialStatus(projectId) | |||
val financialStatus: List<Map<String, Any>> = getFinancialStatus(teamLeadId) | |||
val otFactor = BigDecimal(1) | |||
@@ -55,10 +59,10 @@ open class ReportService( | |||
for (item in financialStatus){ | |||
val normalConsumed = item.getValue("normalConsumed") as Double | |||
val hourlyRate = item.getValue("hourlyRate") as BigDecimal | |||
println("normalConsumed------------- $normalConsumed") | |||
println("hourlyRate------------- $hourlyRate") | |||
// println("normalConsumed------------- $normalConsumed") | |||
// println("hourlyRate------------- $hourlyRate") | |||
val manHourRate = normalConsumed.toBigDecimal().multiply(hourlyRate) | |||
println("manHourRate------------ $manHourRate") | |||
// println("manHourRate------------ $manHourRate") | |||
val otConsumed = item.getValue("otConsumed") as Double | |||
val manOtHourRate = otConsumed.toBigDecimal().multiply(hourlyRate).multiply(otFactor) | |||
@@ -90,9 +94,9 @@ open class ReportService( | |||
} | |||
} | |||
println("tempList---------------------- $tempList") | |||
// println("tempList---------------------- $tempList") | |||
val workbook: Workbook = createFinancialStatusReport(FINANCIAL_STATUS_REPORT, tempList) | |||
val workbook: Workbook = createFinancialStatusReport(FINANCIAL_STATUS_REPORT, tempList, teamLeadId) | |||
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() | |||
workbook.write(outputStream) | |||
@@ -105,10 +109,11 @@ open class ReportService( | |||
fun generateProjectCashFlowReport( | |||
project: Project, | |||
invoices: List<Invoice>, | |||
timesheets: List<Timesheet> | |||
timesheets: List<Timesheet>, | |||
dateType: String | |||
): ByteArray { | |||
// Generate the Excel report with query results | |||
val workbook: Workbook = createProjectCashFlowReport(project, invoices, timesheets, PROJECT_CASH_FLOW_REPORT) | |||
val workbook: Workbook = createProjectCashFlowReport(project, invoices, timesheets, dateType, PROJECT_CASH_FLOW_REPORT) | |||
// Write the workbook to a ByteArrayOutputStream | |||
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() | |||
@@ -122,9 +127,8 @@ open class ReportService( | |||
fun generateStaffMonthlyWorkHourAnalysisReport( | |||
month: LocalDate, | |||
staff: Staff, | |||
timesheets: List<Timesheet>, | |||
leaves: List<Leave>, | |||
projectList: List<String> | |||
timesheets: List<Map<String, Any>>, | |||
leaves: List<Map<String, Any>>, | |||
): ByteArray { | |||
// Generate the Excel report with query results | |||
val workbook: Workbook = createStaffMonthlyWorkHourAnalysisReport( | |||
@@ -132,7 +136,6 @@ open class ReportService( | |||
staff, | |||
timesheets, | |||
leaves, | |||
projectList, | |||
MONTHLY_WORK_HOURS_ANALYSIS_REPORT | |||
) | |||
@@ -180,6 +183,7 @@ open class ReportService( | |||
private fun createFinancialStatusReport( | |||
templatePath: String, | |||
projects: List<Map<String, Any>>, | |||
teamLeadId: Long | |||
) : Workbook { | |||
val resource = ClassPathResource(templatePath) | |||
@@ -190,14 +194,50 @@ open class ReportService( | |||
val sheet = workbook.getSheetAt(0) | |||
//Set Column 2, 3, 4 to auto width | |||
sheet.setColumnWidth(2, 20*256) | |||
sheet.setColumnWidth(3, 45*256) | |||
sheet.setColumnWidth(4, 15*256) | |||
val boldFont = sheet.workbook.createFont() | |||
boldFont.bold = true | |||
val boldFontCellStyle = workbook.createCellStyle() | |||
boldFontCellStyle.setFont(boldFont) | |||
var rowNum = 14 | |||
if (projects.isEmpty()){ | |||
// Fill the cell in Row 2-12 with thr calculated sum | |||
rowNum = 1 | |||
val row1: Row = sheet.getRow(rowNum) | |||
val genDateCell = row1.createCell(2) | |||
genDateCell.setCellValue(LocalDate.now().toString()) | |||
rowNum = 2 | |||
val row2: Row = sheet.getRow(rowNum) | |||
val row2Cell = row2.createCell(2) | |||
val sql = StringBuilder("select" | |||
+ " t.teamLead as id," | |||
+ " t.name, t.code " | |||
+ " from team t" | |||
+ " where t.deleted = false " | |||
+ " and t.teamLead = :teamLead " | |||
) | |||
val args = mapOf("teamLead" to teamLeadId) | |||
val team = jdbcDao.queryForMap(sql.toString(), args).get() | |||
val code = team["code"] | |||
val name = team["name"] | |||
row2Cell.apply { | |||
setCellValue("$code - $name") | |||
} | |||
var rowNum = 14 | |||
rowNum = 4 | |||
val row4: Row = sheet.getRow(rowNum) | |||
val row4Cell = row4.createCell(2) | |||
row4Cell.setCellValue(projects.size.toString()) | |||
return workbook | |||
} | |||
for(item in projects){ | |||
val row: Row = sheet.createRow(rowNum++) | |||
@@ -209,7 +249,10 @@ open class ReportService( | |||
descriptionCell.setCellValue(if (item["description"] != null) item.getValue("description").toString() else "N/A") | |||
val clientCell = row.createCell(2) | |||
clientCell.setCellValue(if (item["client"] != null) item.getValue("client").toString() else "N/A") | |||
clientCell.apply { | |||
setCellValue(if (item["client"] != null) item.getValue("client").toString() else "N/A") | |||
} | |||
CellUtil.setAlignment(clientCell, HorizontalAlignment.CENTER) | |||
val teamLeadCell = row.createCell(3) | |||
teamLeadCell.setCellValue(if (item["teamLead"] != null) item.getValue("teamLead").toString() else "N/A") | |||
@@ -227,7 +270,6 @@ open class ReportService( | |||
cellStyle.dataFormat = accountingStyle | |||
} | |||
val budgetCell = row.createCell(7) | |||
budgetCell.apply { | |||
cellFormula = "G${rowNum} * 80%" | |||
@@ -259,14 +301,14 @@ open class ReportService( | |||
val uninvoiceCell = row.createCell(11) | |||
uninvoiceCell.apply { | |||
cellFormula = " IF(H${rowNum}<I${rowNum},H${rowNum}-K${rowNum},I${rowNum}-K${rowNum}) " | |||
cellFormula = " IF(H${rowNum}<=I${rowNum}, H${rowNum}-K${rowNum}, IF(AND(H${rowNum}>I${rowNum}, I${rowNum}<K${rowNum}), 0, IF(AND(H${rowNum}>I${rowNum}, I${rowNum}>=K${rowNum}), I${rowNum}-K${rowNum}, 0))) " | |||
cellStyle = boldFontCellStyle | |||
cellStyle.dataFormat = accountingStyle | |||
} | |||
val cpiCell = row.createCell(12) | |||
cpiCell.apply { | |||
cellFormula = "K${rowNum}/I${rowNum}" | |||
cellFormula = "IF(K${rowNum} = 0, 0, K${rowNum}/I${rowNum})" | |||
} | |||
val receivedAmountCell = row.createCell(13) | |||
@@ -276,15 +318,14 @@ open class ReportService( | |||
cellStyle.dataFormat = accountingStyle | |||
} | |||
val unsettledAmountCell = row.createCell(14) | |||
unsettledAmountCell.apply { | |||
cellFormula = "K${rowNum}-N${rowNum}" | |||
cellStyle = boldFontCellStyle | |||
cellStyle.dataFormat = accountingStyle | |||
} | |||
} | |||
// Last row calculate the sum | |||
val lastRowNum = rowNum + 1 | |||
val row: Row = sheet.createRow(rowNum) | |||
@@ -373,14 +414,27 @@ open class ReportService( | |||
CellUtil.setCellStyleProperty(sumUnSettleCell,"borderTop", BorderStyle.THIN) | |||
CellUtil.setCellStyleProperty(sumUnSettleCell,"borderBottom", BorderStyle.DOUBLE) | |||
// Fill the cell in Row 2-12 with thr calculated sum | |||
rowNum = 1 | |||
val row1: Row = sheet.getRow(rowNum) | |||
val genDateCell = row1.createCell(2) | |||
genDateCell.setCellValue(LocalDate.now().toString()) | |||
rowNum = 2 | |||
val row2: Row = sheet.getRow(rowNum) | |||
val row2Cell = row2.createCell(2) | |||
if (teamLeadId < 0) { | |||
row2Cell.setCellValue("All") | |||
}else{ | |||
row2Cell.apply { | |||
cellFormula = "D15" | |||
} | |||
} | |||
rowNum = 4 | |||
val row4: Row = sheet.getRow(rowNum) | |||
val row4Cell = row4.createCell(2) | |||
row4Cell.setCellValue(projects.size.toString()) | |||
rowNum = 5 | |||
val row5: Row = sheet.getRow(rowNum) | |||
@@ -446,6 +500,7 @@ open class ReportService( | |||
project: Project, | |||
invoices: List<Invoice>, | |||
timesheets: List<Timesheet>, | |||
dateType: String, | |||
templatePath: String, | |||
): Workbook { | |||
// please create a new function for each report template | |||
@@ -531,7 +586,7 @@ open class ReportService( | |||
rowIndex = 15 | |||
val dateFormatter = DateTimeFormatter.ofPattern("MMM YYYY") | |||
val dateFormatter = if (dateType == "Date") DateTimeFormatter.ofPattern("yyyy/MM/dd") else DateTimeFormatter.ofPattern("MMM YYYY") | |||
val combinedResults = | |||
(invoices.map { it.receiptDate } + timesheets.map { it.recordDate }).filterNotNull().sortedBy { it } | |||
.map { it.format(dateFormatter) }.distinct() | |||
@@ -675,21 +730,35 @@ open class ReportService( | |||
private fun createStaffMonthlyWorkHourAnalysisReport( | |||
month: LocalDate, | |||
staff: Staff, | |||
timesheets: List<Timesheet>, | |||
leaves: List<Leave>, | |||
projectList: List<String>, | |||
timesheets: List<Map<String, Any>>, | |||
leaves: List<Map<String, Any>>, | |||
templatePath: String, | |||
): Workbook { | |||
// val yearMonth = YearMonth.of(2022, 5) // May 2022 | |||
println("t $timesheets") | |||
println("l $leaves") | |||
println("p $projectList") | |||
var projectList: List<String> = listOf() | |||
println("----timesheets-----") | |||
println(timesheets) | |||
// result = timesheet record mapped | |||
var result: Map<String, Any> = mapOf() | |||
if (timesheets.isNotEmpty()) { | |||
projectList = timesheets.map{ "${it["code"]}\n ${it["name"]}"}.toList() | |||
result = timesheets.groupBy( | |||
{ it["id"].toString() }, | |||
{ mapOf( | |||
"date" to it["recordDate"], | |||
"normalConsumed" to it["normalConsumed"], | |||
"otConsumed" to it["otConsumed"], | |||
) } | |||
) | |||
} | |||
println("---result---") | |||
println(result) | |||
println("l $projectList") | |||
val resource = ClassPathResource(templatePath) | |||
val templateInputStream = resource.inputStream | |||
val workbook: Workbook = XSSFWorkbook(templateInputStream) | |||
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") | |||
val monthStyle = workbook.createDataFormat().getFormat("mmm, yyyy") | |||
val monthStyle = workbook.createDataFormat().getFormat("MMM YYYY") | |||
val dateStyle = workbook.createDataFormat().getFormat("dd/mm/yyyy") | |||
val boldStyle = workbook.createCellStyle() | |||
@@ -705,8 +774,6 @@ open class ReportService( | |||
val sheet: Sheet = workbook.getSheetAt(0) | |||
// sheet.forceFormulaRecalculation = true; //Calculate formulas | |||
var rowIndex = 1 // Assuming the location is in (1,2), which is the report date field | |||
var columnIndex = 1 | |||
@@ -723,7 +790,6 @@ open class ReportService( | |||
rowIndex = 2 | |||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||
setCellValue(month) | |||
// cellStyle.setFont(boldStyle) | |||
cellStyle.dataFormat = monthStyle | |||
} | |||
@@ -758,11 +824,9 @@ open class ReportService( | |||
tempCell.setCellValue(dayInfo.date) | |||
tempCell.cellStyle = boldStyle | |||
tempCell.cellStyle.dataFormat = dateStyle | |||
// cellStyle.alignment = HorizontalAlignment.LEFT | |||
tempCell = sheet.getRow(rowIndex).createCell(1) | |||
tempCell.setCellValue(dayInfo.weekday) | |||
tempCell.cellStyle = boldStyle | |||
// cellStyle.alignment = HorizontalAlignment.LEFT | |||
} | |||
rowIndex += 1 | |||
@@ -784,10 +848,11 @@ open class ReportService( | |||
var normalConsumed = 0.0 | |||
var otConsumed = 0.0 | |||
var leaveHours = 0.0 | |||
// normalConsumed data | |||
if (timesheets.isNotEmpty()) { | |||
timesheets.forEach { t -> | |||
normalConsumed += t.normalConsumed!! | |||
otConsumed += t.otConsumed ?: 0.0 | |||
normalConsumed += t["normalConsumed"] as Double | |||
otConsumed += t["otConsumed"] as Double | |||
} | |||
} | |||
tempCell = sheet.getRow(rowIndex).createCell(2) | |||
@@ -815,9 +880,10 @@ open class ReportService( | |||
tempCell.cellStyle = boldStyle | |||
CellUtil.setAlignment(tempCell, HorizontalAlignment.CENTER) | |||
sheet.addMergedRegion(CellRangeAddress(rowIndex,rowIndex , 0, 1)) | |||
// cal total leave hour | |||
if (leaves.isNotEmpty()) { | |||
leaves.forEach { l -> | |||
leaveHours += l.leaveHours!! | |||
leaveHours += l["leaveHours"] as Double | |||
} | |||
} | |||
tempCell = sheet.getRow(rowIndex).createCell(2) | |||
@@ -862,28 +928,30 @@ open class ReportService( | |||
tempCell.setCellValue(0.0) | |||
tempCell.cellStyle.dataFormat = accountingStyle | |||
} | |||
timesheets.forEach { timesheet -> | |||
dayInt = timesheet.recordDate!!.dayOfMonth | |||
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex) | |||
tempCell.setCellValue(timesheet.normalConsumed!!) | |||
result.forEach{(id, list) -> | |||
for (i in 0 until id.toInt()) { | |||
val temp: List<Map<String, Any>> = list as List<Map<String, Any>> | |||
temp.forEachIndexed { i, _ -> | |||
dayInt = temp[i]["date"].toString().toInt() | |||
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex) | |||
tempCell.setCellValue(temp[i]["normalConsumed"] as Double) | |||
} | |||
} | |||
} | |||
columnIndex++ | |||
} | |||
} | |||
// dates | |||
// leave hours data | |||
if (leaves.isNotEmpty()) { | |||
leaves.forEach { leave -> | |||
for (i in 0 until rowSize) { | |||
tempCell = sheet.getRow(8 + i).createCell(columnIndex) | |||
tempCell.setCellValue(0.0) | |||
tempCell.cellStyle.dataFormat = accountingStyle | |||
} | |||
dayInt = leave.recordDate!!.dayOfMonth | |||
dayInt = leave["recordDate"].toString().toInt() | |||
tempCell = sheet.getRow(dayInt.plus(7)).createCell(columnIndex) | |||
tempCell.setCellValue(leave.leaveHours!!) | |||
tempCell.setCellValue(leave["leaveHours"] as Double) | |||
} | |||
} | |||
///////////////////////////////////////////////////////// Leave Hours //////////////////////////////////////////////////////////////////// | |||
@@ -1040,33 +1108,73 @@ open class ReportService( | |||
return workbook | |||
} | |||
open fun getFinancialStatus(projectId: Long?): List<Map<String, Any>> { | |||
open fun getFinancialStatus(teamLeadId: Long?): List<Map<String, Any>> { | |||
val sql = StringBuilder( | |||
" with cte_invoice as (select p.code, sum(i.issueAmount) as sumIssuedAmount , sum(i.paidAmount) as sumPaidAmount" | |||
" with cte_timesheet as (" | |||
+ " Select p.code, s.name as staff, IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.hourlyRate" | |||
+ " from timesheet t" | |||
+ " left join project_task pt on pt.id = t.projectTaskId" | |||
+ " left join project p ON p.id = pt.project_id" | |||
+ " left join staff s on s.id = t.staffId" | |||
+ " left join salary s2 on s.salaryId = s2.salaryPoint" | |||
+ " left join team t2 on t2.id = s.teamId" | |||
+ " )," | |||
+ " cte_invoice as (" | |||
+ " select p.code, sum(i.issueAmount) as sumIssuedAmount , sum(i.paidAmount) as sumPaidAmount" | |||
+ " from invoice i" | |||
+ " left join project p on p.code = i.projectCode" | |||
+ " group by p.code" | |||
+ ")" | |||
+ " Select p.code, p.description, c.name, t2.name, p.planStart , p.planEnd , p.expectedTotalFee ," | |||
+ " s.name , IFNULL(t.normalConsumed, 0) as normalConsumed, IFNULL(t.otConsumed , 0) as otConsumed, s2.hourlyRate," | |||
+ " cte_i.sumIssuedAmount, cte_i.sumPaidAmount" | |||
+ " from timesheet t" | |||
+ " left join project_task pt on pt.id = t.projectTaskId" | |||
+ " left join project p ON p.id = pt.project_id" | |||
+ " left join staff s on s.id = t.staffId" | |||
+ " left join salary s2 on s.salaryId = s2.salaryPoint" | |||
+ " )" | |||
+ " select p.code, p.description, c.name as client, concat(t.code, \' - \', t.name) as teamLead, p.planStart , p.planEnd , p.expectedTotalFee," | |||
+ " IFNULL(cte_ts.normalConsumed, 0) as normalConsumed, IFNULL(cte_ts.otConsumed, 0) as otConsumed," | |||
+ " IFNULL(cte_ts.hourlyRate, 0) as hourlyRate, IFNULL(cte_i.sumIssuedAmount, 0) as sumIssuedAmount, IFNULL(cte_i.sumPaidAmount, 0) as sumPaidAmount" | |||
+ " from project p" | |||
+ " left join cte_timesheet cte_ts on p.code = cte_ts.code" | |||
+ " left join customer c on c.id = p.customerId" | |||
+ " left join team t2 on t2.id = s.teamId" | |||
+ " left join tsmsdb.team t on t.teamLead = p.teamLead" | |||
+ " left join cte_invoice cte_i on cte_i.code = p.code" | |||
+ " where p.status = \'On-going\'" | |||
) | |||
if (projectId!! > 0) { | |||
sql.append(" where p.id = :projectId ") | |||
if (teamLeadId!! > 0) { | |||
sql.append(" and p.teamLead = :teamLeadId ") | |||
} | |||
sql.append(" order by p.code") | |||
val args = mapOf("projectId" to projectId) | |||
val args = mapOf("teamLeadId" to teamLeadId) | |||
return jdbcDao.queryForList(sql.toString(), args) | |||
} | |||
} | |||
open fun getTimesheet(args: Map<String, Any>): List<Map<String, Any>> { | |||
val sql = StringBuilder( | |||
"SELECT" | |||
+ " p.id," | |||
+ " p.name," | |||
+ " p.code," | |||
+ " CAST(DATE_FORMAT(t.recordDate, '%d') AS SIGNED) AS recordDate," | |||
+ " sum(t.normalConsumed) as normalConsumed," | |||
+ " IFNULL(sum(t.otConsumed), 0.0) as otConsumed" | |||
+ " from timesheet t" | |||
+ " left join project_task pt on t.projectTaskId = pt.id" | |||
+ " left join project p on p.id = pt.project_id" | |||
+ " where t.staffId = :staffId" | |||
+ " group by p.id, t.recordDate" | |||
+ " order by p.id, t.recordDate" | |||
+ " and t.recordDate BETWEEN :startDate and :endDate" | |||
) | |||
return jdbcDao.queryForList(sql.toString(), args) | |||
} | |||
open fun getLeaves(args: Map<String, Any>): List<Map<String, Any>> { | |||
val sql = StringBuilder( | |||
" SELECT " | |||
+ " sum(leaveHours) as leaveHours, " | |||
+ " CAST(DATE_FORMAT(recordDate, '%d') AS SIGNED) AS recordDate " | |||
+ " from `leave` " | |||
+ " where staffId = :staffId " | |||
+ " and recordDate BETWEEN :startDate and :endDate " | |||
+ " group by recordDate " | |||
+ " order by recordDate " | |||
) | |||
return jdbcDao.queryForList(sql.toString(), args) | |||
} | |||
} |
@@ -13,6 +13,7 @@ import com.ffii.tsms.modules.report.web.model.LateStartReportRequest | |||
import com.ffii.tsms.modules.project.entity.ProjectRepository | |||
import com.ffii.tsms.modules.timesheet.entity.LeaveRepository | |||
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository | |||
import com.ffii.tsms.modules.timesheet.entity.projections.ProjectMonthlyHoursWithDate | |||
import jakarta.validation.Valid | |||
import org.springframework.core.io.ByteArrayResource | |||
import org.springframework.core.io.Resource | |||
@@ -57,8 +58,8 @@ class ReportController( | |||
@Throws(ServletRequestBindingException::class, IOException::class) | |||
fun getFinancialStatusReport(@RequestBody @Valid request: FinancialStatusReportRequest): ResponseEntity<Resource> { | |||
val reportResult: ByteArray = excelReportService.genFinancialStatusReport(request.projectId) | |||
println(request.teamLeadId) | |||
val reportResult: ByteArray = excelReportService.genFinancialStatusReport(request.teamLeadId) | |||
return ResponseEntity.ok() | |||
.header("filename", "Financial Status Report - " + LocalDate.now() + ".xlsx") | |||
@@ -74,7 +75,7 @@ class ReportController( | |||
val invoices = invoiceService.findAllByProjectAndPaidAmountIsNotNull(project) | |||
val timesheets = timesheetRepository.findAllByProjectTaskIn(projectTasks) | |||
val reportResult: ByteArray = excelReportService.generateProjectCashFlowReport(project, invoices, timesheets) | |||
val reportResult: ByteArray = excelReportService.generateProjectCashFlowReport(project, invoices, timesheets, request.dateType) | |||
// val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | |||
return ResponseEntity.ok() | |||
// .contentType(mediaType) | |||
@@ -89,14 +90,15 @@ class ReportController( | |||
val nextMonth = request.yearMonth.plusMonths(1).atDay(1) | |||
val staff = staffRepository.findById(request.id).orElseThrow() | |||
val timesheets = timesheetRepository.findByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth) | |||
val leaves = leaveRepository.findByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth) | |||
val projects = timesheetRepository.findDistinctProjectTaskByStaffAndRecordDateBetweenOrderByRecordDate(staff, thisMonth, nextMonth) | |||
val projectList: List<String> = projects.map { p -> "${p.projectTask!!.project!!.code}\n ${p.projectTask!!.project!!.name}" } | |||
val reportResult: ByteArray = excelReportService.generateStaffMonthlyWorkHourAnalysisReport(thisMonth, staff, timesheets, leaves, projectList) | |||
val args: Map<String, Any> = mutableMapOf( | |||
"staffId" to request.id, | |||
"startDate" to thisMonth, | |||
"endDate" to nextMonth, | |||
) | |||
val timesheets= excelReportService.getTimesheet(args) | |||
val leaves= excelReportService.getLeaves(args) | |||
val reportResult: ByteArray = excelReportService.generateStaffMonthlyWorkHourAnalysisReport(thisMonth, staff, timesheets, leaves) | |||
// val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | |||
return ResponseEntity.ok() | |||
// .contentType(mediaType) | |||
@@ -166,7 +168,7 @@ fun downloadLateStartReport(@RequestBody @Valid request: LateStartReportRequest) | |||
@GetMapping("/financialReport/{id}") | |||
fun getFinancialReport(@PathVariable id: Long): List<Map<String, Any>> { | |||
println(excelReportService.genFinancialStatusReport(id)) | |||
// println(excelReportService.genFinancialStatusReport(id)) | |||
return excelReportService.getFinancialStatus(id) | |||
} | |||
@@ -4,10 +4,11 @@ import java.time.LocalDate | |||
import java.time.YearMonth | |||
data class FinancialStatusReportRequest ( | |||
val projectId: Long | |||
val teamLeadId: Long | |||
) | |||
data class ProjectCashFlowReportRequest ( | |||
val projectId: Long | |||
val projectId: Long, | |||
val dateType: String, // "Date", "Month" | |||
) | |||
data class StaffMonthlyWorkHourAnalysisReportRequest ( | |||
@@ -2,7 +2,16 @@ package com.ffii.tsms.modules.timesheet.entity.projections | |||
import java.time.LocalDate | |||
data class MonthlyHours( | |||
data class MonthlyLeave( | |||
val date: LocalDate, | |||
val nomralConsumed: Number | |||
val leaveHours: Double | |||
) | |||
data class ProjectMonthlyHoursWithDate( | |||
val id: Long, | |||
val name: String, | |||
val code: String, | |||
val date: LocalDate, | |||
val normalConsumed: Double, | |||
val otConsumed: Double, | |||
) |
@@ -2,16 +2,19 @@ package com.ffii.tsms.modules.timesheet.service | |||
import com.ffii.core.exception.BadRequestException | |||
import com.ffii.tsms.modules.data.service.StaffsService | |||
import com.ffii.tsms.modules.data.service.TeamService | |||
import com.ffii.tsms.modules.project.entity.ProjectRepository | |||
import com.ffii.tsms.modules.project.entity.ProjectTaskRepository | |||
import com.ffii.tsms.modules.project.entity.TaskRepository | |||
import com.ffii.tsms.modules.timesheet.entity.Timesheet | |||
import com.ffii.tsms.modules.timesheet.entity.TimesheetRepository | |||
import com.ffii.tsms.modules.timesheet.web.models.TeamMemberTimeEntries | |||
import com.ffii.tsms.modules.timesheet.web.models.TimeEntry | |||
import org.springframework.stereotype.Service | |||
import org.springframework.transaction.annotation.Transactional | |||
import java.time.LocalDate | |||
import java.time.format.DateTimeFormatter | |||
import kotlin.jvm.optionals.getOrDefault | |||
import kotlin.jvm.optionals.getOrNull | |||
@Service | |||
@@ -20,7 +23,8 @@ open class TimesheetsService( | |||
private val projectTaskRepository: ProjectTaskRepository, | |||
private val projectRepository: ProjectRepository, | |||
private val taskRepository: TaskRepository, | |||
private val staffsService: StaffsService | |||
private val staffsService: StaffsService, | |||
private val teamService: TeamService | |||
) { | |||
@Transactional | |||
open fun saveTimesheet(recordTimeEntry: Map<LocalDate, List<TimeEntry>>): Map<String, List<TimeEntry>> { | |||
@@ -52,12 +56,56 @@ open class TimesheetsService( | |||
return transformToTimeEntryMap(savedTimesheets) | |||
} | |||
@Transactional | |||
open fun saveMemberTimeEntry(staffId: Long, entry: TimeEntry, recordDate: LocalDate?): Map<String, List<TimeEntry>> { | |||
val currentStaff = staffsService.currentStaff() ?: throw BadRequestException() | |||
// Make sure current staff is a team lead | |||
teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException() | |||
val memberStaff = staffsService.getStaff(staffId) | |||
val timesheet = timesheetRepository.findById(entry.id).getOrDefault(Timesheet()).apply { | |||
val task = entry.taskId?.let { taskRepository.findById(it).getOrNull() } | |||
val project = entry.projectId?.let { projectRepository.findById(it).getOrNull() } | |||
val projectTask = project?.let { p -> task?.let { t -> projectTaskRepository.findByProjectAndTask(p, t) } } | |||
this.normalConsumed = entry.inputHours | |||
this.otConsumed = entry.otHours | |||
this.projectTask = projectTask | |||
this.remark = entry.remark | |||
this.recordDate = this.recordDate ?: recordDate | |||
this.staff = this.staff ?: memberStaff | |||
} | |||
timesheetRepository.save(timesheet) | |||
return transformToTimeEntryMap(timesheetRepository.findAllByStaff(memberStaff)) | |||
} | |||
open fun getTimesheet(): Map<String, List<TimeEntry>> { | |||
// Need to be associated with a staff | |||
val currentStaff = staffsService.currentStaff() ?: return emptyMap() | |||
return transformToTimeEntryMap(timesheetRepository.findAllByStaff(currentStaff)) | |||
} | |||
open fun getTimeMemberTimesheet(): Map<Long, TeamMemberTimeEntries> { | |||
val currentStaff = staffsService.currentStaff() ?: return emptyMap() | |||
// Get team where current staff is team lead | |||
val myTeam = teamService.getMyTeamForStaff(currentStaff) ?: return emptyMap() | |||
val teamMembers = staffsService.findAllByTeamId(myTeam.id!!).getOrDefault(emptyList()) | |||
return teamMembers.associate { member -> | |||
Pair( | |||
member.id!!, | |||
TeamMemberTimeEntries( | |||
staffId = member.staffId, | |||
name = member.name, | |||
timeEntries = transformToTimeEntryMap(timesheetRepository.findAllByStaff(member)) | |||
) | |||
) | |||
} | |||
} | |||
private fun transformToTimeEntryMap(timesheets: List<Timesheet>): Map<String, List<TimeEntry>> { | |||
return timesheets | |||
.groupBy { timesheet -> timesheet.recordDate!!.format(DateTimeFormatter.ISO_LOCAL_DATE) } | |||
@@ -5,6 +5,8 @@ import com.ffii.tsms.modules.timesheet.entity.LeaveType | |||
import com.ffii.tsms.modules.timesheet.service.LeaveService | |||
import com.ffii.tsms.modules.timesheet.service.TimesheetsService | |||
import com.ffii.tsms.modules.timesheet.web.models.LeaveEntry | |||
import com.ffii.tsms.modules.timesheet.web.models.TeamMemberTimeEntries | |||
import com.ffii.tsms.modules.timesheet.web.models.TeamTimeEntry | |||
import com.ffii.tsms.modules.timesheet.web.models.TimeEntry | |||
import jakarta.validation.Valid | |||
import org.springframework.web.bind.annotation.GetMapping | |||
@@ -43,6 +45,18 @@ class TimesheetsController(private val timesheetsService: TimesheetsService, pri | |||
return timesheetsService.getTimesheet() | |||
} | |||
@GetMapping("/teamTimesheets") | |||
fun getTeamMemberTimesheetEntries(): Map<Long, TeamMemberTimeEntries> { | |||
return timesheetsService.getTimeMemberTimesheet() | |||
} | |||
@PostMapping("/saveMemberEntry") | |||
fun saveMemberEntry(@Valid @RequestBody request: TeamTimeEntry): Map<String, List<TimeEntry>> { | |||
return timesheetsService.saveMemberTimeEntry(request.staffId, request.entry, runCatching { | |||
LocalDate.parse(request.recordDate, DateTimeFormatter.ISO_LOCAL_DATE) | |||
}.getOrNull()) | |||
} | |||
@GetMapping("/leaves") | |||
fun getLeaveEntry(): Map<String, List<LeaveEntry>> { | |||
return leaveService.getLeaves() | |||
@@ -0,0 +1,7 @@ | |||
package com.ffii.tsms.modules.timesheet.web.models | |||
data class TeamMemberTimeEntries( | |||
val timeEntries: Map<String, List<TimeEntry>>, | |||
val staffId: String?, | |||
val name: String?, | |||
) |
@@ -0,0 +1,7 @@ | |||
package com.ffii.tsms.modules.timesheet.web.models | |||
data class TeamTimeEntry( | |||
val staffId: Long, | |||
val entry: TimeEntry, | |||
val recordDate: String, | |||
) |
@@ -24,7 +24,7 @@ public class UpdateUserReq { | |||
private String locale; | |||
private String remarks; | |||
@NotBlank | |||
// @NotBlank | |||
private String email; | |||
// @NotBlank | |||
@@ -153,19 +153,10 @@ public class UserController{ | |||
@PatchMapping("/admin-change-password") | |||
@ResponseStatus(HttpStatus.NO_CONTENT) | |||
@PreAuthorize("hasAuthority('MAINTAIN_USER')") | |||
public void adminChangePassword(@RequestBody @Valid ChangePwdReq req) { | |||
public void adminChangePassword(@RequestBody @Valid AdminChangePwdReq req) { | |||
long id = req.getId(); | |||
User instance = userService.find(id).orElseThrow(NotFoundException::new); | |||
logger.info("TEST req: "+req.getPassword()); | |||
logger.info("TEST instance: "+instance.getPassword()); | |||
// if (!passwordEncoder.matches(req.getPassword(), instance.getPassword())) { | |||
// throw new BadRequestException(); | |||
// } | |||
PasswordRule rule = new PasswordRule(settingsService); | |||
if (!PasswordUtils.checkPwd(req.getNewPassword(), rule)) { | |||
throw new UnprocessableEntityException(ErrorCodes.USER_WRONG_NEW_PWD); | |||
} | |||
instance.setPassword(passwordEncoder.encode(req.getNewPassword())); | |||
userService.save(instance); | |||
} | |||
@@ -188,6 +179,20 @@ public class UserController{ | |||
return new PasswordRule(settingsService); | |||
} | |||
public static class AdminChangePwdReq { | |||
private Long id; | |||
@NotBlank | |||
private String newPassword; | |||
public Long getId() { return id; } | |||
public Long setId(Long id) { return this.id = id; } | |||
public String getNewPassword() { | |||
return newPassword; | |||
} | |||
public void setNewPassword(String newPassword) { | |||
this.newPassword = newPassword; | |||
} | |||
} | |||
public static class ChangePwdReq { | |||
private Long id; | |||
@NotBlank | |||