From e323ea3e6e6573aa2ff5dd91835c56b68af0cac6 Mon Sep 17 00:00:00 2001 From: BerndBreier <33351391+BerndBreier@users.noreply.github.com> Date: Mon, 12 Mar 2018 13:11:01 +0100 Subject: [PATCH] TSK-322 Calculate due timestamp with respect to custom holidays and weekends --- .../impl/DaysToWorkingDaysConverter.java | 24 ++++++-- .../pro/taskana/impl/TaskServiceImpl.java | 19 ++++++- .../task/UpdateTaskAttachmentsAccTest.java | 17 ++++-- .../impl/DaysToWorkingDaysConverterTest.java | 57 +++++++++++++++++++ 4 files changed, 105 insertions(+), 12 deletions(-) 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 aa6a7604a..d98eed71c 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 @@ -149,6 +149,21 @@ public final class DaysToWorkingDaysConverter { return ageInWorkingDays; } + public long convertWorkingDaysToDays(Instant startTime, long numberOfDays) { + int days = 0; + int workingDays = 0; + while (workingDays < numberOfDays) { + if (isWorkingDay(days, startTime)) { + workingDays++; + } + days++; + while (!isWorkingDay(days, startTime)) { + days++; + } + } + return days; + } + private ArrayList generateNegativeDaysToWorkingDays( List reportLineItemDefinitions, Instant referenceDate) { int minUpperLimit = getSmallestUpperLimit(reportLineItemDefinitions); @@ -205,11 +220,10 @@ public final class DaysToWorkingDaysConverter { } private boolean isWorkingDay(int day, Instant referenceDate) { - if (LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).plusDays(day).getDayOfWeek().equals( - DayOfWeek.SATURDAY) - || LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).plusDays(day).getDayOfWeek().equals( - DayOfWeek.SUNDAY) - || isHoliday(LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).plusDays(day).toLocalDate())) { + LocalDateTime dateTime = LocalDateTime.ofInstant(referenceDate, ZoneId.systemDefault()).plusDays(day); + if (dateTime.getDayOfWeek().equals(DayOfWeek.SATURDAY) + || dateTime.getDayOfWeek().equals(DayOfWeek.SUNDAY) + || isHoliday(dateTime.toLocalDate())) { return false; } return true; 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 ca02fac7a..c7a8a4cb7 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 @@ -58,6 +58,7 @@ public class TaskServiceImpl implements TaskService { private static final String ID_PREFIX_BUSINESS_PROCESS = "BPI"; private static final String MUST_NOT_BE_EMPTY = " must not be empty"; private static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999); + private DaysToWorkingDaysConverter converter; private TaskanaEngineImpl taskanaEngine; private WorkbasketService workbasketService; private ClassificationServiceImpl classificationService; @@ -67,6 +68,13 @@ public class TaskServiceImpl implements TaskService { TaskServiceImpl(TaskanaEngine taskanaEngine, TaskMapper taskMapper, AttachmentMapper attachmentMapper) { super(); + try { + this.converter = DaysToWorkingDaysConverter + .initialize(new ArrayList<>(Arrays.asList(new ReportLineItemDefinition(0))), Instant.now()); + } catch (InvalidArgumentException e) { + LOGGER.error("could not initialize DaysToWorkingDaysConverter. Caught exception " + e); + throw new SystemException("Internal error. Cannot initialize DaysToWorkingDaysConverter"); + } this.taskanaEngine = (TaskanaEngineImpl) taskanaEngine; this.taskMapper = taskMapper; this.workbasketService = taskanaEngine.getWorkbasketService(); @@ -650,7 +658,8 @@ public class TaskServiceImpl implements TaskService { classification.getPriority(), classification.getServiceLevel()); Duration finalDuration = finalPrioDuration.getDuration(); if (finalDuration != null && !MAX_DURATION.equals(finalDuration)) { - Instant due = task.getPlanned().plus(finalDuration); + long days = converter.convertWorkingDaysToDays(task.getPlanned(), finalDuration.toDays()); + Instant due = task.getPlanned().plus(Duration.ofDays(days)); task.setDue(due); } task.setPriority(finalPrioDuration.getPrio()); @@ -1123,7 +1132,9 @@ public class TaskServiceImpl implements TaskService { if (newClassificationSummary == null) { // newClassification is null -> take prio and duration from attachments if (prioDurationFromAttachments.getDuration() != null) { - Instant due = newTaskImpl.getPlanned().plus(prioDurationFromAttachments.getDuration()); + long days = converter.convertWorkingDaysToDays(newTaskImpl.getPlanned(), + prioDurationFromAttachments.getDuration().toDays()); + Instant due = newTaskImpl.getPlanned().plus(Duration.ofDays(days)); newTaskImpl.setDue(due); } if (prioDurationFromAttachments.getPrio() > Integer.MIN_VALUE) { @@ -1150,7 +1161,9 @@ public class TaskServiceImpl implements TaskService { minDuration = durationFromClassification; } - Instant due = newTaskImpl.getPlanned().plus(minDuration); + long days = converter.convertWorkingDaysToDays(newTaskImpl.getPlanned(), minDuration.toDays()); + Instant due = newTaskImpl.getPlanned().plus(Duration.ofDays(days)); + newTaskImpl.setDue(due); } diff --git a/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java index a2b3a565b..6a2f5aafa 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAttachmentsAccTest.java @@ -10,7 +10,9 @@ import static org.junit.Assert.fail; import java.sql.SQLException; import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.junit.Test; @@ -32,6 +34,8 @@ import pro.taskana.exceptions.TaskAlreadyExistException; import pro.taskana.exceptions.TaskNotFoundException; import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.impl.AttachmentImpl; +import pro.taskana.impl.DaysToWorkingDaysConverter; +import pro.taskana.impl.ReportLineItemDefinition; import pro.taskana.impl.TaskImpl; import pro.taskana.security.CurrentUserContext; import pro.taskana.security.JAASRunner; @@ -147,7 +151,7 @@ public class UpdateTaskAttachmentsAccTest extends AbstractAccTest { assertThat(task.getAttachments().size(), equalTo(attachmentCount)); assertThat(task.getAttachments().get(0).getChannel(), equalTo(newChannel)); assertTrue(task.getPriority() == 999); - assertTrue(task.getDue().equals(task.getPlanned().plus(Duration.ofHours(5)))); + assertTrue(task.getDue().equals(task.getPlanned())); } @@ -297,7 +301,7 @@ public class UpdateTaskAttachmentsAccTest extends AbstractAccTest { assertThat(task.getAttachments().size(), equalTo(attachmentCount)); assertThat(task.getAttachments().get(0).getChannel(), equalTo(newChannel)); assertTrue(task.getPriority() == 999); - assertTrue(task.getDue().equals(task.getPlanned().plus(Duration.ofHours(5)))); + assertTrue(task.getDue().equals(task.getPlanned())); } @@ -322,7 +326,7 @@ public class UpdateTaskAttachmentsAccTest extends AbstractAccTest { task = taskService.updateTask(task); task = taskService.getTask(task.getId()); assertTrue(task.getPriority() == 101); - assertTrue(task.getDue().equals(task.getPlanned().plus(Duration.ofHours(7)))); + assertTrue(task.getDue().equals(task.getPlanned())); assertThat(task.getAttachments().size(), equalTo(2)); List attachments = task.getAttachments(); @@ -358,7 +362,12 @@ public class UpdateTaskAttachmentsAccTest extends AbstractAccTest { task = taskService.updateTask(task); task = taskService.getTask(task.getId()); assertTrue(task.getPriority() == 99); - assertTrue(task.getDue().equals(task.getPlanned().plus(Duration.ofDays(16)))); + + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter + .initialize(new ArrayList<>(Arrays.asList(new ReportLineItemDefinition(0))), Instant.now()); + long calendarDays = converter.convertWorkingDaysToDays(task.getDue(), 16); + + assertTrue(task.getDue().equals(task.getPlanned().plus(Duration.ofDays(calendarDays)))); rohrpostFound = false; boolean faxFound = false; 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 6bb9eef24..491e25045 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 @@ -2,6 +2,7 @@ package pro.taskana.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.fail; import java.time.Instant; import java.time.LocalDate; @@ -40,6 +41,62 @@ public class DaysToWorkingDaysConverterTest { assertNotEquals(instance1, instance3); } + @Test + public void testConvertWorkingDaysToDaysForTasks() { + List reportItems = new ArrayList<>(Arrays.asList(new ReportLineItemDefinition(0))); + try { + DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(reportItems, Instant.now()); + + Instant thursday0201 = Instant.parse("2018-02-01T07:00:00.000Z"); + long days = converter.convertWorkingDaysToDays(thursday0201, 0); // = thursday + assertEquals(0, days); + days = converter.convertWorkingDaysToDays(thursday0201, 1); // fri + assertEquals(1, days); + days = converter.convertWorkingDaysToDays(thursday0201, 2); // mon + assertEquals(4, days); + days = converter.convertWorkingDaysToDays(thursday0201, 3); // tues + assertEquals(5, days); + days = converter.convertWorkingDaysToDays(thursday0201, 4); // we + assertEquals(6, days); + days = converter.convertWorkingDaysToDays(thursday0201, 5); // thurs + assertEquals(7, days); + days = converter.convertWorkingDaysToDays(thursday0201, 6); // fri + assertEquals(8, days); + days = converter.convertWorkingDaysToDays(thursday0201, 7); // mon + assertEquals(11, days); + days = converter.convertWorkingDaysToDays(thursday0201, 8); // tue + assertEquals(12, days); + days = converter.convertWorkingDaysToDays(thursday0201, 9); // we + assertEquals(13, days); + days = converter.convertWorkingDaysToDays(thursday0201, 10); // thu + assertEquals(14, days); + days = converter.convertWorkingDaysToDays(thursday0201, 11); // fri + assertEquals(15, days); + + Instant gruenDonnerstag2018 = Instant.parse("2018-03-29T01:00:00.000Z"); + days = converter.convertWorkingDaysToDays(gruenDonnerstag2018, 0); + assertEquals(0, days); + days = converter.convertWorkingDaysToDays(gruenDonnerstag2018, 1); // Karfreitag + assertEquals(5, days); // osterdienstag + days = converter.convertWorkingDaysToDays(gruenDonnerstag2018, 2); // Karfreitag + assertEquals(6, days); // ostermittwoch + + Instant freitag0427 = Instant.parse("2018-04-27T19:00:00.000Z"); + days = converter.convertWorkingDaysToDays(freitag0427, 0); + assertEquals(0, days); + days = converter.convertWorkingDaysToDays(freitag0427, 1); + assertEquals(3, days); // 30.4. + days = converter.convertWorkingDaysToDays(freitag0427, 2); + assertEquals(5, days); // 2.5. + + } catch (InvalidArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + fail(""); + } + + } + @Test public void testInitializeForDifferentDates() throws InvalidArgumentException { DaysToWorkingDaysConverter instance1 = DaysToWorkingDaysConverter