From c24fe5a615a767559ab49e760f9b39af8eb4b0f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Tue, 22 Feb 2011 16:58:16 +0100 Subject: [PATCH] Add getCapacityWithOvertime to ICalendar This method is needed to distribute the load among resources taking into account the overtime. FEA: ItEr71S07FragmentationDeletionItEr70S09 --- .../calendars/entities/BaseCalendar.java | 27 ++++++++--- .../calendars/entities/CombinedWorkHours.java | 23 ++++++++++ .../calendars/entities/ICalendar.java | 12 +++++ .../calendars/entities/ResourceCalendar.java | 14 +----- .../entities/SameWorkHoursEveryDay.java | 10 ++++- .../calendars/entities/BaseCalendarTest.java | 17 +++++++ .../entities/ResourceCalendarTest.java | 45 +++++++++++++++++-- 7 files changed, 126 insertions(+), 22 deletions(-) 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 00539b981..67ee89939 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 @@ -31,6 +31,7 @@ import java.util.Set; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; import org.hibernate.validator.AssertTrue; import org.hibernate.validator.NotEmpty; import org.hibernate.validator.NotNull; @@ -42,6 +43,7 @@ import org.navalplanner.business.calendars.entities.CalendarData.Days; import org.navalplanner.business.common.IntegrationEntity; import org.navalplanner.business.common.entities.EntitySequence; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.resources.entities.VirtualWorker; import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.ResourcesPerDay; import org.navalplanner.business.workingday.IntraDayDate.PartialDay; @@ -265,10 +267,16 @@ public class BaseCalendar extends IntegrationEntity implements ICalendar { } public EffortDuration getCapacityOn(PartialDay date) { - return date.limitDuration(findCapacityAt(date.getDate()) + return date.limitDuration(getCapacityWithOvertime(date.getDate()) .getStandardEffort()); } + @Override + public Capacity getCapacityWithOvertime(LocalDate day) { + Validate.notNull(day); + return multiplyByCalendarUnits(findCapacityAt(day)); + } + private Capacity findCapacityAt(LocalDate date) { if (!isActive(date)) { return Capacity.zero(); @@ -759,14 +767,22 @@ public class BaseCalendar extends IntegrationEntity implements ICalendar { .getStandardEffort()); EffortDuration asDuration = amount .asDurationGivenWorkingDayOf(workableDuration); - return capacity.limitDuration(asDuration); + return multiplyByCalendarUnits(capacity).limitDuration(asDuration); } /** - * This method is intended to be overriden + *

+ * Calendar units are the number of units this calendar is applied to. For + * example a {@link VirtualWorker} composed of ten workers would multiply + * the capacity by ten. + *

+ *

+ * This method is intended to be overridden + *

+ * */ - protected EffortDuration multiplyByCapacity(EffortDuration duration) { - return duration; + protected Capacity multiplyByCalendarUnits(Capacity capacity) { + return capacity; } @Override @@ -996,4 +1012,5 @@ public class BaseCalendar extends IntegrationEntity implements ICalendar { public Integer getLastSequenceCode() { return lastSequenceCode; } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CombinedWorkHours.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CombinedWorkHours.java index 93d28328b..9930abbd2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CombinedWorkHours.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CombinedWorkHours.java @@ -31,6 +31,7 @@ import java.util.Collections; import java.util.List; import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.IntraDayDate.PartialDay; import org.navalplanner.business.workingday.ResourcesPerDay; @@ -92,6 +93,16 @@ public abstract class CombinedWorkHours implements ICalendar { return result; } + @Override + public Capacity getCapacityWithOvertime(LocalDate day) { + Capacity result = null; + for (ICalendar each : calendars) { + Capacity current = each.getCapacityWithOvertime(day); + result = result == null ? current : updateCapacity(result, current); + } + return result; + } + @Override public AvailabilityTimeLine getAvailability() { AvailabilityTimeLine result = AvailabilityTimeLine.allValid(); @@ -110,6 +121,8 @@ public abstract class CombinedWorkHours implements ICalendar { protected abstract EffortDuration updateCapacity(EffortDuration current, EffortDuration each); + protected abstract Capacity updateCapacity(Capacity a, Capacity current); + @Override public boolean thereAreCapacityFor(AvailabilityTimeLine availability, ResourcesPerDay resourcesPerDay, EffortDuration durationToAllocate) { @@ -143,6 +156,11 @@ class Min extends CombinedWorkHours { return accumulated.and(each); } + @Override + protected Capacity updateCapacity(Capacity accumulated, Capacity current) { + return Capacity.min(accumulated, current); + } + } class Max extends CombinedWorkHours { @@ -168,4 +186,9 @@ class Max extends CombinedWorkHours { AvailabilityTimeLine accumulated, AvailabilityTimeLine each) { return accumulated.or(each); } + + @Override + protected Capacity updateCapacity(Capacity accumulated, Capacity current) { + return Capacity.max(accumulated, current); + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ICalendar.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ICalendar.java index 82c0c38ef..32c4b9ee4 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ICalendar.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ICalendar.java @@ -21,6 +21,7 @@ package org.navalplanner.business.calendars.entities; +import org.joda.time.LocalDate; import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.IntraDayDate.PartialDay; import org.navalplanner.business.workingday.ResourcesPerDay; @@ -48,6 +49,17 @@ public interface ICalendar { */ public EffortDuration getCapacityOn(PartialDay partialDay); + /** + * Calculates the capacity information for a given date. It contains + * information about the normal effort and the extra effort, i.e., the + * overtime effort. + * + * @param date + * a not null date + * @return the capacity for the date provided + */ + public Capacity getCapacityWithOvertime(LocalDate date); + public AvailabilityTimeLine getAvailability(); public boolean thereAreCapacityFor(AvailabilityTimeLine availability, diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java index aac138826..fad66c601 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java @@ -84,18 +84,8 @@ public class ResourceCalendar extends BaseCalendar { } @Override - public EffortDuration getCapacityOn(PartialDay date) { - return multiplyByCapacity(super.getCapacityOn(date)); - } - - protected EffortDuration multiplyByCapacity(EffortDuration duration) { - if (duration == null) { - return EffortDuration.zero(); - } - if (capacity == null) { - return duration; - } - return duration.multiplyBy(capacity); + protected Capacity multiplyByCalendarUnits(Capacity capacity) { + return capacity.multiplyBy(getCapacity()); } @AssertTrue(message = "Capacity must be a positive integer number") 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 681860d2e..c0ba251ac 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.IntraDayDate.PartialDay; import org.navalplanner.business.workingday.ResourcesPerDay; @@ -44,7 +45,8 @@ public class SameWorkHoursEveryDay implements ICalendar { @Override public EffortDuration getCapacityOn(PartialDay partialDay) { - return partialDay.limitDuration(EffortDuration.hours(hours)); + return partialDay.limitDuration(getCapacityWithOvertime( + partialDay.getDate()).getStandardEffort()); } @Override @@ -63,4 +65,10 @@ public class SameWorkHoursEveryDay implements ICalendar { return AvailabilityTimeLine.allValid(); } + @Override + public Capacity getCapacityWithOvertime(LocalDate day) { + return Capacity.create(EffortDuration.hours(hours)) + .overAssignableWithoutLimit(true); + } + } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java index 6085e1e53..dfb390aca 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java @@ -801,6 +801,23 @@ public class BaseCalendarTest { ResourcesPerDay.amount(2)), equalTo(hours(16))); } + @Test(expected = IllegalArgumentException.class) + public void getCapacityWithOvertimeMustNotBeCalledWithANullDate() { + BaseCalendar calendar = createBasicCalendar(); + calendar.getCapacityWithOvertime(null); + } + + @Test + public void getCapacityWithOvertimeOnReturnsTheCapacityForThatDay() { + BaseCalendar calendar = createBasicCalendar(); + Capacity capacitySet = Capacity.create(hours(8)) + .overAssignableWithoutLimit(true); + + calendar.setCapacityAt(Days.MONDAY, capacitySet); + assertThat(calendar.getCapacityWithOvertime(MONDAY_LOCAL_DATE), + equalTo(capacitySet)); + } + @Test public void asDurationOnRespectsTheNotOverAssignablePropertyOfCalendarData() { BaseCalendar calendar = createBasicCalendar(); diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/ResourceCalendarTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/ResourceCalendarTest.java index d8950f18f..b2cec2909 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/ResourceCalendarTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/ResourceCalendarTest.java @@ -37,6 +37,8 @@ import org.navalplanner.business.calendars.entities.CalendarData.Days; import org.navalplanner.business.calendars.entities.Capacity; import org.navalplanner.business.calendars.entities.ResourceCalendar; import org.navalplanner.business.workingday.EffortDuration; +import org.navalplanner.business.workingday.IntraDayDate.PartialDay; +import org.navalplanner.business.workingday.ResourcesPerDay; /** * Tests for {@link ResourceCalendar}. @@ -45,17 +47,23 @@ import org.navalplanner.business.workingday.EffortDuration; */ public class ResourceCalendarTest { - public static ResourceCalendar createBasicResourceCalendar() { + private static final Capacity capacityForEveryDay = Capacity.create( + EffortDuration.hours(8)).overAssignableWithoutLimit(true); + + public static ResourceCalendar createBasicResourceCalendar(int capacity) { ResourceCalendar calendar = ResourceCalendar.create(); calendar.setName("Test"); for (Days each : Days.values()) { - calendar.setCapacityAt(each, - Capacity.create(EffortDuration.hours(8)) - .overAssignableWithoutLimit(true)); + calendar.setCapacityAt(each, capacityForEveryDay); } + calendar.setCapacity(capacity); return calendar; } + public static ResourceCalendar createBasicResourceCalendar() { + return createBasicResourceCalendar(1); + } + private static final LocalDate PAST = (new LocalDate()).minusMonths(1); private static final LocalDate FUTURE = (new LocalDate()).plusMonths(1); @@ -77,6 +85,35 @@ public class ResourceCalendarTest { equalTo(EffortDuration.hours(8))); } + @Test + public void getCapacityWithOverTimeIsMultipliedByTheCapacityOfTheResourceCalendar() { + ResourceCalendar calendar = createBasicResourceCalendar(2); + Capacity capacity = calendar.getCapacityWithOvertime(FUTURE); + assertThat(capacity, equalTo(capacityForEveryDay.multiplyBy(2))); + } + + @Test + public void theCapacityEffortIsMultipliedByTheCapacityOfTheResourceCalendar() { + ResourceCalendar calendar = createBasicResourceCalendar(2); + EffortDuration duration = calendar.getCapacityOn(PartialDay + .wholeDay(FUTURE)); + assertThat(duration, equalTo(capacityForEveryDay.getStandardEffort() + .multiplyBy(2))); + } + + @Test + public void asDurationOnDoesntChangeWithTheCapacityOfTheResourceCalendar() { + ResourceCalendar[] calendars = { createBasicResourceCalendar(), + createBasicResourceCalendar(2), createBasicResourceCalendar(3) }; + for (ResourceCalendar each : calendars) { + EffortDuration duration = each.asDurationOn( + PartialDay.wholeDay(FUTURE), + ResourcesPerDay.amount(1)); + assertThat(duration, + equalTo(capacityForEveryDay.getStandardEffort())); + } + } + @Test(expected = IllegalArgumentException.class) public void notAllowCreateCalendarAvailabilityInThePast() { ResourceCalendar calendar = createBasicResourceCalendar();