From de5ce7cafb4997efdd329323663e116f6ea69187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Mon, 16 Nov 2009 10:50:09 +0100 Subject: [PATCH] ItEr35S12CUCreacionUnidadesPlanificacionItEr34S12: OrderElements and TaskElements are now binded by TaskSource. one-to-many relationship between OrderElement and TaskElement removed --- .../business/orders/daos/OrderDAO.java | 17 ++ .../business/orders/entities/Order.java | 7 +- .../orders/entities/OrderElement.java | 175 +++++++++++--- .../business/orders/entities/OrderLine.java | 23 +- .../orders/entities/SchedulingState.java | 4 + .../business/orders/entities/TaskSource.java | 215 ++++++++++++++++- .../business/planner/entities/Task.java | 18 +- .../planner/entities/TaskElement.java | 41 ++-- .../business/planner/entities/TaskGroup.java | 23 +- .../planner/entities/TaskMilestone.java | 12 +- .../business/orders/entities/Orders.hbm.xml | 16 +- .../business/planner/entities/Tasks.hbm.xml | 4 +- .../daos/ResourceAllocationDAOTest.java | 23 +- .../test/planner/daos/TaskElementDAOTest.java | 218 ++++++++++++------ .../planner/entities/TaskElementTest.java | 218 ++++++++++++------ .../test/planner/entities/TaskGroupTest.java | 17 +- .../test/planner/entities/TaskTest.java | 18 +- .../navalplanner/web/orders/OrderModel.java | 25 ++ .../web/orders/OrderModelTest.java | 6 - 19 files changed, 802 insertions(+), 278 deletions(-) diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/daos/OrderDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/daos/OrderDAO.java index a618223ee..b0d7b1a03 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/daos/OrderDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/daos/OrderDAO.java @@ -23,7 +23,11 @@ package org.navalplanner.business.orders.daos; import java.util.List; import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.orders.entities.TaskSource; +import org.navalplanner.business.planner.daos.ITaskSourceDAO; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; @@ -37,9 +41,22 @@ import org.springframework.stereotype.Repository; public class OrderDAO extends GenericDAOHibernate implements IOrderDAO { + @Autowired + private ITaskSourceDAO taskSourceDAO; + @Override public List getOrders() { return list(Order.class); } + @Override + public void remove(Long id) throws InstanceNotFoundException { + Order order = find(id); + List sources = order.getTaskSourcesFromBottomToTop(); + for (TaskSource each : sources) { + taskSourceDAO.remove(each.getId()); + } + super.remove(id); + } + } \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java index fdc952eb3..e053e0172 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java @@ -87,12 +87,7 @@ public class Order extends OrderLineGroup { } public TaskGroup getAssociatedTaskElement() { - Set taskElements = this.getTaskElements(); - if (!taskElements.isEmpty()) { - return (TaskGroup) taskElements.iterator().next(); - } else { - return null; - } + return (TaskGroup) super.getAssociatedTaskElement(); } public List getAssociatedTasks() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java index 59299f832..e3bfb3b3e 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java @@ -44,6 +44,7 @@ import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.labels.entities.Label; import org.navalplanner.business.orders.entities.SchedulingState.ITypeChangedListener; import org.navalplanner.business.orders.entities.SchedulingState.Type; +import org.navalplanner.business.orders.entities.TaskSource.TaskSourceSynchronization; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.requirements.entities.CriterionRequirement; @@ -71,8 +72,6 @@ public abstract class OrderElement extends BaseEntity { private String code; - private Set taskElements = new HashSet(); - private Set criterionRequirements = new HashSet(); protected OrderLineGroup parent; @@ -105,9 +104,6 @@ public abstract class OrderElement extends BaseEntity { @Override public void typeChanged(Type newType) { schedulingStateType = newType; - if (newType == Type.SCHEDULING_POINT) { - taskSource = TaskSource.withHoursGroupOf(OrderElement.this); - } } }); return result; @@ -120,38 +116,119 @@ public abstract class OrderElement extends BaseEntity { } return result; } + + public List calculateSynchronizationsNeeded() { + List result = new ArrayList(); + if (isSchedulingPoint()) { + result.add(synchronizationForSchedulingPoint()); + } else if (isSuperElementPartialOrCompletelyScheduled()) { + removeUnscheduled(result); + result.add(synchronizationForSuperelement()); + } else if (schedulingState.isNoScheduled()) { + removeTaskSource(result); + } + return result; + } + + private TaskSourceSynchronization synchronizationForSuperelement() { + List childrenSynchronizations = childrenSynchronizations(); + if (thereIsNoTaskSource()) { + taskSource = TaskSource.createForGroup(this); + return TaskSource + .mustAddGroup(taskSource, + childrenSynchronizations); + } else { + return taskSource.modifyGroup(childrenSynchronizations); + } + } + + private List childrenSynchronizations() { + List childrenOfGroup = new ArrayList(); + for (OrderElement orderElement : getSomewhatScheduledOrderElements()) { + childrenOfGroup.addAll(orderElement + .calculateSynchronizationsNeeded()); + } + return childrenOfGroup; + } + + private void removeUnscheduled(List result) { + for (OrderElement orderElement : getNoScheduledOrderElements()) { + orderElement.removeTaskSource(result); + } + } + + private TaskSourceSynchronization synchronizationForSchedulingPoint() { + if (thereIsNoTaskSource()) { + taskSource = TaskSource.create(this, getHoursGroups()); + return TaskSource.mustAdd(taskSource); + } else { + return taskSource.withCurrentHoursGroup(getHoursGroups()); + } + } + + private boolean thereIsNoTaskSource() { + return taskSource == null; + } + + private List getSomewhatScheduledOrderElements() { + List result = new ArrayList(); + for (OrderElement orderElement : getChildren()) { + if (orderElement.getSchedulingStateType().isSomewhatScheduled()) { + result.add(orderElement); + } + } + return result; + } + + private List getNoScheduledOrderElements() { + List result = new ArrayList(); + for (OrderElement orderElement : getChildren()) { + if (orderElement.getSchedulingState().isNoScheduled()) { + result.add(orderElement); + } + } + return result; + } + + private void removeTaskSource(List result) { + removeChildrenTaskSource(result); + if (taskSource != null) { + result.add(TaskSource.mustRemove(taskSource)); + taskSource = null; + } + } + + private void removeChildrenTaskSource(List result) { + List children = getChildren(); + for (OrderElement each : children) { + each.removeTaskSource(result); + } + } + + private boolean isSuperElementPartialOrCompletelyScheduled() { + return getSchedulingState().isSomewhatScheduled(); + } + + private boolean isSchedulingPoint() { + return getSchedulingState().getType() == Type.SCHEDULING_POINT; + } + public OrderLineGroup getParent() { return parent; } + public TaskElement getAssociatedTaskElement() { + if (taskSource == null) { + return null; + } else { + return taskSource.getTask(); + } + } + protected void setParent(OrderLineGroup parent) { this.parent = parent; } - protected void hoursGroupAdded(HoursGroup hoursGroup) { - if (isSchedulingPoint()) { - taskSource.added(hoursGroup); - } else if (belongsToSchedulingPoint()) { - getParent().hoursGroupAdded(hoursGroup); - } - } - - protected void hoursGroupDeleted(HoursGroup hoursGroup) { - if (isSchedulingPoint()) { - taskSource.removed(hoursGroup); - } else if (belongsToSchedulingPoint()) { - getParent().hoursGroupDeleted(hoursGroup); - } - } - - private boolean isSchedulingPoint() { - return schedulingStateType == Type.SCHEDULING_POINT; - } - - private boolean belongsToSchedulingPoint() { - return schedulingStateType.belongsToSchedulingPoint(); - } - public abstract Integer getWorkHours(); public abstract List getHoursGroups(); @@ -217,12 +294,8 @@ public abstract class OrderElement extends BaseEntity { public abstract OrderLineGroup toContainer(); - public Set getTaskElements() { - return Collections.unmodifiableSet(taskElements); - } - public boolean isScheduled() { - return !taskElements.isEmpty(); + return schedulingStateType.isSomewhatScheduled(); } public boolean checkAtLeastOneHoursGroup() { @@ -537,4 +610,38 @@ public abstract class OrderElement extends BaseEntity { } return schedulingStateType; } + + public TaskSource getTaskSource() { + return taskSource; + } + + public Set getTaskElements() { + if (taskSource == null) { + return Collections.emptySet(); + } + return Collections.singleton(taskSource.getTask()); + } + + public List getTaskSourcesFromBottomToTop() { + List result = new ArrayList(); + taskSourcesFromBottomToTop(result); + return result; + } + + private void taskSourcesFromBottomToTop(List result) { + if (isSchedulingPoint()) { + // checking taskSource is not null because the OrderElement might + // have not yet been saved, and the taskSources are created on save + if (taskSource != null) { + result.add(taskSource); + } + } else if (isSuperElementPartialOrCompletelyScheduled()) { + for (OrderElement each : getSomewhatScheduledOrderElements()) { + each.taskSourcesFromBottomToTop(result); + } + if (taskSource != null) { + result.add(taskSource); + } + } + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java index 9e47986dc..ce9153c5e 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java @@ -24,7 +24,6 @@ import static org.navalplanner.business.i18n.I18nHelper._; import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -122,13 +121,11 @@ public class OrderLine extends OrderElement { hoursGroup.updateMyCriterionRequirements(); hoursGroups.add(hoursGroup); recalculateHoursGroups(); - hoursGroupAdded(hoursGroup); } public void deleteHoursGroup(HoursGroup hoursGroup) { hoursGroups.remove(hoursGroup); recalculateHoursGroups(); - hoursGroupDeleted(hoursGroup); } /** @@ -156,7 +153,7 @@ public class OrderLine extends OrderElement { hoursGroup.setWorkingHours(workHours); hoursGroup.setPercentage((new BigDecimal(1).setScale(2))); - addHoursGroup(hoursGroup); + hoursGroups.add(hoursGroup); } else { if (!isTotalHoursValid(workHours)) { @@ -246,28 +243,12 @@ public class OrderLine extends OrderElement { } // Set the attribute with the new hours group calculated - deleteHoursGroups(hoursGroups); - addHoursGroups(newHoursGroups); + hoursGroups = newHoursGroups; // Re-calculate percentages recalculateHoursGroups(); } - private void addHoursGroups(Collection newHoursGroups) { - hoursGroups.addAll(newHoursGroups); - for (HoursGroup each : newHoursGroups) { - hoursGroupAdded(each); - } - } - - private void deleteHoursGroups( - Collection oldHoursGroups) { - hoursGroups.removeAll(oldHoursGroups); - for (HoursGroup each : oldHoursGroups) { - hoursGroupDeleted(each); - } - } - /** * Checks if the desired total number of hours is valid taking into account * {@link HoursGroup} policy restrictions. diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/SchedulingState.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/SchedulingState.java index 1a5cfdf86..cb0ef8679 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/SchedulingState.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/SchedulingState.java @@ -277,6 +277,10 @@ public class SchedulingState { return type.isPartiallyScheduled(); } + public boolean isNoScheduled() { + return type == Type.NO_SCHEDULED; + } + public boolean isSomewhatScheduled() { return type.isSomewhatScheduled(); } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/TaskSource.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/TaskSource.java index 00caf0e10..768cadfc3 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/TaskSource.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/TaskSource.java @@ -19,25 +19,198 @@ */ package org.navalplanner.business.orders.entities; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.apache.commons.lang.Validate; import org.hibernate.validator.NotNull; import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.daos.ITaskSourceDAO; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.business.planner.entities.TaskGroup; /** * @author Óscar González Fernández */ public class TaskSource extends BaseEntity { + public static TaskSource create(OrderElement orderElement, + List hoursGroups) { + TaskSource result = create(new TaskSource(orderElement)); + result.setHoursGroups(new HashSet(hoursGroups)); + return result; + } + + public static TaskSourceSynchronization mustAdd( + TaskSource taskSource) { + return new TaskSourceMustBeAdded(taskSource); + } + + public static TaskSourceSynchronization mustAddGroup(TaskSource taskSource, + List childrenOfGroup) { + return new TaskGroupMustBeAdded(taskSource, childrenOfGroup); + } + + public static TaskSourceSynchronization mustRemove(TaskSource taskSource) { + return new TaskSourceMustBeRemoved(taskSource); + } + + public static abstract class TaskSourceSynchronization { + public abstract TaskElement apply(ITaskSourceDAO taskSourceDAO); + + protected void saveTaskSource(ITaskSourceDAO taskSourceDAO, + TaskSource taskSource) { + taskSourceDAO.save(taskSource); + } + } + + static class TaskSourceMustBeAdded extends TaskSourceSynchronization { + + private final TaskSource taskSource; + + public TaskSourceMustBeAdded(TaskSource taskSource) { + this.taskSource = taskSource; + } + + @Override + public TaskElement apply(ITaskSourceDAO taskSourceDAO) { + Task result = Task.createTask(taskSource); + taskSource.setTask(result); + taskSourceDAO.save(taskSource); + return result; + } + } + + static class TaskSourceForTaskModified extends TaskSourceSynchronization { + + private final TaskSource taskSource; + + TaskSourceForTaskModified(TaskSource taskSource) { + this.taskSource = taskSource; + } + + @Override + public TaskElement apply(ITaskSourceDAO taskSourceDAO) { + saveTaskSource(taskSourceDAO, taskSource); + return taskSource.getTask(); + } + } + + static abstract class TaskGroupSynchronization extends + TaskSourceSynchronization { + + protected final TaskSource taskSource; + + private final List synchronizations; + + TaskGroupSynchronization(TaskSource taskSource, + List synchronizations) { + Validate.notNull(taskSource); + Validate.notNull(synchronizations); + this.taskSource = taskSource; + this.synchronizations = synchronizations; + } + + @Override + public TaskElement apply(ITaskSourceDAO taskSourceDAO) { + List children = getChildren(taskSourceDAO); + return apply(taskSourceDAO, children); + } + + private List getChildren(ITaskSourceDAO taskSourceDAO) { + List result = new ArrayList(); + for (TaskSourceSynchronization each : synchronizations) { + TaskElement t = each.apply(taskSourceDAO); + if (t != null) { + // TaskSourceMustBeRemoved gives null + result.add(t); + } + } + return result; + } + + protected abstract TaskElement apply(ITaskSourceDAO taskSourceDAO, + List children); + } + + static class TaskGroupMustBeAdded extends TaskGroupSynchronization { + + private TaskGroupMustBeAdded(TaskSource taskSource, + List synchronizations) { + super(taskSource, synchronizations); + } + + @Override + protected TaskElement apply(ITaskSourceDAO taskSourceDAO, + List children) { + TaskGroup result = TaskGroup.create(taskSource); + for (TaskElement taskElement : children) { + result.addTaskElement(taskElement); + } + taskSource.setTask(result); + saveTaskSource(taskSourceDAO, taskSource); + return result; + } + + } + + static class TaskSourceForTaskGroupModified extends + TaskGroupSynchronization { + + TaskSourceForTaskGroupModified(TaskSource taskSource, + List synchronizations) { + super(taskSource, synchronizations); + } + + @Override + protected TaskElement apply(ITaskSourceDAO taskSourceDAO, + List children) { + TaskGroup taskGroup = (TaskGroup) taskSource.getTask(); + taskGroup.addChildren(children); + taskSourceDAO.save(taskSource); + return taskGroup; + } + } + + static class TaskSourceMustBeRemoved extends TaskSourceSynchronization { + + private final TaskSource taskSource; + + public TaskSourceMustBeRemoved(TaskSource taskSource) { + this.taskSource = taskSource; + } + + @Override + public TaskElement apply(ITaskSourceDAO taskSourceDAO) { + try { + taskSourceDAO.remove(taskSource.getId()); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + return null; + } + + } + public static TaskSource withHoursGroupOf(OrderElement orderElement) { return create(new TaskSource(orderElement)); } + public static TaskSource createForGroup(OrderElement orderElement) { + return create(new TaskSource(orderElement)); + } + @NotNull private OrderElement orderElement; + @NotNull + private TaskElement task; + private Set hoursGroups = new HashSet(); public TaskSource() { @@ -45,17 +218,47 @@ public class TaskSource extends BaseEntity { public TaskSource(OrderElement orderElement) { Validate.notNull(orderElement); + this.setOrderElement(orderElement); + this.setHoursGroups(new HashSet(orderElement + .getHoursGroups())); + } + + public TaskSourceSynchronization withCurrentHoursGroup( + List hoursGroups) { + setHoursGroups(new HashSet(hoursGroups)); + return new TaskSourceForTaskModified(this); + } + + public TaskSourceSynchronization modifyGroup( + List childrenOfGroup) { + return new TaskSourceForTaskGroupModified(this, childrenOfGroup); + } + + private void setTask(TaskElement task) { + this.task = task; + } + + private void setOrderElement(OrderElement orderElement) { this.orderElement = orderElement; - this.hoursGroups = new HashSet(orderElement - .getHoursGroups()); } - public void added(HoursGroup hoursGroup) { - hoursGroups.add(hoursGroup); + public TaskElement getTask() { + return task; } - public void removed(HoursGroup hoursGroup) { - hoursGroups.remove(hoursGroup); + public OrderElement getOrderElement() { + return orderElement; } + private void setHoursGroups(Set hoursGroups) { + this.hoursGroups = hoursGroups; + } + + public Set getHoursGroups() { + return hoursGroups; + } + + public void reloadTask(ITaskElementDAO taskElementDAO) { + taskElementDAO.save(task); + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java index e4912a868..0fb0fa110 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java @@ -35,6 +35,8 @@ import org.joda.time.DateTime; import org.joda.time.Days; import org.joda.time.LocalDate; import org.navalplanner.business.orders.entities.HoursGroup; +import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.orders.entities.TaskSource; import org.navalplanner.business.planner.entities.allocationalgorithms.AllocationBeingModified; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.Resource; @@ -45,10 +47,11 @@ import org.navalplanner.business.resources.entities.Worker; */ public class Task extends TaskElement { - public static Task createTask(HoursGroup hoursGroup) { - Task task = new Task(hoursGroup); - task.setNewObject(true); - return task; + public static Task createTask(TaskSource taskSource) { + Task task = new Task(taskSource.getHoursGroups().iterator().next()); + OrderElement orderElement = taskSource.getOrderElement(); + orderElement.applyStartConstraintIfNeededTo(task); + return create(task, taskSource); } @NotNull @@ -295,15 +298,13 @@ public class Task extends TaskElement { List copied = ModifiedAllocation .copy(resourceAllocations); List allocations = AllocationBeingModified - .fromExistent(ModifiedAllocation - .modified(copied)); + .fromExistent(ModifiedAllocation.modified(copied)); if (allocations.isEmpty()) { return; } switch (calculatedValue) { case NUMBER_OF_HOURS: - ResourceAllocation.allocating(allocations) - .withExistentResources() + ResourceAllocation.allocating(allocations).withExistentResources() .allocateOnTaskLength(); break; case END_DATE: @@ -326,4 +327,3 @@ public class Task extends TaskElement { } } - diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java index 397fe6fcb..78fefdc1f 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java @@ -29,11 +29,11 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; -import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.orders.entities.TaskSource; import org.navalplanner.business.planner.entities.Dependency.Type; /** @@ -41,6 +41,19 @@ import org.navalplanner.business.planner.entities.Dependency.Type; */ public abstract class TaskElement extends BaseEntity { + protected static T create(T taskElement, + TaskSource taskSource) { + taskElement.taskSource = taskSource; + taskElement.setDeadline(new LocalDate(taskSource.getOrderElement() + .getDeadline())); + return create(taskElement); + } + + protected static T createWithoutTaskSource( + T taskElement) { + return create(taskElement); + } + private Date startDate; private Date endDate; @@ -55,14 +68,14 @@ public abstract class TaskElement extends BaseEntity { protected Integer shareOfHours; - private OrderElement orderElement; - private Set dependenciesWithThisOrigin = new HashSet(); private Set dependenciesWithThisDestination = new HashSet(); private BaseCalendar calendar; + private TaskSource taskSource; + public Integer getWorkHours() { if (shareOfHours != null) { return shareOfHours; @@ -76,7 +89,12 @@ public abstract class TaskElement extends BaseEntity { this.name = task.getName(); this.notes = task.getNotes(); this.startDate = task.getStartDate(); - this.orderElement = task.getOrderElement(); + this.taskSource = task.getTaskSource(); + } + + + public TaskSource getTaskSource() { + return taskSource; } protected void copyDependenciesTo(TaskElement result) { @@ -116,18 +134,11 @@ public abstract class TaskElement extends BaseEntity { this.notes = notes; } - public void setOrderElement(OrderElement orderElement) - throws IllegalArgumentException, IllegalStateException { - Validate.notNull(orderElement, "orderElement must be not null"); - if (this.orderElement != null) { - throw new IllegalStateException( - "once a orderElement is set, it cannot be changed"); - } - this.orderElement = orderElement; - } - public OrderElement getOrderElement() { - return orderElement; + if (getTaskSource() == null) { + return null; + } + return getTaskSource().getOrderElement(); } public Set getDependenciesWithThisOrigin() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskGroup.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskGroup.java index 13f4905af..9efbf5886 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskGroup.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskGroup.java @@ -28,16 +28,16 @@ import java.util.Set; import org.apache.commons.lang.Validate; import org.hibernate.validator.AssertTrue; +import org.navalplanner.business.orders.entities.TaskSource; /** * @author Óscar González Fernández */ public class TaskGroup extends TaskElement { - public static TaskGroup create() { + public static TaskGroup create(TaskSource taskSource) { TaskGroup taskGroup = new TaskGroup(); - taskGroup.setNewObject(true); - return taskGroup; + return create(taskGroup, taskSource); } private List taskElements = new ArrayList(); @@ -100,4 +100,21 @@ public class TaskGroup extends TaskElement { protected void moveAllocations() { // do nothing } + + public void addChildren(List children) { + List toRemove = new ArrayList(); + for (TaskElement each : taskElements) { + if (!children.contains(each)) { + toRemove.add(each); + } + } + taskElements.removeAll(toRemove); + for (TaskElement each : children) { + each.setParent(this); + if (!taskElements.contains(each)) { + taskElements.add(each); + } + } + + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java index a653008fa..60681cb10 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java @@ -29,7 +29,6 @@ import org.apache.commons.lang.Validate; import org.hibernate.validator.AssertTrue; import org.joda.time.DateTime; import org.joda.time.Days; -import org.navalplanner.business.orders.entities.OrderElement; /** * @author Lorenzo Tilve Álvaro @@ -38,8 +37,7 @@ public class TaskMilestone extends TaskElement { public static TaskMilestone create() { TaskMilestone milestone = new TaskMilestone(); - milestone.setNewObject(true); - return milestone; + return createWithoutTaskSource(milestone); } private CalculatedValue calculatedValue = CalculatedValue.END_DATE; @@ -98,14 +96,6 @@ public class TaskMilestone extends TaskElement { return new DateTime(startDate.getTime()); } - - @Override - public void setOrderElement(OrderElement orderElement) - throws IllegalStateException { - throw new IllegalStateException( - "milestones can't have orderElements associated"); - } - @Override public Integer defaultWorkHours() { return Integer.valueOf(0); diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml index 9e141a832..038146b96 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml @@ -21,10 +21,6 @@ org.navalplanner.business.orders.entities.SchedulingState$Type - - - - @@ -43,7 +39,7 @@ - + @@ -104,15 +100,17 @@ - orderElement + task - + unique="true" access="field"/> - + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml index 2e0358d29..0bd12bd28 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml @@ -16,10 +16,10 @@ - - + + diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java index b105dd7aa..7c8dd5a25 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue; import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; +import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -37,8 +38,11 @@ import org.navalplanner.business.orders.daos.IHoursGroupDAO; import org.navalplanner.business.orders.daos.IOrderElementDAO; import org.navalplanner.business.orders.entities.HoursGroup; import org.navalplanner.business.orders.entities.OrderLine; +import org.navalplanner.business.orders.entities.TaskSource; +import org.navalplanner.business.orders.entities.TaskSource.TaskSourceSynchronization; import org.navalplanner.business.planner.daos.IResourceAllocationDAO; import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.daos.ITaskSourceDAO; import org.navalplanner.business.planner.entities.GenericResourceAllocation; import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.ResourcesPerDay; @@ -77,6 +81,9 @@ public class ResourceAllocationDAOTest { @Autowired private IWorkerDAO workerDAO; + @Autowired + private ITaskSourceDAO taskSourceDAO; + @Autowired IResourceDAO resourceDAO; @@ -104,18 +111,22 @@ public class ResourceAllocationDAOTest { private ResourceAllocation createValidResourceAllocation( ResourceAllocationType type) { OrderLine orderLine = createValidOrderLine(); + orderLine.getSchedulingState().schedule(); orderElementDAO.save(orderLine); HoursGroup hoursGroup = HoursGroup.create(orderLine); hoursGroupDAO.save(hoursGroup); - Task task = Task.createTask(hoursGroup); - task.setOrderElement(orderLine); - taskElementDAO.save(task); - + List hoursGroups = Arrays.asList(hoursGroup); + TaskSource taskSource = TaskSource.create(orderLine, hoursGroups); + TaskSourceSynchronization synchronization = TaskSource + .mustAdd(taskSource); + synchronization.apply(taskSourceDAO); + Task task = (Task) taskSource.getTask(); if (ResourceAllocationType.SPECIFIC_RESOURCE_ALLOCATION.equals(type)) { SpecificResourceAllocation specificResourceAllocation = SpecificResourceAllocation - .createForTesting(ResourcesPerDay.amount(1), task); + .createForTesting(ResourcesPerDay.amount(1), + task); Worker worker = (Worker) createValidWorker(); resourceDAO.save(worker); specificResourceAllocation.setResource(worker); @@ -125,10 +136,8 @@ public class ResourceAllocationDAOTest { if (ResourceAllocationType.GENERIC_RESOURCE_ALLOCATION.equals(type)) { GenericResourceAllocation specificResourceAllocation = GenericResourceAllocation .createForTesting(ResourcesPerDay.amount(1), task); - return specificResourceAllocation; } - return null; } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/TaskElementDAOTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/TaskElementDAOTest.java index 61841be1f..db002ed96 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/TaskElementDAOTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/TaskElementDAOTest.java @@ -29,6 +29,8 @@ import static org.junit.Assert.assertTrue; import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; @@ -39,6 +41,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.navalplanner.business.IDataBootstrap; +import org.navalplanner.business.common.IAdHocTransactionService; +import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.orders.daos.IOrderDAO; @@ -46,7 +50,10 @@ import org.navalplanner.business.orders.entities.HoursGroup; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.orders.entities.OrderElement; import org.navalplanner.business.orders.entities.OrderLine; +import org.navalplanner.business.orders.entities.TaskSource; +import org.navalplanner.business.orders.entities.TaskSource.TaskSourceSynchronization; import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.daos.ITaskSourceDAO; import org.navalplanner.business.planner.daos.TaskElementDAO; import org.navalplanner.business.planner.entities.Dependency; import org.navalplanner.business.planner.entities.Task; @@ -55,6 +62,7 @@ import org.navalplanner.business.planner.entities.TaskGroup; import org.navalplanner.business.planner.entities.TaskMilestone; import org.navalplanner.business.planner.entities.Dependency.Type; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.NotTransactional; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @@ -84,17 +92,27 @@ public class TaskElementDAOTest { @Autowired private IOrderDAO orderDAO; + @Autowired + private ITaskSourceDAO taskSourceDAO; + @Autowired private SessionFactory sessionFactory; + @Autowired + private IAdHocTransactionService transactionService; + private HoursGroup associatedHoursGroup; private Task createValidTask() { associatedHoursGroup = new HoursGroup(); - Task task = Task.createTask(associatedHoursGroup); OrderLine orderLine = createOrderLine(); orderLine.addHoursGroup(associatedHoursGroup); - task.setOrderElement(orderLine); + TaskSource taskSource = TaskSource + .create(orderLine, Arrays + .asList(associatedHoursGroup)); + TaskSourceSynchronization mustAdd = TaskSource.mustAdd(taskSource); + mustAdd.apply(taskSourceDAO); + Task task = (Task) taskSource.getTask(); return task; } @@ -118,10 +136,13 @@ public class TaskElementDAOTest { } private TaskGroup createValidTaskGroup() { - TaskGroup result = TaskGroup.create(); OrderLine orderLine = createOrderLine(); - result.setOrderElement(orderLine); - return result; + TaskSource taskSource = TaskSource.createForGroup(orderLine); + TaskSourceSynchronization synchronization = TaskSource + .mustAddGroup(taskSource, Collections + . emptyList()); + synchronization.apply(taskSourceDAO); + return (TaskGroup) taskSource.getTask(); } private TaskMilestone createValidTaskMilestone() { @@ -236,56 +257,51 @@ public class TaskElementDAOTest { } @Test - public void savingTaskElementSavesAssociatedDependencies() { - Task child1 = createValidTask(); - Task child2 = createValidTask(); - taskElementDAO.save(child2); - Task oldChild2 = child2; - flushAndEvict(child2); - try { - child2 = (Task) taskElementDAO.find(child2.getId()); - } catch (InstanceNotFoundException e) { - throw new RuntimeException(e); - } - Dependency.create(child1, oldChild2, Type.START_END); - taskElementDAO.save(child1); - oldChild2.dontPoseAsTransientObjectAnymore(); - flushAndEvict(child1); - TaskElement child1Reloaded; - try { - child1Reloaded = (TaskElement) taskElementDAO.find(child1.getId()); - } catch (InstanceNotFoundException e) { - throw new RuntimeException(e); - } - assertThat(child1Reloaded.getDependenciesWithThisOrigin().size(), - equalTo(1)); - assertTrue(child1Reloaded.getDependenciesWithThisDestination() - .isEmpty()); + @NotTransactional + public void savingTaskElementSavesAssociatedDependencies() + throws InstanceNotFoundException { + IOnTransaction createValidTask = new IOnTransaction() { + @Override + public Task execute() { + return createValidTask(); + } + }; + final Task child1 = transactionService + .runOnTransaction(createValidTask); + final Task child2 = transactionService + .runOnTransaction(createValidTask); + IOnTransaction createDependency = new IOnTransaction() { + + @Override + public Void execute() { + child1.dontPoseAsTransientObjectAnymore(); + child2.dontPoseAsTransientObjectAnymore(); + Dependency.create(child1, child2, Type.START_END); + taskElementDAO.save(child1); + return null; + } + }; + transactionService.runOnTransaction(createDependency); assertThat(child2.getDependenciesWithThisDestination().size(), equalTo(1)); assertTrue(child2.getDependenciesWithThisOrigin().isEmpty()); + IOnTransaction checkDependencyWasSaved = new IOnTransaction() { + + @Override + public Void execute() { + TaskElement fromDB = (TaskElement) taskElementDAO + .findExistingEntity(child1.getId()); + assertThat(fromDB.getDependenciesWithThisOrigin() + .size(), equalTo(1)); + assertTrue(fromDB.getDependenciesWithThisDestination() + .isEmpty()); + return null; + } + }; + transactionService.runOnTransaction(checkDependencyWasSaved); } - @Test - public void testInverseManyToOneRelationshipInOrderElement() { - Task task = createValidTask(); - taskElementDAO.save(task); - flushAndEvict(task); - sessionFactory.getCurrentSession().evict(task.getOrderElement()); - TaskElement fromDB; - try { - fromDB = taskElementDAO.find(task.getId()); - } catch (InstanceNotFoundException e) { - throw new RuntimeException(e); - } - OrderElement orderElement = fromDB.getOrderElement(); - assertThat(orderElement.getTaskElements().size(), equalTo(1)); - assertThat(orderElement.getTaskElements().iterator().next(), - equalTo(fromDB)); - } - - @Test public void aTaskCanBeRemoved() { Task task = createValidTask(); taskElementDAO.save(task); @@ -300,23 +316,93 @@ public class TaskElementDAOTest { task.getId())); } - @Test - public void aTaskGroupCanBeRemoved() { - TaskGroup taskGroup = createValidTaskGroup(); - Task task = createValidTask(); - taskGroup.addTaskElement(task); - taskElementDAO.save(taskGroup); - flushAndEvict(taskGroup); - try { - taskElementDAO.remove(taskGroup.getId()); - } catch (InstanceNotFoundException e) { - throw new RuntimeException(e); - } - sessionFactory.getCurrentSession().flush(); - assertNull(sessionFactory.getCurrentSession().get(TaskGroup.class, - taskGroup.getId())); - assertNull(sessionFactory.getCurrentSession().get(TaskElement.class, - task.getId())); + @NotTransactional + public void testInverseManyToOneRelationshipInOrderElementIsSavedCorrectly() { + final Task task = transactionService + .runOnTransaction(new IOnTransaction() { + + @Override + public Task execute() { + return createValidTask(); + } + }); + transactionService.runOnReadOnlyTransaction(new IOnTransaction() { + + @Override + public Void execute() { + TaskElement fromDB = taskElementDAO.findExistingEntity(task + .getId()); + OrderElement orderElement = fromDB.getOrderElement(); + assertThat(orderElement.getTaskElements().size(), equalTo(1)); + assertThat(orderElement.getTaskElements().iterator().next(), + equalTo(fromDB)); + return null; + } + }); } + @Test + @NotTransactional + public void aTaskCanBeRemovedFromItsTaskSource() { + final Task task = transactionService.runOnTransaction(new IOnTransaction(){ + + @Override + public Task execute() { + Task task = createValidTask(); + taskElementDAO.save(task); + return task; + }}); + transactionService.runOnTransaction(new IOnTransaction() { + + @Override + public Void execute() { + try { + taskSourceDAO.remove(task.getTaskSource().getId()); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + sessionFactory.getCurrentSession().flush(); + assertNull(sessionFactory.getCurrentSession().get(TaskElement.class, + task.getId())); + return null; + }}); + } + + @Test + @NotTransactional + public void aTaskGroupCanBeRemovedFromItsTaskSourceIfBelowTasksSourcesAreRemovedFirst() { + final TaskGroup taskGroupWithOneChild = transactionService + .runOnTransaction(new IOnTransaction() { + + @Override + public TaskGroup execute() { + TaskGroup taskGroup = createValidTaskGroup(); + Task task = createValidTask(); + taskGroup.addTaskElement(task); + return taskGroup; + } + }); + transactionService.runOnTransaction(new IOnTransaction() { + + @Override + public Void execute() { + try { + taskSourceDAO.remove(taskGroupWithOneChild.getChildren() + .get(0).getTaskSource().getId()); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + try { + taskSourceDAO.remove(taskGroupWithOneChild.getTaskSource() + .getId()); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + sessionFactory.getCurrentSession().flush(); + assertNull(sessionFactory.getCurrentSession().get( + TaskElement.class, taskGroupWithOneChild.getId())); + return null; + } + }); + } } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskElementTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskElementTest.java index bfa407d40..14b4ffde1 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskElementTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskElementTest.java @@ -20,28 +20,40 @@ package org.navalplanner.business.test.planner.entities; +import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertSame; +import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; +import java.util.Collections; import java.util.Date; +import java.util.List; import javax.annotation.Resource; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.navalplanner.business.IDataBootstrap; import org.navalplanner.business.orders.entities.HoursGroup; +import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.orders.entities.OrderLine; +import org.navalplanner.business.orders.entities.OrderLineGroup; +import org.navalplanner.business.orders.entities.TaskSource; import org.navalplanner.business.planner.entities.Dependency; +import org.navalplanner.business.planner.entities.StartConstraintType; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.planner.entities.TaskGroup; import org.navalplanner.business.planner.entities.TaskMilestone; +import org.navalplanner.business.planner.entities.TaskStartConstraint; import org.navalplanner.business.planner.entities.Dependency.Type; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -66,32 +78,11 @@ public class TaskElementTest { private TaskElement task = new Task(); - private TaskElement taskWithOrderLine; - private Dependency exampleDependency; public TaskElementTest() { - this.taskWithOrderLine = new Task(); - this.taskWithOrderLine.setOrderElement(OrderLine.create()); - this.exampleDependency = Dependency.create(new Task(), - new Task(), Type.END_START); - } - - @Test - public void taskElementHasAOneToOneRelationshipWithOrderElement() { - OrderLine order = OrderLine.create(); - task.setOrderElement(order); - assertSame(order, task.getOrderElement()); - } - - @Test(expected = IllegalArgumentException.class) - public void orderElementCannotBeSetToNull() { - task.setOrderElement(null); - } - - @Test(expected = IllegalStateException.class) - public void onceSetOrderElementCannotBeChanged() { - taskWithOrderLine.setOrderElement(OrderLine.create()); + this.exampleDependency = Dependency.create(new Task(), new Task(), + Type.END_START); } @Test @@ -124,8 +115,7 @@ public class TaskElementTest { Task origin = new Task(); Task destination = new Task(); Type type = Type.START_END; - Dependency.create(origin, - destination, type); + Dependency.create(origin, destination, type); assertThat(origin.getDependenciesWithThisOrigin().size(), equalTo(1)); assertThat(destination.getDependenciesWithThisDestination().size(), equalTo(1)); @@ -135,50 +125,19 @@ public class TaskElementTest { equalTo(0)); } - private void checkPopertiesAreKept(TaskElement original, TaskElement result) { - assertThat(result.getName(), equalTo(original.getName())); - assertThat(result.getNotes(), equalTo(original.getNotes())); - assertThat(result.getStartDate(), equalTo(original.getStartDate())); - assertThat(result.getOrderElement(), - equalTo(original.getOrderElement())); - } - - private void checkDependenciesAreKept( - TaskElement taskResultOfTransformation, Task sourceDependencyTask, - Task destinationDependencyTask) { - assertThat(taskResultOfTransformation - .getDependenciesWithThisDestination().size(), equalTo(1)); - Dependency withTaskResultOfSplitDestination = taskResultOfTransformation - .getDependenciesWithThisDestination().iterator().next(); - assertThat(withTaskResultOfSplitDestination.getDestination(), - equalTo((TaskElement) taskResultOfTransformation)); - assertThat(withTaskResultOfSplitDestination.getOrigin(), - equalTo((TaskElement) sourceDependencyTask)); - - assertThat(taskResultOfTransformation.getDependenciesWithThisOrigin() - .size(), equalTo(1)); - Dependency withTaskResultOfSplitSource = taskResultOfTransformation - .getDependenciesWithThisOrigin().iterator().next(); - assertThat(withTaskResultOfSplitSource.getDestination(), - equalTo((TaskElement) destinationDependencyTask)); - assertThat(withTaskResultOfSplitSource.getOrigin(), - equalTo((TaskElement) taskResultOfTransformation)); - } - private void addDependenciesForChecking(TaskElement taskBeingTransformed, TaskElement sourceDependencyTask, TaskElement destinationDependencyTask) { Dependency.create(sourceDependencyTask, taskBeingTransformed, Type.END_START); - Dependency.create(taskBeingTransformed, - destinationDependencyTask, Type.END_START); + Dependency.create(taskBeingTransformed, destinationDependencyTask, + Type.END_START); } public void detachRemovesDependenciesFromRelatedTasks() { - HoursGroup hoursGroup = new HoursGroup(); - Task taskToDetach = Task.createTask(hoursGroup); - Task sourceDependencyTask = Task.createTask(new HoursGroup()); - Task destinationDependencyTask = Task.createTask(new HoursGroup()); + Task taskToDetach = (Task) TaskTest.createValidTask(); + Task sourceDependencyTask = (Task) TaskTest.createValidTask(); + Task destinationDependencyTask = (Task) TaskTest.createValidTask(); taskToDetach.setName("prueba"); taskToDetach.setNotes("blabla"); taskToDetach.setStartDate(new Date()); @@ -193,10 +152,9 @@ public class TaskElementTest { @Test public void detachRemovesTaskFromParent() { - TaskGroup parent = TaskGroup.create(); - HoursGroup hoursGroup = new HoursGroup(); - Task child = Task.createTask(hoursGroup); - Task anotherChild = Task.createTask(hoursGroup); + TaskGroup parent = TaskGroupTest.createValidTaskGroup(); + Task child = (Task) TaskTest.createValidTask(); + Task anotherChild = (Task) TaskTest.createValidTask(); parent.addTaskElement(child); parent.addTaskElement(anotherChild); child.detach(); @@ -205,14 +163,124 @@ public class TaskElementTest { @Test public void MilestoneOrderElementIsNull() { - TaskMilestone milestone = new TaskMilestone(); - OrderLine orderLine = OrderLine.create(); - try { - milestone.setOrderElement(orderLine); - } catch (IllegalStateException e) { - // Ok Exception expected - } finally { - assertTrue(milestone.getOrderElement() == null); - } + TaskMilestone milestone = TaskMilestone.create(); + assertThat(milestone.getOrderElement(), nullValue()); } + + @Test + public void theDeadlineOfTheOrderElementIsCopied() { + OrderLine orderLine = OrderLine.create(); + LocalDate deadline = new LocalDate(2007, 4, 4); + orderLine.setDeadline(asDate(deadline)); + TaskSource taskSource = asTaskSource(orderLine); + Task task = Task.createTask(taskSource); + assertThat(task.getDeadline(), equalTo(deadline)); + } + + private TaskSource asTaskSource(OrderLine orderLine) { + List hoursGroups = orderLine.getHoursGroups(); + if (hoursGroups.isEmpty()) { + hoursGroups = Collections.singletonList(createHoursGroup(100)); + } + return TaskSource.create(orderLine, hoursGroups); + } + + private static Date asDate(LocalDate localDate) { + return localDate.toDateTimeAtStartOfDay().toDate(); + } + + private static HoursGroup createHoursGroup(int hours) { + HoursGroup result = new HoursGroup(); + result.setWorkingHours(hours); + return result; + } + + @Test + public void ifNoParentWithStartDateTheStartConstraintIsSoonAsPossible() { + OrderLine orderLine = OrderLine.create(); + LocalDate deadline = new LocalDate(2007, 4, 4); + orderLine.setDeadline(asDate(deadline)); + TaskSource taskSource = asTaskSource(orderLine); + Task task = Task.createTask(taskSource); + assertThat(task.getStartConstraint(), + isOfType(StartConstraintType.AS_SOON_AS_POSSIBLE)); + } + + @Test + @SuppressWarnings("unchecked") + public void ifSomeParentHasInitDateTheStartConstraintIsNotEarlierThan() { + Date initDate = asDate(new LocalDate(2005, 10, 5)); + OrderLineGroup group = OrderLineGroup.create(); + group.setInitDate(initDate); + OrderLine orderLine = OrderLine.create(); + group.add(orderLine); + LocalDate deadline = new LocalDate(2007, 4, 4); + orderLine.setDeadline(asDate(deadline)); + TaskSource taskSource = asTaskSource(orderLine); + Task task = Task.createTask(taskSource); + assertThat(task.getStartConstraint(), allOf( + isOfType(StartConstraintType.START_NOT_EARLIER_THAN), + hasValue(initDate))); + } + + @Test + public void unlessTheOnlyParentWithInitDateNotNullIsTheOrder() { + Date initDate = asDate(new LocalDate(2005, 10, 5)); + Order order = Order.create(); + order.setInitDate(initDate); + OrderLine orderLine = OrderLine.create(); + order.add(orderLine); + LocalDate deadline = new LocalDate(2007, 4, 4); + orderLine.setDeadline(asDate(deadline)); + TaskSource taskSource = asTaskSource(orderLine); + Task task = Task.createTask(taskSource); + assertThat(task.getStartConstraint(), + isOfType(StartConstraintType.AS_SOON_AS_POSSIBLE)); + } + + private static Matcher isOfType( + final StartConstraintType type) { + return new BaseMatcher() { + + @Override + public boolean matches(Object object) { + if (object instanceof TaskStartConstraint) { + TaskStartConstraint startConstraint = (TaskStartConstraint) object; + return startConstraint.getStartConstraintType() == type; + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("the start constraint must be of type " + + type); + } + }; + } + + private static Matcher hasValue(final Date value) { + return new BaseMatcher() { + + @Override + public boolean matches(Object object) { + if (object instanceof TaskStartConstraint) { + TaskStartConstraint startConstraint = (TaskStartConstraint) object; + Date constraintDate = startConstraint.getConstraintDate(); + boolean bothNotNull = value != null + && constraintDate != null; + return value == constraintDate || bothNotNull + && constraintDate.equals(value); + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("the start constraint must have date " + + value); + } + }; + } + } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskGroupTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskGroupTest.java index d2a23e5a7..b6769999d 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskGroupTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskGroupTest.java @@ -29,6 +29,8 @@ import java.util.List; import org.junit.Test; import org.navalplanner.business.orders.entities.HoursGroup; +import org.navalplanner.business.orders.entities.OrderLine; +import org.navalplanner.business.orders.entities.TaskSource; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.planner.entities.TaskGroup; @@ -37,7 +39,7 @@ import org.navalplanner.business.planner.entities.TaskGroup; * @author Óscar González Fernández */ public class TaskGroupTest { - private TaskGroup taskGroup = TaskGroup.create(); + private TaskGroup taskGroup = createValidTaskGroup(); @Test public void taskGroupIsAnInstanceOfTaskElement() { @@ -51,7 +53,7 @@ public class TaskGroupTest { .isEmpty()); TaskElement child1 = new Task(); taskGroup.addTaskElement(child1); - TaskGroup child2 = TaskGroup.create(); + TaskGroup child2 = createValidTaskGroup(); taskGroup.addTaskElement(child2); List taskElements = taskGroup.getChildren(); assertThat(taskElements.size(), equalTo(2)); @@ -61,7 +63,7 @@ public class TaskGroupTest { @Test public void addingTaskElementToTaskGroupSetsTheParentProperty() { - Task child = Task.createTask(new HoursGroup()); + Task child = TaskTest.createValidTask(); taskGroup.addTaskElement(child); assertThat(child.getParent(), equalTo(taskGroup)); } @@ -75,4 +77,13 @@ public class TaskGroupTest { public void taskElementsCollectionCannotBeModified() { taskGroup.getChildren().set(0, null); } + + public static TaskGroup createValidTaskGroup() { + HoursGroup hoursGroup = new HoursGroup(); + hoursGroup.setWorkingHours(3); + TaskSource taskSource = TaskSource.create(OrderLine.create(), Arrays + .asList(hoursGroup)); + return TaskGroup.create(taskSource); + } + } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java index c968b9257..328a8e93b 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java @@ -28,8 +28,12 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import java.util.Arrays; + import org.junit.Test; import org.navalplanner.business.orders.entities.HoursGroup; +import org.navalplanner.business.orders.entities.OrderLine; +import org.navalplanner.business.orders.entities.TaskSource; import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.planner.entities.Task; @@ -46,7 +50,9 @@ public class TaskTest { public TaskTest() { hoursGroup = new HoursGroup(); hoursGroup.setWorkingHours(3); - task = Task.createTask(hoursGroup); + TaskSource taskSource = TaskSource.create(OrderLine.create(), Arrays + .asList(hoursGroup)); + task = Task.createTask(taskSource); } @Test @@ -65,10 +71,12 @@ public class TaskTest { assertNotNull(hoursGroup); } - public static TaskElement createValidTask() { - HoursGroup hours = new HoursGroup(); - hours.setWorkingHours(20); - return Task.createTask(hours); + public static Task createValidTask() { + HoursGroup hoursGroup = new HoursGroup(); + hoursGroup.setWorkingHours(3); + TaskSource taskSource = TaskSource.create(OrderLine.create(), Arrays + .asList(hoursGroup)); + return Task.createTask(taskSource); } @Test diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java index 57fdf5187..992347bf5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java @@ -46,6 +46,10 @@ import org.navalplanner.business.orders.entities.IOrderLineGroup; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.orders.entities.OrderElement; import org.navalplanner.business.orders.entities.OrderLineGroup; +import org.navalplanner.business.orders.entities.TaskSource; +import org.navalplanner.business.orders.entities.TaskSource.TaskSourceSynchronization; +import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.daos.ITaskSourceDAO; import org.navalplanner.business.requirements.entities.DirectCriterionRequirement; import org.navalplanner.business.resources.daos.ICriterionDAO; import org.navalplanner.business.resources.daos.ICriterionTypeDAO; @@ -91,8 +95,14 @@ public class OrderModel implements IOrderModel { @Autowired private IOrderElementDAO orderElementDAO; + @Autowired + private ITaskSourceDAO taskSourceDAO; + private Set