Add property endDateWithinADay in order to calculate correctly resources per day

FEA: ItEr60S19TimeUnitDataType
This commit is contained in:
Óscar González Fernández 2010-09-16 19:42:14 +02:00
parent c0b64a0bef
commit b4e18f2ed8
8 changed files with 227 additions and 25 deletions

View file

@ -31,6 +31,7 @@ import org.navalplanner.business.common.BaseEntity;
import org.navalplanner.business.scenarios.entities.Scenario;
import org.navalplanner.business.util.deepcopy.OnCopy;
import org.navalplanner.business.util.deepcopy.Strategy;
import org.navalplanner.business.workingday.TaskDate;
/**
* Object containing the {@link GenericDayAssignment generic day assignments}
@ -53,6 +54,11 @@ public class GenericDayAssignmentsContainer extends BaseEntity {
private Set<GenericDayAssignment> dayAssignments = new HashSet<GenericDayAssignment>();
/**
* It can be <code>null</code>
*/
private TaskDate endDateWithinADay;
private GenericDayAssignmentsContainer(GenericResourceAllocation resourceAllocation,
Scenario scenario) {
Validate.notNull(resourceAllocation);
@ -100,4 +106,12 @@ public class GenericDayAssignmentsContainer extends BaseEntity {
return GenericDayAssignment.copy(this, assignments);
}
public TaskDate getEndDateWithinADay() {
return endDateWithinADay;
}
public void setEndDateWithinADay(TaskDate endDateWithinADay) {
this.endDateWithinADay = endDateWithinADay;
}
}

View file

@ -50,6 +50,7 @@ import org.navalplanner.business.util.deepcopy.OnCopy;
import org.navalplanner.business.util.deepcopy.Strategy;
import org.navalplanner.business.workingday.EffortDuration;
import org.navalplanner.business.workingday.ResourcesPerDay;
import org.navalplanner.business.workingday.TaskDate;
/**
* Represents the relation between {@link Task} and a generic {@link Resource}.
@ -315,6 +316,16 @@ public class GenericResourceAllocation extends
return new ExplicitlySpecifiedScenarioState(
scenario);
}
@Override
TaskDate getEndDateWithinADay() {
return container.getEndDateWithinADay();
}
@Override
public void setEndDateWithinADay(TaskDate endDateWithinADay) {
container.setEndDateWithinADay(endDateWithinADay);
}
}
private class TransientState extends DayAssignmentsState {
@ -322,6 +333,8 @@ public class GenericResourceAllocation extends
private final Set<GenericDayAssignment> genericDayAssignments;
private TaskDate endDateWithinADay;
TransientState(Set<GenericDayAssignment> genericDayAssignments) {
this.genericDayAssignments = genericDayAssignments;
}
@ -366,6 +379,16 @@ public class GenericResourceAllocation extends
result.resetTo(genericDayAssignments);
return result;
}
@Override
TaskDate getEndDateWithinADay() {
return endDateWithinADay;
}
@Override
public void setEndDateWithinADay(TaskDate endDateWithinADay) {
this.endDateWithinADay = endDateWithinADay;
}
}
private Set<GenericDayAssignment> getUnorderedForScenario(
@ -378,6 +401,15 @@ public class GenericResourceAllocation extends
return container.getDayAssignments();
}
private TaskDate getEndDataWithinADayFor(Scenario scenario) {
GenericDayAssignmentsContainer container = containersByScenario().get(
scenario);
if (container == null) {
return null;
}
return container.getEndDateWithinADay();
}
private class GenericDayAssignmentsNoExplicitlySpecifiedScenario extends
NoExplicitlySpecifiedScenario {
@ -391,6 +423,11 @@ public class GenericResourceAllocation extends
protected DayAssignmentsState switchTo(Scenario scenario) {
return new ExplicitlySpecifiedScenarioState(scenario);
}
@Override
protected TaskDate getEndDateWithinADay(Scenario scenario) {
return getEndDataWithinADayFor(scenario);
}
}
@OnCopy(Strategy.IGNORE)

View file

@ -21,6 +21,7 @@
package org.navalplanner.business.planner.entities;
import static org.navalplanner.business.workingday.EffortDuration.hours;
import static org.navalplanner.business.workingday.EffortDuration.min;
import static org.navalplanner.business.workingday.EffortDuration.seconds;
import static org.navalplanner.business.workingday.EffortDuration.zero;
@ -64,6 +65,7 @@ import org.navalplanner.business.util.deepcopy.OnCopy;
import org.navalplanner.business.util.deepcopy.Strategy;
import org.navalplanner.business.workingday.EffortDuration;
import org.navalplanner.business.workingday.ResourcesPerDay;
import org.navalplanner.business.workingday.TaskDate;
/**
* Resources are allocated to planner tasks.
@ -230,10 +232,10 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
@Override
protected void setNewDataForAllocation(
ResourceAllocation<?> allocation,
ResourceAllocation<?> allocation, TaskDate end,
ResourcesPerDay resourcesPerDay,
List<DayAssignment> dayAssignments) {
allocation.resetGenericAssignmentsTo(dayAssignments);
allocation.resetGenericAssignmentsTo(dayAssignments, end);
allocation.updateResourcesPerDay();
}
@ -264,7 +266,8 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
allocation.markAsUnsatisfied();
}
};
return allocator.untilAllocating(hours(hoursToAllocate));
TaskDate result = allocator.untilAllocating(hours(hoursToAllocate));
return result.getDate().plusDays(1);
}
public void allocateOnTaskLength() {
@ -705,18 +708,28 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
protected abstract void copyAssignments(Scenario from, Scenario to);
protected void resetAssignmentsTo(List<T> assignments) {
resetAssignmentsTo(assignments, null);
}
protected void resetAssignmentsTo(List<T> assignments,
TaskDate endDateWithinADay) {
removingAssignments(withoutConsolidated(getAssignments()));
addingAssignments(assignments);
updateOriginalTotalAssigment();
getDayAssignmentsState().setEndDateWithinADay(endDateWithinADay);
}
protected void resetAssigmentsForInterval(LocalDate startInclusive,
LocalDate endExclusive, List<T> assignmentsCreated) {
boolean finishedByEnd = isAlreadyFinishedBy(endExclusive);
removingAssignments(withoutConsolidated(getAssignments(startInclusive,
endExclusive)));
addingAssignments(assignmentsCreated);
updateOriginalTotalAssigment();
updateResourcesPerDay();
if (finishedByEnd) {
getDayAssignmentsState().setEndDateWithinADay(null);
}
}
private static <T extends DayAssignment> List<T> withoutConsolidated(
@ -765,8 +778,16 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
EffortDuration sumWorkableEffort = zero();
final ResourcesPerDay ONE_RESOURCE_PER_DAY = ResourcesPerDay.amount(1);
for (Entry<LocalDate, List<T>> entry : byDay.entrySet()) {
sumWorkableEffort = sumWorkableEffort.plus(getAllocationCalendar()
.asDurationOn(entry.getKey(), ONE_RESOURCE_PER_DAY));
LocalDate day = entry.getKey();
EffortDuration incrementWorkable = getAllocationCalendar()
.asDurationOn(entry.getKey(), ONE_RESOURCE_PER_DAY);
TaskDate endDateWithinADay = getDayAssignmentsState().getEndDateWithinADay();
if (endDateWithinADay != null
&& day.equals(endDateWithinADay.getDate())) {
incrementWorkable = min(incrementWorkable,
endDateWithinADay.getEffortDuration());
}
sumWorkableEffort = sumWorkableEffort.plus(incrementWorkable);
sumTotalEffort = sumTotalEffort.plus(getAssignedDuration(entry
.getValue()));
}
@ -791,8 +812,9 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
protected abstract ICalendar getCalendarGivenTaskCalendar(
ICalendar taskCalendar);
private void resetGenericAssignmentsTo(List<DayAssignment> assignments) {
resetAssignmentsTo(cast(assignments));
private void resetGenericAssignmentsTo(List<DayAssignment> assignments,
TaskDate end) {
resetAssignmentsTo(cast(assignments), end);
}
private List<T> cast(List<DayAssignment> value) {
@ -848,6 +870,21 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
return dayAssignmentsOrdered;
}
/**
* It can be null. It allows to mark that the allocation is finished in
* a point within a day instead of taking the whole day
*/
abstract TaskDate getEndDateWithinADay();
/**
* Set a new endDateWithinADay.
*
* @param endDateWithinADay
* it can be <code>null</code>
* @see getEndDateWithinADay
*/
public abstract void setEndDateWithinADay(TaskDate endDateWithinADay);
protected abstract Collection<T> getUnorderedAssignments();
protected void addingAssignments(Collection<? extends T> assignments) {
@ -915,6 +952,11 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
protected abstract class NoExplicitlySpecifiedScenario extends
DayAssignmentsState {
@Override
public void setEndDateWithinADay(TaskDate endDateWithinADay) {
modificationsNotAllowed();
}
@Override
protected final void removeAssignments(
List<? extends DayAssignment> assignments) {
@ -953,11 +995,20 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
@Override
protected Collection<T> getUnorderedAssignments() {
Scenario currentScenario = Registry
.getScenarioManager().getCurrent();
Scenario currentScenario = currentScenario();
return getUnorderedAssignmentsForScenario(currentScenario);
}
private Scenario currentScenario() {
return Registry.getScenarioManager().getCurrent();
}
TaskDate getEndDateWithinADay() {
return getEndDateWithinADay(currentScenario);
}
protected abstract TaskDate getEndDateWithinADay(Scenario scenario);
protected abstract Collection<T> getUnorderedAssignmentsForScenario(
Scenario scenario);
}
@ -1155,6 +1206,8 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
final void mergeAssignments(ResourceAllocation<?> modifications) {
getDayAssignmentsState().mergeAssignments(modifications);
getDayAssignmentsState().setEndDateWithinADay(
modifications.getDayAssignmentsState().getEndDateWithinADay());
}
public void detach() {

View file

@ -31,6 +31,7 @@ import org.navalplanner.business.common.BaseEntity;
import org.navalplanner.business.scenarios.entities.Scenario;
import org.navalplanner.business.util.deepcopy.OnCopy;
import org.navalplanner.business.util.deepcopy.Strategy;
import org.navalplanner.business.workingday.TaskDate;
/**
* Object containing the {@link SpecificDayAssignment specific day assignments}
@ -53,6 +54,11 @@ public class SpecificDayAssignmentsContainer extends BaseEntity {
private Set<SpecificDayAssignment> dayAssignments = new HashSet<SpecificDayAssignment>();
/**
* It can be <code>null</code>
*/
private TaskDate endDateWithinADay;
@Valid
public Set<SpecificDayAssignment> getDayAssignments() {
return new HashSet<SpecificDayAssignment>(dayAssignments);
@ -100,4 +106,12 @@ public class SpecificDayAssignmentsContainer extends BaseEntity {
return SpecificDayAssignment.copy(this, assignments);
}
public TaskDate getEndDateWithinADay() {
return endDateWithinADay;
}
public void setEndDateWithinADay(TaskDate endDateWithinADay) {
this.endDateWithinADay = endDateWithinADay;
}
}

View file

@ -51,6 +51,7 @@ import org.navalplanner.business.util.deepcopy.OnCopy;
import org.navalplanner.business.util.deepcopy.Strategy;
import org.navalplanner.business.workingday.EffortDuration;
import org.navalplanner.business.workingday.ResourcesPerDay;
import org.navalplanner.business.workingday.TaskDate;
/**
* Represents the relation between {@link Task} and a specific {@link Worker}.
@ -299,6 +300,16 @@ public class SpecificResourceAllocation extends
container.resetTo(assignmentsCopied);
}
@Override
TaskDate getEndDateWithinADay() {
return container.getEndDateWithinADay();
}
@Override
public void setEndDateWithinADay(TaskDate endDateWithinADay) {
container.setEndDateWithinADay(endDateWithinADay);
}
@Override
protected void setParentFor(SpecificDayAssignment each) {
each.setSpecificResourceAllocation(outerSpecificAllocation);
@ -316,6 +327,8 @@ public class SpecificResourceAllocation extends
private final Set<SpecificDayAssignment> specificDaysAssignment;
private TaskDate endDateWithinADay;
TransientState(Set<SpecificDayAssignment> specificDayAssignments) {
this.specificDaysAssignment = specificDayAssignments;
}
@ -360,6 +373,16 @@ public class SpecificResourceAllocation extends
result.resetTo(specificDaysAssignment);
return result;
}
@Override
TaskDate getEndDateWithinADay() {
return endDateWithinADay;
}
@Override
public void setEndDateWithinADay(TaskDate endDateWithinADay) {
this.endDateWithinADay = endDateWithinADay;
}
}
private Set<SpecificDayAssignment> getUnorderedFor(Scenario scenario) {
@ -371,6 +394,15 @@ public class SpecificResourceAllocation extends
return container.getDayAssignments();
}
private TaskDate getEndDataWithinADayFor(Scenario scenario) {
SpecificDayAssignmentsContainer container = containersByScenario().get(
scenario);
if (container == null) {
return null;
}
return container.getEndDateWithinADay();
}
private class SpecificDayAssignmentsNoExplicitlySpecifiedScenario extends
NoExplicitlySpecifiedScenario {
@ -385,6 +417,11 @@ public class SpecificResourceAllocation extends
return new ExplicitlySpecifiedScenarioState(scenario);
}
@Override
protected TaskDate getEndDateWithinADay(Scenario scenario) {
return getEndDataWithinADayFor(scenario);
}
}
@OnCopy(Strategy.IGNORE)

View file

@ -38,6 +38,7 @@ 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;
import org.navalplanner.business.workingday.TaskDate;
public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
@ -60,22 +61,22 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
}
}
public LocalDate untilAllocating(EffortDuration effortToAllocate) {
public TaskDate 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;
TaskDate currentEnd = TaskDate.create(start, zero());
for (EffortPerAllocation each : effortPerAllocation(start,
effortToAllocate)) {
int daysElapsedForCurrent = untilAllocating(start, each.allocation,
TaskDate endCandidate = untilAllocating(start, each.allocation,
each.duration);
maxDaysElapsed = Math.max(maxDaysElapsed, daysElapsedForCurrent);
currentEnd = TaskDate.max(currentEnd, endCandidate);
i++;
}
setAssignmentsForEachAllocation();
return start.plusDays(maxDaysElapsed);
setAssignmentsForEachAllocation(currentEnd);
return currentEnd;
}
private List<EffortPerAllocation> effortPerAllocation(LocalDate start,
@ -84,21 +85,29 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
.calculateEffortsPerAllocation(start, toBeAssigned);
}
private int untilAllocating(LocalDate start,
/**
*
* @param start
* @param resourcesPerDayModification
* @param effortRemaining
* @return the moment on which the allocation would be completed
*/
private TaskDate untilAllocating(LocalDate start,
ResourcesPerDayModification resourcesPerDayModification,
EffortDuration effortRemaining) {
int day = 0;
while (effortRemaining.compareTo(zero()) > 0) {
LocalDate current = start.plusDays(day);
EffortDuration taken = assignForDay(resourcesPerDayModification,
EffortDuration taken = zero();
LocalDate lastDate = start;
for (LocalDate current = start; effortRemaining.compareTo(zero()) > 0; current = current
.plusDays(1)) {
lastDate = current;
taken = assignForDay(resourcesPerDayModification,
current, effortRemaining);
effortRemaining = effortRemaining.minus(taken);
day++;
}
return day;
return TaskDate.create(lastDate, taken);
}
private void setAssignmentsForEachAllocation() {
private void setAssignmentsForEachAllocation(TaskDate end) {
for (Entry<ResourcesPerDayModification, List<DayAssignment>> entry : resultAssignments
.entrySet()) {
ResourceAllocation<?> allocation = entry.getKey()
@ -106,12 +115,14 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
ResourcesPerDay resourcesPerDay = entry.getKey()
.getGoal();
List<DayAssignment> value = entry.getValue();
setNewDataForAllocation(allocation, resourcesPerDay, value);
setNewDataForAllocation(allocation, end, resourcesPerDay,
value);
}
}
protected abstract void setNewDataForAllocation(
ResourceAllocation<?> allocation, ResourcesPerDay resourcesPerDay,
ResourceAllocation<?> allocation, TaskDate explicitEnd,
ResourcesPerDay resourcesPerDay,
List<DayAssignment> dayAssignments);
protected abstract List<DayAssignment> createAssignmentsAtDay(

View file

@ -81,6 +81,14 @@
-->
<many-to-one name="resourceAllocation" column="RESOURCE_ALLOCATION_ID"/>
<many-to-one name="scenario" />
<component name="endDateWithinADay" class="org.navalplanner.business.workingday.TaskDate">
<property name="date" column="endDate"
type="org.joda.time.contrib.hibernate.PersistentLocalDate" />
<property name="effortDuration" column="durationInLastDay"
type="org.navalplanner.business.workingday.hibernate.EffortDurationType"/>
</component>
<set name="dayAssignments" cascade="all-delete-orphan">
<key column="SPECIFIC_CONTAINER_ID" />
<one-to-many class="SpecificDayAssignment"/>
@ -101,6 +109,14 @@
-->
<many-to-one name="resourceAllocation" column="RESOURCE_ALLOCATION_ID"/>
<many-to-one name="scenario" />
<component name="endDateWithinADay" class="org.navalplanner.business.workingday.TaskDate">
<property name="date" column="endDate"
type="org.joda.time.contrib.hibernate.PersistentLocalDate" />
<property name="effortDuration" column="durationInLastDay"
type="org.navalplanner.business.workingday.hibernate.EffortDurationType"/>
</component>
<set name="dayAssignments" cascade="all-delete-orphan">
<key column="GENERIC_CONTAINER_ID" />
<one-to-many class="GenericDayAssignment"/>

View file

@ -89,6 +89,26 @@ public class AllocationUntilFillingHoursTest {
assertThat(allocation.getAssignments(), haveHours(16, 16));
}
@Test
public void theResourcesPerDayIsCalculatedCorrectlyIfTheLastDayHasFilledAllHours() {
givenSpecificAllocations(ResourcesPerDay.amount(1));
ResourceAllocation.allocating(allocations).untilAllocating(32);
ResourceAllocation<?> allocation = allocations.get(0)
.getBeingModified();
assertThat(allocation.getResourcesPerDay(),
equalTo(ResourcesPerDay.amount(1)));
}
@Test
public void theResourcesPerDayIsCalculatedCorrectlyIfHasEndedInTheMiddleOfTheEnd() {
givenSpecificAllocations(ResourcesPerDay.amount(1));
ResourceAllocation.allocating(allocations).untilAllocating(30);
ResourceAllocation<?> allocation = allocations.get(0)
.getBeingModified();
assertThat(allocation.getResourcesPerDay(),
equalTo(ResourcesPerDay.amount(1)));
}
@Test
public void worksWellForSeveralSpecificAllocations() {
givenSpecificAllocations(ResourcesPerDay.amount(1), ResourcesPerDay