diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java b/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java index 5c365f268..df53c97ff 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java @@ -83,7 +83,7 @@ public class CommandOnTaskContextualized { return new ItemAction() { @Override public void onEvent(TaskComponent choosen, Event event) { - doAction(choosen.getTask()); + doAction(choosen); } }; } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java b/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java index c3170884e..7dda488ee 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java @@ -57,9 +57,20 @@ public class DatesMapperOnInterval implements IDatesMapper { return this.toPixels(date); } + @Override + public int toPixelsAbsolute(long milliseconds) { + Date date = new Date(milliseconds); + return this.toPixels(date); + } + @Override public long toMilliseconds(int pixels) { return millisecondsPerPixel * pixels; } + @Override + public long getMilisecondsPerPixel() { + return millisecondsPerPixel; + } + } \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java b/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java index 9ab7cbcc7..8f78f770b 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java @@ -45,6 +45,7 @@ import org.zkoss.ganttz.data.Position; import org.zkoss.ganttz.data.Task; import org.zkoss.ganttz.data.TaskContainer; import org.zkoss.ganttz.data.TaskLeaf; +import org.zkoss.ganttz.data.GanttDiagramGraph.GanttZKDiagramGraph; import org.zkoss.ganttz.data.criticalpath.CriticalPathCalculator; import org.zkoss.ganttz.extensions.IContext; import org.zkoss.ganttz.timetracker.TimeTracker; @@ -166,13 +167,13 @@ public class FunctionalityExposedForExtensions implements IContext { private final IAdapterToTaskFundamentalProperties adapter; private final IStructureNavigator navigator; private final OneToOneMapper mapper = new OneToOneMapper(); - private final GanttDiagramGraph diagramGraph; + private final GanttZKDiagramGraph diagramGraph; private TimeTracker timeTracker; private final PlannerConfiguration configuration; public FunctionalityExposedForExtensions(Planner planner, PlannerConfiguration configuration, - GanttDiagramGraph diagramGraph) { + GanttZKDiagramGraph diagramGraph) { this.planner = planner; this.configuration = configuration; this.adapter = configuration.getAdapter(); @@ -313,7 +314,7 @@ public class FunctionalityExposedForExtensions implements IContext { add(position, newDomainObject); } - public GanttDiagramGraph getDiagramGraph() { + public GanttZKDiagramGraph getDiagramGraph() { return diagramGraph; } @@ -351,7 +352,7 @@ public class FunctionalityExposedForExtensions implements IContext { public void removeDependency(Dependency dependency) { adapter.removeDependency(toDomainDependency(dependency)); - diagramGraph.remove(dependency); + diagramGraph.removeDependency(dependency); getDependencyList().remove(dependency); } @@ -478,4 +479,9 @@ public class FunctionalityExposedForExtensions implements IContext { } } + @Override + public GanttDiagramGraph getGanttDiagramGraph() { + return diagramGraph; + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java b/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java index 29fbdc30e..3ad132c8b 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java @@ -24,6 +24,8 @@ import java.util.Date; public interface IDatesMapper { + final long MILISECONDS_PER_HOUR = 3600000; + int toPixels(Date date); Date toDate(int pixel); @@ -32,4 +34,8 @@ public interface IDatesMapper { long toMilliseconds(int pixels); + int toPixelsAbsolute(long milliseconds); + + long getMilisecondsPerPixel(); + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java b/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java index fda8a090a..cf65c7aa9 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java @@ -389,7 +389,8 @@ public class LeftTasksTreeRow extends GenericForwardComposer { private boolean canChangeStartDate() { return disabilityConfiguration.isMovingTasksEnabled() - && task.canBeExplicitlyMoved(); + && task.canBeExplicitlyMoved() + && !task.isLimitingAndHasDayAssignments(); } private boolean canChangeEndDate() { diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java index 2588fce4a..cfcc09f70 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java @@ -38,6 +38,7 @@ import org.zkoss.ganttz.data.Dependency; import org.zkoss.ganttz.data.GanttDiagramGraph; import org.zkoss.ganttz.data.Position; import org.zkoss.ganttz.data.Task; +import org.zkoss.ganttz.data.GanttDiagramGraph.GanttZKDiagramGraph; import org.zkoss.ganttz.data.GanttDiagramGraph.IGraphChangeListener; import org.zkoss.ganttz.extensions.ICommand; import org.zkoss.ganttz.extensions.ICommandOnTask; @@ -111,7 +112,7 @@ public class Planner extends HtmlMacroComponent { return result; } - private GanttDiagramGraph diagramGraph; + private GanttZKDiagramGraph diagramGraph; private LeftPane leftPane; @@ -257,11 +258,14 @@ public class Planner extends HtmlMacroComponent { return; } - this.diagramGraph = new GanttDiagramGraph(configuration - .getStartConstraints(), configuration.getEndConstraints(), - configuration.isDependenciesConstraintsHavePriority()); + this.diagramGraph = GanttDiagramGraph.create(configuration + .getStartConstraints(), configuration.getEndConstraints(), configuration.isDependenciesConstraintsHavePriority()); FunctionalityExposedForExtensions newContext = new FunctionalityExposedForExtensions( this, configuration, diagramGraph); + diagramGraph.addPreChangeListeners(configuration + .getPreChangeListeners()); + diagramGraph.addPostChangeListeners(configuration + .getPostChangeListeners()); this.contextualizedGlobalCommands = contextualize(newContext, configuration.getGlobalCommands()); this.commandsOnTasksContextualized = contextualize(newContext, diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java b/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java index 03ca5a742..39b683c2d 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java @@ -183,24 +183,8 @@ public class TaskComponent extends Div implements AfterCompose { setHeight(HEIGHT_PER_TASK + "px"); setContext("idContextMenuTaskAssignment"); this.task = task; - String cssClass = ""; - if (task.isSubcontracted()) { - cssClass = "box subcontracted-task"; - } else { - if (task.isContainer()) { - if (task.isExpanded()) { - cssClass = "box standard-task expanded"; - } else { - cssClass = "box standard-task closed"; - } - } else { - cssClass = "box standard-task"; - } + setClass(calculateCSSClass()); - cssClass += " " + getTask().getAssignedStatus(); - - setClass(cssClass); - } setId(UUID.randomUUID().toString()); this.disabilityConfiguration = disabilityConfiguration; taskViolationListener = new IConstraintViolationListener() { @@ -219,64 +203,37 @@ public class TaskComponent extends Div implements AfterCompose { if (canShowResourcesText()) { smartUpdate("resourcesText", getResourcesText()); } - String cssClass = calculateCssClass(); + String cssClass = calculateCSSClass(); response("setClass", new AuInvoke(TaskComponent.this, "setClass", cssClass)); } - private String calculateCssClass() { - String cssClass = (isSubcontracted() ? "box subcontracted-task" - : "box standard-task") - + (isResizingTasksEnabled() ? " yui-resize" : ""); - - if (getTask() instanceof TaskContainer) { - if (getTask().isExpanded()) { - cssClass += " expanded"; - } else { - cssClass += " closed"; - } - } - - cssClass += " " + getTask().getAssignedStatus(); - - return cssClass; - } - }; this.task.addReloadListener(reloadResourcesTextRequested); } - /** - * Note: This method is intended to be overridden. - */ - protected boolean canShowResourcesText() { - return true; + /* Generate CSS class attribute depending on task properties */ + protected String calculateCSSClass() { + String cssClass = isSubcontracted() ? "box subcontracted-task" + : "box standard-task"; + cssClass += isResizingTasksEnabled() ? " yui-resize" : ""; + if (isContainer()) { + cssClass += task.isExpanded() ? " expanded" : " closed "; + } + cssClass += task.isInCriticalPath() ? " critical" : ""; + cssClass += " " + task.getAssignedStatus(); + if (task.isLimiting()) { + cssClass += task.isLimitingAndHasDayAssignments() ? " limiting-assigned " + : " limiting-unassigned "; + } + return cssClass; } - protected String calculateClass() { - String classText; - - if (getSclass() == null || getSclass().equals("null")) { - classText = "box"; - } else { - classText = getSclass(); - } - if (task.isInCriticalPath()) { - classText += " critical"; - } - if (task.isSubcontracted()) { - classText += " subcontracted-task"; - } - - classText += " " + getTask().getAssignedStatus(); - - return classText; - } protected void updateClass() { response(null, new AuInvoke(this, "setClass", - new Object[] { calculateClass() })); + new Object[] { calculateCSSClass() })); } public final void afterCompose() { @@ -311,6 +268,13 @@ public class TaskComponent extends Div implements AfterCompose { updateClass(); } + /** + * Note: This method is intended to be overridden. + */ + protected boolean canShowResourcesText() { + return true; + } + private String _color; private boolean isTopLevel; @@ -356,7 +320,8 @@ public class TaskComponent extends Div implements AfterCompose { } public boolean isResizingTasksEnabled() { - return disabilityConfiguration.isResizingTasksEnabled() + return (disabilityConfiguration != null) + && disabilityConfiguration.isResizingTasksEnabled() && !task.isSubcontracted() && task.canBeExplicitlyResized(); } @@ -366,7 +331,13 @@ public class TaskComponent extends Div implements AfterCompose { } void doUpdatePosition(String leftX, String topY) { + Date startBeforeMoving = this.task.getBeginDate(); this.task.moveTo(getMapper().toDate(stripPx(leftX))); + boolean remainsInOriginalPosition = this.task.getBeginDate().equals( + startBeforeMoving); + if (remainsInOriginalPosition) { + updateProperties(); + } } void doUpdateSize(String size) { @@ -550,6 +521,10 @@ public class TaskComponent extends Div implements AfterCompose { return task.isSubcontracted(); } + public boolean isContainer() { + return task.isContainer(); + } + @Override public String toString() { return task.toString(); diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/TaskContainerComponent.java b/ganttzk/src/main/java/org/zkoss/ganttz/TaskContainerComponent.java index 2e55b33e3..143593206 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/TaskContainerComponent.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/TaskContainerComponent.java @@ -112,8 +112,8 @@ public class TaskContainerComponent extends TaskComponent implements } @Override - protected String calculateClass() { - return super.calculateClass() + " " + protected String calculateCSSClass() { + return super.calculateCSSClass() + " " + (getTaskContainer().isExpanded() ? "expanded" : "closed"); } } \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java index 7de0c65a9..4eb8e684d 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java @@ -29,6 +29,7 @@ import java.util.Map; import org.apache.commons.lang.Validate; import org.zkoss.ganttz.Planner; +import org.zkoss.ganttz.data.GanttDiagramGraph.IGraphChangeListener; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.data.constraint.DateConstraint; import org.zkoss.ganttz.extensions.ICommand; @@ -150,6 +151,10 @@ public class PlannerConfiguration implements IDisabilityConfiguration { private boolean expandPlanningViewCharts; + private final List preGraphChangeListeners = new ArrayList(); + + private final List postGraphChangeListeners = new ArrayList(); + public PlannerConfiguration(IAdapterToTaskFundamentalProperties adapter, IStructureNavigator navigator, List data) { this.adapter = adapter; @@ -380,4 +385,24 @@ public class PlannerConfiguration implements IDisabilityConfiguration { return expandPlanningViewCharts; } + public void addPreGraphChangeListener( + IGraphChangeListener preGraphChangeListener) { + Validate.notNull(preGraphChangeListener); + preGraphChangeListeners.add(preGraphChangeListener); + } + + public void addPostGraphChangeListener( + IGraphChangeListener postGraphChangeListener) { + Validate.notNull(postGraphChangeListener); + postGraphChangeListeners.add(postGraphChangeListener); + } + + public List getPreChangeListeners() { + return Collections.unmodifiableList(preGraphChangeListeners); + } + + public List getPostChangeListeners() { + return Collections.unmodifiableList(postGraphChangeListeners); + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/DefaultFundamentalProperties.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/DefaultFundamentalProperties.java index dc569d8bc..08f18bc5f 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/DefaultFundamentalProperties.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/DefaultFundamentalProperties.java @@ -61,7 +61,7 @@ public class DefaultFundamentalProperties implements ITaskFundamentalProperties long lengthMilliseconds, String notes, Date hoursAdvanceEndDate, Date advanceEndDate, - BigDecimal hoursAdvancePercentage, BigDecimal advancePercentage) { + BigDecimal hoursAdvancePercentage, BigDecimal advancePercentage) { this.name = name; this.beginDate = beginDate.getTime(); this.lengthMilliseconds = lengthMilliseconds; @@ -167,11 +167,23 @@ public class DefaultFundamentalProperties implements ITaskFundamentalProperties return false; } + public boolean isLimiting() { + return false; + } + + public boolean isLimitingAndHasDayAssignments() { + return false; + } + @Override public boolean canBeExplicitlyResized() { return true; } + public boolean hasConsolidations() { + return false; + } + @Override public String getAssignedStatus() { return "unassigned"; diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/Dependency.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/Dependency.java index fa7f2f506..f6d297a80 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/Dependency.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/Dependency.java @@ -27,6 +27,7 @@ import java.util.List; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; +import org.zkoss.ganttz.data.GanttDiagramGraph.GanttZKAdapter; import org.zkoss.ganttz.data.GanttDiagramGraph.TaskPoint; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.data.constraint.Constraint.IConstraintViolationListener; @@ -167,8 +168,9 @@ public class Dependency implements IDependency { return new Dependency(source, destination, type, visible); } - public TaskPoint getDestinationPoint() { - return new TaskPoint(destination, type.getPointModified()); + public TaskPoint getDestinationPoint() { + return new TaskPoint(new GanttZKAdapter(), + destination, type.getPointModified()); } } \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/GanttDiagramGraph.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/GanttDiagramGraph.java index 6aae85a39..64e987634 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/GanttDiagramGraph.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/GanttDiagramGraph.java @@ -20,21 +20,22 @@ package org.zkoss.ganttz.data; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Queue; import java.util.Set; -import java.util.WeakHashMap; +import org.apache.commons.lang.Validate; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.logging.Log; @@ -43,8 +44,8 @@ import org.jgrapht.DirectedGraph; import org.jgrapht.graph.SimpleDirectedGraph; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.data.criticalpath.ICriticalPathCalculable; +import org.zkoss.ganttz.util.IAction; import org.zkoss.ganttz.util.PreAndPostNotReentrantActionsWrapper; -import org.zkoss.ganttz.util.PreAndPostNotReentrantActionsWrapper.IAction; /** * This class contains a graph with the {@link Task tasks} as vertexes and the @@ -52,29 +53,209 @@ import org.zkoss.ganttz.util.PreAndPostNotReentrantActionsWrapper.IAction; * dependencies and in the duration of the tasks using listeners.
* @author Óscar González Fernández */ -public class GanttDiagramGraph implements ICriticalPathCalculable { +public class GanttDiagramGraph { private static final Log LOG = LogFactory.getLog(GanttDiagramGraph.class); + public interface IAdapter { + List getChildren(V task); + + boolean isContainer(V task); + + void registerDependenciesEnforcerHookOn(V task, + IDependenciesEnforcerHookFactory hookFactory); + + Date getStartDate(V task); + + void setStartDateFor(V task, Date newStart); + + Date getEndDateFor(V task); + + void setEndDateFor(V task, Date newEnd); + + Date getSmallestBeginDateFromChildrenFor(V container); + + Constraint getCurrentLenghtConstraintFor(V task); + + Constraint getEndDateBiggerThanStartDateConstraintFor(V task); + + List> getConstraintsGivenIncoming(Set incoming); + + List> getStartCosntraintsGiven(Set withDependencies); + + List> getStartConstraintsFor(V task); + + V getSource(D dependency); + + V getDestination(D dependency); + + Class getDependencyType(); + + D createInvisibleDependency(V origin, V destination, DependencyType type); + + DependencyType getType(D dependency); + + TaskPoint getDestinationPoint(D dependency); + + boolean isVisible(D dependency); + + } + + static class GanttZKAdapter implements IAdapter { + + @Override + public List getChildren(Task task) { + return task.getTasks(); + } + + @Override + public Task getDestination(Dependency dependency) { + return dependency.getDestination(); + } + + @Override + public Task getSource(Dependency dependency) { + return dependency.getSource(); + } + + @Override + public boolean isContainer(Task task) { + return task.isContainer(); + } + + @Override + public void registerDependenciesEnforcerHookOn(Task task, + IDependenciesEnforcerHookFactory hookFactory) { + task.registerDependenciesEnforcerHook(hookFactory); + } + + @Override + public Dependency createInvisibleDependency(Task origin, + Task destination, DependencyType type) { + return new Dependency(origin, destination, type, false); + } + + @Override + public Class getDependencyType() { + return Dependency.class; + } + + @Override + public DependencyType getType(Dependency dependency) { + return dependency.getType(); + } + + @Override + public TaskPoint getDestinationPoint( + Dependency dependency) { + return dependency.getDestinationPoint(); + } + + @Override + public boolean isVisible(Dependency dependency) { + return dependency.isVisible(); + } + + @Override + public Date getEndDateFor(Task task) { + return task.getEndDate(); + } + + @Override + public Constraint getCurrentLenghtConstraintFor(Task task) { + return task.getCurrentLengthConstraint(); + } + + @Override + public Constraint getEndDateBiggerThanStartDateConstraintFor( + Task task) { + return task.getEndDateBiggerThanStartDate(); + } + + @Override + public List> getConstraintsGivenIncoming( + Set incoming) { + return Dependency.getEndConstraints(incoming); + } + + @Override + public void setEndDateFor(Task task, Date newEnd) { + task.setEndDate(newEnd); + } + + @Override + public Date getStartDate(Task task) { + return task.getBeginDate(); + } + + @Override + public void setStartDateFor(Task task, Date newStart) { + task.setBeginDate(newStart); + } + + @Override + public List> getStartCosntraintsGiven( + Set withDependencies) { + return Dependency.getStartConstraints(withDependencies); + } + + @Override + public List> getStartConstraintsFor(Task task) { + return task.getStartConstraints(); + } + + @Override + public Date getSmallestBeginDateFromChildrenFor(Task container) { + return ((TaskContainer) container).getSmallestBeginDateFromChildren(); + } + + } + + public static class GanttZKDiagramGraph extends + GanttDiagramGraph implements + ICriticalPathCalculable { + + private GanttZKDiagramGraph( + List> globalStartConstraints, + List> globalEndConstraints, + boolean dependenciesConstraintsHavePriority) { + super(new GanttZKAdapter(), globalStartConstraints, + globalEndConstraints, + dependenciesConstraintsHavePriority); + } + + } + public interface IGraphChangeListener { public void execute(); } - private final DirectedGraph graph = new SimpleDirectedGraph( - Dependency.class); + public static GanttZKDiagramGraph create( + List> globalStartConstraints, + List> globalEndConstraints, + boolean dependenciesConstraintsHavePriority) { + return new GanttZKDiagramGraph(globalStartConstraints, + globalEndConstraints, dependenciesConstraintsHavePriority); + } - private Map rulesEnforcersByTask = new HashMap(); + private final IAdapter adapter; - private Map parentShrinkingEnforcerByTask = new WeakHashMap(); + private final DirectedGraph graph; - private List topLevelTasks = new ArrayList(); + private List topLevelTasks = new ArrayList(); + + private Map fromChildToParent = new HashMap(); private final List> globalStartConstraints; private final List> globalEndConstraints; + private DependenciesEnforcer enforcer = new DependenciesEnforcer(); + private final boolean dependenciesConstraintsHavePriority; + private final ReentranceGuard positionsUpdatingGuard = new ReentranceGuard(); + private final PreAndPostNotReentrantActionsWrapper preAndPostActions = new PreAndPostNotReentrantActionsWrapper() { @Override @@ -120,385 +301,866 @@ public class GanttDiagramGraph implements ICriticalPathCalculable { postGraphChangeListeners.remove(postGraphChangeListener); } - private PropertyChangeListener decorateWithPreAndPostActions( - final PropertyChangeListener listener) { - return new PropertyChangeListener() { - - @Override - public void propertyChange(final PropertyChangeEvent evt) { - preAndPostActions.doAction(new IAction() { - @Override - public void doAction() { - listener.propertyChange(evt); - } - }); - } - }; + public void addPreChangeListeners( + Collection preChangeListeners) { + for (IGraphChangeListener each : preChangeListeners) { + addPreGraphChangeListener(each); + } } - public GanttDiagramGraph(List> globalStartConstraints, + public void addPostChangeListeners( + Collection postChangeListeners) { + for (IGraphChangeListener each : postChangeListeners) { + addPostGraphChangeListener(each); + } + } + + protected GanttDiagramGraph(IAdapter adapter, + List> globalStartConstraints, List> globalEndConstraints, boolean dependenciesConstraintsHavePriority) { + this.adapter = adapter; this.globalStartConstraints = globalStartConstraints; this.globalEndConstraints = globalEndConstraints; this.dependenciesConstraintsHavePriority = dependenciesConstraintsHavePriority; + this.graph = new SimpleDirectedGraph(adapter.getDependencyType()); } - private List getOutgoing(Task task) { - ArrayList result = new ArrayList(); - for (Dependency dependency : graph.outgoingEdgesOf(task)) { - result.add(rulesEnforcersByTask.get(dependency.getDestination())); + public void enforceAllRestrictions() { + enforcer.enforceRestrictionsOn(getTopLevelTasks()); + } + + public void addTopLevel(V task) { + topLevelTasks.add(task); + addTask(task); + } + + public void addTopLevel(Collection tasks) { + for (V task : tasks) { + addTopLevel(task); } - return result; } - private class ParentShrinkingEnforcer { + public void addTasks(Collection tasks) { + for (V t : tasks) { + addTask(t); + } + } - private final TaskContainer container; - - private final Map alreadyRegistered = new WeakHashMap(); - - private ParentShrinkingEnforcer(final TaskContainer container) { - if (container == null) { - throw new IllegalArgumentException("container cannot be null"); + public void addTask(V task) { + graph.addVertex(task); + adapter.registerDependenciesEnforcerHookOn(task, enforcer); + if (adapter.isContainer(task)) { + List dependenciesToAdd = new ArrayList(); + for (V child : adapter.getChildren(task)) { + fromChildToParent.put(child, task); + addTask(child); + dependenciesToAdd.add(adapter.createInvisibleDependency(child, task, + DependencyType.END_END)); + dependenciesToAdd.add(adapter.createInvisibleDependency(task, + child, DependencyType.START_START)); } - this.container = container; - registerListeners(); + for (D each : dependenciesToAdd) { + add(each); + } + } + } + + public interface IDependenciesEnforcerHook { + public void setStartDate(Date previousStart, long previousLength, + Date newStart); + + public void setLengthMilliseconds(long previousLengthMilliseconds, + long newLengthMilliseconds); + } + + public interface IDependenciesEnforcerHookFactory { + public IDependenciesEnforcerHook create(T task, + INotificationAfterDependenciesEnforcement notification); + } + + public interface INotificationAfterDependenciesEnforcement { + public void onStartDateChange(Date previousStart, long previousLength, + Date newStart); + + public void onLengthChange(long previousLength, long newLength); + } + + public class DeferedNotifier { + + private Map notificationsPending = new LinkedHashMap(); + + public void add(V task, StartDateNofitication notification) { + retrieveOrCreateFor(task).setStartDateNofitication(notification); } - void registerListeners() { - for (Task subtask : this.container.getTasks()) { - if (alreadyRegistered.containsKey(subtask)) { - continue; + private NotificationPendingForTask retrieveOrCreateFor(V task) { + NotificationPendingForTask result = notificationsPending.get(task); + if (result == null) { + result = new NotificationPendingForTask(); + notificationsPending.put(task, result); + } + return result; + } + + void add(V task, LengthNotification notification) { + retrieveOrCreateFor(task).setLengthNofitication(notification); + } + + public void doNotifications() { + for (NotificationPendingForTask each : notificationsPending + .values()) { + each.doNotification(); + } + notificationsPending.clear(); + } + + } + + private class NotificationPendingForTask { + private StartDateNofitication startDateNofitication; + + private LengthNotification lengthNofitication; + + void setStartDateNofitication( + StartDateNofitication startDateNofitication) { + this.startDateNofitication = this.startDateNofitication == null ? startDateNofitication + : this.startDateNofitication + .coalesce(startDateNofitication); + } + + void setLengthNofitication(LengthNotification lengthNofitication) { + this.lengthNofitication = this.lengthNofitication == null ? lengthNofitication + : this.lengthNofitication.coalesce(lengthNofitication); + } + + void doNotification() { + if (startDateNofitication != null) { + startDateNofitication.doNotification(); + } + if (lengthNofitication != null) { + lengthNofitication.doNotification(); + } + } + } + + private class StartDateNofitication { + + private final INotificationAfterDependenciesEnforcement notification; + private final Date previousStart; + private final long previousLength; + private final Date newStart; + + public StartDateNofitication( + INotificationAfterDependenciesEnforcement notification, + Date previousStart, long previousLength, Date newStart) { + this.notification = notification; + this.previousStart = previousStart; + this.previousLength = previousLength; + this.newStart = newStart; + } + + public StartDateNofitication coalesce( + StartDateNofitication startDateNofitication) { + return new StartDateNofitication(notification, previousStart, + previousLength, startDateNofitication.newStart); + } + + void doNotification() { + notification.onStartDateChange(previousStart, previousLength, + newStart); + } + } + + private class LengthNotification { + + private final INotificationAfterDependenciesEnforcement notification; + private final long previousLengthMilliseconds; + private final long newLengthMilliseconds; + + public LengthNotification( + INotificationAfterDependenciesEnforcement notification, + long previousLengthMilliseconds, long lengthMilliseconds) { + this.notification = notification; + this.previousLengthMilliseconds = previousLengthMilliseconds; + this.newLengthMilliseconds = lengthMilliseconds; + + } + + public LengthNotification coalesce(LengthNotification lengthNofitication) { + return new LengthNotification(notification, + previousLengthMilliseconds, + lengthNofitication.newLengthMilliseconds); + } + + void doNotification() { + notification.onLengthChange(previousLengthMilliseconds, + newLengthMilliseconds); + } + } + + private class DependenciesEnforcer implements + IDependenciesEnforcerHookFactory { + + private ThreadLocal deferedNotifier = new ThreadLocal(); + + @Override + public IDependenciesEnforcerHook create(V task, + INotificationAfterDependenciesEnforcement notificator) { + return onlyEnforceDependenciesOnEntrance(onEntrance(task), + onNotification(task, notificator)); + } + + private IDependenciesEnforcerHook onEntrance(final V task) { + return new IDependenciesEnforcerHook() { + + @Override + public void setStartDate(Date previousStart, + long previousLength, Date newStart) { + taskPositionModified(task); } - subtask - .addFundamentalPropertiesChangeListener(decorateWithPreAndPostActions(new PropertyChangeListener() { - @Override - public void propertyChange(PropertyChangeEvent evt) { - enforce(); - } - })); - } + @Override + public void setLengthMilliseconds( + long previousLengthMilliseconds, long lengthMilliseconds) { + taskPositionModified(task); + } + }; } - void enforce() { - Date newBeginDate = this.container - .getSmallestBeginDateFromChildren(); - this.container.setBeginDate(newBeginDate); - Date newEndDate = this.container.getBiggestDateFromChildren(); - this.container.setEndDate(newEndDate); + private IDependenciesEnforcerHook onNotification(final V task, + final INotificationAfterDependenciesEnforcement notification) { + return new IDependenciesEnforcerHook() { + + @Override + public void setStartDate(Date previousStart, + long previousLength, Date newStart) { + StartDateNofitication startDateNotification = new StartDateNofitication( + notification, + previousStart, previousLength, newStart); + deferedNotifier.get().add(task, startDateNotification); + + } + + @Override + public void setLengthMilliseconds( + long previousLengthMilliseconds, + long newLengthMilliseconds) { + LengthNotification lengthNotification = new LengthNotification( + notification, previousLengthMilliseconds, + newLengthMilliseconds); + deferedNotifier.get().add(task, lengthNotification); + } + }; + } - } + private IDependenciesEnforcerHook onlyEnforceDependenciesOnEntrance( + final IDependenciesEnforcerHook onEntrance, + final IDependenciesEnforcerHook notification) { + return new IDependenciesEnforcerHook() { - private class DependencyRulesEnforcer { - private final Task task; + @Override + public void setStartDate(final Date previousStart, + final long previousLength, final Date newStart) { + positionsUpdatingGuard + .entranceRequested(new IReentranceCases() { - private DependencyRulesEnforcer(Task task) { - if (task == null) { - throw new IllegalArgumentException("task cannot be null"); + @Override + public void ifNewEntrance() { + onNewEntrance(new IAction() { + + @Override + public void doAction() { + notification.setStartDate( + previousStart, + previousLength, newStart); + onEntrance.setStartDate( + previousStart, + previousLength, newStart); + } + }); + } + + @Override + public void ifAlreadyInside() { + notification.setStartDate(previousStart, + previousLength, newStart); + + } + }); + } + + @Override + public void setLengthMilliseconds( + final long previousLengthMilliseconds, + final long lengthMilliseconds) { + positionsUpdatingGuard + .entranceRequested(new IReentranceCases() { + + @Override + public void ifNewEntrance() { + onNewEntrance(new IAction() { + + @Override + public void doAction() { + notification.setLengthMilliseconds( + previousLengthMilliseconds, + lengthMilliseconds); + onEntrance.setLengthMilliseconds( + previousLengthMilliseconds, + lengthMilliseconds); + } + }); + } + + @Override + public void ifAlreadyInside() { + notification.setLengthMilliseconds( + previousLengthMilliseconds, + lengthMilliseconds); + } + }); + } + }; + + } + + void enforceRestrictionsOn(Collection tasks) { + List allRecalculations = new ArrayList(); + for (V each : tasks) { + allRecalculations.addAll(getRecalculationsNeededFrom(each)); } - this.task = task; - this.task - .addFundamentalPropertiesChangeListener(decorateWithPreAndPostActions(new PropertyChangeListener() { + enforceRestrictionsOn(allRecalculations); + } + + void enforceRestrictionsOn(V task) { + enforceRestrictionsOn(getRecalculationsNeededFrom(task)); + } + + void enforceRestrictionsOn(final List recalculations) { + executeWithPreAndPostActionsOnlyIfNewEntrance(new IAction() { + @Override + public void doAction() { + doRecalculations(recalculations); + } + }); + } + + private void executeWithPreAndPostActionsOnlyIfNewEntrance( + final IAction action) { + positionsUpdatingGuard.entranceRequested(new IReentranceCases() { + + @Override + public void ifAlreadyInside() { + action.doAction(); + } + + @Override + public void ifNewEntrance() { + onNewEntrance(action); + } + }); + } + + private void onNewEntrance(final IAction action) { + preAndPostActions.doAction(decorateWithNotifications(action)); + } + + private IAction decorateWithNotifications(final IAction action) { + return new IAction() { + + @Override + public void doAction() { + deferedNotifier.set(new DeferedNotifier()); + try { + action.doAction(); + } finally { + DeferedNotifier notifier = deferedNotifier.get(); + notifier.doNotifications(); + deferedNotifier.set(null); + } + } + }; + } + + DeferedNotifier manualNotification(final IAction action) { + final DeferedNotifier result = new DeferedNotifier(); + positionsUpdatingGuard.entranceRequested(new IReentranceCases() { + + @Override + public void ifAlreadyInside() { + throw new RuntimeException("it cannot do a manual notification if it's already inside"); + } + + @Override + public void ifNewEntrance() { + preAndPostActions.doAction(new IAction() { @Override - public void propertyChange(PropertyChangeEvent evt) { - DependencyRulesEnforcer.this.enforce(); - updateOutgoing(DependencyRulesEnforcer.this.task); + public void doAction() { + deferedNotifier.set(result); + try { + action.doAction(); + } finally { + deferedNotifier.set(null); + } } - })); + }); + } + }); + return result; } - void enforce() { - Set incoming = graph.incomingEdgesOf(task); - enforceStartDate(incoming); - enforceEndDate(incoming); + private void taskPositionModified(final V task) { + executeWithPreAndPostActionsOnlyIfNewEntrance(new IAction() { + @Override + public void doAction() { + List recalculationsNeededFrom = getRecalculationsNeededFrom(task); + doRecalculations(recalculationsNeededFrom); + } + }); } - @SuppressWarnings("unchecked") - private void enforceEndDate(Set incoming) { - Constraint currentLength = task.getCurrentLengthConstraint(); - Constraint respectStartDate = task - .getEndDateBiggerThanStartDate(); - Date newEnd = Constraint. initialValue(null) - .withConstraints(currentLength) - .withConstraints(Dependency - .getEndConstraints(incoming)) - .withConstraints(respectStartDate) - .apply(); - if (!task.getEndDate().equals(newEnd)) { - task.setEndDate(newEnd); - } - } - - private void enforceStartDate(Set incoming) { - Date newStart = calculateStartDateFor(task, incoming); - Date childrenEarliest = getEarliestStartDateOfChildren(task); - newStart = maxNotNull(newStart, childrenEarliest); - if (!task.getBeginDate().equals(newStart)) { - task.setBeginDate(newStart); - } - } - - private Date getEarliestStartDateOfChildren(Task task) { - if (!task.isContainer()) { - return null; - } - List startDates = getChildrenStartDates((TaskContainer) task); - if (!startDates.isEmpty()) { - return Collections.min(startDates); - } - return null; - } - - private List getChildrenStartDates(TaskContainer container) { - List children = container.getTasks(); - List startDates = new ArrayList(); - for (Task each : children) { - Set incomingDependencies = withoutDependencyFrom( - container, graph.incomingEdgesOf(each)); - Date dateWithoutContainerInfluence = calculateStartDateFor( - each, incomingDependencies); - if (dateWithoutContainerInfluence != null) { - startDates.add(dateWithoutContainerInfluence); + private void doRecalculations(List recalculationsNeeded) { + Set allModified = new HashSet(); + List calculated = new ArrayList(); + for (Recalculation each : recalculationsNeeded) { + if (each.haveToDoCalculation()) { + calculated.add(each); + } + boolean modified = each.doRecalculation(); + if (modified) { + allModified.add(each.taskPoint.task); } } - return startDates; + List shrunkContainers = shrunkContainersOfModified(allModified); + for (V each : getTaskAffectedByShrinking(shrunkContainers)) { + doRecalculations(getRecalculationsNeededFrom(each)); + } } - private Set withoutDependencyFrom(TaskContainer container, - Set incoming) { - Set result = new HashSet(); - for (Dependency each : incoming) { - if (!each.getSource().equals(container)) { + private List getTaskAffectedByShrinking(List shrunkContainers) { + List tasksAffectedByShrinking = new ArrayList(); + for (V each : shrunkContainers) { + for (D eachDependency : graph.outgoingEdgesOf(each)) { + if (adapter.getType(eachDependency) == DependencyType.START_START + && adapter.isVisible(eachDependency)) { + tasksAffectedByShrinking.add(adapter + .getDestination(eachDependency)); + } + } + } + return tasksAffectedByShrinking; + } + + private List shrunkContainersOfModified( + Set allModified) { + Set topmostToShrink = getTopMostThatCouldPotentiallyNeedShrinking(allModified); + List allToShrink = new ArrayList(); + for (V each : topmostToShrink) { + allToShrink.addAll(getContainersBottomUp(each)); + } + List result = new ArrayList(); + for (V each : allToShrink) { + boolean modified = enforceParentShrinkage(each); + if (modified) { result.add(each); } } return result; } - private Date maxNotNull(Date... dates) { - List list = new ArrayList(); - for (Date each : dates) { - if (each != null) { - list.add(each); + private Set getTopMostThatCouldPotentiallyNeedShrinking( + Collection modified) { + Set result = new HashSet(); + for (V each : modified) { + V t = getTopmostFor(each); + if (adapter.isContainer(t)) { + result.add(t); } } - if (list.isEmpty()) { - return null; - } - return Collections.max(list); + return result; } - } - private Date calculateStartDateFor(Task task, - Set withDependencies) { - List> dependencyConstraints = Dependency - .getStartConstraints(withDependencies); - Date newStart; - if (dependenciesConstraintsHavePriority) { - newStart = Constraint. initialValue(null) - .withConstraints(task.getStartConstraints()) - .withConstraints(dependencyConstraints) - .withConstraints(globalStartConstraints) - .apply(); - - } else { - newStart = Constraint. initialValue(null) - .withConstraints(dependencyConstraints) - .withConstraints(task.getStartConstraints()) - .withConstraints(globalStartConstraints) - .apply(); - } - return newStart; - } - - public void enforceAllRestrictions() { - preAndPostActions.doAction(new IAction() { - - @Override - public void doAction() { - for (DependencyRulesEnforcer rulesEnforcer : rulesEnforcersByTask - .values()) { - rulesEnforcer.enforce(); - } - for (ParentShrinkingEnforcer parentShrinkingEnforcer : parentShrinkingEnforcerByTask - .values()) { - parentShrinkingEnforcer.enforce(); + private Collection getContainersBottomUp( + V container) { + List result = new ArrayList(); + List tasks = adapter.getChildren(container); + for (V each : tasks) { + if (adapter.isContainer(each)) { + result.addAll(getContainersBottomUp(each)); + result.add(each); } } - }); - } - - public void addTopLevel(Task task) { - topLevelTasks.add(task); - addTask(task); - } - - public void addTopLevel(Collection tasks) { - for (Task task : tasks) { - addTopLevel(task); + result.add(container); + return result; } - } - public void addTasks(Collection tasks) { - for (Task t : tasks) { - addTask(t); - } - } - - public void addTask(Task task) { - graph.addVertex(task); - rulesEnforcersByTask.put(task, new DependencyRulesEnforcer(task)); - if (task.isContainer()) { - ParentShrinkingEnforcer parentShrinkingEnforcer = new ParentShrinkingEnforcer( - (TaskContainer) task); - parentShrinkingEnforcerByTask.put(task, parentShrinkingEnforcer); - List dependenciesToAdd = new ArrayList(); - for (Task child : task.getTasks()) { - addTask(child); - dependenciesToAdd.add(new Dependency(child, task, - DependencyType.END_END, false)); - dependenciesToAdd.add(new Dependency(task, child, - DependencyType.START_START, - false)); + boolean enforceParentShrinkage(V container) { + Date oldBeginDate = adapter.getStartDate(container); + Date firstStart = adapter + .getSmallestBeginDateFromChildrenFor(container); + Date previousEnd = adapter.getEndDateFor(container); + if (firstStart.after(oldBeginDate)) { + adapter.setStartDateFor(container, firstStart); + adapter.setEndDateFor(container, previousEnd); + return true; } - for (Dependency each : dependenciesToAdd) { - add(each); + return false; + } + } + + List getRecalculationsNeededFrom(V task) { + List result = new LinkedList(); + Set parentRecalculationsAlreadyDone = new HashSet(); + Recalculation first = recalculationFor(TaskPoint.both(adapter, task)); + first.couldHaveBeenModifiedBeforehand(); + Queue pendingOfNavigate = new LinkedList(); + result.addAll(getParentsRecalculations(parentRecalculationsAlreadyDone, + first.taskPoint)); + result.add(first); + pendingOfNavigate.offer(first); + while (!pendingOfNavigate.isEmpty()) { + Recalculation current = pendingOfNavigate.poll(); + for (TaskPoint each : getImmendiateReachableFrom(current.taskPoint)) { + Recalculation recalculationToAdd = recalculationFor(each); + ListIterator listIterator = result + .listIterator(); + while (listIterator.hasNext()) { + Recalculation previous = listIterator.next(); + if (previous.equals(recalculationToAdd)) { + listIterator.remove(); + recalculationToAdd = previous; + break; + } + } + recalculationToAdd.fromParent(current); + result.addAll(getParentsRecalculations( + parentRecalculationsAlreadyDone, each)); + result.add(recalculationToAdd); + pendingOfNavigate.offer(recalculationToAdd); } } + return result; } - public void remove(Task task) { - List outgoing = getOutgoing(task); + private List getParentsRecalculations( + Set parentRecalculationsAlreadyDone, + TaskPoint taskPoint) { + List result = new ArrayList(); + for (TaskPoint eachParent : parentsRecalculationsNeededFor(taskPoint)) { + Recalculation parentRecalculation = parentRecalculation(eachParent.task); + if (!parentRecalculationsAlreadyDone + .contains(parentRecalculation)) { + parentRecalculationsAlreadyDone.add(parentRecalculation); + result.add(parentRecalculation); + } + } + return result; + } + + private Set> parentsRecalculationsNeededFor( + TaskPoint current) { + Set> result = new LinkedHashSet>(); + if (current.pointType == PointType.BOTH) { + List path = fromTaskToTop(current.task); + if (path.size() > 1) { + path = path.subList(1, path.size()); + Collections.reverse(path); + result.addAll(asBothPoints(path)); + } + } + return result; + } + + private Collection> asBothPoints(List parents) { + List> result = new ArrayList>(); + for (V each : parents) { + result.add(TaskPoint.both(adapter, each)); + } + return result; + } + + private List fromTaskToTop(V task) { + List result = new ArrayList(); + V current = task; + while (current != null) { + result.add(current); + current = fromChildToParent.get(current); + } + return result; + } + + private Recalculation parentRecalculation(V task) { + return new Recalculation(TaskPoint.both(adapter, task), true); + } + + private Recalculation recalculationFor(TaskPoint taskPoint) { + return new Recalculation(taskPoint, false); + } + + private class Recalculation { + + private final boolean parentRecalculation; + + private final TaskPoint taskPoint; + + private Set parents = new HashSet(); + + private boolean recalculationCalled = false; + + private boolean dataPointModified = false; + + private boolean couldHaveBeenModifiedBeforehand = false; + + Recalculation(TaskPoint taskPoint, boolean isParentRecalculation) { + Validate.notNull(taskPoint); + this.taskPoint = taskPoint; + this.parentRecalculation = isParentRecalculation; + } + + public void couldHaveBeenModifiedBeforehand() { + couldHaveBeenModifiedBeforehand = true; + } + + public void fromParent(Recalculation parent) { + parents.add(parent); + } + + boolean doRecalculation() { + recalculationCalled = true; + dataPointModified = haveToDoCalculation() + && taskChangesPosition(); + return dataPointModified; + } + + private boolean haveToDoCalculation() { + return (parents.isEmpty() || parentsHaveBeenModified()); + } + + private boolean taskChangesPosition() { + PointType pointType = taskPoint.pointType; + V task = taskPoint.task; + switch (pointType) { + case BOTH: + return enforceStartAndEnd(task); + case END: + return enforceEnd(task); + default: + return false; + } + } + + private boolean parentsHaveBeenModified() { + for (Recalculation each : parents) { + if (!each.recalculationCalled) { + throw new RuntimeException( + "the parent must be called first"); + } + if (each.dataPointModified + || each.couldHaveBeenModifiedBeforehand) { + return true; + } + } + return false; + } + + private boolean enforceStartAndEnd(V task) { + Set incoming = graph.incomingEdgesOf(task); + Date previousEndDate = adapter.getEndDateFor(task); + boolean startDateChanged = enforceStartDate(task, incoming); + boolean endDateChanged = enforceEndDate(task, previousEndDate, + incoming); + return startDateChanged || endDateChanged; + } + + private boolean enforceEnd(V task) { + Set incoming = graph.incomingEdgesOf(task); + Date previousEndDate = adapter.getEndDateFor(task); + return enforceEndDate(task, previousEndDate, incoming); + } + + @SuppressWarnings("unchecked") + private boolean enforceEndDate(V task, Date previousEndDate, + Set incoming) { + Constraint currentLength = adapter + .getCurrentLenghtConstraintFor(task); + Constraint respectStartDate = adapter + .getEndDateBiggerThanStartDateConstraintFor(task); + Date newEnd = Constraint. initialValue(null) + .withConstraints(currentLength) + .withConstraints(adapter.getConstraintsGivenIncoming(incoming)) + .withConstraints(respectStartDate) + .apply(); + if (!adapter.getEndDateFor(task).equals(newEnd)) { + adapter.setEndDateFor(task, newEnd); + } + return !previousEndDate.equals(newEnd); + } + + private boolean enforceStartDate(V task, Set incoming) { + Date newStart = calculateStartDateFor(task, incoming); + if (!adapter.getStartDate(task).equals(newStart)) { + adapter.setStartDateFor(task, newStart); + return true; + } + return false; + } + + private Date calculateStartDateFor(V task, Set withDependencies) { + List> dependencyConstraints = adapter + .getStartCosntraintsGiven(withDependencies); + Date newStart; + if (dependenciesConstraintsHavePriority) { + newStart = Constraint. initialValue(null) + .withConstraints(adapter.getStartConstraintsFor(task)) + .withConstraints(dependencyConstraints) + .withConstraints(globalStartConstraints).apply(); + + } else { + newStart = Constraint. initialValue(null) + .withConstraints(dependencyConstraints) + .withConstraints(adapter.getStartConstraintsFor(task)) + .withConstraints(globalStartConstraints).apply(); + } + return newStart; + } + + @Override + public int hashCode() { + return new HashCodeBuilder() + .append(parentRecalculation) + .append(taskPoint) + .toHashCode(); + } + + @Override + public String toString() { + return String.format("%s, parentRecalculation: %s, parents: %s", + taskPoint, parentRecalculation, asSimpleString(parents)); + } + + private String asSimpleString( + Collection recalculations) { + StringBuilder result = new StringBuilder(); + result.append("["); + for (Recalculation each : recalculations) { + result.append(each.taskPoint).append(", "); + } + result.append("]"); + return result.toString(); + } + + @Override + public boolean equals(Object obj) { + if (Recalculation.class.isInstance(obj)) { + Recalculation other = (Recalculation) obj; + return new EqualsBuilder().append(parentRecalculation, other.parentRecalculation) + .append(taskPoint, other.taskPoint) + .isEquals(); + } + return false; + } + } + + public void remove(final V task) { + Set needingEnforcing = getOutgoingTasksFor(task); graph.removeVertex(task); - rulesEnforcersByTask.remove(task); topLevelTasks.remove(task); - update(outgoing); - if (task.isContainer()) { - for (Task t : task.getTasks()) { + fromChildToParent.remove(task); + if (adapter.isContainer(task)) { + for (V t : adapter.getChildren(task)) { remove(t); } } + enforcer.enforceRestrictionsOn(needingEnforcing); } - private void updateOutgoing(Task task) { - update(getOutgoing(task)); - } - - private void update(List outgoing) { - for (DependencyRulesEnforcer rulesEnforcer : outgoing) { - rulesEnforcer.enforce(); - } - } - - public void remove(Dependency dependency) { + public void removeDependency(D dependency) { graph.removeEdge(dependency); - Task destination = dependency.getDestination(); - rulesEnforcersByTask.get(destination).enforce(); + V destination = adapter.getDestination(dependency); + enforcer.enforceRestrictionsOn(destination); } - public void add(Dependency dependency) { - Task source = dependency.getSource(); - Task destination = dependency.getDestination(); + public void add(D dependency) { + V source = adapter.getSource(dependency); + V destination = adapter.getDestination(dependency); graph.addEdge(source, destination, dependency); enforceRestrictions(destination); } - public void enforceRestrictions(final Task task) { - preAndPostActions.doAction(new IAction() { - @Override - public void doAction() { - getEnforcer(task).enforce(); - } - }); + public void enforceRestrictions(final V task) { + enforcer.taskPositionModified(task); } - public boolean contains(Dependency dependency) { + public DeferedNotifier manualNotificationOn(IAction action) { + return enforcer.manualNotification(action); + } + + public boolean contains(D dependency) { return graph.containsEdge(dependency); } - private DependencyRulesEnforcer getEnforcer(Task destination) { - return rulesEnforcersByTask.get(destination); + public List getTasks() { + return new ArrayList(graph.vertexSet()); } - @Override - public List getTasks() { - return new ArrayList(graph.vertexSet()); - } - - public List getVisibleDependencies() { - Set edgeSet = graph.edgeSet(); - ArrayList result = new ArrayList(); - for (Dependency dependency : edgeSet) { - if (dependency.isVisible()) { + public List getVisibleDependencies() { + ArrayList result = new ArrayList(); + for (D dependency : graph.edgeSet()) { + if (adapter.isVisible(dependency)) { result.add(dependency); } } return result; } - public List getTopLevelTasks() { + public List getTopLevelTasks() { return Collections.unmodifiableList(topLevelTasks); } - public void childrenAddedTo(TaskContainer task) { - ParentShrinkingEnforcer parentShrinkingEnforcer = parentShrinkingEnforcerByTask - .get(task); - parentShrinkingEnforcer.registerListeners(); - parentShrinkingEnforcer.enforce(); + public void childrenAddedTo(V task) { + enforcer.enforceRestrictionsOn(task); } - @Override - public List getInitialTasks() { - List tasks = new ArrayList(); - - for (Task task : graph.vertexSet()) { + public List getInitialTasks() { + List result = new ArrayList(); + for (V task : graph.vertexSet()) { int dependencies = graph.inDegreeOf(task); if ((dependencies == 0) || (dependencies == getNumberOfIncomingDependenciesByType( task, DependencyType.END_END))) { - tasks.add(task); + result.add(task); } } - - return tasks; + return result; } - @Override - public Dependency getDependencyFrom(Task from, Task to) { + public D getDependencyFrom(V from, V to) { return graph.getEdge(from, to); } - @Override - public Set getOutgoingTasksFor(Task task) { - Set tasks = new HashSet(); - - for (Dependency dependency : graph.outgoingEdgesOf(task)) { - tasks.add(dependency.getDestination()); + public Set getOutgoingTasksFor(V task) { + Set result = new HashSet(); + for (D dependency : graph.outgoingEdgesOf(task)) { + result.add(adapter.getDestination(dependency)); } - - return tasks; + return result; } - @Override - public Set getIncomingTasksFor(Task task) { - Set tasks = new HashSet(); - - for (Dependency dependency : graph.incomingEdgesOf(task)) { - tasks.add(dependency.getSource()); + public Set getIncomingTasksFor(V task) { + Set result = new HashSet(); + for (D dependency : graph.incomingEdgesOf(task)) { + result.add(adapter.getSource(dependency)); } - - return tasks; + return result; } - @Override - public List getLatestTasks() { - List tasks = new ArrayList(); + public List getLatestTasks() { + List tasks = new ArrayList(); - for (Task task : graph.vertexSet()) { + for (V task : graph.vertexSet()) { int dependencies = graph.outDegreeOf(task); if ((dependencies == 0) || (dependencies == getNumberOfOutgoingDependenciesByType( @@ -510,51 +1172,49 @@ public class GanttDiagramGraph implements ICriticalPathCalculable { return tasks; } - private int getNumberOfIncomingDependenciesByType(Task task, + private int getNumberOfIncomingDependenciesByType(V task, DependencyType dependencyType) { int count = 0; - for (Dependency dependency : graph.incomingEdgesOf(task)) { - if (dependency.getType().equals(dependencyType)) { + for (D dependency : graph.incomingEdgesOf(task)) { + if (adapter.getType(dependency).equals(dependencyType)) { count++; } } return count; } - private int getNumberOfOutgoingDependenciesByType(Task task, + private int getNumberOfOutgoingDependenciesByType(V task, DependencyType dependencyType) { int count = 0; - for (Dependency dependency : graph.outgoingEdgesOf(task)) { - if (dependency.getType().equals(dependencyType)) { + for (D dependency : graph.outgoingEdgesOf(task)) { + if (adapter.getType(dependency).equals(dependencyType)) { count++; } } return count; } - @Override - public boolean isContainer(Task task) { + public boolean isContainer(V task) { if (task == null) { return false; } - return task.isContainer(); + return adapter.isContainer(task); } - @Override - public boolean contains(Task container, Task task) { + public boolean contains(V container, V task) { if ((container == null) || (task == null)) { return false; } - if (container.isContainer()) { - return container.getTasks().contains(task); + if (adapter.isContainer(container)) { + return adapter.getChildren(container).contains(task); } return false; } - public boolean doesNotProvokeLoop(Dependency dependency) { - Set reachableFromDestination = getReachableFrom(dependency - .getDestinationPoint()); - for (TaskPoint each : reachableFromDestination) { + public boolean doesNotProvokeLoop(D dependency) { + Set> reachableFromDestination = getReachableFrom(adapter + .getDestinationPoint(dependency)); + for (TaskPoint each : reachableFromDestination) { if (each.sendsModificationsThrough(dependency)) { return false; } @@ -584,29 +1244,38 @@ public class GanttDiagramGraph implements ICriticalPathCalculable { } } - static class TaskPoint { + static class TaskPoint { - public static TaskPoint both(Task task){ - return new TaskPoint(task, PointType.BOTH); + public static TaskPoint both(IAdapter adapter, T task) { + return new TaskPoint(adapter, task, PointType.BOTH); } - public static TaskPoint endOf(Task task) { - return new TaskPoint(task, PointType.END); + public static TaskPoint endOf(IAdapter adapter, + T task) { + return new TaskPoint(adapter, task, PointType.END); } - final Task task; + final T task; final PointType pointType; - TaskPoint(Task task, PointType pointType) { + private final IAdapter adapter; + + TaskPoint(IAdapter adapter, T task, PointType pointType) { + this.adapter = adapter; this.task = task; this.pointType = pointType; } + @Override + public String toString() { + return String.format("%s(%s)", task, pointType); + } + @Override public boolean equals(Object obj) { - if (obj instanceof TaskPoint) { - TaskPoint other = (TaskPoint) obj; + if (obj instanceof TaskPoint) { + TaskPoint other = (TaskPoint) obj; return new EqualsBuilder().append(task, other.task).append( pointType, other.pointType).isEquals(); } @@ -618,22 +1287,22 @@ public class GanttDiagramGraph implements ICriticalPathCalculable { return new HashCodeBuilder().append(task).append(pointType).toHashCode(); } - public boolean sendsModificationsThrough(Dependency dependency) { - DependencyType type = dependency.getType(); - return dependency.getSource().equals(task) + public boolean sendsModificationsThrough(D dependency) { + DependencyType type = adapter.getType(dependency); + return adapter.getSource(dependency).equals(task) && pointType.sendsModificationsThrough(type); } } - private Set getReachableFrom(TaskPoint task) { - Set result = new HashSet(); - Queue pending = new LinkedList(); + private Set> getReachableFrom(TaskPoint task) { + Set> result = new HashSet>(); + Queue> pending = new LinkedList>(); result.add(task); pending.offer(task); while (!pending.isEmpty()) { - TaskPoint current = pending.poll(); - Set immendiate = getImmendiateReachableFrom(current); - for (TaskPoint each : immendiate) { + TaskPoint current = pending.poll(); + Set> immendiate = getImmendiateReachableFrom(current); + for (TaskPoint each : immendiate) { if (!result.contains(each)) { result.add(each); pending.offer(each); @@ -643,15 +1312,52 @@ public class GanttDiagramGraph implements ICriticalPathCalculable { return result; } - private Set getImmendiateReachableFrom(TaskPoint current) { - Set result = new HashSet(); - Set outgoingEdgesOf = graph.outgoingEdgesOf(current.task); - for (Dependency each : outgoingEdgesOf) { - if (current.sendsModificationsThrough(each)) { - result.add(each.getDestinationPoint()); - } + + + private V getTopmostFor(V task) { + V result = task; + while (fromChildToParent.containsKey(result)) { + result = fromChildToParent.get(result); } return result; } + private Set> getImmendiateReachableFrom( + TaskPoint current) { + Set> result = new HashSet>(); + Set outgoingEdgesOf = graph.outgoingEdgesOf(current.task); + for (D each : outgoingEdgesOf) { + if (current.sendsModificationsThrough(each)) { + result.add(adapter.getDestinationPoint(each)); + } + } + return result; + } +} + +interface IReentranceCases { + public void ifNewEntrance(); + + public void ifAlreadyInside(); +} + +class ReentranceGuard { + private final ThreadLocal inside = new ThreadLocal() { + protected Boolean initialValue() { + return false; + }; + }; + + public void entranceRequested(IReentranceCases reentranceCases) { + if (inside.get()) { + reentranceCases.ifAlreadyInside(); + return; + } + inside.set(true); + try { + reentranceCases.ifNewEntrance(); + } finally { + inside.set(false); + } + } } \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/ITaskFundamentalProperties.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/ITaskFundamentalProperties.java index f0adfb582..efec66e04 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/ITaskFundamentalProperties.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/ITaskFundamentalProperties.java @@ -78,6 +78,12 @@ public interface ITaskFundamentalProperties { public boolean isSubcontracted(); + public boolean isLimiting(); + + public boolean isLimitingAndHasDayAssignments(); + + public boolean hasConsolidations(); + public boolean canBeExplicitlyResized(); public String getAssignedStatus(); diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java index 877bcff3f..f07bd9215 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java @@ -30,6 +30,9 @@ import java.util.List; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.Validate; +import org.zkoss.ganttz.data.GanttDiagramGraph.IDependenciesEnforcerHook; +import org.zkoss.ganttz.data.GanttDiagramGraph.IDependenciesEnforcerHookFactory; +import org.zkoss.ganttz.data.GanttDiagramGraph.INotificationAfterDependenciesEnforcement; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.data.constraint.DateConstraint; import org.zkoss.ganttz.data.constraint.Constraint.IConstraintViolationListener; @@ -66,6 +69,46 @@ public abstract class Task implements ITaskFundamentalProperties { private ConstraintViolationNotificator violationNotificator = ConstraintViolationNotificator .create(); + private IDependenciesEnforcerHook dependenciesEnforcerHook = doNothingHook(); + + private final INotificationAfterDependenciesEnforcement notifyDates = new INotificationAfterDependenciesEnforcement() { + + @Override + public void onStartDateChange(Date previousStart, long oldLength, + Date newStart) { + fundamentalPropertiesListeners.firePropertyChange("beginDate", + previousStart, fundamentalProperties.getBeginDate()); + fireLengthMilliseconds(oldLength); + reloadResourcesTextIfChange(newStart, previousStart); + } + + @Override + public void onLengthChange(long previousLength, long newLength) { + fireLengthMilliseconds(previousLength); + } + + private void fireLengthMilliseconds(long previousValue) { + fundamentalPropertiesListeners.firePropertyChange( + "lengthMilliseconds", previousValue, fundamentalProperties + .getLengthMilliseconds()); + } + }; + + private IDependenciesEnforcerHook doNothingHook() { + return new IDependenciesEnforcerHook() { + + @Override + public void setLengthMilliseconds(long previousLengthMilliseconds, + long lengthMilliseconds) { + } + + @Override + public void setStartDate(Date previousStart, long previousLength, + Date newStart) { + } + }; + }; + public Task(ITaskFundamentalProperties fundamentalProperties) { this.fundamentalProperties = fundamentalProperties; } @@ -140,14 +183,19 @@ public abstract class Task implements ITaskFundamentalProperties { previousValue, name); } - public long setBeginDate(Date beginDate) { + public void registerDependenciesEnforcerHook( + IDependenciesEnforcerHookFactory factory) { + Validate.notNull(factory); + dependenciesEnforcerHook = factory.create(this, notifyDates); + Validate.notNull(dependenciesEnforcerHook); + } + + public long setBeginDate(Date newStart) { Date previousValue = fundamentalProperties.getBeginDate(); - long oldLength = fundamentalProperties.getLengthMilliseconds(); - fundamentalProperties.setBeginDate(beginDate); - fundamentalPropertiesListeners.firePropertyChange("beginDate", - previousValue, fundamentalProperties.getBeginDate()); - fireLengthMilliseconds(oldLength); - reloadResourcesTextIfChange(beginDate, previousValue); + long previousLength = fundamentalProperties.getLengthMilliseconds(); + fundamentalProperties.setBeginDate(newStart); + dependenciesEnforcerHook.setStartDate(previousValue, previousLength, + newStart); return fundamentalProperties.getLengthMilliseconds(); } @@ -159,9 +207,10 @@ public abstract class Task implements ITaskFundamentalProperties { public void fireChangesForPreviousValues(Date previousStart, long previousLength) { - fundamentalPropertiesListeners.firePropertyChange("beginDate", - previousStart, fundamentalProperties.getBeginDate()); - fireLengthMilliseconds(previousLength); + dependenciesEnforcerHook.setStartDate(previousStart, previousLength, + fundamentalProperties.getBeginDate()); + dependenciesEnforcerHook.setLengthMilliseconds(previousLength, + fundamentalProperties.getLengthMilliseconds()); } public Date getBeginDate() { @@ -171,12 +220,8 @@ public abstract class Task implements ITaskFundamentalProperties { public void setLengthMilliseconds(long lengthMilliseconds) { long previousValue = fundamentalProperties.getLengthMilliseconds(); fundamentalProperties.setLengthMilliseconds(lengthMilliseconds); - fireLengthMilliseconds(previousValue); - } - - private void fireLengthMilliseconds(long previousValue) { - fundamentalPropertiesListeners.firePropertyChange("lengthMilliseconds", - previousValue, fundamentalProperties.getLengthMilliseconds()); + dependenciesEnforcerHook.setLengthMilliseconds(previousValue, + lengthMilliseconds); } public long getLengthMilliseconds() { @@ -208,6 +253,9 @@ public abstract class Task implements ITaskFundamentalProperties { } public Constraint getCurrentLengthConstraint() { + if (isContainer()) { + return Constraint.emptyConstraint(); + } return violationNotificator.withListener(DateConstraint .biggerOrEqualThan(getEndDate())); } @@ -275,9 +323,7 @@ public abstract class Task implements ITaskFundamentalProperties { Date previousStart = getBeginDate(); long previousLength = getLengthMilliseconds(); fundamentalProperties.moveTo(date); - fireChangesForPreviousValues(previousStart, previousLength); - reloadResourcesTextIfChange(date, previousStart); - reloadResourcesText(); + dependenciesEnforcerHook.setStartDate(previousStart, previousLength, date); } @Override @@ -311,6 +357,18 @@ public abstract class Task implements ITaskFundamentalProperties { return fundamentalProperties.isSubcontracted(); } + public boolean isLimiting() { + return fundamentalProperties.isLimiting(); + } + + public boolean isLimitingAndHasDayAssignments() { + return fundamentalProperties.isLimitingAndHasDayAssignments(); + } + + public boolean hasConsolidations() { + return fundamentalProperties.hasConsolidations(); + } + public boolean canBeExplicitlyResized() { return fundamentalProperties.canBeExplicitlyResized(); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/TaskLeaf.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/TaskLeaf.java index a6f0a805b..8878bc86f 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/TaskLeaf.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/TaskLeaf.java @@ -54,7 +54,7 @@ public class TaskLeaf extends Task { @Override public boolean canBeExplicitlyMoved() { - return !isSubcontracted(); + return !(isSubcontracted() || isLimitingAndHasDayAssignments() || hasConsolidations()); } } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/constraint/Constraint.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/constraint/Constraint.java index 1bb05c442..5d4cda67f 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/constraint/Constraint.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/constraint/Constraint.java @@ -33,6 +33,21 @@ import org.zkoss.ganttz.util.WeakReferencedListeners.IListenerNotification; */ public abstract class Constraint { + public static Constraint emptyConstraint() { + return new Constraint() { + + @Override + protected T applyConstraintTo(T currentValue) { + return currentValue; + } + + @Override + public boolean isSatisfiedBy(T value) { + return true; + } + }; + } + public interface IConstraintViolationListener { public void constraintViolated(Constraint constraint, T value); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/limitingresource/QueueTask.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/limitingresource/QueueTask.java deleted file mode 100644 index a6a2f4946..000000000 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/limitingresource/QueueTask.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is part of NavalPlan - * - * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e - * Desenvolvemento Tecnolóxico de Galicia - * - * 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 . - */ - -package org.zkoss.ganttz.data.limitingresource; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang.Validate; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.joda.time.LocalDate; -import org.zkoss.ganttz.data.resourceload.LoadLevel; - -public class QueueTask { - - private static final Log LOG = LogFactory.getLog(QueueTask.class); - - private final LocalDate start; - - private final LocalDate end; - - private final LoadLevel loadLevel; - - private final int totalResourceWorkHours; - - private final int assignedHours; - - public QueueTask(LocalDate start, LocalDate end, - int totalResourceWorkHours, int assignedHours, LoadLevel loadLevel) { - Validate.notNull(start); - Validate.notNull(end); - Validate.notNull(loadLevel); - Validate.notNull(totalResourceWorkHours); - Validate.notNull(assignedHours); - Validate.isTrue(!start.isAfter(end)); - this.start = start; - this.end = end; - this.loadLevel = loadLevel; - this.totalResourceWorkHours = totalResourceWorkHours; - this.assignedHours = assignedHours; - } - - public LocalDate getStart() { - return start; - } - - public LocalDate getEnd() { - return end; - } - - // public boolean overlaps(QueueTask other) { - // return start.isBefore(other.end) && end.isAfter(other.start); - // } - - /** - * @param loadPeriods - * @return - * @throws IllegalArgumentException - * if some of the QueueTask overlaps - */ - public static List sort(List loadPeriods) - throws IllegalArgumentException { - ArrayList result = new ArrayList(loadPeriods); - // Collections.sort(result, new Comparator() { - - // @Override - // public int compare(QueueTask o1, QueueTask o2) { - // if (o1.overlaps(o2)) { - // LOG.warn(o1 + " overlaps with " + o2); - // throw new IllegalArgumentException(o1 + " overlaps with " - // + o2); - // } - // int comparison = compareLocalDates(o1.start, o2.start); - // if (comparison != 0) { - // return comparison; - // } - // return compareLocalDates(o1.end, o2.end); - // } - // }); - return result; - } - - private static int compareLocalDates(LocalDate l1, LocalDate l2) { - if (l1.isBefore(l2)) { - return -1; - } - if (l1.isAfter(l2)) { - return 1; - } - return 0; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this); - } - - public LoadLevel getLoadLevel() { - return loadLevel; - } - - public int getTotalResourceWorkHours() { - return totalResourceWorkHours; - } - - public int getAssignedHours() { - return assignedHours; - } -} diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java index e39c2ae60..8e4e58328 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java @@ -71,7 +71,6 @@ public class LoadTimeLine { this.timeLineRole = principal.getRole(); this.type = principal.getType(); Validate.notNull(children); - allChildrenAreNotEmpty(children); this.children = Collections .unmodifiableList(new ArrayList(children)); @@ -178,8 +177,8 @@ public class LoadTimeLine { public List getAllChildren() { List result = new ArrayList(); for (LoadTimeLine child : children) { - result.addAll(child.getAllChildren()); result.add(child); + result.addAll(child.getAllChildren()); } return result; } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextRelativeToOtherComponent.java b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextRelativeToOtherComponent.java index 5064f641f..575b3634c 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextRelativeToOtherComponent.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextRelativeToOtherComponent.java @@ -24,6 +24,7 @@ import java.util.List; import org.zkoss.ganttz.adapters.IDomainAndBeansMapper; import org.zkoss.ganttz.adapters.PlannerConfiguration; +import org.zkoss.ganttz.data.GanttDiagramGraph; import org.zkoss.ganttz.data.Position; import org.zkoss.ganttz.data.Task; import org.zkoss.ganttz.timetracker.TimeTracker; @@ -115,4 +116,9 @@ public class ContextRelativeToOtherComponent implements IContext { return context.getTasksOrderedByStartDate(); } + @Override + public GanttDiagramGraph getGanttDiagramGraph() { + return context.getGanttDiagramGraph(); + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextWithPlannerTask.java b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextWithPlannerTask.java index a14c5a42b..3c93c7636 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextWithPlannerTask.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/ContextWithPlannerTask.java @@ -24,6 +24,7 @@ import java.util.List; import org.zkoss.ganttz.adapters.IDomainAndBeansMapper; import org.zkoss.ganttz.adapters.PlannerConfiguration; +import org.zkoss.ganttz.data.GanttDiagramGraph; import org.zkoss.ganttz.data.Position; import org.zkoss.ganttz.data.Task; import org.zkoss.ganttz.timetracker.TimeTracker; @@ -117,4 +118,9 @@ public class ContextWithPlannerTask implements IContextWithPlannerTask { return context.getTasksOrderedByStartDate(); } + @Override + public GanttDiagramGraph getGanttDiagramGraph() { + return context.getGanttDiagramGraph(); + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/IContext.java b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/IContext.java index b24c244d7..5cf46c0d4 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/extensions/IContext.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/extensions/IContext.java @@ -28,6 +28,7 @@ import org.zkoss.ganttz.adapters.IAdapterToTaskFundamentalProperties; import org.zkoss.ganttz.adapters.IDomainAndBeansMapper; import org.zkoss.ganttz.adapters.IStructureNavigator; import org.zkoss.ganttz.adapters.PlannerConfiguration; +import org.zkoss.ganttz.data.GanttDiagramGraph; import org.zkoss.ganttz.data.Position; import org.zkoss.ganttz.data.Task; import org.zkoss.ganttz.timetracker.TimeTracker; @@ -121,4 +122,6 @@ public interface IContext { public void reloadCharts(); + public GanttDiagramGraph getGanttDiagramGraph(); + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java index aec34ac31..9f8d7db24 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java @@ -25,6 +25,7 @@ import static org.zkoss.ganttz.i18n.I18nHelper._; import java.util.List; import org.apache.commons.lang.StringUtils; +import org.zkoss.ganttz.IChartVisibilityChangedListener; import org.zkoss.ganttz.data.resourceload.LoadTimeLine; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.TimeTrackerComponent; @@ -43,6 +44,7 @@ import org.zkoss.zk.ui.HtmlMacroComponent; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; +import org.zkoss.zkex.zul.api.South; import org.zkoss.zul.Button; import org.zkoss.zul.Comboitem; import org.zkoss.zul.ListModel; @@ -88,14 +90,28 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private int filterByNamePosition = 0; private int numberOfGroupsByName = 10; - public ResourcesLoadPanel(List groups, - TimeTracker timeTracker, Component componentOnWhichGiveFeedback) { - this.componentOnWhichGiveFeedback = componentOnWhichGiveFeedback; - init(groups, timeTracker); + private WeakReferencedListeners nameFilterListener = + WeakReferencedListeners.create(); + private Component loadChart; + + private boolean visibleChart = true; + + private WeakReferencedListeners chartVisibilityListeners = WeakReferencedListeners + .create(); + + private final boolean expandResourceLoadViewCharts; + + public ResourcesLoadPanel(List groups, + TimeTracker timeTracker, Component componentOnWhichGiveFeedback, + boolean expandResourceLoadViewCharts) { + this.componentOnWhichGiveFeedback = componentOnWhichGiveFeedback; + this.expandResourceLoadViewCharts = expandResourceLoadViewCharts; + init(groups, timeTracker); } public void init(List groups, TimeTracker timeTracker) { + refreshNameFilter = true; this.groups = groups; this.timeTracker = timeTracker; treeModel = createModelForTree(); @@ -280,9 +296,14 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { TimeTrackerComponent timeTrackerHeader = createTimeTrackerHeader(); getFellow("insertionPointTimetracker").appendChild(timeTrackerHeader); - Component additionalFilter = (Component) getVariable("additionalFilter", true); + // Insert additional filters if any + Component additionalFilter = (Component) getVariable("additionalFilter1", true); if(additionalFilter != null) { - getFellow("additionalFilterInsertionPoint").appendChild(additionalFilter); + getFellow("additionalFilterInsertionPoint1").appendChild(additionalFilter); + } + additionalFilter = (Component) getVariable("additionalFilter2", true); + if(additionalFilter != null) { + getFellow("additionalFilterInsertionPoint2").appendChild(additionalFilter); } timeTrackerHeader.afterCompose(); @@ -293,12 +314,18 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { if(refreshNameFilter) { setupNameFilter(); } + + getFellow("insertionPointChart").appendChild(loadChart); + + this.visibleChart = expandResourceLoadViewCharts; + ((South) getFellow("graphics")).setOpen(this.visibleChart); } public void clearComponents() { getFellow("insertionPointLeftPanel").getChildren().clear(); getFellow("insertionPointRightPanel").getChildren().clear(); getFellow("insertionPointTimetracker").getChildren().clear(); + getFellow("insertionPointChart").getChildren().clear(); } private TimeTrackerComponent createTimeTrackerHeader() { @@ -375,7 +402,34 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { public void onSelectFilterByName(Integer filterByNamePosition) { this.filterByNamePosition = filterByNamePosition.intValue(); this.feedBackMessage = _("filtering by name"); - invalidatingChangeHappenedWithFeedback(); + changeNameFilterWithFeedback(); + } + + private void changeNameFilterWithFeedback() { + LongOperationFeedback.execute(componentOnWhichGiveFeedback, + new ILongOperation() { + + @Override + public void doAction() throws Exception { + treeModel = createModelForTree(); + timeTrackerComponent = timeTrackerForResourcesLoadPanel(timeTracker); + resourceLoadList = new ResourceLoadList(timeTracker, treeModel); + leftPane = new ResourceLoadLeftPane(treeModel, resourceLoadList); + registerNeededScripts(); + nameFilterListener.fireEvent(new IListenerNotification() { + @Override + public void doNotify(IFilterChangedListener listener) { + listener.filterChanged(getFilter()); + } + }); + afterCompose(); + } + + @Override + public String getName() { + return getFeedBackMessage(); + } + }); } public void setNameFilterDisabled(boolean disabled) { @@ -387,4 +441,34 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { } } + public void addNameFilterListener( + IFilterChangedListener iFilterChangedListener) { + nameFilterListener.addListener(iFilterChangedListener); + } + + public void changeChartVisibility(boolean visible) { + visibleChart = visible; + chartVisibilityListeners + .fireEvent(new IListenerNotification() { + @Override + public void doNotify( + IChartVisibilityChangedListener listener) { + listener.chartVisibilityChanged(visibleChart); + } + }); + } + + public boolean isVisibleChart() { + return visibleChart; + } + + public void addChartVisibilityListener( + IChartVisibilityChangedListener chartVisibilityChangedListener) { + chartVisibilityListeners.addListener(chartVisibilityChangedListener); + } + + public void setLoadChart(Component loadChart) { + this.loadChart = loadChart; + } + } \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/util/IAction.java b/ganttzk/src/main/java/org/zkoss/ganttz/util/IAction.java new file mode 100644 index 000000000..968cbb191 --- /dev/null +++ b/ganttzk/src/main/java/org/zkoss/ganttz/util/IAction.java @@ -0,0 +1,33 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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 . + */ +package org.zkoss.ganttz.util; + +import org.zkoss.ganttz.data.GanttDiagramGraph; + +/** + * Represents some computation to execute. It's usually used to execute some + * computation in a context. + * @see PreAndPostNotReentrantActionsWrapper + * {@link GanttDiagramGraph#manualNotificationOn(IAction)} + * @author Óscar González Fernández + */ +public interface IAction { + public void doAction(); +} \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/util/PreAndPostNotReentrantActionsWrapper.java b/ganttzk/src/main/java/org/zkoss/ganttz/util/PreAndPostNotReentrantActionsWrapper.java index ea538f2a6..77049f2a2 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/util/PreAndPostNotReentrantActionsWrapper.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/util/PreAndPostNotReentrantActionsWrapper.java @@ -26,10 +26,6 @@ import org.apache.commons.lang.Validate; */ public abstract class PreAndPostNotReentrantActionsWrapper { - public interface IAction { - public void doAction(); - } - private final ThreadLocal inside = new ThreadLocal() { @Override protected Boolean initialValue() { diff --git a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul index 950b90d85..6b36e72a0 100644 --- a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul +++ b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul @@ -38,17 +38,19 @@ resourcesLoadPanel = self; onSelect="resourcesLoadPanel.setZoomLevel(self.selectedItem.value);" > + + Filter: - - ${i18n:_('Show elements between')}: - - + @@ -85,35 +87,10 @@ resourcesLoadPanel = self; - - -
- - - - - ${i18n:_('Assignation percentage')} - - - - - ${i18n:_('0% - 100%')} - - - - - ${i18n:_('100%')} - - - - - ${i18n:_('+ 100%')} - - - - -
-
+ +
diff --git a/ganttzk/src/main/resources/web/js/ganttz/resourceload/resourcesloadlist.js b/ganttzk/src/main/resources/web/js/ganttz/resourceload/resourcesloadlist.js index b1b127a2d..aa11002fa 100644 --- a/ganttzk/src/main/resources/web/js/ganttz/resourceload/resourcesloadlist.js +++ b/ganttzk/src/main/resources/web/js/ganttz/resourceload/resourcesloadlist.js @@ -47,7 +47,6 @@ function addResourcesLoadListMethods(object) { return YAHOO.util.Selector.query('.rightpanellayout div')[0]; } - function timetrackergap() { return YAHOO.util.Selector.query('.timetrackergap')[0]; } @@ -56,6 +55,9 @@ function addResourcesLoadListMethods(object) { return YAHOO.util.Selector.query('.leftpanelgap .z-tree-body')[0]; } + function rightpanel() { + return YAHOO.util.Selector.query('.rightpanellayout div')[0]; + } object.init = function(cmp) { this.adjustTimeTrackerSize(cmp); @@ -73,16 +75,16 @@ function addResourcesLoadListMethods(object) { var scrolledpannel_ = scrolledpannel(); var resourcesloadgraph_ = resourcesloadgraph(); var leftpanel_ = leftpanel(); + var rightpanel_ = rightpanel(); var onScroll = function() { - timetrackergap_.style["left"] = "-" + scrolledpannel_.scrollLeft + "px"; + var timeplotcontainer_ = YAHOO.util.Selector.query('canvas.timeplot-canvas')[0]; + timeplotcontainer_.style["left"] = "-" + scrolledpannel_.scrollLeft + "px"; leftpanel_.style["top"] = "-" + scrolledpannel_.scrollTop + "px"; resourcesloadgraph_.scrollLeft = scrolledpannel_.scrollLeft; - }; - YAHOO.util.Selector.query('.rightpanellayout div')[0].onscroll = onScroll; - + rightpanel_.onscroll = onScroll; } object.adjustTimeTrackerSize = function(cmp) { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java index 99abba5c8..d5ffa96a4 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java @@ -64,6 +64,15 @@ public class ResourceCalendar extends BaseCalendar { addNewCalendarAvailability(calendarAvailability); } + public Integer getCapacity(LocalDate from, LocalDate to) { + Integer result = getCapacityAt(to); + for (LocalDate date = from; date.isBefore(to);) { + result += getCapacityAt(date); + date = date.plusDays(1); + } + return result; + } + @Override public Integer getCapacityAt(LocalDate date) { if (!isActive(date)) { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java index 62c6a9428..aa58d0354 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java @@ -59,6 +59,8 @@ public class Configuration extends BaseEntity { private Boolean expandOrderPlanningViewCharts = true; + private Boolean expandResourceLoadViewCharts = true; + public void setDefaultCalendar(BaseCalendar defaultCalendar) { this.defaultCalendar = defaultCalendar; } @@ -165,4 +167,13 @@ public class Configuration extends BaseEntity { return expandOrderPlanningViewCharts; } + public void setExpandResourceLoadViewCharts( + Boolean expandResourceLoadViewCharts) { + this.expandResourceLoadViewCharts = expandResourceLoadViewCharts; + } + + public Boolean isExpandResourceLoadViewCharts() { + return expandResourceLoadViewCharts; + } + } \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java index a2f45c6d1..ef01ff8c1 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java @@ -22,14 +22,19 @@ package org.navalplanner.business.planner.daos; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import org.hibernate.Criteria; import org.hibernate.Query; +import org.hibernate.criterion.Restrictions; +import org.joda.time.LocalDate; import org.navalplanner.business.common.daos.GenericDAOHibernate; import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.DerivedDayAssignment; import org.navalplanner.business.planner.entities.GenericDayAssignment; import org.navalplanner.business.planner.entities.SpecificDayAssignment; +import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.scenarios.entities.Scenario; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -57,34 +62,100 @@ public class DayAssignmentDAO extends GenericDAOHibernate @Override public List getAllFor(Scenario scenario) { List result = new ArrayList(); - result.addAll(getSpecific(scenario)); - result.addAll(getGeneric(scenario)); - result.addAll(getDerived(scenario)); + result.addAll(getSpecific(scenario, null, null)); + result.addAll(getGeneric(scenario, null, null)); + result.addAll(getDerived(scenario, null, null)); return result; } - private List getDerived(Scenario scenario) { + public List getAllFor(Scenario scenario, LocalDate init, + LocalDate end) { + List result = new ArrayList(); + result.addAll(getSpecific(scenario, init, end)); + result.addAll(getGeneric(scenario, init, end)); + result.addAll(getDerived(scenario, init, end)); + return result; + } + + private List getDerived(Scenario scenario, + LocalDate initInclusive, LocalDate endInclusive) { String queryString = "select d from DerivedDayAssignmentsContainer c " - + "JOIN c.dayAssignments d where c.scenario = :scenario"; - Query query = getSession().createQuery(queryString) - .setParameter("scenario", scenario); + + "JOIN c.dayAssignments d where c.scenario = :scenario" + + addQueryConditionForInitAndEndDate(initInclusive, + endInclusive); + Query query = getSession().createQuery(queryString); + query = query.setParameter("scenario", scenario); + addInitAndEndParameters(query, initInclusive, endInclusive); return query.list(); } - private List getGeneric(Scenario scenario) { + private String addQueryConditionForInitAndEndDate(LocalDate initInclusive, + LocalDate endInclusive) { + String initCondition = initInclusive != null ? " and d.day >= :init" + : ""; + String endCondition = endInclusive != null ? " and d.day <= :end" : ""; + return initCondition + endCondition; + } + + private Query addInitAndEndParameters(Query query, LocalDate initInclusive, + LocalDate endInclusive) { + if (initInclusive != null) { + query.setParameter("init", initInclusive); + } + if (endInclusive != null) { + query.setParameter("end", endInclusive); + } + return query; + } + + private List getGeneric(Scenario scenario, + LocalDate initInclusive, LocalDate endInclusive) { String queryString = "select d from GenericDayAssignmentsContainer c " - + "JOIN c.dayAssignments d where c.scenario = :scenario"; + + "JOIN c.dayAssignments d where c.scenario = :scenario" + + addQueryConditionForInitAndEndDate(initInclusive, + endInclusive); Query query = getSession().createQuery(queryString).setParameter( "scenario", scenario); + query = addInitAndEndParameters(query, initInclusive, endInclusive); return query.list(); } - private List getSpecific(Scenario scenario) { + private List getSpecific(Scenario scenario, + LocalDate initInclusive, LocalDate endInclusive) { String queryString = "select d from SpecificDayAssignmentsContainer c " - + "JOIN c.dayAssignments d where c.scenario = :scenario"; + + "JOIN c.dayAssignments d where c.scenario = :scenario" + + addQueryConditionForInitAndEndDate(initInclusive, + endInclusive); Query query = getSession().createQuery(queryString).setParameter( "scenario", scenario); + query = addInitAndEndParameters(query, initInclusive, endInclusive); return query.list(); } + @SuppressWarnings("unchecked") + public List listFilteredByDate(LocalDate init, LocalDate end) { + Criteria criteria = getSession().createCriteria(DayAssignment.class); + addDateRestrictionsToDayAssignmentQuery(criteria, init, end); + return criteria.list(); + } + + private void addDateRestrictionsToDayAssignmentQuery(Criteria criteria, + LocalDate init, LocalDate end) { + if(init != null) { + criteria.add(Restrictions.ge("day", init)); + } + if(end != null) { + criteria.add(Restrictions.le("day", end)); + } + } + + @Override + public List findByResources(List resources) { + if (resources.isEmpty()) { + return Collections.emptyList(); + } + return getSession().createCriteria(DayAssignment.class).add( + Restrictions.in("resource", resources)).list(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DependencyDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DependencyDAO.java new file mode 100644 index 000000000..6117fe707 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DependencyDAO.java @@ -0,0 +1,24 @@ +package org.navalplanner.business.planner.daos; + +import java.util.List; + +import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.planner.entities.Dependency; +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; + +/** + * DAO for entity @{link Dedenpency} + * @author Javier Moran Rua + * + */ + +@Repository +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class DependencyDAO extends GenericDAOHibernate + implements IDependencyDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java index d26c060ac..dd20bbe77 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java @@ -23,9 +23,11 @@ package org.navalplanner.business.planner.daos; import java.util.Collection; import java.util.List; +import org.joda.time.LocalDate; import org.navalplanner.business.common.daos.IGenericDAO; import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.DerivedDayAssignment; +import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.scenarios.entities.Scenario; /** @@ -34,12 +36,18 @@ import org.navalplanner.business.scenarios.entities.Scenario; * @author @author Diego Pino García * @author Manuel Rego Casasnovas */ -public interface IDayAssignmentDAO extends - IGenericDAO { +public interface IDayAssignmentDAO extends IGenericDAO { public void removeDerived( Collection derivedAllocations); public List getAllFor(Scenario scenario); + public List getAllFor(Scenario scenario, + LocalDate initInclusive, LocalDate endInclusive); + + List listFilteredByDate(LocalDate init, LocalDate end); + + public List findByResources(List resources); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDependencyDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDependencyDAO.java new file mode 100644 index 000000000..f7bd9fa72 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDependencyDAO.java @@ -0,0 +1,17 @@ +package org.navalplanner.business.planner.daos; + +import java.util.List; + +import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.planner.entities.Dependency; + +/** + * Interface for interface for repositories related with @{link Dependency} + * entity + * + * @author Javier Moran Rua + * + */ +public interface IDependencyDAO extends IGenericDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java index d08cfd66d..47b4cfdea 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java @@ -20,8 +20,11 @@ package org.navalplanner.business.planner.daos; +import java.util.List; + import org.navalplanner.business.common.daos.IGenericDAO; import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; /** * DAO interface for {@link ILimitingResourceQueueDAO} @@ -31,4 +34,8 @@ import org.navalplanner.business.resources.entities.LimitingResourceQueue; public interface ILimitingResourceQueueDAO extends IGenericDAO { + LimitingResourceQueue findQueueByResource(Resource resource); + + List getAll(); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDependencyDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDependencyDAO.java new file mode 100644 index 000000000..121d8f528 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDependencyDAO.java @@ -0,0 +1,18 @@ +package org.navalplanner.business.planner.daos; + +import java.util.List; + +import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; + +/** + * Interface for repositories to implement queies related to + * @{link LimitingResourceQueueDependency} entities + * + * @author Javier Moran Rua + * + */ +public interface ILimitingResourceQueueDependencyDAO extends + IGenericDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IResourceAllocationDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IResourceAllocationDAO.java index 9a164dc82..2b831cd7f 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IResourceAllocationDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IResourceAllocationDAO.java @@ -21,6 +21,7 @@ package org.navalplanner.business.planner.daos; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Map; @@ -44,10 +45,19 @@ public interface IResourceAllocationDAO extends List> findAllocationsRelatedToAnyOf( List resources); + List> findAllocationsRelatedToAnyOf( + List resources, Date intervalFilterStartDate, Date intervalFilterEndDate); + List> findAllocationsRelatedTo(Resource resource); + List> findAllocationsRelatedTo(Resource resource, + Date intervalFilterStartDate, Date intervalFilterEndDate); + Map> findGenericAllocationsByCriterion(); + Map> findGenericAllocationsByCriterion( + Date intervalFilterStartDate, Date intervalFilterEndDate); + List getSpecificAssignmentsBetween( Collection relatedToOne, LocalDate start, LocalDate end); @@ -57,4 +67,9 @@ public interface IResourceAllocationDAO extends Map> findGenericAllocationsBySomeCriterion( List criterions); + + Map> findGenericAllocationsBySomeCriterion( + List criterions, Date intervalFilterStartDate, + Date intervalFilterEndDate); + } \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java index a77f5a1c4..7cd6a7570 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ITaskElementDAO.java @@ -20,6 +20,7 @@ package org.navalplanner.business.planner.daos; +import java.util.Date; import java.util.List; import org.joda.time.LocalDate; @@ -43,4 +44,6 @@ public interface ITaskElementDAO extends IGenericDAO { List getCompletedEstimatedHoursPerTaskReport( Order order, LocalDate deadline); + List listFilteredByDate(Date start, Date end); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java index f64eb94ef..ad3a1720e 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java @@ -20,8 +20,12 @@ package org.navalplanner.business.planner.daos; +import java.util.List; + +import org.hibernate.criterion.Restrictions; import org.navalplanner.business.common.daos.GenericDAOHibernate; import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; @@ -37,4 +41,15 @@ public class LimitingResourceQueueDAO extends GenericDAOHibernate implements ILimitingResourceQueueDAO { + public LimitingResourceQueue findQueueByResource(Resource resource) { + return (LimitingResourceQueue) getSession().createCriteria( + LimitingResourceQueue.class).add( + Restrictions.eq("resource", resource)).uniqueResult(); + } + + @Override + public List getAll() { + return list(LimitingResourceQueue.class); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDependencyDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDependencyDAO.java new file mode 100644 index 000000000..226615763 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDependencyDAO.java @@ -0,0 +1,24 @@ +package org.navalplanner.business.planner.daos; + +import java.util.List; + +import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.planner.entities.Dependency; +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; + +/* + * DAO for the entity @{link LimitingResourceQueueDependency} + * + * @author Javier Moran Rua + */ + +@Repository +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class LimitingResourceQueueDependencyDAO + extends GenericDAOHibernate + implements ILimitingResourceQueueDependencyDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ResourceAllocationDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ResourceAllocationDAO.java index 3ec079e15..55e6c5aff 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ResourceAllocationDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ResourceAllocationDAO.java @@ -23,12 +23,14 @@ package org.navalplanner.business.planner.daos; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.hibernate.Criteria; +import org.hibernate.Query; import org.hibernate.criterion.Restrictions; import org.joda.time.LocalDate; import org.navalplanner.business.common.daos.GenericDAOHibernate; @@ -57,28 +59,64 @@ public class ResourceAllocationDAO extends public List> findAllocationsRelatedToAnyOf( List resources) { List> result = new ArrayList>(); - result.addAll(findSpecificAllocationsRelatedTo(resources)); - result.addAll(findGenericAllocationsFor(resources)); + result.addAll(findSpecificAllocationsRelatedTo(resources, null, null)); + result.addAll(findGenericAllocationsFor(resources, null, null)); + return result; + } + + @Override + public List> findAllocationsRelatedToAnyOf( + List resources, Date intervalFilterStartDate, Date intervalFilterEndDate) { + List> result = new ArrayList>(); + result.addAll(findSpecificAllocationsRelatedTo(resources, intervalFilterStartDate, intervalFilterEndDate)); + result.addAll(findGenericAllocationsFor(resources, intervalFilterStartDate, intervalFilterEndDate)); return result; } @SuppressWarnings("unchecked") - private List findGenericAllocationsFor( - List resources) { - return (List) getSession().createCriteria( - GenericResourceAllocation.class).setResultTransformer( - Criteria.DISTINCT_ROOT_ENTITY).createCriteria( + private List findGenericAllocationsFor(List resources, Date intervalFilterStartDate, Date intervalFilterEndDate) { + if(resources.isEmpty()) { + return new ArrayList(); + } + Criteria criteria = getSession().createCriteria(GenericResourceAllocation.class); + criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) + .createCriteria( "genericDayAssignmentsContainers").createCriteria( "dayAssignments").add( - Restrictions.in("resource", resources)).list(); + Restrictions.in("resource", resources)); + + if(intervalFilterStartDate != null || intervalFilterEndDate != null) { + Criteria dateCriteria = criteria.createCriteria("task"); + if(intervalFilterEndDate != null) { + dateCriteria.add(Restrictions.le("startDate", intervalFilterEndDate)); + } + if(intervalFilterStartDate != null) { + dateCriteria.add(Restrictions.ge("endDate", intervalFilterStartDate)); + } + } + return (List) criteria.list(); } @SuppressWarnings("unchecked") private List findSpecificAllocationsRelatedTo( - List resources) { - return (List) getSession().createCriteria( - SpecificResourceAllocation.class).add( - Restrictions.in("resource", resources)).list(); + List resources, Date intervalFilterStartDate, Date intervalFilterEndDate) { + if(resources.isEmpty()) { + return new ArrayList(); + } + Criteria criteria = getSession().createCriteria( + SpecificResourceAllocation.class); + criteria.add(Restrictions.in("resource", resources)); + + if(intervalFilterStartDate != null || intervalFilterEndDate != null) { + Criteria dateCriteria = criteria.createCriteria("task"); + if(intervalFilterEndDate != null) { + dateCriteria.add(Restrictions.le("startDate", intervalFilterEndDate)); + } + if(intervalFilterStartDate != null) { + dateCriteria.add(Restrictions.ge("endDate", intervalFilterStartDate)); + } + } + return (List) criteria.list(); } @Override @@ -88,6 +126,13 @@ public class ResourceAllocationDAO extends .asList(resource))); } + @Override + public List> findAllocationsRelatedTo( + Resource resource, Date intervalFilterStartDate, Date intervalFilterEndDate) { + return stripAllocationsWithoutAssignations(findAllocationsRelatedToAnyOf(Arrays + .asList(resource), intervalFilterStartDate, intervalFilterEndDate)); + } + private > List stripAllocationsWithoutAssignations( List allocations) { List result = new ArrayList(); @@ -124,6 +169,38 @@ public class ResourceAllocationDAO extends return stripAllocationsWithoutAssignations(byCriterion(results)); } + @SuppressWarnings("unchecked") + @Override + public Map> findGenericAllocationsByCriterion( + Date intervalFilterStartDate, Date intervalFilterEndDate) { + String query = "select generic, criterion " + + "from GenericResourceAllocation as generic " + + "join generic.criterions as criterion "; + if(intervalFilterStartDate != null || intervalFilterEndDate != null) { + query += "inner join generic.task as task "; + if(intervalFilterEndDate != null) { + query += "where task.startDate <= :intervalFilterEndDate "; + } + if(intervalFilterStartDate != null) { + if(intervalFilterEndDate != null) { + query += "and "; + } + else { + query += "where "; + } + query += "task.endDate >= :intervalFilterStartDate "; + } + } + Query q = getSession().createQuery(query); + if(intervalFilterStartDate != null) { + q.setParameter("intervalFilterStartDate", intervalFilterStartDate); + } + if(intervalFilterEndDate != null) { + q.setParameter("intervalFilterEndDate", intervalFilterEndDate); + } + return stripAllocationsWithoutAssignations(byCriterion(q.list())); + } + @SuppressWarnings("unchecked") @Override public Map> findGenericAllocationsByCriterionFor( @@ -156,6 +233,38 @@ public class ResourceAllocationDAO extends return stripAllocationsWithoutAssignations(byCriterion(list)); } + @SuppressWarnings("unchecked") + @Override + public Map> findGenericAllocationsBySomeCriterion( + List criterions, Date intervalFilterStartDate, Date intervalFilterEndDate) { + if (criterions.isEmpty()) { + return new HashMap>(); + } + String query = "select generic, criterion " + + "from GenericResourceAllocation as generic " + + "join generic.criterions as criterion "; + if(intervalFilterStartDate != null || intervalFilterEndDate != null) { + query += "inner join generic.task as task "; + } + query += "where criterion in(:criterions) "; + if(intervalFilterEndDate != null) { + query += "and task.startDate <= :intervalFilterEndDate "; + } + if(intervalFilterStartDate != null) { + query += "and task.endDate >= :intervalFilterStartDate "; + } + + Query q = getSession().createQuery(query); + q.setParameterList("criterions", criterions); + if(intervalFilterStartDate != null) { + q.setParameter("intervalFilterStartDate", intervalFilterStartDate); + } + if(intervalFilterEndDate != null) { + q.setParameter("intervalFilterEndDate", intervalFilterEndDate); + } + return stripAllocationsWithoutAssignations(byCriterion(q.list())); + } + private Map> byCriterion( List results) { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java index 162f6a5d8..2f881ff17 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/TaskElementDAO.java @@ -21,8 +21,10 @@ package org.navalplanner.business.planner.daos; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import org.hibernate.Criteria; import org.hibernate.Query; import org.hibernate.criterion.Restrictions; import org.joda.time.LocalDate; @@ -150,4 +152,17 @@ public class TaskElementDAO extends GenericDAOHibernate } return result; } + + @Override + @SuppressWarnings("unchecked") + public List listFilteredByDate(Date start, Date end) { + Criteria criteria = getSession().createCriteria(TaskElement.class); + if(start != null) { + criteria.add(Restrictions.ge("endDate", start)); + } + if(end != null) { + criteria.add(Restrictions.le("startDate", end)); + } + return criteria.list(); + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DateAndHour.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DateAndHour.java new file mode 100644 index 000000000..393cff5e2 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DateAndHour.java @@ -0,0 +1,83 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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 . + */ + +package org.navalplanner.business.planner.entities; + +import org.joda.time.LocalDate; + +/** + * + * @author Diego Pino Garcia + * + */ +public class DateAndHour implements Comparable { + + private LocalDate date; + + private Integer hour; + + public DateAndHour(LocalDate date, Integer hour) { + this.date = date; + this.hour = hour; + } + + public LocalDate getDate() { + return date; + } + + public Integer getHour() { + return hour; + } + + @Override + public int compareTo(DateAndHour dateAndTime) { + int compareDate = date.compareTo(getDate(dateAndTime)); + return (compareDate != 0) ? compareDate : compareHour(dateAndTime + .getHour()); + } + + private LocalDate getDate(DateAndHour dateAndHour) { + return (dateAndHour != null) ? dateAndHour.getDate() : null; + } + + private int compareHour(int hour) { + int deltaHour = this.hour - hour; + return (deltaHour != 0) ? deltaHour / Math.abs(deltaHour) : 0; + } + + public String toString() { + return date + "; " + hour; + } + + public static DateAndHour Max(DateAndHour arg0, DateAndHour arg1) { + if (arg0 == null) { + return arg1; + } + if (arg1 == null) { + return arg0; + } + return (arg0.compareTo(arg1) > 0) ? arg0 : arg1; + } + + public boolean isBefore(DateAndHour dateAndHour) { + return (this.compareTo(dateAndHour) < 0); + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Dependency.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Dependency.java index 426bb00e8..9742f9426 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Dependency.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Dependency.java @@ -24,7 +24,11 @@ import org.apache.commons.lang.Validate; import org.navalplanner.business.common.BaseEntity; /** + * Entity which represents an associated with properties + * between two @{link Task} + * * @author Óscar González Fernández + * @author Javier Moran Rua */ public class Dependency extends BaseEntity { @@ -45,6 +49,8 @@ public class Dependency extends BaseEntity { private TaskElement destination; + private LimitingResourceQueueDependency queueDependency; + private Type type; /** @@ -76,4 +82,20 @@ public class Dependency extends BaseEntity { return type; } + public void setQueueDependency(LimitingResourceQueueDependency queueDependency) { + this.queueDependency = queueDependency; + } + + public LimitingResourceQueueDependency getQueueDependency() { + return queueDependency; + } + + public boolean isDependencyBetweenLimitedAllocatedTasks() { + return getOrigin().hasLimitedResourceAllocation() && + getDestination().hasLimitedResourceAllocation(); + } + + public boolean hasLimitedQueueDependencyAssociated() { + return queueDependency != null; + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java index 66110949b..c1143d6a8 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericResourceAllocation.java @@ -397,6 +397,25 @@ public class GenericResourceAllocation extends return assignmentsState; } + private List getDayAssignmentsByConsolidated( + boolean consolidated) { + List result = new ArrayList(); + for (GenericDayAssignment day : getAssignments()) { + if (day.isConsolidated() == consolidated) { + result.add(day); + } + } + return result; + } + + public List getNonConsolidatedAssignments() { + return getDayAssignmentsByConsolidated(false); + } + + public List getConsolidatedAssignments() { + return getDayAssignmentsByConsolidated(true); + } + @Override protected Class getDayAssignmentType() { return GenericDayAssignment.class; 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 d86fb4429..e7b678c21 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 @@ -51,6 +51,12 @@ public class HoursCostCalculator implements ICostCalculator { @Override public SortedMap getAdvanceCost(Task task) { + return getAdvanceCost(task, null, null); + } + + @Override + public SortedMap getAdvanceCost(Task task, + LocalDate filterStartDate, LocalDate filterEndDate) { DirectAdvanceAssignment advanceAssignment = task.getOrderElement() .getReportGlobalAdvanceAssignment(); @@ -59,19 +65,25 @@ public class HoursCostCalculator implements ICostCalculator { } return calculateHoursPerDay(task.getHoursSpecifiedAtOrder(), - advanceAssignment.getAdvanceMeasurements()); + advanceAssignment.getAdvanceMeasurements(), + filterStartDate, filterEndDate); } private SortedMap calculateHoursPerDay( Integer totalHours, - SortedSet advanceMeasurements) { + SortedSet advanceMeasurements, + LocalDate filterStartDate, LocalDate filterEndDate) { SortedMap result = new TreeMap(); for (AdvanceMeasurement advanceMeasurement : advanceMeasurements) { - BigDecimal cost = advanceMeasurement.getValue().setScale(2) - .multiply(new BigDecimal(totalHours)).divide( - new BigDecimal(100)); - result.put(advanceMeasurement.getDate(), cost); + LocalDate day = advanceMeasurement.getDate(); + if(((filterStartDate == null) || day.compareTo(filterStartDate) >= 0) && + ((filterEndDate == null) || day.compareTo(filterEndDate) <= 0)) { + BigDecimal cost = advanceMeasurement.getValue().setScale(2) + .multiply(new BigDecimal(totalHours)).divide( + new BigDecimal(100)); + result.put(day, cost); + } } return result; @@ -79,6 +91,12 @@ public class HoursCostCalculator implements ICostCalculator { @Override public SortedMap getEstimatedCost(Task task) { + return getEstimatedCost(task, null, null); + } + + @Override + public SortedMap getEstimatedCost(Task task, + LocalDate filterStartDate, LocalDate filterEndDate) { if (task.isSubcontracted()) { return getAdvanceCost(task); } @@ -93,12 +111,15 @@ public class HoursCostCalculator implements ICostCalculator { for (DayAssignment dayAssignment : dayAssignments) { LocalDate day = dayAssignment.getDay(); - BigDecimal cost = new BigDecimal(dayAssignment.getHours()); + if(((filterStartDate == null) || day.compareTo(filterStartDate) >= 0) && + ((filterEndDate == null) || day.compareTo(filterEndDate) <= 0)) { + BigDecimal cost = new BigDecimal(dayAssignment.getHours()); - if (!result.containsKey(day)) { - result.put(day, BigDecimal.ZERO); + if (!result.containsKey(day)) { + result.put(day, BigDecimal.ZERO); + } + result.put(day, result.get(day).add(cost)); } - result.put(day, result.get(day).add(cost)); } return result; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ICostCalculator.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ICostCalculator.java index 64bc5b9b9..eb72dbcda 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ICostCalculator.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ICostCalculator.java @@ -38,4 +38,10 @@ public interface ICostCalculator { public SortedMap getWorkReportCost(Task task); + SortedMap getEstimatedCost(Task task, + LocalDate filterStartDate, LocalDate filterEndDate); + + SortedMap getAdvanceCost(Task task, + LocalDate filterStartDate, LocalDate filterEndDate); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceAllocator.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceAllocator.java new file mode 100644 index 000000000..b6b7c2e20 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceAllocator.java @@ -0,0 +1,285 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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 . + */ + +package org.navalplanner.business.planner.entities; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.navalplanner.business.calendars.entities.ResourceCalendar; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionCompounder; +import org.navalplanner.business.resources.entities.ICriterion; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; + +/** + * Handles all the logic related to allocation of + * {@link LimitingResourceQueueElement} into {@link LimitingResourceQueue} + * + * The class does not do the allocation itself but provides methods: + * getFirstValidGap, calculateStartAndEndTime or + * generateDayAssignments, needed to do the allocation of + * {@link LimitingResourceQueueElement} + * + * @author Diego Pino Garcia + * + */ +public class LimitingResourceAllocator { + + private final static ResourcesPerDay ONE_RESOURCE_PER_DAY = ResourcesPerDay + .amount(new BigDecimal(1)); + + /** + * Returns first valid gap in queue for element + * + * Returns null if there is not a valid gap. This case can only happen on + * trying to allocate an element related to a generic resource allocation. + * It is possible that queue.resource does not hold element.criteria at any + * interval of time + * + * @param queue search gap inside queue + * @param element element to fit into queue + * @return + */ + public static LimitingResourceQueueElementGap getFirstValidGap(LimitingResourceQueue queue, + LimitingResourceQueueElement element) { + + final Resource resource = queue.getResource(); + final List elements = new LinkedList( + queue.getLimitingResourceQueueElements()); + final int size = elements.size(); + + // Iterate through queue elements + for (int pos = 0; pos <= size; pos++) { + + LimitingResourceQueueElementGap gap = getGapInQueueAtPosition( + resource, elements, element, pos); + + // The queue cannot hold this element (perhaps queue.resource + // doesn't meet element.criteria) + if (gap == null) { + return null; + } + + if (canFitIntoGap(element, gap, resource)) { + return gap; + } + } + return null; + } + + private static boolean canFitIntoGap(LimitingResourceQueueElement element, + LimitingResourceQueueElementGap gap, final Resource resource) { + + final boolean canfit = gap.canFit(element); + final ResourceAllocation resourceAllocation = element + .getResourceAllocation(); + + if (resourceAllocation instanceof SpecificResourceAllocation) { + return canfit; + } else if (resourceAllocation instanceof GenericResourceAllocation) { + // Resource must satisfy element.criteria during for the + // period of time the element will be allocated in the + // queue + final GenericResourceAllocation generic = (GenericResourceAllocation) resourceAllocation; + List dayAssignments = generateDayAssignments( + resourceAllocation, resource, gap.getStartTime()); + DateAndHour[] startAndEndTime = calculateStartAndEndTime(dayAssignments); + return canfit + && (satisfiesCriteriaDuringInterval(resource, generic + .getCriterions(), startAndEndTime)); + } + return false; + } + + /** + * Calculates start and end date out of a list of day assignments. + * + * The first day is the day were the first day assignment happened. + * The last day is the day were the last day assignment happened. + * @param dayAssignments + * @return + */ + public static DateAndHour[] calculateStartAndEndTime(List dayAssignments) { + DateAndHour[] result = new DateAndHour[2]; + + final DayAssignment start = dayAssignments.get(0); + final DayAssignment end = dayAssignments.get(dayAssignments.size() - 1); + result[0] = new DateAndHour(start.getDay(), start.getHours()); + result[1] = new DateAndHour(end.getDay(), end.getHours()); + + return result; + } + + private static boolean satisfiesCriteriaDuringInterval(Resource resource, Set criteria, DateAndHour[] interval) { + final Date startDate = interval[0].getDate().toDateTimeAtStartOfDay().toDate(); + final Date endDate = interval[1].getDate().toDateTimeAtStartOfDay().toDate(); + return satisfiesCriteriaDuringInterval(resource, criteria, startDate, endDate); + } + + private static boolean satisfiesCriteriaDuringInterval(Resource resource, Set criteria, Date startDate, Date endDate) { + ICriterion compositedCriterion = CriterionCompounder.buildAnd(criteria) + .getResult(); + return compositedCriterion.isSatisfiedBy(resource, startDate, endDate); + } + + private static LimitingResourceQueueElementGap getGapInQueueAtPosition( + Resource resource, List elements, + LimitingResourceQueueElement element, int pos) { + + final int size = elements.size(); + final DateAndHour startTimeBecauseOfGantt = getStartTimeBecauseOfGantt(element); + + if (size > 0) { + + if (pos == size) { + return createLastGap(element, elements.get(size - 1), resource); + } + + LimitingResourceQueueElement current = elements.get(pos); + // First element + if (pos == 0 + && startTimeBecauseOfGantt.getDate().isBefore( + current.getStartDate())) { + return LimitingResourceQueueElementGap.create(resource, + startTimeBecauseOfGantt, current.getStartTime()); + } + + // Rest of elements + if (pos + 1 < size) { + LimitingResourceQueueElement next = elements.get(pos + 1); + if (startTimeBecauseOfGantt.isBefore(current.getEndTime())) { + return LimitingResourceQueueElementGap.create(resource, + current.getEndTime(), next.getStartTime()); + } else { + return LimitingResourceQueueElementGap.create(resource, + DateAndHour.Max(current.getEndTime(), + startTimeBecauseOfGantt), next + .getStartTime()); + } + } else { + // Current was the last element + return createLastGap(element, current, resource); + } + } + return null; + } + + private static DateAndHour getStartTimeBecauseOfGantt(LimitingResourceQueueElement element) { + return new DateAndHour(new LocalDate(element.getEarlierStartDateBecauseOfGantt()), 0); + } + + private static LimitingResourceQueueElementGap createLastGap( + LimitingResourceQueueElement candidate, + LimitingResourceQueueElement element, Resource resource) { + + DateAndHour startTime = DateAndHour.Max( + getStartTimeBecauseOfGantt(candidate), element.getEndTime()); + return LimitingResourceQueueElementGap + .create(resource, startTime, null); + } + + /** + * Generates a list of {@link DayAssignment} for {@link Resource} starting + * from startTime + * + * The returned list is not associated to resouceAllocation. + * + * resourceAllocation is passed to know if the list of day assignments + * should be {@link GenericDayAssignment} or {@link SpecificDayAssignment} + * + * @param resourceAllocation + * @param resource + * @param startTime + * @return + */ + public static List generateDayAssignments( + ResourceAllocation resourceAllocation, + Resource resource, + DateAndHour startTime) { + + List assignments = new ArrayList(); + + LocalDate date = startTime.getDate(); + int totalHours = resourceAllocation.getIntendedTotalHours(); + + // Generate first day assignment + int hoursCanAllocate = hoursCanWorkOnDay(resource, date, startTime.getHour()); + if (hoursCanAllocate > 0) { + int hoursToAllocate = Math.min(totalHours, hoursCanAllocate); + DayAssignment dayAssignment = createDayAssignment(resourceAllocation, resource, date, hoursToAllocate); + totalHours -= addDayAssignment(assignments, dayAssignment); + } + + // Generate rest of day assignments + for (date = date.plusDays(1); totalHours > 0; date = date.plusDays(1)) { + totalHours -= addDayAssignment(assignments, generateDayAssignment( + resourceAllocation, resource, date, totalHours)); + } + return assignments; + } + + private static DayAssignment createDayAssignment(ResourceAllocation resourceAllocation, + Resource resource, LocalDate date, int hoursToAllocate) { + if (resourceAllocation instanceof SpecificResourceAllocation) { + return SpecificDayAssignment.create(date, hoursToAllocate, resource); + } else if (resourceAllocation instanceof GenericResourceAllocation) { + return GenericDayAssignment.create(date, hoursToAllocate, resource); + } + return null; + } + + private static int addDayAssignment(List list, DayAssignment dayAssignment) { + if (dayAssignment != null) { + list.add(dayAssignment); + return dayAssignment.getHours(); + } + return 0; + } + + private static int hoursCanWorkOnDay(final Resource resource, + final LocalDate date, int alreadyWorked) { + final ResourceCalendar calendar = resource.getCalendar(); + int hoursCanAllocate = calendar.toHours(date, ONE_RESOURCE_PER_DAY); + return hoursCanAllocate - alreadyWorked; + } + + private static DayAssignment generateDayAssignment( + final ResourceAllocation resourceAllocation, + Resource resource, + final LocalDate date, int intentedHours) { + + final ResourceCalendar calendar = resource.getCalendar(); + + int hoursCanAllocate = calendar.toHours(date, ONE_RESOURCE_PER_DAY); + if (hoursCanAllocate > 0) { + int hoursToAllocate = Math.min(intentedHours, hoursCanAllocate); + return createDayAssignment(resourceAllocation, resource, date, hoursToAllocate); + } + return null; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueDependency.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueDependency.java new file mode 100644 index 000000000..6af565b50 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueDependency.java @@ -0,0 +1,103 @@ +package org.navalplanner.business.planner.entities; + +import static org.navalplanner.business.i18n.I18nHelper._; + +import java.util.EnumMap; + +import org.apache.commons.lang.Validate; +import org.navalplanner.business.common.BaseEntity; + + +/** + * Entity which represents the relationships between two + * @{link LimitingResourceQueueElement}. One of the + * @{link LimitingResourceQueueElement} is the origin of the relationship + * and the other is the destiny of the relationship. + * + * @author Javier Moran Rua + * + */ +public class LimitingResourceQueueDependency extends BaseEntity { + + public static enum QueueDependencyType { + START_START, END_START, END_END, START_END + }; + + private static + EnumMap translationMap; + + static { + translationMap = new EnumMap(Dependency.Type.class); + translationMap.put(Dependency.Type.START_START, + QueueDependencyType.START_START); + translationMap.put(Dependency.Type.START_END, + QueueDependencyType.START_END); + translationMap.put(Dependency.Type.END_START, + QueueDependencyType.END_START); + translationMap.put(Dependency.Type.END_END, + QueueDependencyType.END_END); + } + + private LimitingResourceQueueElement hasAsOrigin; + private LimitingResourceQueueElement hasAsDestiny; + private Dependency ganttDependency; + private QueueDependencyType type; + + public static LimitingResourceQueueDependency create( + LimitingResourceQueueElement origin, + LimitingResourceQueueElement destiny, + Dependency ganttDependency, + QueueDependencyType type) { + LimitingResourceQueueDependency dependency = new + LimitingResourceQueueDependency(origin,destiny,ganttDependency,type); + dependency.setNewObject(true); + origin.add(dependency); + destiny.add(dependency); + ganttDependency.setQueueDependency(dependency); + return dependency; + } + + public static LimitingResourceQueueDependency.QueueDependencyType + convertFromTypeToQueueDepedencyType(Dependency.Type type) { + return translationMap.get(type); + } + + /** + * Contructor for Hibernate. Do not use ! + */ + public LimitingResourceQueueDependency() {} + + private LimitingResourceQueueDependency(LimitingResourceQueueElement origin, + LimitingResourceQueueElement destiny, + Dependency ganttDependency, + QueueDependencyType type) { + Validate.notNull(origin); + Validate.notNull(destiny); + Validate.notNull(ganttDependency); + Validate.isTrue(!origin.equals(destiny), _("A queue dependency has to " + + "have an origin different from destiny")); + this.hasAsOrigin = origin; + this.hasAsDestiny = destiny; + this.ganttDependency = ganttDependency; + this.type = type; + } + + public LimitingResourceQueueElement getHasAsOrigin() { + return hasAsOrigin; + } + + public LimitingResourceQueueElement getHasAsDestiny() { + return hasAsDestiny; + } + + public QueueDependencyType getType() { + return type; + } + + public Dependency getGanttDependency() { + return ganttDependency; + } +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java index f60349695..19c33d5c6 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java @@ -20,16 +20,24 @@ package org.navalplanner.business.planner.entities; +import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Set; +import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; /** * - * @author Diego Pino Garcia + * Entity which represents an element in the queue which represents + * the limiting resources. * + * @author Diego Pino Garcia + * @author Javier Moran Rua */ public class LimitingResourceQueueElement extends BaseEntity { @@ -45,12 +53,22 @@ public class LimitingResourceQueueElement extends BaseEntity { private long creationTimestamp; + private Set dependenciesAsOrigin = + new HashSet(); + + private Set dependenciesAsDestiny = + new HashSet(); + public static LimitingResourceQueueElement create() { return create(new LimitingResourceQueueElement()); } protected LimitingResourceQueueElement() { creationTimestamp = (new Date()).getTime(); + startQueuePosition = new QueuePosition(); + startQueuePosition.setHour(0); + endQueuePosition = new QueuePosition(); + endQueuePosition.setHour(0); } public ResourceAllocation getResourceAllocation() { @@ -118,4 +136,52 @@ public class LimitingResourceQueueElement extends BaseEntity { this.creationTimestamp = creationTimestamp; } + public Resource getResource() { + if (resourceAllocation instanceof SpecificResourceAllocation) { + final SpecificResourceAllocation specific = (SpecificResourceAllocation) resourceAllocation; + return specific.getResource(); + } + return null; + } + + public Integer getIntentedTotalHours() { + return (getResourceAllocation() != null) ? getResourceAllocation() + .getIntendedTotalHours() : null; + } + + public DateAndHour getStartTime() { + return new DateAndHour(getStartDate(), getStartHour()); + } + + public DateAndHour getEndTime() { + return new DateAndHour(getEndDate(), getEndHour()); + } + + public void add(LimitingResourceQueueDependency d) { + Validate.notNull(d); + if (d.getHasAsOrigin().equals(this)) { + dependenciesAsOrigin.add(d); + } else if (d.getHasAsDestiny().equals(this)) { + dependenciesAsDestiny.add(d); + } else { + throw new IllegalArgumentException("It cannot be added a dependency" + + " in which the current queue element is neither origin" + + " not desinty"); + } + } + + public void remove(LimitingResourceQueueDependency d) { + if (dependenciesAsOrigin.contains(d)) + dependenciesAsOrigin.remove(d); + if (dependenciesAsDestiny.contains(d)) + dependenciesAsDestiny.remove(d); + } + + public Set getDependenciesAsOrigin() { + return Collections.unmodifiableSet(dependenciesAsOrigin); + } + + public Set getDependenciesAsDestiny() { + return Collections.unmodifiableSet(dependenciesAsDestiny); + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java new file mode 100644 index 000000000..b74e51ff2 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java @@ -0,0 +1,123 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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 . + */ + +package org.navalplanner.business.planner.entities; + +import org.joda.time.LocalDate; +import org.navalplanner.business.calendars.entities.ResourceCalendar; +import org.navalplanner.business.resources.entities.Resource; + +/** + * + * @author Diego Pino Garcia + * + */ +public class LimitingResourceQueueElementGap implements Comparable { + + private DateAndHour startTime; + + private DateAndHour endTime; + + private Integer hoursInGap; + + public LimitingResourceQueueElementGap(Resource resource, DateAndHour startTime, + DateAndHour endTime) { + this.startTime = startTime; + this.endTime = endTime; + hoursInGap = calculateHoursInGap(resource, startTime, endTime); + } + + private Integer calculateHoursInGap(Resource resource, DateAndHour startTime, DateAndHour endTime) { + return (endTime == null) ? Integer.MAX_VALUE : calculateHoursInGap( + resource, startTime.getDate(), startTime.getHour(), endTime + .getDate(), endTime.getHour()); + } + + public int getHoursInGap() { + return hoursInGap; + } + + private Integer calculateHoursInGap(Resource resource, LocalDate startDate, + int startHour, LocalDate endDate, int endHour) { + + final ResourceCalendar calendar = resource.getCalendar(); + + if (startDate.equals(endDate)) { + return calendar.getCapacityAt(startDate) - Math.max(startHour, endHour); + } else { + int hoursAtStart = calendar.getCapacityAt(startDate) - startHour; + int hoursInBetween = calendar.getWorkableHours(startDate + .plusDays(1), endDate.minusDays(1)); + return hoursAtStart + hoursInBetween + endHour; + } + } + + public static LimitingResourceQueueElementGap create(Resource resource, DateAndHour startTime, + DateAndHour endTime) { + return new LimitingResourceQueueElementGap(resource, startTime, endTime); + } + + public DateAndHour getStartTime() { + return startTime; + } + + public DateAndHour getEndTime() { + return endTime; + } + + /** + * Returns true if the gap starts after earlierStartDateBecauseOfGantt and + * if it's big enough for fitting candidate + * + * @param hours + * @return + */ + public boolean canFit(LimitingResourceQueueElement candidate) { + final LocalDate earlierStartDateBecauseOfGantt = new LocalDate( + candidate.getEarlierStartDateBecauseOfGantt()); + final LocalDate startDate = startTime.getDate(); + if (earlierStartDateBecauseOfGantt.isBefore(startDate) + || earlierStartDateBecauseOfGantt.isEqual(startDate)) { + return hoursInGap - candidate.getIntentedTotalHours() >= 0; + } + return false; + } + + public String toString() { + String result = startTime.getDate() + " - " + startTime.getHour(); + if (endTime != null) { + result += "; " + endTime.getDate() + " - " + endTime.getHour(); + } + return result; + } + + @Override + public int compareTo(LimitingResourceQueueElementGap o) { + if (o == null) { + return 1; + } + return this.getStartTime().compareTo(o.getStartTime()); + } + + public boolean isBefore(LimitingResourceQueueElementGap gap) { + return (compareTo(gap) < 0); + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java index 446779e1c..9bb69b6a2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java @@ -576,6 +576,10 @@ public abstract class ResourceAllocation extends return getLimitingResourceQueueElement() != null; } + public boolean isLimitingAndHasDayAssignments() { + return isLimiting() && hasAssignments(); + } + public boolean isSatisfied() { return hasAssignments(); } @@ -594,6 +598,15 @@ public abstract class ResourceAllocation extends getDayAssignmentsState().addingAssignments(assignments); } + public void removeLimitingDayAssignments() { + allocateLimitingDayAssignments(Collections.emptyList()); + } + + public void allocateLimitingDayAssignments(List assignments) { + assert isLimiting(); + resetAssignmentsTo(assignments); + } + protected final void removingAssignments( List assignments){ getDayAssignmentsState().removingAssignments(assignments); @@ -605,8 +618,12 @@ public abstract class ResourceAllocation extends } private ResourcesPerDay calculateResourcesPerDayFromAssignments() { - Map> byDay = DayAssignment - .byDay(getAssignments()); + return calculateResourcesPerDayFromAssignments(getAssignments()); + } + + private ResourcesPerDay calculateResourcesPerDayFromAssignments( + Collection assignments) { + Map> byDay = DayAssignment.byDay(assignments); int sumTotalHours = 0; int sumWorkableHours = 0; final ResourcesPerDay one = ResourcesPerDay.amount(1); @@ -837,6 +854,14 @@ public abstract class ResourceAllocation extends protected abstract DayAssignmentsState getDayAssignmentsState(); + public int getConsolidatedHours() { + return DayAssignment.sum(getConsolidatedAssignments()); + } + + public int getNonConsolidatedHours() { + return DayAssignment.sum(getNonConsolidatedAssignments()); + } + /** * @return a list of {@link DayAssignment} ordered by date */ @@ -844,6 +869,33 @@ public abstract class ResourceAllocation extends return getDayAssignmentsState().getOrderedDayAssignments(); } + public List getNonConsolidatedAssignments(){ + return getDayAssignmentsByConsolidated(false); + } + + public List getConsolidatedAssignments() { + return getDayAssignmentsByConsolidated(true); + } + + private List getDayAssignmentsByConsolidated( + boolean consolidated) { + List result = new ArrayList(); + for (T day : getAssignments()) { + if (day.isConsolidated() == consolidated) { + result.add(day); + } + } + return result; + } + + public ResourcesPerDay getNonConsolidatedResourcePerDay() { + return calculateResourcesPerDayFromAssignments(getNonConsolidatedAssignments()); + } + + public ResourcesPerDay getConsolidatedResourcePerDay() { + return calculateResourcesPerDayFromAssignments(getConsolidatedAssignments()); + } + public ResourcesPerDay getResourcesPerDay() { return resourcesPerDay; } @@ -879,11 +931,7 @@ public abstract class ResourceAllocation extends } public LocalDate getStartDate() { - List assignments = getAssignments(); - if (assignments.isEmpty()) { - return null; - } - return assignments.get(0).getDay(); + return LocalDate.fromDateFields(task.getStartDate()); } public LocalDate getEndDate() { @@ -1006,8 +1054,10 @@ public abstract class ResourceAllocation extends public void setLimitingResourceQueueElement(LimitingResourceQueueElement element) { limitingResourceQueueElements.clear(); - element.setResourceAllocation(this); - limitingResourceQueueElements.add(element); + if (element != null) { + element.setResourceAllocation(this); + limitingResourceQueueElements.add(element); + } } public Integer getIntendedTotalHours() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java index c32719290..44d9eb4e2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java @@ -58,10 +58,25 @@ public class SpecificResourceAllocation extends task)); } - public static SpecificResourceAllocation create(Resource resource, Task task) { + /** + * Creates a {@link SpecificResourceAllocation} for a + * {@link LimitingResourceQueueElement} + * + * The process of creating a specific resource allocation for a queue + * element is different as it's necessary to assign a resource and a number + * of resources per day without allocating day assignments + * + * @param resource + * @param task + * @return + */ + public static SpecificResourceAllocation create(Resource resource, + Task task) { + assert resource.isLimitingResource(); SpecificResourceAllocation result = create(new SpecificResourceAllocation( task)); result.setResource(resource); + result.setResourcesPerDay(ResourcesPerDay.amount(1)); return result; } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java index 803c5cc1a..1c441c6fb 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java @@ -183,6 +183,16 @@ public class Task extends TaskElement { return !(getLimitingResourceAllocations().isEmpty()); } + private ResourceAllocation getAssociatedLimitingResourceAllocation() { + Set> resourceAllocations = getLimitingResourceAllocations(); + return (resourceAllocations.size() > 0) ? resourceAllocations.iterator().next() : null; + } + + public boolean isLimitingAndHasDayAssignments() { + ResourceAllocation resourceAllocation = getAssociatedLimitingResourceAllocation(); + return (resourceAllocation != null) ? resourceAllocation.isLimitingAndHasDayAssignments() : false; + } + public void addResourceAllocation(ResourceAllocation resourceAllocation) { addResourceAllocation(resourceAllocation, true); } @@ -559,7 +569,8 @@ public class Task extends TaskElement { @Override protected boolean canBeResized() { - return calculatedValue != CalculatedValue.END_DATE; + return ((calculatedValue != CalculatedValue.END_DATE) || (resourceAllocations + .isEmpty())); } @Override @@ -598,4 +609,13 @@ public class Task extends TaskElement { return consolidation; } + @Override + public boolean hasLimitedResourceAllocation() { + return !getLimitingResourceAllocations().isEmpty(); + + } + + public boolean hasConsolidations() { + return ((consolidation != null) && (!consolidation.isEmpty())); + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java index 147a50ac1..cd88ca8b2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java @@ -413,6 +413,19 @@ public abstract class TaskElement extends BaseEntity { return false; } + public boolean isLimiting() { + return false; + } + + public boolean isLimitingAndHasDayAssignments() { + return false; + } + + public boolean hasConsolidations() { + // Just Task could be consolidated + return false; + } + public TaskElement getTopMost() { TaskElement result = this; while (result.getParent() != null) { @@ -446,4 +459,5 @@ public abstract class TaskElement extends BaseEntity { return "assigned"; } + public abstract boolean hasLimitedResourceAllocation(); } 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 98f335e94..541d42a67 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 @@ -36,6 +36,7 @@ import org.navalplanner.business.scenarios.entities.Scenario; /** * @author Óscar González Fernández + * @author Javier Moran Rua */ public class TaskGroup extends TaskElement { @@ -176,4 +177,9 @@ public class TaskGroup extends TaskElement { public boolean isMilestone() { return false; } + + @Override + public boolean hasLimitedResourceAllocation() { + return false; + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java index 6a3c314b3..838019c4a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskMilestone.java @@ -33,6 +33,7 @@ import org.navalplanner.business.scenarios.entities.Scenario; /** * @author Lorenzo Tilve Álvaro + * @author Javier Moran Rua */ public class TaskMilestone extends TaskElement { @@ -136,4 +137,9 @@ public class TaskMilestone extends TaskElement { return true; } + @Override + public boolean hasLimitedResourceAllocation() { + return false; + } + } \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidation.java index f3992ed9b..50e5607f0 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidation.java @@ -103,4 +103,9 @@ public class CalculatedConsolidation extends Consolidation { return true; } + @Override + public boolean isEmpty() { + return consolidatedValues.isEmpty(); + } + } \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/Consolidation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/Consolidation.java index 03c822d34..4959fed19 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/Consolidation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/Consolidation.java @@ -34,6 +34,8 @@ public abstract class Consolidation extends BaseEntity { public abstract boolean isCalculated(); + public abstract boolean isEmpty(); + private Task task; protected Consolidation() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidation.java index c143d6b3e..e451c82d2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidation.java @@ -104,4 +104,8 @@ public class NonCalculatedConsolidation extends Consolidation { return false; } + @Override + public boolean isEmpty() { + return consolidatedValues.isEmpty(); + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java index d241bf0dd..7b82c7b36 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java @@ -20,6 +20,7 @@ package org.navalplanner.business.resources.entities; +import static org.navalplanner.business.i18n.I18nHelper._; import java.util.ArrayList; import java.util.Collection; @@ -49,6 +50,8 @@ import org.navalplanner.business.resources.daos.ICriterionDAO; */ public class Criterion extends IntegrationEntity implements ICriterion { + private static final String ALL_WORKERS = _("[generic all workers]"); + public static Criterion createUnvalidated(String code, String name, CriterionType type, Criterion parent, Boolean active) { @@ -104,6 +107,20 @@ public class Criterion extends IntegrationEntity implements ICriterion { return result; } + /** + * Returns a string of criterion names separated by comma + * + * @param criteria + * @return + */ + public static String getNames(Collection criteria) { + List names = new ArrayList(); + for (Criterion each: criteria) { + names.add(each.getName()); + } + return (names.isEmpty()) ? Criterion.ALL_WORKERS : StringUtils.join(names, ","); + } + public void updateUnvalidated(String name, Boolean active) { if (!StringUtils.isBlank(name)) { @@ -303,4 +320,5 @@ public class Criterion extends IntegrationEntity implements ICriterion { protected ICriterionDAO getIntegrationEntityDAO() { return Registry.getCriterionDAO(); } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java index fdc3a4332..8f25e3989 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java @@ -21,6 +21,7 @@ package org.navalplanner.business.resources.entities; import java.util.Comparator; +import org.joda.time.LocalDate; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; /** @@ -34,17 +35,24 @@ public class LimitingResourceQueueElementComparator implements @Override public int compare(LimitingResourceQueueElement arg0, LimitingResourceQueueElement arg1) { - final int deltaHour = arg0.getStartHour() - arg1.getStartHour(); - if (deltaHour != 0) { - return deltaHour / Math.abs(deltaHour); - } - if (arg0.getStartDate() == null) { + int compareDates = compare(arg0.getStartDate(), arg1.getStartDate()); + return (compareDates != 0) ? compareDates : compare( + arg0.getStartHour(), arg1.getStartHour()); + } + + private int compare(LocalDate arg0, LocalDate arg1) { + if (arg0 == null) { return -1; } - if (arg1.getStartDate() == null) { + if (arg1 == null) { return 1; } - return arg0.getStartDate().compareTo(arg1.getStartDate()); + return arg0.compareTo(arg1); + } + + private int compare(int arg0, int arg1) { + final int deltaHour = arg0 - arg1; + return (deltaHour != 0) ? deltaHour / Math.abs(deltaHour) : 0; } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/IWorkReportLineDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/IWorkReportLineDAO.java index 7c212a2b4..de720cb92 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/IWorkReportLineDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/workreports/daos/IWorkReportLineDAO.java @@ -20,6 +20,7 @@ package org.navalplanner.business.workreports.daos; +import java.util.Date; import java.util.List; import org.navalplanner.business.common.daos.IIntegrationEntityDAO; @@ -41,4 +42,6 @@ public interface IWorkReportLineDAO extends List findByOrderElementAndChildren(OrderElement orderElement, boolean sortByDate); + List findFilteredByDate(Date start, Date end); + } 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 508a44571..7a374983f 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.Collection; +import java.util.Date; import java.util.List; import org.hibernate.Criteria; @@ -74,4 +75,17 @@ public class WorkReportLineDAO extends IntegrationEntityDAO return criteria.list(); } + @Override + @SuppressWarnings("unchecked") + public List findFilteredByDate(Date start, Date end) { + Criteria criteria = getSession().createCriteria(WorkReportLine.class); + if(start != null) { + criteria.add(Restrictions.ge("date", start)); + } + if(end != null) { + criteria.add(Restrictions.le("date", end)); + } + return criteria.list(); + } + } diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml index 0fb93f284..51a862755 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml @@ -26,6 +26,7 @@ + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml index 7f76e6d61..a12a619c7 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml @@ -131,7 +131,7 @@ - + @@ -149,6 +149,48 @@ + + + + + + + + + + + + + + + + + 100 + + + + + + org.navalplanner.business.planner.entities.LimitingResourceQueueDependency$QueueDependencyType + + + + + + + + + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml index b9054c087..1ce63d858 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml @@ -19,12 +19,12 @@ - + - + @@ -87,6 +87,11 @@ column="ORIGIN" /> + org.navalplanner.business.planner.entities.Dependency$Type diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java index adf0f2e76..da445ed48 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/ResourceAllocationDAOTest.java @@ -259,4 +259,29 @@ public class ResourceAllocationDAOTest { .list(GenericResourceAllocation.class); assertEquals(previous + 2, list.size()); } + + @Test + public void testFindAllocationsRelatedToResourcesWithDateFilter() { + ResourceAllocation resourceAllocation1 = createValidSpecificResourceAllocation(); + resourceAllocationDAO.save(resourceAllocation1); + + Date intervalInitDate = resourceAllocation1.getTask().getStartDate(); + Date intervalEndDate = resourceAllocation1.getTask().getEndDate(); + List resources = resourceAllocation1.getAssociatedResources(); + + assertTrue(resourceAllocationDAO.findAllocationsRelatedToAnyOf(resources, + intervalInitDate, intervalEndDate).contains(resourceAllocation1)); + + intervalEndDate.setDate(intervalInitDate.getDate()); + intervalInitDate.setMonth(intervalInitDate.getMonth()-1); + assertTrue(resourceAllocationDAO.findAllocationsRelatedToAnyOf(resources, + intervalInitDate, intervalEndDate).contains(resourceAllocation1)); + + intervalEndDate.setMonth(intervalEndDate.getMonth()-1); + assertFalse(resourceAllocationDAO.findAllocationsRelatedToAnyOf(resources, + intervalInitDate, intervalEndDate).contains(resourceAllocation1)); + + assertTrue(resourceAllocationDAO.findAllocationsRelatedToAnyOf(resources, + intervalInitDate, null).contains(resourceAllocation1)); + } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java index 12d0317fb..862787eaa 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java @@ -366,4 +366,14 @@ public class ConfigurationController extends GenericForwardComposer { return configurationModel.isExpandOrderPlanningViewCharts(); } + public void setExpandResourceLoadViewCharts( + Boolean expandResourceLoadViewCharts) { + configurationModel + .setExpandResourceLoadViewCharts(expandResourceLoadViewCharts); + } + + public Boolean isExpandResourceLoadViewCharts() { + return configurationModel.isExpandResourceLoadViewCharts(); + } + } \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java index 6cedbf46b..ea47f2f96 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java @@ -373,4 +373,22 @@ public class ConfigurationModel implements IConfigurationModel { return configuration.isExpandOrderPlanningViewCharts(); } + + @Override + public void setExpandResourceLoadViewCharts( + Boolean expandResourceLoadViewCharts) { + if (configuration != null) { + configuration + .setExpandResourceLoadViewCharts(expandResourceLoadViewCharts); + } + } + + @Override + public Boolean isExpandResourceLoadViewCharts() { + if (configuration == null) { + return null; + } + return configuration.isExpandResourceLoadViewCharts(); + } + } \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java index 72a45bf17..bef605ba7 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java @@ -85,6 +85,10 @@ public interface IConfigurationModel { Boolean isExpandOrderPlanningViewCharts(); + void setExpandResourceLoadViewCharts(Boolean expandResourceLoadViewCharts); + + Boolean isExpandResourceLoadViewCharts(); + /* * Final conversation steps */ diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java index 4fe25b5f1..114492aef 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java @@ -23,28 +23,100 @@ package org.navalplanner.web.limitingresources; import java.util.List; import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.Interval; +/** + * Contains operations for showing {@link LimitingResourceQueue} and its + * elements ({@link LimitingResourceQueueElement}), plus showing all + * {@link LimitingResourceQueueElement} which are not assigned to any + * {@link LimitingResourceQueue} + * + * Conversational protocol: + *
    + *
  • + * Initial conversation step: initGlobalView
  • + *
  • + * Intermediate conversation steps: + * assignLimitingResourceQueueElement, + * getLimitingResourceQueues, + * getUnassignedLimitingResourceQueueElements
  • + *
  • + * Final conversation step: confirm
  • + * + * + * @author Diego Pino Garcia + * + */ public interface ILimitingResourceQueueModel { + /** + * Assigns a {@link LimitingResourceQueueElement} to its corresponding + * {@link LimitingResourceQueue} + * + * There is one and only one queue for every limiting resource. An element + * is assigned to its queue searching by element.resource. + * + * Allocation within the queue is done by finding the first gap in the queue + * that fits the initial intented hours assigned to + * element.resourceallocation. + * + * The method also generates {@link DayAssignment} once the allocation is + * done + * + * Returns true if the process was successful. The only case were an + * allocation cannot be done is if there's not any queue that can hold the + * element (only for a generic allocation, there's not any queue that + * matches the criteria of the element) + * + * @param element + */ + boolean assignLimitingResourceQueueElement(LimitingResourceQueueElement element); + + ZoomLevel calculateInitialZoomLevel(); + + /** + * Saves all {@link LimitingResourceQueue} + */ + void confirm(); + + /** + * Return all {@link LimitingResourceQueue} + * + * @return + */ + List getLimitingResourceQueues(); + + Order getOrderByTask(TaskElement task); + + /** + * Returns all existing {@link LimitingResourceQueueElement} which are not + * assigned to any {@link LimitingResourceQueue} + * + * @return + */ + List getUnassignedLimitingResourceQueueElements(); + + Interval getViewInterval(); + + /** + * Loads {@link LimitingResourceQueue} and unassigned {@link LimitingResourceQueueElement} from DB + * + * @param filterByResources + */ void initGlobalView(boolean filterByResources); void initGlobalView(Order filterBy, boolean filterByResources); - List getLimitingResourceQueues(); - - Interval getViewInterval(); - - ZoomLevel calculateInitialZoomLevel(); - - Order getOrderByTask(TaskElement task); - boolean userCanRead(Order order, String loginName); - List getUnassignedLimitingResourceQueueElements(); + void unschedule(LimitingResourceQueueElement element); + + void removeUnassignedLimitingResourceQueueElement( + LimitingResourceQueueElement element); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyComponent.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyComponent.java index d566ceb2a..5797ec3a5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyComponent.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyComponent.java @@ -25,15 +25,12 @@ import java.beans.PropertyChangeListener; import java.util.Date; import org.apache.commons.lang.Validate; -import org.zkoss.ganttz.TaskComponent; import org.zkoss.ganttz.data.Dependency; import org.zkoss.ganttz.data.DependencyType; -import org.zkoss.ganttz.data.Task; import org.zkoss.ganttz.data.constraint.Constraint; import org.zkoss.ganttz.data.constraint.Constraint.IConstraintViolationListener; import org.zkoss.zk.au.out.AuInvoke; import org.zkoss.zk.ui.ext.AfterCompose; -import org.zkoss.zul.Div; import org.zkoss.zul.impl.XulElement; /** @@ -44,9 +41,9 @@ import org.zkoss.zul.impl.XulElement; public class LimitingDependencyComponent extends XulElement implements AfterCompose { - private Div source; + private QueueTask source; - private Div destination; + private QueueTask destination; private DependencyType type; @@ -54,13 +51,15 @@ public class LimitingDependencyComponent extends XulElement implements private IConstraintViolationListener violationListener; - public LimitingDependencyComponent(Div source, Div destination) { + public LimitingDependencyComponent(QueueTask source, QueueTask destination, + DependencyType type) { + this(source, destination); + this.type = type; + } + + public LimitingDependencyComponent(QueueTask source, QueueTask destination) { Validate.notNull(source); Validate.notNull(destination); - // Validate.isTrue(source.getTask() == dependency.getSource()); - // Validate.isTrue(destination.getTask() == - // dependency.getDestination()); - // this.type = dependency.getType(); this.source = source; this.destination = destination; // this.dependency = dependency; @@ -100,8 +99,8 @@ public class LimitingDependencyComponent extends XulElement implements } - private TaskComponent findTaskComponent(String idTaskOrig) { - return (TaskComponent) getFellow(idTaskOrig); + private QueueTask findTaskComponent(String idTaskOrig) { + return (QueueTask) getFellow(idTaskOrig); } /** @@ -123,18 +122,18 @@ public class LimitingDependencyComponent extends XulElement implements response("zoomChanged", new AuInvoke(this, "draw")); } - public boolean contains(Task task) { + public boolean contains(QueueTask task) { return false; // Task sourceTask = getSource().getTask(); // Task destinationTask = getDestination().getTask(); // return task.equals(sourceTask) || task.equals(destinationTask); } - public Div getSource() { + public QueueTask getSource() { return source; } - public Div getDestination() { + public QueueTask getDestination() { return destination; } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyList.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyList.java index 88ba483bc..333b5aede 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyList.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyList.java @@ -20,18 +20,16 @@ package org.navalplanner.web.limitingresources; -import static org.zkoss.ganttz.i18n.I18nHelper._; - import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; import org.zkoss.ganttz.DependencyList; -import org.zkoss.ganttz.FunctionalityExposedForExtensions; -import org.zkoss.ganttz.GanttPanel; import org.zkoss.ganttz.TaskComponent; import org.zkoss.ganttz.data.Dependency; import org.zkoss.ganttz.data.DependencyType; @@ -41,12 +39,9 @@ import org.zkoss.ganttz.timetracker.TimeTrackerComponent; import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.ComponentsFinder; -import org.zkoss.ganttz.util.MenuBuilder; import org.zkoss.ganttz.util.MenuBuilder.ItemAction; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.ext.AfterCompose; -import org.zkoss.zul.Div; -import org.zkoss.zul.Menupopup; import org.zkoss.zul.impl.XulElement; /** @@ -95,7 +90,6 @@ public class LimitingDependencyList extends XulElement implements AfterCompose { void toggleDependencyExistence(boolean visible) { if (visible) { appendChild(dependencyComponent); - addContextMenu(dependencyComponent); } else { removeChild(dependencyComponent); } @@ -114,43 +108,31 @@ public class LimitingDependencyList extends XulElement implements AfterCompose { private transient IZoomLevelChangedListener listener; - private final FunctionalityExposedForExtensions context; + private final LimitingResourcesPanel panel; - public LimitingDependencyList(FunctionalityExposedForExtensions context) { - this.context = context; + public LimitingDependencyList(LimitingResourcesPanel panel) { + this.panel = panel; } + @SuppressWarnings("unchecked") private List getLimitingDependencyComponents() { - List children = getChildren(); return ComponentsFinder.findComponentsOfType( - LimitingDependencyComponent.class, children); + LimitingDependencyComponent.class, getChildren()); } - void addDependencyComponent( + public void addDependencyComponent( final LimitingDependencyComponent dependencyComponent) { - Div source = dependencyComponent.getSource(); - Div destination = dependencyComponent.getDestination(); - // DependencyVisibilityToggler visibilityToggler = new - // DependencyVisibilityToggler( - // source.getTask(), destination.getTask(), dependencyComponent); - // source.getTask().addVisibilityPropertiesChangeListener( - // visibilityToggler); - // destination.getTask().addVisibilityPropertiesChangeListener( - // visibilityToggler); - // boolean dependencyMustBeVisible = visibilityToggler - // .dependencyMustBeVisible(); - // visibilityToggler.toggleDependencyExistence(dependencyMustBeVisible); - // if (dependencyMustBeVisible) { - // dependencyComponent.redrawDependency(); - // } + dependencyComponent.redrawDependency(); + dependencyComponent.setParent(this); } - private void addContextMenu(LimitingDependencyComponent dependencyComponent) { - dependencyComponent.setContext(getContextMenu()); - } - - private GanttPanel getGanttPanel() { - return (GanttPanel) getParent(); + public void removeDependencyComponents(QueueTask queueTask) { + for (LimitingDependencyComponent dependency : getLimitingDependencyComponents()) { + if (dependency.getSource().equals(queueTask) + || dependency.getDestination().equals(queueTask)) { + removeChild(dependency); + } + } } public void setDependencyComponents( @@ -166,72 +148,31 @@ public class LimitingDependencyList extends XulElement implements AfterCompose { listener = new IZoomLevelChangedListener() { @Override public void zoomLevelChanged(ZoomLevel detailLevel) { - if (!isInPage()) { - return; - } for (LimitingDependencyComponent dependencyComponent : getLimitingDependencyComponents()) { dependencyComponent.zoomChanged(); } } }; - // getTimeTracker().addZoomListener(listener); + getTimeTracker().addZoomListener(listener); } - // addContextMenu(); - } - - private boolean isInPage() { - return getParent() != null && getGanttPanel() != null - && getGanttPanel().getParent() != null; + redrawDependencies(); } private TimeTracker getTimeTracker() { return getTimeTrackerComponent().getTimeTracker(); } - private void addContextMenu() { - for (LimitingDependencyComponent dependencyComponent : getLimitingDependencyComponents()) { - addContextMenu(dependencyComponent); - } - } - - private Menupopup contextMenu; - - private Menupopup getContextMenu() { - if (contextMenu == null) { - MenuBuilder contextMenuBuilder = MenuBuilder - .on(getPage(), getLimitingDependencyComponents()).item(_("Erase"), - "/common/img/ico_borrar.png", - new ItemAction() { - @Override - public void onEvent( - final LimitingDependencyComponent choosen, - Event event) { -// context -// .removeDependency(choosen.getDependency()); - } - }); - contextMenuBuilder.item(_("Set End-Start"), null, - new ChangeTypeAction( - DependencyType.END_START)); - - contextMenuBuilder.item(_("Set Start-Start"), null, - new ChangeTypeAction( - DependencyType.START_START)); - - contextMenuBuilder.item(_("Set End-End"), null, - new ChangeTypeAction( - DependencyType.END_END)); - - contextMenu = contextMenuBuilder.create(); - - } - return contextMenu; - } - private TimeTrackerComponent getTimeTrackerComponent() { - return getGanttPanel().getTimeTrackerComponent(); + return panel.getTimeTrackerComponent(); } + + // private boolean isInPage() { + // return getParent() != null && getGanttPanel() != null + // && getGanttPanel().getParent() != null; + // } + + public void redrawDependenciesConnectedTo(TaskComponent taskComponent) { redrawDependencyComponents(getDependencyComponentsConnectedTo(taskComponent)); } @@ -261,7 +202,7 @@ public class LimitingDependencyList extends XulElement implements AfterCompose { } } - public void taskRemoved(Task task) { + public void taskRemoved(QueueTask task) { for (LimitingDependencyComponent dependencyComponent : LimitingDependencyList.this .getLimitingDependencyComponents()) { if (dependencyComponent.contains(task)) { @@ -278,4 +219,5 @@ public class LimitingDependencyList extends XulElement implements AfterCompose { } } } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java index 423c1ccff..dfdf9a008 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java @@ -20,33 +20,46 @@ package org.navalplanner.web.limitingresources; -import static org.navalplanner.web.I18nHelper._; - import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import org.hibernate.Hibernate; +import org.hibernate.proxy.HibernateProxy; import org.joda.time.LocalDate; -import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.calendars.entities.BaseCalendar; +import org.navalplanner.business.calendars.entities.CalendarAvailability; +import org.navalplanner.business.calendars.entities.CalendarData; +import org.navalplanner.business.calendars.entities.CalendarException; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; -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.planner.daos.IDependencyDAO; +import org.navalplanner.business.planner.daos.ILimitingResourceQueueDAO; +import org.navalplanner.business.planner.daos.ILimitingResourceQueueDependencyDAO; import org.navalplanner.business.planner.daos.ILimitingResourceQueueElementDAO; -import org.navalplanner.business.planner.daos.IResourceAllocationDAO; +import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.entities.DateAndHour; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.Dependency; import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.LimitingResourceAllocator; +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElementGap; import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; -import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.users.daos.IOrderAuthorizationDAO; @@ -61,7 +74,6 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import org.zkoss.ganttz.data.resourceload.TimeLineRole; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.Interval; @@ -69,18 +81,9 @@ import org.zkoss.ganttz.util.Interval; @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { - @Autowired - private IResourceDAO resourcesDAO; - @Autowired private IOrderElementDAO orderElementDAO; - @Autowired - private IOrderDAO orderDAO; - - @Autowired - private IResourceAllocationDAO resourceAllocationDAO; - @Autowired private IUserDAO userDAO; @@ -90,30 +93,222 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { @Autowired private ILimitingResourceQueueElementDAO limitingResourceQueueElementDAO; - private List limitingResourceQueues = new ArrayList(); + @Autowired + private ILimitingResourceQueueDAO limitingResourceQueueDAO; + + @Autowired + private ITaskElementDAO taskDAO; + + @Autowired + private ILimitingResourceQueueDependencyDAO limitingResourceQueueDependencyDAO; private Interval viewInterval; - private Order filterBy; + private List limitingResourceQueues = new ArrayList(); - private boolean filterByResources = true; + private List unassignedLimitingResourceQueueElements = new ArrayList(); + + private Set toBeRemoved = new HashSet(); + + private Set toBeSaved = new HashSet(); @Override @Transactional(readOnly = true) public void initGlobalView(boolean filterByResources) { - filterBy = null; - this.filterByResources = filterByResources; doGlobalView(); } @Override @Transactional(readOnly = true) public void initGlobalView(Order filterBy, boolean filterByResources) { - this.filterBy = orderDAO.findExistingEntity(filterBy.getId()); - this.filterByResources = filterByResources; doGlobalView(); } + private void doGlobalView() { + loadUnassignedLimitingResourceQueueElements(); + loadLimitingResourceQueues(); + final Date startingDate = getEarliestDate(); + viewInterval = new Interval(startingDate, plusFiveYears(startingDate)); + } + + private Date getEarliestDate() { + final LimitingResourceQueueElement element = getEarliestQueueElement(); + return (element != null) ? element.getStartDate() + .toDateTimeAtCurrentTime().toDate() : new Date(); + } + + private LimitingResourceQueueElement getEarliestQueueElement() { + LimitingResourceQueueElement earliestQueueElement = null; + + if (!limitingResourceQueues.isEmpty()) { + for (LimitingResourceQueue each : limitingResourceQueues) { + LimitingResourceQueueElement element = getFirstLimitingResourceQueueElement(each); + if (element == null) { + continue; + } + if (earliestQueueElement == null + || isEarlier(element, earliestQueueElement)) { + earliestQueueElement = element; + } + } + } + return earliestQueueElement; + } + + private boolean isEarlier(LimitingResourceQueueElement arg1, + LimitingResourceQueueElement arg2) { + return (arg1.getStartDate().isBefore(arg2.getStartDate())); + } + + private LimitingResourceQueueElement getFirstLimitingResourceQueueElement( + LimitingResourceQueue queue) { + return getFirstChild(queue.getLimitingResourceQueueElements()); + } + + private LimitingResourceQueueElement getFirstChild( + SortedSet elements) { + return (elements.isEmpty()) ? null : elements.iterator().next(); + } + + private Date plusFiveYears(Date date) { + return (new LocalDate(date)).plusYears(5).toDateTimeAtCurrentTime() + .toDate(); + } + + /** + * Loads unassigned {@link LimitingResourceQueueElement} from DB + * + * @return + */ + private void loadUnassignedLimitingResourceQueueElements() { + unassignedLimitingResourceQueueElements.clear(); + unassignedLimitingResourceQueueElements + .addAll(initializeLimitingResourceQueueElements(limitingResourceQueueElementDAO + .getUnassigned())); + } + + private List initializeLimitingResourceQueueElements( + List elements) { + for (LimitingResourceQueueElement each : elements) { + initializeLimitingResourceQueueElement(each); + } + return elements; + } + + private void initializeLimitingResourceQueueElement( + LimitingResourceQueueElement element) { + ResourceAllocation resourceAllocation = element + .getResourceAllocation(); + resourceAllocation = initializeResourceAllocationIfNecessary(resourceAllocation); + element.setResourceAllocation(resourceAllocation); + initializeTask(resourceAllocation.getTask()); + initializeResourceIfAny(element.getResource()); + } + + private void initializeTask(Task task) { + Hibernate.initialize(task); + for (ResourceAllocation each: task.getAllResourceAllocations()) { + Hibernate.initialize(each); + } + for (Dependency each: task.getDependenciesWithThisOrigin()) { + Hibernate.initialize(each); + } + for (Dependency each: task.getDependenciesWithThisDestination()) { + Hibernate.initialize(each); + } + } + + private void initializeCalendarIfAny(BaseCalendar calendar) { + if (calendar != null) { + Hibernate.initialize(calendar); + initializeCalendarAvailabilities(calendar); + initializeCalendarExceptions(calendar); + initializeCalendarDataVersions(calendar); + } + } + + private void initializeCalendarAvailabilities(BaseCalendar calendar) { + for (CalendarAvailability each : calendar.getCalendarAvailabilities()) { + Hibernate.initialize(each); + } + } + + private void initializeCalendarExceptions(BaseCalendar calendar) { + for (CalendarException each : calendar.getExceptions()) { + Hibernate.initialize(each); + Hibernate.initialize(each.getType()); + } + } + + private void initializeCalendarDataVersions(BaseCalendar calendar) { + for (CalendarData each : calendar.getCalendarDataVersions()) { + Hibernate.initialize(each); + Hibernate.initialize(each.getHoursPerDay()); + initializeCalendarIfAny(each.getParent()); + } + } + + private ResourceAllocation initializeResourceAllocationIfNecessary( + ResourceAllocation resourceAllocation) { + if (resourceAllocation instanceof HibernateProxy) { + resourceAllocation = (ResourceAllocation) ((HibernateProxy) resourceAllocation) + .getHibernateLazyInitializer().getImplementation(); + if (resourceAllocation instanceof GenericResourceAllocation) { + GenericResourceAllocation generic = (GenericResourceAllocation) resourceAllocation; + initializeCriteria(generic.getCriterions()); + } + Hibernate.initialize(resourceAllocation.getAssignments()); + Hibernate.initialize(resourceAllocation.getLimitingResourceQueueElement()); + } + return resourceAllocation; + } + + private void initializeCriteria(Set criteria) { + for (Criterion each: criteria) { + initializeCriterion(each); + } + } + + private void initializeCriterion(Criterion criterion) { + Hibernate.initialize(criterion); + Hibernate.initialize(criterion.getType()); + } + + private void loadLimitingResourceQueues() { + limitingResourceQueues.clear(); + limitingResourceQueues + .addAll(initializeLimitingResourceQueues(limitingResourceQueueDAO + .getAll())); + } + + private List initializeLimitingResourceQueues( + List queues) { + for (LimitingResourceQueue each : queues) { + initializeLimitingResourceQueue(each); + } + return queues; + } + + private void initializeLimitingResourceQueue(LimitingResourceQueue queue) { + initializeResourceIfAny(queue.getResource()); + for (LimitingResourceQueueElement each : queue + .getLimitingResourceQueueElements()) { + initializeLimitingResourceQueueElement(each); + } + } + + private void initializeResourceIfAny(Resource resource) { + if (resource != null) { + Hibernate.initialize(resource); + for (CriterionSatisfaction each : resource + .getCriterionSatisfactions()) { + Hibernate.initialize(each); + initializeCriterion(each.getCriterion()); + initializeCalendarIfAny(resource.getCalendar()); + } + } + } + @Override @Transactional(readOnly = true) public Order getOrderByTask(TaskElement task) { @@ -121,6 +316,11 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { .loadOrderAvoidingProxyFor(task.getOrderElement()); } + @Override + public Interval getViewInterval() { + return viewInterval; + } + @Override @Transactional(readOnly = true) public boolean userCanRead(Order order, String loginName) { @@ -145,142 +345,14 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { return false; } - private void doGlobalView() { - limitingResourceQueues = calculateLimitingResourceQueues(); - if (!limitingResourceQueues.isEmpty()) { - // Build interval - // viewInterval = - // LimitingResourceQueue.getIntervalFrom(limitingResourceQueues); - viewInterval = new Interval(new Date(), plusFiveYears(new Date())); - } else { - viewInterval = new Interval(new Date(), plusFiveYears(new Date())); - } - } - - private Date plusFiveYears(Date date) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - calendar.add(Calendar.YEAR, 5); - return calendar.getTime(); - } - - private List calculateLimitingResourceQueues() { - List result = new ArrayList(); - result.addAll(groupsFor(resourcesToShow())); - return result; - } - - private List resourcesToShow() { - if (filter()) { - return resourcesForActiveTasks(); - } else { - return allLimitingResources(); - } - } - - private boolean filter() { - return filterBy != null; - } - - private List resourcesForActiveTasks() { - return Resource.sortByName(resourcesDAO - .findResourcesRelatedTo(justTasks(filterBy - .getAllChildrenAssociatedTaskElements()))); - } - - private List justTasks(Collection tasks) { - List result = new ArrayList(); - for (TaskElement taskElement : tasks) { - if (taskElement instanceof Task) { - result.add((Task) taskElement); - } - } - return result; - } - - private List allLimitingResources() { - List result = Resource.sortByName(resourcesDAO - .getAllLimitingResources()); - for (Resource each : result) { - each.getLimitingResourceQueue().getLimitingResourceQueueElements() - .size(); - limitingResourceQueues.add(each.getLimitingResourceQueue()); - } - return result; - } - - private TimeLineRole getCurrentTimeLineRole(BaseEntity entity) { - return new TimeLineRole(entity); - } - - private List groupsFor(List allResources) { - List result = new ArrayList(); - for (Resource resource : allResources) { - LimitingResourceQueue group = resource.getLimitingResourceQueue(); - result.add(group); - } - return result; - } - - private void initializeIfNeeded( - Map>> result, Order order) { - if (!result.containsKey(order)) { - result.put(order, new ArrayList>()); - } - } - - @Transactional(readOnly = true) - public Map>> byOrder( - Collection> allocations) { - Map>> result = new HashMap>>(); - for (ResourceAllocation resourceAllocation : allocations) { - if ((resourceAllocation.isSatisfied()) - && (resourceAllocation.getTask() != null)) { - OrderElement orderElement = resourceAllocation.getTask() - .getOrderElement(); - Order order = orderElementDAO - .loadOrderAvoidingProxyFor(orderElement); - initializeIfNeeded(result, order); - result.get(order).add(resourceAllocation); - } - } - return result; - } - - - private List onlyGeneric( - List> sortedByStartDate) { - return ResourceAllocation.getOfType(GenericResourceAllocation.class, - sortedByStartDate); - } - - public static String getName(Collection criterions, - Task task) { - String prefix = task.getName(); - return (prefix + " :: " + getName(criterions)); - } - - public static String getName(Collection criterions) { - if (criterions.isEmpty()) { - return _("[generic all workers]"); - } - String[] names = new String[criterions.size()]; - int i = 0; - for (Criterion criterion : criterions) { - names[i++] = criterion.getName(); - } - return (Arrays.toString(names)); - } - - @Override public List getLimitingResourceQueues() { - return limitingResourceQueues; + return Collections.unmodifiableList(limitingResourceQueues); } - @Override - public Interval getViewInterval() { - return viewInterval; + public List getUnassignedLimitingResourceQueueElements() { + return Collections + .unmodifiableList(unassignedLimitingResourceQueueElements); } public ZoomLevel calculateInitialZoomLevel() { @@ -290,15 +362,307 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { } @Override - @Transactional(readOnly=true) - public List getUnassignedLimitingResourceQueueElements() { - List result = limitingResourceQueueElementDAO - .getUnassigned(); - for (LimitingResourceQueueElement each : result) { - limitingResourceQueueElementDAO.reattach(each); - each.getResourceAllocation().getTask().getName(); + public boolean assignLimitingResourceQueueElement( + LimitingResourceQueueElement element) { + + LimitingResourceQueue queue = null; + DateAndHour startTime = null; + + LimitingResourceQueueElement queueElement = retrieveQueueElementFromModel(element); + + final ResourceAllocation resourceAllocation = queueElement + .getResourceAllocation(); + if (resourceAllocation instanceof SpecificResourceAllocation) { + // Retrieve queue + queue = retrieveQueueByResourceFromModel(queueElement.getResource()); + // Set start time + final LimitingResourceQueueElementGap firstGap = LimitingResourceAllocator + .getFirstValidGap(queue, queueElement); + startTime = firstGap.getStartTime(); + } else if (resourceAllocation instanceof GenericResourceAllocation) { + // Get the first gap for all the queues that can allocate the + // element during a certain interval of time + Map firstGapsForQueues = findFirstGapsInAllQueues( + limitingResourceQueues, element); + // Among those queues, get the earliest gap + LimitingResourceQueueElementGap earliestGap = findEarliestGap(firstGapsForQueues + .keySet()); + if (earliestGap == null) { + return false; + } + // Select queue and start time + queue = firstGapsForQueues.get(earliestGap); + startTime = earliestGap.getStartTime(); + } + + // Generate day assignments and adjust start and end times for element + List dayAssignments = LimitingResourceAllocator + .generateDayAssignments(queueElement.getResourceAllocation(), + queue.getResource(), startTime); + DateAndHour[] startAndEndTime = LimitingResourceAllocator + .calculateStartAndEndTime(dayAssignments); + updateStartAndEndTimes(queueElement, startAndEndTime); + + // Add element to queue + addLimitingResourceQueueElement(queue, queueElement); + markAsModified(queueElement); + return true; + } + + private LimitingResourceQueueElementGap findEarliestGap(Set gaps) { + LimitingResourceQueueElementGap earliestGap = null; + for (LimitingResourceQueueElementGap each: gaps) { + if (earliestGap == null || each.isBefore(earliestGap)) { + earliestGap = each; + } + } + return earliestGap; + } + + private Map findFirstGapsInAllQueues( + List queues, + LimitingResourceQueueElement element) { + + Map result = new HashMap(); + + for (LimitingResourceQueue each : queues) { + LimitingResourceQueueElementGap gap = LimitingResourceAllocator + .getFirstValidGap(each, element); + if (gap != null) { + result.put(gap, each); + } } return result; } + private void markAsModified(LimitingResourceQueueElement element) { + if (!toBeSaved.contains(element)) { + toBeSaved.add(element); + } + } + + public LimitingResourceQueueElementGap createGap(Resource resource, DateAndHour startTime, + DateAndHour endTime) { + return LimitingResourceQueueElementGap.create(resource, startTime, endTime); + } + + private void updateStartAndEndTimes(LimitingResourceQueueElement element, + DateAndHour[] startAndEndTime) { + + final DateAndHour startTime = startAndEndTime[0]; + final DateAndHour endTime = startAndEndTime[1]; + + element.setStartDate(startTime.getDate()); + element.setStartHour(startTime.getHour()); + element.setEndDate(endTime.getDate()); + element.setEndHour(endTime.getHour()); + + // Update starting and ending dates for associated Task + Task task = element.getResourceAllocation().getTask(); + updateStartingAndEndingDate(task, startTime.getDate(), endTime + .getDate()); + } + + private void updateStartingAndEndingDate(Task task, LocalDate startDate, + LocalDate endDate) { + task.setStartDate(toDate(startDate)); + task.setEndDate(endDate.toDateTimeAtStartOfDay().toDate()); + task.explicityMoved(toDate(startDate)); + } + + private Date toDate(LocalDate date) { + return date.toDateTimeAtStartOfDay().toDate(); + } + + private void addLimitingResourceQueueElement(LimitingResourceQueue queue, + LimitingResourceQueueElement element) { + queue.addLimitingResourceQueueElement(element); + unassignedLimitingResourceQueueElements.remove(element); + } + + private LimitingResourceQueue retrieveQueueByResourceFromModel(Resource resource) { + return findQueueByResource(limitingResourceQueues, resource); + } + + private LimitingResourceQueue findQueueByResource( + List queues, Resource resource) { + for (LimitingResourceQueue each : queues) { + if (each.getResource().getId().equals(resource.getId())) { + return each; + } + } + return null; + } + + private LimitingResourceQueue retrieveQueueFromModel(LimitingResourceQueue queue) { + return findQueue(limitingResourceQueues, queue); + } + + private LimitingResourceQueue findQueue(List queues, + LimitingResourceQueue queue) { + for (LimitingResourceQueue each : limitingResourceQueues) { + if (each.getId().equals(queue.getId())) { + return each; + } + } + return null; + } + + private LimitingResourceQueueElement retrieveQueueElementFromModel( + LimitingResourceQueueElement element) { + final LimitingResourceQueue queue = element.getLimitingResourceQueue(); + if (queue != null) { + return findQueueElement(queue.getLimitingResourceQueueElements(), + element); + } else { + return findQueueElement(unassignedLimitingResourceQueueElements, + element); + } + } + + private LimitingResourceQueueElement findQueueElement( + Collection elements, + LimitingResourceQueueElement element) { + for (LimitingResourceQueueElement each : elements) { + if (each.getId().equals(element.getId())) { + return each; + } + } + return null; + } + + + + @Override + @Transactional + public void confirm() { + applyChanges(); + } + + private void applyChanges() { + removeQueueElements(); + saveQueueElements(); + } + + private void saveQueueElements() { + for (LimitingResourceQueueElement each: toBeSaved) { + if (each != null) { + saveQueueElement(each); + } + } + toBeSaved.clear(); + } + + private void saveQueueElement(LimitingResourceQueueElement element) { + limitingResourceQueueElementDAO.save(element); + taskDAO.save(getAssociatedTask(element)); + } + + private Task getAssociatedTask(LimitingResourceQueueElement element) { + return element.getResourceAllocation().getTask(); + } + + private void removeQueueElements() { + for (LimitingResourceQueueElement each: toBeRemoved) { + removeQueueElement(each); + } + toBeRemoved.clear(); + } + + private void removeQueueElement(LimitingResourceQueueElement element) { + Task task = getAssociatedTask(element); + removeQueueDependenciesIfAny(task); + removeQueueElementById(element.getId()); + } + + private void removeQueueElementById(Long id) { + try { + limitingResourceQueueElementDAO.remove(id); + } catch (InstanceNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException("Trying to remove non-existing entity"); + } + } + + private void removeQueueDependenciesIfAny(Task task) { + for (Dependency each: task.getDependenciesWithThisOrigin()) { + removeQueueDependencyIfAny(each); + } + for (Dependency each: task.getDependenciesWithThisDestination()) { + removeQueueDependencyIfAny(each); + } + } + + private void removeQueueDependencyIfAny(Dependency dependency) { + LimitingResourceQueueDependency queueDependency = dependency.getQueueDependency(); + if (queueDependency != null) { + queueDependency.getHasAsOrigin().remove(queueDependency); + queueDependency.getHasAsDestiny().remove(queueDependency); + dependency.setQueueDependency(null); + dependencyDAO.save(dependency); + removeQueueDependencyById(queueDependency.getId()); + } + } + + @Autowired + private IDependencyDAO dependencyDAO; + + private void removeQueueDependencyById(Long id) { + try { + limitingResourceQueueDependencyDAO.remove(id); + } catch (InstanceNotFoundException e) { + e.printStackTrace(); + throw new RuntimeException("Trying to remove non-existing entity"); + } + } + + /** + * Unschedules an element from the list of queue elements. The element is + * later added to the list of unassigned elements + */ + @Override + public void unschedule(LimitingResourceQueueElement element) { + LimitingResourceQueueElement queueElement = retrieveQueueElementFromModel(element); + LimitingResourceQueue queue = retrieveQueueFromModel(element.getLimitingResourceQueue()); + + queue.removeLimitingResourceQueueElement(queueElement); + + // Set as unassigned element + queueElement.setLimitingResourceQueue(null); + queueElement.setStartDate(null); + queueElement.setStartHour(0); + queueElement.setEndDate(null); + queueElement.setEndHour(0); + + queueElement.getResourceAllocation().removeLimitingDayAssignments(); + unassignedLimitingResourceQueueElements.add(queueElement); + markAsModified(queueElement); + } + + /** + * Removes an {@link LimitingResourceQueueElement} from the list of + * unassigned elements + * + */ + @Override + public void removeUnassignedLimitingResourceQueueElement( + LimitingResourceQueueElement element) { + LimitingResourceQueueElement queueElement = retrieveQueueElementFromModel(element); + + queueElement.getResourceAllocation().setLimitingResourceQueueElement(null); + queueElement.getResourceAllocation().getTask() + .removeAllResourceAllocations(); + unassignedLimitingResourceQueueElements.remove(queueElement); + markAsRemoved(queueElement); + } + + private void markAsRemoved(LimitingResourceQueueElement element) { + if (toBeSaved.contains(element)) { + toBeSaved.remove(element); + } + if (!toBeRemoved.contains(element)) { + toBeRemoved.add(element); + } + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java index 806a6e5d2..2abcb21ff 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java @@ -22,14 +22,26 @@ package org.navalplanner.web.limitingresources; import static org.navalplanner.web.I18nHelper._; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; +import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; +import org.jfree.util.Log; import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; -import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.SpecificResourceAllocation; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.web.common.Util; import org.navalplanner.web.limitingresources.LimitingResourcesPanel.IToolbarCommand; import org.navalplanner.web.planner.order.BankHolidaysMarker; import org.springframework.beans.factory.annotation.Autowired; @@ -45,6 +57,9 @@ import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.Composer; import org.zkoss.zul.Button; import org.zkoss.zul.Checkbox; +import org.zkoss.zul.Div; +import org.zkoss.zul.Grid; +import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Row; @@ -71,6 +86,8 @@ public class LimitingResourcesController implements Composer { private TimeTracker timeTracker; + private Grid gridUnassignedLimitingResourceQueueElements; + private final LimitingResourceQueueElementsRenderer limitingResourceQueueElementsRenderer = new LimitingResourceQueueElementsRenderer(); @@ -111,6 +128,8 @@ public class LimitingResourcesController implements Composer { this.parent.getChildren().clear(); this.parent.appendChild(limitingResourcesPanel); limitingResourcesPanel.afterCompose(); + gridUnassignedLimitingResourceQueueElements = (Grid) limitingResourcesPanel + .getFellowIfAny("gridUnassignedLimitingResourceQueueElements"); addCommands(limitingResourcesPanel); } catch (IllegalArgumentException e) { try { @@ -151,21 +170,126 @@ public class LimitingResourcesController implements Composer { } private LimitingResourcesPanel buildLimitingResourcesPanel() { - LimitingResourcesPanel result = new LimitingResourcesPanel( - limitingResourceQueueModel.getLimitingResourceQueues(), - timeTracker); - result.setVariable("limitingResourcesController", this, true); + return new LimitingResourcesPanel(this, timeTracker); + } + + /** + * Returns unassigned {@link LimitingResourceQueueElement} + * + * It's necessary to convert elements to a DTO that encapsulates properties + * such as task name or order name, since the only way of sorting by these + * fields is by having properties getTaskName or getOrderName on the + * elements returned + * + * @return + */ + public List getUnassignedLimitingResourceQueueElements() { + List result = new ArrayList(); + for (LimitingResourceQueueElement each : limitingResourceQueueModel + .getUnassignedLimitingResourceQueueElements()) { + result.add(toLimitingResourceQueueElementDTO(each)); + } return result; } - public List getUnassignedLimitingResourceQueueElements() { - return limitingResourceQueueModel.getUnassignedLimitingResourceQueueElements(); + private LimitingResourceQueueElementDTO toLimitingResourceQueueElementDTO( + LimitingResourceQueueElement element) { + final Task task = element.getResourceAllocation().getTask(); + final Order order = limitingResourceQueueModel.getOrderByTask(task); + return new LimitingResourceQueueElementDTO(element, order + .getName(), task.getName(), element + .getEarlierStartDateBecauseOfGantt()); + } + + /** + * DTO for list of unassigned {@link LimitingResourceQueueElement} + * + * @author Diego Pino Garcia + * + */ + public class LimitingResourceQueueElementDTO { + + private final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy"); + + private LimitingResourceQueueElement original; + + private String orderName; + + private String taskName; + + private String date; + + private Integer hoursToAllocate; + + private String resourceOrCriteria; + + public LimitingResourceQueueElementDTO( + LimitingResourceQueueElement element, String orderName, + String taskName, Date date) { + this.original = element; + this.orderName = orderName; + this.taskName = taskName; + this.date = DATE_FORMAT.format(date); + this.hoursToAllocate = element.getIntentedTotalHours(); + this.resourceOrCriteria = getResourceOrCriteria(element.getResourceAllocation()); + } + + private String getResourceOrCriteria(ResourceAllocation resourceAllocation) { + if (resourceAllocation instanceof SpecificResourceAllocation) { + final Resource resource = ((SpecificResourceAllocation) resourceAllocation) + .getResource(); + return (resource != null) ? resource.getName() : ""; + } else if (resourceAllocation instanceof GenericResourceAllocation) { + Set criteria = ((GenericResourceAllocation) resourceAllocation).getCriterions(); + return Criterion.getNames(criteria); + } + return StringUtils.EMPTY; + } + + public LimitingResourceQueueElement getOriginal() { + return original; + } + + public String getOrderName() { + return orderName; + } + + public String getTaskName() { + return taskName; + } + + public String getDate() { + return date; + } + + public Integer getHoursToAllocate() { + return (hoursToAllocate != null) ? hoursToAllocate : 0; + } + + public String getResourceOrCriteria() { + return resourceOrCriteria; + } + } public void filterBy(Order order) { this.filterBy = order; } + public void saveQueues() { + limitingResourceQueueModel.confirm(); + notifyUserThatSavingIsDone(); + } + + private void notifyUserThatSavingIsDone() { + try { + Messagebox.show(_("Scheduling saved"), _("Information"), Messagebox.OK, + Messagebox.INFORMATION); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + public LimitingResourceQueueElementsRenderer getLimitingResourceQueueElementsRenderer() { return limitingResourceQueueElementsRenderer; } @@ -174,29 +298,86 @@ public class LimitingResourcesController implements Composer { @Override public void render(Row row, Object data) throws Exception { - LimitingResourceQueueElement element = (LimitingResourceQueueElement) data; + LimitingResourceQueueElementDTO element = (LimitingResourceQueueElementDTO) data; - row.appendChild(label(getTaskName(element))); - row.appendChild(label(getOrderName(element))); - row.appendChild(assignButton(element)); + row.appendChild(label(element.getOrderName())); + row.appendChild(label(element.getTaskName())); + row.appendChild(label(element.getResourceOrCriteria())); + row.appendChild(label(element.getDate())); + row.appendChild(label(element.getHoursToAllocate().toString())); + row.appendChild(operations(element)); row.appendChild(automaticQueueing(element)); } - private Button assignButton(final LimitingResourceQueueElement element) { + private Hbox operations(LimitingResourceQueueElementDTO element) { + Hbox hbox = new Hbox(); + hbox.appendChild(assignButton(element)); + hbox.appendChild(removeButton(element)); + return hbox; + } + + private Button removeButton(final LimitingResourceQueueElementDTO element) { Button result = new Button(); - result.setLabel("Assign"); - result.setTooltiptext(_("Assign to queue")); + result.setLabel(_("Remove")); + result.setTooltiptext(_("Remove limiting resource element")); result.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - // FIXME: assign element to queue + removeUnassignedLimitingResourceQueueElement(element); } }); return result; } - private Checkbox automaticQueueing(final LimitingResourceQueueElement element) { + private void removeUnassignedLimitingResourceQueueElement( + LimitingResourceQueueElementDTO dto) { + + LimitingResourceQueueElement element = dto.getOriginal(); + limitingResourceQueueModel + .removeUnassignedLimitingResourceQueueElement(element); + Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); + } + + private Button assignButton( + final LimitingResourceQueueElementDTO element) { + Button result = new Button(); + result.setLabel(_("Assign")); + result.setTooltiptext(_("Assign to queue")); + result.addEventListener(Events.ON_CLICK, new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + assignLimitingResourceQueueElement(element); + } + }); + return result; + } + + private void assignLimitingResourceQueueElement( + LimitingResourceQueueElementDTO dto) { + + LimitingResourceQueueElement element = dto.getOriginal(); + if (limitingResourceQueueModel + .assignLimitingResourceQueueElement(element)) { + Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); + limitingResourcesPanel.appendQueueElementToQueue(element); + } else { + showErrorMessage(_("Cannot allocate selected element. There is not any queue " + + "that matches resource allocation criteria at any interval of time")); + } + } + + private void showErrorMessage(String error) { + try { + Messagebox.show(error, _("Error"), Messagebox.OK, Messagebox.ERROR); + } catch (InterruptedException e) { + + } + } + + private Checkbox automaticQueueing( + final LimitingResourceQueueElementDTO element) { Checkbox result = new Checkbox(); result.setTooltiptext(_("Select for automatic queuing")); return result; @@ -206,22 +387,15 @@ public class LimitingResourcesController implements Composer { return new Label(value); } - private TaskElement getTask(LimitingResourceQueueElement element) { - return element.getResourceAllocation().getTask(); - } + } - private String getTaskName(LimitingResourceQueueElement element) { - return getTask(element).getName(); - } - - private Order getOrder(LimitingResourceQueueElement element) { - return limitingResourceQueueModel.getOrderByTask(getTask(element)); - } - - private String getOrderName(LimitingResourceQueueElement element) { - return getOrder(element).getName(); - } + public List getLimitingResourceQueues() { + return limitingResourceQueueModel.getLimitingResourceQueues(); + } + public void unschedule(QueueTask task) { + limitingResourceQueueModel.unschedule(task.getLimitingResourceQueueElement()); + Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesLeftPane.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesLeftPane.java index ebbe37c99..5d3e783e3 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesLeftPane.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesLeftPane.java @@ -21,7 +21,6 @@ package org.navalplanner.web.limitingresources; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import org.navalplanner.business.resources.entities.LimitingResourceQueue; @@ -32,7 +31,6 @@ import org.zkoss.zul.Div; import org.zkoss.zul.Label; import org.zkoss.zul.Popup; import org.zkoss.zul.Treecell; -import org.zkoss.zul.Treechildren; import org.zkoss.zul.Treeitem; import org.zkoss.zul.TreeitemRenderer; import org.zkoss.zul.Treerow; @@ -41,11 +39,11 @@ import org.zkoss.zul.api.Tree; public class LimitingResourcesLeftPane extends HtmlMacroComponent { private MutableTreeModel modelForTree; - private final LimitingResourcesList limitingResourcesList; + private final QueueListComponent limitingResourcesList; public LimitingResourcesLeftPane( MutableTreeModel treeModel, - LimitingResourcesList resourceLoadList) { + QueueListComponent resourceLoadList) { this.limitingResourcesList = resourceLoadList; this.modelForTree = treeModel; } @@ -87,35 +85,26 @@ public class LimitingResourcesLeftPane extends HtmlMacroComponent { }; } - private void collapse(LimitingResourceQueue line) { - // unnecesary - limitingResourcesList.collapse(line); - } - - private void expand(LimitingResourceQueue line, - List closed) { - // unnecesary - // limitingResourcesList.expand(line, closed); - } - - private List calculatedClosedItems(Treeitem item) { - List result = new ArrayList(); - Treechildren treeChildren = item.getTreechildren(); - if (treeChildren != null) { - List myTreeItems = (List) treeChildren - .getChildren(); - Iterator iterator = myTreeItems.iterator(); - while (iterator.hasNext()) { - Treeitem child = (Treeitem) iterator.next(); - if (!child.isOpen()) { - result.addAll(getLineChildrenBy(child)); - } else { - result.addAll(calculatedClosedItems(child)); - } - } - } - return result; - } + // private List calculatedClosedItems(Treeitem item) + // { + // List result = new + // ArrayList(); + // Treechildren treeChildren = item.getTreechildren(); + // if (treeChildren != null) { + // List myTreeItems = (List) treeChildren + // .getChildren(); + // Iterator iterator = myTreeItems.iterator(); + // while (iterator.hasNext()) { + // Treeitem child = (Treeitem) iterator.next(); + // if (!child.isOpen()) { + // result.addAll(getLineChildrenBy(child)); + // } else { + // result.addAll(calculatedClosedItems(child)); + // } + // } + // } + // return result; + // } private List getLineChildrenBy(Treeitem item) { List result = new ArrayList(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java deleted file mode 100644 index 80d34ac0c..000000000 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of NavalPlan - * - * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e - * Desenvolvemento Tecnolóxico de Galicia - * - * 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 . - */ - -package org.navalplanner.web.limitingresources; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.navalplanner.business.resources.entities.LimitingResourceQueue; -import org.zkoss.ganttz.timetracker.TimeTracker; -import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; -import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; -import org.zkoss.ganttz.util.MutableTreeModel; -import org.zkoss.zk.au.out.AuInvoke; -import org.zkoss.zk.ui.HtmlMacroComponent; -import org.zkoss.zk.ui.ext.AfterCompose; - -/** - * Component to include a list of ResourceLoads inside the ResourcesLoadPanel. - * @author Lorenzo Tilve Álvaro - */ -public class LimitingResourcesList extends HtmlMacroComponent implements - AfterCompose { - - private final IZoomLevelChangedListener zoomListener; - - private Map fromTimeLineToComponent = new HashMap(); - - private final MutableTreeModel timelinesTree; - - private List limitingResourcesComponents = new ArrayList(); - - public LimitingResourcesList(TimeTracker timeTracker, - MutableTreeModel timelinesTree) { - this.timelinesTree = timelinesTree; - zoomListener = adjustTimeTrackerSizeListener(); - timeTracker.addZoomListener(zoomListener); - LimitingResourceQueue current = timelinesTree.getRoot(); - List toInsert = new ArrayList(); - fill(timelinesTree, current, toInsert); - insertAsComponents(timeTracker, toInsert); - } - - private void fill(MutableTreeModel timelinesTree, - LimitingResourceQueue current, List result) { - final int length = timelinesTree.getChildCount(current); - for (int i = 0; i < length; i++) { - LimitingResourceQueue child = timelinesTree.getChild(current, i); - result.add(child); - fill(timelinesTree, child, result); - } - } - - private IZoomLevelChangedListener adjustTimeTrackerSizeListener() { - return new IZoomLevelChangedListener() { - - @Override - public void zoomLevelChanged(ZoomLevel detailLevel) { - response(null, new AuInvoke(LimitingResourcesList.this, - "adjustTimeTrackerSize")); - response(null, new AuInvoke(LimitingResourcesList.this, - "adjustResourceLoadRows")); - } - }; - } - - private void insertAsComponents(TimeTracker timetracker, - List children) { - for (LimitingResourceQueue LimitingResourceQueue : children) { - LimitingResourcesComponent component = LimitingResourcesComponent - .create(timetracker, LimitingResourceQueue); - limitingResourcesComponents.add(component); - appendChild(component); - fromTimeLineToComponent.put(LimitingResourceQueue, component); - } - } - - public void collapse(LimitingResourceQueue line) { - } - - private LimitingResourcesComponent getComponentFor(LimitingResourceQueue l) { - LimitingResourcesComponent resourceLoadComponent = fromTimeLineToComponent - .get(l); - return resourceLoadComponent; - } - - public void expand(LimitingResourceQueue line, - List closed) { - } - - @Override - public void afterCompose() { - super.afterCompose(); - for (LimitingResourcesComponent each : limitingResourcesComponents) { - each.afterCompose(); - } - } -} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java index 4fc6e9a5b..5c8f6e79b 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java @@ -20,12 +20,14 @@ package org.navalplanner.web.limitingresources; -import static org.zkoss.ganttz.i18n.I18nHelper._; +import static org.navalplanner.web.I18nHelper._; -import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.apache.commons.lang.StringUtils; +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.springframework.beans.factory.annotation.Autowired; @@ -43,7 +45,6 @@ import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zul.Button; -import org.zkoss.zul.Div; import org.zkoss.zul.ListModel; import org.zkoss.zul.Listbox; import org.zkoss.zul.Separator; @@ -59,23 +60,18 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { public String getImage(); } + private LimitingResourcesController limitingResourcesController; + private TimeTrackerComponent timeTrackerComponent; private LimitingResourcesLeftPane leftPane; - private LimitingResourcesList limitingResourcesList; - - private List limitingResourceQueues = new ArrayList(); + private QueueListComponent queueListComponent; private MutableTreeModel treeModel; private TimeTracker timeTracker; - // private LimitingDependencyList dependencyList; - - // private WeakReferencedListeners zoomListeners = - // WeakReferencedListeners.create(); - private Listbox listZoomLevels; @Autowired @@ -85,24 +81,64 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { private static final String filterCriterions = _("Filter by criterions"); private boolean filterbyResources = true; - public LimitingResourcesPanel(List groups, - TimeTracker timeTracker) { - init(groups, timeTracker); + private LimitingDependencyList dependencyList = new LimitingDependencyList(this); + /** + * Returns the closest upper {@link LimitingResourcesPanel} instance going + * all the way up from comp + * + * @param comp + * @return + */ + public static LimitingResourcesPanel getLimitingResourcesPanel(Component comp) { + if (comp == null) { + return null; + } + if (comp instanceof LimitingResourcesPanel) { + return (LimitingResourcesPanel) comp; + } + return getLimitingResourcesPanel(comp.getParent()); } - public void init(List groups, TimeTracker timeTracker) { - this.limitingResourceQueues = groups; + public LimitingResourcesPanel(LimitingResourcesController limitingResourcesController, + TimeTracker timeTracker) { + init(limitingResourcesController, timeTracker); + } + + public void init(LimitingResourcesController limitingResourcesController, + TimeTracker timeTracker) { + this.limitingResourcesController = limitingResourcesController; this.timeTracker = timeTracker; + this.setVariable("limitingResourcesController", + limitingResourcesController, true); + treeModel = createModelForTree(); timeTrackerComponent = timeTrackerForResourcesLoadPanel(timeTracker); - limitingResourcesList = new LimitingResourcesList(timeTracker, + queueListComponent = new QueueListComponent(timeTracker, treeModel); + leftPane = new LimitingResourcesLeftPane(treeModel, - limitingResourcesList); + queueListComponent); registerNeededScripts(); } + public void appendQueueElementToQueue(LimitingResourceQueueElement element) { + queueListComponent.appendQueueElement(element); + } + + private MutableTreeModel createModelForTree() { + MutableTreeModel result = MutableTreeModel + .create(LimitingResourceQueue.class); + for (LimitingResourceQueue LimitingResourceQueue : getLimitingResourceQueues()) { + result.addToRoot(LimitingResourceQueue); + } + return result; + } + + private List getLimitingResourceQueues() { + return limitingResourcesController.getLimitingResourceQueues(); + } + public ListModel getFilters() { String[] filters = new String[] { filterResources, filterCriterions }; return new SimpleListModel(filters); @@ -175,7 +211,8 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { } private void registerNeededScripts() { - // getScriptsRegister().register(ScriptsRequiredByResourceLoadPanel.class); + // getScriptsRegister().register( + // ScriptsRequiredByLimitingResourcesPanel.class); } private IScriptsRegister getScriptsRegister() { @@ -183,22 +220,12 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { .retrieve(); } - private MutableTreeModel createModelForTree() { - MutableTreeModel result = MutableTreeModel - .create(LimitingResourceQueue.class); - for (LimitingResourceQueue LimitingResourceQueue : this.limitingResourceQueues) { - result.addToRoot(LimitingResourceQueue); - } - return result; - } - - private TimeTrackerComponent timeTrackerForResourcesLoadPanel( TimeTracker timeTracker) { return new TimeTrackerComponent(timeTracker) { @Override protected void scrollHorizontalPercentage(int pixelsDisplacement) { - response("", new AuInvoke(limitingResourcesList, + response("", new AuInvoke(queueListComponent, "adjustScrollHorizontalPosition", pixelsDisplacement + "")); } @@ -210,28 +237,19 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { super.afterCompose(); - // Insert resourcesList left pane component + // Insert leftPane component with limitingresources list getFellow("insertionPointLeftPanel").appendChild(leftPane); leftPane.afterCompose(); - // Insert timetracker watermarks and limitingResourcesQueues getFellow("insertionPointRightPanel").appendChild(timeTrackerComponent); - getFellow("insertionPointRightPanel") - .appendChild(limitingResourcesList); - limitingResourcesList.afterCompose(); + getFellow("insertionPointRightPanel").appendChild(queueListComponent); + queueListComponent.afterCompose(); - Div source = new Div(); - Div destination = new Div(); - - LimitingDependencyComponent limitingDependencyComponent = new LimitingDependencyComponent( - source, destination); - - LimitingDependencyList dependencyList = new LimitingDependencyList(null); - dependencyList.addDependencyComponent(limitingDependencyComponent); - - getFellow("insertionPointRightPanel").appendChild(dependencyList); - - dependencyList.afterCompose(); + dependencyList = generateDependencyComponentsList(); + if (dependencyList != null) { + dependencyList.afterCompose(); + getFellow("insertionPointRightPanel").appendChild(dependencyList); + } // Insert timetracker headers TimeTrackerComponent timeTrackerHeader = createTimeTrackerHeader(); @@ -243,20 +261,77 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { listZoomLevels.setSelectedIndex(timeTracker.getDetailLevel().ordinal()); } + private LimitingDependencyList generateDependencyComponentsList() { + final Map queueElementsMap = queueListComponent + .getLimitingResourceElementToQueueTaskMap(); + + for (LimitingResourceQueueElement queueElement : queueElementsMap + .keySet()) { + for (LimitingResourceQueueDependency dependency : queueElement + .getDependenciesAsOrigin()) { + addDependencyComponent(dependencyList, queueElementsMap, dependency); + } + } + return dependencyList; + } + + public void addDependencyComponent(LimitingResourceQueueDependency dependency) { + final Map queueElementsMap = queueListComponent + .getLimitingResourceElementToQueueTaskMap(); + addDependencyComponent(dependencyList, queueElementsMap, dependency); + } + + private void addDependencyComponent( + LimitingDependencyList dependencyList, + Map queueElementsMap, + LimitingResourceQueueDependency dependency) { + + LimitingDependencyComponent component = createDependencyComponent(queueElementsMap, dependency); + if (component != null) { + dependencyList.addDependencyComponent(component); + } + } + + private LimitingDependencyComponent createDependencyComponent( + Map queueElementsMap, + LimitingResourceQueueDependency dependency) { + + final QueueTask origin = queueElementsMap.get(dependency + .getHasAsOrigin()); + final QueueTask destination = queueElementsMap.get(dependency + .getHasAsDestiny()); + return (origin != null && destination != null) ? new LimitingDependencyComponent( + origin, destination) + : null; + } + public void clearComponents() { getFellow("insertionPointLeftPanel").getChildren().clear(); getFellow("insertionPointRightPanel").getChildren().clear(); getFellow("insertionPointTimetracker").getChildren().clear(); } - private TimeTrackerComponent createTimeTrackerHeader() { - return new TimeTrackerComponent( - timeTracker) { + public TimeTrackerComponent getTimeTrackerComponent() { + return timeTrackerComponent; + } - @Override - protected void scrollHorizontalPercentage(int pixelsDisplacement) { - } + private TimeTrackerComponent createTimeTrackerHeader() { + return new TimeTrackerComponent(timeTracker) { + + @Override + protected void scrollHorizontalPercentage(int pixelsDisplacement) { + } }; } -} \ No newline at end of file + public void unschedule(QueueTask task) { + limitingResourcesController.unschedule(task); + removeQueueTask(task); + } + + private void removeQueueTask(QueueTask task) { + task.detach(); + dependencyList.removeDependencyComponents(task); + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueComponent.java similarity index 50% rename from navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java rename to navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueComponent.java index b762ab899..531f331e7 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueComponent.java @@ -27,16 +27,17 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -import org.joda.time.LocalDate; import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.zkoss.ganttz.DatesMapperOnInterval; import org.zkoss.ganttz.IDatesMapper; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.MenuBuilder; import org.zkoss.ganttz.util.MenuBuilder.ItemAction; +import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.ext.AfterCompose; import org.zkoss.zul.Div; @@ -46,21 +47,25 @@ import org.zkoss.zul.impl.XulElement; * This class wraps ResourceLoad data inside an specific HTML Div component. * @author Lorenzo Tilve Álvaro */ -public class LimitingResourcesComponent extends XulElement implements +public class QueueComponent extends XulElement implements AfterCompose { - public static LimitingResourcesComponent create(TimeTracker timeTracker, + public static QueueComponent create(TimeTracker timeTracker, LimitingResourceQueue limitingResourceQueue) { - return new LimitingResourcesComponent(timeTracker, + return new QueueComponent(timeTracker, limitingResourceQueue); } private final LimitingResourceQueue limitingResourceQueue; private final TimeTracker timeTracker; private transient IZoomLevelChangedListener zoomChangedListener; - private List
    queueElementDivs = new ArrayList
    (); + private List queueTasks = new ArrayList(); - private LimitingResourcesComponent(final TimeTracker timeTracker, + public List getQueueTasks() { + return queueTasks; + } + + private QueueComponent(final TimeTracker timeTracker, final LimitingResourceQueue limitingResourceQueue) { this.limitingResourceQueue = limitingResourceQueue; this.timeTracker = timeTracker; @@ -79,64 +84,44 @@ public class LimitingResourcesComponent extends XulElement implements private void createChildren(LimitingResourceQueue limitingResourceQueue, IDatesMapper mapper) { - List
    divs = createDivsForQueueElements(mapper, + List queueTasks = createQueueTasks(mapper, limitingResourceQueue.getLimitingResourceQueueElements()); - if (divs != null) { - for (Div div : divs) { - appendChild(div); - } - queueElementDivs.addAll(divs); + if (queueTasks != null) { + appendQueueTasks(queueTasks); } } - public String getResourceName() { - return limitingResourceQueue.getResource().getName(); + private void appendQueueTasks(List queueTasks) { + for (QueueTask each: queueTasks) { + appendQueueTask(each); + } } - private static List
    createDivsForQueueElements( + private void appendQueueTask(QueueTask queueTask) { + queueTasks.add(queueTask); + appendChild(queueTask); + } + + private static List createQueueTasks( IDatesMapper datesMapper, Set list) { - List
    result = new ArrayList
    (); - for (LimitingResourceQueueElement queueElement : list) { - validateQueueElement(queueElement); - result.add(createDivForQueueElement(datesMapper, queueElement)); + List result = new ArrayList(); + for (LimitingResourceQueueElement each : list) { + result.add(createQueueTask(datesMapper, each)); } - return result; } - private static void validateQueueElement( + private static QueueTask createQueueTask(IDatesMapper datesMapper, LimitingResourceQueueElement element) { + validateQueueElement(element); + return createDivForElement(datesMapper, element); + } + + private static QueueTask createDivForElement(IDatesMapper datesMapper, LimitingResourceQueueElement queueElement) { - if ((queueElement.getStartDate() == null) - || (queueElement.getStartDate() == null)) { - throw new ValidationException(_("Invalid queue element")); - } - } - private void appendMenu(Div divElement) { - if (divElement.getPage() != null) { - MenuBuilder
    menuBuilder = MenuBuilder.on(divElement.getPage(), - queueElementDivs); - menuBuilder.item(_("Unassign"), "/common/img/ico_borrar.png", - new ItemAction
    () { - @Override - public void onEvent(Div choosen, Event event) { - unnasign(choosen); - } - }); - divElement.setContext(menuBuilder.createWithoutSettingContext()); - } - } - - // FIXME: Implement real unnasign operation - private void unnasign(Div choosen) { - choosen.detach(); - } - - private static Div createDivForQueueElement(IDatesMapper datesMapper, - LimitingResourceQueueElement queueElement) { - Div result = new Div(); + QueueTask result = new QueueTask(queueElement); result.setClass("queue-element"); result.setTooltiptext(queueElement.getLimitingResourceQueue() @@ -145,19 +130,55 @@ public class LimitingResourcesComponent extends XulElement implements result.setLeft(forCSS(getStartPixels(datesMapper, queueElement))); result.setWidth(forCSS(getWidthPixels(datesMapper, queueElement))); + result.appendChild(generateNonWorkableShade(datesMapper, queueElement)); + return result; } - private static int getWidthPixels(IDatesMapper datesMapper, - LimitingResourceQueueElement loadPeriod) { - LocalDate start = loadPeriod.getStartDate(); - LocalDate end = loadPeriod.getEndDate(); - return datesMapper - .toPixels(toMilliseconds(end) - toMilliseconds(start)); + private static Div generateNonWorkableShade(IDatesMapper datesMapper, + LimitingResourceQueueElement queueElement) { + + int workableHours = queueElement.getLimitingResourceQueue() + .getResource().getCalendar().getCapacityAt( + queueElement.getEndDate()); + + int shadeWidth = new Long((24 - workableHours) + * DatesMapperOnInterval.MILISECONDS_PER_HOUR + / datesMapper.getMilisecondsPerPixel()).intValue(); + + int shadeLeft = new Long((workableHours - queueElement.getEndHour()) + * DatesMapperOnInterval.MILISECONDS_PER_HOUR + / datesMapper.getMilisecondsPerPixel()).intValue() + + shadeWidth; + ; + + Div notWorkableHoursShade = new Div(); + notWorkableHoursShade + .setTooltiptext(_("Workable capacity for this period ") + + workableHours + _(" hours")); + + notWorkableHoursShade.setContext(""); + notWorkableHoursShade.setSclass("not-workable-hours"); + + notWorkableHoursShade.setStyle("left: " + shadeLeft + "px; width: " + + shadeWidth + "px;"); + return notWorkableHoursShade; } - private static long toMilliseconds(LocalDate localDate) { - return localDate.toDateMidnight().getMillis(); + private static int getWidthPixels(IDatesMapper datesMapper, + LimitingResourceQueueElement queueElement) { + return datesMapper.toPixels(getEndMillis(queueElement) + - getStartMillis(queueElement)); + } + + private static long getStartMillis(LimitingResourceQueueElement queueElement) { + return queueElement.getStartDate().toDateMidnight().getMillis() + + (queueElement.getStartHour() * DatesMapperOnInterval.MILISECONDS_PER_HOUR); + } + + private static long getEndMillis(LimitingResourceQueueElement queueElement) { + return queueElement.getEndDate().toDateMidnight().getMillis() + + (queueElement.getEndHour() * DatesMapperOnInterval.MILISECONDS_PER_HOUR); } private static String forCSS(int pixels) { @@ -166,12 +187,58 @@ public class LimitingResourcesComponent extends XulElement implements private static int getStartPixels(IDatesMapper datesMapper, LimitingResourceQueueElement queueElement) { - return datesMapper.toPixels(queueElement.getStartDate().toDateMidnight() - .toDate()); + return datesMapper + .toPixelsAbsolute((queueElement.getStartDate().toDateMidnight() +.getMillis() + queueElement.getStartHour() + * DatesMapperOnInterval.MILISECONDS_PER_HOUR)); + } + + public void appendQueueElement(LimitingResourceQueueElement element) { + QueueTask queueTask = createQueueTask(element); + appendQueueTask(queueTask); + appendMenu(queueTask); + } + + private QueueTask createQueueTask(LimitingResourceQueueElement element) { + validateQueueElement(element); + return createDivForElement(timeTracker.getMapper(), element); + } + + public String getResourceName() { + return limitingResourceQueue.getResource().getName(); + } + + private static void validateQueueElement( + LimitingResourceQueueElement queueElement) { + if ((queueElement.getStartDate() == null) + || (queueElement.getEndDate() == null)) { + throw new ValidationException(_("Invalid queue element")); + } + } + + private void appendMenu(QueueTask divElement) { + if (divElement.getPage() != null) { + MenuBuilder menuBuilder = MenuBuilder.on(divElement + .getPage(), divElement); + menuBuilder.item(_("Unassign"), "/common/img/ico_borrar.png", + new ItemAction() { + @Override + public void onEvent(QueueTask choosen, Event event) { + unnasign(choosen); + } + }); + divElement.setContext(menuBuilder.createWithoutSettingContext()); + } + } + + private void unnasign(QueueTask choosen) { + final LimitingResourcesPanel panel = LimitingResourcesPanel + .getLimitingResourcesPanel(choosen.getParent()); + panel.unschedule(choosen); } private void appendContextMenus() { - for (Div each : queueElementDivs) { + for (QueueTask each : queueTasks) { appendMenu(each); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueListComponent.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueListComponent.java new file mode 100644 index 000000000..5a7297de7 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueListComponent.java @@ -0,0 +1,148 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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 . + */ + +package org.navalplanner.web.limitingresources; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.zkoss.ganttz.timetracker.TimeTracker; +import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.MutableTreeModel; +import org.zkoss.zk.au.out.AuInvoke; +import org.zkoss.zk.ui.HtmlMacroComponent; +import org.zkoss.zk.ui.ext.AfterCompose; + +/** + * Component to include a list of {@link LimitingResourceQueue} inside the {@link LimitingResourcesPanel} + * + * @author Lorenzo Tilve Álvaro + * @author Diego Pino Garcia + */ +public class QueueListComponent extends HtmlMacroComponent implements + AfterCompose { + + private final IZoomLevelChangedListener zoomListener; + + private MutableTreeModel model; + + private TimeTracker timeTracker; + + private Map fromQueueToComponent = new HashMap(); + + public QueueListComponent(TimeTracker timeTracker, + MutableTreeModel timelinesTree) { + + this.model = timelinesTree; + + zoomListener = adjustTimeTrackerSizeListener(); + timeTracker.addZoomListener(zoomListener); + this.timeTracker = timeTracker; + + insertAsComponents(timelinesTree.asList()); + } + + private void insertAsComponents(List children) { + for (LimitingResourceQueue each : children) { + insertAsComponent(each); + } + } + + private void insertAsComponent(LimitingResourceQueue queue) { + QueueComponent component = QueueComponent.create(timeTracker, queue); + this.appendChild(component); + fromQueueToComponent.put(queue, component); + } + + public void setModel(MutableTreeModel model) { + this.model = model; + } + + public void invalidate() { + fromQueueToComponent.clear(); + this.getChildren().clear(); + insertAsComponents(model.asList()); + super.invalidate(); + } + + public void appendQueueElement(LimitingResourceQueueElement element) { + QueueComponent queueComponent = fromQueueToComponent.get(element + .getLimitingResourceQueue()); + queueComponent.appendQueueElement(element); + addDependenciesInPanel(element); + } + + private void addDependenciesInPanel(LimitingResourceQueueElement element) { + LimitingResourcesPanel panel = LimitingResourcesPanel + .getLimitingResourcesPanel(this); + for (LimitingResourceQueueDependency each : element + .getDependenciesAsDestiny()) { + panel.addDependencyComponent(each); + } + for (LimitingResourceQueueDependency each : element + .getDependenciesAsOrigin()) { + panel.addDependencyComponent(each); + } + } + + private IZoomLevelChangedListener adjustTimeTrackerSizeListener() { + return new IZoomLevelChangedListener() { + + @Override + public void zoomLevelChanged(ZoomLevel detailLevel) { + response(null, new AuInvoke(QueueListComponent.this, + "adjustTimeTrackerSize")); + response(null, new AuInvoke(QueueListComponent.this, + "adjustResourceLoadRows")); + } + }; + } + + @Override + public void afterCompose() { + super.afterCompose(); + for (QueueComponent each : fromQueueToComponent.values()) { + each.afterCompose(); + } + } + + public List getQueueTasks() { + List result = new ArrayList(); + for (QueueComponent each : fromQueueToComponent.values()) { + result.addAll(each.getQueueTasks()); + } + return result; + } + + public Map getLimitingResourceElementToQueueTaskMap() { + Map result = new HashMap(); + for (QueueTask each : getQueueTasks()) { + result.put(each.getLimitingResourceQueueElement(), each); + } + return result; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTask.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTask.java new file mode 100644 index 000000000..45dc000a4 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTask.java @@ -0,0 +1,86 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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 . + */ + +package org.navalplanner.web.limitingresources; + +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.LocalDate; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.util.Clients; +import org.zkoss.zul.Div; + +public class QueueTask extends Div { + + private static final Log LOG = LogFactory.getLog(QueueTask.class); + + private final LocalDate start; + + private final LocalDate end; + + private final LimitingResourceQueueElement element; + + public QueueTask(LimitingResourceQueueElement element) { + Validate.notNull(element.getStartDate()); + Validate.notNull(element.getEndDate()); + Validate + .isTrue(!(element.getStartDate()).isAfter(element.getEndDate())); + this.start = element.getStartDate(); + this.end = element.getEndDate(); + this.element = element; + setAction("onmouseover: zkLimitingDependencies.showDependenciesForQueueElement('" + + getUuid() + + "');onmouseout: zkLimitingDependencies.hideDependenciesForQueueElement('" + + getUuid() + "')"); + + final String taskUid = this.getUuid(); + this.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + Clients + .evalJavaScript("zkLimitingDependencies.toggleDependenciesForQueueElement('" + + taskUid + "')"); + } + }); + } + + public LocalDate getStart() { + return start; + } + + public LocalDate getEnd() { + return end; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public LimitingResourceQueueElement getLimitingResourceQueueElement() { + return element; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTaskGenerator.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTaskGenerator.java index 7247617c1..bf1a1b28e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTaskGenerator.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTaskGenerator.java @@ -20,7 +20,6 @@ package org.navalplanner.web.limitingresources; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -33,7 +32,6 @@ import org.apache.commons.lang.Validate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.joda.time.LocalDate; -import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.GenericResourceAllocation; import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.SpecificDayAssignment; @@ -42,8 +40,6 @@ import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionCompounder; import org.navalplanner.business.resources.entities.ICriterion; import org.navalplanner.business.resources.entities.Resource; -import org.zkoss.ganttz.data.limitingresource.QueueTask; -import org.zkoss.ganttz.data.resourceload.LoadLevel; interface QueueTaskGeneratorFactory { QueueTaskGenerator create(ResourceAllocation allocation); @@ -201,24 +197,8 @@ abstract class QueueTaskGenerator { && other.end.compareTo(end) <= 0; } - public QueueTask build() { - return new QueueTask(start, end, getTotalWorkHours(), - getHoursAssigned(), new LoadLevel( - calculateLoadPercentage())); - } - protected abstract int getTotalWorkHours(); - private int calculateLoadPercentage() { - final int totalResourceWorkHours = getTotalWorkHours(); - int assigned = getHoursAssigned(); - if (totalResourceWorkHours == 0) { - return assigned == 0 ? 0 : Integer.MAX_VALUE; - } - double proportion = assigned / (double) totalResourceWorkHours; - return new BigDecimal(proportion).scaleByPowerOfTen(2).intValue(); - } - protected abstract int getHoursAssigned(); protected final int sumAllocations() { @@ -344,22 +324,4 @@ class QueueTaskGeneratorOnCriterion extends QueueTaskGenerator { private Map> specificByResourceCached = new HashMap>(); - private List getSpecificOrderedAssignmentsFor( - Resource resource) { - if (!specificByResourceCached.containsKey(resource)) { - specificByResourceCached.put(resource, DayAssignment - .specific(DayAssignment.orderedByDay(resource - .getAssignments()))); - } - return specificByResourceCached.get(resource); - } - - private int sum(List specific) { - int result = 0; - for (SpecificDayAssignment s : specific) { - result += s.getHours(); - } - return result; - } - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java index 1bacef8a5..d54407b0c 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java @@ -68,6 +68,7 @@ import org.zkoss.zul.Listcell; import org.zkoss.zul.Listitem; import org.zkoss.zul.ListitemRenderer; import org.zkoss.zul.Radio; +import org.zkoss.zul.Tabbox; import org.zkoss.zul.XYModel; /** @@ -94,6 +95,8 @@ public class ManageOrderElementAdvancesController extends private Component messagesContainerAdvances; + private Tabbox tabboxOrderElement; + @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); @@ -139,6 +142,7 @@ public class ManageOrderElementAdvancesController extends Level.ERROR, e.getMessage()); LOG.error(_("Couldn't find element: {0}", e.getKey()), e); } + increaseScreenHeight(); return false; } @@ -165,6 +169,20 @@ public class ManageOrderElementAdvancesController extends return orderElementModel.getOrderElement(); } + private void increaseScreenHeight() { + if (tabboxOrderElement != null) { + tabboxOrderElement.setHeight("680px"); + tabboxOrderElement.invalidate(); + } + } + + private void resetScreenHeight() { + if (tabboxOrderElement != null) { + tabboxOrderElement.setHeight("620px"); + tabboxOrderElement.invalidate(); + } + } + private void resetAdvancesGrid() { manageOrderElementAdvancesModel.resetAdvanceAssignment(); this.indexSelectedItem = -1; @@ -173,6 +191,7 @@ public class ManageOrderElementAdvancesController extends private void reloadAdvances() { Util.reloadBindings(self); + resetScreenHeight(); if (indexSelectedItem > -1) { editAdvances.setSelectedItem(editAdvances .getItemAtIndex(indexSelectedItem)); @@ -522,7 +541,8 @@ public class ManageOrderElementAdvancesController extends new EventListener() { @Override public void onEvent(Event event) throws Exception { - setReportGlobalAdvance(listItem); + resetScreenHeight(); + setReportGlobalAdvance(listItem); } }); Listcell listCell = new Listcell(); @@ -1135,6 +1155,7 @@ public class ManageOrderElementAdvancesController extends break; } if (!StringUtils.isBlank(message)) { + increaseScreenHeight(); messagesForUser.showMessage(Level.ERROR, message); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java index 2552c3a2e..d696d0dc2 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java @@ -320,7 +320,6 @@ public class ManageOrderElementAdvancesModel implements @Override public void cleanAdvance(DirectAdvanceAssignment advanceAssignment) { if (advanceAssignment != null) { - advanceAssignment.setReportGlobalAdvance(false); advanceAssignment.getAdvanceMeasurements().clear(); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementController.java index 8ddf58df2..263e2fc7a 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementController.java @@ -20,6 +20,9 @@ package org.navalplanner.web.orders; +import static org.navalplanner.web.I18nHelper._; + +import org.apache.commons.lang.StringUtils; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.orders.entities.OrderElement; import org.navalplanner.business.orders.entities.OrderLine; @@ -109,6 +112,15 @@ public class OrderElementController extends GenericForwardComposer { } } + public String getOrderElementName() { + String name = ""; + if ((getOrderElement() != null) + && (!StringUtils.isBlank(getOrderElement().getName()))) { + name = ": " + getOrderElement().getName(); + } + return _("Edit order element {0}", name); + } + public void setupManageOrderElementAdvancesController() throws Exception { if (manageOrderElementAdvancesController == null) { @@ -188,6 +200,7 @@ public class OrderElementController extends GenericForwardComposer { assignedTaskQualityFormsController = null; try { + ((Window) self).setTitle(getOrderElementName()); ((Window) self).doModal(); } catch (SuspendNotAllowedException e) { throw new RuntimeException(e); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java index 41d94c102..16ab7a47c 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java @@ -60,14 +60,12 @@ import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; -import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.KeyEvent; import org.zkoss.zul.Button; import org.zkoss.zul.Constraint; import org.zkoss.zul.Datebox; import org.zkoss.zul.Hbox; import org.zkoss.zul.Intbox; -import org.zkoss.zul.Label; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Tab; import org.zkoss.zul.Textbox; @@ -320,7 +318,7 @@ public class OrderElementTreeController extends TreeController { } private void registerKeyboardListener(final InputElement inputElement) { - inputElement.setCtrlKeys("#up#down#left#right"); + inputElement.setCtrlKeys("#up#down"); inputElement.addEventListener("onCtrlKey", new EventListener() { private Treerow treerow = getCurrentTreeRow(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderPredicate.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderPredicate.java index 45a80b0f1..6207afe09 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderPredicate.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderPredicate.java @@ -195,20 +195,14 @@ public class OrderPredicate implements IPredicate { if ((initDate == null) && (startDate == null)) { return true; } - return isInTheRangeFilterDates(initDate); + return isLowerToFinishDate(initDate, finishDate); } protected boolean acceptFinishDate(Date deadLine) { if ((deadLine == null) && (finishDate == null)) { return true; } - return isInTheRangeFilterDates(deadLine); - } - - private boolean isInTheRangeFilterDates(Date date) { - // Check if date is into interval between the startdate and finish date - return (isGreaterToStartDate(date, startDate) && isLowerToFinishDate( - date, finishDate)); + return isGreaterToStartDate(deadLine, startDate); } private boolean isGreaterToStartDate(Date date, Date startDate) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/ITaskElementAdapter.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/ITaskElementAdapter.java index 8d1d184f8..275fafc3f 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/ITaskElementAdapter.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/ITaskElementAdapter.java @@ -20,7 +20,6 @@ package org.navalplanner.web.planner; -import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.scenarios.entities.Scenario; import org.zkoss.ganttz.adapters.IAdapterToTaskFundamentalProperties; @@ -31,14 +30,5 @@ import org.zkoss.ganttz.adapters.IAdapterToTaskFundamentalProperties; */ public interface ITaskElementAdapter extends IAdapterToTaskFundamentalProperties{ - public interface IOnMoveListener { - public void moved(TaskElement taskElement); - } - - public void addListener(IOnMoveListener moveListener); - - public void removeListener(IOnMoveListener moveListener); - - void initialize(Order order, Scenario scenario); - + void useScenario(Scenario scenario); } 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 f59b164bb..3d96db9cb 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 @@ -25,6 +25,7 @@ import static org.navalplanner.web.I18nHelper._; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -35,7 +36,6 @@ import java.util.Set; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.Validate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.Hibernate; @@ -44,8 +44,8 @@ import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.labels.entities.Label; 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.OrderStatusEnum; import org.navalplanner.business.planner.daos.IResourceAllocationDAO; import org.navalplanner.business.planner.daos.ITaskElementDAO; import org.navalplanner.business.planner.entities.Dependency; @@ -86,8 +86,6 @@ public class TaskElementAdapter implements ITaskElementAdapter { private static final Log LOG = LogFactory.getLog(TaskElementAdapter.class); - private Order order; - @Autowired private IAdHocTransactionService transactionService; @@ -106,13 +104,11 @@ public class TaskElementAdapter implements ITaskElementAdapter { @Autowired private IResourceAllocationDAO resourceAllocationDAO; - private List listeners = new ArrayList(); - private Scenario scenario; + @Override - public void initialize(Order order, Scenario scenario) { - this.order = order; + public void useScenario(Scenario scenario) { this.scenario = scenario; } @@ -169,7 +165,6 @@ public class TaskElementAdapter implements ITaskElementAdapter { public Long execute() { stepsBeforePossibleReallocation(); Long result = setBeginDateInsideTransaction(beginDate); - fireTaskElementMoved(taskElement); return result; } }); @@ -220,7 +215,6 @@ public class TaskElementAdapter implements ITaskElementAdapter { return null; } }); - fireTaskElementMoved(taskElement); } private void updateEndDate(long lengthMilliseconds) { @@ -481,6 +475,7 @@ public class TaskElementAdapter implements ITaskElementAdapter { result.append(_("Hours invested") + ": ").append( getHoursAdvancePercentage().multiply(new BigDecimal(100))) .append("%
    "); + result.append(_("State") +": ").append(getOrderState()); String labels = buildLabelsText(); if (!labels.equals("")) { result.append("
    " + _("Labels") @@ -489,6 +484,29 @@ public class TaskElementAdapter implements ITaskElementAdapter { return result.toString(); } + private String getOrderState() { + String cssClass; + OrderStatusEnum state = taskElement.getOrderElement().getOrder().getState(); + + if(Arrays.asList(OrderStatusEnum.ACCEPTED, + OrderStatusEnum.OFFERED,OrderStatusEnum.STARTED, + OrderStatusEnum.SUBCONTRACTED_PENDING_ORDER) + .contains(state)) { + if(taskElement.getAssignedStatus() == "assigned") { + cssClass="order-open-assigned"; + } + else { + cssClass="order-open-unassigned"; + } + } + else { + cssClass="order-closed"; + } + return "" + + state.toString() + + ""; + } + @Override public List> getStartConstraints() { if (taskElement instanceof Task) { @@ -540,6 +558,20 @@ public class TaskElementAdapter implements ITaskElementAdapter { return taskElement.isSubcontracted(); } + @Override + public boolean isLimiting() { + return taskElement.isLimiting(); + } + + @Override + public boolean isLimitingAndHasDayAssignments() { + return taskElement.isLimitingAndHasDayAssignments(); + } + + public boolean hasConsolidations() { + return taskElement.hasConsolidations(); + } + private void stepsBeforePossibleReallocation() { taskDAO.reattach(taskElement); reattachAllResourcesForTask(); @@ -637,21 +669,4 @@ public class TaskElementAdapter implements ITaskElementAdapter { type); } - private void fireTaskElementMoved(TaskElement taskElement) { - for (IOnMoveListener moveListener : listeners) { - moveListener.moved(taskElement); - } - } - - @Override - public void addListener(IOnMoveListener moveListener) { - Validate.notNull(moveListener); - listeners.add(moveListener); - } - - @Override - public void removeListener(IOnMoveListener moveListener) { - listeners.remove(moveListener); - } - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AdvancedAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AdvancedAllocationController.java index 497a618bf..ee04137f6 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AdvancedAllocationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AdvancedAllocationController.java @@ -50,6 +50,7 @@ import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.planner.entities.StretchesFunction.Type; +import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.web.common.IMessagesForUser; import org.navalplanner.web.common.Level; import org.navalplanner.web.common.MessagesForUser; @@ -754,15 +755,19 @@ public class AdvancedAllocationController extends GenericForwardComposer { } rowsCached = new ArrayList(); for (AllocationInput allocationInput : allocationInputs) { - Row groupingRow = buildGroupingRow(allocationInput); - groupingRows.put(allocationInput, groupingRow); - rowsCached.add(groupingRow); - List genericRows = genericRows(allocationInput); - groupingRow.listenTo(genericRows); - rowsCached.addAll(genericRows); - List specificRows = specificRows(allocationInput); - groupingRow.listenTo(specificRows); - rowsCached.addAll(specificRows); + if (allocationInput.getAggregate() + .getAllocationsSortedByStartDate().isEmpty()) { + } else { + Row groupingRow = buildGroupingRow(allocationInput); + groupingRows.put(allocationInput, groupingRow); + rowsCached.add(groupingRow); + List genericRows = genericRows(allocationInput); + groupingRow.listenTo(genericRows); + rowsCached.addAll(genericRows); + List specificRows = specificRows(allocationInput); + groupingRow.listenTo(specificRows); + rowsCached.addAll(specificRows); + } } return filterRows(rowsCached); } @@ -808,7 +813,8 @@ public class AdvancedAllocationController extends GenericForwardComposer { specificResourceAllocation.getResource() .getName(), 1, Arrays .asList(specificResourceAllocation), specificResourceAllocation - .getResource().getShortDescription()); + .getResource().getShortDescription(), + specificResourceAllocation.getResource().isLimitingResource()); } private List genericRows(AllocationInput allocationInput) { @@ -824,10 +830,10 @@ public class AdvancedAllocationController extends GenericForwardComposer { private Row buildGenericRow( GenericResourceAllocation genericResourceAllocation, Restriction restriction) { - return Row.createRow(messages, restriction, - ResourceLoadModel - .getName(genericResourceAllocation.getCriterions()), 1, Arrays - .asList(genericResourceAllocation)); + return Row.createRow(messages, restriction, Criterion + .getNames(genericResourceAllocation.getCriterions()), 1, Arrays + .asList(genericResourceAllocation), genericResourceAllocation + .isLimiting()); } private Row buildGroupingRow(AllocationInput allocationInput) { @@ -835,7 +841,7 @@ public class AdvancedAllocationController extends GenericForwardComposer { .createRestriction(); String taskName = _("{0}", allocationInput.getTaskName()); Row groupingRow = Row.createRow(messages, restriction, taskName, 0, - allocationInput.getAllocationsSortedByStartDate()); + allocationInput.getAllocationsSortedByStartDate(), false); return groupingRow; } @@ -951,16 +957,19 @@ class Row { AdvancedAllocationController.Restriction restriction, String name, int level, List> allocations, - String description) { - Row newRow = new Row(messages, restriction, name, level, allocations); + String description, boolean limiting) { + Row newRow = new Row(messages, restriction, name, level, allocations, + limiting); newRow.setDescription(description); return newRow; } static Row createRow(IMessagesForUser messages, AdvancedAllocationController.Restriction restriction, String name, - int level, List> allocations) { - return new Row(messages, restriction, name, level, allocations); + int level, List> allocations, + boolean limiting) { + return new Row(messages, restriction, name, level, allocations, + limiting); } public void markErrorOnTotal(String message) { @@ -1040,11 +1049,12 @@ class Row { } private Component buildAllHours() { - return isGroupingRow() ? new Label() : noNegativeIntbox(); + return (isGroupingRow() || isLimiting) ? new Label() + : noNegativeIntbox(); } private void addListenerIfNeeded(Component allHoursComponent) { - if (isGroupingRow()) { + if (isGroupingRow() || isLimiting) { return; } final Intbox intbox = (Intbox) allHoursComponent; @@ -1072,7 +1082,7 @@ class Row { } private void reloadAllHours() { - if (isGroupingRow()) { + if (isGroupingRow() || isLimiting) { Label label = (Label) allHoursInput; int totalHours = aggregate.getTotalHours(); if (label != null) { @@ -1085,12 +1095,17 @@ class Row { } else { Intbox intbox = (Intbox) allHoursInput; intbox.setValue(aggregate.getTotalHours()); + if (isLimiting) { + intbox.setDisabled(true); + } } } Component getFunction() { if (isGroupingRow()) { return new Label(); + } else if (isLimiting) { + return new Label(_("Limiting assignment")); } else { Hbox hbox = new Hbox(); @@ -1238,6 +1253,8 @@ class Row { private IAssignmentFunctionConfiguration[] functions = { none, defaultStrechesFunction, strechesWithInterpolation }; + private boolean isLimiting; + private Combobox getAssignmentFunctionsCombo() { AssignmentFunction assignmentFunction = getAllocation() .getAssignmentFunction(); @@ -1289,11 +1306,13 @@ class Row { private Row(IMessagesForUser messages, AdvancedAllocationController.Restriction restriction, String name, int level, - List> allocations) { + List> allocations, + boolean limiting) { this.messages = messages; this.restriction = restriction; this.name = name; this.level = level; + this.isLimiting = limiting; this.aggregate = new AggregateOfResourceAllocations( new ArrayList>(allocations)); } @@ -1358,6 +1377,9 @@ class Row { } else { Intbox intbox = (Intbox) component; intbox.setValue(getHoursForDetailItem(item)); + if (isLimiting) { + intbox.setDisabled(true); + } } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java index 88cff794e..3179d04d1 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java @@ -42,7 +42,6 @@ import org.navalplanner.web.planner.allocation.ResourceAllocationController.Deri import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; -import org.zkoss.zul.Checkbox; import org.zkoss.zul.Constraint; import org.zkoss.zul.Decimalbox; import org.zkoss.zul.Detail; @@ -73,7 +72,7 @@ public abstract class AllocationRow { ResourcesPerDay[] resourcesPerDay) { int i = 0; for (AllocationRow each : rows) { - each.setResourcesPerDay(resourcesPerDay[i++]); + each.setNonConsolidatedResourcesPerDay(resourcesPerDay[i++]); } } @@ -169,7 +168,19 @@ public abstract class AllocationRow { private String name; - private ResourcesPerDay resourcesPerDay; + private int originalHours; + + private int totalHours; + + private int consolidatedHours; + + private int nonConsolidatedHours; + + private ResourcesPerDay totalResourcesPerDay; + + private ResourcesPerDay nonConsolidatedResourcesPerDay; + + private ResourcesPerDay consolidatedResourcesPerDay; private Intbox hoursInput = new Intbox(); @@ -177,16 +188,15 @@ public abstract class AllocationRow { private Grid derivedAllocationsGrid; - private Checkbox satisfiedCheckbox; - private void initializeResourcesPerDayInput() { resourcesPerDayInput.setConstraint(new SimpleConstraint( SimpleConstraint.NO_NEGATIVE)); + resourcesPerDayInput.setWidth("80px"); Util.bind(resourcesPerDayInput, new Util.Getter() { @Override public BigDecimal get() { - return getResourcesPerDay().getAmount(); + return getNonConsolidatedResourcesPerDay().getAmount(); } }, new Util.Setter() { @@ -194,15 +204,23 @@ public abstract class AllocationRow { @Override public void set(BigDecimal value) { BigDecimal amount = value == null ? new BigDecimal(0) : value; - resourcesPerDay = ResourcesPerDay.amount(amount); + setNonConsolidatedResourcesPerDay(ResourcesPerDay + .amount(amount)); } }); } public AllocationRow() { - resourcesPerDay = ResourcesPerDay.amount(0); + originalHours = 0; + totalHours = 0; + consolidatedHours = 0; + nonConsolidatedHours = 0; + consolidatedResourcesPerDay = ResourcesPerDay.amount(0); + setNonConsolidatedResourcesPerDay(ResourcesPerDay.amount(0)); + totalResourcesPerDay = ResourcesPerDay.amount(0); initializeResourcesPerDayInput(); hoursInput.setValue(0); + hoursInput.setWidth("80px"); hoursInput.setDisabled(true); hoursInput.setConstraint(new SimpleConstraint( SimpleConstraint.NO_NEGATIVE)); @@ -254,8 +272,8 @@ public abstract class AllocationRow { this.name = name; } - public ResourcesPerDay getResourcesPerDay() { - return this.resourcesPerDay; + public ResourcesPerDay getNonConsolidatedResourcesPerDay() { + return this.nonConsolidatedResourcesPerDay; } public ResourcesPerDay getResourcesPerDayFromInput() { @@ -264,8 +282,9 @@ public abstract class AllocationRow { return ResourcesPerDay.amount(value); } - public void setResourcesPerDay(ResourcesPerDay resourcesPerDay) { - this.resourcesPerDay = resourcesPerDay; + public void setNonConsolidatedResourcesPerDay( + ResourcesPerDay resourcesPerDay) { + this.nonConsolidatedResourcesPerDay = resourcesPerDay; resourcesPerDayInput.setValue(getAmount(resourcesPerDay)); } @@ -282,7 +301,7 @@ public abstract class AllocationRow { public abstract boolean isGeneric(); public boolean isEmptyResourcesPerDay() { - return getResourcesPerDay().isZero(); + return getNonConsolidatedResourcesPerDay().isZero(); } public abstract List getAssociatedResources(); @@ -311,10 +330,10 @@ public abstract class AllocationRow { private Integer getHours() { if (temporal != null) { - return temporal.getAssignedHours(); + return temporal.getNonConsolidatedHours(); } if (origin != null) { - return origin.getAssignedHours(); + return origin.getNonConsolidatedHours(); } return 0; } @@ -419,19 +438,53 @@ public abstract class AllocationRow { } } - public Checkbox getSatisfiedCheckbox() { - if (satisfiedCheckbox != null) { - satisfiedCheckbox.setChecked(isSatisfied()); - return satisfiedCheckbox; - } - Checkbox result = new Checkbox(); - result.setChecked(isSatisfied()); - result.setDisabled(true); - return satisfiedCheckbox = result; + public void setOriginalHours(int originalHours) { + this.originalHours = originalHours; } - public void loadSatisfied() { - satisfiedCheckbox.setChecked(isSatisfied()); + public int getOriginalHours() { + return originalHours; + } + + public void setTotalHours(int totalHours) { + this.totalHours = totalHours; + } + + public int getTotalHours() { + return totalHours; + } + + public void setConsolidatedHours(int consolidatedHours) { + this.consolidatedHours = consolidatedHours; + } + + public int getConsolidatedHours() { + return consolidatedHours; + } + + public void setNonConsolidatedHours(int nonConsolidatedHours) { + this.nonConsolidatedHours = nonConsolidatedHours; + } + + public int getNonConsolidatedHours() { + return nonConsolidatedHours; + } + + public void setTotalResourcesPerDay(ResourcesPerDay totalResourcesPerDay) { + this.totalResourcesPerDay = totalResourcesPerDay; + } + + public ResourcesPerDay getTotalResourcesPerDay() { + return totalResourcesPerDay; + } + + public void setConsolidatedResourcesPerDay( + ResourcesPerDay consolidatedResourcesPerDay) { + this.consolidatedResourcesPerDay = consolidatedResourcesPerDay; + } + + public ResourcesPerDay getConsolidatedResourcesPerDay() { + return consolidatedResourcesPerDay; } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java index 141a30971..81f2b4f86 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/FormBinder.java @@ -61,6 +61,7 @@ import org.zkoss.zul.Datebox; import org.zkoss.zul.Decimalbox; import org.zkoss.zul.Grid; import org.zkoss.zul.Intbox; +import org.zkoss.zul.Label; import org.zkoss.zul.SimpleConstraint; import org.zkoss.zul.Tab; import org.zkoss.zul.impl.api.InputElement; @@ -69,14 +70,19 @@ public class FormBinder { private Intbox allHoursInput; + private Label allOriginalHours; + private Label allTotalHours; + private Label allConsolidatedHours; + + private Label allTotalResourcesPerDay; + private Label allConsolidatedResourcesPerDay; + private final AllocationRowsHandler allocationRowsHandler; private AggregateOfResourceAllocations aggregate; private AllocationResult lastAllocation; - private Datebox taskStartDateBox; - private Datebox endDate; private Button applyButton; @@ -244,22 +250,34 @@ public class FormBinder { return result; } + private int sumAllOriginalHours() { + int result = 0; + for (AllocationRow each : rows) { + result += each.getOriginalHours(); + } + return result; + } + + private int sumAllTotalHours() { + int result = 0; + for (AllocationRow each : rows) { + result += each.getTotalHours(); + } + return result; + } + + private int sumAllConsolidatedHours() { + int result = 0; + for (AllocationRow each : rows) { + result += each.getConsolidatedHours(); + } + return result; + } + public CalculatedValue getCalculatedValue() { return allocationRowsHandler.getCalculatedValue(); } - public void setTaskStartDateBox(Datebox taskStartDateBox) { - this.taskStartDateBox = taskStartDateBox; - this.taskStartDateBox.setDisabled(true); - loadValueForTaskStartDateBox(); - onChangeEnableApply(taskStartDateBox); - } - - private void loadValueForTaskStartDateBox() { - this.taskStartDateBox.setValue(allocationRowsHandler.getTask() - .getStartDate()); - } - private void onChangeEnableApply(InputElement inputElement) { inputElement.addEventListener(Events.ON_CHANGE, onChangeEnableApply); @@ -331,6 +349,7 @@ public class FormBinder { allHoursInputComponentDisabilityRule(); bindAllResourcesPerDayToRows(); allResourcesPerDayVisibilityRule(); + loadAggregatedCalculations(); return result; } @@ -367,11 +386,29 @@ public class FormBinder { private void reloadValues() { loadHoursValues(); - loadIsSatisfiedValues(); loadValueForAssignedHoursComponent(); - loadValueForTaskStartDateBox(); loadValueForEndDate(); loadDerivedAllocations(); + loadSclassRowSatisfied(); + } + + @SuppressWarnings("unchecked") + private void loadSclassRowSatisfied() { + try { + List rows = (List) allocationsGrid + .getRows().getChildren(); + for (org.zkoss.zul.Row row : rows) { + if (row.getValue() instanceof AllocationRow) { + if (!((AllocationRow) row.getValue()).isSatisfied()) { + row.setSclass("allocation-not-satisfied"); + } else { + row.setSclass("allocation-satisfied"); + } + } + } + } catch (ClassCastException e) { + throw new RuntimeException(); + } } private void loadHoursValues() { @@ -380,12 +417,6 @@ public class FormBinder { } } - private void loadIsSatisfiedValues() { - for (AllocationRow each : rows) { - each.loadSatisfied(); - } - } - private void loadDerivedAllocations() { for (AllocationRow each : rows) { each.reloadDerivedAllocationsGrid(); @@ -459,13 +490,13 @@ public class FormBinder { Level.ERROR, _( "there are no resources for required criteria: {0}. So the generic allocation can't be added", - ResourceLoadModel.getName(criterions))); + Criterion.getNames(criterions))); } public void markThereisAlreadyAssignmentWith(Set criterions) { messagesForUser.showMessage(Level.ERROR, _( "already exists an allocation for criteria {0}", - ResourceLoadModel.getName(criterions))); + Criterion.getNames(criterions))); } public void markEndDateMustBeAfterStartDate() { @@ -606,9 +637,72 @@ public class FormBinder { return sum; } - public void setStartDate(Date date) { - taskStartDateBox.setValue(date); - doApply(); + private BigDecimal sumAllTotalResourcesPerDay() { + BigDecimal sum = BigDecimal.ZERO; + for (AllocationRow each : rows) { + sum = sum.add(each.getTotalResourcesPerDay().getAmount()); + } + return sum; + } + + private BigDecimal sumAllConsolidatedResourcesPerDay() { + BigDecimal sum = BigDecimal.ZERO; + for (AllocationRow each : rows) { + sum = sum.add(each.getConsolidatedResourcesPerDay().getAmount()); + } + return sum; + } + + public void setAllOriginalHours(Label allOriginalHours) { + this.allOriginalHours = allOriginalHours; + } + + public Label getAllOriginalHours() { + return allOriginalHours; + } + + public void setAllTotalHours(Label allTotalHours) { + this.allTotalHours = allTotalHours; + } + + public Label getAllTotalHours() { + return allTotalHours; + } + + public void setAllConsolidatedHours(Label alCo1nsolidatedHours) { + this.allConsolidatedHours = alCo1nsolidatedHours; + } + + public Label getAllConsolidatedHours() { + return allConsolidatedHours; + } + + public void setAllTotalResourcesPerDay(Label allTotalResourcesPerDay) { + this.allTotalResourcesPerDay = allTotalResourcesPerDay; + } + + public Label getAllTotalResourcesPerDay() { + return allTotalResourcesPerDay; + } + + public void setAllConsolidatedResourcesPerDay( + Label allConsolidatedResourcesPerDay) { + this.allConsolidatedResourcesPerDay = allConsolidatedResourcesPerDay; + } + + public Label getAllConsolidatedResourcesPerDay() { + return allConsolidatedResourcesPerDay; + } + + public void loadAggregatedCalculations() { + allOriginalHours.setValue(Integer.toString(sumAllOriginalHours())); + allTotalHours.setValue(Integer.toString(sumAllTotalHours())); + allConsolidatedHours.setValue(Integer + .toString(sumAllConsolidatedHours())); + allTotalResourcesPerDay.setValue(sumAllTotalResourcesPerDay() + .toString()); + allConsolidatedResourcesPerDay + .setValue(sumAllConsolidatedResourcesPerDay().toString()); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/GenericAllocationRow.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/GenericAllocationRow.java index 965d6d05e..259e90bc4 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/GenericAllocationRow.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/GenericAllocationRow.java @@ -38,7 +38,6 @@ import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.web.resourceload.ResourceLoadModel; -import org.springframework.beans.factory.annotation.Autowired; /** * The information required for creating a {@link GenericResourceAllocation} @@ -49,7 +48,7 @@ public class GenericAllocationRow extends AllocationRow { private static GenericAllocationRow createDefault() { GenericAllocationRow result = new GenericAllocationRow(); result.setName(_("Generic")); - result.setResourcesPerDay(ResourcesPerDay.amount(0)); + result.setNonConsolidatedResourcesPerDay(ResourcesPerDay.amount(0)); return result; } @@ -60,18 +59,30 @@ public class GenericAllocationRow extends AllocationRow { GenericAllocationRow result = createDefault(); result.criterions = criterions; result.resources = new ArrayList(resources); - result.setName(ResourceLoadModel.getName(criterions)); + result.setName(Criterion.getNames(criterions)); return result; } public static GenericAllocationRow from( GenericResourceAllocation resourceAllocation, IResourceDAO resourceDAO) { GenericAllocationRow result = createDefault(); - result.setResourcesPerDay(resourceAllocation.getResourcesPerDay()); result.setOrigin(resourceAllocation); + + result.setOriginalHours(resourceAllocation.getOriginalTotalAssigment()); + result.setTotalHours(resourceAllocation.getAssignedHours()); + result.setConsolidatedHours(resourceAllocation.getConsolidatedHours()); + result.setNonConsolidatedHours(resourceAllocation + .getNonConsolidatedHours()); + + result.setNonConsolidatedResourcesPerDay(resourceAllocation + .getNonConsolidatedResourcePerDay()); + result.setConsolidatedResourcesPerDay(resourceAllocation + .getConsolidatedResourcePerDay()); + result.setTotalResourcesPerDay(resourceAllocation.getResourcesPerDay()); + result.criterions = resourceAllocation.getCriterions(); result.resources = resourceDAO.findSatisfyingCriterionsAtSomePoint(result.criterions); - result.setName(ResourceLoadModel.getName(result.criterions)); + result.setName(Criterion.getNames(result.criterions)); return result; } @@ -98,7 +109,7 @@ public class GenericAllocationRow extends AllocationRow { public ResourcesPerDayModification toResourcesPerDayModification(Task task) { GenericResourceAllocation newGeneric = createGenericAllocation(task); return ResourcesPerDayModification - .create(newGeneric, getResourcesPerDay(), this.resources); + .create(newGeneric, getNonConsolidatedResourcesPerDay(), this.resources); } private GenericResourceAllocation createGenericAllocation(Task task) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java index 907d4aba7..67caaee6e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/IResourceAllocationModel.java @@ -73,6 +73,8 @@ public interface IResourceAllocationModel extends INewAllocationsAdder { ProportionalDistributor addDefaultAllocations(); + Date getTaskStart(); + void setStartDate(Date date); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingAllocationRow.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingAllocationRow.java index 61b90fe88..adeafa8e5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingAllocationRow.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingAllocationRow.java @@ -74,20 +74,30 @@ public class LimitingAllocationRow { private LimitingAllocationRow(ResourceAllocation resourceAllocation, Task task) { + init(resourceAllocation, task); + } + + private void init(ResourceAllocation resourceAllocation, + Task task) { + initializeIntentedTotalHoursIfNeeded(resourceAllocation, task); this.resourceAllocation = resourceAllocation; this.task = task; - this.hours = calculateTotalHours(); + this.hours = resourceAllocation.getIntendedTotalHours(); this.priority = task.getPriority(); } /** - * Returns initial total hours + * Sets resourceAllocation.intentedTotalHours to task.totalHours if null * - * @return + * @param resourceAllocation + * @param task */ - private Integer calculateTotalHours() { - return (resourceAllocation != null && resourceAllocation.getIntendedTotalHours() != null) ? - resourceAllocation.getIntendedTotalHours() : task.getTotalHours(); + private void initializeIntentedTotalHoursIfNeeded( + ResourceAllocation resourceAllocation, Task task) { + Integer intentedTotalHours = resourceAllocation.getIntendedTotalHours(); + if (intentedTotalHours == null) { + resourceAllocation.setIntendedTotalHours(task.getTotalHours()); + } } public static LimitingAllocationRow create(Set criteria, @@ -111,13 +121,8 @@ public class LimitingAllocationRow { private LimitingAllocationRow(ResourceAllocation resourceAllocation, Task task, int priority) { - - this.resourceAllocation = resourceAllocation; - this.task = task; - this.hours = calculateTotalHours(); - task.setPriority(priority); - this.priority = task.getPriority(); + init(resourceAllocation, task); } public AllocationType getAllocationType() { @@ -133,7 +138,7 @@ public class LimitingAllocationRow { final AllocationType type = getAllocationType(); if (AllocationType.GENERIC.equals(type)) { final GenericResourceAllocation generic = (GenericResourceAllocation) resourceAllocation; - return formatCriteria(generic.getCriterions()); + return Criterion.getNames(generic.getCriterions()); } if (AllocationType.SPECIFIC.equals(type)) { return formatResources(resourceAllocation.getAssociatedResources()); @@ -141,14 +146,6 @@ public class LimitingAllocationRow { return ""; } - private String formatCriteria(Set criteria) { - List criteriaNames = new ArrayList(); - for (Criterion each: criteria) { - criteriaNames.add(each.getName()); - } - return (criteriaNames.isEmpty()) ? _("[generic all workers]") : StringUtils.join(criteriaNames, ","); - } - private String formatResources(List resources) { List resourcesNames = new ArrayList(); for (Resource each: resources) { @@ -229,4 +226,8 @@ public class LimitingAllocationRow { && resourceAllocation instanceof GenericResourceAllocation; } + public boolean hasDayAssignments() { + return resourceAllocation.hasAssignments(); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java index c2199086b..5a3c53508 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java @@ -68,6 +68,8 @@ public class LimitingResourceAllocationController extends GenericForwardComposer private Tab tabLimitingResourceAllocation; + private Tab tabLimitingWorkerSearch; + private Grid gridLimitingOrderElementHours; private Grid gridLimitingAllocations; @@ -93,10 +95,12 @@ public class LimitingResourceAllocationController extends GenericForwardComposer IMessagesForUser messagesForUser) { try { resourceAllocationModel.init(task); + tabLimitingWorkerSearch.setDisabled(existsResourceAllocationWithDayAssignments()); limitingNewAllocationSelector.setAllocationsAdder(resourceAllocationModel); gridLimitingOrderElementHours.setModel(new ListModelList( resourceAllocationModel.getHoursAggregatedByCriteria())); gridLimitingOrderElementHours.setRowRenderer(createOrderElementHoursRenderer()); + Util.reloadBindings(gridLimitingAllocations); } catch (Exception e) { LOG.error(e.getStackTrace()); } @@ -172,7 +176,9 @@ public class LimitingResourceAllocationController extends GenericForwardComposer } private Intbox intboxHours(final LimitingAllocationRow resourceAllocation) { - return bindToHours(new Intbox(), resourceAllocation); + Intbox result = bindToHours(new Intbox(), resourceAllocation); + result.setDisabled(resourceAllocation.hasDayAssignments()); + return result; } private Intbox bindToHours(Intbox intbox, final LimitingAllocationRow resourceAllocation) { @@ -194,7 +200,9 @@ public class LimitingResourceAllocationController extends GenericForwardComposer } private Listbox listboxPriority(final LimitingAllocationRow resourceAllocation) { - return bindToPriority(buildPriorityList(resourceAllocation.getPriority()), resourceAllocation); + Listbox result = bindToPriority(buildPriorityList(resourceAllocation.getPriority()), resourceAllocation); + result.setDisabled(resourceAllocation.hasDayAssignments()); + return result; } private Listbox buildPriorityList(int selectedValue) { @@ -237,4 +245,17 @@ public class LimitingResourceAllocationController extends GenericForwardComposer } + public boolean existsResourceAllocationWithDayAssignments() { + final LimitingAllocationRow limitingAllocationRow = getLimitingAllocationRow(); + return (limitingAllocationRow != null) ? limitingAllocationRow + .hasDayAssignments() : false; + } + + private LimitingAllocationRow getLimitingAllocationRow() { + final List limitingAllocationRows = resourceAllocationModel + .getResourceAllocationRows(); + return (limitingAllocationRows.size() > 0) ? limitingAllocationRows + .get(0) : null; + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java index 815ff1b77..b5b9dd7b0 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java @@ -265,12 +265,12 @@ public class LimitingResourceAllocationModel implements ILimitingResourceAllocat ResourceAllocation resourceAllocation = getAssociatedResourceAllocation(); if (resourceAllocation != null && resourceAllocation.isNewObject()) { task.removeAllResourceAllocations(); - addResourceAllocation(task, resourceAllocation); + addAssociatedLimitingResourceQueueElement(task, resourceAllocation); } taskDAO.save(task); } - private void addResourceAllocation(Task task, ResourceAllocation resourceAllocation) { + private void addAssociatedLimitingResourceQueueElement(Task task, ResourceAllocation resourceAllocation) { LimitingResourceQueueElement element = LimitingResourceQueueElement.create(); element.setEarlierStartDateBecauseOfGantt(task.getStartDate()); resourceAllocation.setLimitingResourceQueueElement(element); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java index 422814096..de3d6aac6 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java @@ -62,6 +62,7 @@ import org.zkoss.zul.Columns; import org.zkoss.zul.Datebox; import org.zkoss.zul.Decimalbox; import org.zkoss.zul.Grid; +import org.zkoss.zul.Hbox; import org.zkoss.zul.Intbox; import org.zkoss.zul.Label; import org.zkoss.zul.ListModelList; @@ -71,6 +72,7 @@ import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.Tab; +import org.zkoss.zul.Window; /** * Controller for {@link ResourceAllocation} view. @@ -100,18 +102,25 @@ public class ResourceAllocationController extends GenericForwardComposer { private Intbox assignedHoursComponent; - private Datebox taskStartDateBox; - private Grid calculationTypesGrid; private Radiogroup calculationTypeSelector; private Checkbox recommendedAllocationCheckbox; + private Checkbox extendedViewCheckbox; + private Datebox taskEndDate; private Decimalbox allResourcesPerDay; + private Label allOriginalHours; + private Label allTotalHours; + private Label allConsolidatedHours; + + private Label allTotalResourcesPerDay; + private Label allConsolidatedResourcesPerDay; + private Button applyButton; private NewAllocationSelector newAllocationSelector; @@ -120,6 +129,8 @@ public class ResourceAllocationController extends GenericForwardComposer { private Tab workerSearchTab; + private Window editTaskWindow; + public static void registerNeededScripts() { getScriptsRegister() .register(ScriptsRequiredByAdvancedAllocation.class); @@ -133,16 +144,29 @@ public class ResourceAllocationController extends GenericForwardComposer { @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); + taskEndDate = new Datebox(); allResourcesPerDay = new Decimalbox(); + allResourcesPerDay.setWidth("80px"); newAllocationSelector.setLimitingResourceFilter(false); + initAllocationLabels(); makeReadyInputsForCalculationTypes(); prepareCalculationTypesGrid(); } + private void initAllocationLabels() { + allOriginalHours = new Label(); + allTotalHours = new Label(); + allConsolidatedHours = new Label(); + + allTotalResourcesPerDay = new Label(); + allConsolidatedResourcesPerDay = new Label(); + } + private void makeReadyInputsForCalculationTypes() { - final String width = "300px"; + final String width = "90px"; taskEndDate.setWidth(width); assignedHoursComponent = new Intbox(); + assignedHoursComponent.setWidth("80px"); } private void prepareCalculationTypesGrid() { @@ -157,11 +181,27 @@ public class ResourceAllocationController extends GenericForwardComposer { @Override public Component cellFor(Integer column, CalculationTypeRadio data) { - return data.createRadio(); + return data.createComponent(getController(), + getCalculationTypeRadio()); } }; } + private Datebox getTaskEndDate() { + return taskEndDate; + } + + private CalculationTypeRadio getCalculationTypeRadio() { + if (formBinder != null) { + return CalculationTypeRadio.from(formBinder.getCalculatedValue()); + } + return null; + } + + public ResourceAllocationController getController() { + return this; + } + /** * Shows Resource Allocation window * @param task @@ -179,18 +219,27 @@ public class ResourceAllocationController extends GenericForwardComposer { context, planningState); formBinder = allocationRows.createFormBinder(planningState .getCurrentScenario(), resourceAllocationModel); + formBinder.setAllOriginalHours(allOriginalHours); + formBinder.setAllTotalHours(allTotalHours); + formBinder.setAllConsolidatedHours(allConsolidatedHours); formBinder.setAssignedHoursComponent(assignedHoursComponent); - formBinder.setTaskStartDateBox(taskStartDateBox); - formBinder.setEndDate(taskEndDate); + + formBinder.setAllTotalResourcesPerDay(allTotalResourcesPerDay); + formBinder + .setAllConsolidatedResourcesPerDay(allConsolidatedResourcesPerDay); formBinder.setAllResourcesPerDay(allResourcesPerDay); + + formBinder.setEndDate(taskEndDate); formBinder.setApplyButton(applyButton); formBinder.setAllocationsGrid(allocationsGrid); formBinder.setMessagesForUser(messagesForUser); formBinder.setWorkerSearchTab(workerSearchTab); formBinder.setCheckbox(recommendedAllocationCheckbox); + CalculationTypeRadio calculationTypeRadio = CalculationTypeRadio .from(formBinder.getCalculatedValue()); calculationTypeRadio.doTheSelectionOn(calculationTypeSelector); + tbResourceAllocation.setSelected(true); orderElementHoursGrid.setModel(new ListModelList( resourceAllocationModel.getHoursAggregatedByCriterions())); @@ -202,6 +251,10 @@ public class ResourceAllocationController extends GenericForwardComposer { } } + public Date getTaskStart() { + return resourceAllocationModel.getTaskStart(); + } + public enum HoursRendererColumn { @@ -224,8 +277,7 @@ public class ResourceAllocationController extends GenericForwardComposer { @Override public Component cell(HoursRendererColumn column, AggregatedHoursGroup data) { - Intbox result = new Intbox(data.getHours()); - result.setDisabled(true); + Label result = new Label(Integer.toString(data.getHours())); return result; } }; @@ -280,9 +332,38 @@ public class ResourceAllocationController extends GenericForwardComposer { } } + /** + * Shows the extended view of the resources allocations + */ + public void onCheckExtendedView() { + if (isExtendedView()) { + editTaskWindow.setWidth("970px"); + } else { + editTaskWindow.setWidth("870px"); + } + editTaskWindow.invalidate(); + Util.reloadBindings(allocationsGrid); + } + + public boolean isExtendedView() { + return extendedViewCheckbox.isChecked(); + } + + public int getColspanHours() { + if (isExtendedView()) { + return 4; + } + return 1; + } + + public int getColspanResources() { + if (isExtendedView()) { + return 3; + } + return 1; + } /** * Close search worker in worker search tab - * * @param e */ public void onCloseSelectWorkers() { @@ -346,21 +427,53 @@ public class ResourceAllocationController extends GenericForwardComposer { public abstract Component input( ResourceAllocationController resourceAllocationController); - public Radio createRadio() { + public Component createComponent( + ResourceAllocationController resourceAllocationController, + CalculationTypeRadio calculationTypeRadio) { + if (this.equals(END_DATE)) { + return createHbox(resourceAllocationController.taskEndDate, + calculationTypeRadio); + } else { + return createRadio(calculationTypeRadio); + } + } + + public Radio createRadio(CalculationTypeRadio calculationTypeRadio) { Radio result = new Radio(); result.setLabel(getName()); result.setValue(toString()); + result.setChecked(isSameCalculationTypeRadio(result, + calculationTypeRadio)); return result; } - public void doTheSelectionOn(Radiogroup radiogroup) { - for (int i = 0; i < radiogroup.getItemCount(); i++) { - Radio radio = radiogroup.getItemAtIndex(i); - if (name().equals(radio.getValue())) { - radiogroup.setSelectedIndex(i); - break; - } + public Hbox createHbox(Datebox datebox, + CalculationTypeRadio calculationTypeRadio) { + Hbox hbox = new Hbox(); + hbox.setSpacing("65px"); + Radio radio = createRadio(calculationTypeRadio); + + hbox.appendChild(radio); + hbox.appendChild(datebox); + return hbox; + } + + public boolean isSameCalculationTypeRadio(Radio radio, + CalculationTypeRadio calculationTypeRadio) { + if (calculationTypeRadio != null) { + return name().equals(calculationTypeRadio.name()); } + return false; + } + + public void doTheSelectionOn(final Radiogroup radiogroup) { + for (int i = 0; i < radiogroup.getItemCount(); i++) { + Radio radio = radiogroup.getItemAtIndex(i); + if (name().equals(radio.getValue())) { + radiogroup.setSelectedIndex(i); + break; + } + } } private final CalculatedValue calculatedValue; @@ -502,9 +615,16 @@ public class ResourceAllocationController extends GenericForwardComposer { throws Exception { row.setValue(data); append(row, data.createDetail()); - append(row, data.getSatisfiedCheckbox()); append(row, new Label(data.getName())); + append(row, new Label(Integer.toString(data.getOriginalHours()))); + append(row, new Label(Integer.toString(data.getTotalHours()))); + append(row, + new Label(Integer.toString(data.getConsolidatedHours()))); append(row, data.getHoursInput()); + append(row, new Label(data.getTotalResourcesPerDay().getAmount() + .toString())); + append(row, new Label(data.getConsolidatedResourcesPerDay() + .getAmount().toString())); append(row, data.getResourcesPerDayInput()); // On click delete button Button deleteButton = appendDeleteButton(row); @@ -516,16 +636,27 @@ public class ResourceAllocationController extends GenericForwardComposer { removeAllocation(data); } }); + + if (!data.isSatisfied()) { + row.setSclass("allocation-not-satisfied"); + } else { + row.setSclass("allocation-satisfied"); + } } private void renderAggregatingRow(Row row) { ResourceAllocationController controller = ResourceAllocationController.this; append(row, new Label()); - append(row, new Label()); append(row, new Label(_("Sum of all rows"))); - append(row, CalculationTypeRadio.NUMBER_OF_HOURS.input(controller)); + append(row, allOriginalHours); + append(row, allTotalHours); + append(row, allConsolidatedHours); + append(row, CalculationTypeRadio.NUMBER_OF_HOURS + .input(controller)); + append(row, allTotalResourcesPerDay); + append(row, allConsolidatedResourcesPerDay); append(row, CalculationTypeRadio.RESOURCES_PER_DAY - .input(controller)); + .input(controller)); append(row, new Label()); } @@ -567,7 +698,6 @@ public class ResourceAllocationController extends GenericForwardComposer { public void setStartDate(Date date) { resourceAllocationModel.setStartDate(date); - formBinder.setStartDate(date); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java index 987f6eadc..26e0bdaf7 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.Set; import org.hibernate.Hibernate; -import org.navalplanner.business.calendars.daos.IBaseCalendarDAO; import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.ProportionalDistributor; @@ -84,9 +83,6 @@ public class ResourceAllocationModel implements IResourceAllocationModel { private Task task; - @Autowired - private IBaseCalendarDAO calendarDAO; - @Autowired private ICriterionDAO criterionDAO; @@ -385,6 +381,14 @@ public class ResourceAllocationModel implements IResourceAllocationModel { return AggregatedHoursGroup.sum(task.getAggregatedByCriterions()); } + @Override + public Date getTaskStart() { + if (task == null) { + return null; + } + return task.getStartDate(); + } + @Override public void setStartDate(Date date) { if (task != null) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/SpecificAllocationRow.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/SpecificAllocationRow.java index b0c3e2813..63a9a0677 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/SpecificAllocationRow.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/SpecificAllocationRow.java @@ -85,8 +85,19 @@ public class SpecificAllocationRow extends AllocationRow { public static SpecificAllocationRow from(SpecificResourceAllocation specific) { SpecificAllocationRow result = forResource(specific.getResource()); - result.setResourcesPerDay(specific.getResourcesPerDay()); result.setOrigin(specific); + + result.setOriginalHours(specific.getOriginalTotalAssigment()); + result.setTotalHours(specific.getAssignedHours()); + result.setConsolidatedHours(specific.getConsolidatedHours()); + result.setNonConsolidatedHours(specific.getNonConsolidatedHours()); + + result.setNonConsolidatedResourcesPerDay(specific + .getNonConsolidatedResourcePerDay()); + result.setConsolidatedResourcesPerDay(specific + .getConsolidatedResourcePerDay()); + result.setTotalResourcesPerDay(specific.getResourcesPerDay()); + return result; } @@ -94,7 +105,7 @@ public class SpecificAllocationRow extends AllocationRow { SpecificAllocationRow result = new SpecificAllocationRow(); result.setName(resource.getShortDescription()); result.setResource(resource); - result.setResourcesPerDay(ResourcesPerDay.amount(1)); + result.setNonConsolidatedResourcesPerDay(ResourcesPerDay.amount(1)); return result; } @@ -103,7 +114,7 @@ public class SpecificAllocationRow extends AllocationRow { @Override public ResourcesPerDayModification toResourcesPerDayModification(Task task) { return ResourcesPerDayModification.create(createSpecific(task), - getResourcesPerDay()); + getNonConsolidatedResourcesPerDay()); } private SpecificResourceAllocation createSpecific(Task task) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/chart/ChartFiller.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/chart/ChartFiller.java index 12fd35d7f..37c398b56 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/chart/ChartFiller.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/chart/ChartFiller.java @@ -204,13 +204,19 @@ public abstract class ChartFiller implements IChartFiller { } private void fillZeroValueFromStart(PrintWriter writer) { - printLine(writer, start, BigDecimal.ZERO); - if (startIsPreviousToPreviousDayToFirstAssignment()) { - printLine(writer, previousDayToFirstAssignment(), - BigDecimal.ZERO); + if (!startIsDayOfFirstAssignment()) { + printLine(writer, start, BigDecimal.ZERO); + if (startIsPreviousToPreviousDayToFirstAssignment()) { + printLine(writer, previousDayToFirstAssignment(), + BigDecimal.ZERO); + } } } + private boolean startIsDayOfFirstAssignment() { + return !map.isEmpty() && start.compareTo(map.firstKey()) == 0; + } + private boolean startIsPreviousToPreviousDayToFirstAssignment() { return !map.isEmpty() && start.compareTo(previousDayToFirstAssignment()) < 0; @@ -221,10 +227,17 @@ public abstract class ChartFiller implements IChartFiller { } private void fillZeroValueToFinish(PrintWriter writer) { - if (finishIsPosteriorToNextDayToLastAssignment()) { - printLine(writer, nextDayToLastAssignment(), BigDecimal.ZERO); + if (!finishIsDayOfLastAssignment()) { + if (finishIsPosteriorToNextDayToLastAssignment()) { + printLine(writer, nextDayToLastAssignment(), + BigDecimal.ZERO); + } + printLine(writer, finish, BigDecimal.ZERO); } - printLine(writer, finish, BigDecimal.ZERO); + } + + private boolean finishIsDayOfLastAssignment() { + return !map.isEmpty() && start.compareTo(map.lastKey()) == 0; } private boolean finishIsPosteriorToNextDayToLastAssignment() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningController.java index 5332cf9da..30e6a5952 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningController.java @@ -23,12 +23,15 @@ package org.navalplanner.web.planner.company; import static org.navalplanner.web.I18nHelper._; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.Validate; +import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.orders.entities.OrderStatusEnum; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.web.common.components.bandboxsearch.BandboxMultipleSearch; import org.navalplanner.web.common.components.finders.FilterPair; @@ -124,12 +127,32 @@ public class CompanyPlanningController implements Composer{ } + private void setDefaultFilterValues() { + List list = model.getOrdersToShow(); + Date startDate = new Date(); + for(Order order : list) { + //get the init date of the first open project + //the list is ordered by init date + if(Arrays.asList(OrderStatusEnum.ACCEPTED, + OrderStatusEnum.OFFERED,OrderStatusEnum.STARTED, + OrderStatusEnum.SUBCONTRACTED_PENDING_ORDER) + .contains(order.getState())) { + startDate = order.getInitDate(); + break; + } + } + filterStartDate.setValue(startDate); + model.setFilterStartDate(startDate); + } + public void setConfigurationForPlanner() { // Added predicate model .setConfigurationToPlanner(planner, additional, doubleClickCommand, createPredicate()); model.setTabsController(tabsController); + setDefaultFilterValues(); + onApplyFilter(); planner.updateSelectedZoomLevel(); planner.invalidate(); } @@ -206,6 +229,9 @@ public class CompanyPlanningController implements Composer{ } private void filterByPredicate(IPredicate predicate) { + model.setFilterStartDate(filterStartDate.getValue()); + model.setFilterFinishDate(filterFinishDate.getValue()); + // Recalculate predicate model.setConfigurationToPlanner(planner, additional, doubleClickCommand, predicate); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java index 4a359be3b..3a3455d56 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java @@ -166,6 +166,11 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { private Scenario currentScenario; + private List ordersToShow; + + private Date filterStartDate; + private Date filterFinishDate; + public void setPlanningControllerEntryPoints( MultipleTabsPlannerController entryPoints) { this.tabs = entryPoints; @@ -320,7 +325,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { }); } - private void appendLoadChartAndLegend(Tabpanel loadChartPannel, + public static void appendLoadChartAndLegend(Tabpanel loadChartPannel, Timeplot loadChart) { Hbox hbox = new Hbox(); hbox.appendChild(getLoadChartLegend()); @@ -333,7 +338,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { loadChartPannel.appendChild(hbox); } - private org.zkoss.zk.ui.Component getLoadChartLegend() { + public static org.zkoss.zk.ui.Component getLoadChartLegend() { Hbox hbox = new Hbox(); hbox.setClass("legend-container"); hbox.setAlign("center"); @@ -599,7 +604,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { IPredicate predicate) { ITaskElementAdapter taskElementAdapter = getTaskElementAdapter(); List toShow; - toShow = sortByStartDate(retainOnlyTopLevel(predicate)); + toShow = retainOnlyTopLevel(predicate); forceLoadOfDataAssociatedTo(toShow); forceLoadOfDependenciesCollections(toShow); @@ -615,18 +620,18 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { } } - private List sortByStartDate(List list) { - List result = new ArrayList(list); - Collections.sort(result, new Comparator() { + private List sortByStartDate(List list) { + List result = new ArrayList(list); + Collections.sort(result, new Comparator() { @Override - public int compare(TaskElement o1, TaskElement o2) { - if (o1.getStartDate() == null) { + public int compare(Order o1, Order o2) { + if (o1.getInitDate() == null) { return -1; } - if (o2.getStartDate() == null) { + if (o2.getInitDate() == null) { return 1; } - return o1.getStartDate().compareTo(o2.getStartDate()); + return o1.getInitDate().compareTo(o2.getInitDate()); } }); return result; @@ -635,6 +640,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { private List retainOnlyTopLevel(IPredicate predicate) { List result = new ArrayList(); User user; + ordersToShow = new ArrayList(); try { user = userDAO.findByLoginName(SecurityUtils.getSessionUserLoginName()); @@ -648,7 +654,6 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { currentScenario = scenarioManager.getCurrent(); List list = orderDAO.getOrdersByReadAuthorizationByScenario( user, currentScenario); - for (Order order : list) { order.useSchedulingDataFor(currentScenario); TaskGroup associatedTaskElement = order.getAssociatedTaskElement(); @@ -656,6 +661,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { if (associatedTaskElement != null) { if (predicate == null || predicate.accepts(order)) { result.add(associatedTaskElement); + ordersToShow.add(order); } } } @@ -701,6 +707,41 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { // spring method injection protected abstract ITaskElementAdapter getTaskElementAdapter(); + @Override + public List getOrdersToShow() { + return ordersToShow; + } + + @Override + public void setFilterStartDate(Date filterStartDate) { + this.filterStartDate = filterStartDate; + } + + @Override + public Date getFilterStartDate() { + return filterStartDate; + } + + private LocalDate getFilterStartLocalDate() { + return filterStartDate != null ? + LocalDate.fromDateFields(filterStartDate) : null; + } + + @Override + public void setFilterFinishDate(Date filterFinishDate) { + this.filterFinishDate = filterFinishDate; + } + + @Override + public Date getFilterFinishDate() { + return filterFinishDate; + } + + private LocalDate getFilterFinishLocalDate() { + return filterFinishDate != null ? + LocalDate.fromDateFields(filterFinishDate) : null; + } + private class CompanyLoadChartFiller extends ChartFiller { @Override @@ -713,20 +754,21 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { resetMinimumAndMaximumValueForChart(); - Plotinfo plotInfoLoad = createPlotinfo(getLoad(interval.getStart(), - interval.getFinish()), interval); + Date start = filterStartDate!=null ? filterStartDate : interval.getStart(); + Date finish = filterFinishDate != null ? filterFinishDate + : interval.getFinish(); + + Plotinfo plotInfoLoad = createPlotinfo(getLoad(start, finish), interval); plotInfoLoad.setFillColor(COLOR_ASSIGNED_LOAD_GLOBAL); plotInfoLoad.setLineWidth(0); Plotinfo plotInfoMax = createPlotinfo( - getCalendarMaximumAvailability(interval.getStart(), - interval.getFinish()), interval); + getCalendarMaximumAvailability(start, finish), interval); plotInfoMax.setLineColor(COLOR_CAPABILITY_LINE); plotInfoMax.setFillColor("#FFFFFF"); plotInfoMax.setLineWidth(2); - Plotinfo plotInfoOverload = createPlotinfo(getOverload(interval - .getStart(), interval.getFinish()), interval); + Plotinfo plotInfoOverload = createPlotinfo(getOverload(start, finish), interval); plotInfoOverload.setFillColor(COLOR_OVERLOAD_GLOBAL); plotInfoOverload.setLineWidth(0); @@ -742,8 +784,9 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { } private SortedMap getLoad(Date start, Date finish) { - List dayAssignments = dayAssignmentDAO.getAllFor(currentScenario); - + List dayAssignments = dayAssignmentDAO.getAllFor( + currentScenario, LocalDate.fromDateFields(start), LocalDate + .fromDateFields(finish)); SortedMap> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments); SortedMap mapDayAssignments = calculateHoursAdditionByDayWithoutOverload(dayAssignmentGrouped); @@ -752,7 +795,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { private SortedMap getOverload(Date start, Date finish) { - List dayAssignments = dayAssignmentDAO.getAllFor(currentScenario); + List dayAssignments = dayAssignmentDAO.getAllFor(currentScenario, LocalDate.fromDateFields(start), LocalDate.fromDateFields(finish)); SortedMap> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments); SortedMap mapDayAssignments = calculateHoursAdditionByDayJustOverload(dayAssignmentGrouped); @@ -879,14 +922,15 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { private class CompanyEarnedValueChartFiller extends EarnedValueChartFiller { protected void calculateBudgetedCostWorkScheduled(Interval interval) { - List list = taskElementDAO.list(TaskElement.class); + List list = taskElementDAO.listFilteredByDate(filterStartDate, filterFinishDate); SortedMap estimatedCost = new TreeMap(); for (TaskElement taskElement : list) { if (taskElement instanceof Task) { addCost(estimatedCost, hoursCostCalculator - .getEstimatedCost((Task) taskElement)); + .getEstimatedCost((Task) taskElement, + getFilterStartLocalDate(), getFilterFinishLocalDate())); } } @@ -909,7 +953,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { SortedMap result = new TreeMap(); List workReportLines = workReportLineDAO - .list(WorkReportLine.class); + .findFilteredByDate(filterStartDate, filterFinishDate); if (workReportLines.isEmpty()) { return result; @@ -929,14 +973,15 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { } protected void calculateBudgetedCostWorkPerformed(Interval interval) { - List list = taskElementDAO.list(TaskElement.class); + List list = taskElementDAO.listFilteredByDate(filterStartDate, filterFinishDate); SortedMap advanceCost = new TreeMap(); for (TaskElement taskElement : list) { if (taskElement instanceof Task) { addCost(advanceCost, hoursCostCalculator - .getAdvanceCost((Task) taskElement)); + .getAdvanceCost((Task) taskElement, + getFilterStartLocalDate(), getFilterFinishLocalDate())); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/ICompanyPlanningModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/ICompanyPlanningModel.java index 99df6ddd1..cf6302691 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/ICompanyPlanningModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/ICompanyPlanningModel.java @@ -21,7 +21,10 @@ package org.navalplanner.web.planner.company; import java.util.Collection; +import java.util.Date; +import java.util.List; +import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.web.planner.tabs.MultipleTabsPlannerController; import org.zkoss.ganttz.IPredicate; @@ -48,4 +51,14 @@ public interface ICompanyPlanningModel { public void setTabsController(MultipleTabsPlannerController tabsController); + List getOrdersToShow(); + + void setFilterStartDate(Date filterStartDate); + + Date getFilterStartDate(); + + void setFilterFinishDate(Date filterFinishDate); + + Date getFilterFinishDate(); + } 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 4e41dd570..0aab05129 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 @@ -81,7 +81,6 @@ import org.navalplanner.business.users.entities.UserRole; import org.navalplanner.web.calendars.BaseCalendarModel; import org.navalplanner.web.common.ViewSwitcher; import org.navalplanner.web.planner.ITaskElementAdapter; -import org.navalplanner.web.planner.ITaskElementAdapter.IOnMoveListener; import org.navalplanner.web.planner.allocation.IResourceAllocationCommand; import org.navalplanner.web.planner.calendar.CalendarAllocationController; import org.navalplanner.web.planner.calendar.ICalendarAllocationCommand; @@ -115,6 +114,7 @@ import org.zkoss.ganttz.adapters.IStructureNavigator; import org.zkoss.ganttz.adapters.PlannerConfiguration; import org.zkoss.ganttz.adapters.PlannerConfiguration.IPrintAction; import org.zkoss.ganttz.adapters.PlannerConfiguration.IReloadChartListener; +import org.zkoss.ganttz.data.GanttDiagramGraph.IGraphChangeListener; import org.zkoss.ganttz.extensions.ICommand; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.zoom.DetailItem; @@ -721,10 +721,13 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { if(saveCommand != null) { saveCommand.addListener(fillChartOnSave(loadChart, planner)); } - taskElementAdapter.addListener(readOnlyProxy(transactionService, - IOnMoveListener.class, new IOnMoveListener() { + configuration.addPostGraphChangeListener(readOnlyProxy( + transactionService, IGraphChangeListener.class, new IGraphChangeListener() { @Override - public void moved(TaskElement taskElement) { + public void execute() { + if (isExecutingOutsideZKExecution()) { + return; + } if (planner.isVisibleChart()) { loadChart.fillChart(); } @@ -741,6 +744,10 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { })); } + private boolean isExecutingOutsideZKExecution() { + return Executions.getCurrent() == null; + } + private void addAdditional(List> additional, PlannerConfiguration configuration) { for (ICommand c : additional) { @@ -932,7 +939,7 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { private PlannerConfiguration createConfiguration( Order orderReloaded) { taskElementAdapter = getTaskElementAdapter(); - taskElementAdapter.initialize(orderReloaded, currentScenario); + taskElementAdapter.useScenario(currentScenario); planningState = createPlanningStateFor(orderReloaded); PlannerConfiguration result = new PlannerConfiguration( taskElementAdapter, @@ -1004,6 +1011,7 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { if (each.getCalendar() != null) { BaseCalendarModel.forceLoadBaseCalendar(each.getCalendar()); } + each.hasConsolidations(); } private static void switchAllocationsToScenario(Scenario scenario, diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java index 3678eedce..09e9006fc 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java @@ -35,15 +35,19 @@ import org.apache.commons.logging.LogFactory; import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.orders.daos.IOrderDAO; import org.navalplanner.business.planner.daos.IConsolidationDAO; +import org.navalplanner.business.planner.daos.ILimitingResourceQueueDependencyDAO; import org.navalplanner.business.planner.daos.ISubcontractedTaskDataDAO; import org.navalplanner.business.planner.daos.ITaskElementDAO; import org.navalplanner.business.planner.daos.ITaskSourceDAO; 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.DerivedDayAssignment; import org.navalplanner.business.planner.entities.DerivedDayAssignmentsContainer; +import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.Task; @@ -69,7 +73,13 @@ import org.zkoss.zul.Messagebox; /** * A command that saves the changes in the taskElements. * It can be considered the final step in the conversation
    + * + * 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 + * @author Javier Moran Rua */ @OnConcurrentModification(goToPage = "/planner/index.zul;company_scheduling") public class SaveCommand implements ISaveCommand { @@ -88,6 +98,9 @@ public class SaveCommand implements ISaveCommand { @Autowired private ISubcontractedTaskDataDAO subcontractedTaskDataDAO; + @Autowired + private ILimitingResourceQueueDependencyDAO limitingResourceQueueDependencyDAO; + private PlanningState state; @Autowired @@ -169,6 +182,8 @@ public class SaveCommand implements ISaveCommand { && taskElement.getTaskSource().isNewObject()) { saveTaskSources(taskElement); } + // Recursive iteration to put all the tasks of the + // gantt as transiet dontPoseAsTransient(taskElement); } if (!state.getTasksToSave().isEmpty()) { @@ -234,6 +249,8 @@ public class SaveCommand implements ISaveCommand { if (taskElement.isNewObject()) { taskElement.dontPoseAsTransientObjectAnymore(); } + dontPoseAsTransient(taskElement.getDependenciesWithThisOrigin()); + dontPoseAsTransient(taskElement.getDependenciesWithThisDestination()); Set> resourceAllocations = taskElement.getSatisfiedResourceAllocations(); dontPoseAsTransient(resourceAllocations); if (!taskElement.isLeaf()) { @@ -242,10 +259,85 @@ public class SaveCommand implements ISaveCommand { } } if (taskElement instanceof Task) { + updateLimitingQueueDependencies((Task) taskElement); dontPoseAsTransient(((Task) taskElement).getConsolidation()); } } + private void dontPoseAsTransient( + Collection dependencies) { + for (Dependency each : dependencies) { + each.dontPoseAsTransientObjectAnymore(); + } + } + + private void updateLimitingQueueDependencies(Task t) { + + for (Dependency each: t.getDependenciesWithThisOrigin()) { + addLimitingDependencyIfNeeded(each); + removeLimitingDependencyIfNeeded(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, + LimitingResourceQueueDependency. + convertFromTypeToQueueDepedencyType(d.getType())); + d.setQueueDependency(queueDependency); + limitingResourceQueueDependencyDAO.save(queueDependency); + queueDependency.dontPoseAsTransientObjectAnymore(); + } + } + + private LimitingResourceQueueElement + calculateQueueElementFromDependency(Task t) { + + LimitingResourceQueueElement result = null; + // TODO: Improve this method: One Task can only have one + // limiting resource allocation + Set> 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 dontPoseAsTransient(Consolidation consolidation) { if (consolidation != null) { consolidation.dontPoseAsTransientObjectAnymore(); @@ -292,6 +384,12 @@ public class SaveCommand implements ISaveCommand { private void dontPoseAsTransient(LimitingResourceQueueElement element) { if (element != null) { + for (LimitingResourceQueueDependency d: element.getDependenciesAsOrigin()) { + d.dontPoseAsTransientObjectAnymore(); + } + for (LimitingResourceQueueDependency d: element.getDependenciesAsDestiny()) { + d.dontPoseAsTransientObjectAnymore(); + } element.dontPoseAsTransientObjectAnymore(); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/reassign/ReassignCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/reassign/ReassignCommand.java index 334154f4a..2da7f3e20 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/reassign/ReassignCommand.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/reassign/ReassignCommand.java @@ -27,6 +27,11 @@ import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; import org.apache.commons.lang.Validate; import org.navalplanner.business.common.IAdHocTransactionService; @@ -45,8 +50,11 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.zkoss.ganttz.adapters.IDomainAndBeansMapper; +import org.zkoss.ganttz.data.GanttDiagramGraph; import org.zkoss.ganttz.data.Task; +import org.zkoss.ganttz.data.GanttDiagramGraph.DeferedNotifier; import org.zkoss.ganttz.extensions.IContext; +import org.zkoss.ganttz.util.IAction; import org.zkoss.ganttz.util.LongOperationFeedback; import org.zkoss.ganttz.util.LongOperationFeedback.IBackGroundOperation; import org.zkoss.ganttz.util.LongOperationFeedback.IDesktopUpdate; @@ -99,6 +107,71 @@ public class ReassignCommand implements IReassignCommand { }); } + private class NotBlockingDesktopUpdates implements + IDesktopUpdatesEmitter, Runnable { + private BlockingQueue queue = new LinkedBlockingQueue(); + private final IDesktopUpdatesEmitter original; + + private final IDesktopUpdate END_MARK = new IDesktopUpdate() { + + @Override + public void doUpdate() { + } + }; + + NotBlockingDesktopUpdates( + IDesktopUpdatesEmitter original) { + this.original = original; + } + + @Override + public void doUpdate(IDesktopUpdate value) { + queue.add(value); + } + + void finish() { + queue.add(END_MARK); + } + + @Override + public void run() { + List batch = new ArrayList(); + while (true) { + batch.clear(); + IDesktopUpdate current = null; + try { + current = queue.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (current == END_MARK) { + return; + } + batch.add(current); + while ((current = queue.poll()) != null) { + if (current == END_MARK) { + break; + } + batch.add(current); + } + if (!batch.isEmpty()) { + original + .doUpdate(asOneUpdate(batch)); + } + if (current == END_MARK) { + return; + } + } + } + + private IDesktopUpdate asOneUpdate(List batch) { + return and(batch.toArray(new IDesktopUpdate[0])); + } + + } + + private ExecutorService executorService = Executors.newCachedThreadPool(); + private IBackGroundOperation reassignations( final IContext context, final List reassignations) { @@ -106,41 +179,65 @@ public class ReassignCommand implements IReassignCommand { @Override public void doOperation( - IDesktopUpdatesEmitter updater) { + final IDesktopUpdatesEmitter updater) { updater.doUpdate(showStart(reassignations.size())); + DeferedNotifier notifications = null; + NotBlockingDesktopUpdates notBlockingDesktopUpdates = new NotBlockingDesktopUpdates( + updater); + Future previousNotifications = executorService + .submit(notBlockingDesktopUpdates); try { - doReassignations(reassignations, updater); + GanttDiagramGraph ganttDiagramGraph = context.getGanttDiagramGraph(); + notifications = ganttDiagramGraph + .manualNotificationOn(doReassignations( + ganttDiagramGraph, reassignations, + notBlockingDesktopUpdates)); } finally { - updater.doUpdate(and(reloadCharts(context), showEnd())); + notBlockingDesktopUpdates.finish(); + if (notifications != null) { + // null if error + waitUntilFinish(previousNotifications); + updater.doUpdate(and(doNotifications(notifications), + reloadCharts(context), showEnd())); + } else { + updater.doUpdate(showEnd()); + } + } + } + + private void waitUntilFinish(Future showingProgress){ + try { + showingProgress.get(); + } catch (Exception e) { + throw new RuntimeException(e); } } }; } - private void doReassignations( + private IAction doReassignations(final GanttDiagramGraph diagramGraph, final List reassignations, - IDesktopUpdatesEmitter updater) { - int i = 1; - final int total = reassignations.size(); - for (final WithAssociatedEntity each : reassignations) { - IDesktopUpdate notifyChanges = changesNotificatorFor(each.ganntTask); - transactionService - .runOnReadOnlyTransaction(reassignmentTransaction(each)); - updater.doUpdate(notifyChanges); - updater.doUpdate(showCompleted(i, total)); - updater.doUpdate(and(notifyChanges, showCompleted(i, total))); - i++; - } - } + final IDesktopUpdatesEmitter updater) { + return new IAction() { - private IDesktopUpdate changesNotificatorFor(final Task ganttTask) { - final Date previousBeginDate = ganttTask.getBeginDate(); - final long previousLength = ganttTask.getLengthMilliseconds(); - return new IDesktopUpdate() { @Override - public void doUpdate() { - ganttTask.fireChangesForPreviousValues(previousBeginDate, - previousLength); + public void doAction() { + int i = 1; + final int total = reassignations.size(); + for (final WithAssociatedEntity each : reassignations) { + Task ganttTask = each.ganntTask; + final Date previousBeginDate = ganttTask.getBeginDate(); + final long previousLength = ganttTask + .getLengthMilliseconds(); + + transactionService + .runOnReadOnlyTransaction(reassignmentTransaction(each)); + diagramGraph.enforceRestrictions(each.ganntTask); + ganttTask.fireChangesForPreviousValues(previousBeginDate, + previousLength); + updater.doUpdate(showCompleted(i, total)); + i++; + } } }; } @@ -173,6 +270,15 @@ public class ReassignCommand implements IReassignCommand { }; } + private IDesktopUpdate doNotifications(final DeferedNotifier notifier) { + return new IDesktopUpdate() { + @Override + public void doUpdate() { + notifier.doNotifications(); + } + }; + } + private IDesktopUpdate showEnd() { return new IDesktopUpdate() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java index 0541db042..f312233a5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java @@ -47,6 +47,7 @@ import org.navalplanner.web.planner.taskedition.TaskPropertiesController.Resourc import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; +import org.zkoss.ganttz.TaskComponent; import org.zkoss.ganttz.extensions.IContextWithPlannerTask; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.util.GenericForwardComposer; @@ -304,6 +305,10 @@ public class EditTaskController extends GenericForwardComposer { if (context != null) { context.getTask().reloadResourcesText(); context.reloadCharts(); + + if (context.getRelativeTo() instanceof TaskComponent) { + ((TaskComponent) context.getRelativeTo()).invalidate(); + } } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java index cce341ac8..021194e49 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/TaskPropertiesController.java @@ -184,6 +184,12 @@ public class TaskPropertiesController extends GenericForwardComposer { setOldState(null); originalState = getResourceAllocationType(currentTaskElement); + final boolean disabled = currentTaskElement.isSubcontracted() + || currentTaskElement.isLimitingAndHasDayAssignments(); + startConstraintTypes.setDisabled(disabled); + startConstraintDate.setDisabled(disabled); + lbResourceAllocationType.setDisabled(disabled); + taskEditFormComposer.init(context.getRelativeTo(), context.getTask()); updateComponentValuesForTask(); } @@ -335,6 +341,7 @@ public class TaskPropertiesController extends GenericForwardComposer { } }); + } private boolean thereIsTransition(ResourceAllocationTypeEnum newState) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/print/CutyPrint.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/print/CutyPrint.java index 911635432..d649b9ed9 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/print/CutyPrint.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/print/CutyPrint.java @@ -54,7 +54,7 @@ public class CutyPrint { private static final String CUTYCAPT_COMMAND = "/usr/bin/CutyCapt "; // Estimated maximum execution time (ms) - private static final int CUTYCAPT_TIMEOUT = 20000; + private static final int CUTYCAPT_TIMEOUT = 60000; // Taskdetails left padding private static int TASKDETAILS_WIDTH = 310; diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java index 1c29e86b7..f28dc2d87 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java @@ -20,9 +20,11 @@ package org.navalplanner.web.resourceload; +import java.util.Date; import java.util.List; import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.Resource; @@ -54,4 +56,18 @@ public interface IResourceLoadModel { void clearCriteriaToShow(); + void setInitDateFilter(Date value); + + void setEndDateFilter(Date value); + + Date getInitDateFilter(); + + Date getEndDateFilter(); + + List getDayAssignments(); + + List getResources(); + + boolean isExpandResourceLoadViewCharts(); + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java index 5571ae369..7d1ffe741 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java @@ -201,7 +201,16 @@ abstract class LoadPeriodGenerator { && other.end.compareTo(end) <= 0; } + /** + * @return null if the data is invalid + */ public LoadPeriod build() { + if(start.isAfter(end)){ + LOG + .warn("the start date is after end date. Inconsistent state for " + + allocationsOnInterval + ". LoadPeriod ignored"); + return null; + } return new LoadPeriod(start, end, getTotalWorkHours(), getHoursAssigned(), new LoadLevel( calculateLoadPercentage())); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java index 221a00d73..ee33563f0 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java @@ -22,17 +22,32 @@ package org.navalplanner.web.resourceload; import static org.navalplanner.web.I18nHelper._; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.Map.Entry; import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; +import org.navalplanner.business.calendars.entities.BaseCalendar; +import org.navalplanner.business.calendars.entities.SameWorkHoursEveryDay; import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.web.common.components.bandboxsearch.BandboxMultipleSearch; import org.navalplanner.web.common.components.finders.FilterPair; +import org.navalplanner.web.planner.chart.Chart; +import org.navalplanner.web.planner.chart.ChartFiller; +import org.navalplanner.web.planner.company.CompanyPlanningModel; import org.navalplanner.web.planner.order.BankHolidaysMarker; import org.navalplanner.web.planner.order.IOrderPlanningGate; import org.navalplanner.web.security.SecurityUtils; @@ -40,21 +55,36 @@ 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.zkforge.timeplot.Plotinfo; +import org.zkforge.timeplot.Timeplot; +import org.zkforge.timeplot.geometry.TimeGeometry; +import org.zkforge.timeplot.geometry.ValueGeometry; +import org.zkoss.ganttz.IChartVisibilityChangedListener; import org.zkoss.ganttz.data.resourceload.LoadTimeLine; import org.zkoss.ganttz.resourceload.IFilterChangedListener; import org.zkoss.ganttz.resourceload.ISeeScheduledOfListener; import org.zkoss.ganttz.resourceload.ResourcesLoadPanel; import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.IToolbarCommand; import org.zkoss.ganttz.timetracker.TimeTracker; +import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; import org.zkoss.ganttz.timetracker.zoom.SeveralModificators; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.Interval; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.util.Clients; import org.zkoss.zk.ui.util.Composer; import org.zkoss.zul.Button; +import org.zkoss.zul.Datebox; import org.zkoss.zul.Hbox; +import org.zkoss.zul.Label; import org.zkoss.zul.Messagebox; +import org.zkoss.zul.Tab; +import org.zkoss.zul.Tabbox; +import org.zkoss.zul.Tabpanel; +import org.zkoss.zul.Tabpanels; +import org.zkoss.zul.Tabs; /** * Controller for global resourceload view @@ -88,6 +118,14 @@ public class ResourceLoadController implements Composer { private boolean currentFilterByResources = true; private boolean filterHasChanged = false; + private ZoomLevel zoomLevel; + + private List keepAliveZoomListeners = new ArrayList(); + + private List keepAliveChartVisibilityListeners = new ArrayList(); + + private Chart loadChart; + public ResourceLoadController() { } @@ -99,7 +137,6 @@ public class ResourceLoadController implements Composer { @Override public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { this.parent = comp; - reload(); } public void reload() { @@ -111,33 +148,29 @@ public class ResourceLoadController implements Composer { } private void reload(boolean filterByResources) { - try { - this.filterHasChanged = (filterByResources != currentFilterByResources); - this.currentFilterByResources = filterByResources; + this.filterHasChanged = (filterByResources != currentFilterByResources); + this.currentFilterByResources = filterByResources; - if (filterBy == null) { - resourceLoadModel.initGlobalView(filterByResources); - } else { - resourceLoadModel.initGlobalView(filterBy, filterByResources); + if (filterBy == null) { + if (resourcesLoadPanel == null) { + resetAdditionalFilters(); } - timeTracker = buildTimeTracker(); - buildResourcesLoadPanel(); - - this.parent.getChildren().clear(); - this.parent.appendChild(resourcesLoadPanel); - - resourcesLoadPanel.afterCompose(); - addCommands(resourcesLoadPanel); - } catch (IllegalArgumentException e) { - try { - Messagebox - .show( - _("Some lines have not allocation periods.\nBelow it shows the load all company resources"), - _("Error"), Messagebox.OK, Messagebox.ERROR); - } catch (InterruptedException o) { - throw new RuntimeException(e); + resourceLoadModel.initGlobalView(filterByResources); + } else { + if (resourcesLoadPanel == null) { + deleteAdditionalFilters(); } + resourceLoadModel.initGlobalView(filterBy, filterByResources); } + timeTracker = buildTimeTracker(); + buildResourcesLoadPanel(); + + this.parent.getChildren().clear(); + this.parent.appendChild(resourcesLoadPanel); + + resourcesLoadPanel.afterCompose(); + addSchedulingScreenListeners(); + addCommands(resourcesLoadPanel); } private void addListeners() { @@ -150,7 +183,19 @@ public class ResourceLoadController implements Composer { } }; resourcesLoadPanel.addFilterListener(filterChangedListener); + addNameFilterListener(); + } + private void addNameFilterListener() { + resourcesLoadPanel.addNameFilterListener(new IFilterChangedListener() { + @Override + public void filterChanged(boolean filter) { + addSchedulingScreenListeners(); + } + }); + } + + private void addSchedulingScreenListeners() { /* Listener to show the scheduling screen */ seeScheduledOfListener = new ISeeScheduledOfListener() { @@ -171,7 +216,7 @@ public class ResourceLoadController implements Composer { } private TimeTracker buildTimeTracker() { - ZoomLevel zoomLevel = (timeTracker == null) ? resourceLoadModel + zoomLevel = (timeTracker == null) ? resourceLoadModel .calculateInitialZoomLevel() : timeTracker.getDetailLevel(); return new TimeTracker(resourceLoadModel.getViewInterval(), zoomLevel, SeveralModificators.create(), SeveralModificators @@ -202,20 +247,26 @@ public class ResourceLoadController implements Composer { } resourcesLoadPanel.init(resourceLoadModel.getLoadTimeLines(), timeTracker); + if(filterHasChanged) { + addNameFilterListener(); + } } else { resourcesLoadPanel = new ResourcesLoadPanel(resourceLoadModel - .getLoadTimeLines(), timeTracker, parent); + .getLoadTimeLines(), timeTracker, parent, resourceLoadModel + .isExpandResourceLoadViewCharts()); if(filterBy == null) { addWorkersBandbox(); + addTimeFilter(); } addListeners(); } + resourcesLoadPanel.setLoadChart(buildChart()); } private void addWorkersBandbox() { bandBox = new BandboxMultipleSearch(); bandBox.setId("workerBandboxMultipleSearch"); - bandBox.setWidthBandbox("285px"); + bandBox.setWidthBandbox("185px"); bandBox.setWidthListbox("300px"); bandBox.setFinder("workerMultipleFiltersFinder"); bandBox.afterCompose(); @@ -240,7 +291,58 @@ public class ResourceLoadController implements Composer { hbox.appendChild(button); hbox.setAlign("center"); - resourcesLoadPanel.setVariable("additionalFilter", hbox, true); + resourcesLoadPanel.setVariable("additionalFilter2", hbox, true); + } + + private void addTimeFilter() { + Label label1 = new Label(_("Time filter") + ":"); + Label label2 = new Label("-"); + final Datebox initDate = new Datebox(); + initDate.setValue(resourceLoadModel.getInitDateFilter()); + initDate.setWidth("75px"); + initDate.addEventListener(Events.ON_CHANGE, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + resourceLoadModel.setInitDateFilter(initDate.getValue()); + reload(currentFilterByResources); + } + }); + final Datebox endDate = new Datebox(); + endDate.setValue(resourceLoadModel.getEndDateFilter()); + endDate.setWidth("75px"); + endDate.addEventListener(Events.ON_CHANGE, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + resourceLoadModel.setEndDateFilter(endDate.getValue()); + reload(currentFilterByResources); + } + }); + Hbox hbox = new Hbox(); + hbox.appendChild(label1); + hbox.appendChild(initDate); + hbox.appendChild(label2); + hbox.appendChild(endDate); + hbox.setAlign("center"); + + resourcesLoadPanel.setVariable("additionalFilter1", hbox, true); + } + + private void resetAdditionalFilters() { + Date initDateValue = new Date(); + initDateValue.setDate(initDateValue.getDate() -15); + resourceLoadModel.setInitDateFilter(initDateValue); + resourceLoadModel.setEndDateFilter(null); + + resourceLoadModel.setCriteriaToShow(new ArrayList()); + resourceLoadModel.setResourcesToShow(new ArrayList()); + } + + private void deleteAdditionalFilters() { + resourceLoadModel.setInitDateFilter(null); + resourceLoadModel.setEndDateFilter(null); + + resourceLoadModel.setCriteriaToShow(new ArrayList()); + resourceLoadModel.setResourcesToShow(new ArrayList()); } @SuppressWarnings("unchecked") @@ -309,4 +411,261 @@ public class ResourceLoadController implements Composer { } } + + private org.zkoss.zk.ui.Component buildChart() { + Tabbox chartComponent = new Tabbox(); + chartComponent.setOrient("vertical"); + chartComponent.setHeight("200px"); + + Tabs chartTabs = new Tabs(); + chartTabs.appendChild(new Tab(_("Load"))); + chartComponent.appendChild(chartTabs); + chartTabs.setWidth("124px"); + + Tabpanels chartTabpanels = new Tabpanels(); + Tabpanel loadChartPannel = new Tabpanel(); + CompanyPlanningModel.appendLoadChartAndLegend(loadChartPannel, + buildLoadChart()); + chartTabpanels.appendChild(loadChartPannel); + chartComponent.appendChild(chartTabpanels); + + return chartComponent; + } + + private Timeplot buildLoadChart() { + Timeplot chartLoadTimeplot = createEmptyTimeplot(); + + loadChart = new Chart(chartLoadTimeplot, + new ResourceLoadChartFiller(), timeTracker); + loadChart.setZoomLevel(zoomLevel); + if (resourcesLoadPanel.isVisibleChart()) { + loadChart.fillChart(); + } + timeTracker.addZoomListener(fillOnZoomChange(loadChart)); + resourcesLoadPanel + .addChartVisibilityListener(fillOnChartVisibilityChange(loadChart)); + + return chartLoadTimeplot; + } + + private IZoomLevelChangedListener fillOnZoomChange(final Chart loadChart) { + + IZoomLevelChangedListener zoomListener = new IZoomLevelChangedListener() { + + @Override + public void zoomLevelChanged(ZoomLevel detailLevel) { + loadChart.setZoomLevel(detailLevel); + + if (resourcesLoadPanel.isVisibleChart()) { + loadChart.fillChart(); + } + } + }; + + keepAliveZoomListeners.add(zoomListener); + + return zoomListener; + } + + private IChartVisibilityChangedListener fillOnChartVisibilityChange( + final Chart loadChart) { + IChartVisibilityChangedListener chartVisibilityChangedListener = new IChartVisibilityChangedListener() { + + @Override + public void chartVisibilityChanged(final boolean visible) { + if (visible) { + loadChart.fillChart(); + } + } + }; + + keepAliveChartVisibilityListeners.add(chartVisibilityChangedListener); + return chartVisibilityChangedListener; + } + + private Timeplot createEmptyTimeplot() { + Timeplot timeplot = new Timeplot(); + timeplot.appendChild(new Plotinfo()); + return timeplot; + } + + private class ResourceLoadChartFiller extends ChartFiller { + + @Override + public void fillChart(Timeplot chart, Interval interval, Integer size) { + chart.getChildren().clear(); + chart.invalidate(); + + String javascript = "zkTasklist.timeplotcontainer_rescroll();"; + Clients.evalJavaScript(javascript); + + resetMinimumAndMaximumValueForChart(); + + Plotinfo plotInfoLoad = createPlotinfo(getLoad(interval.getStart(), + interval.getFinish()), interval); + plotInfoLoad + .setFillColor(CompanyPlanningModel.COLOR_ASSIGNED_LOAD_GLOBAL); + plotInfoLoad.setLineWidth(0); + + Plotinfo plotInfoMax = createPlotinfo( + getCalendarMaximumAvailability(interval.getStart(), + interval.getFinish()), interval); + plotInfoMax + .setLineColor(CompanyPlanningModel.COLOR_CAPABILITY_LINE); + plotInfoMax.setFillColor("#FFFFFF"); + plotInfoMax.setLineWidth(2); + + Plotinfo plotInfoOverload = createPlotinfo(getOverload(interval + .getStart(), interval.getFinish()), interval); + plotInfoOverload + .setFillColor(CompanyPlanningModel.COLOR_OVERLOAD_GLOBAL); + plotInfoOverload.setLineWidth(0); + + ValueGeometry valueGeometry = getValueGeometry(); + TimeGeometry timeGeometry = getTimeGeometry(interval); + + appendPlotinfo(chart, plotInfoLoad, valueGeometry, timeGeometry); + appendPlotinfo(chart, plotInfoMax, valueGeometry, timeGeometry); + appendPlotinfo(chart, plotInfoOverload, valueGeometry, timeGeometry); + + chart.setWidth(size + "px"); + chart.setHeight("150px"); + } + + private SortedMap getLoad(Date start, Date finish) { + List dayAssignments = resourceLoadModel + .getDayAssignments(); + + SortedMap> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments); + SortedMap mapDayAssignments = calculateHoursAdditionByDayWithoutOverload(dayAssignmentGrouped); + + return mapDayAssignments; + } + + private SortedMap getOverload(Date start, + Date finish) { + List dayAssignments = resourceLoadModel + .getDayAssignments(); + + SortedMap> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments); + SortedMap mapDayAssignments = calculateHoursAdditionByDayJustOverload(dayAssignmentGrouped); + SortedMap mapMaxAvailability = calculateHoursAdditionByDay( + resourceLoadModel.getResources(), start, finish); + + for (LocalDate day : mapDayAssignments.keySet()) { + if ((day.compareTo(new LocalDate(start)) >= 0) + && (day.compareTo(new LocalDate(finish)) <= 0)) { + BigDecimal overloadHours = mapDayAssignments.get(day); + BigDecimal maxHours = mapMaxAvailability.get(day); + mapDayAssignments.put(day, overloadHours.add(maxHours)); + } + } + + return mapDayAssignments; + } + + private SortedMap calculateHoursAdditionByDayWithoutOverload( + SortedMap> dayAssignmentGrouped) { + SortedMap map = new TreeMap(); + + for (LocalDate day : dayAssignmentGrouped.keySet()) { + int result = 0; + + for (Resource resource : dayAssignmentGrouped.get(day).keySet()) { + BaseCalendar calendar = resource.getCalendar(); + + int workableHours = SameWorkHoursEveryDay + .getDefaultWorkingDay().getCapacityAt(day); + if (calendar != null) { + workableHours = calendar.getCapacityAt(day); + } + + int assignedHours = dayAssignmentGrouped.get(day).get( + resource); + + if (assignedHours <= workableHours) { + result += assignedHours; + } else { + result += workableHours; + } + } + + map.put(day, result); + } + + return convertAsNeededByZoom(convertToBigDecimal(map)); + } + + private SortedMap calculateHoursAdditionByDayJustOverload( + SortedMap> dayAssignmentGrouped) { + SortedMap map = new TreeMap(); + + for (LocalDate day : dayAssignmentGrouped.keySet()) { + int result = 0; + + for (Resource resource : dayAssignmentGrouped.get(day).keySet()) { + BaseCalendar calendar = resource.getCalendar(); + + int workableHours = SameWorkHoursEveryDay + .getDefaultWorkingDay().getCapacityAt(day); + if (calendar != null) { + workableHours = calendar.getCapacityAt(day); + } + + int assignedHours = dayAssignmentGrouped.get(day).get( + resource); + + if (assignedHours > workableHours) { + result += assignedHours - workableHours; + } + } + + map.put(day, result); + } + + return convertAsNeededByZoom(convertToBigDecimal(map)); + } + + private SortedMap getCalendarMaximumAvailability( + Date start, Date finish) { + SortedMap mapDayAssignments = calculateHoursAdditionByDay( + resourceLoadModel.getResources(), start, finish); + + return mapDayAssignments; + } + + private SortedMap calculateHoursAdditionByDay( + List resources, Date start, Date finish) { + return new HoursByDayCalculator>>() { + + @Override + protected LocalDate getDayFor( + Entry> element) { + return element.getKey(); + } + + @Override + protected int getHoursFor( + Entry> element) { + LocalDate day = element.getKey(); + List resources = element.getValue(); + return sumHoursForDay(resources, day); + } + + }.calculate(getResourcesByDateBetween(resources, start, finish)); + } + + private Set>> getResourcesByDateBetween( + List resources, Date start, Date finish) { + LocalDate end = new LocalDate(finish); + Map> result = new HashMap>(); + for (LocalDate date = new LocalDate(start); date.compareTo(end) <= 0; date = date + .plusDays(1)) { + result.put(date, resources); + } + return result.entrySet(); + } + + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java index ec3fbca4a..2abe86f3f 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java @@ -36,14 +36,19 @@ import java.util.Set; import java.util.Map.Entry; import org.joda.time.LocalDate; +import org.navalplanner.business.calendars.daos.IBaseCalendarDAO; +import org.navalplanner.business.calendars.entities.ResourceCalendar; import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.common.daos.IConfigurationDAO; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; 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.planner.daos.IDayAssignmentDAO; import org.navalplanner.business.planner.daos.IResourceAllocationDAO; import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.GenericResourceAllocation; import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.SpecificResourceAllocation; @@ -60,6 +65,7 @@ import org.navalplanner.business.users.entities.OrderAuthorization; import org.navalplanner.business.users.entities.OrderAuthorizationType; import org.navalplanner.business.users.entities.User; import org.navalplanner.business.users.entities.UserRole; +import org.navalplanner.web.calendars.BaseCalendarModel; import org.navalplanner.web.security.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -111,6 +117,18 @@ public class ResourceLoadModel implements IResourceLoadModel { private List criteriaToShowList = new ArrayList(); + private Date initDateFilter; + private Date endDateFilter; + + @Autowired + private IDayAssignmentDAO dayAssignmentDAO; + + @Autowired + private IBaseCalendarDAO baseCalendarDAO; + + @Autowired + private IConfigurationDAO configurationDAO; + @Override @Transactional(readOnly = true) public void initGlobalView(boolean filterByResources) { @@ -190,7 +208,7 @@ public class ResourceLoadModel implements IResourceLoadModel { private Map> genericAllocationsByCriterion() { if(!criteriaToShowList.isEmpty()) { return resourceAllocationDAO - .findGenericAllocationsBySomeCriterion(criteriaToShowList); + .findGenericAllocationsBySomeCriterion(criteriaToShowList, initDateFilter, endDateFilter); } if (filter()) { List criterions = new ArrayList(); @@ -206,9 +224,9 @@ public class ResourceLoadModel implements IResourceLoadModel { } } return resourceAllocationDAO - .findGenericAllocationsBySomeCriterion(criterions); + .findGenericAllocationsBySomeCriterion(criterions, initDateFilter, endDateFilter); } else { - return resourceAllocationDAO.findGenericAllocationsByCriterion(); + return resourceAllocationDAO.findGenericAllocationsByCriterion(initDateFilter, endDateFilter); } } @@ -403,7 +421,7 @@ public class ResourceLoadModel implements IResourceLoadModel { for (CriterionSatisfaction satisfaction : satisfactions) { criterions.add(satisfaction.getCriterion()); } - return " :: " + getName(criterions); + return " :: " + Criterion.getNames(criterions); } private LoadTimeLine createPrincipal(Criterion criterion, @@ -415,8 +433,15 @@ public class ResourceLoadModel implements IResourceLoadModel { private List createPeriods(Criterion criterion, List value) { - return PeriodsBuilder.build(LoadPeriodGenerator.onCriterion(criterion, - resourcesDAO), value); + if(initDateFilter != null || endDateFilter != null) { + return PeriodsBuilder + .build(LoadPeriodGenerator.onCriterion(criterion, + resourcesDAO), value, + initDateFilter, endDateFilter); + } + return PeriodsBuilder + .build(LoadPeriodGenerator.onCriterion(criterion, + resourcesDAO), value); } private List groupsFor(List allResources) { @@ -433,7 +458,7 @@ public class ResourceLoadModel implements IResourceLoadModel { private LoadTimeLine buildGroup(Resource resource) { List> sortedByStartDate = ResourceAllocation .sortedByStartDate(resourceAllocationDAO - .findAllocationsRelatedTo(resource)); + .findAllocationsRelatedTo(resource, initDateFilter, endDateFilter)); TimeLineRole role = getCurrentTimeLineRole(resource); LoadTimeLine result = new LoadTimeLine(buildTimeLine(resource, resource .getName(), sortedByStartDate, "resource", role), @@ -601,27 +626,24 @@ public class ResourceLoadModel implements IResourceLoadModel { public static String getName(Collection criterions, Task task) { String prefix = task.getName(); - return (prefix + " :: " + getName(criterions)); - } - - public static String getName(Collection criterions) { - if (criterions.isEmpty()) { - return _("[generic all workers]"); - } - String[] names = new String[criterions.size()]; - int i = 0; - for (Criterion criterion : criterions) { - names[i++] = criterion.getName(); - } - return (Arrays.toString(names)); + return (prefix + " :: " + Criterion.getNames(criterions)); } private LoadTimeLine buildTimeLine(Resource resource, String name, List> sortedByStartDate, String type, TimeLineRole role) { - return new LoadTimeLine(name, PeriodsBuilder.build(LoadPeriodGenerator - .onResource(resource), sortedByStartDate), type, role); + List loadPeriods; + if(initDateFilter != null || endDateFilter != null) { + loadPeriods = PeriodsBuilder + .build(LoadPeriodGenerator.onResource(resource), sortedByStartDate, + initDateFilter, endDateFilter); + } + else { + loadPeriods = PeriodsBuilder + .build(LoadPeriodGenerator.onResource(resource), sortedByStartDate); + } + return new LoadTimeLine(name, loadPeriods, type, role); } private LoadTimeLine buildTimeLine(Criterion criterion, String name, @@ -647,8 +669,18 @@ public class ResourceLoadModel implements IResourceLoadModel { TimeLineRole role) { LoadPeriodGeneratorFactory periodGeneratorFactory = LoadPeriodGenerator .onResourceSatisfying(resource, criterions); - return new LoadTimeLine(getName(criterions, task), PeriodsBuilder - .build(periodGeneratorFactory, allocationsSortedByStartDate), + List loadPeriods; + if(initDateFilter != null || endDateFilter != null) { + loadPeriods = PeriodsBuilder + .build(periodGeneratorFactory, allocationsSortedByStartDate, + initDateFilter, endDateFilter); + } + else { + loadPeriods = PeriodsBuilder + .build(periodGeneratorFactory, allocationsSortedByStartDate); + } + + return new LoadTimeLine(getName(criterions, task), loadPeriods, type, role); } @@ -705,6 +737,53 @@ public class ResourceLoadModel implements IResourceLoadModel { criteriaToShowList.clear(); criteriaToShowList.addAll(criteriaList); } + + @Override + public void setEndDateFilter(Date value) { + endDateFilter = value; + } + + @Override + public void setInitDateFilter(Date value) { + initDateFilter = value; + } + + @Override + public Date getEndDateFilter() { + return endDateFilter; + } + + @Override + public Date getInitDateFilter() { + return initDateFilter; + } + + @Transactional(readOnly = true) + public List getDayAssignments() { + return dayAssignmentDAO.findByResources(getResources()); + } + + @Override + @Transactional(readOnly = true) + public List getResources() { + List resources = resourcesToShow(); + for (Resource resource : resources) { + resourcesDAO.reattach(resource); + ResourceCalendar calendar = resource.getCalendar(); + baseCalendarDAO.reattach(calendar); + BaseCalendarModel.forceLoadBaseCalendar(calendar); + resource.getAssignments().size(); + } + return resources; + } + + @Override + @Transactional(readOnly = true) + public boolean isExpandResourceLoadViewCharts() { + return configurationDAO.getConfiguration() + .isExpandResourceLoadViewCharts(); + } + } class PeriodsBuilder { @@ -726,6 +805,35 @@ class PeriodsBuilder { return new PeriodsBuilder(factory, sortedByStartDate).buildPeriods(); } + public static List build(LoadPeriodGeneratorFactory factory, + List> sortedByStartDate, + Date startDateFilter, Date endDateFilter) { + List list = new PeriodsBuilder(factory, sortedByStartDate).buildPeriods(); + List toReturn = new ArrayList(); + for(LoadPeriod loadPeriod : list) { + LocalDate finalStartDate = loadPeriod.getStart(); + LocalDate finalEndDate = loadPeriod.getEnd(); + if(startDateFilter != null) { + LocalDate startDateFilterLocalDate = new LocalDate(startDateFilter.getTime()); + if(finalStartDate.compareTo(startDateFilterLocalDate) < 0) { + finalStartDate = startDateFilterLocalDate; + } + } + if(endDateFilter != null) { + LocalDate endDateFilterLocalDate = new LocalDate(endDateFilter.getTime()); + if(loadPeriod.getEnd().compareTo(endDateFilterLocalDate) > 0) { + finalEndDate = endDateFilterLocalDate; + } + } + if(finalStartDate.compareTo(finalEndDate) < 0) { + toReturn.add(new LoadPeriod(finalStartDate, finalEndDate, + loadPeriod.getTotalResourceWorkHours(), + loadPeriod.getAssignedHours(), loadPeriod.getLoadLevel())); + } + } + return toReturn; + } + private List buildPeriods() { for (ResourceAllocation resourceAllocation : sortedByStartDate) { loadPeriodsGenerators.add(factory.create(resourceAllocation)); @@ -737,7 +845,10 @@ class PeriodsBuilder { private List toGenerators(List generators) { List result = new ArrayList(); for (LoadPeriodGenerator loadPeriodGenerator : generators) { - result.add(loadPeriodGenerator.build()); + LoadPeriod period = loadPeriodGenerator.build(); + if (period != null) { + result.add(period); + } } return result; } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java index 5f0ffca5f..2141631ee 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java @@ -450,7 +450,8 @@ public abstract class TreeController> extends @Override public void onEvent(Event event) throws Exception { - item.setSelected(true); + boolean selected = (!item.isSelected()); + item.setSelected(selected); Util.reloadBindings(item.getParent()); } }); diff --git a/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml b/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml index 64462c444..4fd5bd638 100755 --- a/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml +++ b/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml @@ -89,11 +89,11 @@ - LimitingResourcesList - org.navalplanner.web.limitingresources.LimitingResourcesList + queueListComponent + org.navalplanner.web.limitingresources.QueueListComponent default - /limitingresources/limitingresourceslist.dsp + /limitingresources/queuelistcomponent.dsp @@ -104,11 +104,11 @@ - limitingresources - org.navalplanner.web.limitingresources.LimitingResourcesComponent + queuecomponent + org.navalplanner.web.limitingresources.QueueComponent default - /limitingresources/limitingresources.dsp + /limitingresources/queuecomponent.dsp @@ -122,5 +122,15 @@ + + limitingdependency + org.navalplanner.web.limitingresources.LimitingDependencyComponent + + default + /limitingresources/limitingdependency.dsp + + + + diff --git a/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependency.js b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependency.js new file mode 100644 index 000000000..0115d4aeb --- /dev/null +++ b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependency.js @@ -0,0 +1,263 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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 . + */ + +/** + * Javascript behaviour and drawing algorithms for queue dependencies + * @author Lorenzo Tilve Álvaro + */ + +webapp_context_path = window.location.pathname.split( '/' )[1]; + +zkLimitingDependencies = {}; +zkLimitingDependencies.constants = { + END_START: "END_START", + START_START: "START_START", + END_END: "END_END" +}; + +zkLimitingDependencies.CORNER = 4; +zkLimitingDependencies.HEIGHT = 12; +zkLimitingDependencies.ARROW_PADDING = 10; +zkLimitingDependencies.HALF_ARROW_PADDING = 5; + +/* TODO: Optimize function */ +zkLimitingDependencies.showDependenciesForQueueElement = function (task) { + var dependencies = YAHOO.util.Selector.query('.dependency'); + for (var i = 0; i < dependencies.length; i++) { + if ( (dependencies[i].getAttribute("idTaskOrig") == task) || (dependencies[i].getAttribute("idTaskEnd") == task) ) { + dependencies[i].style.display ="inline"; + dependencies[i].style.opacity ="1"; + } + } +} + +/* TODO: Optimize function */ +zkLimitingDependencies.hideDependenciesForQueueElement = function (task) { + var dependencies = YAHOO.util.Selector.query('.dependency'); + for (var i = 0; i < dependencies.length; i++) { + if ( (dependencies[i].getAttribute("idTaskOrig") == task) || (dependencies[i].getAttribute("idTaskEnd") == task) ) { + dependencies[i].style.display ="none"; + dependencies[i].style.removeProperty("opacity"); + } + } +} + +/* TODO: Optimize function */ +zkLimitingDependencies.toggleDependenciesForQueueElement = function (task) { + var dependencies = YAHOO.util.Selector.query('.dependency'); + for (var i = 0; i < dependencies.length; i++) { + if ( (dependencies[i].getAttribute("idTaskOrig") == task) || (dependencies[i].getAttribute("idTaskEnd") == task) ) { + dependencies[i].setAttribute("class", "dependency toggled"); + } + } +} + + +zkLimitingDependencies.addRelatedDependency = function(cmp, dependency) { + if (!cmp['relatedDependencies']) { + cmp.relatedDependencies = []; + } + cmp.relatedDependencies.push(dependency); +} + +zkLimitingDependencies.getImagesDir = function() { + return "/" + webapp_context_path + "/zkau/web/ganttz/img/"; +} + +zkLimitingDependencies.init = function(planner){ +} + +zkLimitingDependencies.findImageElement = function(arrow, name) { + var children = arrow.getElementsByTagName("img"); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.getAttribute("class").indexOf(name) != -1) { + return child; + } + } + return null; +} + + +function get_origin() { + return YAHOO.util.Dom.getXY('listlimitingdependencies'); +} + + +zkLimitingDependencies.findPos = function(obj) { + var pos1 = get_origin(); + var pos2 = YAHOO.util.Dom.getXY(obj.id); + return [ pos2[0] - pos1[0], pos2[1] - pos1[1] ]; +} + + +zkLimitingDependencies.findPosForMouseCoordinates = function(x, y){ + /* var pos1 = get_origin() */ + var pos1 = YAHOO.util.Dom.getXY('listtasks'); + return [x - pos1[0], y - pos1[1]]; +} + + +function getContextPath(element){ + return element.getAttribute('contextpath'); +} + + +zkLimitingDependencies.setupArrow = function(arrowDiv){ + var image_data = [ [ "start", "pixel.gif" ], [ "mid", "pixel.gif" ], + [ "end", "pixel.gif" ], [ "arrow", "arrow.png" ] ]; + for ( var i = 0; i < image_data.length; i++) { + var img = document.createElement('img'); + img.setAttribute("class", image_data[i][0]+" extra_padding"); + img.src = this.getImagesDir() + image_data[i][1]; + arrowDiv.appendChild(img); + } +} + + +zkLimitingDependency = {}; + + +zkLimitingDependency.origin = function(dependency) { + var id = dependency.getAttribute("idTaskOrig"); + return document.getElementById(id); +} + +zkLimitingDependency.destination = function(dependency) { + var id = dependency.getAttribute("idTaskEnd"); + return document.getElementById(id); +} + +/* ----------- Generic Limiting dependency draw function ---------- */ +zkLimitingDependencies.newdraw = function(arrow, orig, dest, param) { + var xorig = orig[0]; + var yorig = orig[1] - zkLimitingDependencies.CORNER; + var xend = dest[0]; + var yend = dest[1] - zkLimitingDependencies.CORNER; + + if (yend < yorig) { + yend = yend + zkLimitingDependencies.HEIGHT; + } else { + yorig = yorig + zkLimitingDependencies.HEIGHT; + } + + var width = Math.abs(xend - xorig); + var height = Math.abs(yorig - yend); + + // --------- First segment ----------- + var depstart = this.findImageElement(arrow, 'start'); + depstart.style.left = xorig + "px"; + if (yend > yorig) { + depstart.style.top = yorig + "px"; + depstart.style.height = ( height - param ) + "px"; + } else if (yend == yorig) { + depstart.style.top = yorig + "px"; + depstart.style.height = param + "px"; + } else if (yend < yorig) { + depstart.style.top = ( yend + param ) + "px"; + depstart.style.height = ( height - param ) + "px"; + } + + // --------- Second segment ----------- + var depmid = this.findImageElement(arrow, 'mid'); + depmid.style.width = width + "px"; + if (xorig < xend ) { + depmid.style.left = xorig + "px"; + } else { + depmid.style.left = xend + "px"; + } + if (yend > yorig) { + depmid.style.top = ( yend - param ) + "px"; + } else if (yend == yorig) { + depmid.style.top = ( yend + param ) + "px"; + } else if (yend < yorig) { + depmid.style.top = ( yend + param ) + "px"; + } + + // --------- Third segment ----------- + var depend = this.findImageElement(arrow, 'end'); + depend.style.left = xend + "px"; + if (yend > yorig) { + depend.style.top = ( yend - param ) + "px"; + depend.style.height = param + "px"; + } else if (yend == yorig) { + depend.style.top = yorig + "px"; + depend.style.height = param + "px"; + } else if (yend < yorig) { + depend.style.top = yend + "px"; + depend.style.height = param + "px"; + } + + // --------- Arrow ----------- + var deparrow = this.findImageElement(arrow, 'arrow'); + deparrow.style.left = ( xend - zkLimitingDependencies.HALF_ARROW_PADDING ) + "px"; + if (yend > yorig) { + deparrow.src = this.getImagesDir()+"arrow2.png"; + deparrow.style.top = ( yend - zkLimitingDependencies.ARROW_PADDING ) + "px"; + } else if (yend == yorig) { + deparrow.src = this.getImagesDir()+"arrow4.png"; + deparrow.style.top = yorig + "px"; + } else if (yend < yorig) { + deparrow.src = this.getImagesDir()+"arrow4.png"; + deparrow.style.top = yend + "px"; + } +} + + +zkLimitingDependency.draw = function(dependency) { + var posOrig = this.origin(dependency); + var posDest = this.destination(dependency); + if ( ( posOrig != null ) && ( posDest!= null ) ) { + var orig = zkLimitingDependencies.findPos(posOrig); + var dest = zkLimitingDependencies.findPos(posDest); + + var verticalSeparation = 15; + switch(dependency.getAttribute('type')) + { + case zkLimitingDependencies.constants.START_START: + verticalSeparation = 20; + orig[0] = orig[0] - zkLimitingDependencies.CORNER; + dest[0] = dest[0] - zkLimitingDependencies.CORNER; + break; + case zkLimitingDependencies.constants.END_END: + verticalSeparation = 25; + break; + case zkLimitingDependencies.constants.END_START: + default: + verticalSeparation = 15; + } + zkLimitingDependencies.newdraw(dependency, orig, dest, verticalSeparation); + } +} + +zkLimitingDependency.init = function(dependency) { + zkLimitingDependencies.setupArrow(dependency); + var parent = dependency.parentNode; + if (parent.id !== "listlimitingdependencies") { + document.getElementById("listlimitingdependencies").appendChild(dependency); + } + YAHOO.util.Event.onDOMReady(function() { + var origin = zkLimitingDependency.origin(dependency); + var destination = zkLimitingDependency.destination(dependency); + zkLimitingDependency.draw(dependency); + zkLimitingDependency.addRelatedDependency(origin, dependency); + zkLimitingDependency.addRelatedDependency(destination, dependency); + }); +} diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependencylist.js b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependencylist.js similarity index 91% rename from navalplanner-webapp/src/main/webapp/limitingresources/limitingdependencylist.js rename to navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependencylist.js index 0dd28a648..7bd025148 100644 --- a/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependencylist.js +++ b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependencylist.js @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -zkDependencylist = {}; +zkLimitingDependencylist = {}; -zkDependencylist.init = function (cmp) { +zkLimitingDependencylist.init = function (cmp) { } diff --git a/ganttzk/src/main/resources/web/js/ganttz/limitingresources/limitingresourceslist.js b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingresourceslist.js similarity index 100% rename from ganttzk/src/main/resources/web/js/ganttz/limitingresources/limitingresourceslist.js rename to navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingresourceslist.js diff --git a/navalplanner-webapp/src/main/webapp/WEB-INF/zk.xml b/navalplanner-webapp/src/main/webapp/WEB-INF/zk.xml index fe39b9c51..e91666da1 100644 --- a/navalplanner-webapp/src/main/webapp/WEB-INF/zk.xml +++ b/navalplanner-webapp/src/main/webapp/WEB-INF/zk.xml @@ -25,27 +25,5 @@ org.springframework.security.context.ThreadLocalSecurityContextHolderStrategy=contextHolder - - - PollingServerPush.delay.min - 100 - - - - PollingServerPush.delay.max - 1000 - - - - PollingServerPush.delay.factor - 2 - - - - ThreadLocal - - org.springframework.security.context.ThreadLocalSecurityContextHolderStrategy=contextHolder - - diff --git a/navalplanner-webapp/src/main/webapp/common/configuration.zul b/navalplanner-webapp/src/main/webapp/common/configuration.zul index 81348be04..fa69f5f80 100644 --- a/navalplanner-webapp/src/main/webapp/common/configuration.zul +++ b/navalplanner-webapp/src/main/webapp/common/configuration.zul @@ -108,6 +108,10 @@
    + + +
    diff --git a/navalplanner-webapp/src/main/webapp/common/css/login.css b/navalplanner-webapp/src/main/webapp/common/css/login.css index 1261f03ae..cb40a25a8 100644 --- a/navalplanner-webapp/src/main/webapp/common/css/login.css +++ b/navalplanner-webapp/src/main/webapp/common/css/login.css @@ -17,7 +17,7 @@ body { } .entrar { - cursor: hand; + cursor: pointer; background-color: #E4F3D9; /* green0 (normal background) */ color: #58A758; /* green3 (text) */ border: 1px solid #439e32; /* green4 (border) */ diff --git a/navalplanner-webapp/src/main/webapp/common/css/navalplan_zk.css b/navalplanner-webapp/src/main/webapp/common/css/navalplan_zk.css index cce7804b0..9b14cf5b8 100644 --- a/navalplanner-webapp/src/main/webapp/common/css/navalplan_zk.css +++ b/navalplanner-webapp/src/main/webapp/common/css/navalplan_zk.css @@ -830,6 +830,10 @@ div.z-grid { margin-right:5px; } +.resourcesloadlayout .legend-container { + width:186px; +} + /* Graphical improvements */ @@ -1261,6 +1265,30 @@ overflow: visible; background-color: #CDE6F5; } +/* Resource allocation*/ +.allocation-not-satisfied .resource-allocation tr.z-grid-odd td.z-row-inner, .allocation-not-satisfied tr.z-grid-odd { + background-color: #EECCCC; +} + +.allocation-not-satisfied td.z-row-inner { + background-color: #EECCCC; +} + +.assignedresources .allocation-not-satisfied td.z-row-inner { + background-color: #EECCCC; +} + +.allocation-satisfied .resource-allocation tr.z-grid-odd td.z-row-inner, .allocation-satisfied tr.z-grid-odd { + background-color: none; +} + +.allocation-satisfied td.z-row-inner { + background-color: none; +} + +.assignedresources .allocation-satisfied td.z-row-inner { + background-color: none; +} /* Advanced allocation */ @@ -1273,7 +1301,7 @@ overflow: visible; } .timeTrackedTableWithLeftPane .z-grid-body .z-row-inner { - background-color: #E8E8E8; + background-color: #f9f9f9; } @@ -1292,3 +1320,25 @@ tr.z-tree-row-seld td.z-row-inner { tr.z-tree-row-seld .z-row-cnt { color: #FFFFFF; } + +/* color code for order state */ +.order-open-unassigned { + color: #AACCEE; +} +.order-open-assigned { + color: #007BBE; +} +.order-closed { + color: #000000; +} + +/* ----------- Limiting Resources window ------- */ +.not-workable-hours { + position:relative; + z-index: -2; + top: -1px; + height: 12px; + float:right; + background-color: #CCCCCC; +} + diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul b/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul index a3fec3107..883d01600 100644 --- a/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul +++ b/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul @@ -26,40 +26,52 @@ resourcesLoadPanel = self; ]]> - - - - - - - - - -
    - + - - - - + + + + + + diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependency.dsp b/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependency.dsp new file mode 100644 index 000000000..4d29837b6 --- /dev/null +++ b/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependency.dsp @@ -0,0 +1,9 @@ +<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %> +<%@ taglib uri="http://www.zkoss.org/dsp/zk/core" prefix="z" %> + + + +
    +
    diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependencylist.dsp b/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependencylist.dsp index 4da3e1bb4..4877c680e 100644 --- a/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependencylist.dsp +++ b/navalplanner-webapp/src/main/webapp/limitingresources/limitingdependencylist.dsp @@ -3,9 +3,9 @@ -
    +
    -
    +
    ${z:redraw(child, null)} diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/limitingresources.dsp b/navalplanner-webapp/src/main/webapp/limitingresources/queuecomponent.dsp similarity index 100% rename from navalplanner-webapp/src/main/webapp/limitingresources/limitingresources.dsp rename to navalplanner-webapp/src/main/webapp/limitingresources/queuecomponent.dsp diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/limitingresourceslist.dsp b/navalplanner-webapp/src/main/webapp/limitingresources/queuelistcomponent.dsp similarity index 81% rename from navalplanner-webapp/src/main/webapp/limitingresources/limitingresourceslist.dsp rename to navalplanner-webapp/src/main/webapp/limitingresources/queuelistcomponent.dsp index 915dbe395..9ee77c45c 100644 --- a/navalplanner-webapp/src/main/webapp/limitingresources/limitingresourceslist.dsp +++ b/navalplanner-webapp/src/main/webapp/limitingresources/queuelistcomponent.dsp @@ -4,7 +4,7 @@
    + z.type="limitingresources.limitingresourceslist.LimitingResourcesList"> ${z:redraw(child, null)} diff --git a/navalplanner-webapp/src/main/webapp/orders/_editOrderElement.zul b/navalplanner-webapp/src/main/webapp/orders/_editOrderElement.zul index 9f4047f0d..b1ab406e3 100644 --- a/navalplanner-webapp/src/main/webapp/orders/_editOrderElement.zul +++ b/navalplanner-webapp/src/main/webapp/orders/_editOrderElement.zul @@ -30,7 +30,7 @@ macroURI="/orders/components/_listOrderElementMaterials.zul"?> - diff --git a/navalplanner-webapp/src/main/webapp/orders/_listOrderElementAdvances.zul b/navalplanner-webapp/src/main/webapp/orders/_listOrderElementAdvances.zul index 65c502530..4f9b4eeba 100644 --- a/navalplanner-webapp/src/main/webapp/orders/_listOrderElementAdvances.zul +++ b/navalplanner-webapp/src/main/webapp/orders/_listOrderElementAdvances.zul @@ -31,11 +31,11 @@ - + pageSize="4"> @@ -62,7 +62,7 @@ + mold="paging" pageSize="5" sclass="advance-measurement"> diff --git a/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul b/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul index cd71399a8..36cf489f1 100644 --- a/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul +++ b/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul @@ -28,8 +28,13 @@ - - + + + @@ -38,22 +43,22 @@ - - - + + + - - - + + + - @@ -77,8 +82,8 @@ - - + +
    + width="870px" visible="false">