/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2012 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libreplan.web.planner.order;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.LocalDate;
import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes;
import org.libreplan.business.advance.entities.AdvanceMeasurement;
import org.libreplan.business.advance.entities.AdvanceType;
import org.libreplan.business.advance.entities.DirectAdvanceAssignment;
import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException;
import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException;
import org.libreplan.business.common.BaseEntity;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.IOnTransaction;
import org.libreplan.business.common.Registry;
import org.libreplan.business.common.daos.IEntitySequenceDAO;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.common.exceptions.ValidationException.InvalidValue;
import org.libreplan.business.orders.daos.IOrderDAO;
import org.libreplan.business.orders.daos.IOrderElementDAO;
import org.libreplan.business.orders.entities.HoursGroup;
import org.libreplan.business.orders.entities.ISumChargedEffortRecalculator;
import org.libreplan.business.orders.entities.ISumExpensesRecalculator;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.OrderLineGroup;
import org.libreplan.business.orders.entities.TaskSource;
import org.libreplan.business.planner.daos.IConsolidationDAO;
import org.libreplan.business.planner.daos.IDependencyDAO;
import org.libreplan.business.planner.daos.ISubcontractedTaskDataDAO;
import org.libreplan.business.planner.daos.ITaskElementDAO;
import org.libreplan.business.planner.daos.ITaskSourceDAO;
import org.libreplan.business.planner.entities.DayAssignment;
import org.libreplan.business.planner.entities.Dependency;
import org.libreplan.business.planner.entities.DerivedAllocation;
import org.libreplan.business.planner.entities.DerivedDayAssignment;
import org.libreplan.business.planner.entities.DerivedDayAssignmentsContainer;
import org.libreplan.business.planner.entities.ResourceAllocation;
import org.libreplan.business.planner.entities.SubcontractedTaskData;
import org.libreplan.business.planner.entities.SubcontractorDeliverDate;
import org.libreplan.business.planner.entities.Task;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.business.planner.entities.TaskGroup;
import org.libreplan.business.planner.entities.consolidations.CalculatedConsolidatedValue;
import org.libreplan.business.planner.entities.consolidations.CalculatedConsolidation;
import org.libreplan.business.planner.entities.consolidations.ConsolidatedValue;
import org.libreplan.business.planner.entities.consolidations.Consolidation;
import org.libreplan.business.planner.entities.consolidations.NonCalculatedConsolidatedValue;
import org.libreplan.business.planner.entities.consolidations.NonCalculatedConsolidation;
import org.libreplan.business.planner.limiting.daos.ILimitingResourceQueueDependencyDAO;
import org.libreplan.business.planner.limiting.entities.LimitingResourceQueueDependency;
import org.libreplan.business.planner.limiting.entities.LimitingResourceQueueElement;
import org.libreplan.business.scenarios.daos.IScenarioDAO;
import org.libreplan.business.scenarios.entities.Scenario;
import org.libreplan.business.users.daos.IOrderAuthorizationDAO;
import org.libreplan.business.users.entities.OrderAuthorization;
import org.libreplan.business.workingday.IntraDayDate;
import org.libreplan.web.common.ConfirmCloseUtil;
import org.libreplan.web.common.IMessagesForUser;
import org.libreplan.web.common.MessagesForUser;
import org.libreplan.web.common.concurrentdetection.ConcurrentModificationHandling;
import org.libreplan.web.planner.TaskElementAdapter;
import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState;
import org.libreplan.web.planner.taskedition.TaskPropertiesController;
import org.libreplan.web.security.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.zkoss.ganttz.adapters.DomainDependency;
import org.zkoss.ganttz.adapters.IAdapterToTaskFundamentalProperties;
import org.zkoss.ganttz.adapters.PlannerConfiguration;
import org.zkoss.ganttz.data.ConstraintCalculator;
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.Label;
import org.zkoss.zul.Messagebox;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import static org.libreplan.business.planner.limiting.entities.LimitingResourceQueueDependency.toQueueDependencyType;
import static org.libreplan.web.I18nHelper._;
/**
* Builds a command that saves the changes in the taskElements.
* It can be considered the final step in the conversation.
* <br />
* In the save operation it is also kept the consistency of the
* LimitingResourceQueueDependencies with the Dependecies between the task of the planning gantt.
*
* @author Óscar González Fernández <ogonzalez@igalia.com>
* @author Javier Moran Rua <jmoran@igalia.com>
* @author Manuel Rego Casasnovas <rego@igalia.com>
* @author Vova Perebykivskyi <vova@libreplan-enterprise.com>
*/
@Component
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class SaveCommandBuilder {
private static final Log LOG = LogFactory.getLog(SaveCommandBuilder.class);
public static TaskPropertiesController taskPropertiesController;
@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;
@Autowired
private ISubcontractedTaskDataDAO subcontractedTaskDataDAO;
@Autowired
private ILimitingResourceQueueDependencyDAO limitingResourceQueueDependencyDAO;
@Autowired
private IAdHocTransactionService transactionService;
@Autowired
private IOrderAuthorizationDAO orderAuthorizationDAO;
@Autowired
private IDependencyDAO dependencyDAO;
@Autowired
private ISumChargedEffortRecalculator sumChargedEffortRecalculator;
@Autowired
private ISumExpensesRecalculator sumExpensesRecalculator;
public ISaveCommand build(PlanningState planningState, PlannerConfiguration<TaskElement> plannerConfiguration) {
SaveCommand result = new SaveCommand(planningState, plannerConfiguration);
return ConcurrentModificationHandling.addHandling(
"/planner/index.zul;company_scheduling", ISaveCommand.class, result);
}
public static void dontPoseAsTransientAndChildrenObjects(
Collection<? extends ResourceAllocation<?>> resourceAllocations) {
for (ResourceAllocation<?> each : resourceAllocations) {
each.dontPoseAsTransientObjectAnymore();
each.makeAssignmentsContainersDontPoseAsTransientAnyMore();
for (DayAssignment eachAssignment : each.getAssignments()) {
eachAssignment.dontPoseAsTransientObjectAnymore();
}
for (DerivedAllocation eachDerived : each.getDerivedAllocations()) {
eachDerived.dontPoseAsTransientObjectAnymore();
Collection<DerivedDayAssignmentsContainer> containers = eachDerived.getContainers();
for (DerivedDayAssignmentsContainer eachContainer : containers) {
eachContainer.dontPoseAsTransientObjectAnymore();
}
for (DerivedDayAssignment eachAssignment : eachDerived.getAssignments()) {
eachAssignment.dontPoseAsTransientObjectAnymore();
}
}
dontPoseAsTransient(each.getLimitingResourceQueueElement());
}
}
private static void dontPoseAsTransient(LimitingResourceQueueElement element) {
if ( element != null ) {
for (LimitingResourceQueueDependency d : element.getDependenciesAsOrigin()) {
d.dontPoseAsTransientObjectAnymore();
}
for (LimitingResourceQueueDependency d : element.getDependenciesAsDestiny()) {
d.dontPoseAsTransientObjectAnymore();
}
element.dontPoseAsTransientObjectAnymore();
}
}
private class SaveCommand implements ISaveCommand {
private PlanningState state;
private PlannerConfiguration<TaskElement> configuration;
private ConstraintCalculator<TaskElement> constraintCalculator;
private IAdapterToTaskFundamentalProperties<TaskElement> adapter;
private final List<IAfterSaveListener> listeners = new ArrayList<>();
private boolean disabled = false;
public SaveCommand(PlanningState planningState, PlannerConfiguration<TaskElement> configuration) {
this.state = planningState;
this.configuration = configuration;
this.adapter = configuration.getAdapter();
this.constraintCalculator = new ConstraintCalculator<TaskElement>(configuration.isScheduleBackwards()) {
@Override
protected GanttDate getStartDate(TaskElement vertex) {
return TaskElementAdapter.toGantt(vertex.getIntraDayStartDate());
}
@Override
protected GanttDate getEndDate(TaskElement vertex) {
return TaskElementAdapter.toGantt(vertex.getIntraDayEndDate());
}
};
}
@Override
public void doAction(IContext<TaskElement> context) {
if ( disabled ) {
return;
}
save(null, () -> {
// A little bit hack
if ( taskPropertiesController != null )
taskPropertiesController.emailNotificationAddNew();
notifyUserThatSavingIsDone();
});
}
@Override
public void save(IBeforeSaveActions beforeSaveActions) {
save(beforeSaveActions, () -> notifyUserThatSavingIsDone());
}
@Override
public void save(final IBeforeSaveActions beforeSaveActions, IAfterSaveActions afterSaveActions) {
try {
if ( state.getScenarioInfo().isUsingTheOwnerScenario() || userAcceptsCreateANewOrderVersion() ) {
transactionService.runOnTransaction((IOnTransaction<Void>) () -> {
if (beforeSaveActions != null) {
beforeSaveActions.doActions();
}
doTheSaving();
return null;
});
dontPoseAsTransientObjectAnymore(state.getOrder());
dontPoseAsTransientObjectAnymore(state.getOrder().getEndDateCommunicationToCustomer());
state.getScenarioInfo().afterCommit();
if ( state.getOrder().isNeededToRecalculateSumChargedEfforts() )
sumChargedEffortRecalculator.recalculate(state.getOrder().getId());
if ( state.getOrder().isNeededToRecalculateSumExpenses() )
sumExpensesRecalculator.recalculate(state.getOrder().getId());
fireAfterSave();
if ( afterSaveActions != null )
afterSaveActions.doActions();
}
} catch (ValidationException validationException) {
if ( Executions.getCurrent() == null )
throw validationException;
StringBuilder message = new StringBuilder();
LabelCreatorForInvalidValues labelCreator = new LabelCreatorForInvalidValues();
for (InvalidValue invalidValue : validationException.getInvalidValues()) {
message
.append("* ")
.append(((Label) labelCreator.createLabelFor(invalidValue)).getValue())
.append("\n");
}
if ( validationException.getInvalidValues().isEmpty() ) {
message.append(validationException.getMessage()) ;
}
LOG.warn("Error saving the project", validationException);
Messagebox.show(
_("Error saving the project\n{0}", message.toString()),
_("Error"), Messagebox.OK, Messagebox.ERROR);
}
}
private void fireAfterSave() {
for (IAfterSaveListener listener : listeners) {
listener.onAfterSave();
}
}
private void notifyUserThatSavingIsDone() {
if ( Executions.getCurrent() == null ) {
// Test environment
return;
}
Messagebox.show(_("Project saved"), _("Information"), Messagebox.OK, Messagebox.INFORMATION);
if ( Executions.getCurrent() != null ) {
// Reset timer of warning on leaving page
ConfirmCloseUtil.resetConfirmClose();
if ( SecurityUtils.loggedUserCanWrite(state.getOrder()) )
ConfirmCloseUtil.setConfirmClose(
Executions.getCurrent().getDesktop(),
_("You are about to leave the planning editing. Unsaved changes will be lost!"));
}
}
private void doTheSaving() {
Order order = state.getOrder();
generateOrderElementCodes(order);
createAdvancePercentagesIfRequired(order);
order.calculateAndSetTotalHours();
checkConstraintOrderUniqueCode(order);
checkConstraintHoursGroupUniqueCode(order);
state.synchronizeTrees();
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();
deleteUnboundedDependencies();
updateTasksRelatedData();
removeTasksToRemove();
loadDataAccessedWithNotPosedAsTransientInOrder(state.getOrder());
loadDataAccessedWithNotPosedAsTransient(state.getOrder());
if ( state.getRootTask() != null )
loadDependenciesCollectionsForTaskRoot(state.getRootTask());
subcontractedTaskDataDAO.removeOrphanedSubcontractedTaskData();
saveOrderAuthorizations();
removeTaskElementsWithTaskSourceNull();
state.updateSavedOrderState();
}
private void removeTaskElementsWithTaskSourceNull() {
List<TaskElement> toRemove = taskElementDAO.getTaskElementsNoMilestonesWithoutTaskSource();
List<TaskElement> parentsWithChangesToSave = new ArrayList<>();
for (TaskElement taskElement : toRemove) {
try {
taskElementDAO.remove(taskElement.getId());
TaskGroup parent = taskElement.getParent();
if (parent != null && !toRemove.contains(parent)) {
parent.remove(taskElement);
parentsWithChangesToSave.add(parent);
}
LOG.info("TaskElement removed because of TaskSource was null. " + taskElement);
} catch (InstanceNotFoundException ignored) {
// Do nothing
// Maybe it was already removed before reaching this point
// so if it's not in the database there isn't any problem
}
}
for (TaskElement taskElement : parentsWithChangesToSave) {
taskElementDAO.save(taskElement);
}
}
private void saveOrderAuthorizations() {
for (OrderAuthorization each : state.getOrderAuthorizationsAddition()) {
orderAuthorizationDAO.save(each);
}
for (OrderAuthorization each : state.getOrderAuthorizationsRemoval()) {
try {
orderAuthorizationDAO.remove(each.getId());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
state.cleanOrderAuthorizationsAdditionAndRemoval();
}
private void createAdvancePercentagesIfRequired(Order order) {
List<OrderElement> allChildren = order.getAllChildren();
for (OrderElement each : allChildren) {
createAdvancePercentageIfRequired(each);
}
}
private void createAdvancePercentageIfRequired(OrderElement orderElement) {
DirectAdvanceAssignment advancePercentage =
orderElement.getDirectAdvanceAssignmentByType(PredefinedAdvancedTypes.PERCENTAGE.getType());
if ( (orderElement.isSchedulingPoint()) &&
(orderElement.getReportGlobalAdvanceAssignment() == null) &&
(advancePercentage == null) )
createAdvancePercentage(orderElement);
}
private void createAdvancePercentage(OrderElement orderElement) {
DirectAdvanceAssignment newAdvance = DirectAdvanceAssignment.create();
newAdvance.setOrderElement(orderElement);
AdvanceType type = PredefinedAdvancedTypes.PERCENTAGE.getType();
newAdvance.setAdvanceType(type);
newAdvance.setMaxValue(type.getDefaultMaxValue());
newAdvance.setReportGlobalAdvance(true);
try {
orderElement.addAdvanceAssignment(newAdvance);
} catch (DuplicateValueTrueReportGlobalAdvanceException e) {
// This shouldn't happen
throw new RuntimeException(e);
} catch (DuplicateAdvanceAssignmentForOrderElementException ignored) {
// Do nothing.
// This means that some parent has already defined an advance
// percentage so we don't need to create it at this point
}
}
private void generateOrderElementCodes(Order order) {
order.generateOrderElementCodes(entitySequenceDAO.getNumberOfDigitsCode(EntityNameEnum.ORDER));
}
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 != null) {
repeatedHoursGroup = (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<Scenario> derivedScenarios = scenarioDAO.getDerivedScenarios(state.getCurrentScenario());
for (Scenario scenario : derivedScenarios) {
scenario.addOrder(order, order.getCurrentOrderVersion());
}
}
private void deleteOrderElementWithoutParent() throws ValidationException {
List<OrderElement> listToBeRemoved = orderElementDAO.findWithoutParent();
for (OrderElement orderElement : listToBeRemoved) {
if (!(orderElement instanceof Order))
tryToRemove(orderElement);
}
}
private void deleteUnboundedDependencies() {
try {
dependencyDAO.deleteUnattachedDependencies();
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
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())) {
// It might have already been saved in a previous save action
try {
taskElementDAO.remove(taskElement.getId());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
}
}
private void updateTasksRelatedData() {
TaskGroup rootTask = state.getRootTask();
if (rootTask == null)
return;
for (TaskElement taskElement : rootTask.getChildren()) {
removeEmptyConsolidation(taskElement);
updateLimitingResourceQueueElementDates(taskElement);
if (taskElement.getTaskSource() != null && taskElement.getTaskSource().isNewObject())
saveTaskSources(taskElement);
updateLimitingQueueDependencies(taskElement);
}
saveRootTask();
}
private void saveRootTask() {
TaskGroup rootTask = state.getRootTask();
updateRootTaskPosition(rootTask);
taskElementDAO.save(rootTask);
}
private void updateRootTaskPosition(TaskGroup rootTask) {
final IntraDayDate min = TaskElement.minDate(rootTask.getChildren());
if (min != null)
rootTask.setIntraDayStartDate(min);
final IntraDayDate max = TaskElement.maxDate(rootTask.getChildren());
if (max != null)
rootTask.setIntraDayEndDate(max);
}
private void saveTaskSources(TaskElement taskElement) {
TaskSource taskSource = taskElement.getTaskSource();
if (taskSource != null)
taskSourceDAO.save(taskSource);
if (taskElement.isLeaf())
return;
for (TaskElement each : taskElement.getChildren()) {
saveTaskSources(each);
}
}
private void updateLimitingResourceQueueElementDates(TaskElement taskElement) {
if (taskElement.isLimiting()) {
Task task = (Task) taskElement;
updateLimitingResourceQueueElementDates(task);
} else if (!taskElement.isLeaf()) {
for (TaskElement each : taskElement.getChildren()) {
updateLimitingResourceQueueElementDates(each);
}
}
}
private void updateLimitingResourceQueueElementDates(Task task) {
try {
LimitingResourceQueueElement limiting = task.getAssociatedLimitingResourceQueueElementIfAny();
GanttDate earliestStart = resolveConstraints(task, Point.START);
GanttDate earliestEnd = resolveConstraints(task, Point.END);
limiting.updateDates(
TaskElementAdapter.toIntraDay(earliestStart),
TaskElementAdapter.toIntraDay(earliestEnd));
} catch (Exception e) {
// If this fails all the saving shouldn't fail
LOG.error("error updating associated LimitingResourceQueueElement for task: " + task, e);
}
}
private GanttDate resolveConstraints(Task task, Point point) {
List<Constraint<GanttDate>> dependencyConstraints =
toConstraints(adapter.getIncomingDependencies(task), point);
List<Constraint<GanttDate>> taskConstraints = getTaskConstraints(task);
boolean dependenciesHavePriority = configuration.isDependenciesConstraintsHavePriority();
if (dependenciesHavePriority) {
return Constraint
.initialValue(TaskElementAdapter.toGantt(getOrderInitDate()))
.withConstraints(taskConstraints)
.withConstraints(dependencyConstraints)
.applyWithoutFinalCheck();
} else {
return Constraint
.initialValue(TaskElementAdapter.toGantt(getOrderInitDate()))
.withConstraints(dependencyConstraints)
.withConstraints(taskConstraints)
.applyWithoutFinalCheck();
}
}
private List<Constraint<GanttDate>> getTaskConstraints(Task task) {
return TaskElementAdapter.getStartConstraintsFor(task, getOrderInitDate());
}
private LocalDate getOrderInitDate() {
return LocalDate.fromDateFields(state.getRootTask().getOrderElement().getInitDate());
}
private List<Constraint<GanttDate>> toConstraints(
List<DomainDependency<TaskElement>> incomingDependencies, Point point) {
List<Constraint<GanttDate>> result = new ArrayList<>();
for (DomainDependency<TaskElement> each : incomingDependencies) {
result.addAll(constraintCalculator.getConstraints(each, point));
}
return result;
}
private void removeEmptyConsolidation(TaskElement taskElement) {
if ((taskElement.isLeaf()) && (!taskElement.isMilestone())) {
Consolidation consolidation = ((Task) taskElement).getConsolidation();
if ((consolidation != null) && (isEmptyConsolidation(consolidation))) {
if (!consolidation.isNewObject()) {
try {
consolidationDAO.remove(consolidation.getId());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
((Task) taskElement).setConsolidation(null);
}
}
}
private boolean isEmptyConsolidation(final Consolidation consolidation) {
return transactionService.runOnTransaction(() -> {
consolidationDAO.reattach(consolidation);
if (consolidation instanceof CalculatedConsolidation) {
SortedSet<CalculatedConsolidatedValue> consolidatedValues =
((CalculatedConsolidation) consolidation).getCalculatedConsolidatedValues();
return consolidatedValues.isEmpty();
}
if (consolidation instanceof NonCalculatedConsolidation) {
SortedSet<NonCalculatedConsolidatedValue> consolidatedValues =
((NonCalculatedConsolidation) consolidation).getNonCalculatedConsolidatedValues();
return consolidatedValues.isEmpty();
}
return false;
});
}
private void updateLimitingQueueDependencies(TaskElement t) {
for (Dependency each : t.getDependenciesWithThisOrigin()) {
addLimitingDependencyIfNeeded(each);
removeLimitingDependencyIfNeeded(each);
}
for (TaskElement each : t.getChildren()) {
updateLimitingQueueDependencies(each);
}
}
private void addLimitingDependencyIfNeeded(Dependency d) {
if (d.isDependencyBetweenLimitedAllocatedTasks() && !d.hasLimitedQueueDependencyAssociated()) {
LimitingResourceQueueElement origin = calculateQueueElementFromDependency((Task) d.getOrigin());
LimitingResourceQueueElement destiny = calculateQueueElementFromDependency((Task) d.getDestination());
LimitingResourceQueueDependency queueDependency =
LimitingResourceQueueDependency.create(origin, destiny, d, toQueueDependencyType(d.getType()));
d.setQueueDependency(queueDependency);
limitingResourceQueueDependencyDAO.save(queueDependency);
}
}
private LimitingResourceQueueElement calculateQueueElementFromDependency(Task t) {
LimitingResourceQueueElement result = null;
// TODO: Improve this method: One Task can only have one limiting resource allocation
Set<ResourceAllocation<?>> allocations = t.getLimitingResourceAllocations();
if (allocations.isEmpty() || allocations.size() != 1)
throw new ValidationException("Incorrect limiting resource " + "allocation configuration");
for (ResourceAllocation<?> r : allocations) {
result = r.getLimitingResourceQueueElement();
}
return result;
}
private void removeLimitingDependencyIfNeeded(Dependency d) {
if (!d.isDependencyBetweenLimitedAllocatedTasks() && (d.hasLimitedQueueDependencyAssociated())) {
LimitingResourceQueueDependency queueDependency = d.getQueueDependency();
queueDependency.getHasAsOrigin().remove(queueDependency);
queueDependency.getHasAsDestiny().remove(queueDependency);
d.setQueueDependency(null);
try {
limitingResourceQueueDependencyDAO.remove(queueDependency.getId());
} catch (InstanceNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("Trying to delete instance " + " does not exist");
}
}
}
private void loadDataAccessedWithNotPosedAsTransientInOrder(Order order) {
order.getEndDateCommunicationToCustomer().size();
}
private void loadDataAccessedWithNotPosedAsTransient(OrderElement orderElement) {
orderElement.getDirectAdvanceAssignments().size();
getAllMeasurements(orderElement.getDirectAdvanceAssignments());
orderElement.getIndirectAdvanceAssignments().size();
orderElement.getCriterionRequirements().size();
orderElement.getLabels().size();
orderElement.getTaskQualityForms().size();
orderElement.getAllMaterialAssignments().size();
for (HoursGroup hoursGroup : orderElement.getHoursGroups()) {
dontPoseAsTransientObjectAnymore(hoursGroup.getCriterionRequirements());
}
for (OrderElement each : orderElement.getChildren()) {
loadDataAccessedWithNotPosedAsTransient(each);
}
}
/**
* Avoid LazyInitializationException when forcing the don't pose as transient.
*/
private void loadDependenciesCollectionsForTaskRoot(TaskElement taskElement) {
taskElement.getDependenciesWithThisOrigin().size();
taskElement.getDependenciesWithThisDestination().size();
}
@Override
public String getName() {
return _("Save");
}
@Override
public void addListener(IAfterSaveListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(IAfterSaveListener listener) {
listeners.remove(listener);
}
@Override
public String getImage() {
return "/common/img/ico_save.png";
}
private boolean userAcceptsCreateANewOrderVersion() {
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;
}
private void dontPoseAsTransientObjectAnymore(OrderElement orderElement) {
orderElement.dontPoseAsTransientObjectAnymore();
dontPoseAsTransientObjectAnymore(orderElement.getOrderVersions());
dontPoseAsTransientObjectAnymore(orderElement.getTaskSourcesFromBottomToTop());
dontPoseAsTransientObjectAnymore(orderElement.getSchedulingDataForVersionFromBottomToTop());
dontPoseAsTransientObjectAnymore(orderElement.getDirectAdvanceAssignments());
dontPoseAsTransientObjectAnymore(getAllMeasurements(orderElement.getDirectAdvanceAssignments()));
dontPoseAsTransientObjectAnymore(orderElement.getIndirectAdvanceAssignments());
dontPoseAsTransientObjectAnymore(orderElement.getCriterionRequirements());
dontPoseAsTransientObjectAnymore(orderElement.getLabels());
dontPoseAsTransientObjectAnymoreTasks(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 void dontPoseAsTransientObjectAnymore(Collection<? extends BaseEntity> collection) {
for (BaseEntity entity : collection) {
entity.dontPoseAsTransientObjectAnymore();
}
}
private List<AdvanceMeasurement> getAllMeasurements(Collection<? extends DirectAdvanceAssignment> assignments) {
List<AdvanceMeasurement> result = new ArrayList<>();
for (DirectAdvanceAssignment each : assignments) {
result.addAll(each.getAdvanceMeasurements());
}
return result;
}
private void dontPoseAsTransientObjectAnymoreTasks(Collection<? extends TaskElement> taskElements) {
for (TaskElement each : taskElements)
dontPoseAsTransient(each);
}
private void dontPoseAsTransient(TaskElement taskElement) {
if (taskElement.isNewObject())
taskElement.dontPoseAsTransientObjectAnymore();
dontPoseAsTransient(taskElement.getDependenciesWithThisOrigin());
dontPoseAsTransient(taskElement.getDependenciesWithThisDestination());
Set<ResourceAllocation<?>> resourceAllocations = taskElement.getAllResourceAllocations();
dontPoseAsTransientAndChildrenObjects(resourceAllocations);
if (!taskElement.isLeaf()) {
for (TaskElement each : taskElement.getChildren())
dontPoseAsTransient(each);
}
if (taskElement instanceof Task) {
dontPoseAsTransient(((Task) taskElement).getConsolidation());
dontPoseAsTransient(((Task) taskElement).getSubcontractedTaskData());
}
if (taskElement instanceof TaskGroup)
((TaskGroup) taskElement).dontPoseAsTransientPlanningData();
}
private void dontPoseAsTransient(Consolidation consolidation) {
if (consolidation != null) {
consolidation.dontPoseAsTransientObjectAnymore();
if (consolidation.isCalculated())
dontPoseAsTransient(((CalculatedConsolidation) consolidation).getCalculatedConsolidatedValues());
else
dontPoseAsTransient(((NonCalculatedConsolidation) consolidation).getNonCalculatedConsolidatedValues());
}
}
private void dontPoseAsTransient(SubcontractedTaskData subcontractedTaskData) {
if (subcontractedTaskData != null) {
// dontPoseAsTransient - subcontractedTaskData
subcontractedTaskData.dontPoseAsTransientObjectAnymore();
for (SubcontractorDeliverDate subDeliverDate : subcontractedTaskData.getRequiredDeliveringDates()) {
// dontPoseAsTransient - DeliverDate
subDeliverDate.dontPoseAsTransientObjectAnymore();
}
}
}
private void dontPoseAsTransient(SortedSet<? extends ConsolidatedValue> values) {
for (ConsolidatedValue value : values)
value.dontPoseAsTransientObjectAnymore();
}
private void dontPoseAsTransient(Collection<? extends Dependency> dependencies) {
for (Dependency each : dependencies) {
each.dontPoseAsTransientObjectAnymore();
if (each.hasLimitedQueueDependencyAssociated())
each.getQueueDependency().dontPoseAsTransientObjectAnymore();
}
}
@Override
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
@Override
public boolean isDisabled() {
return disabled;
}
@Override
public boolean isPlannerCommand() {
return false;
}
}
private static final class LabelCreatorForInvalidValues implements IMessagesForUser.ICustomLabelCreator {
@Override
public org.zkoss.zk.ui.Component createLabelFor(InvalidValue invalidValue) {
if ( invalidValue.getRootBean() instanceof OrderElement ) {
Label result = new Label();
String orderElementName;
if ( invalidValue.getRootBean() instanceof Order )
orderElementName = _("Project");
else
orderElementName = _("Task {0}", ((OrderElement) invalidValue.getRootBean()).getName());
result.setValue(orderElementName + ": " + _(invalidValue.getMessage()));
return result;
} else if (invalidValue.getRootBean() instanceof HoursGroup) {
Label result = new Label();
HoursGroup hoursGroup = (HoursGroup) invalidValue.getRootBean();
result.setValue(
_("Hours Group at {0}", getParentName(hoursGroup)) + ": " + _(invalidValue.getMessage()));
return result;
} else {
return MessagesForUser.createLabelFor(invalidValue);
}
}
private String getParentName(HoursGroup hoursGroup) {
return (hoursGroup.getParentOrderLine() != null)
? hoursGroup.getParentOrderLine().getName()
: hoursGroup.getOrderLineTemplate().getName();
}
}
}