diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java index 364189757..ab3f1d8e8 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java @@ -682,15 +682,45 @@ public class Task extends TaskElement implements ITaskLeafConstraint { public Integer getWorkableDays() { if (workableDays == null) { - return getDaysBetweenDates(); + return getWorkableDaysBetweenDates(); } return workableDays; } + public Integer getDaysBetweenDates() { Days daysBetween = Days.daysBetween(getStartAsLocalDate(), getIntraDayEndDate().asExclusiveEnd()); return daysBetween.getDays(); } + private Integer getWorkableDaysBetweenDates() { + int result = 0; + LocalDate start = getStartAsLocalDate(); + LocalDate end = getIntraDayEndDate().asExclusiveEnd(); + for (LocalDate current = start; current.compareTo(end) < 0; current = current + .plusDays(1)) { + if (isWorkable(current)) { + result++; + } + } + return result; + } + + public LocalDate calculateEndGivenWorkableDays(int workableDays) { + LocalDate result = getIntraDayStartDate().getDate(); + for (int i = 0; i < workableDays; result = result.plusDays(1)) { + if (isWorkable(result)) { + i++; + } + } + return result; + } + + private boolean isWorkable(LocalDate day) { + ICalendar calendar = getCalendar(); + assert calendar != null; + return !calendar.getCapacityOn(PartialDay.wholeDay(day)).isZero(); + } + } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java index 11ca64882..e9408933e 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java @@ -22,21 +22,27 @@ package org.navalplanner.business.test.planner.entities; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.getCurrentArguments; +import static org.easymock.EasyMock.isA; import static org.easymock.classextension.EasyMock.createNiceMock; import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.resetToNice; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; +import static org.navalplanner.business.workingday.EffortDuration.hours; import java.util.Arrays; import java.util.Date; +import org.easymock.IAnswer; import org.joda.time.LocalDate; import org.junit.Test; import org.junit.runner.RunWith; +import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.orders.entities.HoursGroup; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.orders.entities.OrderLine; @@ -49,6 +55,7 @@ import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueue import org.navalplanner.business.scenarios.entities.OrderVersion; import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.IntraDayDate; +import org.navalplanner.business.workingday.IntraDayDate.PartialDay; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @@ -74,6 +81,8 @@ public class TaskTest { private HoursGroup hoursGroup; + private BaseCalendar calendar; + public TaskTest() { hoursGroup = new HoursGroup(); hoursGroup.setWorkingHours(3); @@ -82,6 +91,7 @@ public class TaskTest { order.setInitDate(new Date()); OrderLine orderLine = OrderLine.create(); order.add(orderLine); + order.setCalendar(stubCalendar()); SchedulingDataForVersion version = TaskElementTest .mockSchedulingDataForVersion(orderLine); TaskSource taskSource = TaskSource.create(version, Arrays @@ -89,6 +99,14 @@ public class TaskTest { task = Task.createTask(taskSource); } + private BaseCalendar stubCalendar() { + calendar = createNiceMock(BaseCalendar.class); + expect(calendar.getCapacityOn(isA(PartialDay.class))) + .andReturn(hours(8)).anyTimes(); + replay(calendar); + return calendar; + } + @Test public void taskIsASubclassOfTaskElement() { assertTrue(task instanceof TaskElement); @@ -173,7 +191,7 @@ public class TaskTest { } @Test - public void theDaysBetweenIsCalculatedBasedOnlyOnDatesNotHours() { + public void theWorkableDaysAreCalculatedBasedOnlyOnDatesNotHours() { task.setIntraDayStartDate(IntraDayDate.create( new LocalDate(2010, 1, 13), EffortDuration.hours(3))); task.setIntraDayEndDate(IntraDayDate.startOfDay(new LocalDate(2010, 1, @@ -201,6 +219,30 @@ public class TaskTest { assertThat(task.getWorkableDays(), equalTo(2)); } + @Test + public void ifSomeDayIsNotWorkableIsNotCounted() { + final LocalDate start = new LocalDate(2010, 1, 13); + + resetToNice(calendar); + expect(calendar.getCapacityOn(isA(PartialDay.class))).andAnswer( + new IAnswer() { + @Override + public EffortDuration answer() throws Throwable { + Object[] args = getCurrentArguments(); + PartialDay day = (PartialDay) args[0]; + return day.getDate().equals(start.plusDays(1)) ? hours(0) + : hours(8); + } + }).anyTimes(); + replay(calendar); + + task.setIntraDayStartDate(IntraDayDate.create(start, + EffortDuration.hours(3))); + task.setIntraDayEndDate(IntraDayDate.create(start.plusDays(1), + EffortDuration.minutes(1))); + assertThat(task.getWorkableDays(), equalTo(1)); + } + /** * @param task * @param hours diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java index 3be25739c..19902cbed 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java @@ -308,11 +308,12 @@ public class FormBinder { @Override public void onEvent(Event event) throws Exception { - Date newEndDate = calculateEndDate(taskWorkableDays - .getValue()); + Task task = allocationRowsHandler.getTask(); + LocalDate newEndDate = task + .calculateEndGivenWorkableDays(taskWorkableDays + .getValue()); taskPropertiesController.updateTaskEndDate(newEndDate); - showValueOfDateOn(labelTaskEnd, - LocalDate.fromDateFields(newEndDate)); + showValueOfDateOn(labelTaskEnd, newEndDate); } }); taskDurationDisabilityRule(); @@ -325,18 +326,9 @@ public class FormBinder { label.setValue(formatter.print(date)); } - private Date calculateEndDate(int duration) { - LocalDate result = new LocalDate(getPlannedTaskStart()); - result = result.plusDays(duration); - return toDate(result); - } - - private Date toDate(LocalDate date) { - return date.toDateTimeAtStartOfDay().toDate(); - } - - public Date getPlannedTaskStart() { - return resourceAllocationModel.getTaskStart(); + public LocalDate getAllocationEnd() { + Task task = allocationRowsHandler.getTask(); + return task.calculateEndGivenWorkableDays(taskWorkableDays.getValue()); } public void setAllResourcesPerDay(Decimalbox allResourcesPerDay) { @@ -481,11 +473,6 @@ public class FormBinder { return result; } - public LocalDate getAllocationEnd() { - LocalDate result = new LocalDate(taskStartDate); - return result.plusDays(getWorkableDays().intValue()); - } - public Integer getWorkableDays() { return taskWorkableDays.getValue(); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java index b1d898315..04d10a215 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java @@ -73,8 +73,6 @@ public interface IResourceAllocationModel extends INewAllocationsAdder { ProportionalDistributor addDefaultAllocations(); - Date getTaskStart(); - void setStartDate(Date date); Date getTaskEnd(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java index 1af356105..5a89744dd 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java @@ -388,14 +388,6 @@ public class ResourceAllocationModel implements IResourceAllocationModel { return AggregatedHoursGroup.sum(task.getAggregatedByCriterions()); } - @Override - public Date getTaskStart() { - if (task == null) { - return null; - } - return task.getStartDate(); - } - @Override public Date getTaskEnd() { if (task == null) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java index bb9477a32..2f169a909 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java @@ -663,8 +663,8 @@ public class TaskPropertiesController extends GenericForwardComposer { return false; } - public void updateTaskEndDate(Date endDate) { - getGanttTaskDTO().endDate = endDate; + public void updateTaskEndDate(LocalDate endDate) { + getGanttTaskDTO().endDate = endDate.toDateTimeAtStartOfDay().toDate(); Util.reloadBindings(endDateBox); } }