| @@ -1,5 +1,6 @@ | |||||
| package com.ffii.tsms.modules.project.entity | package com.ffii.tsms.modules.project.entity | ||||
| import com.fasterxml.jackson.annotation.JsonManagedReference | |||||
| import com.ffii.core.entity.IdEntity | import com.ffii.core.entity.IdEntity | ||||
| import jakarta.persistence.* | import jakarta.persistence.* | ||||
| import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
| @@ -21,4 +22,12 @@ open class TaskTemplate : IdEntity<Long>() { | |||||
| inverseJoinColumns = [JoinColumn(name = "tasksId")]) | inverseJoinColumns = [JoinColumn(name = "tasksId")]) | ||||
| @OrderBy | @OrderBy | ||||
| open var tasks: MutableList<Task> = mutableListOf() | open var tasks: MutableList<Task> = mutableListOf() | ||||
| @JsonManagedReference | |||||
| @OneToMany(mappedBy = "taskTemplate", cascade = [CascadeType.ALL], orphanRemoval = true) | |||||
| open var gradeAllocations: MutableSet<TaskTemplateGradeAllocation> = mutableSetOf() | |||||
| @JsonManagedReference | |||||
| @OneToMany(mappedBy = "taskTemplate", cascade = [CascadeType.ALL], orphanRemoval = true) | |||||
| open var groupAllocations: MutableSet<TaskTemplateGroupAllocation> = mutableSetOf() | |||||
| } | } | ||||
| @@ -0,0 +1,27 @@ | |||||
| package com.ffii.tsms.modules.project.entity | |||||
| import com.fasterxml.jackson.annotation.JsonBackReference | |||||
| import com.ffii.core.entity.IdEntity | |||||
| import com.ffii.tsms.modules.data.entity.Grade | |||||
| import jakarta.persistence.Column | |||||
| import jakarta.persistence.Entity | |||||
| import jakarta.persistence.JoinColumn | |||||
| import jakarta.persistence.ManyToOne | |||||
| import jakarta.persistence.Table | |||||
| @Entity | |||||
| @Table(name = "task_template_grade_allocation") | |||||
| open class TaskTemplateGradeAllocation: IdEntity<Long>() { | |||||
| @ManyToOne | |||||
| @JsonBackReference | |||||
| @JoinColumn(name = "taskTemplateId") | |||||
| open var taskTemplate: TaskTemplate? = null | |||||
| @ManyToOne | |||||
| // @JsonBackReference | |||||
| @JoinColumn(name = "gradeId") | |||||
| open var grade: Grade? = null | |||||
| @Column(name = "percentage") | |||||
| open var percentage: Double? = null | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package com.ffii.tsms.modules.project.entity | |||||
| import com.ffii.core.support.AbstractRepository | |||||
| import com.ffii.tsms.modules.data.entity.Grade | |||||
| interface TaskTemplateGradeAllocationRepository : AbstractRepository<TaskTemplateGradeAllocation, Long> { | |||||
| fun findByTaskTemplateAndGrade(taskTemplate: TaskTemplate, grade: Grade): TaskTemplateGradeAllocation? | |||||
| fun findAllByTaskTemplate(taskTemplate: TaskTemplate): List<TaskTemplateGradeAllocation> | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| package com.ffii.tsms.modules.project.entity | |||||
| import com.fasterxml.jackson.annotation.JsonBackReference | |||||
| import com.ffii.core.entity.IdEntity | |||||
| import com.ffii.tsms.modules.data.entity.Grade | |||||
| import jakarta.persistence.* | |||||
| @Entity | |||||
| @Table(name = "task_template_group_allocation") | |||||
| open class TaskTemplateGroupAllocation: IdEntity<Long>() { | |||||
| @ManyToOne | |||||
| @JsonBackReference | |||||
| @JoinColumn(name = "taskTemplateId") | |||||
| open var taskTemplate: TaskTemplate? = null | |||||
| @ManyToOne | |||||
| // @JsonBackReference | |||||
| @JoinColumn(name = "taskGroupId") | |||||
| open var taskGroup: TaskGroup? = null | |||||
| @Column(name = "percentage") | |||||
| open var percentage: Double? = null | |||||
| } | |||||
| @@ -0,0 +1,9 @@ | |||||
| package com.ffii.tsms.modules.project.entity | |||||
| import com.ffii.core.support.AbstractRepository | |||||
| interface TaskTemplateGroupAllocationRepository : AbstractRepository<TaskTemplateGroupAllocation, Long> { | |||||
| fun findByTaskTemplateAndTaskGroup(taskTemplate: TaskTemplate, taskGroup: TaskGroup): TaskTemplateGroupAllocation? | |||||
| fun findAllByTaskTemplate(taskTemplate: TaskTemplate): List<TaskTemplateGroupAllocation> | |||||
| } | |||||
| @@ -223,9 +223,9 @@ 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()) | |||||
| val milestonesToDelete = milestoneRepository.findAllByProject(project).subtract(milestones.toSet()) | |||||
| val tasksToDelete = projectTaskRepository.findAllByProject(project).subtract(tasksToSave.toSet()) | |||||
| val gradeAllocationsToDelete = gradeAllocationRepository.findByProject(project).subtract(gradeAllocations.toSet()) | |||||
| milestoneRepository.deleteAll(milestonesToDelete) | milestoneRepository.deleteAll(milestonesToDelete) | ||||
| projectTaskRepository.deleteAll(tasksToDelete) | projectTaskRepository.deleteAll(tasksToDelete) | ||||
| gradeAllocationRepository.deleteAll(gradeAllocationsToDelete) | gradeAllocationRepository.deleteAll(gradeAllocationsToDelete) | ||||
| @@ -1,8 +1,12 @@ | |||||
| package com.ffii.tsms.modules.project.service | package com.ffii.tsms.modules.project.service | ||||
| import com.ffii.core.support.JdbcDao | 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.entity.* | ||||
| import com.ffii.tsms.modules.project.web.models.EditTaskTemplateDetails | 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 org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| @@ -12,6 +16,9 @@ class TasksService( | |||||
| private val taskRepository: TaskRepository, | private val taskRepository: TaskRepository, | ||||
| private val taskGroupRepository: TaskGroupRepository, | private val taskGroupRepository: TaskGroupRepository, | ||||
| private val jdbcDao: JdbcDao, | private val jdbcDao: JdbcDao, | ||||
| private val taskTemplateGroupAllocationRepository: TaskTemplateGroupAllocationRepository, | |||||
| private val gradeRepository: GradeRepository, | |||||
| private val taskTemplateGradeAllocationRepository: TaskTemplateGradeAllocationRepository, | |||||
| ) { | ) { | ||||
| fun allTasks(): List<Task> { | fun allTasks(): List<Task> { | ||||
| return taskRepository.findAll() | return taskRepository.findAll() | ||||
| @@ -33,26 +40,73 @@ class TasksService( | |||||
| val taskTemplate = taskTemplateRepository.findById(id) | val taskTemplate = taskTemplateRepository.findById(id) | ||||
| return taskTemplate.getOrNull()?.let { | return taskTemplate.getOrNull()?.let { | ||||
| val gradeAllocations = it.gradeAllocations | |||||
| .filter { gradeAllocation -> gradeAllocation.grade?.id != null } | |||||
| .associate { gradeAllocation -> Pair(gradeAllocation.grade!!.id!!, gradeAllocation.percentage ?: 0.0) } | |||||
| val groupAllocations = it.groupAllocations | |||||
| .filter { groupAllocation -> groupAllocation.taskGroup?.id != null } | |||||
| .associateBy { groupAllocation -> groupAllocation.taskGroup!!.id!! } | |||||
| EditTaskTemplateDetails( | EditTaskTemplateDetails( | ||||
| id = it.id, | id = it.id, | ||||
| code = it.code, | code = it.code, | ||||
| name = it.name, | name = it.name, | ||||
| taskIds = it.tasks.map { task: Task -> task.id!! } | |||||
| manhourPercentageByGrade = gradeAllocations, | |||||
| taskGroups = it.tasks.groupBy { task: Task -> task.taskGroup!!.id!! } | |||||
| .mapValues { (taskGroupId, tasks) -> TaskGroupAllocation( | |||||
| taskIds = tasks.mapNotNull { task -> task.id }, | |||||
| percentAllocation = if(groupAllocations.containsKey(taskGroupId)) groupAllocations[taskGroupId]?.percentage!! else 0.0 | |||||
| ) } | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| fun saveTaskTemplate(code: String, name: String, taskIds: List<Long>, id: Long?): TaskTemplate { | |||||
| fun saveTaskTemplate(request: NewTaskTemplateRequest): TaskTemplate { | |||||
| val id = request.id | |||||
| val taskTemplate = if (id != null && id > 0) findTaskTemplate(id) else TaskTemplate() | val taskTemplate = if (id != null && id > 0) findTaskTemplate(id) else TaskTemplate() | ||||
| taskTemplate.apply { | |||||
| this.name = request.name | |||||
| this.code = request.code | |||||
| this.tasks = taskRepository.findAllById(request.taskGroups.values.flatMap { taskGroup -> taskGroup.taskIds }) | |||||
| } | |||||
| val gradeAllocations = request.manhourPercentageByGrade.entries.map { | |||||
| val grade = gradeRepository.findById(it.key).orElseThrow() | |||||
| val gradeAllocation = if (taskTemplate.id != null && taskTemplate.id!! > 0) taskTemplateGradeAllocationRepository.findByTaskTemplateAndGrade(taskTemplate, grade) ?:TaskTemplateGradeAllocation() else TaskTemplateGradeAllocation() | |||||
| gradeAllocation.apply { | |||||
| this.taskTemplate = taskTemplate | |||||
| this.grade = grade | |||||
| percentage = it.value | |||||
| } | |||||
| } | |||||
| return taskTemplateRepository.save<TaskTemplate>( | |||||
| taskTemplate.apply { | |||||
| this.name = name | |||||
| this.code = code | |||||
| this.tasks = taskRepository.findAllById(taskIds) | |||||
| val groupAllocations = request.taskGroups.entries.map { | |||||
| val taskGroup = taskGroupRepository.findById(it.key).orElseThrow() | |||||
| val groupAllocation = if (taskTemplate.id != null && taskTemplate.id!! > 0) taskTemplateGroupAllocationRepository.findByTaskTemplateAndTaskGroup(taskTemplate, taskGroup) ?:TaskTemplateGroupAllocation() else TaskTemplateGroupAllocation() | |||||
| groupAllocation.apply { | |||||
| this.taskTemplate = taskTemplate | |||||
| this.taskGroup = taskGroup | |||||
| percentage = it.value.percentAllocation | |||||
| } | } | ||||
| ) | |||||
| } | |||||
| 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 | |||||
| } | } | ||||
| fun allTaskGroups(): List<TaskGroup> { | fun allTaskGroups(): List<TaskGroup> { | ||||
| @@ -41,11 +41,6 @@ class TasksController(private val tasksService: TasksService) { | |||||
| @PostMapping("/templates/save") | @PostMapping("/templates/save") | ||||
| fun saveTaskTemplate(@Valid @RequestBody newTaskTemplate: NewTaskTemplateRequest): TaskTemplate { | fun saveTaskTemplate(@Valid @RequestBody newTaskTemplate: NewTaskTemplateRequest): TaskTemplate { | ||||
| return tasksService.saveTaskTemplate( | |||||
| newTaskTemplate.code, | |||||
| newTaskTemplate.name, | |||||
| newTaskTemplate.taskIds, | |||||
| newTaskTemplate.id | |||||
| ) | |||||
| return tasksService.saveTaskTemplate(newTaskTemplate) | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,5 +5,7 @@ data class EditTaskTemplateDetails ( | |||||
| val code: String?, | val code: String?, | ||||
| val name: String?, | val name: String?, | ||||
| val taskIds: List<Long>? | |||||
| val manhourPercentageByGrade: Map<Long, Double>, | |||||
| val taskGroups: Map<Long, TaskGroupAllocation>?, | |||||
| ) | ) | ||||
| @@ -7,7 +7,9 @@ data class NewTaskTemplateRequest( | |||||
| val code: String, | val code: String, | ||||
| @field:NotBlank(message = "name cannot be empty") | @field:NotBlank(message = "name cannot be empty") | ||||
| val name: String, | val name: String, | ||||
| val taskIds: List<Long> = emptyList(), | |||||
| // val taskIds: List<Long> = emptyList(), | |||||
| val manhourPercentageByGrade: Map<Long, Double>, | |||||
| val taskGroups: Map<Long, TaskGroupAllocation>, | |||||
| val id: Long? | val id: Long? | ||||
| ) | ) | ||||
| @@ -0,0 +1,41 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset cyril:task_template_grade_allocation, task_template_stage_allocation | |||||
| CREATE TABLE `task_template_grade_allocation` ( | |||||
| `id` INT NOT NULL AUTO_INCREMENT, | |||||
| `taskTemplateId` INT NULL DEFAULT NULL, | |||||
| `gradeId` INT NULL DEFAULT NULL, | |||||
| `percentage` DOUBLE NULL DEFAULT NULL, | |||||
| PRIMARY KEY (`id`), | |||||
| INDEX `FK_TASK_TEMPLATE_GRADE_ALLOCATION_ON_GRADEID` (`gradeId` ASC) VISIBLE, | |||||
| INDEX `FK_TASK_TEMPLATE_GRADE_ALLOCATION_ON_TASKTEMPLATEID` (`taskTemplateId` ASC) VISIBLE, | |||||
| CONSTRAINT `FK_TASK_TEMPLATE_GRADE_ALLOCATION_ON_GRADEID` | |||||
| FOREIGN KEY (`gradeId`) | |||||
| REFERENCES `grade` (`id`) | |||||
| ON DELETE NO ACTION | |||||
| ON UPDATE NO ACTION, | |||||
| CONSTRAINT `FK_TASK_TEMPLATE_GRADE_ALLOCATION_ON_TASKTEMPLATEID` | |||||
| FOREIGN KEY (`taskTemplateId`) | |||||
| REFERENCES `task_template` (`id`) | |||||
| ON DELETE NO ACTION | |||||
| ON UPDATE NO ACTION); | |||||
| CREATE TABLE `task_template_group_allocation` ( | |||||
| `id` INT NOT NULL AUTO_INCREMENT, | |||||
| `taskTemplateId` INT NULL, | |||||
| `taskGroupId` INT NULL, | |||||
| `percentage` DOUBLE NULL, | |||||
| PRIMARY KEY (`id`), | |||||
| INDEX `FK_TASK_TEMPLATE_GRADE_ALLOCATION_ON_TASKGROUPID` (`taskGroupId` ASC) VISIBLE, | |||||
| INDEX `FK_TASK_TEMPLATE_GRADE_ALLOCATION_ON_TASKTEMPLATEID` (`taskTemplateId` ASC) VISIBLE, | |||||
| CONSTRAINT `FK_TASK_TEMPLATE_GROUP_ALLOCATION_ON_TASKGROUPID` | |||||
| FOREIGN KEY (`taskGroupId`) | |||||
| REFERENCES `task_group` (`id`) | |||||
| ON DELETE NO ACTION | |||||
| ON UPDATE NO ACTION, | |||||
| CONSTRAINT `FK_TASK_TEMPLATE_GROUP_ALLOCATION_ON_TASKTEMPLATEID` | |||||
| FOREIGN KEY (`taskTemplateId`) | |||||
| REFERENCES `task_template` (`id`) | |||||
| ON DELETE NO ACTION | |||||
| ON UPDATE NO ACTION); | |||||