Bläddra i källkod

Merge branch 'master' of https://git.2fi-solutions.com/davidhui/TSMS-backend

tags/Baseline_30082024_BACKEND_UAT
leoho2fi 1 år sedan
förälder
incheckning
4c139584d2
15 ändrade filer med 107 tillägg och 48 borttagningar
  1. +6
    -1
      src/main/java/com/ffii/tsms/config/security/jwt/web/JwtAuthenticationController.java
  2. +4
    -5
      src/main/java/com/ffii/tsms/model/JwtResponse.java
  3. +21
    -0
      src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt
  4. +3
    -1
      src/main/java/com/ffii/tsms/modules/project/entity/StaffAllocationRepository.kt
  5. +1
    -0
      src/main/java/com/ffii/tsms/modules/project/entity/TaskTemplateRepository.kt
  6. +22
    -23
      src/main/java/com/ffii/tsms/modules/project/service/ProjectsService.kt
  7. +18
    -10
      src/main/java/com/ffii/tsms/modules/project/service/TasksService.kt
  8. +2
    -1
      src/main/java/com/ffii/tsms/modules/project/web/TasksController.kt
  9. +8
    -0
      src/main/java/com/ffii/tsms/modules/project/web/models/NewTaskTemplateResponse.kt
  10. +1
    -0
      src/main/java/com/ffii/tsms/modules/project/web/models/ProjectWithTasks.kt
  11. +14
    -7
      src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt
  12. +6
    -0
      src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt
  13. +1
    -0
      src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt
  14. Binär
      src/main/resources/templates/report/AR05_Project Completion Report.xlsx
  15. Binär
      src/main/resources/templates/report/AR06_Project Completion Report with Outstanding Accounts Receivable v02.xlsx

+ 6
- 1
src/main/java/com/ffii/tsms/config/security/jwt/web/JwtAuthenticationController.java Visa fil

@@ -3,10 +3,12 @@ package com.ffii.tsms.config.security.jwt.web;
import java.time.Instant;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

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.user.service.GroupService;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -58,6 +60,9 @@ public class JwtAuthenticationController {
@Autowired
private GroupService groupService;

@Autowired
private StaffsService staffsService;

@Autowired
private StaffRepository staffRepository;

@@ -102,7 +107,7 @@ public class JwtAuthenticationController {

final Map<String, Object> args = Map.of("userId", user.getId());
final String role = groupService.getGroupName(args);
final Staff staff = staffRepository.findIdAndNameByUserIdAndDeletedFalse(user.getId()).orElse(null);
final Map<String, Object> staff = Objects.requireNonNull(staffsService.getCurrentStaff(user.getId())).orElse(null);

Set<AbilityModel> abilities = new HashSet<>();
userAuthorityService.getUserAuthority(user).forEach(auth -> abilities.add(new AbilityModel(auth.getAuthority())));


+ 4
- 5
src/main/java/com/ffii/tsms/model/JwtResponse.java Visa fil

@@ -1,6 +1,7 @@
package com.ffii.tsms.model;

import java.io.Serializable;
import java.util.Map;
import java.util.Set;

import com.ffii.tsms.modules.data.entity.Staff;
@@ -16,11 +17,9 @@ public class JwtResponse implements Serializable {
private final String refreshToken;
private final String role;
private final Set<AbilityModel> abilities;
private final Staff staff;
private final Map<String, Object> staff;



public JwtResponse(String accessToken, String refreshToken, String role, User user, Set<AbilityModel> abilities, Staff staff) {
public JwtResponse(String accessToken, String refreshToken, String role, User user, Set<AbilityModel> abilities, Map<String, Object> staff) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
this.role = role;
@@ -55,7 +54,7 @@ public class JwtResponse implements Serializable {
return email;
}

public Staff getStaff() { return staff; }
public Map<String, Object> getStaff() { return staff; }


public Set<AbilityModel> getAbilities() {


+ 21
- 0
src/main/java/com/ffii/tsms/modules/data/service/StaffsService.kt Visa fil

@@ -93,6 +93,27 @@ open class StaffsService(
return jdbcDao.queryForMap(sql.toString(), args)
}

open fun getCurrentStaff(userId: Long): Optional<MutableMap<String, Any>>? {
val staff = staffRepository.findByUserId(userId).orElse(null)
logger.info(staff)

if (staff == null) {
return Optional.ofNullable(null)
}

val sql = StringBuilder("select " +
" s.id as id, " +
" t.id as teamId, " +
" case when t.teamLead = s.id then true else false end as isTeamLead " +
" from staff s " +
" left join team t on t.id = s.teamId " +
" where s.deleted = false "
+ " and s.id = " + staff.id
)

return jdbcDao.queryForMap(sql.toString())
}

@Transactional(rollbackFor = [Exception::class])
open fun saveStaff(req: NewStaffRequest): Staff {
val checkStaffIdList: List<StaffSearchInfo> = staffRepository.findStaffSearchInfoByAndDeletedFalse()


+ 3
- 1
src/main/java/com/ffii/tsms/modules/project/entity/StaffAllocationRepository.kt Visa fil

@@ -3,10 +3,12 @@ package com.ffii.tsms.modules.project.entity;
import com.ffii.core.support.AbstractRepository
import com.ffii.tsms.modules.data.entity.Staff
import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo
import org.springframework.data.jpa.repository.Query
import java.time.LocalDate

interface StaffAllocationRepository : AbstractRepository<StaffAllocation, Long> {
fun findAssignedProjectsByStaff(staff: Staff): List<StaffAllocation>
@Query("SELECT sa.project FROM StaffAllocation sa WHERE sa.staff = ?1 AND sa.project.status = 'On-going'")
fun findOnGoingAssignedProjectsByStaff(staff: Staff): List<Project>

fun findByProject(project: Project): List<StaffAllocation>
}

+ 1
- 0
src/main/java/com/ffii/tsms/modules/project/entity/TaskTemplateRepository.kt Visa fil

@@ -3,4 +3,5 @@ package com.ffii.tsms.modules.project.entity;
import com.ffii.core.support.AbstractRepository

interface TaskTemplateRepository : AbstractRepository<TaskTemplate, Long> {
fun findByCode(code: String): TaskTemplate?
}

+ 22
- 23
src/main/java/com/ffii/tsms/modules/project/service/ProjectsService.kt Visa fil

@@ -75,29 +75,27 @@ open class ProjectsService(
open fun allAssignedProjects(): List<AssignedProject> {
return SecurityUtils.getUser().getOrNull()?.let { user ->
staffRepository.findByUserId(user.id).getOrNull()?.let { staff ->
staffAllocationRepository.findAssignedProjectsByStaff(staff).mapNotNull {
it.project?.let { project ->
val timesheetHours = timesheetRepository.totalHoursConsumedByProject(project)

AssignedProject(id = project.id!!,
code = project.code!!,
name = project.name!!,
tasks = projectTaskRepository.findAllByProject(project).mapNotNull { pt -> pt.task },
milestones = milestoneRepository.findAllByProject(project)
.filter { milestone -> milestone.taskGroup?.id != null }
.associateBy { milestone -> milestone.taskGroup!!.id!! }
.mapValues { (_, milestone) ->
MilestoneInfo(
startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE),
endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE)
)
},
hoursAllocated = project.totalManhour ?: 0.0,
hoursAllocatedOther = 0.0,
hoursSpent = timesheetHours.normalConsumed,
hoursSpentOther = timesheetHours.otConsumed
)
}
staffAllocationRepository.findOnGoingAssignedProjectsByStaff(staff).map { project ->
val timesheetHours = timesheetRepository.totalHoursConsumedByProject(project)

AssignedProject(id = project.id!!,
code = project.code!!,
name = project.name!!,
tasks = projectTaskRepository.findAllByProject(project).mapNotNull { pt -> pt.task },
milestones = milestoneRepository.findAllByProject(project)
.filter { milestone -> milestone.taskGroup?.id != null }
.associateBy { milestone -> milestone.taskGroup!!.id!! }
.mapValues { (_, milestone) ->
MilestoneInfo(
startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE),
endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE)
)
},
hoursAllocated = project.totalManhour ?: 0.0,
hoursAllocatedOther = 0.0,
hoursSpent = timesheetHours.normalConsumed,
hoursSpentOther = timesheetHours.otConsumed
)
}
}
} ?: emptyList()
@@ -108,6 +106,7 @@ open class ProjectsService(
ProjectWithTasks(id = project.id!!,
code = project.code!!,
name = project.name!!,
status = project.status,
tasks = projectTaskRepository.findAllByProject(project).mapNotNull { pt -> pt.task },
milestones = milestoneRepository.findAllByProject(project)
.filter { milestone -> milestone.taskGroup?.id != null }


+ 18
- 10
src/main/java/com/ffii/tsms/modules/project/service/TasksService.kt Visa fil

@@ -3,10 +3,7 @@ package com.ffii.tsms.modules.project.service
import com.ffii.core.support.JdbcDao
import com.ffii.tsms.modules.data.entity.GradeRepository
import com.ffii.tsms.modules.project.entity.*
import com.ffii.tsms.modules.project.web.models.EditTaskTemplateDetails
import com.ffii.tsms.modules.project.web.models.NewProjectRequest
import com.ffii.tsms.modules.project.web.models.NewTaskTemplateRequest
import com.ffii.tsms.modules.project.web.models.TaskGroupAllocation
import com.ffii.tsms.modules.project.web.models.*
import org.springframework.stereotype.Service
import kotlin.jvm.optionals.getOrNull

@@ -36,6 +33,10 @@ class TasksService(
taskTemplateRepository.deleteById(id)
}

fun findTaskTemplateByCode(code: String): TaskTemplate? {
return taskTemplateRepository.findByCode(code)
}

fun getTaskTemplateDetails(id: Long): EditTaskTemplateDetails? {
val taskTemplate = taskTemplateRepository.findById(id)

@@ -63,8 +64,19 @@ class TasksService(
}
}

fun saveTaskTemplate(request: NewTaskTemplateRequest): TaskTemplate {
fun saveTaskTemplate(request: NewTaskTemplateRequest): NewTaskTemplateResponse {
val id = request.id

val duplicateTaskTemplate = findTaskTemplateByCode(request.code)

//check duplicate customer
if (duplicateTaskTemplate != null && duplicateTaskTemplate.id?.equals(id) == false) {
return NewTaskTemplateResponse(
taskTemplate = duplicateTaskTemplate,
message = "The task template code has already existed"
);
}

val taskTemplate = if (id != null && id > 0) findTaskTemplate(id) else TaskTemplate()
taskTemplate.apply {
this.name = request.name
@@ -96,17 +108,13 @@ class TasksService(

val savedTaskTemplate = taskTemplateRepository.save<TaskTemplate>(taskTemplate)


println(taskTemplateGroupAllocationRepository.findAllByTaskTemplate(taskTemplate).size)
println(groupAllocations.size)
println(taskTemplateGroupAllocationRepository.findAllByTaskTemplate(taskTemplate).subtract(gradeAllocations.toSet()).size)
taskTemplateGradeAllocationRepository.deleteAll(taskTemplateGradeAllocationRepository.findAllByTaskTemplate(taskTemplate).subtract(gradeAllocations.toSet()))
taskTemplateGroupAllocationRepository.deleteAll(taskTemplateGroupAllocationRepository.findAllByTaskTemplate(taskTemplate).subtract(groupAllocations.toSet()))

taskTemplateGradeAllocationRepository.saveAll<TaskTemplateGradeAllocation>(gradeAllocations)
taskTemplateGroupAllocationRepository.saveAll<TaskTemplateGroupAllocation>(groupAllocations)

return savedTaskTemplate
return NewTaskTemplateResponse(taskTemplate = savedTaskTemplate, message = "Success");
}

fun allTaskGroups(): List<TaskGroup> {


+ 2
- 1
src/main/java/com/ffii/tsms/modules/project/web/TasksController.kt Visa fil

@@ -6,6 +6,7 @@ import com.ffii.tsms.modules.project.entity.TaskTemplate
import com.ffii.tsms.modules.project.service.TasksService
import com.ffii.tsms.modules.project.web.models.EditTaskTemplateDetails
import com.ffii.tsms.modules.project.web.models.NewTaskTemplateRequest
import com.ffii.tsms.modules.project.web.models.NewTaskTemplateResponse
import jakarta.validation.Valid
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
@@ -40,7 +41,7 @@ class TasksController(private val tasksService: TasksService) {
}

@PostMapping("/templates/save")
fun saveTaskTemplate(@Valid @RequestBody newTaskTemplate: NewTaskTemplateRequest): TaskTemplate {
fun saveTaskTemplate(@Valid @RequestBody newTaskTemplate: NewTaskTemplateRequest): NewTaskTemplateResponse {
return tasksService.saveTaskTemplate(newTaskTemplate)
}
}

+ 8
- 0
src/main/java/com/ffii/tsms/modules/project/web/models/NewTaskTemplateResponse.kt Visa fil

@@ -0,0 +1,8 @@
package com.ffii.tsms.modules.project.web.models

import com.ffii.tsms.modules.project.entity.TaskTemplate

data class NewTaskTemplateResponse (
val taskTemplate: TaskTemplate,
val message: String,
)

+ 1
- 0
src/main/java/com/ffii/tsms/modules/project/web/models/ProjectWithTasks.kt Visa fil

@@ -5,6 +5,7 @@ import com.ffii.tsms.modules.project.entity.Task
data class ProjectWithTasks(
val id: Long,
val code: String,
val status: String?,
val name: String,
val tasks: List<Task>,
val milestones: Map<Long, MilestoneInfo>


+ 14
- 7
src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt Visa fil

@@ -1721,9 +1721,11 @@ open class ReportService(
+ " result.code, "
+ " result.name, "
+ " result.teamCode, "
+ " result.custCode, " )
+ " result.custCode, "
+ " result.subCode, " )
if (args.get("outstanding") as Boolean) {
sql.append(" result.projectFee - (result.totalBudget - COALESCE(i.issueAmount , 0) + COALESCE(i.issueAmount, 0) - COALESCE(i.paidAmount, 0)) as `Receivable Remained`, ")
sql.append(" result.projectFee - COALESCE(i.paidAmount, 0) as `Receivable Remained`, ")
// sql.append(" result.projectFee - (result.totalBudget - COALESCE(i.issueAmount , 0) + COALESCE(i.issueAmount, 0) - COALESCE(i.paidAmount, 0)) as `Receivable Remained`, ")
}
sql.append(
" DATE_FORMAT(result.actualEnd, '%d/%m/%Y') as actualEnd "
@@ -1734,6 +1736,7 @@ open class ReportService(
+ " min(p.name) as name, "
+ " min(t.code) as teamCode, "
+ " min(c.code) as custCode, "
+ " COALESCE(concat(min(ss.code),' - ',min(ss.name)), 'N/A') as subCode, "
+ " min(p.actualEnd) as actualEnd, "
+ " min(p.expectedTotalFee) as projectFee, "
+ " sum(COALESCE(tns.totalConsumed*sal.hourlyRate, 0)) as totalBudget "
@@ -1754,14 +1757,18 @@ open class ReportService(
+ " left join salary sal on s.salaryId = sal.salaryPoint "
+ " left JOIN team t ON s.teamId = t.id "
+ " left join customer c on c.id = p.customerId "
+ " where p.deleted = false "
+ " and p.status = 'Completed' "
+ " LEFT JOIN subsidiary ss on p.customerSubsidiaryId = ss.id "
+ " where p.deleted = false ")
if (args.containsKey("teamId")) {
sql.append("t.id = :teamId")
}
sql.append(
" and p.status = 'Completed' "
+ " and p.actualEnd BETWEEN :startDate and :endDate "
+ " group by pt.project_id "
+ " ) as result "
+ " left join invoice i on result.code = i.projectCode "
+ " order by result.actualEnd "
)
+ " order by result.actualEnd ")

return jdbcDao.queryForList(sql.toString(), args)
}
@@ -1794,7 +1801,7 @@ open class ReportService(
+ " p.name, "
+ " t.code as team, "
+ " c.code as client, "
+ " COALESCE(ss.name, 'N/A') as subsidiary, "
+ " COALESCE(ss.code, 'N/A') as subsidiary, "
+ " p.expectedTotalFee * 0.8 as plannedBudget, "
+ " COALESCE(tns.totalBudget, 0) as actualConsumedBudget, "
+ " COALESCE(p.totalManhour, 0) as plannedManhour, "


+ 6
- 0
src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt Visa fil

@@ -2,9 +2,11 @@ package com.ffii.tsms.modules.report.web

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.ffii.tsms.modules.common.SecurityUtils
import com.ffii.tsms.modules.data.entity.*
//import com.ffii.tsms.modules.data.entity.projections.FinancialStatusReportInfo
import com.ffii.tsms.modules.data.service.CustomerService
import com.ffii.tsms.modules.data.service.StaffsService
import com.ffii.tsms.modules.data.service.TeamService
import com.ffii.tsms.modules.project.entity.*
import com.ffii.tsms.modules.project.service.InvoiceService
@@ -48,6 +50,7 @@ class ReportController(
private val customerRepository: CustomerRepository,
private val staffRepository: StaffRepository,
private val leaveRepository: LeaveRepository,
private val staffsService: StaffsService,
private val teamService: TeamService,
private val customerService: CustomerService,
private val subsidiaryService: SubsidiaryService,
@@ -184,6 +187,9 @@ class ReportController(
"endDate" to request.endDate,
"outstanding" to request.outstanding
)
if (request.teamId != null) {
args["teamId"] = request.teamId
}
val result = excelReportService.getProjectCompletionReport(args);
val reportResult: ByteArray = excelReportService.generateProjectCompletionReport(args, result)
// val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")


+ 1
- 0
src/main/java/com/ffii/tsms/modules/report/web/model/ReportRequest.kt Visa fil

@@ -52,6 +52,7 @@ data class ProjectResourceOverconsumptionReport (
val lowerLimit: Double
)
data class ProjectCompletionReport (
val teamId: Long?,
val startDate: LocalDate,
val endDate: LocalDate,
val outstanding: Boolean

Binär
src/main/resources/templates/report/AR05_Project Completion Report.xlsx Visa fil


Binär
src/main/resources/templates/report/AR06_Project Completion Report with Outstanding Accounts Receivable v02.xlsx Visa fil


Laddar…
Avbryt
Spara