From 1be5121b12fbc245c0ac5c1ae4a516e2d9ab30f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Sun, 25 Sep 2011 13:35:23 +0200 Subject: [PATCH] Now project's details view shares the state with gantt and resource load OrderModel references and uses the PlanningState. The changes in the WBS are seen in the gantt view even if the WBS is not saved. FEA: ItEr75S11PreventLooseChanges --- .../FunctionalityExposedForExtensions.java | 1 + .../zkoss/ganttz/adapters/AutoAdapter.java | 5 + .../IAdapterToTaskFundamentalProperties.java | 2 + .../common/daos/GenericDAOHibernate.java | 5 +- .../orders/entities/OrderElement.java | 14 +- .../planner/entities/HoursCostCalculator.java | 1 - .../business/planner/entities/TaskGroup.java | 6 + .../workreports/daos/WorkReportLineDAO.java | 4 + .../navalplanner/web/orders/IOrderModel.java | 10 +- .../web/orders/OrderCRUDController.java | 15 +- .../navalplanner/web/orders/OrderModel.java | 406 +++--------------- .../web/planner/TaskElementAdapter.java | 9 + .../milestone/AddMilestoneCommand.java | 10 - .../milestone/IAddMilestoneCommand.java | 2 - .../web/planner/order/ISaveCommand.java | 13 + .../web/planner/order/OrderPlanningModel.java | 1 - .../planner/order/PlanningStateCreator.java | 174 +++----- .../web/planner/order/SaveCommandBuilder.java | 200 ++++++++- .../tabs/MultipleTabsPlannerController.java | 2 +- .../web/orders/OrderModelTest.java | 83 ++-- 20 files changed, 430 insertions(+), 533 deletions(-) diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java b/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java index 9649c7fc6..8d3f23753 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java @@ -306,6 +306,7 @@ public class FunctionalityExposedForExtensions implements IContext { diagramGraph.remove(task); task.removed(); planner.removeTask(task); + adapter.doRemovalOf(mapper.findAssociatedDomainObject(task)); mapper.remove(domainObject); return position; } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/AutoAdapter.java b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/AutoAdapter.java index f944250e5..b4343f6c5 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/AutoAdapter.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/AutoAdapter.java @@ -67,4 +67,9 @@ public class AutoAdapter implements //do nothing } + @Override + public void doRemovalOf(ITaskFundamentalProperties object) { + // do nothing + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IAdapterToTaskFundamentalProperties.java b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IAdapterToTaskFundamentalProperties.java index 23d257def..5823f3c92 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IAdapterToTaskFundamentalProperties.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IAdapterToTaskFundamentalProperties.java @@ -37,6 +37,8 @@ public interface IAdapterToTaskFundamentalProperties { public List> getIncomingDependencies(T object); + public void doRemovalOf(T object); + public boolean canAddDependency(DomainDependency dependency); public void addDependency(DomainDependency dependency); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/GenericDAOHibernate.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/GenericDAOHibernate.java index bba34374d..d6ad74521 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/GenericDAOHibernate.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/GenericDAOHibernate.java @@ -27,6 +27,7 @@ import java.lang.reflect.ParameterizedType; import java.util.List; import org.apache.commons.lang.Validate; +import org.hibernate.Hibernate; import org.hibernate.LockMode; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -107,7 +108,9 @@ public class GenericDAOHibernate result) { removeChildrenTaskSource(result); - if (getTaskSource() != null) { + if (getOnDBTaskSource() != null) { result.add(taskSourceRemoval()); } } + private TaskSource getOnDBTaskSource() { + OrderVersion version = getCurrentSchedulingData() + .getOriginOrderVersion(); + SchedulingDataForVersion schedulingDataForVersion = schedulingDatasForVersion + .get(version); + return schedulingDataForVersion.getTaskSource(); + } + private TaskSourceSynchronization taskSourceRemoval() { - Validate.notNull(getTaskSource()); + Validate.notNull(getOnDBTaskSource()); TaskSourceSynchronization result = TaskSource - .mustRemove(getTaskSource()); + .mustRemove(getOnDBTaskSource()); getCurrentSchedulingData().taskSourceRemovalRequested(); return result; } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/HoursCostCalculator.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/HoursCostCalculator.java index 0fdd459f9..b5c597ba0 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/HoursCostCalculator.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/HoursCostCalculator.java @@ -135,7 +135,6 @@ public class HoursCostCalculator implements ICostCalculator { } SortedMap result = new TreeMap(); - List workReportLines = workReportLineDAO .findByOrderElementAndChildren(task.getOrderElement()); 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 23e7b5c82..58b1f07a9 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 @@ -270,6 +270,12 @@ public class TaskGroup extends TaskElement { planningData.update(criticalPathJustTasks); } + public void dontPoseAsTransientPlanningData() { + if (planningData != null) { + planningData.dontPoseAsTransientObjectAnymore(); + } + } + /** * For a root task, retrieves the progress selected by the progressType * If there's not progressType, return taskElement.advancePercentage diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/WorkReportLineDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/WorkReportLineDAO.java index f16371a37..d1c28707d 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/WorkReportLineDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/WorkReportLineDAO.java @@ -21,6 +21,7 @@ package org.navalplanner.business.workreports.daos; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -80,6 +81,9 @@ public class WorkReportLineDAO extends IntegrationEntityDAO @Override public List findByOrderElementAndChildren( OrderElement orderElement) { + if (orderElement.isNewObject()) { + return new ArrayList(); + } return findByOrderElementAndChildren(orderElement, false); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IOrderModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IOrderModel.java index 2324b5741..c91e7b6b9 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IOrderModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IOrderModel.java @@ -37,7 +37,9 @@ import org.navalplanner.business.resources.entities.CriterionType; import org.navalplanner.business.templates.entities.OrderElementTemplate; import org.navalplanner.business.templates.entities.OrderTemplate; import org.navalplanner.web.common.IIntegrationEntityModel; +import org.navalplanner.web.planner.order.PlanningStateCreator.PlanningState; import org.zkoss.ganttz.IPredicate; +import org.zkoss.zk.ui.Desktop; /** * Contract for {@link OrderModel}
@@ -81,15 +83,15 @@ public interface IOrderModel extends IIntegrationEntityModel { List getOrders(); - void initEdit(Order order); + void initEdit(Order order, Desktop desktop); - void prepareForCreate(); + void prepareForCreate(Desktop desktop); void remove(Order order); void save() throws ValidationException; - void setOrder(Order order); + void setPlanningState(PlanningState planningState); List getBaseCalendars(); @@ -101,7 +103,7 @@ public interface IOrderModel extends IIntegrationEntityModel { boolean isCodeAutogenerated(); - void prepareCreationFrom(OrderTemplate template); + void prepareCreationFrom(OrderTemplate template, Desktop desktop); OrderElement createFrom(OrderLineGroup parent, OrderElementTemplate template); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java index ad1db0cf3..0a38cb965 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java @@ -72,6 +72,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.zkoss.ganttz.util.LongOperationFeedback; import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.event.Event; @@ -181,7 +182,7 @@ public class OrderCRUDController extends GenericForwardComposer { public void showCreateFormFromTemplate(OrderTemplate template) { showOrderElementFilter(); showCreateButtons(false); - orderModel.prepareCreationFrom(template); + orderModel.prepareCreationFrom(template, getDesktop()); prepareEditWindow(); showEditWindow(_("Create project from Template")); } @@ -948,7 +949,7 @@ public class OrderCRUDController extends GenericForwardComposer { } } - orderModel.initEdit(order); + orderModel.initEdit(order, getDesktop()); if (editWindow != null) { resetTabControllers(); setupOrderElementTreeController(); @@ -960,6 +961,10 @@ public class OrderCRUDController extends GenericForwardComposer { showEditWindow(_("Edit project")); } + private Desktop getDesktop() { + return listWindow.getDesktop(); + } + private void resetTabControllers() { orderElementTreeController = null; assignedHoursController = null; @@ -1062,12 +1067,12 @@ public class OrderCRUDController extends GenericForwardComposer { } public void goToCreateForm() { - prepareForCreate(); + prepareForCreate(getDesktop()); getCreationPopup().showWindow(this, null); } - public void prepareForCreate() { - orderModel.prepareForCreate(); + public void prepareForCreate(Desktop desktop) { + orderModel.prepareForCreate(desktop); } private void editNewCreatedOrder() { 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 5698c4bd0..81ff1d806 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 @@ -38,11 +38,7 @@ import org.navalplanner.business.advance.entities.DirectAdvanceAssignment; import org.navalplanner.business.advance.entities.IndirectAdvanceAssignment; import org.navalplanner.business.calendars.daos.IBaseCalendarDAO; import org.navalplanner.business.calendars.entities.BaseCalendar; -import org.navalplanner.business.common.BaseEntity; -import org.navalplanner.business.common.IAdHocTransactionService; -import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.IntegrationEntity; -import org.navalplanner.business.common.Registry; import org.navalplanner.business.common.daos.IConfigurationDAO; import org.navalplanner.business.common.entities.Configuration; import org.navalplanner.business.common.entities.EntityNameEnum; @@ -58,12 +54,6 @@ 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.OrderLineGroup; -import org.navalplanner.business.orders.entities.TaskSource; -import org.navalplanner.business.orders.entities.TaskSource.IOptionalPersistence; -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.entities.TaskElement; import org.navalplanner.business.qualityforms.daos.IQualityFormDAO; import org.navalplanner.business.qualityforms.entities.QualityForm; import org.navalplanner.business.requirements.entities.DirectCriterionRequirement; @@ -88,7 +78,10 @@ import org.navalplanner.business.users.entities.UserRole; import org.navalplanner.web.common.IntegrationEntityModel; import org.navalplanner.web.common.concurrentdetection.OnConcurrentModification; import org.navalplanner.web.orders.labels.LabelsOnConversation; +import org.navalplanner.web.planner.order.ISaveCommand.IBeforeSaveActions; import org.navalplanner.web.planner.order.PlanningStateCreator; +import org.navalplanner.web.planner.order.PlanningStateCreator.IActionsOnRetrieval; +import org.navalplanner.web.planner.order.PlanningStateCreator.PlanningState; import org.navalplanner.web.security.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -96,7 +89,7 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.zkoss.ganttz.IPredicate; -import org.zkoss.zul.Messagebox; +import org.zkoss.zk.ui.Desktop; /** * Model for UI operations related to {@link Order}.
@@ -109,9 +102,6 @@ import org.zkoss.zul.Messagebox; @OnConcurrentModification(goToPage = "/planner/index.zul;orders_list") public class OrderModel extends IntegrationEntityModel implements IOrderModel { - @Autowired - private PlanningStateCreator planningStateCreator; - @Autowired private ICriterionTypeDAO criterionTypeDAO; @@ -125,7 +115,10 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Autowired private IOrderDAO orderDAO; - private Order order; + @Autowired + private PlanningStateCreator planningStateCreator; + + private PlanningState planningState; private OrderElementTreeModel orderElementTreeModel; @@ -147,12 +140,6 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Autowired private IOrderElementTemplateDAO templateDAO; - @Autowired - private ITaskSourceDAO taskSourceDAO; - - @Autowired - private ITaskElementDAO taskElementDAO; - @Autowired private IBaseCalendarDAO baseCalendarDAO; @@ -173,9 +160,6 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Autowired private IScenarioManager scenarioManager; - @Autowired - private IAdHocTransactionService transactionService; - @Autowired private IOrderVersionDAO orderVersionDAO; @@ -196,12 +180,6 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { private QualityFormsOnConversation qualityFormsOnConversation; - private Scenario currentScenario; - - private List derivedScenarios = new ArrayList(); - - private boolean isEditing = false; - private QualityFormsOnConversation getQualityFormsOnConversation() { if (qualityFormsOnConversation == null) { qualityFormsOnConversation = new QualityFormsOnConversation( @@ -278,43 +256,29 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Override @Transactional(readOnly = true) - public void initEdit(Order order) { - Validate.notNull(order); - isEditing = true; + public void initEdit(Order orderToEdit, Desktop desktop) { + Validate.notNull(orderToEdit); loadNeededDataForConversation(); - this.order = getFromDB(order); - this.orderElementTreeModel = new OrderElementTreeModel(this.order); - forceLoadAdvanceAssignmentsAndMeasurements(this.order); - forceLoadCriterionRequirements(this.order); + this.planningState = planningStateCreator.retrieveOrCreate(desktop, + orderToEdit, new IActionsOnRetrieval() { + + @Override + public void onRetrieval(PlanningState planningState) { + planningState.reattach(); + } + }); + Order order = this.planningState.getOrder(); + this.orderElementTreeModel = new OrderElementTreeModel(order); + forceLoadAdvanceAssignmentsAndMeasurements(order); + forceLoadCriterionRequirements(order); forceLoadCalendar(this.getCalendar()); - forceLoadCustomer(this.order.getCustomer()); - forceLoadLabels(this.order); - forceLoadMaterialAssignments(this.order); - forceLoadTaskQualityForms(this.order); - currentScenario = scenarioManager.getCurrent(); - this.order.useSchedulingDataFor(currentScenario); - loadTasks(this.order); + forceLoadCustomer(order.getCustomer()); + forceLoadLabels(order); + forceLoadMaterialAssignments(order); + forceLoadTaskQualityForms(order); initOldCodes(); } - private void loadTasks(Order order) { - TaskSource taskSource = order.getTaskSource(); - if (taskSource == null) { - return; - } - loadTask(taskSource.getTask()); - } - - private void loadTask(TaskElement task) { - task.getDependenciesWithThisDestination().size(); - task.getDependenciesWithThisOrigin().size(); - if (!task.isLeaf()) { - for (TaskElement each : task.getChildren()) { - loadTask(each); - } - } - } - private void forceLoadLabels(OrderElement orderElement) { orderElement.getLabels().size(); for (OrderElement each : orderElement.getChildren()) { @@ -405,54 +369,37 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { } } - private Order getFromDB(Order order) { - try { - return orderDAO.find(order.getId()); - } catch (InstanceNotFoundException e) { - throw new RuntimeException(e); - } - } - @Override @Transactional(readOnly = true) - public void prepareForCreate() { + public void prepareForCreate(Desktop desktop) { loadNeededDataForConversation(); - this.order = Order.create(); + this.planningState = planningStateCreator.createOn(desktop, + Order.create()); initializeOrder(); initializeCalendar(); - currentScenario = scenarioManager.getCurrent(); - addOrderToCurrentScenario(this.order); - this.order.useSchedulingDataFor(currentScenario); - } - - private OrderVersion addOrderToCurrentScenario(Order order) { - OrderVersion orderVersion = currentScenario.addOrder(order); - order.setVersionForScenario(currentScenario, orderVersion); - derivedScenarios = scenarioDAO.getDerivedScenarios(currentScenario); - for (Scenario scenario : derivedScenarios) { - scenario.addOrder(order, orderVersion); - } - return orderVersion; } private void initializeOrder() { - this.orderElementTreeModel = new OrderElementTreeModel(this.order); - this.order.setInitDate(new Date()); + Order order = planningState.getOrder(); + this.orderElementTreeModel = new OrderElementTreeModel( + order); + order.setInitDate(new Date()); setDefaultCode(); - this.order.setCodeAutogenerated(true); + order.setCodeAutogenerated(true); } private void initializeCalendar() { - this.order.setCalendar(getDefaultCalendar()); + this.planningState.getOrder().setCalendar(getDefaultCalendar()); } @Override @Transactional(readOnly = true) - public void prepareCreationFrom(OrderTemplate template) { + public void prepareCreationFrom(OrderTemplate template, Desktop desktop) { loadNeededDataForConversation(); - this.order = createOrderFrom((OrderTemplate) templateDAO + Order order = createOrderFrom((OrderTemplate) templateDAO .findExistingEntity(template.getId())); - forceLoadAdvanceAssignmentsAndMeasurements(this.order); + planningStateCreator.createOn(desktop, order); + forceLoadAdvanceAssignmentsAndMeasurements(planningState.getOrder()); initializeOrder(); } @@ -489,251 +436,24 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Override public void save() throws ValidationException { - final boolean newOrderVersionNeeded = isEditing - && order.hasSchedulingDataBeingModified() - && !order.isUsingTheOwnerScenario(); - if (!newOrderVersionNeeded || userAcceptsCreateANewOrderVersion()) { - transactionService.runOnTransaction(new IOnTransaction() { - @Override - public Void execute() { - saveOnTransaction(newOrderVersionNeeded); - return null; - } - }); - dontPoseAsTransientObjectAnymore(order); - } - } + this.planningState.getSaveCommand().save(new IBeforeSaveActions() { - private void dontPoseAsTransientObjectAnymore(Collection collection) { - for(BaseEntity entity : collection) { - entity.dontPoseAsTransientObjectAnymore(); - } - } - - private void dontPoseAsTransientObjectAnymore(OrderElement orderElement) { - orderElement.dontPoseAsTransientObjectAnymore(); - dontPoseAsTransientObjectAnymore(orderElement.getTaskSourcesFromBottomToTop()); - dontPoseAsTransientObjectAnymore(orderElement.getSchedulingDatasForVersionFromBottomToTop()); - - dontPoseAsTransientObjectAnymore(orderElement.getDirectAdvanceAssignments()); - dontPoseAsTransientObjectAnymore(getAllMeasurements(orderElement.getDirectAdvanceAssignments())); - - dontPoseAsTransientObjectAnymore(orderElement - .getIndirectAdvanceAssignments()); - dontPoseAsTransientObjectAnymore(orderElement - .getCriterionRequirements()); - dontPoseAsTransientObjectAnymore(orderElement.getLabels()); - dontPoseAsTransientObjectAnymore(orderElement.getTaskElements()); - dontPoseAsTransientObjectAnymore(orderElement.getHoursGroups()); - dontPoseAsTransientObjectAnymore(orderElement.getTaskQualityForms()); - dontPoseAsTransientObjectAnymore(orderElement - .getAllMaterialAssignments()); - - for (HoursGroup hoursGroup : orderElement.getHoursGroups()) { - dontPoseAsTransientObjectAnymore(hoursGroup - .getCriterionRequirements()); - } - - for(OrderElement child : orderElement.getAllChildren()) { - child.dontPoseAsTransientObjectAnymore(); - dontPoseAsTransientObjectAnymore(child); - } - } - - private List getAllMeasurements( - Collection assignments) { - List result = new ArrayList(); - for (DirectAdvanceAssignment each : assignments) { - result.addAll(each.getAdvanceMeasurements()); - } - return result; - } - - private void saveOnTransaction(boolean newOrderVersionNeeded) { - checkConstraintOrderUniqueCode(order); - checkConstraintHoursGroupUniqueCode(order); - - reattachCalendar(); - reattachCriterions(); - reattachTasksForTasksSources(); - - if (order.isCodeAutogenerated()) { - generateOrderElementCodes(); - } - calculateAndSetTotalHours(); - orderDAO.save(order); - reattachCurrentTaskSources(); - - if (newOrderVersionNeeded) { - OrderVersion newVersion = OrderVersion - .createInitialVersion(currentScenario); - reattachAllTaskSources(); - order.writeSchedulingDataChangesTo(currentScenario, newVersion); - createAndSaveNewOrderVersion(scenarioManager.getCurrent(), - newVersion); - synchronizeWithSchedule(order, - TaskSource.persistButDontRemoveTaskSources(taskSourceDAO)); - order.writeSchedulingDataChanges(); - } else { - OrderVersion orderVersion = order.getCurrentVersionInfo() - .getOrderVersion(); - orderVersion.savingThroughOwner(); - synchronizeWithSchedule(order, - TaskSource.persistTaskSources(taskSourceDAO)); - order.writeSchedulingDataChanges(); - } - saveDerivedScenarios(); - deleteOrderElementWithoutParent(); - } - - private static void checkConstraintOrderUniqueCode(OrderElement order) { - OrderElement repeatedOrder; - - // Check no code is repeated in this order - if (order instanceof OrderLineGroup) { - repeatedOrder = ((OrderLineGroup) order).findRepeatedOrderCode(); - if (repeatedOrder != null) { - throw new ValidationException(_( - "Repeated Project code {0} in Project {1}", - repeatedOrder.getCode(), repeatedOrder.getName())); + @Override + public void doActions() { + reattachCalendar(); + reattachCriterions(); } - } - - // Check no code is repeated within the DB - repeatedOrder = Registry.getOrderElementDAO() - .findRepeatedOrderCodeInDB(order); - if (repeatedOrder != null) { - throw new ValidationException(_( - "Repeated Project code {0} in Project {1}", - repeatedOrder.getCode(), repeatedOrder.getName())); - } + }); } - - private static void checkConstraintHoursGroupUniqueCode(Order order) { - HoursGroup repeatedHoursGroup; - - if (order instanceof OrderLineGroup) { - repeatedHoursGroup = ((OrderLineGroup) order) - .findRepeatedHoursGroupCode(); - if (repeatedHoursGroup != null) { - throw new ValidationException(_( - "Repeated Hours Group code {0} in Project {1}", - repeatedHoursGroup.getCode(), repeatedHoursGroup - .getParentOrderLine().getName())); - } - } - - repeatedHoursGroup = Registry.getHoursGroupDAO() - .findRepeatedHoursGroupCodeInDB(order.getHoursGroups()); - if (repeatedHoursGroup != null) { - throw new ValidationException(_( - "Repeated Hours Group code {0} in Project {1}", - repeatedHoursGroup.getCode(), repeatedHoursGroup - .getParentOrderLine().getName())); - } - } - - private void createAndSaveNewOrderVersion(Scenario currentScenario, - OrderVersion newOrderVersion) { - OrderVersion previousOrderVersion = currentScenario - .getOrderVersion(order); - currentScenario.setOrderVersion(order, newOrderVersion); - scenarioDAO.updateDerivedScenariosWithNewVersion(previousOrderVersion, - order, currentScenario, newOrderVersion); - } - - private boolean userAcceptsCreateANewOrderVersion() { - try { - int status = Messagebox - .show( - _("Confirm creating a new project version for this scenario and derived. Are you sure?"), - _("New project version"), Messagebox.OK - | Messagebox.CANCEL, Messagebox.QUESTION); - return (Messagebox.OK == status); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - private void saveDerivedScenarios() { - if (derivedScenarios != null) { - for (Scenario scenario : derivedScenarios) { - scenarioDAO.save(scenario); - } - } - } - - private void calculateAndSetTotalHours() { - Integer result = 0; - for (OrderElement orderElement : order.getChildren()) { - result = result + orderElement.getWorkHours(); - } - order.setTotalHours(result); - } - - private void generateOrderElementCodes() { - order.generateOrderElementCodes(getNumberOfDigitsCode()); - } - - private void reattachCurrentTaskSources() { - for (TaskSource each : order.getTaskSourcesFromBottomToTop()) { - taskSourceDAO.reattach(each); - } - } - + private void reattachCalendar() { - if (order.getCalendar() == null) { + if (planningState.getOrder().getCalendar() == null) { return; } - BaseCalendar calendar = order.getCalendar(); + BaseCalendar calendar = planningState.getOrder().getCalendar(); baseCalendarDAO.reattachUnmodifiedEntity(calendar); } - private void reattachAllTaskSources() { - // avoid LazyInitializationException for when doing - // removePredecessorsDayAssignmentsFor - for (TaskSource each : order - .getAllScenariosTaskSourcesFromBottomToTop()) { - taskSourceDAO.reattach(each); - } - } - - private void reattachTasksForTasksSources() { - for (TaskSource each : order.getTaskSourcesFromBottomToTop()) { - each.reattachTask(taskElementDAO); - } - } - - private void synchronizeWithSchedule(OrderElement orderElement, - IOptionalPersistence persistence) { - - List synchronizationsNeeded = orderElement - .calculateSynchronizationsNeeded(); - for (TaskSourceSynchronization each : synchronizationsNeeded) { - each.apply(persistence); - } - } - - private void deleteOrderElementWithoutParent() throws ValidationException { - List listToBeRemoved = orderElementDAO - .findWithoutParent(); - for (OrderElement orderElement : listToBeRemoved) { - if (!(orderElement instanceof Order)) { - try { - // checking no work reports for that orderElement - if (!orderElementDAO - .isAlreadyInUseThisOrAnyOfItsChildren(orderElement)) { - orderElementDAO.remove(orderElement.getId()); - } - } catch (InstanceNotFoundException e) { - throw new ValidationException(_("" - + "It not could remove the task " - + orderElement.getName())); - } - } - } - } - private void reattachCriterions() { for (List list : mapCriterions.values()) { for (Criterion criterion : list) { @@ -744,7 +464,7 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Override public OrderLineGroup getOrder() { - return order; + return planningState.getOrder(); } @Override @@ -817,7 +537,8 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { IPredicate predicate) { // Iterate through orderElements from order List orderElements = new ArrayList(); - for (OrderElement orderElement : order.getAllOrderElements()) { + for (OrderElement orderElement : planningState.getOrder() + .getAllOrderElements()) { if (!orderElement.isNewObject()) { reattachOrderElement(orderElement); } @@ -828,7 +549,8 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { } } // Return list of filtered elements - return new OrderElementTreeModel(order, orderElements); + return new OrderElementTreeModel(planningState.getOrder(), + orderElements); } private void reattachLabels() { @@ -853,8 +575,8 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { } @Override - public void setOrder(Order order) { - this.order = order; + public void setPlanningState(PlanningState planningState) { + this.planningState = planningState; } @Override @@ -882,25 +604,25 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Override public BaseCalendar getCalendar() { - if (order == null) { + if (planningState == null) { return null; } - return order.getCalendar(); + return planningState.getOrder().getCalendar(); } @Override public void setCalendar(BaseCalendar calendar) { - if (order != null) { - order.setCalendar(calendar); + if (planningState != null) { + planningState.getOrder().setCalendar(calendar); } } @Override public boolean isCodeAutogenerated() { - if (order == null) { + if (planningState == null) { return false; } - return order.isCodeAutogenerated(); + return planningState.getOrder().isCodeAutogenerated(); } @Override @@ -1079,15 +801,15 @@ public class OrderModel extends IntegrationEntityModel implements IOrderModel { @Override public Set getChildren() { Set children = new HashSet(); - if (order != null) { - children.addAll(order.getOrderElements()); + if (planningState != null) { + children.addAll(planningState.getOrder().getOrderElements()); } return children; } @Override public IntegrationEntity getCurrentEntity() { - return this.order; + return this.planningState.getOrder(); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/TaskElementAdapter.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/TaskElementAdapter.java index e1f9cce60..6f57dccac 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/TaskElementAdapter.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/TaskElementAdapter.java @@ -1133,5 +1133,14 @@ public class TaskElementAdapter { type); } + @Override + public void doRemovalOf(TaskElement taskElement) { + taskElement.detach(); + TaskGroup parent = taskElement.getParent(); + if (parent != null) { + parent.remove(taskElement); + } + } + } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/AddMilestoneCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/AddMilestoneCommand.java index ecb93b54e..c805f7b3a 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/AddMilestoneCommand.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/AddMilestoneCommand.java @@ -27,7 +27,6 @@ import org.navalplanner.business.planner.daos.ITaskElementDAO; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.planner.entities.TaskGroup; import org.navalplanner.business.planner.entities.TaskMilestone; -import org.navalplanner.web.planner.order.PlanningStateCreator.PlanningState; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -44,16 +43,9 @@ import org.zkoss.ganttz.extensions.IContextWithPlannerTask; @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class AddMilestoneCommand implements IAddMilestoneCommand { - private PlanningState planningState; - @Autowired private ITaskElementDAO taskElementDAO; - @Override - public void setState(PlanningState planningState) { - this.planningState = planningState; - } - @Override @Transactional(readOnly = true) public void doAction(IContextWithPlannerTask context, @@ -68,8 +60,6 @@ public class AddMilestoneCommand implements IAddMilestoneCommand { TaskGroup parent = task.getParent(); parent.addTaskElement(insertAt, milestone); context.add(taskPosition.sameLevelAt(insertAt), milestone); - - planningState.added(milestone.getParent()); } @Override diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/IAddMilestoneCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/IAddMilestoneCommand.java index 41e5a0940..0b1fb536a 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/IAddMilestoneCommand.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/milestone/IAddMilestoneCommand.java @@ -22,7 +22,6 @@ package org.navalplanner.web.planner.milestone; import org.navalplanner.business.planner.entities.TaskElement; -import org.navalplanner.web.planner.order.PlanningStateCreator.PlanningState; import org.zkoss.ganttz.extensions.ICommandOnTask; /** @@ -31,5 +30,4 @@ import org.zkoss.ganttz.extensions.ICommandOnTask; */ public interface IAddMilestoneCommand extends ICommandOnTask { - public void setState(PlanningState planningState); } \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/ISaveCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/ISaveCommand.java index 0b34a8c0f..766969146 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/ISaveCommand.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/ISaveCommand.java @@ -21,6 +21,7 @@ package org.navalplanner.web.planner.order; +import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.planner.entities.TaskElement; import org.zkoss.ganttz.extensions.ICommand; @@ -40,5 +41,17 @@ public interface ISaveCommand extends ICommand { public String getImage(); + public interface IBeforeSaveActions { + public void doActions(); + } + + public interface IAfterSaveActions { + public void doActions(); + } + + void save(IBeforeSaveActions beforeSaveActions); + + void save(IBeforeSaveActions beforeSaveActions, + IAfterSaveActions afterSaveActions) throws ValidationException; } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java index 4c1f34628..e2c4a925d 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java @@ -973,7 +973,6 @@ public class OrderPlanningModel implements IOrderPlanningModel { } private IAddMilestoneCommand buildMilestoneCommand() { - addMilestoneCommand.setState(planningState); return addMilestoneCommand; } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/PlanningStateCreator.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/PlanningStateCreator.java index be863669f..3f2137475 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/PlanningStateCreator.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/PlanningStateCreator.java @@ -21,21 +21,20 @@ package org.navalplanner.web.planner.order; import static org.navalplanner.business.planner.entities.TaskElement.justTasks; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.Validate; import org.hibernate.Hibernate; import org.joda.time.LocalDate; -import org.navalplanner.business.common.exceptions.InstanceNotFoundException; -import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.common.daos.IEntitySequenceDAO; +import org.navalplanner.business.common.entities.EntityNameEnum; import org.navalplanner.business.labels.entities.Label; import org.navalplanner.business.orders.daos.IOrderDAO; -import org.navalplanner.business.orders.daos.IOrderElementDAO; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.orders.entities.OrderElement; import org.navalplanner.business.orders.entities.TaskSource; @@ -45,6 +44,7 @@ import org.navalplanner.business.planner.daos.ITaskElementDAO; import org.navalplanner.business.planner.daos.ITaskSourceDAO; import org.navalplanner.business.planner.entities.AssignmentFunction; import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.Dependency; import org.navalplanner.business.planner.entities.DerivedAllocation; import org.navalplanner.business.planner.entities.GenericResourceAllocation; import org.navalplanner.business.planner.entities.ResourceAllocation; @@ -131,15 +131,15 @@ public class PlanningStateCreator { @Autowired private IOrderDAO orderDAO; - @Autowired - private IOrderElementDAO orderElementDAO; - @Autowired private IScenarioDAO scenarioDAO; @Autowired private ITaskSourceDAO taskSourceDAO; + @Autowired + private IEntitySequenceDAO entitySequenceDAO; + @Autowired private TaskElementAdapter taskElementAdapterCreator; @@ -159,6 +159,22 @@ public class PlanningStateCreator { public void onRetrieval(PlanningState planningState); } + public PlanningState createOn(Desktop desktop, Order order) { + Validate.notNull(desktop); + Validate.notNull(order); + setupScenario(order); + PlanningState result = createPlanning(order); + desktop.setAttribute(ATTRIBUTE_NAME, result); + return result; + } + + void setupScenario(Order order) { + Scenario currentScenario = scenarioManager.getCurrent(); + OrderVersion orderVersion = currentScenario.addOrder(order); + order.setVersionForScenario(currentScenario, orderVersion); + order.useSchedulingDataFor(currentScenario); + } + public PlanningState retrieveOrCreate(Desktop desktop, Order order) { return retrieveOrCreate(desktop, order, null); } @@ -168,13 +184,15 @@ public class PlanningStateCreator { Object existent = desktop.getAttribute(ATTRIBUTE_NAME); if (existent instanceof PlanningState) { PlanningState result = (PlanningState) existent; - result.onRetrieval(); - if (onRetrieval != null) { - onRetrieval.onRetrieval(result); + if (ObjectUtils.equals(order.getId(), result.getOrder().getId())) { + result.onRetrieval(); + if (onRetrieval != null) { + onRetrieval.onRetrieval(result); + } + return result; } - return result; } - PlanningState result = createInitialPlanning(reload(order)); + PlanningState result = createPlanning(reload(order)); desktop.setAttribute(ATTRIBUTE_NAME, result); return result; } @@ -185,20 +203,20 @@ public class PlanningStateCreator { return result; } - private PlanningState createInitialPlanning(Order orderReloaded) { + private PlanningState createPlanning(Order orderReloaded) { Scenario currentScenario = scenarioManager.getCurrent(); final List allResources = resourceDAO.list(Resource.class); criterionDAO.list(Criterion.class); TaskGroup rootTask = orderReloaded.getAssociatedTaskElement(); if (rootTask != null) { - forceLoadOfChildren(Arrays.asList(rootTask)); + forceLoadOf(rootTask); forceLoadDayAssignments(orderReloaded.getResources()); + forceLoadOfDepedenciesCollections(rootTask); } PlanningState result = new PlanningState(orderReloaded, allResources, currentScenario); - forceLoadOfDependenciesCollections(result.getInitial()); forceLoadOfWorkingHours(result.getInitial()); forceLoadOfLabels(result.getInitial()); return result; @@ -210,18 +228,17 @@ public class PlanningStateCreator { } } - private void forceLoadOfChildren(Collection initial) { - for (TaskElement each : initial) { - forceLoadOfDataAssociatedTo(each); - if (each instanceof TaskGroup) { - findChildrenWithQueryToAvoidProxies((TaskGroup) each); - List children = each.getChildren(); - forceLoadOfChildren(children); + private void forceLoadOf(TaskElement taskElement) { + forceLoadOfDataAssociatedTo(taskElement); + if (taskElement instanceof TaskGroup) { + findChildrenWithQueryToAvoidProxies((TaskGroup) taskElement); + for (TaskElement each : taskElement.getChildren()) { + forceLoadOf(each); } } } - private static void forceLoadOfDataAssociatedTo(TaskElement each) { + private void forceLoadOfDataAssociatedTo(TaskElement each) { forceLoadOfResourceAllocationsResourcesAndAssignmentFunction(each); forceLoadOfCriterions(each); if (each.getCalendar() != null) { @@ -272,6 +289,7 @@ public class PlanningStateCreator { private void findChildrenWithQueryToAvoidProxies(TaskGroup group) { for (TaskElement eachTask : taskDAO.findChildrenOf(group)) { Hibernate.initialize(eachTask); + eachTask.getParent().getName(); } } @@ -302,19 +320,19 @@ public class PlanningStateCreator { } } - private void forceLoadOfDependenciesCollections( - Collection elements) { - for (TaskElement task : elements) { - forceLoadOfDepedenciesCollections(task); - if (!task.isLeaf()) { - forceLoadOfDependenciesCollections(task.getChildren()); - } + private void forceLoadOfDepedenciesCollections(TaskElement task) { + loadDependencies(task.getDependenciesWithThisOrigin()); + loadDependencies(task.getDependenciesWithThisDestination()); + for (TaskElement each : task.getChildren()) { + forceLoadOfDepedenciesCollections(each); } } - private void forceLoadOfDepedenciesCollections(TaskElement task) { - task.getDependenciesWithThisOrigin().size(); - task.getDependenciesWithThisDestination().size(); + private void loadDependencies(Set dependenciesWithThisOrigin) { + for (Dependency each : dependenciesWithThisOrigin) { + each.getOrigin().getName(); + each.getDestination().getName(); + } } private void forceLoadOfWorkingHours(List initial) { @@ -547,14 +565,10 @@ public class PlanningStateCreator { private ArrayList initial; - private Set toSave; - private Set toRemove = new HashSet(); private Set resources = new HashSet(); - private TaskGroup rootTask; - private final IScenarioInfo scenarioInfo; public PlanningState(Order order, @@ -562,7 +576,7 @@ public class PlanningStateCreator { Scenario currentScenario) { Validate.notNull(order); this.order = order; - rebuildTasksState(order); + rebuildTasksState(); this.scenarioInfo = new ChangeScenarioInfoOnSave( buildScenarioInfo(order), order); this.resources = OrderPlanningModel @@ -574,24 +588,26 @@ public class PlanningStateCreator { cachedConfiguration = null; cachedCommand = null; synchronizeScheduling(); - rebuildTasksState(order); + generateOrderElementCodes(); + rebuildTasksState(); } void synchronizeScheduling() { synchronizeWithSchedule(order, TaskSource.dontPersist()); } - private void rebuildTasksState(Order order) { - this.rootTask = order.getAssociatedTaskElement(); - if (this.rootTask == null) { + private void generateOrderElementCodes() { + order.generateOrderElementCodes(entitySequenceDAO + .getNumberOfDigitsCode(EntityNameEnum.ORDER)); + } + + private void rebuildTasksState() { + TaskGroup rootTask = getRootTask(); + if (rootTask == null) { this.initial = new ArrayList(); - this.toSave = new HashSet(); } else { this.initial = new ArrayList( rootTask.getChildren()); - this.toSave = rootTask == null ? new HashSet() - : new HashSet(rootTask.getChildren()); - this.toSave.removeAll(this.toRemove); } } @@ -600,7 +616,6 @@ public class PlanningStateCreator { Scenario currentScenario = getCurrentScenario(); for (Resource each : resources) { each.useScenario(currentScenario); - } } @@ -609,7 +624,7 @@ public class PlanningStateCreator { } public boolean isEmpty() { - return rootTask == null; + return getRootTask() == null; } /** @@ -663,18 +678,14 @@ public class PlanningStateCreator { getConfiguration()); } - public Collection getTasksToSave() { - return Collections.unmodifiableCollection(toSave); - } - public List getInitial() { return new ArrayList(initial); } public List getAllTasks() { List result = new ArrayList(); - if (rootTask != null) { - findTasks(rootTask, result); + if (getRootTask() != null) { + findTasks(getRootTask(), result); } return result; } @@ -750,7 +761,6 @@ public class PlanningStateCreator { if (!isTopLevel(taskElement)) { return; } - toSave.remove(taskElement); toRemove.add(taskElement); } @@ -758,19 +768,11 @@ public class PlanningStateCreator { if (taskElement instanceof TaskMilestone) { return true; } - return taskElement.getParent() == rootTask; - } - - public void added(TaskElement taskElement) { - if (!isTopLevel(taskElement)) { - return; - } - toRemove.remove(taskElement); - toSave.add(taskElement); + return taskElement.getParent() == getRootTask(); } public TaskGroup getRootTask() { - return rootTask; + return order.getAssociatedTaskElement(); } public IScenarioInfo getScenarioInfo() { @@ -789,49 +791,7 @@ public class PlanningStateCreator { } public void synchronizeTrees() { - orderDAO.save(order); scenarioInfo.saveVersioningInfo(); - reattachCurrentTaskSources(); - saveDerivedScenarios(); - deleteOrderElementWithoutParent(); - } - - private void reattachCurrentTaskSources() { - for (TaskSource each : order.getTaskSourcesFromBottomToTop()) { - taskSourceDAO.reattach(each); - } - } - - private void saveDerivedScenarios() { - List derivedScenarios = scenarioDAO - .getDerivedScenarios(getCurrentScenario()); - for (Scenario scenario : derivedScenarios) { - scenario.addOrder(order, order.getCurrentOrderVersion()); - } - } - - private void deleteOrderElementWithoutParent() - throws ValidationException { - List listToBeRemoved = orderElementDAO - .findWithoutParent(); - for (OrderElement orderElement : listToBeRemoved) { - if (!(orderElement instanceof Order)) { - tryToRemove(orderElement); - } - } - } - - private void tryToRemove(OrderElement orderElement) { - // checking no work reports for that orderElement - if (orderElementDAO - .isAlreadyInUseThisOrAnyOfItsChildren(orderElement)) { - return; - } - try { - orderElementDAO.remove(orderElement.getId()); - } catch (InstanceNotFoundException e) { - throw new RuntimeException(e); - } } public List getResourcesRelatedWithAllocations() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommandBuilder.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommandBuilder.java index e08d194a1..b69e14bf6 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommandBuilder.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommandBuilder.java @@ -40,10 +40,17 @@ import org.navalplanner.business.advance.entities.DirectAdvanceAssignment; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; +import org.navalplanner.business.common.Registry; +import org.navalplanner.business.common.daos.IEntitySequenceDAO; +import org.navalplanner.business.common.entities.EntityNameEnum; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.orders.daos.IOrderDAO; +import org.navalplanner.business.orders.daos.IOrderElementDAO; 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.OrderLineGroup; import org.navalplanner.business.planner.daos.IConsolidationDAO; import org.navalplanner.business.planner.daos.ISubcontractedTaskDataDAO; import org.navalplanner.business.planner.daos.ITaskElementDAO; @@ -66,6 +73,8 @@ import org.navalplanner.business.planner.entities.consolidations.NonCalculatedCo import org.navalplanner.business.planner.limiting.daos.ILimitingResourceQueueDependencyDAO; import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; +import org.navalplanner.business.scenarios.daos.IScenarioDAO; +import org.navalplanner.business.scenarios.entities.Scenario; import org.navalplanner.web.common.concurrentdetection.ConcurrentModificationHandling; import org.navalplanner.web.planner.TaskElementAdapter; import org.navalplanner.web.planner.order.PlanningStateCreator.PlanningState; @@ -81,6 +90,7 @@ import org.zkoss.ganttz.data.DependencyType.Point; import org.zkoss.ganttz.data.GanttDate; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.extensions.IContext; +import org.zkoss.zk.ui.Executions; import org.zkoss.zul.Messagebox; /** @@ -104,10 +114,9 @@ public class SaveCommandBuilder { PlannerConfiguration plannerConfiguration) { SaveCommand result = new SaveCommand(planningState, plannerConfiguration); - return ConcurrentModificationHandling.addHandling( - "/planner/index.zul;company_scheduling", - ISaveCommand.class, result); + "/planner/index.zul;company_scheduling", ISaveCommand.class, + result); } public static void dontPoseAsTransientAndChildrenObjects( @@ -153,9 +162,21 @@ public class SaveCommandBuilder { @Autowired private IConsolidationDAO consolidationDAO; + @Autowired + private IEntitySequenceDAO entitySequenceDAO; + @Autowired private ITaskElementDAO taskElementDAO; + @Autowired + private IOrderDAO orderDAO; + + @Autowired + private IOrderElementDAO orderElementDAO; + + @Autowired + private IScenarioDAO scenarioDAO; + @Autowired private ITaskSourceDAO taskSourceDAO; @@ -204,11 +225,32 @@ public class SaveCommandBuilder { @Override public void doAction(IContext context) { + save(null, new IAfterSaveActions() { + + @Override + public void doActions() { + notifyUserThatSavingIsDone(); + } + }); + } + + @Override + public void save(IBeforeSaveActions beforeSaveActions) { + save(beforeSaveActions, null); + } + + @Override + public void save(final IBeforeSaveActions beforeSaveActions, + IAfterSaveActions afterSaveActions) + throws ValidationException { if (state.getScenarioInfo().isUsingTheOwnerScenario() || userAcceptsCreateANewOrderVersion()) { transactionService.runOnTransaction(new IOnTransaction() { @Override public Void execute() { + if (beforeSaveActions != null) { + beforeSaveActions.doActions(); + } doTheSaving(); return null; } @@ -216,7 +258,9 @@ public class SaveCommandBuilder { dontPoseAsTransientObjectAnymore(state.getOrder()); state.getScenarioInfo().afterCommit(); fireAfterSave(); - notifyUserThatSavingIsDone(); + if (afterSaveActions != null) { + afterSaveActions.doActions(); + } } } @@ -227,6 +271,10 @@ public class SaveCommandBuilder { } private void notifyUserThatSavingIsDone() { + if (Executions.getCurrent() == null) { + // test environment + return; + } try { Messagebox.show(_("Scheduling saved"), _("Information"), Messagebox.OK, Messagebox.INFORMATION); @@ -236,8 +284,28 @@ public class SaveCommandBuilder { } private void doTheSaving() { + Order order = state.getOrder(); + if (order.isCodeAutogenerated()) { + generateOrderElementCodes(order); + } + calculateAndSetTotalHours(order); + checkConstraintOrderUniqueCode(order); + checkConstraintHoursGroupUniqueCode(order); state.synchronizeTrees(); - saveTasksToSave(); + TaskGroup rootTask = state.getRootTask(); + if (rootTask != null) { + // This reattachment is needed to ensure that the root task in + // the state is the one associated to the transaction's session. + // Otherwise if some order element has been removed, when doing + // the deletes on cascade a new root task is fetched causing a + // NonUniqueObjectException later + taskElementDAO.reattach(rootTask); + } + orderDAO.save(order); + saveDerivedScenarios(order); + deleteOrderElementWithoutParent(order); + + updateTasksRelatedData(); removeTasksToRemove(); loadDataAccessedWithNotPosedAsTransient(state.getOrder()); if (state.getRootTask() != null) { @@ -246,6 +314,99 @@ public class SaveCommandBuilder { subcontractedTaskDataDAO.removeOrphanedSubcontractedTaskData(); } + private void generateOrderElementCodes(Order order) { + order.generateOrderElementCodes(entitySequenceDAO + .getNumberOfDigitsCode(EntityNameEnum.ORDER)); + } + + private void calculateAndSetTotalHours(Order order) { + int result = 0; + for (OrderElement orderElement : order.getChildren()) { + result = result + orderElement.getWorkHours(); + } + order.setTotalHours(result); + } + + private void checkConstraintOrderUniqueCode(OrderElement order) { + OrderElement repeatedOrder; + + // Check no code is repeated in this order + if (order instanceof OrderLineGroup) { + repeatedOrder = ((OrderLineGroup) order) + .findRepeatedOrderCode(); + if (repeatedOrder != null) { + throw new ValidationException(_( + "Repeated Project code {0} in Project {1}", + repeatedOrder.getCode(), repeatedOrder.getName())); + } + } + + // Check no code is repeated within the DB + repeatedOrder = Registry.getOrderElementDAO() + .findRepeatedOrderCodeInDB(order); + if (repeatedOrder != null) { + throw new ValidationException(_( + "Repeated Project code {0} in Project {1}", + repeatedOrder.getCode(), repeatedOrder.getName())); + } + } + + private void checkConstraintHoursGroupUniqueCode(Order order) { + HoursGroup repeatedHoursGroup; + + if (order instanceof OrderLineGroup) { + repeatedHoursGroup = ((OrderLineGroup) order) + .findRepeatedHoursGroupCode(); + if (repeatedHoursGroup != null) { + throw new ValidationException(_( + "Repeated Hours Group code {0} in Project {1}", + repeatedHoursGroup.getCode(), repeatedHoursGroup + .getParentOrderLine().getName())); + } + } + + repeatedHoursGroup = Registry.getHoursGroupDAO() + .findRepeatedHoursGroupCodeInDB(order.getHoursGroups()); + if (repeatedHoursGroup != null) { + throw new ValidationException(_( + "Repeated Hours Group code {0} in Project {1}", + repeatedHoursGroup.getCode(), repeatedHoursGroup + .getParentOrderLine().getName())); + } + } + + private void saveDerivedScenarios(Order order) { + List derivedScenarios = scenarioDAO + .getDerivedScenarios(state.getCurrentScenario()); + for (Scenario scenario : derivedScenarios) { + scenario.addOrder(order, order.getCurrentOrderVersion()); + } + } + + private void deleteOrderElementWithoutParent(Order order) + throws ValidationException { + List listToBeRemoved = orderElementDAO + .findWithoutParent(); + for (OrderElement orderElement : listToBeRemoved) { + if (!(orderElement instanceof Order)) { + tryToRemove(orderElement); + } + } + } + + private void tryToRemove(OrderElement orderElement) { + // checking no work reports for that orderElement + if (orderElementDAO + .isAlreadyInUseThisOrAnyOfItsChildren(orderElement)) { + return; + } + try { + orderElementDAO.remove(orderElement.getId()); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + private void removeTasksToRemove() { for (TaskElement taskElement : state.getToRemove()) { if (taskElementDAO.exists(taskElement.getId())) { @@ -260,35 +421,35 @@ public class SaveCommandBuilder { } } - private void saveTasksToSave() { - for (TaskElement taskElement : state.getTasksToSave()) { + private void updateTasksRelatedData() { + TaskGroup rootTask = state.getRootTask(); + if (rootTask == null) { + return; + } + for (TaskElement taskElement : rootTask.getChildren()) { removeEmptyConsolidation(taskElement); updateLimitingResourceQueueElementDates(taskElement); - taskElementDAO.save(taskElement); if (taskElement.getTaskSource() != null && taskElement.getTaskSource().isNewObject()) { saveTaskSources(taskElement); } updateLimitingQueueDependencies(taskElement); } - saveRootTaskIfNecessary(); + saveRootTask(); } - private void saveRootTaskIfNecessary() { - if (!state.getTasksToSave().isEmpty()) { - TaskGroup rootTask = state.getRootTask(); - - updateRootTaskPosition(rootTask); - taskElementDAO.save(rootTask); - } + private void saveRootTask() { + TaskGroup rootTask = state.getRootTask(); + updateRootTaskPosition(rootTask); + taskElementDAO.save(rootTask); } private void updateRootTaskPosition(TaskGroup rootTask) { - final Date min = minDate(state.getTasksToSave()); + final Date min = minDate(rootTask.getChildren()); if (min != null) { rootTask.setStartDate(min); } - final Date max = maxDate(state.getTasksToSave()); + final Date max = maxDate(rootTask.getChildren()); if (max != null) { rootTask.setEndDate(max); } @@ -661,6 +822,9 @@ public class SaveCommandBuilder { if (taskElement instanceof Task) { dontPoseAsTransient(((Task) taskElement).getConsolidation()); } + if (taskElement instanceof TaskGroup) { + ((TaskGroup) taskElement).dontPoseAsTransientPlanningData(); + } } private void dontPoseAsTransient(Consolidation consolidation) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java index 738cf05bf..8b2eaeec4 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java @@ -412,7 +412,7 @@ public class MultipleTabsPlannerController implements Composer, } public void goToCreateForm() { - orderCRUDController.prepareForCreate(); + orderCRUDController.prepareForCreate(tabsSwitcher.getDesktop()); orderCRUDController.getCreationPopup().showWindow(orderCRUDController, this); diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/orders/OrderModelTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/orders/OrderModelTest.java index 30f92e709..0664e2875 100644 --- a/navalplanner-webapp/src/test/java/org/navalplanner/web/orders/OrderModelTest.java +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/orders/OrderModelTest.java @@ -35,11 +35,13 @@ import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_SECURITY import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.UUID; import javax.annotation.Resource; +import org.easymock.EasyMock; import org.hibernate.SessionFactory; import org.junit.Before; import org.junit.Ignore; @@ -71,12 +73,15 @@ import org.navalplanner.business.resources.entities.ResourceEnum; import org.navalplanner.business.scenarios.IScenarioManager; import org.navalplanner.business.scenarios.entities.OrderVersion; import org.navalplanner.business.scenarios.entities.Scenario; +import org.navalplanner.web.planner.order.PlanningStateCreator; +import org.navalplanner.web.planner.order.PlanningStateCreator.PlanningState; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.NotTransactional; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; +import org.zkoss.zk.ui.Desktop; /** * Tests for {@link OrderModel}.
@@ -108,9 +113,6 @@ public class OrderModelTest { @Resource private IDataBootstrap scenariosBootstrap; - @Autowired - private IScenarioManager scenarioManager; - @Before public void loadRequiredaData() { defaultAdvanceTypesBootstrapListener.loadRequiredData(); @@ -152,8 +154,27 @@ public class OrderModelTest { @Autowired private IExternalCompanyDAO externalCompanyDAO; + @Autowired + private PlanningStateCreator planningStateCreator; + private Criterion criterion; + private Desktop mockDesktop() { + return EasyMock.createNiceMock(Desktop.class); + } + + private PlanningState createPlanningStateFor(final Order newOrder) { + return adHocTransaction + .runOnAnotherReadOnlyTransaction(new IOnTransaction() { + + @Override + public PlanningState execute() { + return planningStateCreator.createOn(mockDesktop(), + newOrder); + } + }); + } + private Order createValidOrder() { Order order = Order.create(); order.setDescription("description"); @@ -161,10 +182,9 @@ public class OrderModelTest { order.setName("name"); order.setResponsible("responsible"); order.setCode("code-" + UUID.randomUUID()); - order.setCalendar(configurationDAO.getConfiguration() + order.setCalendar(configurationDAO + .getConfigurationWithReadOnlyTransaction() .getDefaultCalendar()); - setupVersionUsing(scenarioManager, order); - order.useSchedulingDataFor(scenarioManager.getCurrent()); return order; } @@ -187,7 +207,7 @@ public class OrderModelTest { public void testCreation() throws ValidationException { Order order = createValidOrder(); order.setCustomer(createValidExternalCompany()); - orderModel.setOrder(order); + orderModel.setPlanningState(createPlanningStateFor(order)); orderModel.save(); assertTrue(orderDAO.exists(order.getId())); } @@ -197,7 +217,7 @@ public class OrderModelTest { .runOnAnotherReadOnlyTransaction(new IOnTransaction() { @Override public Void execute() { - orderModel.prepareForCreate(); + orderModel.prepareForCreate(mockDesktop()); return null; } }); @@ -249,7 +269,7 @@ public class OrderModelTest { List list = orderModel.getOrders(); Order order = createValidOrder(); order.setCustomer(createValidExternalCompany()); - orderModel.setOrder(order); + orderModel.setPlanningState(createPlanningStateFor(order)); orderModel.save(); assertThat(orderModel.getOrders().size(), equalTo(list.size() + 1)); } @@ -257,7 +277,7 @@ public class OrderModelTest { @Test public void testRemove() { Order order = createValidOrder(); - orderModel.setOrder(order); + orderModel.setPlanningState(createPlanningStateFor(order)); orderModel.save(); assertTrue(orderDAO.exists(order.getId())); orderModel.remove(order); @@ -269,14 +289,14 @@ public class OrderModelTest { throws ValidationException { Order order = createValidOrder(); order.setDeadline(year(0)); - orderModel.setOrder(order); + orderModel.setPlanningState(createPlanningStateFor(order)); orderModel.save(); } @Test public void testFind() throws InstanceNotFoundException { Order order = createValidOrder(); - orderModel.setOrder(order); + orderModel.setPlanningState(createPlanningStateFor(order)); orderModel.save(); assertThat(orderDAO.find(order.getId()), notNullValue()); } @@ -285,12 +305,8 @@ public class OrderModelTest { @NotTransactional public void testOrderPreserved() throws ValidationException, InstanceNotFoundException { - final Order order = adHocTransaction.runOnReadOnlyTransaction(new IOnTransaction() { - @Override - public Order execute() { - return createValidOrder(); - } - }); + final Order order = createValidOrder(); + orderModel.setPlanningState(createPlanningStateFor(order)); final OrderElement[] containers = new OrderLineGroup[10]; for (int i = 0; i < containers.length; i++) { containers[i] = adHocTransaction @@ -319,7 +335,6 @@ public class OrderModelTest { orderLineGroup.add(leaf); } - orderModel.setOrder(order); orderModel.save(); adHocTransaction.runOnTransaction(new IOnTransaction() { @@ -376,13 +391,8 @@ public class OrderModelTest { @Test @NotTransactional public void testAddingOrderElement() { - final Order order = adHocTransaction - .runOnReadOnlyTransaction(new IOnTransaction() { - @Override - public Order execute() { - return createValidOrder(); - } - }); + final Order order = createValidOrder(); + orderModel.setPlanningState(createPlanningStateFor(order)); OrderLineGroup container = adHocTransaction .runOnTransaction(new IOnTransaction() { @Override @@ -401,7 +411,6 @@ public class OrderModelTest { hoursGroup.setCode("hoursGroupName"); hoursGroup.setWorkingHours(3); leaf.addHoursGroup(hoursGroup); - orderModel.setOrder(order); orderModel.save(); adHocTransaction.runOnTransaction(new IOnTransaction() { @@ -432,13 +441,8 @@ public class OrderModelTest { @NotTransactional public void testManyToManyHoursGroupCriterionMapping() { givenCriterion(); - final Order order = adHocTransaction - .runOnReadOnlyTransaction(new IOnTransaction() { - @Override - public Order execute() { - return createValidOrder(); - } - }); + final Order order = createValidOrder(); + orderModel.setPlanningState(createPlanningStateFor(order)); OrderLine orderLine = OrderLine.create(); orderLine.setName("Order element"); @@ -461,7 +465,6 @@ public class OrderModelTest { hoursGroup.addCriterionRequirement(criterionRequirement); //hoursGroup2.addCriterionRequirement(criterionRequirement); - orderModel.setOrder(order); orderModel.save(); adHocTransaction.runOnTransaction(new IOnTransaction() { @@ -523,14 +526,18 @@ public class OrderModelTest { @Test(expected = ValidationException.class) public void testAtLeastOneHoursGroup() { Order order = createValidOrder(); + orderModel.setPlanningState(createPlanningStateFor(order)); OrderLine orderLine = OrderLine.create(); - orderLine.setName("foo"); - orderLine.setCode("000000000"); + orderLine.setName(randomize("foo" + new Random().nextInt())); + orderLine.setCode(randomize("000000000")); order.add(orderLine); - orderModel.setOrder(order); orderModel.save(); } + private static String randomize(String original) { + return original + new Random().nextInt(); + } + }