diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java index e01077c35..0e55e96dd 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java @@ -411,9 +411,8 @@ public class GenericResourceAllocation extends return GenericDayAssignment.class; } - public List createAssignmentsAtDay( - List resources, LocalDate day, - ResourcesPerDay resourcesPerDay, + public List createAssignmentsAtDay(List resources, + LocalDate day, ResourcesPerDay resourcesPerDay, final EffortDuration maxLimit) { final EffortDuration durations = min( calculateTotalToDistribute(day, resourcesPerDay), maxLimit); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java index 74e2d693e..378875e69 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java @@ -211,7 +211,7 @@ public abstract class ResourceAllocation extends @Override protected List createAssignmentsAtDay( ResourcesPerDayModification allocation, LocalDate day, - Integer limit) { + EffortDuration limit) { return allocation.createAssignmentsAtDay(day, limit); } @@ -228,7 +228,7 @@ public abstract class ResourceAllocation extends protected boolean thereAreAvailableHoursFrom( LocalDate start, ResourcesPerDayModification resourcesPerDayModification, - int hoursToAllocate) { + EffortDuration effortToAllocate) { IWorkHours workHoursPerDay = getWorkHoursPerDay(resourcesPerDayModification); ResourcesPerDay resourcesPerDay = resourcesPerDayModification .getGoal(); @@ -236,7 +236,7 @@ public abstract class ResourceAllocation extends .getAvailability(); availability.invalidUntil(start); return workHoursPerDay.thereAreHoursOn(availability, - resourcesPerDay, hoursToAllocate); + resourcesPerDay, effortToAllocate.roundToHours()); } private CombinedWorkHours getWorkHoursPerDay( @@ -252,7 +252,7 @@ public abstract class ResourceAllocation extends allocation.markAsUnsatisfied(); } }; - return allocator.untilAllocating(hoursToAllocate); + return allocator.untilAllocating(hours(hoursToAllocate)); } public void allocateOnTaskLength() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java index 8ae00a722..1b4e81449 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java @@ -182,8 +182,8 @@ public class SpecificResourceAllocation extends @Override protected List distributeForDay( LocalDate day, EffortDuration effort) { - return Arrays.asList(SpecificDayAssignment.create(day, - effort.roundToHours(), resource)); + return Arrays.asList(SpecificDayAssignment.create(day, effort, + resource)); } @Override @@ -212,7 +212,7 @@ public class SpecificResourceAllocation extends ResourcesPerDay resourcesPerDay, EffortDuration limit) { EffortDuration effort = calculateTotalToDistribute(day, resourcesPerDay); SpecificDayAssignment specific = SpecificDayAssignment.create(day, - min(limit, effort).roundToHours(), resource); + min(limit, effort), resource); List result = new ArrayList(); result.add(specific); return result; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java index 7bfa56797..d98d6f85c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java @@ -20,10 +20,10 @@ package org.navalplanner.business.planner.entities.allocationalgorithms; +import static org.navalplanner.business.workingday.EffortDuration.zero; + import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -32,9 +32,11 @@ import java.util.Map.Entry; import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; +import org.navalplanner.business.common.ProportionalDistributor; import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.ResourcesPerDay; public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { @@ -58,17 +60,17 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { } } - public LocalDate untilAllocating(int hoursToAllocate) { + public LocalDate untilAllocating(EffortDuration effortToAllocate) { LocalDate taskStart = LocalDate.fromDateFields(task.getStartDate()); LocalDate start = (task.getFirstDayNotConsolidated().compareTo( taskStart) >= 0) ? task.getFirstDayNotConsolidated() : taskStart; int i = 0; int maxDaysElapsed = 0; - for (HoursPerAllocation each : hoursPerAllocation(start, - hoursToAllocate)) { + for (EffortPerAllocation each : effortPerAllocation(start, + effortToAllocate)) { int daysElapsedForCurrent = untilAllocating(start, each.allocation, - each.hours); + each.duration); maxDaysElapsed = Math.max(maxDaysElapsed, daysElapsedForCurrent); i++; } @@ -76,22 +78,21 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { return start.plusDays(maxDaysElapsed); } - private List hoursPerAllocation(LocalDate start, - int toBeAssigned) { + private List effortPerAllocation(LocalDate start, + EffortDuration toBeAssigned) { return new HoursPerAllocationCalculator(allocations) - .calculateHoursPerAllocation(start, toBeAssigned); + .calculateEffortsPerAllocation(start, toBeAssigned); } private int untilAllocating(LocalDate start, ResourcesPerDayModification resourcesPerDayModification, - Integer hoursToAllocate) { - int hoursRemaining = hoursToAllocate; + EffortDuration effortRemaining) { int day = 0; - while (hoursRemaining > 0) { + while (effortRemaining.compareTo(zero()) > 0) { LocalDate current = start.plusDays(day); - int taken = assignForDay(resourcesPerDayModification, current, - hoursRemaining); - hoursRemaining = hoursRemaining - taken; + EffortDuration taken = assignForDay(resourcesPerDayModification, + current, effortRemaining); + effortRemaining = effortRemaining.minus(taken); day++; } return day; @@ -114,44 +115,45 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { List dayAssignments); protected abstract List createAssignmentsAtDay( - ResourcesPerDayModification allocation, LocalDate day, Integer limit); + ResourcesPerDayModification allocation, LocalDate day, + EffortDuration limit); protected abstract boolean thereAreAvailableHoursFrom(LocalDate start, ResourcesPerDayModification resourcesPerDayModification, - int hoursToAllocate); + EffortDuration remainingDuration); protected abstract void markUnsatisfied(ResourceAllocation beingModified); - private int assignForDay( + private EffortDuration assignForDay( ResourcesPerDayModification resourcesPerDayModification, - LocalDate day, int remaining) { + LocalDate day, EffortDuration remaining) { List newAssignments = createAssignmentsAtDay( resourcesPerDayModification, day, remaining); resultAssignments.get(resourcesPerDayModification).addAll( newAssignments); - return DayAssignment.sum(newAssignments).roundToHours(); + return DayAssignment.sum(newAssignments); } - private static class HoursPerAllocation { - final int hours; + private static class EffortPerAllocation { + final EffortDuration duration; final ResourcesPerDayModification allocation; - private HoursPerAllocation(int hours, + private EffortPerAllocation(EffortDuration duration, ResourcesPerDayModification allocation) { - this.hours = hours; + this.duration = duration; this.allocation = allocation; } - public static List wrap( + public static List wrap( List allocations, - List hours) { - Validate.isTrue(hours.size() == allocations.size()); + List durations) { + Validate.isTrue(durations.size() == allocations.size()); int i = 0; - List result = new ArrayList(); + List result = new ArrayList(); for(i = 0; i < allocations.size(); i++){ - result.add(new HoursPerAllocation(hours.get(i), allocations - .get(i))); + result.add(new EffortPerAllocation(durations.get(i), + allocations.get(i))); } return result; } @@ -166,12 +168,12 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { allocations); } - public List calculateHoursPerAllocation( - LocalDate start, int toAssign) { + public List calculateEffortsPerAllocation( + LocalDate start, EffortDuration toAssign) { do { - List hours = calculateHours(toAssign); - List result = HoursPerAllocation.wrap( - allocations, hours); + List durations = divideEffort(toAssign); + List result = EffortPerAllocation.wrap( + allocations, durations); List unsatisfied = getUnsatisfied( start, result); if (unsatisfied.isEmpty()) { @@ -186,77 +188,53 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { } private List getUnsatisfied( - LocalDate start, List hoursPerAllocations) { + LocalDate start, List hoursPerAllocations) { List cannotSatisfy = new ArrayList(); - for (HoursPerAllocation each : hoursPerAllocations) { + for (EffortPerAllocation each : hoursPerAllocations) { if (!thereAreAvailableHoursFrom(start, each.allocation, - each.hours)) { + each.duration)) { cannotSatisfy.add(each.allocation); } } return cannotSatisfy; } - private List calculateHours(int toAssign) { - BigDecimal[] limits = new BigDecimal[allocations.size()]; - BigDecimal sumAll = sumAll(); - for (int i = 0; i < limits.length; i++) { - BigDecimal amount = allocations.get(i).getGoal().getAmount(); - limits[i] = amount.divide(sumAll, RoundingMode.DOWN).multiply( - new BigDecimal(toAssign)); - } - final int remainder = toAssign - sumIntegerParts(limits); - return distributeRemainder(limits, remainder); + private List divideEffort(EffortDuration toBeDivided) { + ProportionalDistributor distributor = ProportionalDistributor + .create(createShares()); + int[] secondsDivided = distributor.distribute(toBeDivided + .getSeconds()); + return asDurations(secondsDivided); } - private List distributeRemainder(BigDecimal[] decimals, - final int remainder) { - for (int i = 0; i < remainder; i++) { - int position = positionOfBiggestDecimalPart(decimals); - decimals[position] = new BigDecimal(decimals[position] - .intValue() + 1); - } - return asIntegers(decimals); - } - - private List asIntegers(BigDecimal[] decimals) { - Integer[] result = new Integer[decimals.length]; + private int[] createShares() { + int[] result = new int[allocations.size()]; for (int i = 0; i < result.length; i++) { - result[i] = decimals[i].intValue(); - } - return Arrays.asList(result); - } - - private int positionOfBiggestDecimalPart(BigDecimal[] decimals) { - int result = 0; - BigDecimal currentBiggestDecimalPart = new BigDecimal(0); - for (int i = 0; i < decimals.length; i++) { - BigDecimal fractionalPart = decimals[i] - .subtract(new BigDecimal(decimals[i].intValue())); - if (currentBiggestDecimalPart.compareTo(fractionalPart) < 0) { - currentBiggestDecimalPart = fractionalPart; - result = i; - } + result[i] = normalize(allocations.get(i).getGoal() + .getAmount()); } return result; } - private int sumIntegerParts(BigDecimal[] decimals) { - int sum = 0; - for (BigDecimal decimal : decimals) { - sum += decimal.intValue(); - } - return sum; - } - - private BigDecimal sumAll() { - BigDecimal result = new BigDecimal(0); - for (ResourcesPerDayModification r : allocations) { - result = result.add(r.getGoal().getAmount()); + private List asDurations(int[] secondsDivided) { + List result = new ArrayList(); + for (int each : secondsDivided) { + result.add(EffortDuration.seconds(each)); } return result; } + /** + * Returns a normalized amount for {@link ProportionalDistributor}. For + * example, for 2.03, 203 is returned. + * + * @param amount + * @return + */ + private int normalize(BigDecimal amount) { + return amount.movePointRight(2).intValue(); + } + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java index a4c1a9cdd..12bcf95aa 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/ResourcesPerDayModification.java @@ -20,8 +20,6 @@ package org.navalplanner.business.planner.entities.allocationalgorithms; -import static org.navalplanner.business.workingday.EffortDuration.hours; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -42,6 +40,7 @@ import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.business.workingday.EffortDuration; import org.navalplanner.business.workingday.ResourcesPerDay; public abstract class ResourcesPerDayModification extends @@ -73,9 +72,9 @@ public abstract class ResourcesPerDayModification extends @Override public List createAssignmentsAtDay(LocalDate day, - int limit) { + EffortDuration limit) { return genericAllocation.createAssignmentsAtDay(getResources(), - day, getGoal(), hours(limit)); + day, getGoal(), limit); } @Override @@ -125,9 +124,9 @@ public abstract class ResourcesPerDayModification extends @Override public List createAssignmentsAtDay(LocalDate day, - int limit) { + EffortDuration limit) { return resourceAllocation.createAssignmentsAtDay(day, getGoal(), - hours(limit)); + limit); } @Override @@ -211,7 +210,7 @@ public abstract class ResourcesPerDayModification extends public abstract void applyAllocationUntil(LocalDate endExclusive); public abstract List createAssignmentsAtDay(LocalDate day, - int limit); + EffortDuration limit); public abstract AvailabilityTimeLine getAvailability();