@@ -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); | |||||