| @@ -1,5 +1,6 @@ | |||
| package com.ffii.tsms.modules.project.entity | |||
| import com.fasterxml.jackson.annotation.JsonManagedReference | |||
| import com.ffii.core.entity.IdEntity | |||
| import jakarta.persistence.* | |||
| import jakarta.validation.constraints.NotNull | |||
| @@ -21,4 +22,12 @@ open class TaskTemplate : IdEntity<Long>() { | |||
| inverseJoinColumns = [JoinColumn(name = "tasksId")]) | |||
| @OrderBy | |||
| 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 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) | |||
| projectTaskRepository.deleteAll(tasksToDelete) | |||
| gradeAllocationRepository.deleteAll(gradeAllocationsToDelete) | |||
| @@ -1,8 +1,12 @@ | |||
| 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 org.springframework.stereotype.Service | |||
| import kotlin.jvm.optionals.getOrNull | |||
| @@ -12,6 +16,9 @@ class TasksService( | |||
| private val taskRepository: TaskRepository, | |||
| private val taskGroupRepository: TaskGroupRepository, | |||
| private val jdbcDao: JdbcDao, | |||
| private val taskTemplateGroupAllocationRepository: TaskTemplateGroupAllocationRepository, | |||
| private val gradeRepository: GradeRepository, | |||
| private val taskTemplateGradeAllocationRepository: TaskTemplateGradeAllocationRepository, | |||
| ) { | |||
| fun allTasks(): List<Task> { | |||
| return taskRepository.findAll() | |||
| @@ -33,26 +40,73 @@ class TasksService( | |||
| val taskTemplate = taskTemplateRepository.findById(id) | |||
| 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( | |||
| id = it.id, | |||
| code = it.code, | |||
| 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() | |||
| 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> { | |||
| @@ -41,11 +41,6 @@ class TasksController(private val tasksService: TasksService) { | |||
| @PostMapping("/templates/save") | |||
| 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 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, | |||
| @field:NotBlank(message = "name cannot be empty") | |||
| 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? | |||
| ) | |||
| @@ -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); | |||