From 4e095f4580f5457376ff6e67e428d4e63a1f88b2 Mon Sep 17 00:00:00 2001 From: Benjamin Eckstein <13351939+benjamineckstein@users.noreply.github.com> Date: Thu, 14 Nov 2019 13:01:21 +0100 Subject: [PATCH] TSK-926: Intertwine planned and due Date for task creation --- .../src/main/java/pro/taskana/Task.java | 11 ++- .../impl/DaysToWorkingDaysConverter.java | 11 ++- .../main/java/pro/taskana/impl/TaskImpl.java | 1 + .../pro/taskana/impl/TaskServiceImpl.java | 64 ++++++++------- .../acceptance/task/CreateTaskAccTest.java | 81 +++++++++++++++++++ .../impl/DaysToWorkingDaysConverterTest.java | 16 +++- 6 files changed, 149 insertions(+), 35 deletions(-) diff --git a/lib/taskana-core/src/main/java/pro/taskana/Task.java b/lib/taskana-core/src/main/java/pro/taskana/Task.java index 504abf393..ba5f93daa 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/Task.java +++ b/lib/taskana-core/src/main/java/pro/taskana/Task.java @@ -25,6 +25,7 @@ public interface Task { * @return external Id */ String getExternalId(); + /** * Sets the external Id. It can be used to correlate the task to a task in an external system. * The external Id is enforced to be unique. An attempt to create a task with @@ -36,7 +37,7 @@ public interface Task { */ void setExternalId(String externalId); - /** + /** * Gets the UserId of the task-creator. * * @return creator @@ -93,6 +94,14 @@ public interface Task { */ Instant getDue(); + /** + * Sets the time when the work on this task should be finished. + * + * @param due + * as exact {@link Instant} + */ + void setDue(Instant due); + /** * Return the name of the current task. * diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/DaysToWorkingDaysConverter.java b/lib/taskana-core/src/main/java/pro/taskana/impl/DaysToWorkingDaysConverter.java index a329c8edb..b72930bed 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/DaysToWorkingDaysConverter.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/DaysToWorkingDaysConverter.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.LongStream; import java.util.stream.Stream; @@ -96,6 +97,10 @@ public final class DaysToWorkingDaysConverter { return instance; } + public static Optional getLastCreatedInstance() { + return Optional.ofNullable(instance); + } + public static void setGermanPublicHolidaysEnabled(boolean germanPublicHolidaysEnabled) { germanHolidaysEnabled = germanPublicHolidaysEnabled; } @@ -179,8 +184,10 @@ public final class DaysToWorkingDaysConverter { public long convertWorkingDaysToDays(Instant startTime, long numberOfDays) { int days = 0; int workingDays = 0; - while (workingDays < numberOfDays) { - workingDays += isWorkingDay(++days, startTime) ? 1 : 0; + int direction = numberOfDays > 0 ? 1 : -1; + while (workingDays < numberOfDays * direction) { + days += direction; + workingDays += isWorkingDay(days, startTime) ? 1 : 0; } return days; } diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskImpl.java index 482cb3e22..d31cc8379 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskImpl.java @@ -146,6 +146,7 @@ public class TaskImpl implements Task { return due; } + @Override public void setDue(Instant due) { this.due = due; } diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java index 931b7bc35..0b423cd25 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java @@ -637,18 +637,46 @@ public class TaskServiceImpl implements TaskService { } task.setCreator(creator); - if (task.getPlanned() == null) { - task.setPlanned(now); - } - // if no business process id is provided, a unique id is created. if (task.getBusinessProcessId() == null) { task.setBusinessProcessId(IdGenerator.generateWithPrefix(ID_PREFIX_BUSINESS_PROCESS)); } // insert Classification specifications if Classification is given. + if (classification == null) { + if (task.getPlanned() == null) { + task.setPlanned(now); + } + } else { + // get duration in days from planned to due + PrioDurationHolder finalPrioDuration = getNewPrioDuration(prioDurationFromAttachments, + classification.getPriority(), classification.getServiceLevel()); + Duration finalDuration = finalPrioDuration.getDuration(); + if (finalDuration != null && !MAX_DURATION.equals(finalDuration)) { + // if we have a due date we need to go x days backwards, + // else we take the planned date (or now as fallback) and add x Days + if (task.getDue() != null) { + long days = converter.convertWorkingDaysToDays(task.getDue(), -finalDuration.toDays()); + //days < 0 -> so we ne need to add, not substract + Instant planned = task.getDue().plus(Duration.ofDays(days)); + task.setPlanned(planned); + } else { + task.setPlanned(task.getPlanned() == null ? now : task.getPlanned()); + long days = converter.convertWorkingDaysToDays(task.getPlanned(), finalDuration.toDays()); + Instant due = task.getPlanned().plus(Duration.ofDays(days)); + task.setDue(due); + } + } + task.setPriority(finalPrioDuration.getPrio()); + } - processStandardSettingsForConfiguration(task, classification, prioDurationFromAttachments); + if (task.getName() == null && classification != null) { + task.setName(classification.getName()); + } + + if (task.getDescription() == null && classification != null) { + task.setDescription(classification.getDescription()); + } // insert Attachments if needed List attachments = task.getAttachments(); @@ -665,32 +693,6 @@ public class TaskServiceImpl implements TaskService { LOGGER.debug("exit from standardSettings()"); } - private void processStandardSettingsForConfiguration(TaskImpl task, Classification classification, - PrioDurationHolder prioDurationFromAttachments) { - LOGGER.debug("entry to processStandardSettingsForConfiguration()"); - if (classification != null) { - PrioDurationHolder finalPrioDuration = getNewPrioDuration(prioDurationFromAttachments, - classification.getPriority(), classification.getServiceLevel()); - Duration finalDuration = finalPrioDuration.getDuration(); - if (finalDuration != null && !MAX_DURATION.equals(finalDuration)) { - long days = converter.convertWorkingDaysToDays(task.getPlanned(), finalDuration.toDays()); - Instant due = task.getPlanned().plus(Duration.ofDays(days)); - task.setDue(due); - } - task.setPriority(finalPrioDuration.getPrio()); - - } - - if (task.getName() == null && classification != null) { - task.setName(classification.getName()); - } - - if (task.getDescription() == null && classification != null) { - task.setDescription(classification.getDescription()); - } - LOGGER.debug("exit from processStandardSettingsForConfiguration()"); - } - private void setCallbackStateOnTaskCreation(TaskImpl task) throws InvalidArgumentException { Map callbackInfo = task.getCallbackInfo(); if (callbackInfo != null && callbackInfo.containsKey(Task.CALLBACK_STATE)) { diff --git a/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java index fb2e00b18..ae42d15c3 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java @@ -14,6 +14,7 @@ import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import org.apache.ibatis.session.Configuration; @@ -24,6 +25,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import acceptance.AbstractAccTest; import pro.taskana.Attachment; +import pro.taskana.Classification; +import pro.taskana.ClassificationService; import pro.taskana.ObjectReference; import pro.taskana.Task; import pro.taskana.TaskService; @@ -332,6 +335,84 @@ class CreateTaskAccTest extends AbstractAccTest { assertNotNull(readTask.getAttachments().get(0).getObjectReference()); } + @WithAccessId( + userName = "user_1_1", + groupNames = {"group_1"}) + @Test + void testCalculationOfDueDateAtCreate() + throws NotAuthorizedException, InvalidArgumentException, ClassificationNotFoundException, + WorkbasketNotFoundException, TaskAlreadyExistException, TaskNotFoundException { + + TaskService taskService = taskanaEngine.getTaskService(); + ClassificationService classificationService = taskanaEngine.getClassificationService(); + + //SL P16D + Classification classification = classificationService.getClassification("L110105", "DOMAIN_A"); + long serviceLevelDays = Duration.parse(classification.getServiceLevel()).toDays(); + assertTrue(serviceLevelDays > 5); + + Task newTask = taskService.newTask("USER_1_1", classification.getDomain()); + newTask.setClassificationKey(classification.getKey()); + newTask.setPrimaryObjRef(createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567")); + + Instant planned = Instant.now().plus(10, ChronoUnit.DAYS); + newTask.setPlanned(planned); + Task createdTask = taskService.createTask(newTask); + assertNotNull(createdTask.getId()); + + Task readTask = taskService.getTask(createdTask.getId()); + assertNotNull(readTask); + assertEquals(planned, readTask.getPlanned()); + + Optional shouldBeDueDate = DaysToWorkingDaysConverter.getLastCreatedInstance() + .map(converter -> converter.convertWorkingDaysToDays(readTask.getPlanned(), serviceLevelDays)) + .map( + calendarDays -> readTask.getPlanned().plus(Duration.ofDays(calendarDays))); + assertTrue(shouldBeDueDate.isPresent()); + assertEquals(readTask.getDue(), shouldBeDueDate.get()); + } + + @WithAccessId( + userName = "user_1_1", + groupNames = {"group_1"}) + @Test + void testCalculationOfPlannedDateAtCreate() + throws NotAuthorizedException, InvalidArgumentException, ClassificationNotFoundException, + WorkbasketNotFoundException, TaskAlreadyExistException, TaskNotFoundException { + + TaskService taskService = taskanaEngine.getTaskService(); + ClassificationService classificationService = taskanaEngine.getClassificationService(); + + //SL P16D + Classification classification = classificationService.getClassification("L110105", "DOMAIN_A"); + long serviceLevelDays = Duration.parse(classification.getServiceLevel()).toDays(); + assertTrue(serviceLevelDays > 5); + + Task newTask = taskService.newTask("USER_1_1", classification.getDomain()); + newTask.setClassificationKey(classification.getKey()); + newTask.setPrimaryObjRef(createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567")); + + Instant due = Instant.now().plus(40, ChronoUnit.DAYS); + newTask.setDue(due); + Task createdTask = taskService.createTask(newTask); + assertNotNull(createdTask.getId()); + + Task readTask = taskService.getTask(createdTask.getId()); + assertNotNull(readTask); + assertEquals(due, readTask.getDue()); + + + Optional calendarDaysToSubstract = DaysToWorkingDaysConverter.getLastCreatedInstance() + .map(converter -> converter.convertWorkingDaysToDays(readTask.getPlanned(), -serviceLevelDays)); + + assertTrue(calendarDaysToSubstract.isPresent()); + assertTrue(calendarDaysToSubstract.get() < 0); + assertTrue(calendarDaysToSubstract.get() <= -serviceLevelDays); + + Instant shouldBePlannedDate = due.plus(Duration.ofDays(calendarDaysToSubstract.get())); + assertEquals(readTask.getPlanned(), shouldBePlannedDate); + } + @WithAccessId( userName = "user_1_1", groupNames = {"group_1"}) diff --git a/lib/taskana-core/src/test/java/pro/taskana/impl/DaysToWorkingDaysConverterTest.java b/lib/taskana-core/src/test/java/pro/taskana/impl/DaysToWorkingDaysConverterTest.java index 28821050a..31f9b1e9a 100644 --- a/lib/taskana-core/src/test/java/pro/taskana/impl/DaysToWorkingDaysConverterTest.java +++ b/lib/taskana-core/src/test/java/pro/taskana/impl/DaysToWorkingDaysConverterTest.java @@ -49,7 +49,21 @@ class DaysToWorkingDaysConverterTest { Instant thursday0201 = Instant.parse("2018-02-01T07:00:00.000Z"); DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(reportItems, thursday0201); - long days = converter.convertWorkingDaysToDays(thursday0201, 0); // = thursday + long days = converter.convertWorkingDaysToDays(thursday0201, -7); // = tuesday (sat + sun) + assertEquals(-9, days); + days = converter.convertWorkingDaysToDays(thursday0201, -6); // = wednesday (sat + sun) + assertEquals(-8, days); + days = converter.convertWorkingDaysToDays(thursday0201, -5); // = thursday (sat + sun) + assertEquals(-7, days); + days = converter.convertWorkingDaysToDays(thursday0201, -4); // = friday + assertEquals(-6, days); + days = converter.convertWorkingDaysToDays(thursday0201, -3); // monday + assertEquals(-3, days); + days = converter.convertWorkingDaysToDays(thursday0201, -2); // tuesday + assertEquals(-2, days); + days = converter.convertWorkingDaysToDays(thursday0201, -1); // wednesday + assertEquals(-1, days); + days = converter.convertWorkingDaysToDays(thursday0201, 0); // = thursday assertEquals(0, days); days = converter.convertWorkingDaysToDays(thursday0201, 1); // fri assertEquals(1, days);