|
|
@@ -6,6 +6,7 @@ import com.ffii.tsms.modules.data.service.CustomerContactService |
|
|
|
import com.ffii.tsms.modules.project.entity.projections.ProjectSearchInfo |
|
|
|
import com.ffii.tsms.modules.data.service.CustomerService |
|
|
|
import com.ffii.tsms.modules.data.service.GradeService |
|
|
|
import com.ffii.tsms.modules.data.service.SubsidiaryContactService |
|
|
|
import com.ffii.tsms.modules.project.entity.* |
|
|
|
import com.ffii.tsms.modules.project.entity.Milestone |
|
|
|
import com.ffii.tsms.modules.project.entity.projections.InvoiceInfoSearchInfo |
|
|
@@ -44,10 +45,12 @@ open class ProjectsService( |
|
|
|
private val taskGroupRepository: TaskGroupRepository, |
|
|
|
private val timesheetRepository: TimesheetRepository, |
|
|
|
private val taskTemplateRepository: TaskTemplateRepository, |
|
|
|
private val subsidiaryRepository: SubsidiaryRepository, private val subsidiaryContactRepository: SubsidiaryContactRepository |
|
|
|
private val subsidiaryContactService: SubsidiaryContactService, |
|
|
|
private val subsidiaryContactRepository: SubsidiaryContactRepository |
|
|
|
) { |
|
|
|
open fun allProjects(): List<ProjectSearchInfo> { |
|
|
|
return projectRepository.findProjectSearchInfoByOrderByCreatedDesc().sortedByDescending { it.status?.lowercase() != "deleted" } |
|
|
|
return projectRepository.findProjectSearchInfoByOrderByCreatedDesc() |
|
|
|
.sortedByDescending { it.status?.lowercase() != "deleted" } |
|
|
|
} |
|
|
|
|
|
|
|
open fun allInvoices(): List<InvoiceSearchInfo> { |
|
|
@@ -63,63 +66,57 @@ open class ProjectsService( |
|
|
|
} |
|
|
|
|
|
|
|
open fun markDeleted(id: Long) { |
|
|
|
projectRepository.save(projectRepository.findById(id).orElseThrow() |
|
|
|
.apply { |
|
|
|
deleted = true |
|
|
|
status = "Deleted" |
|
|
|
}) |
|
|
|
projectRepository.save(projectRepository.findById(id).orElseThrow().apply { |
|
|
|
deleted = true |
|
|
|
status = "Deleted" |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
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.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 |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} ?: emptyList() |
|
|
|
} |
|
|
|
|
|
|
|
open fun allProjectWithTasks(): List<ProjectWithTasks> { |
|
|
|
return projectRepository.findAll().map { project -> |
|
|
|
ProjectWithTasks( |
|
|
|
id = project.id!!, |
|
|
|
ProjectWithTasks(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) -> |
|
|
|
.associateBy { milestone -> milestone.taskGroup!!.id!! }.mapValues { (_, milestone) -> |
|
|
|
MilestoneInfo( |
|
|
|
startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), |
|
|
|
endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) |
|
|
|
) |
|
|
|
} |
|
|
|
) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@@ -135,15 +132,28 @@ open class ProjectsService( |
|
|
|
|
|
|
|
if (latestProjectCode != null) { |
|
|
|
val lastFix = latestProjectCode |
|
|
|
return "$prefix-" + String.format("%04d", lastFix + 1L) |
|
|
|
return "$prefix-" + String.format("%04d", lastFix + 1L) |
|
|
|
} else { |
|
|
|
return "$prefix-0001" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
open fun createSubProjectCode(mainProject: Project, project: Project): String { |
|
|
|
val prefix = mainProject.code |
|
|
|
|
|
|
|
val latestProjectCode = projectRepository.getLatestCodeNumberBySubProject(prefix!!, project.id ?: -1) |
|
|
|
|
|
|
|
if (latestProjectCode != null) { |
|
|
|
val lastFix = latestProjectCode |
|
|
|
return "$prefix-" + String.format("%03d", lastFix + 1L) |
|
|
|
} else { |
|
|
|
return "$prefix-001" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Transactional |
|
|
|
open fun saveProject(request: NewProjectRequest): NewProjectResponse { |
|
|
|
val projectCategory = |
|
|
|
projectCategoryRepository.findById(request.projectCategoryId).orElseThrow() |
|
|
|
val projectCategory = projectCategoryRepository.findById(request.projectCategoryId).orElseThrow() |
|
|
|
val fundingType = fundingTypeRepository.findById(request.fundingTypeId).orElseThrow() |
|
|
|
val serviceType = serviceTypeRepository.findById(request.serviceTypeId).orElseThrow() |
|
|
|
val contractType = contractTypeRepository.findById(request.contractTypeId).orElseThrow() |
|
|
@@ -158,6 +168,7 @@ open class ProjectsService( |
|
|
|
val subsidiaryContact = subsidiaryContactRepository.findById(request.clientContactId).orElse(null) |
|
|
|
val clientContact = customerContactService.findByContactId(request.clientContactId) |
|
|
|
val customerSubsidiary = request.clientSubsidiaryId?.let { subsidiaryService.findSubsidiary(it) } |
|
|
|
val mainProject = if (request.mainProjectId != null && request.mainProjectId > 0) projectRepository.findById(request.mainProjectId).orElse(null) else null |
|
|
|
|
|
|
|
val allTasksMap = tasksService.allTasks().associateBy { it.id } |
|
|
|
val taskGroupMap = tasksService.allTaskGroups().associateBy { it.id } |
|
|
@@ -169,7 +180,7 @@ open class ProjectsService( |
|
|
|
project.apply { |
|
|
|
name = request.projectName |
|
|
|
description = request.projectDescription |
|
|
|
code = if (this.code.isNullOrEmpty()) createProjectCode(request.isClpProject, project) else this.code |
|
|
|
code = if (this.code.isNullOrEmpty() && request.mainProjectId == null) createProjectCode(request.isClpProject, project) else if (this.code.isNullOrEmpty() && request.mainProjectId != null && mainProject != null) createSubProjectCode(mainProject, project) else this.code |
|
|
|
expectedTotalFee = request.expectedProjectFee |
|
|
|
totalManhour = request.totalManhour |
|
|
|
actualStart = request.projectActualStart |
|
|
@@ -179,6 +190,7 @@ open class ProjectsService( |
|
|
|
else if (this.actualStart != null) "On-going" |
|
|
|
else "Pending To Start" |
|
|
|
isClpProject = request.isClpProject |
|
|
|
this.mainProject = mainProject |
|
|
|
|
|
|
|
this.projectCategory = projectCategory |
|
|
|
this.fundingType = fundingType |
|
|
@@ -191,9 +203,12 @@ open class ProjectsService( |
|
|
|
|
|
|
|
this.teamLead = teamLead |
|
|
|
this.customer = customer |
|
|
|
custLeadName = if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.name else subsidiaryContact.name |
|
|
|
custLeadEmail = if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.email else subsidiaryContact.email |
|
|
|
custLeadPhone = if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.phone else subsidiaryContact.phone |
|
|
|
custLeadName = |
|
|
|
if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.name else subsidiaryContact.name |
|
|
|
custLeadEmail = |
|
|
|
if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.email else subsidiaryContact.email |
|
|
|
custLeadPhone = |
|
|
|
if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.phone else subsidiaryContact.phone |
|
|
|
this.customerSubsidiary = customerSubsidiary |
|
|
|
} |
|
|
|
|
|
|
@@ -203,8 +218,7 @@ open class ProjectsService( |
|
|
|
val milestones = request.taskGroups.entries.map { (taskStageId, taskGroupAllocation) -> |
|
|
|
val taskGroup = taskGroupRepository.findById(taskStageId).orElse(TaskGroup()) ?: TaskGroup() |
|
|
|
val milestone = if (project.id != null && project.id!! > 0L) milestoneRepository.findByProjectAndTaskGroup( |
|
|
|
project, |
|
|
|
taskGroup |
|
|
|
project, taskGroup |
|
|
|
) ?: Milestone() else Milestone() |
|
|
|
milestone.apply { |
|
|
|
val newMilestone = this |
|
|
@@ -233,8 +247,7 @@ open class ProjectsService( |
|
|
|
taskGroupAllocation.taskIds.map { taskId -> |
|
|
|
val projectTask = |
|
|
|
if (project.id != null && project.id!! > 0L) projectTaskRepository.findByProjectAndTask( |
|
|
|
project, |
|
|
|
allTasksMap[taskId]!! |
|
|
|
project, allTasksMap[taskId]!! |
|
|
|
) ?: ProjectTask() else ProjectTask() |
|
|
|
|
|
|
|
projectTask.apply { |
|
|
@@ -252,8 +265,7 @@ open class ProjectsService( |
|
|
|
val gradeAllocations = request.manhourPercentageByGrade.entries.map { |
|
|
|
val gradeAllocation = |
|
|
|
if (project.id != null && project.id!! > 0L) gradeAllocationRepository.findByProjectAndGrade( |
|
|
|
project, |
|
|
|
gradeMap[it.key]!! |
|
|
|
project, gradeMap[it.key]!! |
|
|
|
) ?: GradeAllocation() else GradeAllocation() |
|
|
|
|
|
|
|
gradeAllocation.apply { |
|
|
@@ -313,16 +325,16 @@ open class ProjectsService( |
|
|
|
val project = projectRepository.findById(projectId) |
|
|
|
|
|
|
|
return project.getOrNull()?.let { |
|
|
|
val customerContact = |
|
|
|
it.customer?.id?.let { customerId -> customerContactService.findAllByCustomerId(customerId) } |
|
|
|
?: emptyList() |
|
|
|
val subsidiaryContact = it.customerSubsidiary?.id?.let { subsidiaryId -> |
|
|
|
subsidiaryContactService.findAllBySubsidiaryId(subsidiaryId) |
|
|
|
} ?: emptyList() |
|
|
|
val customerContact = it.customer?.id?.let { customerId -> customerContactService.findAllByCustomerId(customerId) } |
|
|
|
?: emptyList() |
|
|
|
|
|
|
|
val milestoneMap = it.milestones |
|
|
|
.filter { milestone -> milestone.taskGroup?.id != null } |
|
|
|
val milestoneMap = it.milestones.filter { milestone -> milestone.taskGroup?.id != null } |
|
|
|
.associateBy { milestone -> milestone.taskGroup!!.id!! } |
|
|
|
|
|
|
|
EditProjectDetails( |
|
|
|
projectId = it.id, |
|
|
|
EditProjectDetails(projectId = it.id, |
|
|
|
projectDeleted = it.deleted, |
|
|
|
projectCode = it.code, |
|
|
|
projectName = it.name, |
|
|
@@ -341,7 +353,7 @@ open class ProjectsService( |
|
|
|
buildingTypeIds = it.buildingTypes.mapNotNull { buildingType -> buildingType.id }, |
|
|
|
workNatureIds = it.workNatures.mapNotNull { workNature -> workNature.id }, |
|
|
|
clientId = it.customer?.id, |
|
|
|
clientContactId = customerContact.find { contact -> contact.name == it.custLeadName }?.id, |
|
|
|
clientContactId = subsidiaryContact.find { contact -> contact.name == it.custLeadName }?.id ?: customerContact.find { contact -> contact.name == it.custLeadName }?.id, |
|
|
|
clientSubsidiaryId = it.customerSubsidiary?.id, |
|
|
|
totalManhour = it.totalManhour, |
|
|
|
manhourPercentageByGrade = gradeAllocationRepository.findByProject(it) |
|
|
@@ -349,8 +361,7 @@ open class ProjectsService( |
|
|
|
.associate { allocation -> Pair(allocation.grade!!.id!!, allocation.manhour ?: 0.0) }, |
|
|
|
taskGroups = projectTaskRepository.findAllByProject(it) |
|
|
|
.mapNotNull { projectTask -> if (projectTask.task?.taskGroup?.id != null) projectTask.task else null } |
|
|
|
.groupBy { task -> task.taskGroup!!.id!! } |
|
|
|
.mapValues { (taskGroupId, tasks) -> |
|
|
|
.groupBy { task -> task.taskGroup!!.id!! }.mapValues { (taskGroupId, tasks) -> |
|
|
|
TaskGroupAllocation( |
|
|
|
taskIds = tasks.mapNotNull { task -> task.id }, |
|
|
|
percentAllocation = milestoneMap[taskGroupId]?.stagePercentAllocation ?: 0.0 |
|
|
@@ -359,8 +370,9 @@ open class ProjectsService( |
|
|
|
allocatedStaffIds = staffAllocationRepository.findByProject(it) |
|
|
|
.mapNotNull { allocation -> allocation.staff?.id }, |
|
|
|
milestones = milestoneMap.mapValues { (_, milestone) -> |
|
|
|
com.ffii.tsms.modules.project.web.models.Milestone( |
|
|
|
startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), |
|
|
|
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( |
|
|
@@ -369,8 +381,7 @@ open class ProjectsService( |
|
|
|
description = payment.description!!, |
|
|
|
date = payment.date!!.format(DateTimeFormatter.ISO_LOCAL_DATE) |
|
|
|
) |
|
|
|
} |
|
|
|
) |
|
|
|
}) |
|
|
|
}, |
|
|
|
expectedProjectFee = it.expectedTotalFee |
|
|
|
) |
|
|
@@ -400,4 +411,37 @@ open class ProjectsService( |
|
|
|
open fun allWorkNatures(): List<WorkNature> { |
|
|
|
return workNatureRepository.findAll() |
|
|
|
} |
|
|
|
|
|
|
|
open fun allMainProjects(): List<MainProjectDetails> { |
|
|
|
val mainProjects: List<Project> = projectRepository.findAllByStatusIsNotAndMainProjectIsNull("Deleted") |
|
|
|
|
|
|
|
return mainProjects.map { project: Project -> |
|
|
|
val subsidiaryContact = project.customerSubsidiary?.id?.let { subsidiaryId -> |
|
|
|
subsidiaryContactService.findAllBySubsidiaryId(subsidiaryId) |
|
|
|
} ?: emptyList() |
|
|
|
val customerContact = project.customer?.id?.let { customerId -> customerContactService.findAllByCustomerId(customerId) } |
|
|
|
?: emptyList() |
|
|
|
|
|
|
|
MainProjectDetails( |
|
|
|
projectId = project.id, |
|
|
|
projectCode = project.code, |
|
|
|
projectName = project.name, |
|
|
|
projectCategoryId = project.projectCategory?.id, |
|
|
|
projectDescription = project.description, |
|
|
|
projectLeadId = project.teamLead?.id, |
|
|
|
projectStatus = project.status, |
|
|
|
isClpProject = project.isClpProject, |
|
|
|
serviceTypeId = project.serviceType?.id, |
|
|
|
fundingTypeId = project.fundingType?.id, |
|
|
|
contractTypeId = project.contractType?.id, |
|
|
|
locationId = project.location?.id, |
|
|
|
buildingTypeIds = project.buildingTypes.mapNotNull { buildingType -> buildingType.id }, |
|
|
|
workNatureIds = project.workNatures.mapNotNull { workNature -> workNature.id }, |
|
|
|
clientId = project.customer?.id, |
|
|
|
clientContactId = subsidiaryContact.find { contact -> contact.name == project.custLeadName }?.id ?: customerContact.find { contact -> contact.name == project.custLeadName }?.id, |
|
|
|
clientSubsidiaryId = project.customerSubsidiary?.id, |
|
|
|
expectedProjectFee = project.expectedTotalFee |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |