|
@@ -15,29 +15,31 @@ import org.springframework.stereotype.Service |
|
|
import org.springframework.transaction.annotation.Transactional |
|
|
import org.springframework.transaction.annotation.Transactional |
|
|
import java.time.LocalDate |
|
|
import java.time.LocalDate |
|
|
import java.time.format.DateTimeFormatter |
|
|
import java.time.format.DateTimeFormatter |
|
|
|
|
|
import java.util.Optional |
|
|
import kotlin.jvm.optionals.getOrElse |
|
|
import kotlin.jvm.optionals.getOrElse |
|
|
import kotlin.jvm.optionals.getOrNull |
|
|
import kotlin.jvm.optionals.getOrNull |
|
|
|
|
|
|
|
|
@Service |
|
|
@Service |
|
|
open class ProjectsService( |
|
|
open class ProjectsService( |
|
|
private val projectRepository: ProjectRepository, |
|
|
|
|
|
private val customerService: CustomerService, |
|
|
|
|
|
private val tasksService: TasksService, |
|
|
|
|
|
private val customerContactService: CustomerContactService, |
|
|
|
|
|
private val subsidiaryService: SubsidiaryService, |
|
|
|
|
|
private val gradeService: GradeService, |
|
|
|
|
|
private val projectCategoryRepository: ProjectCategoryRepository, |
|
|
|
|
|
private val staffRepository: StaffRepository, |
|
|
|
|
|
private val staffAllocationRepository: StaffAllocationRepository, |
|
|
|
|
|
private val fundingTypeRepository: FundingTypeRepository, |
|
|
|
|
|
private val serviceTypeRepository: ServiceTypeRepository, |
|
|
|
|
|
private val contractTypeRepository: ContractTypeRepository, |
|
|
|
|
|
private val locationRepository: LocationRepository, |
|
|
|
|
|
private val buildingTypeRepository: BuildingTypeRepository, |
|
|
|
|
|
private val workNatureRepository: WorkNatureRepository, |
|
|
|
|
|
private val milestoneRepository: MilestoneRepository, |
|
|
|
|
|
private val gradeAllocationRepository: GradeAllocationRepository, |
|
|
|
|
|
private val projectTaskRepository: ProjectTaskRepository |
|
|
|
|
|
|
|
|
private val projectRepository: ProjectRepository, |
|
|
|
|
|
private val customerService: CustomerService, |
|
|
|
|
|
private val tasksService: TasksService, |
|
|
|
|
|
private val customerContactService: CustomerContactService, |
|
|
|
|
|
private val subsidiaryService: SubsidiaryService, |
|
|
|
|
|
private val gradeService: GradeService, |
|
|
|
|
|
private val projectCategoryRepository: ProjectCategoryRepository, |
|
|
|
|
|
private val staffRepository: StaffRepository, |
|
|
|
|
|
private val staffAllocationRepository: StaffAllocationRepository, |
|
|
|
|
|
private val fundingTypeRepository: FundingTypeRepository, |
|
|
|
|
|
private val serviceTypeRepository: ServiceTypeRepository, |
|
|
|
|
|
private val contractTypeRepository: ContractTypeRepository, |
|
|
|
|
|
private val locationRepository: LocationRepository, |
|
|
|
|
|
private val buildingTypeRepository: BuildingTypeRepository, |
|
|
|
|
|
private val workNatureRepository: WorkNatureRepository, |
|
|
|
|
|
private val milestoneRepository: MilestoneRepository, |
|
|
|
|
|
private val gradeAllocationRepository: GradeAllocationRepository, |
|
|
|
|
|
private val projectTaskRepository: ProjectTaskRepository, |
|
|
|
|
|
private val milestonePaymentRepository: MilestonePaymentRepository, private val taskGroupRepository: TaskGroupRepository |
|
|
) { |
|
|
) { |
|
|
open fun allProjects(): List<ProjectSearchInfo> { |
|
|
open fun allProjects(): List<ProjectSearchInfo> { |
|
|
return projectRepository.findProjectSearchInfoByOrderByCreatedDesc() |
|
|
return projectRepository.findProjectSearchInfoByOrderByCreatedDesc() |
|
@@ -93,7 +95,7 @@ open class ProjectsService( |
|
|
@Transactional |
|
|
@Transactional |
|
|
open fun saveProject(request: NewProjectRequest): NewProjectResponse { |
|
|
open fun saveProject(request: NewProjectRequest): NewProjectResponse { |
|
|
val projectCategory = |
|
|
val projectCategory = |
|
|
projectCategoryRepository.findById(request.projectCategoryId).orElseThrow() |
|
|
|
|
|
|
|
|
projectCategoryRepository.findById(request.projectCategoryId).orElseThrow() |
|
|
val fundingType = fundingTypeRepository.findById(request.fundingTypeId).orElseThrow() |
|
|
val fundingType = fundingTypeRepository.findById(request.fundingTypeId).orElseThrow() |
|
|
val serviceType = serviceTypeRepository.findById(request.serviceTypeId).orElseThrow() |
|
|
val serviceType = serviceTypeRepository.findById(request.serviceTypeId).orElseThrow() |
|
|
val contractType = contractTypeRepository.findById(request.contractTypeId).orElseThrow() |
|
|
val contractType = contractTypeRepository.findById(request.contractTypeId).orElseThrow() |
|
@@ -112,65 +114,76 @@ open class ProjectsService( |
|
|
|
|
|
|
|
|
val project = if (request.projectId != null && request.projectId > 0) projectRepository.findById(request.projectId).orElseThrow() else Project() |
|
|
val project = if (request.projectId != null && request.projectId > 0) projectRepository.findById(request.projectId).orElseThrow() else Project() |
|
|
project.apply { |
|
|
project.apply { |
|
|
name = request.projectName |
|
|
|
|
|
description = request.projectDescription |
|
|
|
|
|
code = request.projectCode |
|
|
|
|
|
expectedTotalFee = request.expectedProjectFee |
|
|
|
|
|
totalManhour = request.totalManhour |
|
|
|
|
|
actualStart = request.projectActualStart |
|
|
|
|
|
actualEnd = request.projectActualEnd |
|
|
|
|
|
|
|
|
|
|
|
this.projectCategory = projectCategory |
|
|
|
|
|
this.fundingType = fundingType |
|
|
|
|
|
this.serviceType = serviceType |
|
|
|
|
|
this.contractType = contractType |
|
|
|
|
|
this.location = location |
|
|
|
|
|
this.buildingTypes = buildingTypes |
|
|
|
|
|
this.workNatures = workNatures |
|
|
|
|
|
|
|
|
|
|
|
this.teamLead = teamLead |
|
|
|
|
|
this.customer = customer |
|
|
|
|
|
custLeadName = clientContact.name |
|
|
|
|
|
custLeadEmail = clientContact.email |
|
|
|
|
|
custLeadPhone = clientContact.phone |
|
|
|
|
|
this.customerSubsidiary = customerSubsidiary |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
name = request.projectName |
|
|
|
|
|
description = request.projectDescription |
|
|
|
|
|
code = request.projectCode |
|
|
|
|
|
expectedTotalFee = request.expectedProjectFee |
|
|
|
|
|
totalManhour = request.totalManhour |
|
|
|
|
|
actualStart = request.projectActualStart |
|
|
|
|
|
actualEnd = request.projectActualEnd |
|
|
|
|
|
|
|
|
|
|
|
this.projectCategory = projectCategory |
|
|
|
|
|
this.fundingType = fundingType |
|
|
|
|
|
this.serviceType = serviceType |
|
|
|
|
|
this.contractType = contractType |
|
|
|
|
|
this.location = location |
|
|
|
|
|
this.buildingTypes = buildingTypes |
|
|
|
|
|
this.workNatures = workNatures |
|
|
|
|
|
|
|
|
|
|
|
this.teamLead = teamLead |
|
|
|
|
|
this.customer = customer |
|
|
|
|
|
custLeadName = clientContact.name |
|
|
|
|
|
custLeadEmail = clientContact.email |
|
|
|
|
|
custLeadPhone = clientContact.phone |
|
|
|
|
|
this.customerSubsidiary = customerSubsidiary |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Milestones and tasks |
|
|
// Milestones and tasks |
|
|
val tasksToSave = mutableListOf<ProjectTask>() |
|
|
val tasksToSave = mutableListOf<ProjectTask>() |
|
|
val milestones = request.taskGroups.entries.map { (taskStageId, taskGroupAllocation) -> |
|
|
val milestones = request.taskGroups.entries.map { (taskStageId, taskGroupAllocation) -> |
|
|
Milestone().apply { |
|
|
|
|
|
|
|
|
val taskGroup = taskGroupRepository.findById(taskStageId).orElse(TaskGroup()) |
|
|
|
|
|
val milestone = milestoneRepository.findByProjectAndTaskGroup(project, taskGroup) ?: Milestone() |
|
|
|
|
|
milestone.apply { |
|
|
val newMilestone = this |
|
|
val newMilestone = this |
|
|
val requestMilestone = request.milestones[taskStageId] |
|
|
val requestMilestone = request.milestones[taskStageId] |
|
|
this.project = project |
|
|
this.project = project |
|
|
this.taskGroup = taskGroupMap[taskStageId] |
|
|
this.taskGroup = taskGroupMap[taskStageId] |
|
|
this.startDate = requestMilestone?.startDate?.let { LocalDate.parse(it) } |
|
|
this.startDate = requestMilestone?.startDate?.let { LocalDate.parse(it) } |
|
|
this.endDate = requestMilestone?.endDate?.let { LocalDate.parse(it) } |
|
|
this.endDate = requestMilestone?.endDate?.let { LocalDate.parse(it) } |
|
|
this.milestonePayments = requestMilestone?.payments?.map { |
|
|
|
|
|
MilestonePayment().apply { |
|
|
|
|
|
|
|
|
requestMilestone?.payments?.map { |
|
|
|
|
|
val milestonePayment = if (it.id > 0) milestonePaymentRepository.findById(it.id).orElse(MilestonePayment()) else MilestonePayment() |
|
|
|
|
|
|
|
|
|
|
|
this.milestonePayments.add(milestonePayment.apply { |
|
|
|
|
|
this.milestone = newMilestone |
|
|
this.description = it.description |
|
|
this.description = it.description |
|
|
this.amount = it.amount |
|
|
this.amount = it.amount |
|
|
this.date = LocalDate.parse(it.date) |
|
|
this.date = LocalDate.parse(it.date) |
|
|
this.milestone = newMilestone |
|
|
|
|
|
} |
|
|
|
|
|
}?.toMutableList() ?: mutableListOf() |
|
|
|
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Tasks |
|
|
// Tasks |
|
|
this.stagePercentAllocation = taskGroupAllocation.percentAllocation |
|
|
this.stagePercentAllocation = taskGroupAllocation.percentAllocation |
|
|
|
|
|
|
|
|
taskGroupAllocation.taskIds.map { taskId -> ProjectTask().apply { |
|
|
|
|
|
this.project = project |
|
|
|
|
|
this.milestone = newMilestone |
|
|
|
|
|
this.task = allTasksMap[taskId] |
|
|
|
|
|
} }.let { tasksToSave.addAll(it) } |
|
|
|
|
|
|
|
|
taskGroupAllocation.taskIds.map { taskId -> |
|
|
|
|
|
val projectTask = projectTaskRepository.findByProjectAndTask(project, allTasksMap[taskId]!!) ?:ProjectTask() |
|
|
|
|
|
projectTask.apply { |
|
|
|
|
|
this.project = project |
|
|
|
|
|
this.milestone = newMilestone |
|
|
|
|
|
this.task = allTasksMap[taskId] |
|
|
|
|
|
} |
|
|
|
|
|
}.let { |
|
|
|
|
|
tasksToSave.addAll(it) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Grade allocation (from manhourPercentageByGrade) |
|
|
// Grade allocation (from manhourPercentageByGrade) |
|
|
val gradeAllocations = request.manhourPercentageByGrade.entries.map { |
|
|
val gradeAllocations = request.manhourPercentageByGrade.entries.map { |
|
|
GradeAllocation().apply { |
|
|
|
|
|
|
|
|
val gradeAllocation = gradeAllocationRepository.findByProjectAndGrade(project, gradeMap[it.key]!!) ?: GradeAllocation() |
|
|
|
|
|
|
|
|
|
|
|
gradeAllocation.apply { |
|
|
this.project = project |
|
|
this.project = project |
|
|
this.manhour = it.value |
|
|
this.manhour = it.value |
|
|
this.grade = gradeMap[it.key] |
|
|
this.grade = gradeMap[it.key] |
|
@@ -185,6 +198,14 @@ open class ProjectsService( |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
val savedProject = projectRepository.save(project) |
|
|
val savedProject = projectRepository.save(project) |
|
|
|
|
|
|
|
|
|
|
|
val milestonesToDelete = milestoneRepository.findAllByProject(savedProject).subtract(milestones.toSet()) |
|
|
|
|
|
val tasksToDelete = projectTaskRepository.findAllByProject(savedProject).subtract(tasksToSave.toSet()) |
|
|
|
|
|
val gradeAllocationsToDelete = gradeAllocationRepository.findByProject(savedProject).subtract(gradeAllocations.toSet()) |
|
|
|
|
|
milestoneRepository.deleteAll(milestonesToDelete) |
|
|
|
|
|
projectTaskRepository.deleteAll(tasksToDelete) |
|
|
|
|
|
gradeAllocationRepository.deleteAll(gradeAllocationsToDelete) |
|
|
|
|
|
|
|
|
milestoneRepository.saveAll(milestones) |
|
|
milestoneRepository.saveAll(milestones) |
|
|
projectTaskRepository.saveAll(tasksToSave) |
|
|
projectTaskRepository.saveAll(tasksToSave) |
|
|
gradeAllocationRepository.saveAll(gradeAllocations) |
|
|
gradeAllocationRepository.saveAll(gradeAllocations) |
|
@@ -195,6 +216,8 @@ open class ProjectsService( |
|
|
this.project = savedProject |
|
|
this.project = savedProject |
|
|
this.staff = staff |
|
|
this.staff = staff |
|
|
} } |
|
|
} } |
|
|
|
|
|
val staffAllocationsToDelete = staffAllocationRepository.findByProject(savedProject).subtract(staffAllocations.toSet()) |
|
|
|
|
|
staffAllocationRepository.deleteAll(staffAllocationsToDelete) |
|
|
staffAllocationRepository.saveAll(staffAllocations) |
|
|
staffAllocationRepository.saveAll(staffAllocations) |
|
|
|
|
|
|
|
|
return savedProject.let { |
|
|
return savedProject.let { |
|
@@ -251,15 +274,15 @@ open class ProjectsService( |
|
|
) }, |
|
|
) }, |
|
|
allocatedStaffIds = staffAllocationRepository.findByProject(it).mapNotNull { allocation -> allocation.staff?.id }, |
|
|
allocatedStaffIds = staffAllocationRepository.findByProject(it).mapNotNull { allocation -> allocation.staff?.id }, |
|
|
milestones = milestoneMap.mapValues { (_, milestone) -> com.ffii.tsms.modules.project.web.models.Milestone( |
|
|
milestones = milestoneMap.mapValues { (_, milestone) -> com.ffii.tsms.modules.project.web.models.Milestone( |
|
|
startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), |
|
|
|
|
|
endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), |
|
|
|
|
|
payments = milestone.milestonePayments.map { payment -> PaymentInputs( |
|
|
|
|
|
id = payment.id!!, |
|
|
|
|
|
amount = payment.amount!!, |
|
|
|
|
|
description = payment.description!!, |
|
|
|
|
|
date = payment.date!!.format(DateTimeFormatter.ISO_LOCAL_DATE) |
|
|
|
|
|
)} |
|
|
|
|
|
)}, |
|
|
|
|
|
|
|
|
startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), |
|
|
|
|
|
endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), |
|
|
|
|
|
payments = milestone.milestonePayments.map { payment -> PaymentInputs( |
|
|
|
|
|
id = payment.id!!, |
|
|
|
|
|
amount = payment.amount!!, |
|
|
|
|
|
description = payment.description!!, |
|
|
|
|
|
date = payment.date!!.format(DateTimeFormatter.ISO_LOCAL_DATE) |
|
|
|
|
|
)} |
|
|
|
|
|
)}, |
|
|
expectedProjectFee = it.expectedTotalFee |
|
|
expectedProjectFee = it.expectedTotalFee |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|