diff --git a/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java b/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java index d650327..b9fc459 100644 --- a/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java +++ b/src/main/java/com/ffii/tsms/modules/data/entity/StaffRepository.java @@ -22,5 +22,7 @@ public interface StaffRepository extends AbstractRepository { Optional findByUserId(@Param("userId") Long userId); Optional> findAllByTeamIdAndDeletedFalse(Long id); + Optional> findAllByDeletedFalse(); + Optional findIdAndNameByUserIdAndDeletedFalse(Long id); } \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt b/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt index 795823a..8ef4048 100644 --- a/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt +++ b/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt @@ -19,6 +19,7 @@ open class DashboardService( private val customerTypeRepository: CustomerTypeRepository, private val customerSubsidiaryService: CustomerSubsidiaryService, private val customerContactService: CustomerContactService, + private val staffsService: StaffsService, private val jdbcDao: JdbcDao ) { @@ -43,6 +44,7 @@ open class DashboardService( + " from customer c" + " left join project p on c.id = p.customerId" + " left join subsidiary s on p.customerSubsidiaryId = s.id" + + " left join team t on t.teamLead = p.teamLead" + " where c.deleted = 0" + " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")" ) @@ -52,6 +54,14 @@ open class DashboardService( if (args.containsKey("customerCode")) sql.append(" AND c.code = :customerCode"); } + + if (viewDashboardAuthority() == "self") { + val teamId = staffsService.currentStaff()?.team?.id + if (teamId != null) { + sql.append(" AND t.id = $teamId") + } + } + sql.append(" group 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"); return jdbcDao.queryForList(sql.toString(), args) } @@ -93,9 +103,16 @@ open class DashboardService( + " 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" ) + if (viewDashboardAuthority() == "self") { + val teamId = staffsService.currentStaff()?.team?.id + if (teamId != null) { + sql.append(" and te.id = $teamId") + } + } + + sql.append(" group by p.id, p.code, p.name, te.code, s.name, tg.name, p.totalManhour, milestonePayment.comingPaymentMilestone") return jdbcDao.queryForList(sql.toString(), args) } @@ -236,9 +253,16 @@ open class DashboardService( + " left join project p on s.id = p.teamLead" + " where t.deleted = 0" + " and p.status not in (\"Pending to Start\",\"Completed\",\"Deleted\")" - + " group by t.id,t.teamLead,t.code,t.name" ) + if (viewDashboardAuthority() == "self") { + val teamId = staffsService.currentStaff()?.team?.id + if (teamId != null) { + sql.append(" and t.id = $teamId") + } + } + + sql.append(" group by t.id,t.teamLead,t.code,t.name") return jdbcDao.queryForList(sql.toString(), args) } @@ -339,6 +363,14 @@ open class DashboardService( + " where t.deleted = 0" + " and p.status = 'On-going'" ) + + if (viewDashboardAuthority() == "self") { + val teamId = staffsService.currentStaff()?.team?.id + if (teamId != null) { + sql.append(" and t.id = $teamId") + } + } + sql.append(" group by t.id, t.name") return jdbcDao.queryForList(sql.toString(), args) @@ -565,6 +597,13 @@ open class DashboardService( + " and p.status = 'On-going'" ) + if (viewDashboardAuthority() == "self") { + val teamId = staffsService.currentStaff()?.team?.id + if (teamId != null) { + sql.append(" and t.id = $teamId") + } + } + return jdbcDao.queryForList(sql.toString(), args) } fun CashFlowMonthlyIncomeByMonth(args: Map): List> { @@ -977,6 +1016,13 @@ open class DashboardService( + " and p.deleted = 0" ) + if (viewDashboardAuthority() == "self") { + val teamLeadId = staffsService.currentStaff()?.id + if (teamLeadId != null) { + sql.append(" and p.teamLead = $teamLeadId") + } + } + return jdbcDao.queryForList(sql.toString(), args) } fun projectResourceSummaryInformation(args: Map): List> { @@ -1303,6 +1349,7 @@ open class DashboardService( + " AND dates.missing_date = ts.recordDate" + " WHERE" + " st.teamId = :teamId" + + " and st.deleted = 0" + " AND ts.recordDate IS NULL" + " GROUP BY" + " st.id," @@ -1357,6 +1404,7 @@ open class DashboardService( + " ON st.id = ts.staffId AND dates.missing_date = ts.recordDate" + " WHERE" + " st.teamId = :teamId" + + " and st.deleted = 0" + " AND ts.recordDate IS NULL" + " GROUP BY" + " st.id," @@ -1384,11 +1432,20 @@ open class DashboardService( + " where g.deleted = 0" + " and t.recordDate >= :startdate" + " and t.recordDate < DATE_FORMAT(:enddate, '%Y-%m-%d 23:59:59')" - + " group by g.id" - + " ) as records on records.gid = g.id" - + " group by g.id, g.name,records.manhours" + ) + if (viewDashboardAuthority() == "self") { + val teamId = staffsService.currentStaff()?.team?.id + if (teamId != null) { + sql.append(" and s.teamId = $teamId") + } + } + + sql.append(" group by g.id" + + " ) as records on records.gid = g.id" + + " group by g.id, g.name,records.manhours") + return jdbcDao.queryForList(sql.toString(), args) } fun staffGradeTotalPlannedManhours(args: Map): List> { @@ -1417,9 +1474,17 @@ open class DashboardService( + " where p.status = 'On-going'" + " and p.planEnd > :startdate" + " and p.planStart < :enddate" - + " order by g.id" ) + if (viewDashboardAuthority() == "self") { + val teamLeadId = staffsService.currentStaff()?.id + if (teamLeadId != null) { + sql.append(" and p.teamLead = $teamLeadId") + } + } + + sql.append(" order by g.id") + return jdbcDao.queryForList(sql.toString(), args) } fun IndividualStaffManhoursSpentByMonth(args: Map): List> { @@ -1502,7 +1567,7 @@ open class DashboardService( + " left join project p on sa.project_id = p.id" + " left join timesheet t on p.id = t.projectId" + " where s.id = :staffId" - + " and t.recordDate >= :startdate" + + " and t.recordDate = :startdate" + " group by p.id, p.name" + " ) as result on result.pid = p2.id" + " where s2.id = :staffId" @@ -1606,8 +1671,30 @@ open class DashboardService( + " where s.deleted = 0" ) + if (viewDashboardAuthority() == "self") { + val teamId = staffsService.currentStaff()?.team?.id + if (teamId != null) { + sql.append(" and s.teamId = $teamId") + } + } + return jdbcDao.queryForList(sql.toString(), args) } + + fun viewDashboardAuthority(): String { + val authorities = staffsService.currentAuthorities() ?: return "no_authority" + + val authorityViewDashboardAll = authorities.stream().anyMatch { it.authority.equals("VIEW_DASHBOARD_ALL") } + val authorityViewDashboardSelf = authorities.stream().anyMatch { it.authority.equals("VIEW_DASHBOARD_SELF") } + + return if (authorityViewDashboardAll) { + "all" + } else if (authorityViewDashboardSelf) { + "self" + } else { + "no_authority" + } + } } diff --git a/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt b/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt index ad3b6bf..07ad658 100644 --- a/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt +++ b/src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt @@ -9,6 +9,7 @@ import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo import com.ffii.tsms.modules.data.web.models.NewStaffRequest import com.ffii.tsms.modules.user.entity.User import com.ffii.tsms.modules.user.entity.UserRepository +import org.springframework.security.core.GrantedAuthority import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -83,6 +84,9 @@ open class StaffsService( return staffRepository.findAllByTeamIdAndDeletedFalse(id); } + open fun findAll(): Optional> { + return staffRepository.findAllByDeletedFalse() + } open fun getStaff(args: Map): Optional>? { val sql = StringBuilder("select" + " s.id as id," @@ -94,12 +98,7 @@ open class StaffsService( } open fun getCurrentStaff(userId: Long): Optional>? { - val staff = staffRepository.findByUserId(userId).orElse(null) - logger.info(staff) - - if (staff == null) { - return Optional.ofNullable(null) - } + val staff = staffRepository.findByUserId(userId).orElse(null) ?: return Optional.ofNullable(null) val sql = StringBuilder("select " + " s.id as id, " + @@ -255,4 +254,8 @@ open class StaffsService( staffRepository.findByUserId(user.id).getOrNull() } } + + open fun currentAuthorities(): Collection? { + return SecurityUtils.getUser().getOrNull()?.authorities + } } \ No newline at end of file diff --git a/src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt b/src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt index 80f1055..3d068a5 100644 --- a/src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt +++ b/src/main/java/com/ffii/tsms/modules/data/web/DashboardController.kt @@ -2,10 +2,6 @@ package com.ffii.tsms.modules.data.web import com.ffii.tsms.modules.data.entity.Customer import com.ffii.tsms.modules.data.entity.CustomerType -import com.ffii.tsms.modules.data.service.CustomerContactService -import com.ffii.tsms.modules.data.service.CustomerService -import com.ffii.tsms.modules.data.service.CustomerSubsidiaryService -import com.ffii.tsms.modules.data.service.DashboardService 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 @@ -22,6 +18,7 @@ import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.ResponseStatus import com.ffii.core.response.RecordsRes import com.ffii.core.utils.CriteriaArgsBuilder +import com.ffii.tsms.modules.data.service.* @RestController @RequestMapping("/dashboard") @@ -29,7 +26,8 @@ class DashboardController( private val customerService: CustomerService, private val customerSubsidiaryService: CustomerSubsidiaryService, private val customerContactService: CustomerContactService, - private val dashboardService: DashboardService + private val dashboardService: DashboardService, + private val staffsService: StaffsService, ) { @GetMapping("/searchCustomerSubsidiary") fun searchCustomerSubsidiary(request: HttpServletRequest?): List> { @@ -94,13 +92,19 @@ class DashboardController( } @GetMapping("/searchFinancialSummaryCard") fun searchFinancialSummaryCard(request: HttpServletRequest?): List> { - val args = mutableMapOf() - val allTeamCardData = dashboardService.searchFinancialSummaryAllTeamCard(args) - val cardData = dashboardService.searchFinancialSummaryCard(args) + val authority = dashboardService.viewDashboardAuthority() + val args = mutableMapOf() val result = mutableListOf>() - result.addAll(allTeamCardData) - result.addAll(cardData) + if (authority == "all") { + val allTeamCardData = dashboardService.searchFinancialSummaryAllTeamCard(args) + val cardData = dashboardService.searchFinancialSummaryCard(args) + result.addAll(allTeamCardData) + result.addAll(cardData) + } else if (authority == "self") { + val cardData = dashboardService.searchFinancialSummaryCard(args) + result.addAll(cardData) + } return result } diff --git a/src/main/java/com/ffii/tsms/modules/data/web/TeamController.kt b/src/main/java/com/ffii/tsms/modules/data/web/TeamController.kt index 95d9831..66bbcb3 100644 --- a/src/main/java/com/ffii/tsms/modules/data/web/TeamController.kt +++ b/src/main/java/com/ffii/tsms/modules/data/web/TeamController.kt @@ -43,14 +43,9 @@ class TeamController( @GetMapping("/{id}") fun getStaff(@PathVariable id: Long): Map { val staffList = staffsService.findAllByTeamId(id).orElseThrow { NotFoundException() } - val staffIdList: MutableList = mutableListOf() - for (staff in staffList) { - staffIdList.add(staff.id as Long) - } -// val map: Map = java.util.Map.of("team" to teamService.find(id).orElseThrow { NotFoundException() }) -// map["staffIds"] = staffIdList + val staffIdList: List = staffList.map { it.id } return java.util.Map.of( - "team", staffsService.find(id).orElseThrow { NotFoundException() }, + "team", teamService.find(id).orElseThrow { NotFoundException() }, "staffIds", staffIdList ) } diff --git a/src/main/java/com/ffii/tsms/modules/data/web/models/NewStaffRequest.kt b/src/main/java/com/ffii/tsms/modules/data/web/models/NewStaffRequest.kt index 39a8feb..b536172 100644 --- a/src/main/java/com/ffii/tsms/modules/data/web/models/NewStaffRequest.kt +++ b/src/main/java/com/ffii/tsms/modules/data/web/models/NewStaffRequest.kt @@ -21,7 +21,7 @@ data class NewStaffRequest( val email: String, @field:NotBlank(message = "Staff employType cannot be empty") val employType: String, - @field:NotBlank(message = "Staff grade cannot be empty") + @field:NotNull(message = "Staff grade cannot be empty") val gradeId: Long, val joinDate: LocalDate?, diff --git a/src/main/java/com/ffii/tsms/modules/project/entity/ProjectTask.kt b/src/main/java/com/ffii/tsms/modules/project/entity/ProjectTask.kt index 3715a43..820df0c 100644 --- a/src/main/java/com/ffii/tsms/modules/project/entity/ProjectTask.kt +++ b/src/main/java/com/ffii/tsms/modules/project/entity/ProjectTask.kt @@ -11,7 +11,7 @@ open class ProjectTask : IdEntity() { @ManyToOne open var project: Project? = null - @ManyToOne(cascade = [CascadeType.ALL]) + @ManyToOne @JoinColumn(name = "milestoneId") open var milestone: Milestone? = null diff --git a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt index cb678cb..ba386ac 100644 --- a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt +++ b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt @@ -1606,7 +1606,9 @@ open class ReportService( // NEW Column F: Subsidiary Name val subsidiaryNameCell = row.createCell(5) - subsidiaryNameCell.setCellValue(data["subsidiary_name"] as String) +// subsidiaryNameCell.setCellValue(data["subsidiary_name"] as String) + val subsidiaryName = data["subsidiary_name"] as? String ?: "N/A" // Checks if subsidiary_name is null and replaces it with "N/A" + subsidiaryNameCell.setCellValue(subsidiaryName) // Column G: Project Plan Start Date val projectPlanStartCell = row.createCell(6) @@ -1745,7 +1747,7 @@ open class ReportService( + " min(p.code) as code, " + " min(p.name) as name, " + " min(t.code) as teamCode, " - + " min(c.code) as custCode, " + + " concat(min(c.code), ' - ', min(c.name)) as custCode, " + " COALESCE(concat(min(ss.code),' - ',min(ss.name)), 'N/A') as subCode, " + " min(p.actualEnd) as actualEnd, " + " min(p.expectedTotalFee) as projectFee, " @@ -1810,8 +1812,8 @@ open class ReportService( + " p.code, " + " p.name, " + " t.code as team, " - + " c.code as client, " - + " COALESCE(ss.code, 'N/A') as subsidiary, " + + " concat(c.code, ' - ', c.name) as client, " + + " COALESCE(concat(ss.code, ' - ', ss.name), 'N/A') as subsidiary, " + " p.expectedTotalFee * 0.8 as plannedBudget, " + " COALESCE(tns.totalBudget, 0) as actualConsumedBudget, " + " COALESCE(p.totalManhour, 0) as plannedManhour, " diff --git a/src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt b/src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt index 73a42ba..5a0f7f5 100644 --- a/src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt +++ b/src/main/java/com/ffii/tsms/modules/timesheet/service/LeaveService.kt @@ -57,9 +57,15 @@ open class LeaveService( @Transactional open fun saveMemberLeaveEntry(staffId: Long, entry: LeaveEntry, recordDate: LocalDate?): Map> { - val currentStaff = staffsService.currentStaff() ?: throw BadRequestException() - // Make sure current staff is a team lead - teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException() + val authorities = staffsService.currentAuthorities() ?: throw BadRequestException() + + if (!authorities.stream().anyMatch { it.authority.equals("MAINTAIN_TIMESHEET") }) { + throw BadRequestException() + } + +// val currentStaff = staffsService.currentStaff() ?: throw BadRequestException() +// // Make sure current staff is a team lead +// teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException() val leaveTypesMap = getLeaveTypes().associateBy { it.id } val memberStaff = staffsService.getStaff(staffId) @@ -87,22 +93,28 @@ open class LeaveService( } open fun getTeamMemberLeave(): Map { - 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!!, - TeamMemberLeaveEntries( - staffId = member.staffId, - name = member.name, - leaveEntries = transformToLeaveEntryMap(leaveRepository.findAllByStaff(member)) + val authorities = staffsService.currentAuthorities() ?: return emptyMap() + if (authorities.stream().anyMatch { it.authority.equals("MAINTAIN_TIMESHEET") }) { + val currentStaff = staffsService.currentStaff() + + // Get team where current staff is team lead + val myTeam = if (currentStaff != null) teamService.getMyTeamForStaff(currentStaff) else null + val teamMembers = if (myTeam != null) { + staffsService.findAllByTeamId(myTeam.id!!).getOrDefault(emptyList()) + } else { + staffsService.findAll().getOrDefault(emptyList()) + } + return teamMembers.associate { member -> + Pair( + member.id!!, + TeamMemberLeaveEntries( + staffId = member.staffId, + name = member.name, + leaveEntries = transformToLeaveEntryMap(leaveRepository.findAllByStaff(member)) + ) ) - ) - } + } + } else return emptyMap() } private fun transformToLeaveEntryMap(leaves: List): Map> { diff --git a/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt b/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt index 96f9c23..a387127 100644 --- a/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt +++ b/src/main/java/com/ffii/tsms/modules/timesheet/service/TimesheetsService.kt @@ -1,6 +1,8 @@ package com.ffii.tsms.modules.timesheet.service import com.ffii.core.exception.BadRequestException +import com.ffii.tsms.modules.data.entity.Staff +import com.ffii.tsms.modules.data.entity.StaffRepository import com.ffii.tsms.modules.data.service.StaffsService import com.ffii.tsms.modules.data.service.TeamService import com.ffii.tsms.modules.project.entity.ProjectRepository @@ -24,8 +26,8 @@ open class TimesheetsService( private val projectRepository: ProjectRepository, private val taskRepository: TaskRepository, private val staffsService: StaffsService, - private val teamService: TeamService - ) { + private val teamService: TeamService, private val staffRepository: StaffRepository +) { @Transactional open fun saveTimesheet(recordTimeEntry: Map>): Map> { // Need to be associated with a staff @@ -59,9 +61,14 @@ open class TimesheetsService( @Transactional open fun saveMemberTimeEntry(staffId: Long, entry: TimeEntry, recordDate: LocalDate?): Map> { - val currentStaff = staffsService.currentStaff() ?: throw BadRequestException() - // Make sure current staff is a team lead - teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException() + val authorities = staffsService.currentAuthorities() ?: throw BadRequestException() + + if (!authorities.stream().anyMatch { it.authority.equals("MAINTAIN_TIMESHEET") }) { + throw BadRequestException() + } +// val currentStaff = staffsService.currentStaff() ?: throw BadRequestException() +// // Make sure current staff is a team lead +// teamService.getMyTeamForStaff(currentStaff) ?: throw BadRequestException() val memberStaff = staffsService.getStaff(staffId) @@ -101,22 +108,29 @@ open class TimesheetsService( } open fun getTeamMemberTimesheet(): Map { - 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)) + val authorities = staffsService.currentAuthorities() ?: return emptyMap() + if (authorities.stream().anyMatch { it.authority.equals("MAINTAIN_TIMESHEET")}) { + val currentStaff = staffsService.currentStaff() + // Get team where current staff is team lead + + val myTeam = if (currentStaff != null) teamService.getMyTeamForStaff(currentStaff) else null + val teamMembers = if (myTeam != null) { + staffsService.findAllByTeamId(myTeam.id!!).getOrDefault(emptyList()) + } else { + staffsService.findAll().getOrDefault(emptyList()) + } + + return teamMembers.associate { member -> + Pair( + member.id!!, + TeamMemberTimeEntries( + staffId = member.staffId, + name = member.name, + timeEntries = transformToTimeEntryMap(timesheetRepository.findAllByStaff(member)) + ) ) - ) - } + } + } else return emptyMap() } private fun transformToTimeEntryMap(timesheets: List): Map> { diff --git a/src/main/resources/db/changelog/changes/20240604_01_cyril/01_insert_authority.sql b/src/main/resources/db/changelog/changes/20240604_01_cyril/01_insert_authority.sql new file mode 100644 index 0000000..323764e --- /dev/null +++ b/src/main/resources/db/changelog/changes/20240604_01_cyril/01_insert_authority.sql @@ -0,0 +1,8 @@ +-- liquibase formatted sql +-- changeset cyril:authority, user_authority + +INSERT INTO authority (authority,name) +VALUES ('MAINTAIN_TIMESHEET_FAST_TIME_ENTRY','enter fast time entry in timesheet'); + +INSERT INTO user_authority (userId,authId) +VALUES (1,20);