From 7da6dafc622da66a15bc89a91140532daa2624cc Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 9 Sep 2011 10:11:34 +0200 Subject: [PATCH] Remove date attribute from Stretch Date is a relative value depending on allocation dates and length percentage defined by Stretch. FEA: ItEr75S23FixAllocationModel --- .../business/planner/entities/Stretch.java | 85 ++++----- .../planner/entities/StretchesFunction.java | 81 ++++----- .../src/main/resources/db.changelog-1.1.xml | 4 + .../entities/ResourceAllocations.hbm.xml | 2 - .../entities/StretchesFunctionTest.java | 169 +++++++++++------- .../streches/GraphicForStreches.java | 74 ++++---- .../streches/IStretchesFunctionModel.java | 4 + .../streches/StretchesFunctionController.java | 2 +- .../streches/StretchesFunctionModel.java | 91 +++------- 9 files changed, 251 insertions(+), 261 deletions(-) diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Stretch.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Stretch.java index c8e236c42..933a5e11e 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Stretch.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Stretch.java @@ -39,28 +39,43 @@ import org.joda.time.LocalDate; */ public class Stretch { - public static Stretch create(LocalDate date, BigDecimal datePercent, BigDecimal workPercent) { - return new Stretch(date, datePercent, workPercent); + public static Stretch create(BigDecimal datePercent, BigDecimal workPercent) { + return new Stretch(datePercent, workPercent); + } + + public static LocalDate getDateByLengthProportion( + ResourceAllocation allocation, BigDecimal lengthProportion) { + int allocationDuration = Days.daysBetween(allocation.getStartDate(), + allocation.getEndDate()).getDays(); + int days = lengthProportion.multiply( + BigDecimal.valueOf(allocationDuration)).intValue(); + return allocation.getStartDate().plusDays(days); + } + + public static BigDecimal getLengthProportionByDate( + ResourceAllocation allocation, LocalDate date) { + int allocationDuration = Days.daysBetween(allocation.getStartDate(), + allocation.getEndDate()).getDays(); + int days = Days.daysBetween(allocation.getStartDate(), date).getDays(); + return daysProportion(days, allocationDuration); + } + + private static BigDecimal daysProportion(int daysPartial, int daysTotal) { + if (daysTotal == 0) { + return BigDecimal.ZERO; + } + return BigDecimal.valueOf(daysPartial).divide( + BigDecimal.valueOf(daysTotal), 2, BigDecimal.ROUND_HALF_EVEN); } /** - * Infers the datePercent based on duration of task and the date of the - * Stretch - * - * @param date - * @param task - * @param workPercent - * @return + * Infers the lengthPercent based on duration of resource allocation and the + * date of the Stretch */ - public static Stretch create(LocalDate date, Task task, - BigDecimal workPercent) { - LocalDate start = task.getStartAsLocalDate(); - LocalDate end = task.getEndAsLocalDate(); - - Days taskDuration = Days.daysBetween(start, end); - Days daysDuration = Days.daysBetween(start, date); - BigDecimal daysPercent = daysPercent(daysDuration, taskDuration); - return new Stretch(date, daysPercent, workPercent); + public static Stretch create(LocalDate date, + ResourceAllocation allocation, BigDecimal workPercent) { + return new Stretch(getLengthProportionByDate(allocation, date), + workPercent); } protected static BigDecimal daysPercent(Days daysPartial, Days daysTotal) { @@ -81,7 +96,6 @@ public class Stretch { public static Stretch copy(Stretch stretch) { Stretch result = new Stretch(); - result.date = stretch.date; result.lengthPercentage = stretch.lengthPercentage; result.amountWorkPercentage = stretch.amountWorkPercentage; result.consolidated = stretch.consolidated; @@ -93,20 +107,18 @@ public class Stretch { return ConsolidatedStretch.fromConsolidatedProgress(resourceAllocation); } - public static List sortByDate( + public static List sortByLengthPercentage( List stretches) { Collections.sort(stretches, new Comparator() { @Override public int compare(Stretch o1, Stretch o2) { - return o1.getDate().compareTo(o2.getDate()); + return o1.getLengthPercentage().compareTo( + o2.getLengthPercentage()); } }); return stretches; } - @NotNull - private LocalDate date = new LocalDate(); - @NotNull private BigDecimal lengthPercentage = BigDecimal.ZERO; @@ -119,8 +131,7 @@ public class Stretch { // Transient value, a stretch is consolidated if it's a consolidated stretch private boolean consolidated = false; - private Stretch(LocalDate date, BigDecimal lengthPercent, BigDecimal progressPercent) { - this.date = date; + private Stretch(BigDecimal lengthPercent, BigDecimal progressPercent) { this.lengthPercentage = lengthPercent; this.amountWorkPercentage = progressPercent; } @@ -129,12 +140,12 @@ public class Stretch { } - public void setDate(LocalDate date) { - this.date = date; + public void setDateIn(ResourceAllocation allocation, LocalDate date) { + setLengthPercentage(getLengthProportionByDate(allocation, date)); } - public LocalDate getDate() { - return date; + public LocalDate getDateIn(ResourceAllocation allocation) { + return getDateByLengthProportion(allocation, lengthPercentage); } /** @@ -178,7 +189,8 @@ public class Stretch { } public String toString() { - return String.format("(%s, %s, %s, readOnly: %s) ", date, lengthPercentage, amountWorkPercentage, readOnly); + return String.format("(%s, %s, readOnly: %s) ", lengthPercentage, + amountWorkPercentage, readOnly); } public boolean isReadOnly() { @@ -219,15 +231,8 @@ class ConsolidatedStretch { final Task task = resourceAllocation.getTask(); final LocalDate consolidatedEnd = lastDay(consolidated); - return create(consolidatedEnd.plusDays(1), task.getAdvancePercentage(), task); - } - - private static Stretch create(LocalDate date, BigDecimal advancePercentage, - Task task) { - Stretch result = Stretch.create(date, task, advancePercentage); - result.readOnly(true); - result.consolidated(true); - return result; + return Stretch.create(consolidatedEnd.plusDays(1), resourceAllocation, + task.getAdvancePercentage()); } private ConsolidatedStretch() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/StretchesFunction.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/StretchesFunction.java index 04bcf80aa..c71831deb 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/StretchesFunction.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/StretchesFunction.java @@ -206,8 +206,8 @@ public class StretchesFunction extends AssignmentFunction { // Transient. Calculated from resourceAllocation private Stretch consolidatedStretch; - // Transient. Used to calculate read-only last stretch - private LocalDate taskEndDate; + // Transient. Used to calculate stretches dates + private ResourceAllocation resourceAllocation; public static StretchesFunction create() { return (StretchesFunction) create(new StretchesFunction()); @@ -220,14 +220,14 @@ public class StretchesFunction extends AssignmentFunction { } - public static List intervalsFor( + public static List intervalsFor(ResourceAllocation allocation, Collection streches) { ArrayList result = new ArrayList(); LocalDate previous = null, stretchDate = null; BigDecimal sumOfProportions = BigDecimal.ZERO, loadedProportion = BigDecimal.ZERO; for (Stretch each : streches) { - stretchDate = each.getDate(); + stretchDate = each.getDateIn(allocation); loadedProportion = each.getAmountWorkPercentage().subtract( sumOfProportions); if (loadedProportion.signum() < 0) { @@ -251,7 +251,7 @@ public class StretchesFunction extends AssignmentFunction { result.type = type; result.desiredType = desiredType; result.consolidatedStretch = consolidatedStretch; - result.taskEndDate = taskEndDate; + result.resourceAllocation = resourceAllocation; return result; } @@ -264,20 +264,28 @@ public class StretchesFunction extends AssignmentFunction { } public List getStretchesDefinedByUser() { - return Collections.unmodifiableList(Stretch.sortByDate(stretches)); + return Collections.unmodifiableList(Stretch + .sortByLengthPercentage(stretches)); } @Valid public List getStretches() { - List result = new ArrayList(stretches); - if (taskEndDate != null) { - result.add(getLastStretch()); - } - return Collections.unmodifiableList(Stretch.sortByDate(result)); + List result = new ArrayList(); + result.add(getFirstStretch()); + result.addAll(stretches); + result.add(getLastStretch()); + return Collections.unmodifiableList(Stretch + .sortByLengthPercentage(result)); } private Stretch getLastStretch() { - Stretch result = Stretch.create(taskEndDate, BigDecimal.ONE, BigDecimal.ONE); + Stretch result = Stretch.create(BigDecimal.ONE, BigDecimal.ONE); + result.readOnly(true); + return result; + } + + private Stretch getFirstStretch() { + Stretch result = Stretch.create(BigDecimal.ZERO, BigDecimal.ZERO); result.readOnly(true); return result; } @@ -308,7 +316,8 @@ public class StretchesFunction extends AssignmentFunction { @AssertTrue(message = "At least one stretch is needed") public boolean checkNoEmpty() { - return !getStretchesPlusConsolidated().isEmpty(); + // first 0%-0% and last 100%-100% stretches are added automatically + return getStretchesPlusConsolidated().size() > 2; } @AssertTrue(message = "Some stretch has lower or equal values than the " @@ -323,9 +332,6 @@ public class StretchesFunction extends AssignmentFunction { Stretch previous = iterator.next(); while (iterator.hasNext()) { Stretch current = iterator.next(); - if (current.getDate().compareTo(previous.getDate()) <= 0) { - return false; - } if (current.getLengthPercentage().compareTo( previous.getLengthPercentage()) <= 0) { return false; @@ -345,7 +351,8 @@ public class StretchesFunction extends AssignmentFunction { if (consolidatedStretch != null) { result.add(consolidatedStretch); } - return Collections.unmodifiableList(Stretch.sortByDate(result)); + return Collections.unmodifiableList(Stretch + .sortByLengthPercentage(result)); } @AssertTrue(message = "Last stretch should have one hundred percent for " @@ -374,16 +381,11 @@ public class StretchesFunction extends AssignmentFunction { if (resourceAllocation.getFirstNonConsolidatedDate() == null) { return; } - updateStretchesDates(resourceAllocation); - taskEndDate = getTaskEndDate(resourceAllocation); + this.resourceAllocation = resourceAllocation; getDesiredType().applyTo(resourceAllocation, this); type = getDesiredType(); } - private LocalDate getTaskEndDate(ResourceAllocation resourceAllocation) { - return resourceAllocation.getTask().getEndAsLocalDate(); - } - @Override public String getName() { if (StretchesFunctionTypeEnum.INTERPOLATED.equals(type)) { @@ -399,7 +401,7 @@ public class StretchesFunction extends AssignmentFunction { return Collections.emptyList(); } checkStretchesSumOneHundredPercent(); - return intervalsFor(stretches); + return intervalsFor(resourceAllocation, stretches); } private List stretchesFor(StretchesFunctionTypeEnum type) { @@ -428,8 +430,8 @@ public class StretchesFunction extends AssignmentFunction { } public boolean checkFirstIntervalIsPosteriorToDate(LocalDate date) { - List intervals = StretchesFunction - .intervalsFor(getStretchesPlusConsolidated()); + List intervals = StretchesFunction.intervalsFor( + resourceAllocation, getStretchesPlusConsolidated()); if (intervals.isEmpty()) { return false; } @@ -449,31 +451,8 @@ public class StretchesFunction extends AssignmentFunction { return consolidatedStretch; } - public void setTaskEndDate(LocalDate taskEndDate) { - this.taskEndDate = taskEndDate; - } - - /** - * {@link Stretch} is storing date in an attribute. When a task is moved - * these dates have to be updated. - * - * FIXME: Maybe in the future we could remove these dates as they could be - * calculated from task information. - */ - private void updateStretchesDates(ResourceAllocation resourceAllocation) { - Task task = resourceAllocation.getTask(); - - long startDate = task.getStartDate().getTime(); - long endDate = task.getEndDate().getTime(); - - for (Stretch stretch : stretches) { - // startDate + (percentage * (endDate - startDate)) - long stretchDate = startDate - + stretch.getLengthPercentage() - .multiply(new BigDecimal(endDate - startDate)) - .longValue(); - stretch.setDate(new LocalDate(stretchDate)); - } + public void setResourceAllocation(ResourceAllocation resourceAllocation) { + this.resourceAllocation = resourceAllocation; } @Override diff --git a/navalplanner-business/src/main/resources/db.changelog-1.1.xml b/navalplanner-business/src/main/resources/db.changelog-1.1.xml index 8ef87767d..55c405e73 100644 --- a/navalplanner-business/src/main/resources/db.changelog-1.1.xml +++ b/navalplanner-business/src/main/resources/db.changelog-1.1.xml @@ -296,4 +296,8 @@ columnDataType="BIGINT" /> + + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml index a557bab4c..cdc0a5dbe 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml @@ -312,8 +312,6 @@ - diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/StretchesFunctionTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/StretchesFunctionTest.java index bc2165ea8..ea638391f 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/StretchesFunctionTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/StretchesFunctionTest.java @@ -21,16 +21,22 @@ package org.navalplanner.business.test.planner.entities; +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createNiceMock; +import static org.easymock.classextension.EasyMock.replay; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.List; import org.joda.time.LocalDate; import org.junit.Test; +import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.Stretch; import org.navalplanner.business.planner.entities.StretchesFunction; import org.navalplanner.business.planner.entities.StretchesFunction.Interval; @@ -41,10 +47,26 @@ import org.navalplanner.business.planner.entities.StretchesFunction.Interval; * @author Manuel Rego Casasnovas */ public class StretchesFunctionTest { + + private static final LocalDate START_DATE = new LocalDate(); + private static final LocalDate END_DATE = START_DATE.plusDays(10); + private StretchesFunction stretchesFunction; + private ResourceAllocation resourceAllocation; + + private ResourceAllocation givenResourceAllocation() { + resourceAllocation = createNiceMock(ResourceAllocation.class); + + expect(resourceAllocation.getStartDate()).andReturn(START_DATE).anyTimes(); + expect(resourceAllocation.getEndDate()).andReturn(END_DATE).anyTimes(); + + replay(resourceAllocation); + return resourceAllocation; + } private StretchesFunction givenStretchesFunction() { stretchesFunction = StretchesFunction.create(); + stretchesFunction.setResourceAllocation(givenResourceAllocation()); return stretchesFunction; } @@ -54,11 +76,9 @@ public class StretchesFunctionTest { return result; } - private Stretch givenStretchAsChild(LocalDate date, - BigDecimal lengthPercentage, + private Stretch givenStretchAsChild(BigDecimal lengthPercentage, BigDecimal amountWorkPercentage) { Stretch stretch = givenStretchAsChild(); - stretch.setDate(date); stretch.setLengthPercentage(lengthPercentage); stretch.setAmountWorkPercentage(amountWorkPercentage); return stretch; @@ -66,7 +86,9 @@ public class StretchesFunctionTest { private Stretch givenStretchAsChild(LocalDate date, BigDecimal amountWorkPercentage) { - return givenStretchAsChild(date, BigDecimal.ZERO, amountWorkPercentage); + return givenStretchAsChild( + Stretch.getLengthProportionByDate(resourceAllocation, date), + amountWorkPercentage); } @Test @@ -85,48 +107,27 @@ public class StretchesFunctionTest { @Test public void stretchesFunctionCheckOneHundredPercent1() { givenStretchesFunction(); - assertFalse(stretchesFunction.checkOneHundredPercent()); + assertTrue(stretchesFunction.checkOneHundredPercent()); } @Test public void stretchesFunctionCheckOneHundredPercent2() { givenStretchesFunction(); givenStretchAsChild(); - assertFalse(stretchesFunction.checkOneHundredPercent()); - } - - @Test - public void stretchesFunctionCheckOneHundredPercent3() { - givenStretchesFunction(); - givenStretchAsChild(new LocalDate(), BigDecimal.ONE, BigDecimal.ZERO); - assertFalse(stretchesFunction.checkOneHundredPercent()); - } - - @Test - public void stretchesFunctionCheckOneHundredPercent4() { - givenStretchesFunction(); - givenStretchAsChild(new LocalDate(), BigDecimal.ZERO, BigDecimal.ONE); - assertFalse(stretchesFunction.checkOneHundredPercent()); - } - - @Test - public void stretchesFunctionCheckOneHundredPercent5() { - givenStretchesFunction(); - givenStretchAsChild(new LocalDate(), BigDecimal.ONE, BigDecimal.ONE); assertTrue(stretchesFunction.checkOneHundredPercent()); } @Test public void stretchesFunctionCheckStretchesOrder1() { givenStretchesFunction(); - assertFalse(stretchesFunction.checkStretchesOrder()); + assertTrue(stretchesFunction.checkStretchesOrder()); } @Test public void stretchesFunctionCheckStretchesOrder2() { givenStretchesFunction(); givenStretchAsChild(); - assertTrue(stretchesFunction.checkStretchesOrder()); + assertFalse(stretchesFunction.checkStretchesOrder()); } @Test @@ -141,7 +142,7 @@ public class StretchesFunctionTest { public void stretchesFunctionCheckStretchesOrder4() { givenStretchesFunction(); givenStretchAsChild(); - givenStretchAsChild(new LocalDate(), BigDecimal.ONE, BigDecimal.ONE); + givenStretchAsChild(BigDecimal.ONE, BigDecimal.ONE); assertFalse(stretchesFunction.checkStretchesOrder()); } @@ -149,8 +150,7 @@ public class StretchesFunctionTest { public void stretchesFunctionCheckStretchesOrder5() { givenStretchesFunction(); givenStretchAsChild(); - givenStretchAsChild(new LocalDate().plusMonths(1), BigDecimal.ZERO, - BigDecimal.ZERO); + givenStretchAsChild(BigDecimal.ZERO, BigDecimal.ZERO); assertFalse(stretchesFunction.checkStretchesOrder()); } @@ -158,8 +158,7 @@ public class StretchesFunctionTest { public void stretchesFunctionCheckStretchesOrder7() { givenStretchesFunction(); givenStretchAsChild(); - givenStretchAsChild(new LocalDate().minusMonths(1), BigDecimal.ONE, - BigDecimal.ONE); + givenStretchAsChild(BigDecimal.ONE, BigDecimal.ONE); assertFalse(stretchesFunction.checkStretchesOrder()); } @@ -167,43 +166,63 @@ public class StretchesFunctionTest { public void stretchesFunctionCheckStretchesOrder6() { givenStretchesFunction(); givenStretchAsChild(); - givenStretchAsChild(new LocalDate().plusMonths(1), BigDecimal.ONE, - BigDecimal.ONE); - assertTrue(stretchesFunction.checkStretchesOrder()); + givenStretchAsChild(BigDecimal.ONE, BigDecimal.ONE); + assertFalse(stretchesFunction.checkStretchesOrder()); + } + + @Test + public void stretchesFunctionCheckStretchesOrder8() { + givenStretchesFunction(); + givenStretchAsChild(BigDecimal.ONE, BigDecimal.ONE); + assertFalse(stretchesFunction.checkStretchesOrder()); + } + + @Test + public void stretchesFunctionCheckStretchesOrder9() { + givenStretchesFunction(); + givenStretchAsChild(BigDecimal.ZERO, BigDecimal.ZERO); + assertFalse(stretchesFunction.checkStretchesOrder()); } @Test public void ifNoStrechesNoIntervalDefinedByStreches() { givenStretchesFunction(); - assertTrue(stretchesFunction.getIntervalsDefinedByStreches().isEmpty()); - } - - @Test(expected = IllegalStateException.class) - public void theLastStrechMustHaveAllTheLoad() { - givenStretchesFunction(); - givenStretchAsChild(new LocalDate().plusMonths(1), new BigDecimal(0.6)); - stretchesFunction.getIntervalsDefinedByStreches(); + assertThat(stretchesFunction.getIntervalsDefinedByStreches().size(), + equalTo(2)); } @Test - public void oneStrechImpliesOneInterval() { + public void theLastStrechMustHaveAllTheLoad() { givenStretchesFunction(); - givenStretchAsChild(new LocalDate().plusMonths(1), new BigDecimal(1)); + givenStretchAsChild(new LocalDate().plusDays(1), + BigDecimal.valueOf(0.6)); + List intervals = stretchesFunction + .getIntervalsDefinedByStreches(); + assertThat(intervals.size(), equalTo(3)); + assertThat(intervals.get(intervals.size() - 1).getLoadProportion(), + equalTo(BigDecimal.valueOf(0.4).setScale(2))); + } + + @Test + public void oneStrechImpliesThreeInterval() { + givenStretchesFunction(); + givenStretchAsChild(new LocalDate().plusDays(1), new BigDecimal(1)); assertThat(stretchesFunction.getIntervalsDefinedByStreches().size(), - equalTo(1)); + equalTo(3)); } @Test public void oneStrechImpliesOneIntervalUntilDateWithLoadSpecifiedByStrech() { givenStretchesFunction(); - LocalDate strechDate = new LocalDate().plusMonths(1); - BigDecimal amountOfWorkProportion = new BigDecimal(0.5).setScale(2); + LocalDate strechDate = new LocalDate().plusDays(1); + BigDecimal amountOfWorkProportion = BigDecimal.valueOf(0.5).setScale(2); givenStretchAsChild(strechDate, amountOfWorkProportion); - givenStretchAsChild(new LocalDate().plusMonths(2), new BigDecimal(1.0)); + givenStretchAsChild(new LocalDate().plusDays(2), + BigDecimal.valueOf(1.0)); Interval firstInterval = stretchesFunction - .getIntervalsDefinedByStreches().get(0); + .getIntervalsDefinedByStreches().get(1); assertThat(firstInterval.getEnd(), equalTo(strechDate)); - assertTrue(firstInterval.hasNoStart()); + assertFalse(firstInterval.hasNoStart()); assertThat(firstInterval.getLoadProportion(), equalTo(amountOfWorkProportion)); } @@ -211,24 +230,24 @@ public class StretchesFunctionTest { @Test public void theLastIntervalHasStart() { givenStretchesFunction(); - LocalDate strechDate = new LocalDate().plusMonths(1); + LocalDate strechDate = new LocalDate().plusDays(1); givenStretchAsChild(strechDate, new BigDecimal(0.5)); - givenStretchAsChild(strechDate.plusDays(20), new BigDecimal(1)); + givenStretchAsChild(strechDate.plusDays(2), new BigDecimal(1)); Interval lastInterval = stretchesFunction - .getIntervalsDefinedByStreches().get(1); + .getIntervalsDefinedByStreches().get(2); assertThat(lastInterval.getStart(), equalTo(strechDate)); } @Test public void aIntervalInTheMiddleHasStart() { givenStretchesFunction(); - LocalDate start = new LocalDate().plusMonths(1); + LocalDate start = new LocalDate().plusDays(1); givenStretchAsChild(start, new BigDecimal(0.5)); - LocalDate middleEnd = start.plusMonths(2); + LocalDate middleEnd = start.plusDays(2); givenStretchAsChild(middleEnd, new BigDecimal(0.6)); - givenStretchAsChild(middleEnd.plusDays(10), new BigDecimal(1)); - Interval middle = stretchesFunction.getIntervalsDefinedByStreches().get( - 1); + givenStretchAsChild(middleEnd.plusDays(3), new BigDecimal(1)); + Interval middle = stretchesFunction.getIntervalsDefinedByStreches() + .get(2); assertFalse(middle.hasNoStart()); assertThat(middle.getStart(), equalTo(start)); assertThat(middle.getEnd(), equalTo(middleEnd)); @@ -237,17 +256,17 @@ public class StretchesFunctionTest { @Test public void eachIntervalHasTheCorrespondingLoadForThatInterval() { givenStretchesFunction(); - LocalDate start = new LocalDate().plusMonths(1); + LocalDate start = new LocalDate().plusDays(1); givenStretchAsChild(start, new BigDecimal(0.5)); - LocalDate middleEnd = start.plusMonths(2); + LocalDate middleEnd = start.plusDays(2); givenStretchAsChild(middleEnd, new BigDecimal(0.8)); - givenStretchAsChild(middleEnd.plusDays(10), new BigDecimal(1)); + givenStretchAsChild(middleEnd.plusDays(3), new BigDecimal(1)); Interval first = stretchesFunction.getIntervalsDefinedByStreches().get( - 0); + 1); Interval middle = stretchesFunction.getIntervalsDefinedByStreches() - .get(1); - Interval last = stretchesFunction.getIntervalsDefinedByStreches() .get(2); + Interval last = stretchesFunction.getIntervalsDefinedByStreches() + .get(3); assertThat(first.getLoadProportion(), equalTo(new BigDecimal(0.5) .setScale(2))); assertThat(middle.getLoadProportion(), equalTo(new BigDecimal(0.3) @@ -286,4 +305,22 @@ public class StretchesFunctionTest { assertThat(interval.getEnd(), equalTo(end)); } + @Test + public void checkCalculatedDateForStretches() { + givenStretchesFunction(); + givenStretchAsChild(BigDecimal.valueOf(0.2), BigDecimal.valueOf(0.5)); + givenStretchAsChild(BigDecimal.valueOf(0.5), BigDecimal.valueOf(0.8)); + + List intervals = stretchesFunction + .getIntervalsDefinedByStreches(); + assertNull(intervals.get(0).getStart()); + assertThat(intervals.get(0).getEnd(), equalTo(START_DATE)); + assertThat(intervals.get(1).getStart(), equalTo(START_DATE)); + assertThat(intervals.get(1).getEnd(), equalTo(START_DATE.plusDays(2))); + assertThat(intervals.get(2).getStart(), equalTo(START_DATE.plusDays(2))); + assertThat(intervals.get(2).getEnd(), equalTo(START_DATE.plusDays(5))); + assertThat(intervals.get(3).getStart(), equalTo(START_DATE.plusDays(5))); + assertThat(intervals.get(3).getEnd(), equalTo(END_DATE)); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/GraphicForStreches.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/GraphicForStreches.java index 26bf98102..d405c217f 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/GraphicForStreches.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/GraphicForStreches.java @@ -27,6 +27,7 @@ import java.util.List; import org.joda.time.Days; import org.joda.time.LocalDate; import org.navalplanner.business.calendars.entities.BaseCalendar; +import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.Stretch; import org.navalplanner.business.planner.entities.StretchesFunction; import org.navalplanner.business.planner.entities.StretchesFunction.Interval; @@ -60,7 +61,7 @@ public abstract class GraphicForStreches implements IGraphicGenerator { return new SimpleXYModel(); } return getAccumulatedHoursChartData(stretches, - stretchesFunctionModel.getTaskStartDate(), new BigDecimal( + stretchesFunctionModel.getResourceAllocation(), new BigDecimal( stretchesFunctionModel.getAllocationHours())); } @@ -73,17 +74,18 @@ public abstract class GraphicForStreches implements IGraphicGenerator { return new SimpleXYModel(); } return getDedicationChart(stretches, - stretchesFunctionModel.getTaskStartDate(), - new BigDecimal(stretchesFunctionModel.getAllocationHours()), + stretchesFunctionModel.getResourceAllocation(), new BigDecimal( + stretchesFunctionModel.getAllocationHours()), stretchesFunctionModel.getTaskCalendar()); } protected abstract XYModel getDedicationChart(List stretches, - LocalDate startDate, BigDecimal totalHours, + ResourceAllocation allocation, BigDecimal totalHours, BaseCalendar taskCalendar); protected abstract XYModel getAccumulatedHoursChartData( - List stretches, LocalDate startDate, BigDecimal taskHours); + List stretches, ResourceAllocation allocation, + BigDecimal taskHours); private static class ForDefaultStreches extends GraphicForStreches { @@ -94,19 +96,19 @@ public abstract class GraphicForStreches implements IGraphicGenerator { @Override public XYModel getAccumulatedHoursChartData(List stretches, - LocalDate startDate, BigDecimal taskHours) { + ResourceAllocation allocation, BigDecimal taskHours) { XYModel xymodel = new SimpleXYModel(); String title = "percentage"; - xymodel.addValue(title, startDate.toDateTimeAtStartOfDay() - .getMillis(), 0); + xymodel.addValue(title, allocation.getStartDate() + .toDateTimeAtStartOfDay().getMillis(), 0); for (Stretch stretch : stretches) { BigDecimal amountWork = stretch.getAmountWorkPercentage() .multiply(taskHours); - xymodel.addValue(title, stretch.getDate() + xymodel.addValue(title, stretch.getDateIn(allocation) .toDateTimeAtStartOfDay().getMillis(), amountWork); } @@ -114,12 +116,12 @@ public abstract class GraphicForStreches implements IGraphicGenerator { } protected XYModel getDedicationChart(List stretches, - LocalDate startDate, BigDecimal taskHours, + ResourceAllocation allocation, BigDecimal taskHours, BaseCalendar calendar){ XYModel xymodel = new SimpleXYModel(); String title = "hours"; - LocalDate previousDate = startDate; + LocalDate previousDate = allocation.getStartDate(); BigDecimal previousPercentage = BigDecimal.ZERO; xymodel.addValue(title, previousDate.toDateTimeAtStartOfDay() .getMillis(), 0); @@ -127,12 +129,12 @@ public abstract class GraphicForStreches implements IGraphicGenerator { for (Stretch stretch : stretches) { BigDecimal amountWork = stretch.getAmountWorkPercentage() .subtract(previousPercentage).multiply(taskHours); - Integer days = Days - .daysBetween(previousDate, stretch.getDate()).getDays(); + Integer days = Days.daysBetween(previousDate, + stretch.getDateIn(allocation)).getDays(); if (calendar != null) { days -= calendar.getNonWorkableDays(previousDate, - stretch.getDate()).size(); + stretch.getDateIn(allocation)).size(); } BigDecimal hoursPerDay = BigDecimal.ZERO; @@ -143,10 +145,10 @@ public abstract class GraphicForStreches implements IGraphicGenerator { xymodel.addValue(title, previousDate.toDateTimeAtStartOfDay() .getMillis() + 1, hoursPerDay); - xymodel.addValue(title, stretch.getDate() + xymodel.addValue(title, stretch.getDateIn(allocation) .toDateTimeAtStartOfDay().getMillis(), hoursPerDay); - previousDate = stretch.getDate(); + previousDate = stretch.getDateIn(allocation); previousPercentage = stretch.getAmountWorkPercentage(); } @@ -162,51 +164,53 @@ public abstract class GraphicForStreches implements IGraphicGenerator { @Override public boolean areChartsEnabled(IStretchesFunctionModel model) { - return canComputeChartFrom(model.getStretchesPlusConsolidated(), - model.getTaskStartDate()); + return canComputeChartFrom(model.getResourceAllocation(), + model.getStretchesPlusConsolidated()); } @Override protected XYModel getAccumulatedHoursChartData(List stretches, - LocalDate startDate, BigDecimal taskHours) { - if (!canComputeChartFrom(stretches, startDate)) { + ResourceAllocation allocation, BigDecimal taskHours) { + if (!canComputeChartFrom(allocation, stretches)) { return new SimpleXYModel(); } int[] hoursForEachDayUsingSplines = hoursForEachDayInterpolatedUsingSplines( - stretches, startDate, taskHours); - return createModelFrom(startDate, + stretches, allocation, taskHours); + return createModelFrom(allocation.getStartDate(), accumulatedFrom(hoursForEachDayUsingSplines)); } @Override protected XYModel getDedicationChart(List stretches, - LocalDate startDate, BigDecimal totalHours, + ResourceAllocation allocation, BigDecimal totalHours, BaseCalendar taskCalendar) { - if (!canComputeChartFrom(stretches, startDate)) { + if (!canComputeChartFrom(allocation, stretches)) { return new SimpleXYModel(); } int[] dataForChart = hoursForEachDayInterpolatedUsingSplines( - stretches, startDate, totalHours); - return createModelFrom(startDate, dataForChart); + stretches, allocation, totalHours); + return createModelFrom(allocation.getStartDate(), dataForChart); } - private boolean canComputeChartFrom(List stretches, - LocalDate start) { - return StretchesFunctionModel.areValidForInterpolation(stretches, - start); + private boolean canComputeChartFrom(ResourceAllocation allocation, + List stretches) { + return StretchesFunctionModel.areValidForInterpolation(allocation, + stretches); } private int[] hoursForEachDayInterpolatedUsingSplines( - List stretches, LocalDate startDate, + List stretches, ResourceAllocation allocation, BigDecimal taskHours) { - List intervals = StretchesFunction - .intervalsFor(stretches); - double[] dayPoints = Interval.getDayPointsFor(startDate, intervals); + List intervals = StretchesFunction.intervalsFor( + allocation, stretches); + double[] dayPoints = Interval.getDayPointsFor( + allocation.getStartDate(), intervals); double[] hourPoints = Interval.getHoursPointsFor(taskHours .intValue(), intervals); final Stretch lastStretch = stretches.get(stretches.size() - 1); return StretchesFunctionTypeEnum.hoursForEachDayUsingSplines( - dayPoints, hourPoints, startDate, lastStretch.getDate()); + dayPoints, hourPoints, allocation.getStartDate(), + lastStretch.getDateIn(allocation)); } private int[] accumulatedFrom(int[] hoursForEachDayUsingSplines) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/IStretchesFunctionModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/IStretchesFunctionModel.java index 35c1f4464..c7a46aeb2 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/IStretchesFunctionModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/IStretchesFunctionModel.java @@ -67,6 +67,8 @@ public interface IStretchesFunctionModel { AssignmentFunction getStretchesFunction(); + Date getStretchDate(Stretch stretch); + void setStretchDate(Stretch stretch, Date date) throws IllegalArgumentException; void setStretchLengthPercentage(Stretch stretch, BigDecimal lengthPercentage) @@ -78,6 +80,8 @@ public interface IStretchesFunctionModel { BaseCalendar getTaskCalendar(); + ResourceAllocation getResourceAllocation(); + /* * Final conversation steps */ diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionController.java index 8235b2f42..436080d15 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionController.java @@ -293,7 +293,7 @@ public class StretchesFunctionController extends GenericForwardComposer { Datebox datebox = Util.bind(tempDatebox, new Util.Getter() { @Override public Date get() { - return stretch.getDate().toDateTimeAtStartOfDay().toDate(); + return stretchesFunctionModel.getStretchDate(stretch); } }, new Util.Setter() { @Override diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionModel.java index e9348df6f..32196a582 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/streches/StretchesFunctionModel.java @@ -24,9 +24,7 @@ package org.navalplanner.web.planner.allocation.streches; import static org.navalplanner.web.I18nHelper._; import java.math.BigDecimal; -import java.math.RoundingMode; import java.text.DateFormat; -import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; @@ -101,7 +99,7 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { this.taskEndDate = task.getEndDate(); // Initialize stretchesFunction - stretchesFunction.setTaskEndDate(task.getEndAsLocalDate()); + stretchesFunction.setResourceAllocation(resourceAllocation); this.originalStretchesFunction = stretchesFunction; this.stretchesFunction = stretchesFunction.copy(); this.stretchesFunction.changeTypeTo(type); @@ -150,10 +148,7 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { * @return */ private List allStretches() { - List result = new ArrayList(); - result.add(firstStretch()); - result.addAll(stretchesFunction.getStretchesPlusConsolidated()); - return result; + return stretchesFunction.getStretchesPlusConsolidated(); } @Override @@ -165,18 +160,6 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { .getStretchesPlusConsolidated()); } - /** - * Defines an initial read-only stretch with 0% hours worked and 0% progress - * - * @return - */ - private Stretch firstStretch() { - Stretch result = Stretch.create(task.getStartAsLocalDate(), - BigDecimal.ZERO, BigDecimal.ZERO); - result.readOnly(true); - return result; - } - @Override public void confirm() throws ValidationException { if (stretchesFunction != null) { @@ -214,10 +197,11 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { } } - public static boolean areValidForInterpolation(List stretches, - LocalDate start) { + public static boolean areValidForInterpolation( + ResourceAllocation resourceAllocation, List stretches) { return atLeastTwoStreches(stretches) - && theFirstIntervalIsPosteriorToFirstDay(stretches, start); + && theFirstIntervalIsPosteriorToFirstDay(resourceAllocation, + stretches); } private static boolean atLeastTwoStreches(List stretches) { @@ -225,13 +209,14 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { } private static boolean theFirstIntervalIsPosteriorToFirstDay( - List stretches, LocalDate start) { - List intervals = StretchesFunction.intervalsFor(stretches); + ResourceAllocation resourceAllocation, List stretches) { + List intervals = StretchesFunction.intervalsFor( + resourceAllocation, stretches); if (intervals.isEmpty()) { return false; } Interval first = intervals.get(0); - return first.getEnd().compareTo(start) > 0; + return first.getEnd().compareTo(resourceAllocation.getStartDate()) > 0; } @Override @@ -253,10 +238,11 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { Stretch consolidatedStretch = stretchesFunction .getConsolidatedStretch(); if (consolidatedStretch != null) { - startDate = consolidatedStretch.getDate().plusDays(1); + startDate = consolidatedStretch.getDateIn(resourceAllocation) + .plusDays(1); amountWorkPercent = consolidatedStretch.getAmountWorkPercentage().add(BigDecimal.ONE.divide(BigDecimal.valueOf(100))); } - return Stretch.create(startDate, task, amountWorkPercent); + return Stretch.create(startDate, resourceAllocation, amountWorkPercent); } @Override @@ -279,6 +265,12 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { return stretchesFunction; } + @Override + public Date getStretchDate(Stretch stretch) { + return stretch.getDateIn(resourceAllocation).toDateTimeAtStartOfDay() + .toDate(); + } + @Override public void setStretchDate(Stretch stretch, Date date) throws IllegalArgumentException { @@ -294,15 +286,7 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { + sameFormatAsDefaultZK(taskEndDate))); } - stretch.setDate(new LocalDate(date)); - - if ((date.compareTo(taskEndDate) > 0) - || (stretch.getAmountWorkPercentage().compareTo(BigDecimal.ONE) == 0)) { - taskEndDate = date; - recalculateStretchesPercentages(); - } else { - calculatePercentage(stretch); - } + stretch.setDateIn(resourceAllocation, new LocalDate(date)); } private String sameFormatAsDefaultZK(Date date) { @@ -312,40 +296,10 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { return formatter.format(date); } - private void recalculateStretchesPercentages() { - List stretches = stretchesFunction.getStretches(); - if (!stretches.isEmpty()) { - for (Stretch stretch : stretches) { - calculatePercentage(stretch); - } - } - } - - private void calculatePercentage(Stretch stretch) { - long stretchDate = stretch.getDate().toDateTimeAtStartOfDay().toDate() - .getTime(); - long startDate = task.getStartDate().getTime(); - long endDate = taskEndDate.getTime(); - - // (stretchDate - startDate) / (endDate - startDate) - BigDecimal lengthPercenage = (new BigDecimal(stretchDate - startDate) - .setScale(2)).divide(new BigDecimal(endDate - startDate), - RoundingMode.DOWN); - stretch.setLengthPercentage(lengthPercenage); - } - @Override public void setStretchLengthPercentage(Stretch stretch, BigDecimal lengthPercentage) throws IllegalArgumentException { stretch.setLengthPercentage(lengthPercentage); - - long startDate = task.getStartDate().getTime(); - long endDate = taskEndDate.getTime(); - - // startDate + (percentage * (endDate - startDate)) - long stretchDate = startDate + lengthPercentage.multiply( - new BigDecimal(endDate - startDate)).longValue(); - stretch.setDate(new LocalDate(stretchDate)); } @Override @@ -364,4 +318,9 @@ public class StretchesFunctionModel implements IStretchesFunctionModel { return task.getCalendar(); } + @Override + public ResourceAllocation getResourceAllocation() { + return resourceAllocation; + } + }