diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java index 1045931df..5870f6c05 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java @@ -23,6 +23,7 @@ package org.navalplanner.business.calendars.entities; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.EnumMap; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -37,6 +38,8 @@ import org.navalplanner.business.calendars.daos.IBaseCalendarDAO; import org.navalplanner.business.calendars.entities.CalendarData.Days; import org.navalplanner.business.common.IntegrationEntity; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.workingday.EffortDuration; +import org.navalplanner.business.workingday.EffortDuration.Granularity; import org.navalplanner.business.workingday.ResourcesPerDay; /** @@ -886,11 +889,30 @@ public class BaseCalendar extends IntegrationEntity implements IWorkHours { calendarAvailability.setEndDate(endDate); } + public static int roundToHours(EffortDuration effortDuration) { + if (effortDuration.equals(EffortDuration.zero())) { + return 0; + } + return Math.max(1, roundHalfUpToHours(effortDuration.decompose())); + } + + private static int roundHalfUpToHours( + EnumMap components) { + int seconds = components.get(Granularity.SECONDS); + int minutes = components.get(Granularity.MINUTES) + + (seconds < 30 ? 0 : 1); + int hours = components.get(Granularity.HOURS) + (minutes < 30 ? 0 : 1); + return hours; + } + @Override public Integer toHours(LocalDate day, ResourcesPerDay resourcesPerDay) { final Integer workableHours = getWorkableHours(day); - return limitOverAssignability(day, resourcesPerDay - .asHoursGivenResourceWorkingDayOf(workableHours), workableHours); + return limitOverAssignability(day, + roundToHours(resourcesPerDay + .asDurationGivenWorkingDayOf(EffortDuration + .hours(workableHours))), + workableHours); } private Integer limitOverAssignability(LocalDate day, diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/SameWorkHoursEveryDay.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/SameWorkHoursEveryDay.java index ab3e214a6..6fa1e4193 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/SameWorkHoursEveryDay.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/SameWorkHoursEveryDay.java @@ -22,6 +22,7 @@ package org.navalplanner.business.calendars.entities; import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; +import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.ResourcesPerDay; public class SameWorkHoursEveryDay implements IWorkHours { @@ -47,7 +48,9 @@ public class SameWorkHoursEveryDay implements IWorkHours { @Override public Integer toHours(LocalDate day, ResourcesPerDay amount) { - return amount.asHoursGivenResourceWorkingDayOf(getCapacityAt(day)); + return BaseCalendar.roundToHours(amount + .asDurationGivenWorkingDayOf(EffortDuration + .hours(getCapacityAt(day)))); } @Override diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/workingday/ResourcesPerDay.java b/navalplanner-business/src/main/java/org/navalplanner/business/workingday/ResourcesPerDay.java index 3b4484ac3..60a55af61 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/workingday/ResourcesPerDay.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/workingday/ResourcesPerDay.java @@ -25,6 +25,7 @@ import java.math.RoundingMode; import org.apache.commons.lang.Validate; import org.navalplanner.business.common.ProportionalDistributor; +import org.navalplanner.business.workingday.EffortDuration.Granularity; public class ResourcesPerDay { @@ -99,15 +100,16 @@ public class ResourcesPerDay { return amount; } - public int asHoursGivenResourceWorkingDayOf( - Integer resourceWorkingDayHours) { + public EffortDuration asDurationGivenWorkingDayOf( + EffortDuration resourceWorkingDayDuration) { BigDecimal multiply = getAmount().multiply( - new BigDecimal(resourceWorkingDayHours)); - if(multiply.compareTo(BigDecimal.ZERO)>0){ - return Math.max(1, multiply.setScale(0, RoundingMode.HALF_UP) - .intValue()); + new BigDecimal(resourceWorkingDayDuration.getSeconds())); + if (multiply.compareTo(BigDecimal.ZERO) > 0) { + return EffortDuration.elapsing(Math.max(1, + multiply.setScale(0, RoundingMode.HALF_UP).intValue()), + Granularity.SECONDS); } else { - return 0; + return EffortDuration.zero(); } } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java index 70af2fc88..c8edbe04d 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java @@ -60,6 +60,7 @@ import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.VirtualWorker; import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.business.scenarios.entities.Scenario; +import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.ResourcesPerDay; public class GenericResourceAllocationTest { @@ -232,8 +233,9 @@ public class GenericResourceAllocationTest { @Override public Integer answer() throws Throwable { ResourcesPerDay resourcesPerDay = (ResourcesPerDay) getCurrentArguments()[1]; - return resourcesPerDay - .asHoursGivenResourceWorkingDayOf(hoursPerDay); + return BaseCalendar.roundToHours(resourcesPerDay + .asDurationGivenWorkingDayOf(EffortDuration + .hours(hoursPerDay))); } }).anyTimes(); expect(baseCalendar.canWork(isA(LocalDate.class))).andReturn(true) @@ -325,7 +327,8 @@ public class GenericResourceAllocationTest { List orderedAssignmentsFor = genericResourceAllocation .getOrderedAssignmentsFor(worker1); - int hoursPerDay = resourcesPerDay.asHoursGivenResourceWorkingDayOf(8); + int hoursPerDay = resourcesPerDay.asDurationGivenWorkingDayOf( + EffortDuration.hours(8)).getHours(); assertThat(orderedAssignmentsFor, haveHours(hoursPerDay, hoursPerDay)); } @@ -347,7 +350,8 @@ public class GenericResourceAllocationTest { List orderedAssignmentsFor = genericResourceAllocation .getOrderedAssignmentsFor(worker1); - int hoursPerDay = resourcesPerDay.asHoursGivenResourceWorkingDayOf(8); + int hoursPerDay = resourcesPerDay.asDurationGivenWorkingDayOf( + EffortDuration.hours(8)).getHours(); assertThat(orderedAssignmentsFor, haveHours(hoursPerDay, hoursPerDay)); } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java index 05a2cd1d8..9b336d0d2 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java @@ -49,12 +49,13 @@ import org.junit.Test; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine; import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.calendars.entities.ResourceCalendar; +import org.navalplanner.business.planner.entities.ResourceAllocation.DetachDayAssignmentOnRemoval; +import org.navalplanner.business.planner.entities.ResourceAllocation.IOnDayAssignmentRemoval; import org.navalplanner.business.planner.entities.SpecificDayAssignment; import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.planner.entities.Task; -import org.navalplanner.business.planner.entities.ResourceAllocation.DetachDayAssignmentOnRemoval; -import org.navalplanner.business.planner.entities.ResourceAllocation.IOnDayAssignmentRemoval; import org.navalplanner.business.resources.entities.Worker; +import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.ResourcesPerDay; public class SpecificResourceAllocationTest { @@ -96,7 +97,9 @@ public class SpecificResourceAllocationTest { public Integer answer() throws Throwable { ResourcesPerDay perDay = (ResourcesPerDay) EasyMock .getCurrentArguments()[1]; - return perDay.asHoursGivenResourceWorkingDayOf(hours); + return BaseCalendar.roundToHours(perDay + .asDurationGivenWorkingDayOf(EffortDuration + .hours(hours))); } }; } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/workingday/ResourcesPerDayTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/workingday/ResourcesPerDayTest.java index c04dc808d..7fe6a653b 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/workingday/ResourcesPerDayTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/workingday/ResourcesPerDayTest.java @@ -25,6 +25,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.navalplanner.business.workingday.EffortDuration.hours; +import static org.navalplanner.business.workingday.EffortDuration.seconds; +import static org.navalplanner.business.workingday.EffortDuration.zero; import java.math.BigDecimal; @@ -32,6 +35,8 @@ import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.Test; +import org.navalplanner.business.workingday.EffortDuration; +import org.navalplanner.business.workingday.EffortDuration.Granularity; import org.navalplanner.business.workingday.ResourcesPerDay; import org.navalplanner.business.workingday.ResourcesPerDay.ResourcesPerDayDistributor; @@ -107,17 +112,43 @@ public class ResourcesPerDayTest { } @Test - public void canBeConvertedToHoursGivenTheWorkingDayHours() { + public void canBeConvertedToDurationsGivenTheWorkingDayInDifferentGranularities() { ResourcesPerDay units = ResourcesPerDay.amount(2); - assertThat(units.asHoursGivenResourceWorkingDayOf(8), equalTo(16)); + for (Granularity each : Granularity.values()) { + assertThat(units.asDurationGivenWorkingDayOf(EffortDuration + .elapsing(8, each)), equalTo(EffortDuration.elapsing(16, + each))); + } } @Test - public void ifTheAmountIsDecimalTheRoundingIsHalfUp() { - ResourcesPerDay units = ResourcesPerDay.amount(new BigDecimal(2.4)); - assertThat(units.asHoursGivenResourceWorkingDayOf(8), equalTo(19)); - assertThat(units.asHoursGivenResourceWorkingDayOf(10), equalTo(24)); - assertThat(units.asHoursGivenResourceWorkingDayOf(2), equalTo(5)); + public void ifTheAmountIsDecimalTheSecondsAreMultiplied() { + ResourcesPerDay resourcesPerDay = ResourcesPerDay + .amount(new BigDecimal(2.4)); + assertThat(resourcesPerDay.asDurationGivenWorkingDayOf(hours(8)), + equalTo(hours(19).and(12, Granularity.MINUTES))); + assertThat(resourcesPerDay.asDurationGivenWorkingDayOf(hours(10)), + equalTo(hours(24))); + assertThat(resourcesPerDay.asDurationGivenWorkingDayOf(hours(2)), + equalTo(hours(4).and(48, Granularity.MINUTES))); + } + + @Test + public void theSecondsAreRoundedHalfUpUnlessItIsMinusThanOneSecond() { + ResourcesPerDay resourcesPerDay = ResourcesPerDay + .amount(new BigDecimal(2.4)); + assertThat(resourcesPerDay.asDurationGivenWorkingDayOf(seconds(1)), + equalTo(seconds(2))); + assertThat(resourcesPerDay.asDurationGivenWorkingDayOf(seconds(2)), + equalTo(seconds(5))); + } + + @Test + public void asSecondsMustReturnOneIfResultingAmountFromMultiplicationIsGreaterThanZero() { + ResourcesPerDay resourcesPerDay = ResourcesPerDay + .amount(new BigDecimal(0.1)); + assertThat(resourcesPerDay.asDurationGivenWorkingDayOf(seconds(1)), + equalTo(seconds(1))); } @Test @@ -128,19 +159,11 @@ public class ResourcesPerDayTest { assertEquals(a, b); } - @Test - public void asHoursMustReturnOneIfAmountIsGreaterThanZero() { - ResourcesPerDay amount = ResourcesPerDay.amount(new BigDecimal(0.05)); - int hours = amount - .asHoursGivenResourceWorkingDayOf(8); - assertThat(hours, equalTo(1)); - } - @Test public void ifTheAmountIsZeroMustReturnZero() { ResourcesPerDay amount = ResourcesPerDay.amount(BigDecimal.ZERO); - int hours = amount.asHoursGivenResourceWorkingDayOf(8); - assertThat(hours, equalTo(0)); + EffortDuration result = amount.asDurationGivenWorkingDayOf(hours(8)); + assertThat(result, equalTo(zero())); } @Test