diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java b/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java index df53c97ff..60f3557af 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/CommandOnTaskContextualized.java @@ -97,4 +97,7 @@ public class CommandOnTaskContextualized { return commandOnTask.isApplicableTo(domainObject); } + public IDomainAndBeansMapper getMapper() { + return this.mapper; + } } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java b/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java index 7dda488ee..b34d59c28 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/DatesMapperOnInterval.java @@ -73,4 +73,9 @@ public class DatesMapperOnInterval implements IDatesMapper { return millisecondsPerPixel; } + @Override + public int getHorizontalSize() { + return this.horizontalSize; + } + } \ 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 8f78f770b..2f80997c3 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/FunctionalityExposedForExtensions.java @@ -161,6 +161,12 @@ public class FunctionalityExposedForExtensions implements IContext { return result; } + @Override + public List getParents(Task task) { + Position position = findPositionFor(task); + return position.getAncestors(); + } + } private final Planner planner; diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java b/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java index 3ad132c8b..0dc679522 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/IDatesMapper.java @@ -38,4 +38,6 @@ public interface IDatesMapper { long getMilisecondsPerPixel(); + int getHorizontalSize(); + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java index cfcc09f70..add4c0874 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java @@ -33,6 +33,7 @@ import java.util.Set; import java.util.Map.Entry; import org.zkoss.ganttz.adapters.IDisabilityConfiguration; +import org.zkoss.ganttz.adapters.IDomainAndBeansMapper; import org.zkoss.ganttz.adapters.PlannerConfiguration; import org.zkoss.ganttz.data.Dependency; import org.zkoss.ganttz.data.GanttDiagramGraph; @@ -161,6 +162,31 @@ public class Planner extends HtmlMacroComponent { return getTaskList().getTasksNumber(); } + private static int PIXELS_PER_TASK_LEVEL = 21; + private static int PIXELS_PER_CHARACTER = 5; + + public int calculateMinimumWidthForTaskNameColumn(boolean expand) { + return calculateMinimumWidthForTaskNameColumn(expand, getTaskList().getAllTasks()); + } + + private int calculateMinimumWidthForTaskNameColumn(boolean expand, List tasks) { + IDomainAndBeansMapper mapper = getContext().getMapper(); + int widest = 0; + for(Task task : tasks) { + int numberOfAncestors = + mapper.findPositionFor(task).getAncestors().size(); + int numberOfCharacters = task.getName().length(); + widest = Math.max(widest, + numberOfCharacters * PIXELS_PER_CHARACTER + + numberOfAncestors * PIXELS_PER_TASK_LEVEL); + if(expand && !task.isLeaf()) { + widest = Math.max(widest, + calculateMinimumWidthForTaskNameColumn(expand, task.getTasks())); + } + } + return widest; + } + public int getAllTasksNumber() { return diagramGraph.getTasks().size(); } @@ -206,7 +232,10 @@ public class Planner extends HtmlMacroComponent { } public ListModel getZoomLevels() { - return new SimpleListModel(ZoomLevel.values()); + ZoomLevel[] selectableZoomlevels = { ZoomLevel.DETAIL_ONE, + ZoomLevel.DETAIL_TWO, ZoomLevel.DETAIL_THREE, + ZoomLevel.DETAIL_FOUR, ZoomLevel.DETAIL_FIVE }; + return new SimpleListModel(selectableZoomlevels); } public void setZoomLevel(final ZoomLevel zoomLevel) { @@ -262,10 +291,7 @@ public class Planner extends HtmlMacroComponent { .getStartConstraints(), configuration.getEndConstraints(), configuration.isDependenciesConstraintsHavePriority()); FunctionalityExposedForExtensions newContext = new FunctionalityExposedForExtensions( this, configuration, diagramGraph); - diagramGraph.addPreChangeListeners(configuration - .getPreChangeListeners()); - diagramGraph.addPostChangeListeners(configuration - .getPostChangeListeners()); + addGraphChangeListenersFromConfiguration(configuration); this.contextualizedGlobalCommands = contextualize(newContext, configuration.getGlobalCommands()); this.commandsOnTasksContextualized = contextualize(newContext, @@ -627,4 +653,12 @@ public class Planner extends HtmlMacroComponent { chartVisibilityListeners.addListener(chartVisibilityChangedListener); } + public void addGraphChangeListenersFromConfiguration( + PlannerConfiguration configuration) { + diagramGraph.addPreChangeListeners(configuration + .getPreChangeListeners()); + diagramGraph.addPostChangeListeners(configuration + .getPostChangeListeners()); + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java b/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java index 39b683c2d..1a669033f 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/TaskComponent.java @@ -62,6 +62,8 @@ public class TaskComponent extends Div implements AfterCompose { private static final Log LOG = LogFactory.getLog(TaskComponent.class); private static final int HEIGHT_PER_TASK = 10; + private static final int CONSOLIDATED_MARK_HALF_WIDTH = 3; + private static Pattern pixelsSpecificationPattern = Pattern .compile("\\s*(\\d+)px\\s*;?\\s*"); @@ -207,6 +209,9 @@ public class TaskComponent extends Div implements AfterCompose { response("setClass", new AuInvoke(TaskComponent.this, "setClass", cssClass)); + + // FIXME: Refactorize to another listener + updateDeadline(); } }; @@ -437,9 +442,17 @@ public class TaskComponent extends Div implements AfterCompose { String position = getMapper().toPixels(task.getDeadline()) + "px"; response(null, new AuInvoke(this, "moveDeadline", position)); } + if (task.getConsolidatedline() != null) { + String position = (getMapper().toPixels(task.getConsolidatedline()) - CONSOLIDATED_MARK_HALF_WIDTH) + + "px"; + response(null, new AuInvoke(this, "moveConsolidatedline", position)); + } else { + // Move consolidated line out of visible area + response(null, new AuInvoke(this, "moveConsolidatedline", "-100px")); + } } - private void updateCompletionIfPossible() { + public void updateCompletionIfPossible() { try { updateCompletion(); } catch (Exception e) { @@ -475,6 +488,10 @@ public class TaskComponent extends Div implements AfterCompose { widthAdvancePercentage)); } + public void updateTooltipText() { + smartUpdate("taskTooltipText", task.updateTooltipText()); + } + private DependencyList getDependencyList() { return getGanntPanel().getDependencyList(); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java b/ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java index 9af30e62b..da3b6e245 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java @@ -93,6 +93,10 @@ public class TaskList extends XulElement implements AfterCompose { this.predicate = predicate; } + public List getAllTasks() { + return new ArrayList(currentTotalTasks); + } + public static TaskList createFor( FunctionalityExposedForExtensions context, CommandOnTaskContextualized doubleClickCommand, @@ -307,8 +311,31 @@ public class TaskList extends XulElement implements AfterCompose { } for (CommandOnTaskContextualized command : commandsOnTasksContextualized) { if (command.accepts(taskComponent)) { - menuBuilder.item(command.getName(), command.getIcon(), + if (command.getName().equals("Advance assignment")) { + final CommandOnTaskContextualized commandAdvances = command; + menuBuilder.item(command.getName(), command.getIcon(), + new ItemAction() { + @Override + public void onEvent(TaskComponent choosen, + Event event) { + commandAdvances.doAction(choosen); + if (commandAdvances.getMapper() != null) { + List listTaskComponents = new ArrayList( + commandAdvances + .getMapper() + .getParents( + choosen + .getTask())); + listTaskComponents.add(choosen + .getTask()); + updateTaskAndItsParents(listTaskComponents); + } + } + }); + } else { + menuBuilder.item(command.getName(), command.getIcon(), command.toItemAction()); + } } } Menupopup result = menuBuilder.createWithoutSettingContext(); @@ -318,6 +345,14 @@ public class TaskList extends XulElement implements AfterCompose { return contextMenus.get(taskComponent); } + private void updateTaskAndItsParents(List taskList) { + for (Task task : taskList) { + TaskComponent choosen = this.find(task); + choosen.updateCompletionIfPossible(); + choosen.updateTooltipText(); + } + } + GanttPanel getGanttPanel() { return (GanttPanel) getParent(); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDomainAndBeansMapper.java b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDomainAndBeansMapper.java index daa17dd96..a55679e67 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDomainAndBeansMapper.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDomainAndBeansMapper.java @@ -20,10 +20,12 @@ package org.zkoss.ganttz.adapters; +import java.util.List; import java.util.Map; import org.zkoss.ganttz.data.Position; import org.zkoss.ganttz.data.Task; +import org.zkoss.ganttz.data.TaskContainer; /** * @author Óscar González Fernández @@ -62,5 +64,13 @@ public interface IDomainAndBeansMapper { */ Task findAssociatedBean(T domainObject) throws IllegalArgumentException; + /** + * Used to know the parents for a given task. + * @return If the task is top level returns an empty list.
+ * Otherwise it returns the list of parents from more immediate to + * most distant parent + */ + List getParents(Task task); + public Map getMapDomainToTask(); } 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 735349edd..a1d770200 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java @@ -393,13 +393,17 @@ public class PlannerConfiguration implements IDisabilityConfiguration { public void addPreGraphChangeListener( IGraphChangeListener preGraphChangeListener) { Validate.notNull(preGraphChangeListener); - preGraphChangeListeners.add(preGraphChangeListener); + if (!preGraphChangeListeners.contains(preGraphChangeListener)) { + preGraphChangeListeners.add(preGraphChangeListener); + } } public void addPostGraphChangeListener( IGraphChangeListener postGraphChangeListener) { Validate.notNull(postGraphChangeListener); - postGraphChangeListeners.add(postGraphChangeListener); + if (!postGraphChangeListeners.contains(postGraphChangeListener)) { + postGraphChangeListeners.add(postGraphChangeListener); + } } public List getPreChangeListeners() { 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 08f18bc5f..9cfdf9763 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/DefaultFundamentalProperties.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/DefaultFundamentalProperties.java @@ -189,4 +189,18 @@ public class DefaultFundamentalProperties implements ITaskFundamentalProperties return "unassigned"; } + @Override + public boolean isFixed() { + return false; + } + + @Override + public Date getConsolidatedline() { + return null; + } + + public String updateTooltipText() { + return null; + } + } 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 a0fe9b02c..2a88e527b 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/GanttDiagramGraph.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/GanttDiagramGraph.java @@ -120,6 +120,8 @@ public class GanttDiagramGraph { boolean isVisible(D dependency); + boolean isFixed(V task); + } static class GanttZKAdapter implements IAdapter { @@ -230,6 +232,11 @@ public class GanttDiagramGraph { return ((TaskContainer) container).getSmallestBeginDateFromChildren(); } + @Override + public boolean isFixed(Task task) { + return task.isFixed(); + } + } public static class GanttZKDiagramGraph extends @@ -772,11 +779,7 @@ public class GanttDiagramGraph { 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); @@ -1032,6 +1035,9 @@ public class GanttDiagramGraph { @SuppressWarnings("unchecked") private boolean enforceEndDate(V task, Date previousEndDate, Set incoming) { + if (adapter.isFixed(task)) { + return false; + } Constraint currentLength = adapter .getCurrentLenghtConstraintFor(task); Constraint respectStartDate = adapter @@ -1048,6 +1054,9 @@ public class GanttDiagramGraph { } private boolean enforceStartDate(V task, Set incoming) { + if (adapter.isFixed(task)) { + return false; + } Date newStart = calculateStartDateFor(task, incoming); if (!adapter.getStartDate(task).equals(newStart)) { adapter.setStartDateFor(task, newStart); 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 efec66e04..54a8f10d0 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/ITaskFundamentalProperties.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/ITaskFundamentalProperties.java @@ -50,6 +50,8 @@ public interface ITaskFundamentalProperties { */ public Date getDeadline(); + public Date getConsolidatedline(); + public void setLengthMilliseconds(long lengthMilliseconds); public long getLengthMilliseconds(); @@ -88,4 +90,8 @@ public interface ITaskFundamentalProperties { public String getAssignedStatus(); + public boolean isFixed(); + + public String updateTooltipText(); + } 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 f27444c1e..4e979bdb7 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/Task.java @@ -296,6 +296,10 @@ public abstract class Task implements ITaskFundamentalProperties { return fundamentalProperties.getTooltipText(); } + public String updateTooltipText() { + return fundamentalProperties.updateTooltipText(); + } + public String getLabelsText() { return fundamentalProperties.getLabelsText(); } @@ -316,6 +320,11 @@ public abstract class Task implements ITaskFundamentalProperties { return fundamentalProperties.getDeadline(); } + @Override + public Date getConsolidatedline() { + return fundamentalProperties.getConsolidatedline(); + } + public void addConstraintViolationListener( IConstraintViolationListener listener) { violationNotificator.addConstraintViolationListener(listener); @@ -373,4 +382,8 @@ public abstract class Task implements ITaskFundamentalProperties { return fundamentalProperties.getAssignedStatus(); } + public boolean isFixed() { + return fundamentalProperties.isFixed(); + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java index 9fcc67370..30beeea3b 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java @@ -20,8 +20,6 @@ package org.zkoss.ganttz.data.resourceload; -import static org.zkoss.ganttz.i18n.I18nHelper._; - /** * @author Susana Montes Pedreira @@ -55,13 +53,13 @@ public class TimeLineRole { */ public enum TimeLineRoleEnum { - NONE(_("None")), WORKER(_("Worker")), ORDER(_("Order")), TASK(_("Task")) { + NONE("None"), WORKER("Worker"), ORDER("Order"), TASK("Task") { @Override public boolean isVisibleScheduled() { return true; } }, - CRITERION(_("Criterion")); + CRITERION("Criterion"); private String name; diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/TaskAssignmentMoveCommand.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/IPaginationFilterChangedListener.java similarity index 67% rename from ganttzk/src/main/java/org/zkoss/ganttz/TaskAssignmentMoveCommand.java rename to ganttzk/src/main/java/org/zkoss/ganttz/resourceload/IPaginationFilterChangedListener.java index c71273ba2..fdfcf468e 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/TaskAssignmentMoveCommand.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/IPaginationFilterChangedListener.java @@ -18,27 +18,10 @@ * along with this program. If not, see . */ -package org.zkoss.ganttz; +package org.zkoss.ganttz.resourceload; -import org.zkoss.zk.au.AuRequest; -import org.zkoss.zk.au.Command; +public interface IPaginationFilterChangedListener { -/** - * - * @author Francisco Javier Moran Rúa - * - */ -public class TaskAssignmentMoveCommand extends Command { - - public TaskAssignmentMoveCommand(String event,int flags) { - super(event,flags); - } - - protected void process(AuRequest request) { - - System.out.println("Processing command"); - - - } + public void filterChanged(int initialPosition); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java index 34ba35504..c373d8f01 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java @@ -36,6 +36,7 @@ import org.zkoss.zk.au.out.AuInvoke; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.HtmlMacroComponent; import org.zkoss.zk.ui.ext.AfterCompose; +import org.zkoss.zk.ui.util.Clients; /** * Component to include a list of ResourceLoads inside the ResourcesLoadPanel. @@ -98,6 +99,8 @@ public class ResourceLoadList extends HtmlMacroComponent implements for (LoadTimeLine l : line.getAllChildren()) { getComponentFor(l).detach(); } + Clients + .evalJavaScript("zkResourcesLoadList.recalculateTimetrackerHeight();"); } private ResourceLoadComponent getComponentFor(LoadTimeLine l) { @@ -118,7 +121,8 @@ public class ResourceLoadList extends HtmlMacroComponent implements insertBefore(child, nextSibling); nextSibling = child; } - + Clients + .evalJavaScript("zkResourcesLoadList.recalculateTimetrackerHeight();"); } private List getChildrenReverseOrderFor(LoadTimeLine line) { 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 9f8d7db24..4ef6be8da 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java @@ -82,15 +82,16 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private Listbox listZoomLevels; private static final String filterResources = _("by resources"); - private static final String filterCriterions = _("by criterions"); + private static final String filterCriteria = _("by criteria"); private String feedBackMessage; private Boolean filterbyResources; private boolean refreshNameFilter = true; private int filterByNamePosition = 0; private int numberOfGroupsByName = 10; + private PaginationType paginationType; - private WeakReferencedListeners nameFilterListener = + private WeakReferencedListeners nameFilterListener = WeakReferencedListeners.create(); private Component loadChart; @@ -104,9 +105,10 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { public ResourcesLoadPanel(List groups, TimeTracker timeTracker, Component componentOnWhichGiveFeedback, - boolean expandResourceLoadViewCharts) { + boolean expandResourceLoadViewCharts, PaginationType paginationType) { this.componentOnWhichGiveFeedback = componentOnWhichGiveFeedback; this.expandResourceLoadViewCharts = expandResourceLoadViewCharts; + this.paginationType = paginationType; init(groups, timeTracker); } @@ -122,7 +124,7 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { } public ListModel getFilters() { - String[] filters = new String[] { filterResources, filterCriterions }; + String[] filters = new String[] { filterResources, filterCriteria }; return new SimpleListModel(filters); } @@ -132,7 +134,7 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { this.feedBackMessage = _("showing resources"); } else { this.filterbyResources = false; - this.feedBackMessage = _("showing criterions"); + this.feedBackMessage = _("showing criteria"); } refreshNameFilter = true; filterByNamePosition = 0; @@ -178,7 +180,10 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { } public ListModel getZoomLevels() { - return new SimpleListModel(ZoomLevel.values()); + ZoomLevel[] selectableZoomlevels = { ZoomLevel.DETAIL_ONE, + ZoomLevel.DETAIL_TWO, ZoomLevel.DETAIL_THREE, + ZoomLevel.DETAIL_FOUR, ZoomLevel.DETAIL_FIVE }; + return new SimpleListModel(selectableZoomlevels); } public void setZoomLevel(final ZoomLevel zoomLevel) { @@ -311,9 +316,13 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { listZoomLevels = (Listbox) getFellow("listZoomLevels"); listZoomLevels.setSelectedIndex(timeTracker.getDetailLevel().ordinal()); - if(refreshNameFilter) { + if(paginationType == PaginationType.INTERNAL_PAGINATION && refreshNameFilter) { setupNameFilter(); } + else if(paginationType == PaginationType.NONE) { + getFellow("filterByNameCombo").setVisible(false); + getFellow("filterByNameLabel").setVisible(false); + } getFellow("insertionPointChart").appendChild(loadChart); @@ -389,7 +398,8 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { * @return */ private List getGroupsToShow() { - if(filterByNamePosition == -1) { + if(paginationType != PaginationType.INTERNAL_PAGINATION || + filterByNamePosition == -1) { return groups; } int endPosition = @@ -400,9 +410,11 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { } public void onSelectFilterByName(Integer filterByNamePosition) { + if(paginationType != PaginationType.NONE) { this.filterByNamePosition = filterByNamePosition.intValue(); this.feedBackMessage = _("filtering by name"); changeNameFilterWithFeedback(); + } } private void changeNameFilterWithFeedback() { @@ -411,15 +423,18 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { @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() { + if(paginationType == PaginationType.INTERNAL_PAGINATION) { + //if the pagination is internal, we are in charge of repainting the graph + 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()); + public void doNotify(IPaginationFilterChangedListener listener) { + listener.filterChanged(filterByNamePosition); } }); afterCompose(); @@ -432,7 +447,7 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { }); } - public void setNameFilterDisabled(boolean disabled) { + public void setInternalPaginationDisabled(boolean disabled) { Combobox combo = ((Combobox) getFellow("filterByNameCombo")); if(combo.isDisabled() != disabled) { filterByNamePosition = disabled? -1 : @@ -442,7 +457,7 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { } public void addNameFilterListener( - IFilterChangedListener iFilterChangedListener) { + IPaginationFilterChangedListener iFilterChangedListener) { nameFilterListener.addListener(iFilterChangedListener); } @@ -471,4 +486,32 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { this.loadChart = loadChart; } + public int getPaginationFilterPageSize() { + return numberOfGroupsByName; + } + + public Combobox getPaginationFilterCombobox() { + if(paginationType == PaginationType.EXTERNAL_PAGINATION) { + return (Combobox) getFellow("filterByNameCombo"); + } + return null; + } + + public enum PaginationType { + /** + * Sets the widget to take care of the pagination of all the LoadTimeLine objects received. + */ + INTERNAL_PAGINATION, + /** + * The widget will only show the combo box but its content has to be configured externally. + * The pagination has to be managed externally too: the widget will show all the LoadTimeLine + * objects received. + */ + EXTERNAL_PAGINATION, + /** + * Disables pagination. Shows all the LoadTimeLine objects received. + */ + NONE; + } + } \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java index 211c69be7..624f2ae25 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java @@ -55,6 +55,10 @@ public class TimeTracker { public Collection selectsSecondLevel( Collection secondLevelDetails); + public Interval getCurrentPaginationInterval(); + + public void resetInterval(); + } private ZoomLevel detailLevel = ZoomLevel.DETAIL_ONE; @@ -80,6 +84,10 @@ public class TimeTracker { private IDetailItemFilter filter = null; + public IDetailItemFilter getFilter() { + return filter; + } + public TimeTracker(Interval interval, ZoomLevel zoomLevel, Component parent) { this(interval, zoomLevel, SeveralModificators.empty(), SeveralModificators.empty(), parent); @@ -115,6 +123,7 @@ public class TimeTracker { public void setFilter(IDetailItemFilter filter) { this.filter = filter; + datesMapper = null; } public ZoomLevel getDetailLevel() { @@ -194,10 +203,20 @@ public class TimeTracker { realIntervalCached = null; } + public void resetMapper() { + datesMapper = null; + realIntervalCached = null; + } + public IDatesMapper getMapper() { if (datesMapper == null) { - datesMapper = new DatesMapperOnInterval(getHorizontalSize(), - getRealInterval()); + if (filter == null) { + datesMapper = new DatesMapperOnInterval(getHorizontalSize(), + getRealInterval()); + } else { + datesMapper = new DatesMapperOnInterval(getHorizontalSize(), + filter.getCurrentPaginationInterval()); + } } return datesMapper; } @@ -306,7 +325,12 @@ public class TimeTracker { private Date startMinusTwoWeeks(Task task) { // the deadline could be before the start - return new LocalDate(min(task.getBeginDate(), task.getDeadline())) - .minusWeeks(2).toDateMidnight().toDate(); + Date start = min(task.getBeginDate(), task.getDeadline()); + // the last consolidated value could be before the start + if (task.getConsolidatedline() != null) { + start = min(start, task.getConsolidatedline()); + } + return new LocalDate(start).minusWeeks(2).toDateMidnight().toDate(); } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTrackerComponent.java b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTrackerComponent.java index 976e20f49..fc0b4edc8 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTrackerComponent.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTrackerComponent.java @@ -57,13 +57,19 @@ public abstract class TimeTrackerComponent extends HtmlMacroComponent { @Override public void zoomLevelChanged(ZoomLevel detailLevel) { - recreate(); + if (isInPage()) { + recreate(); + } } }; this.timeTracker.addZoomListener(zoomListener); timeTrackerElementId = timetrackerId; } + private boolean isInPage() { + return getPage() != null; + } + public int getHorizontalSizePixels() { return timeTracker.getHorizontalSize(); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/DetailSixTimeTrackerState.java b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/DetailSixTimeTrackerState.java new file mode 100644 index 000000000..cb253cb13 --- /dev/null +++ b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/DetailSixTimeTrackerState.java @@ -0,0 +1,110 @@ +/* + * 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.timetracker.zoom; + +import org.joda.time.DateTime; +import org.joda.time.Days; +import org.joda.time.LocalDate; +import org.joda.time.ReadablePeriod; + +/** + * Zoom level for weeks in the first level and days in the second level + * @author Óscar González Fernández + * @author Lorenzo Tilve Álvaro + */ +public class DetailSixTimeTrackerState extends TimeTrackerStateUsingJodaTime { + + private static final int NUMBER_OF_DAYS_MINIMUM = 50; + public static final int FIRST_LEVEL_SIZE = 672; + public static final int SECOND_LEVEL_SIZE = 96; + + DetailSixTimeTrackerState(IDetailItemModificator firstLevelModificator, + IDetailItemModificator secondLevelModificator) { + super(firstLevelModificator, secondLevelModificator); + } + + public final double daysPerPixel() { + return ((double) 1 / SECOND_LEVEL_SIZE); + } + + @Override + protected IDetailItemCreator getDetailItemCreatorFirstLevel() { + return new IDetailItemCreator() { + + @Override + public DetailItem create(DateTime dateTime) { + return new DetailItem(FIRST_LEVEL_SIZE, dateTime + .getWeekOfWeekyear() + + dateTime.toString(", MMM YYYY"), dateTime, dateTime + .plusDays(7)); + } + }; + } + + @Override + protected IDetailItemCreator getDetailItemCreatorSecondLevel() { + return new IDetailItemCreator() { + + @Override + public DetailItem create(DateTime dateTime) { + return new DetailItem(SECOND_LEVEL_SIZE, dateTime + .getDayOfMonth() + + "", dateTime, dateTime.plusDays(1)); + } + }; + } + + @Override + protected ReadablePeriod getPeriodFirstLevel() { + return Days.days(7); + } + + @Override + protected ReadablePeriod getPeriodSecondLevel() { + return Days.days(1); + } + + @Override + protected LocalDate round(LocalDate date, boolean down) { + int dayOfWeek = date.getDayOfWeek(); + if (dayOfWeek == 1) { + return date; + } + return down ? date.withDayOfWeek(1) : date.withDayOfWeek(1) + .plusWeeks(1); + } + + @Override + protected Days getMinimumPeriod() { + return Days.days(NUMBER_OF_DAYS_MINIMUM); + } + + @Override + protected ZoomLevel getZoomLevel() { + return ZoomLevel.DETAIL_FIVE; + } + + @Override + public int getSecondLevelSize() { + return SECOND_LEVEL_SIZE; + } + +} \ No newline at end of file diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java index 0bfcb55d2..931ea0638 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java @@ -89,6 +89,19 @@ public enum ZoomLevel { return new DetailFiveTimeTrackerState(firstLevel, secondLevel); } + @Override + public boolean isSuitableFor(int days) { + return true; + } + }, + DETAIL_SIX(_("Hour")) { + @Override + public TimeTrackerState getTimeTrackerState( + IDetailItemModificator firstLevel, + IDetailItemModificator secondLevel) { + return new DetailSixTimeTrackerState(firstLevel, secondLevel); + } + @Override public boolean isSuitableFor(int days) { return true; diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/util/Interval.java b/ganttzk/src/main/java/org/zkoss/ganttz/util/Interval.java index b39696ee4..0b2765393 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/util/Interval.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/util/Interval.java @@ -78,8 +78,8 @@ public class Interval { public double getProportion(Date date) { if (!isIncluded(date)) { - throw new IllegalArgumentException("date " + date - + " must be between [" + start + "," + finish + "]"); + // Negative proportions are allowed for tasks starting before + // interval so no exception raised } return ((double) date.getTime() - start.getTime()) / lengthBetween; } diff --git a/ganttzk/src/main/resources/i18n/es.po b/ganttzk/src/main/resources/i18n/es.po index c7b895e41..5f0bde914 100644 --- a/ganttzk/src/main/resources/i18n/es.po +++ b/ganttzk/src/main/resources/i18n/es.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-03-08 19:27+0100\n" -"PO-Revision-Date: 2010-03-02 14:13+0100\n" +"POT-Creation-Date: 2010-05-17 02:07+0200\n" +"PO-Revision-Date: 2010-05-17 02:15+0200\n" "Last-Translator: Jacobo Aragunde Pérez \n" "Language-Team: Spanish <>\n" "MIME-Version: 1.0\n" @@ -21,18 +21,103 @@ msgstr "" msgid "Erase" msgstr "Borrar" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:106 -msgid "0% - 100%" -msgstr "0% - 100%" - -#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:303 +#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:297 msgid "Add Dependency" msgstr "Añadir dependencia" +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Worker" +msgstr "Trabajador" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:28 +msgid "Start" +msgstr "Inicio" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:85 +msgid "by criteria" +msgstr "por criterios" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:35 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:21 +msgid "Zoom" +msgstr "Zoom" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Order" +msgstr "Pedido" + +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:214 +msgid "Set End-End" +msgstr "Asignar Fin-Fin" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Task" +msgstr "Tarea" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:84 +msgid "by resources" +msgstr "por recursos" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:64 +msgid "Criterion" +msgstr "Criterio" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:17 +msgid "Print" +msgstr "Imprimir" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:71 +msgid "Week" +msgstr "Semana" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:404 +msgid "filtering by name" +msgstr "filtrando por nombre" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java:108 +msgid "See scheduling" +msgstr "Ver planificación" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 +msgid "End" +msgstr "Fin" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java:221 +msgid "changing zoom" +msgstr "cambiando zoom" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 +msgid "Quarter" +msgstr "Cuarto" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "None" +msgstr "Ninguno" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:41 +msgid "Flatten/Unflatten tree" +msgstr "Aplanar/expandir árbol" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:43 +msgid "Filter" +msgstr "Filtro" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:49 +msgid "Name filter" +msgstr "Filtro por nombres" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:27 +msgid "Name" +msgstr "Nombre" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:37 msgid "Expand/Collapse all" msgstr "Expandir/plegar todo" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:135 +msgid "showing criteria" +msgstr "mostrando criterios" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 msgid "Year" msgstr "Año" @@ -45,31 +130,18 @@ msgstr "Mes" msgid "Show/Hide resources" msgstr "Mostrar/ocultar recursos" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:35 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:21 -msgid "Zoom" -msgstr "Zoom" - #: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:206 msgid "Set End-Start" msgstr "Asignar Fin-Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:214 -msgid "Set End-End" -msgstr "Asignar Fin-Fin" - -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:232 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:245 msgid "decreasing zoom" msgstr "reduciendo zoom" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:443 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:479 msgid "Hide critical path" msgstr "Ocultar camino crítico" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:116 -msgid "+ 100%" -msgstr "+ 100%" - #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:84 msgid "Day" msgstr "Día" @@ -78,52 +150,57 @@ msgstr "Día" msgid "Show/Hide Critical path" msgstr "Mostrar/ocultar camino crítico" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:111 -msgid "100%" -msgstr "100%" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:132 +msgid "showing resources" +msgstr "mostrando recursos" #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:31 msgid "Show/Hide labels" msgstr "Mostrar/ocultar etiquetas" -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:17 -msgid "Print" -msgstr "Imprimir" - -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:101 -msgid "Assignation percentage" -msgstr "Porcentaje asignado" +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 +msgid "Graphics" +msgstr "Gráficas" #: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:210 msgid "Set Start-Start" msgstr "Definir Inicio-Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:71 -msgid "Week" -msgstr "Semana" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java:142 +msgid "See resource allocation" +msgstr "Ver asignación de recursos" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:215 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:228 msgid "increasing zoom" msgstr "aumentando zoom" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java:221 -msgid "changing zoom" -msgstr "cambiando zoom" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:378 +msgid "Show all elements" +msgstr "Mostrar todos los elementos" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 -msgid "Quarter" -msgstr "Cuarto" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:377 +msgid "All" +msgstr "Todos" #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:14 msgid "Refresh" msgstr "Actualizar" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:438 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:474 msgid "Show critical path" msgstr "Mostrar camino crítico" -#~ msgid "Filter" -#~ msgstr "Filtro" +#~ msgid "0% - 100%" +#~ msgstr "0% - 100%" + +#~ msgid "+ 100%" +#~ msgstr "+ 100%" + +#~ msgid "100%" +#~ msgstr "100%" + +#~ msgid "Assignation percentage" +#~ msgstr "Porcentaje asignado" #~ msgid "Critical path" #~ msgstr "Camino crítico" diff --git a/ganttzk/src/main/resources/i18n/gl.po b/ganttzk/src/main/resources/i18n/gl.po index 99848db44..a0eaa143b 100644 --- a/ganttzk/src/main/resources/i18n/gl.po +++ b/ganttzk/src/main/resources/i18n/gl.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-03-08 19:27+0100\n" -"PO-Revision-Date: 2010-03-02 14:13+0100\n" +"POT-Creation-Date: 2010-05-17 02:07+0200\n" +"PO-Revision-Date: 2010-05-17 02:15+0200\n" "Last-Translator: Jacobo Aragunde Pérez \n" "Language-Team: Spanish <>\n" "MIME-Version: 1.0\n" @@ -21,18 +21,103 @@ msgstr "" msgid "Erase" msgstr "Borrar" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:106 -msgid "0% - 100%" -msgstr "0% - 100%" - -#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:303 +#: ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:297 msgid "Add Dependency" msgstr "Engadir dependencia" +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Worker" +msgstr "Traballador" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:28 +msgid "Start" +msgstr "Inicio" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:85 +msgid "by criteria" +msgstr "por criterios" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:35 +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:21 +msgid "Zoom" +msgstr "Zoom" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Order" +msgstr "Pedido" + +#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:214 +msgid "Set End-End" +msgstr "Definir Fin-Fin" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Task" +msgstr "Tarefa" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:84 +msgid "by resources" +msgstr "por recursos" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:64 +msgid "Criterion" +msgstr "Criterio" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:17 +msgid "Print" +msgstr "Imprimir" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:71 +msgid "Week" +msgstr "Semana" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:404 +msgid "filtering by name" +msgstr "filtrando por nome" + +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java:108 +msgid "See scheduling" +msgstr "Ver planificación" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 +msgid "End" +msgstr "Fin" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java:221 +msgid "changing zoom" +msgstr "cambiando zoom" + +#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 +msgid "Quarter" +msgstr "Cuarto" + +#: ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "None" +msgstr "Ningún" + +#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:41 +msgid "Flatten/Unflatten tree" +msgstr "Aplanar/expandir árbore" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:43 +msgid "Filter" +msgstr "Filtro" + +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:49 +msgid "Name filter" +msgstr "Filtro por nomes" + +#: ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:27 +msgid "Name" +msgstr "Nome" + #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:37 msgid "Expand/Collapse all" msgstr "Expandir/pregar todo" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:135 +msgid "showing criteria" +msgstr "mostrando criterios" + #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 msgid "Year" msgstr "Ano" @@ -45,31 +130,18 @@ msgstr "Mes" msgid "Show/Hide resources" msgstr "Mostrar/ocultar recursos" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:35 -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:21 -msgid "Zoom" -msgstr "Zoom" - #: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:206 msgid "Set End-Start" msgstr "Definir Fin-Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:214 -msgid "Set End-End" -msgstr "Definir Fin-Fin" - -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:232 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:245 msgid "decreasing zoom" msgstr "reducindo zoom" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:443 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:479 msgid "Hide critical path" msgstr "Ocultar camiño crítico" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:116 -msgid "+ 100%" -msgstr "+ 100%" - #: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:84 msgid "Day" msgstr "Día" @@ -78,52 +150,57 @@ msgstr "Día" msgid "Show/Hide Critical path" msgstr "Ocultar/ocultar camiño crítico" -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:111 -msgid "100%" -msgstr "100%" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:132 +msgid "showing resources" +msgstr "mostrando recursos" #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:31 msgid "Show/Hide labels" msgstr "Mostrar/ocultar etiquetas" -#: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:17 -msgid "Print" -msgstr "Imprimir" - -#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:101 -msgid "Assignation percentage" -msgstr "Porcentaxe asignado" +#: ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 +msgid "Graphics" +msgstr "Gráficas" #: ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:210 msgid "Set Start-Start" msgstr "Definir Inicio-Inicio" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:71 -msgid "Week" -msgstr "Semana" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java:142 +msgid "See resource allocation" +msgstr "Ver asignación de recursoss" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:215 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:228 msgid "increasing zoom" msgstr "aumentando zoom" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java:221 -msgid "changing zoom" -msgstr "cambiando zoom" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:378 +msgid "Show all elements" +msgstr "Mostrar tódolos elementos" -#: ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 -msgid "Quarter" -msgstr "Cuarto" +#: ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:377 +msgid "All" +msgstr "Todos" #: ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:14 msgid "Refresh" msgstr "Actualizar" -#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:438 +#: ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:474 msgid "Show critical path" msgstr "Mostrar camiño crítico" -#~ msgid "Filter" -#~ msgstr "Filtro" +#~ msgid "0% - 100%" +#~ msgstr "0% - 100%" + +#~ msgid "+ 100%" +#~ msgstr "+ 100%" + +#~ msgid "100%" +#~ msgstr "100%" + +#~ msgid "Assignation percentage" +#~ msgstr "Porcentaxe asignado" #~ msgid "Critical path" #~ msgstr "Camiño crítico" diff --git a/ganttzk/src/main/resources/i18n/keys.pot b/ganttzk/src/main/resources/i18n/keys.pot index 901a5fd68..3f94823f9 100644 --- a/ganttzk/src/main/resources/i18n/keys.pot +++ b/ganttzk/src/main/resources/i18n/keys.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-03-08 19:27+0100\n" +"POT-Creation-Date: 2010-05-17 02:07+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -20,18 +20,103 @@ msgstr "" msgid "Erase" msgstr "" -#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:106 -msgid "0% - 100%" +#: ./ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:297 +msgid "Add Dependency" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/TaskList.java:303 -msgid "Add Dependency" +#: ./ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Worker" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:28 +msgid "Start" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:85 +msgid "by criteria" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:35 +#: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:21 +msgid "Zoom" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Order" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:214 +msgid "Set End-End" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "Task" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:84 +msgid "by resources" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:64 +msgid "Criterion" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:17 +msgid "Print" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:71 +msgid "Week" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:404 +msgid "filtering by name" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java:108 +msgid "See scheduling" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:29 +msgid "End" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java:221 +msgid "changing zoom" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 +msgid "Quarter" +msgstr "" + +#: ./ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/TimeLineRole.java:58 +msgid "None" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:41 +msgid "Flatten/Unflatten tree" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:43 +msgid "Filter" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:49 +msgid "Name filter" +msgstr "" + +#: ./ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul:27 +msgid "Name" msgstr "" #: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:37 msgid "Expand/Collapse all" msgstr "" +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:135 +msgid "showing criteria" +msgstr "" + #: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:32 msgid "Year" msgstr "" @@ -44,31 +129,18 @@ msgstr "" msgid "Show/Hide resources" msgstr "" -#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:35 -#: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:21 -msgid "Zoom" -msgstr "" - #: ./ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:206 msgid "Set End-Start" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:214 -msgid "Set End-End" -msgstr "" - -#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:232 +#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:245 msgid "decreasing zoom" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:443 +#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:479 msgid "Hide critical path" msgstr "" -#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:116 -msgid "+ 100%" -msgstr "" - #: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:84 msgid "Day" msgstr "" @@ -77,46 +149,42 @@ msgstr "" msgid "Show/Hide Critical path" msgstr "" -#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:111 -msgid "100%" +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:132 +msgid "showing resources" msgstr "" #: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:31 msgid "Show/Hide labels" msgstr "" -#: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:17 -msgid "Print" -msgstr "" - -#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:101 -msgid "Assignation percentage" +#: ./ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul:90 +msgid "Graphics" msgstr "" #: ./ganttzk/src/main/java/org/zkoss/ganttz/DependencyList.java:210 msgid "Set Start-Start" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:71 -msgid "Week" +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java:142 +msgid "See resource allocation" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:215 +#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:228 msgid "increasing zoom" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/TimeTracker.java:221 -msgid "changing zoom" +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:378 +msgid "Show all elements" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/timetracker/zoom/ZoomLevel.java:45 -msgid "Quarter" +#: ./ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java:377 +msgid "All" msgstr "" #: ./ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul:14 msgid "Refresh" msgstr "" -#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:438 +#: ./ganttzk/src/main/java/org/zkoss/ganttz/Planner.java:474 msgid "Show critical path" msgstr "" diff --git a/ganttzk/src/main/resources/web/ganttz/img/deadline.png b/ganttzk/src/main/resources/web/ganttz/img/deadline.png index bf951eb5d..68377c08e 100644 Binary files a/ganttzk/src/main/resources/web/ganttz/img/deadline.png and b/ganttzk/src/main/resources/web/ganttz/img/deadline.png differ diff --git a/ganttzk/src/main/resources/web/ganttz/img/deadline_green.png b/ganttzk/src/main/resources/web/ganttz/img/deadline_green.png new file mode 100644 index 000000000..3f181ae13 Binary files /dev/null and b/ganttzk/src/main/resources/web/ganttz/img/deadline_green.png differ diff --git a/ganttzk/src/main/resources/web/ganttz/img/deadline_red.png b/ganttzk/src/main/resources/web/ganttz/img/deadline_red.png new file mode 100644 index 000000000..bf951eb5d Binary files /dev/null and b/ganttzk/src/main/resources/web/ganttz/img/deadline_red.png differ diff --git a/ganttzk/src/main/resources/web/ganttz/row.dsp b/ganttzk/src/main/resources/web/ganttz/row.dsp index 2c170f6d8..b1c21112e 100644 --- a/ganttzk/src/main/resources/web/ganttz/row.dsp +++ b/ganttzk/src/main/resources/web/ganttz/row.dsp @@ -8,4 +8,5 @@ ${z:redraw(child, null)}
+
\ No newline at end of file diff --git a/ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul b/ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul index 2d7b955ae..b059827cf 100644 --- a/ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul +++ b/ganttzk/src/main/resources/web/ganttz/zul/leftTasksTree.zul @@ -17,12 +17,16 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> + + +
- - - + + +
\ No newline at end of file diff --git a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul index 6b36e72a0..b5a2a76eb 100644 --- a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul +++ b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul @@ -40,13 +40,13 @@ resourcesLoadPanel = self; - Filter: + ${i18n:_('Filter')}: - ${i18n:_('Name filter')}: + @@ -60,11 +60,11 @@ resourcesLoadPanel = self; splittable="true" autoscroll="true"> - + - + @@ -78,7 +78,7 @@ resourcesLoadPanel = self;
-
+
@@ -87,7 +87,7 @@ resourcesLoadPanel = self;
-
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 aa11002fa..58b32e45f 100644 --- a/ganttzk/src/main/resources/web/js/ganttz/resourceload/resourcesloadlist.js +++ b/ganttzk/src/main/resources/web/js/ganttz/resourceload/resourcesloadlist.js @@ -20,6 +20,28 @@ zkResourcesLoadList = addResourcesLoadListMethods( {}); +zkResourcesLoadList.WATERMARK_MIN_HEIGHT = 450; +zkResourcesLoadList.WATERMARK_MARGIN_BOTTOM = 40; + + +zkResourcesLoadList.recalculateTimetrackerHeight = function (cmp) { + + zkResourcesLoadList.resourceloadlist = function(elem) { + return YAHOO.util.Selector.query('.resourceloadlist')[0]; + } + + zkResourcesLoadList.firstWatermarkColumn = function(elem) { + return YAHOO.util.Selector.query('.rightpanellayout tr#watermark td')[0]; + } + + if (zkResourcesLoadList.resourceloadlist() != undefined && zkResourcesLoadList.firstWatermarkColumn() != undefined) { + var height = Math.max( + zkResourcesLoadList.resourceloadlist().clientHeight + zkResourcesLoadList.WATERMARK_MARGIN_BOTTOM, + zkResourcesLoadList.WATERMARK_MIN_HEIGHT); + zkResourcesLoadList.firstWatermarkColumn().style.height = height + "px"; + } +} + function addResourcesLoadListMethods(object) { var scrollSync; @@ -65,12 +87,10 @@ function addResourcesLoadListMethods(object) { zkResourcesLoadList.adjustTimeTrackerSize, cmp); scrollSync = new ScrollSync(cmp); scrollSync.synchXChangeTo(timetracker); - listenToScroll(); }; function listenToScroll() { - var timetrackergap_ = timetrackergap(); var scrolledpannel_ = scrolledpannel(); var resourcesloadgraph_ = resourcesloadgraph(); @@ -80,6 +100,7 @@ function addResourcesLoadListMethods(object) { var onScroll = function() { var timeplotcontainer_ = YAHOO.util.Selector.query('canvas.timeplot-canvas')[0]; timeplotcontainer_.style["left"] = "-" + scrolledpannel_.scrollLeft + "px"; + timetrackergap_.style["left"] = "-" + scrolledpannel_.scrollLeft + "px"; leftpanel_.style["top"] = "-" + scrolledpannel_.scrollTop + "px"; resourcesloadgraph_.scrollLeft = scrolledpannel_.scrollLeft; }; @@ -87,17 +108,14 @@ function addResourcesLoadListMethods(object) { rightpanel_.onscroll = onScroll; } + object.adjustTimeTrackerSize = function(cmp) { + zkResourcesLoadList.recalculateTimetrackerHeight(); watermark().style["height"] = cmp.clientHeight + "px"; timetracker().style["width"] = cmp.clientWidth + "px"; /* Set watermark width */ YAHOO.util.Selector.query('.resourceloadlist')[0].style["width"] = YAHOO.util.Selector - .query('.second_level_')[0].clientWidth - + "px"; - YAHOO.util.Selector.query('.rightpanellayout tr#watermark td')[0].style["height"] = - /* Calculate min : taskspanelgap().clientHeight + 120 + 'px'; ) */ - YAHOO.util.Selector.query('.resourceloadlist')[0].clientHeight + 120 - + "px"; + .query('.second_level_')[0].clientWidth + "px"; }; object.adjustResourceLoadRows = function(cmp) { diff --git a/ganttzk/src/main/resources/web/js/ganttz/tasklist.js b/ganttzk/src/main/resources/web/js/ganttz/tasklist.js index 1b46ec0fe..812ee55c4 100644 --- a/ganttzk/src/main/resources/web/js/ganttz/tasklist.js +++ b/ganttzk/src/main/resources/web/js/ganttz/tasklist.js @@ -407,6 +407,20 @@ zkTask.setAttr = function(cmp, name, val) { } }; +zkTask.setAttr = function(cmp, name, val) { + switch (name) { + case "taskTooltipText":{ + var taskTooltipTextElement = YAHOO.util.Selector.query( + '.task_tooltip', cmp, true); + taskTooltipTextElement.innerHTML = val; + return true; + } + default: { + return false; + } + } +}; + zkTask.addDependency = function(cmp) { zkTask.createArrow(cmp); }; @@ -567,11 +581,19 @@ zkTask.getElementsByAttribute = function(oElm, strTagName, strAttributeName, return arrReturnElements; } + zkTask.moveDeadline = function(cmp, width) { var deadlineDiv = zkTask.next(cmp); deadlineDiv["style"].left = width; } +zkTask.moveConsolidatedline = function(cmp, width) { + var deadlineDiv = zkTask.next(cmp); + var consolidatedlineDiv = zkTask.next(deadlineDiv); + consolidatedlineDiv["style"].left = width; +} + + zkTask.resizeCompletionAdvance = function(cmp, width) { var completionDiv = YAHOO.util.Selector.query('.completion', cmp, true); completionDiv["style"].width = width; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceAssignment.java index c8dea68ac..eb96248f7 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceAssignment.java @@ -43,6 +43,9 @@ public abstract class AdvanceAssignment extends BaseEntity { public void setReportGlobalAdvance(boolean reportGlobalAdvance) { this.reportGlobalAdvance = reportGlobalAdvance; + if (this.orderElement != null) { + this.orderElement.markAsDirtyLastAdvanceMeasurementForSpreading(); + } } public boolean getReportGlobalAdvance() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java index 62937f18e..6b3363603 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java @@ -86,6 +86,10 @@ public class AdvanceMeasurement extends BaseEntity { this.value.setScale(2, BigDecimal.ROUND_DOWN); } resetCommunicationDate(); + if (advanceAssignment != null) { + advanceAssignment.getOrderElement() + .markAsDirtyLastAdvanceMeasurementForSpreading(); + } } @NotNull(message = "value not specified") diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java index 9980ce442..e08efc86f 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java @@ -101,6 +101,7 @@ public class DirectAdvanceAssignment extends AdvanceAssignment { } public AdvanceMeasurement getLastAdvanceMeasurement() { + if (advanceMeasurements.isEmpty()) { return null; } @@ -136,13 +137,14 @@ public class DirectAdvanceAssignment extends AdvanceAssignment { if (advanceMeasurement == null) { return BigDecimal.ZERO; } - return advanceMeasurement.getValue().setScale(2).divide(maxValue, + return advanceMeasurement.getValue().divide(maxValue, 2, RoundingMode.DOWN); } public void addAdvanceMeasurements(AdvanceMeasurement advanceMeasurement) { this.advanceMeasurements.add(advanceMeasurement); advanceMeasurement.setAdvanceAssignment(this); + getOrderElement().markAsDirtyLastAdvanceMeasurementForSpreading(); } public AdvanceMeasurement getAdvanceMeasurementAtExactDate(LocalDate date) { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/ProportionalDistributor.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/ProportionalDistributor.java index 1fed4ddbe..3893a6fd0 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/ProportionalDistributor.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/ProportionalDistributor.java @@ -88,6 +88,9 @@ public class ProportionalDistributor { } public int[] distribute(final int total) { + if (proportions.length == 0) { + return new int[0]; + } int[] result = new int[proportions.length]; int remaining = total - assignIntegerParts(total, result); if (remaining == 0) { @@ -117,7 +120,8 @@ public class ProportionalDistributor { .transform(difference(currentProportions)); Collections.sort(transform, Collections.reverseOrder()); for (int i = 0; i < remaining; i++) { - ProportionWithPosition proportionWithPosition = transform.get(i); + ProportionWithPosition proportionWithPosition = transform.get(i + % currentProportions.length); result[proportionWithPosition.position] = result[proportionWithPosition.position] + 1; } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java index 8ab979612..31a6b0019 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/Order.java @@ -55,16 +55,12 @@ public class Order extends OrderLineGroup { public static Order create() { Order order = new Order(); order.setNewObject(true); - - OrderLineGroup.setupOrderLineGroup(order); - return order; } public static Order createUnvalidated(String code) { Order order = create(new Order(), code); - OrderLineGroup.setupOrderLineGroup(order); return order; } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java index 496bca253..2d1f2916a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderElement.java @@ -103,6 +103,10 @@ public abstract class OrderElement extends IntegrationEntity implements private OrderElementTemplate template; + private BigDecimal lastAdvanceMeausurementForSpreading = BigDecimal.ZERO; + + private Boolean dirtyLastAdvanceMeasurementForSpreading = true; + public OrderElementTemplate getTemplate() { return template; } @@ -502,9 +506,12 @@ public abstract class OrderElement extends IntegrationEntity implements public DirectAdvanceAssignment getDirectAdvanceAssignmentByType( AdvanceType advanceType) { - for (DirectAdvanceAssignment directAdvanceAssignment : getDirectAdvanceAssignments()) { - if (directAdvanceAssignment.getAdvanceType().equals(advanceType)) { + if (advanceType != null) { + for (DirectAdvanceAssignment directAdvanceAssignment : getDirectAdvanceAssignments()) { + if (directAdvanceAssignment.getAdvanceType().getId().equals( + advanceType.getId())) { return directAdvanceAssignment; + } } } return null; @@ -532,6 +539,7 @@ public abstract class OrderElement extends IntegrationEntity implements if (this.getParent() != null) { this.getParent().removeIndirectAdvanceAssignment( advanceAssignment.getAdvanceType()); + removeChildrenAdvanceInParents(this.getParent()); } } } @@ -584,11 +592,41 @@ public abstract class OrderElement extends IntegrationEntity implements this.directAdvanceAssignments.add(newAdvanceAssignment); if (this.getParent() != null) { + addChildrenAdvanceInParents(this.getParent()); this.getParent().addIndirectAdvanceAssignment( newAdvanceAssignment.createIndirectAdvanceFor(this.getParent())); } } + public void addChildrenAdvanceInParents(OrderLineGroup parent) { + if ((parent != null) && (!parent.existChildrenAdvance())) { + parent.addChildrenAdvanceOrderLineGroup(); + addChildrenAdvanceInParents(parent.getParent()); + } + + } + + public void removeChildrenAdvanceInParents(OrderLineGroup parent) { + if ((parent != null) && (parent.existChildrenAdvance()) + && (!itsChildsHasAdvances(parent))) { + parent.removeChildrenAdvanceOrderLineGroup(); + removeChildrenAdvanceInParents(parent.getParent()); + } + } + + private boolean itsChildsHasAdvances(OrderElement orderElement) { + for (OrderElement child : orderElement.getChildren()) { + if ((!child.getIndirectAdvanceAssignments().isEmpty()) + || (!child.getDirectAdvanceAssignments().isEmpty())) { + return true; + } + if (itsChildsHasAdvances(child)) { + return true; + } + } + return false; + } + protected void checkNoOtherGlobalAdvanceAssignment( DirectAdvanceAssignment newAdvanceAssignment) throws DuplicateValueTrueReportGlobalAdvanceException { @@ -616,7 +654,9 @@ public abstract class OrderElement extends IntegrationEntity implements OrderElement orderElement, DirectAdvanceAssignment newAdvanceAssignment) throws DuplicateAdvanceAssignmentForOrderElementException { - for (DirectAdvanceAssignment directAdvanceAssignment : orderElement.directAdvanceAssignments) { + for (DirectAdvanceAssignment directAdvanceAssignment : orderElement + .getDirectAdvanceAssignments()) { + if (AdvanceType.equivalentInDB(directAdvanceAssignment .getAdvanceType(), newAdvanceAssignment.getAdvanceType())) { throw new DuplicateAdvanceAssignmentForOrderElementException( @@ -642,7 +682,8 @@ public abstract class OrderElement extends IntegrationEntity implements OrderElement orderElement, DirectAdvanceAssignment newAdvanceAssignment) throws DuplicateAdvanceAssignmentForOrderElementException { - for (DirectAdvanceAssignment directAdvanceAssignment : orderElement.directAdvanceAssignments) { + for (DirectAdvanceAssignment directAdvanceAssignment : orderElement + .getDirectAdvanceAssignments()) { if (AdvanceType.equivalentInDB(directAdvanceAssignment .getAdvanceType(), newAdvanceAssignment.getAdvanceType())) { throw new DuplicateAdvanceAssignmentForOrderElementException( @@ -660,13 +701,23 @@ public abstract class OrderElement extends IntegrationEntity implements } public BigDecimal getAdvancePercentage() { - return getAdvancePercentage(null); + if ((dirtyLastAdvanceMeasurementForSpreading == null) + || dirtyLastAdvanceMeasurementForSpreading) { + lastAdvanceMeausurementForSpreading = getAdvancePercentage(null); + dirtyLastAdvanceMeasurementForSpreading = false; + } + return lastAdvanceMeausurementForSpreading; } public abstract BigDecimal getAdvancePercentage(LocalDate date); public abstract Set getIndirectAdvanceAssignments(); + public abstract DirectAdvanceAssignment calculateFakeDirectAdvanceAssignment( + IndirectAdvanceAssignment indirectAdvanceAssignment); + + public abstract BigDecimal getAdvancePercentageChildren(); + public List getAllChildren() { List children = getChildren(); List result = new ArrayList(); @@ -1196,4 +1247,12 @@ public abstract class OrderElement extends IntegrationEntity implements protected IIntegrationEntityDAO getIntegrationEntityDAO() { return Registry.getOrderElementDAO(); } + + public void markAsDirtyLastAdvanceMeasurementForSpreading() { + if (parent != null) { + parent.markAsDirtyLastAdvanceMeasurementForSpreading(); + } + dirtyLastAdvanceMeasurementForSpreading = true; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java index c3dbeef1e..6ffc81c90 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLine.java @@ -321,6 +321,17 @@ public class OrderLine extends OrderElement { return OrderLineTemplate.create(this); } + @Override + public DirectAdvanceAssignment calculateFakeDirectAdvanceAssignment( + IndirectAdvanceAssignment indirectAdvanceAssignment) { + return null; + } + + @Override + public BigDecimal getAdvancePercentageChildren() { + return BigDecimal.ZERO; + } + @Override public Set getIndirectAdvanceAssignments() { return Collections.emptySet(); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLineGroup.java b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLineGroup.java index f70bd9520..a35272554 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLineGroup.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/orders/entities/OrderLineGroup.java @@ -108,25 +108,69 @@ public class OrderLineGroup extends OrderElement implements public static OrderLineGroup create() { OrderLineGroup result = new OrderLineGroup(); result.setNewObject(true); - - setupOrderLineGroup(result); - return result; } public static OrderLineGroup createUnvalidated(String code) { OrderLineGroup orderLineGroup = create(new OrderLineGroup(), code); - setupOrderLineGroup(orderLineGroup); return orderLineGroup; } - protected static void setupOrderLineGroup(OrderLineGroup result) { + public void addChildrenAdvanceOrderLineGroup() { + boolean spread = (getReportGlobalAdvanceAssignment() == null); IndirectAdvanceAssignment indirectAdvanceAssignment = IndirectAdvanceAssignment - .create(true); + .create(spread); AdvanceType advanceType = PredefinedAdvancedTypes.CHILDREN.getType(); indirectAdvanceAssignment.setAdvanceType(advanceType); - indirectAdvanceAssignment.setOrderElement(result); - result.addIndirectAdvanceAssignment(indirectAdvanceAssignment); + indirectAdvanceAssignment.setOrderElement(this); + addIndirectAdvanceAssignment(indirectAdvanceAssignment); + } + + public void removeChildrenAdvanceOrderLineGroup() { + for (IndirectAdvanceAssignment advance : getIndirectAdvanceAssignments()) { + if (advance.getAdvanceType().getUnitName().equals( + PredefinedAdvancedTypes.CHILDREN.getTypeName())) { + indirectAdvanceAssignments.remove(advance); + updateSpreadAdvance(); + } + } + } + + private void updateSpreadAdvance(){ + if(getReportGlobalAdvanceAssignment() == null){ + AdvanceType type = PredefinedAdvancedTypes.PERCENTAGE.getType(); + DirectAdvanceAssignment advancePercentage = getAdvanceAssignmentByType(type); + if(advancePercentage != null) { + if(advancePercentage.isFake()){ + for (IndirectAdvanceAssignment each : getIndirectAdvanceAssignments()) { + if (type != null && each.getAdvanceType().getId().equals(type.getId())) { + each.setReportGlobalAdvance(true); + } + } + }else{ + advancePercentage.setReportGlobalAdvance(true); + } + } else { + for (DirectAdvanceAssignment advance : getDirectAdvanceAssignments()) { + advance.setReportGlobalAdvance(true); + return; + } + for (IndirectAdvanceAssignment advance : getIndirectAdvanceAssignments()) { + advance.setReportGlobalAdvance(true); + return; + } + } + } + } + + public boolean existChildrenAdvance() { + for (IndirectAdvanceAssignment advance : getIndirectAdvanceAssignments()) { + if (advance.getAdvanceType().getUnitName().equals( + PredefinedAdvancedTypes.CHILDREN.getTypeName())) { + return true; + } + } + return false; } private List children = new ArrayList(); @@ -301,6 +345,7 @@ public class OrderLineGroup extends OrderElement implements return BigDecimal.ZERO; } + @Override public BigDecimal getAdvancePercentageChildren() { return getAdvancePercentageChildren(null); } @@ -329,6 +374,7 @@ public class OrderLineGroup extends OrderElement implements return result; } + @Override public DirectAdvanceAssignment calculateFakeDirectAdvanceAssignment( IndirectAdvanceAssignment indirectAdvanceAssignment) { if (indirectAdvanceAssignment.getAdvanceType().getUnitName().equals( 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 5a8654363..540dd4431 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 @@ -151,15 +151,19 @@ public class DayAssignmentDAO extends GenericDAOHibernate @Override public List findByResources(Scenario scenario, List resources) { + // TODO incorporate scenario filtering to the query instead of doing it + // in memory + return DayAssignment.withScenario(scenario, findByResources(resources)); + } + + @Override + public List findByResources(List resources) { if (resources.isEmpty()) { return Collections.emptyList(); } Criteria criteria = getSession().createCriteria(DayAssignment.class) .add(Restrictions.in("resource", resources)); - List list = criteria.list(); - // TODO incorporate scenario filtering to the query instead of doing it - // in memory - return DayAssignment.withScenario(scenario, list); + return (List) criteria.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 index 6117fe707..caa4f6fb2 100644 --- 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 @@ -4,8 +4,8 @@ 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.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; 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 4c46766af..f6c985cb6 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 @@ -50,4 +50,6 @@ public interface IDayAssignmentDAO extends IGenericDAO { public List findByResources(Scenario scenario, List resources); + public List findByResources(List resources); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/AvailabilityCalculator.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/AvailabilityCalculator.java index 1c901a079..11dedac7c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/AvailabilityCalculator.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/AvailabilityCalculator.java @@ -63,7 +63,7 @@ public class AvailabilityCalculator { return result.and(getCriterionsAvailabilityFor(criterions, each)); } - private static AvailabilityTimeLine getCriterionsAvailabilityFor( + public static AvailabilityTimeLine getCriterionsAvailabilityFor( Collection criterions, Resource resource) { AvailabilityTimeLine result = AvailabilityTimeLine.allValid(); for (Criterion each : criterions) { 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 deleted file mode 100644 index 393cff5e2..000000000 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DateAndHour.java +++ /dev/null @@ -1,83 +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.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/DayAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DayAssignment.java index c5905092c..8ae93dc86 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DayAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DayAssignment.java @@ -168,7 +168,7 @@ public abstract class DayAssignment extends BaseEntity { @OnCopy(Strategy.SHARE) private Resource resource; - private Boolean consolidated; + private Boolean consolidated = false; protected DayAssignment() { @@ -237,8 +237,11 @@ public abstract class DayAssignment extends BaseEntity { final void detach() { getResource().removeAssignments(Arrays.asList(this)); + detachFromAllocation(); } + protected abstract void detachFromAllocation(); + public abstract boolean belongsTo(Object allocation); /** @@ -247,4 +250,6 @@ public abstract class DayAssignment extends BaseEntity { */ public abstract Scenario getScenario(); + public abstract DayAssignment withHours(int newHours); + } 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 9742f9426..6fed85d01 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 @@ -20,8 +20,11 @@ package org.navalplanner.business.planner.entities; +import java.util.Date; + import org.apache.commons.lang.Validate; import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; /** * Entity which represents an associated with properties @@ -33,7 +36,54 @@ import org.navalplanner.business.common.BaseEntity; public class Dependency extends BaseEntity { public enum Type { - END_START, START_START, END_END, START_END; + END_START { + @Override + public boolean modifiesDestinationStart() { + return true; + } + + @Override + public boolean modifiesDestinationEnd() { + return false; + } + }, + START_START { + @Override + public boolean modifiesDestinationStart() { + return true; + } + + @Override + public boolean modifiesDestinationEnd() { + return false; + } + }, + END_END { + @Override + public boolean modifiesDestinationStart() { + return false; + } + + @Override + public boolean modifiesDestinationEnd() { + return true; + } + }, + START_END { + @Override + public boolean modifiesDestinationStart() { + return false; + } + + @Override + public boolean modifiesDestinationEnd() { + return true; + } + }; + + public abstract boolean modifiesDestinationStart(); + + public abstract boolean modifiesDestinationEnd(); } public static Dependency create(TaskElement origin, @@ -98,4 +148,17 @@ public class Dependency extends BaseEntity { public boolean hasLimitedQueueDependencyAssociated() { return queueDependency != null; } + + public Date getDateFromOrigin() { + switch (type) { + case END_START: + case END_END: + return origin.getEndDate(); + case START_END: + case START_START: + return origin.getStartDate(); + default: + throw new RuntimeException("unexpected type"); + } + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DerivedDayAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DerivedDayAssignment.java index d04369831..46f830e3a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DerivedDayAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DerivedDayAssignment.java @@ -90,6 +90,20 @@ public class DerivedDayAssignment extends DayAssignment { } } + private class DetachedState extends ParentState { + + @Override + protected DerivedAllocation getAllocation() { + return null; + } + + @Override + public Scenario getScenario() { + return null; + } + + } + @NotNull private DerivedDayAssignmentsContainer container; @@ -145,4 +159,14 @@ public class DerivedDayAssignment extends DayAssignment { return parentState.getScenario(); } + @Override + public DayAssignment withHours(int newHours) { + throw new UnsupportedOperationException(); + } + + @Override + protected void detachFromAllocation() { + this.parentState = new DetachedState(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericDayAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericDayAssignment.java index d3f16374c..2d071cc78 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericDayAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/GenericDayAssignment.java @@ -114,6 +114,7 @@ public class GenericDayAssignment extends DayAssignment { } } + public static GenericDayAssignment create(LocalDate day, int hours, Resource resource) { GenericDayAssignment result = new GenericDayAssignment(day, hours, @@ -127,6 +128,7 @@ public class GenericDayAssignment extends DayAssignment { Set result = new HashSet(); for (GenericDayAssignment a : assignemnts) { GenericDayAssignment created = copy(newParent, a); + created.associateToResource(); result.add(created); } return result; @@ -137,6 +139,7 @@ public class GenericDayAssignment extends DayAssignment { GenericDayAssignment toBeCopied) { GenericDayAssignment result = create(toBeCopied.getDay(), toBeCopied .getHours(), toBeCopied.getResource()); + result.setConsolidated(toBeCopied.isConsolidated()); result.parentState = result.parentState.setParent(newParent); result.associateToResource(); return result; @@ -170,6 +173,7 @@ public class GenericDayAssignment extends DayAssignment { } protected void detachFromAllocation() { + this.parentState = new ContainerNotSpecified(); } @Override @@ -188,4 +192,14 @@ public class GenericDayAssignment extends DayAssignment { return parentState.getScenario(); } + public DayAssignment withHours(int newHours) { + GenericDayAssignment result = create(getDay(), newHours, getResource()); + if (container != null) { + result.parentState.setParent(container); + } else if (this.getGenericResourceAllocation() != null) { + result.parentState.setParent(this.getGenericResourceAllocation()); + } + return result; + } + } 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 eedf5dc6c..b05e495f5 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 @@ -406,14 +406,6 @@ public class GenericResourceAllocation extends return result; } - public List getNonConsolidatedAssignments() { - return getDayAssignmentsByConsolidated(false); - } - - public List getConsolidatedAssignments() { - return getDayAssignmentsByConsolidated(true); - } - @Override protected Class getDayAssignmentType() { return GenericDayAssignment.class; @@ -532,4 +524,11 @@ public class GenericResourceAllocation extends } } + public void overrideConsolidatedDayAssignments( + GenericResourceAllocation origin) { + if (origin != null) { + resetAssignmentsTo(origin.getConsolidatedAssignments()); + } + } + } 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 deleted file mode 100644 index b6b7c2e20..000000000 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceAllocator.java +++ /dev/null @@ -1,285 +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.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 deleted file mode 100644 index 6af565b50..000000000 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueDependency.java +++ /dev/null @@ -1,103 +0,0 @@ -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/LimitingResourceQueueElementGap.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java deleted file mode 100644 index b74e51ff2..000000000 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java +++ /dev/null @@ -1,123 +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.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 bd9e993b2..ed61ca957 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 @@ -21,12 +21,14 @@ package org.navalplanner.business.planner.entities; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -49,6 +51,7 @@ import org.navalplanner.business.planner.entities.allocationalgorithms.Allocator import org.navalplanner.business.planner.entities.allocationalgorithms.AllocatorForTaskDurationAndSpecifiedResourcesPerDay; import org.navalplanner.business.planner.entities.allocationalgorithms.HoursModification; import org.navalplanner.business.planner.entities.allocationalgorithms.ResourcesPerDayModification; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Machine; import org.navalplanner.business.resources.entities.MachineWorkersConfigurationUnit; @@ -307,6 +310,38 @@ public abstract class ResourceAllocation extends private int originalTotalAssignment = 0; + private IOnDayAssignmentRemoval dayAssignmenteRemoval = new DoNothing(); + + public interface IOnDayAssignmentRemoval { + + public void onRemoval(ResourceAllocation allocation, + DayAssignment assignment); + } + + public static class DoNothing implements IOnDayAssignmentRemoval { + + @Override + public void onRemoval( + ResourceAllocation allocation, DayAssignment assignment) { + } + } + + public static class DetachDayAssignmentOnRemoval implements + IOnDayAssignmentRemoval { + + @Override + public void onRemoval(ResourceAllocation allocation, + DayAssignment assignment) { + assignment.detach(); + } + } + + public void setOnDayAssignmentRemoval( + IOnDayAssignmentRemoval dayAssignmentRemoval) { + Validate.notNull(dayAssignmentRemoval); + this.dayAssignmenteRemoval = dayAssignmentRemoval; + } + /** * Constructor for hibernate. Do not use! */ @@ -365,8 +400,19 @@ public abstract class ResourceAllocation extends return task; } - public void setOriginalTotalAssigment(int originalTotalAssigment) { - this.originalTotalAssignment = originalTotalAssigment; + private void updateOriginalTotalAssigment() { + if ((task.getConsolidation() == null) + || (task.getConsolidation().getConsolidatedValues().isEmpty())) { + originalTotalAssignment = getNonConsolidatedHours(); + } else { + BigDecimal lastConslidation = task.getConsolidation() + .getConsolidatedValues().last().getValue(); + originalTotalAssignment = new BigDecimal(getNonConsolidatedHours()) + .divide( + BigDecimal.ONE.subtract(lastConslidation.divide( + new BigDecimal(100), RoundingMode.DOWN)), + RoundingMode.DOWN).intValue(); + } } @Min(0) @@ -414,14 +460,18 @@ public abstract class ResourceAllocation extends @Override public void allocate(ResourcesPerDay resourcesPerDay) { Task currentTask = getTask(); - LocalDate startInclusive = new LocalDate(currentTask + LocalDate taskStart = LocalDate.fromDateFields(currentTask .getStartDate()); + LocalDate startInclusive = (currentTask + .getFirstDayNotConsolidated().compareTo(taskStart) >= 0) ? currentTask + .getFirstDayNotConsolidated() + : taskStart; List assignmentsCreated = createAssignments( - resourcesPerDay, startInclusive, - endExclusive); + resourcesPerDay, startInclusive, endExclusive); resetAssignmentsTo(assignmentsCreated); setResourcesPerDay(calculateResourcesPerDayFromAssignments()); } + }; } @@ -476,7 +526,10 @@ public abstract class ResourceAllocation extends } private void allocate(LocalDate end, int hours) { - LocalDate startInclusive = new LocalDate(getTask().getStartDate()); + LocalDate taskStart = LocalDate.fromDateFields(task.getStartDate()); + LocalDate startInclusive = (task.getFirstDayNotConsolidated() + .compareTo(taskStart) >= 0) ? task + .getFirstDayNotConsolidated() : taskStart; List assignmentsCreated = createAssignments(startInclusive, end, hours); resetAssignmentsTo(assignmentsCreated); @@ -485,12 +538,12 @@ public abstract class ResourceAllocation extends private void allocate(LocalDate startInclusive, LocalDate endExclusive, int hours) { + LocalDate firstDayNotConsolidated = getTask().getFirstDayNotConsolidated(); + LocalDate start = startInclusive.compareTo(firstDayNotConsolidated) >= 0 ? startInclusive + : firstDayNotConsolidated; List assignmentsCreated = createAssignments(startInclusive, endExclusive, hours); - removingAssignments(getAssignments(startInclusive, endExclusive)); - addingAssignments(assignmentsCreated); - setResourcesPerDay(calculateResourcesPerDayFromAssignments()); - setOriginalTotalAssigment(getAssignedHours()); + resetAssigmentsForInterval(start, endExclusive, assignmentsCreated); } protected abstract AvailabilityTimeLine getResourcesAvailability(); @@ -596,10 +649,31 @@ public abstract class ResourceAllocation extends protected abstract void copyAssignments(Scenario from, Scenario to); - private void resetAssignmentsTo(List assignments) { - removingAssignments(getAssignments()); + protected void resetAssignmentsTo(List assignments) { + removingAssignments((List) removeConsolidated(getAssignments())); addingAssignments(assignments); - setOriginalTotalAssigment(getAssignedHours()); + updateOriginalTotalAssigment(); + } + + protected void resetAssigmentsForInterval(LocalDate startInclusive, + LocalDate endExclusive, List assignmentsCreated) { + removingAssignments(removeConsolidated(getAssignments(startInclusive, + endExclusive))); + addingAssignments(assignmentsCreated); + setResourcesPerDay(calculateResourcesPerDayFromAssignments(getAssignments())); + updateOriginalTotalAssigment(); + } + + private List removeConsolidated( + List assignments) { + for (Iterator iterator = assignments + .iterator(); iterator.hasNext();) { + DayAssignment dayAssignment = (DayAssignment) iterator.next(); + if (dayAssignment.isConsolidated()) { + iterator.remove(); + } + } + return assignments; } protected final void addingAssignments(Collection assignments) { @@ -610,13 +684,14 @@ public abstract class ResourceAllocation extends allocateLimitingDayAssignments(Collections.emptyList()); } - public void allocateLimitingDayAssignments(List assignments) { + @SuppressWarnings("unchecked") + public void allocateLimitingDayAssignments(List assignments) { assert isLimiting(); - resetAssignmentsTo(assignments); + resetAssignmentsTo((List) assignments); } - protected final void removingAssignments( - List assignments){ + private void removingAssignments( + List assignments) { getDayAssignmentsState().removingAssignments(assignments); } @@ -776,6 +851,9 @@ public abstract class ResourceAllocation extends List assignments){ removeAssignments(assignments); clearCachedData(); + for (DayAssignment each : assignments) { + dayAssignmenteRemoval.onRemoval(ResourceAllocation.this, each); + } } protected abstract void removeAssignments( @@ -877,11 +955,11 @@ public abstract class ResourceAllocation extends return getDayAssignmentsState().getOrderedDayAssignments(); } - public List getNonConsolidatedAssignments(){ + public List getNonConsolidatedAssignments() { return getDayAssignmentsByConsolidated(false); } - public List getConsolidatedAssignments() { + public List getConsolidatedAssignments() { return getDayAssignmentsByConsolidated(true); } @@ -939,6 +1017,14 @@ public abstract class ResourceAllocation extends return Collections.unmodifiableSet(derivedAllocations); } + public LocalDate getStartConsideringAssignments() { + List assignments = getAssignments(); + if (assignments.isEmpty()) { + return getStartDate(); + } + return assignments.get(0).getDay(); + } + public LocalDate getStartDate() { return LocalDate.fromDateFields(task.getStartDate()); } @@ -1011,7 +1097,7 @@ public abstract class ResourceAllocation extends switchToScenario(scenario); mergeAssignments(modifications); setResourcesPerDay(modifications.getResourcesPerDay()); - setOriginalTotalAssigment(modifications.getOriginalTotalAssigment()); + updateOriginalTotalAssigment(); setWithoutApply(modifications.getAssignmentFunction()); mergeDerivedAllocations(scenario, modifications.getDerivedAllocations()); } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificDayAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificDayAssignment.java index 7683f389b..e3700899a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificDayAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificDayAssignment.java @@ -115,6 +115,7 @@ public class SpecificDayAssignment extends DayAssignment { } } + public static Set copy( SpecificDayAssignmentsContainer container, Collection specificDaysAssignment) { @@ -123,6 +124,7 @@ public class SpecificDayAssignment extends DayAssignment { SpecificDayAssignment created = create(s.getDay(), s.getHours(), s .getResource()); created.parentState = created.parentState.setParent(container); + created.setConsolidated(s.isConsolidated()); created.associateToResource(); result.add(created); } @@ -173,4 +175,20 @@ public class SpecificDayAssignment extends DayAssignment { public Scenario getScenario() { return parentState.getScenario(); } + + @Override + public DayAssignment withHours(int newHours) { + SpecificDayAssignment result = create(getDay(), newHours, getResource()); + if (container != null) { + result.parentState.setParent(container); + } else if (this.getSpecificResourceAllocation() != null) { + result.parentState.setParent(this.getSpecificResourceAllocation()); + } + return result; + } + + @Override + protected void detachFromAllocation() { + this.parentState = new ContainerNotSpecified(); + } } 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 ca05054f9..58afc2d27 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 @@ -37,8 +37,10 @@ import org.joda.time.LocalDate; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine; import org.navalplanner.business.calendars.entities.CombinedWorkHours; import org.navalplanner.business.calendars.entities.IWorkHours; +import org.navalplanner.business.common.ProportionalDistributor; import org.navalplanner.business.planner.entities.allocationalgorithms.HoursModification; import org.navalplanner.business.planner.entities.allocationalgorithms.ResourcesPerDayModification; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.resources.entities.Worker; @@ -425,4 +427,41 @@ public class SpecificResourceAllocation extends } } + public void allocateKeepingProportions(LocalDate start, + LocalDate endExclusive, int newHoursForInterval) { + List assignments = getAssignments(start, endExclusive); + ProportionalDistributor distributor = ProportionalDistributor + .create(asHours(assignments)); + int[] newHoursPerDay = distributor.distribute(newHoursForInterval); + resetAssigmentsForInterval(start, endExclusive, assignmentsForNewHours( + assignments, newHoursPerDay)); + } + + private List assignmentsForNewHours( + List assignments, int[] newHoursPerDay) { + List result = new ArrayList(); + int i = 0; + for (DayAssignment each : assignments) { + result.add(SpecificDayAssignment.create(each.getDay(), + newHoursPerDay[i++], resource)); + } + return result; + } + + private int[] asHours(List assignments) { + int[] result = new int[assignments.size()]; + int i = 0; + for (DayAssignment each : assignments) { + result[i++] = each.getHours(); + } + return result; + } + + public void overrideConsolidatedDayAssignments( + SpecificResourceAllocation origin) { + if (origin != null) { + resetAssignmentsTo(origin.getConsolidatedAssignments()); + } + } + } 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 6463edd52..e362e3862 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 @@ -45,6 +45,7 @@ import org.navalplanner.business.planner.entities.DerivedAllocationGenerator.IWo import org.navalplanner.business.planner.entities.allocationalgorithms.HoursModification; import org.navalplanner.business.planner.entities.allocationalgorithms.ResourcesPerDayModification; import org.navalplanner.business.planner.entities.consolidations.Consolidation; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.Resource; @@ -188,6 +189,14 @@ public class Task extends TaskElement { return (resourceAllocations.size() > 0) ? resourceAllocations.iterator().next() : null; } + public LimitingResourceQueueElement getAssociatedLimitingResourceQueueElementIfAny() { + if (!isLimiting()) { + throw new IllegalStateException("this is not a limiting task"); + } + return getAssociatedLimitingResourceAllocation() + .getLimitingResourceQueueElement(); + } + public boolean isLimitingAndHasDayAssignments() { ResourceAllocation resourceAllocation = getAssociatedLimitingResourceAllocation(); return (resourceAllocation != null) ? resourceAllocation.isLimitingAndHasDayAssignments() : false; @@ -449,14 +458,8 @@ public class Task extends TaskElement { reassign(scenario, new WithAnotherResources(resourceDAO)); } - private void reassign(Scenario onScenario, - AllocationModificationStrategy strategy) { + private void reassign(Scenario onScenario, AllocationModificationStrategy strategy) { if (isLimiting()) { - Set> resourceAllocations = getSatisfiedResourceAllocations(); - ResourceAllocation resourceAlloation = resourceAllocations - .iterator().next(); - resourceAlloation.getLimitingResourceQueueElement() - .setEarlierStartDateBecauseOfGantt(getStartDate()); return; } List copied = ModifiedAllocation.copy(onScenario, @@ -624,4 +627,15 @@ public class Task extends TaskElement { public boolean hasConsolidations() { return ((consolidation != null) && (!consolidation.isEmpty())); } + + public LocalDate getFirstDayNotConsolidated() { + if (consolidation != null) { + LocalDate until = consolidation.getConsolidatedUntil(); + if (until != null) { + return until.plusDays(1); + } + } + return LocalDate.fromDateFields(getStartDate()); + } + } 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 c6aa87e93..a369ec0bf 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 @@ -20,6 +20,7 @@ package org.navalplanner.business.planner.entities; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -130,6 +131,8 @@ public abstract class TaskElement extends BaseEntity { private TaskSource taskSource; + private BigDecimal advancePercentage = BigDecimal.ZERO; + public void initializeEndDateIfDoesntExist() { if (getEndDate() == null) { initializeEndDate(); @@ -515,4 +518,13 @@ public abstract class TaskElement extends BaseEntity { } } + public BigDecimal getAdvancePercentage() { + return (advancePercentage == null) ? BigDecimal.ZERO + : advancePercentage; + } + + public void setAdvancePercentage(BigDecimal advancePercentage) { + this.advancePercentage = advancePercentage; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java index 09e224e23..9de4ba272 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/allocationalgorithms/AllocatorForSpecifiedResourcesPerDayAndHours.java @@ -59,7 +59,10 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours { } public LocalDate untilAllocating(int hoursToAllocate) { - LocalDate start = LocalDate.fromDateFields(task.getStartDate()); + LocalDate taskStart = LocalDate.fromDateFields(task.getStartDate()); + LocalDate start = (task.getFirstDayNotConsolidated().compareTo( + taskStart) >= 0) ? task.getFirstDayNotConsolidated() + : taskStart; int i = 0; int maxDaysElapsed = 0; for (HoursPerAllocation each : hoursPerAllocation(start, diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidatedValue.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidatedValue.java index d23e54224..dd9772786 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidatedValue.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidatedValue.java @@ -21,7 +21,6 @@ package org.navalplanner.business.planner.entities.consolidations; import java.math.BigDecimal; -import java.util.Set; import org.joda.time.LocalDate; @@ -38,17 +37,13 @@ public class CalculatedConsolidatedValue extends ConsolidatedValue { } public static CalculatedConsolidatedValue create(LocalDate date, - BigDecimal value, - Set pendingConsolidatedHours) { - return create(new CalculatedConsolidatedValue(date, value, - pendingConsolidatedHours)); + BigDecimal value, LocalDate taskEndDate) { + return create(new CalculatedConsolidatedValue(date, value, taskEndDate)); } - protected CalculatedConsolidatedValue( - LocalDate date, - BigDecimal value, - Set pendingConsolidatedHours) { - super(date, value, pendingConsolidatedHours); + protected CalculatedConsolidatedValue(LocalDate date, BigDecimal value, + LocalDate taskEndDate) { + super(date, value, taskEndDate); } protected CalculatedConsolidatedValue() { 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 50e5607f0..e0e6b2d57 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 @@ -70,7 +70,10 @@ public class CalculatedConsolidation extends Consolidation { @Override public SortedSet getConsolidatedValues() { - return new TreeSet(consolidatedValues); + SortedSet result = new TreeSet( + new ConsolidatedValueComparator()); + result.addAll(consolidatedValues); + return result; } public SortedSet getCalculatedConsolidatedValues() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValue.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValue.java index f1d05770e..d73b63257 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValue.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValue.java @@ -21,13 +21,10 @@ package org.navalplanner.business.planner.entities.consolidations; import java.math.BigDecimal; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import org.hibernate.validator.NotNull; import org.joda.time.LocalDate; import org.navalplanner.business.common.BaseEntity; -import org.navalplanner.business.planner.entities.ResourceAllocation; /** * @author Susana Montes Pedreira @@ -37,7 +34,7 @@ public abstract class ConsolidatedValue extends BaseEntity { private LocalDate date; private BigDecimal value; - private Set pendingConsolidatedHours = new HashSet(); + private LocalDate taskEndDate; public abstract boolean isCalculated(); @@ -45,13 +42,11 @@ public abstract class ConsolidatedValue extends BaseEntity { } - protected ConsolidatedValue( - LocalDate date, - BigDecimal value, - Set pendingConsolidatedHours) { + protected ConsolidatedValue(LocalDate date, BigDecimal value, + LocalDate taskEndDate) { this.date = date; this.value = value; - this.pendingConsolidatedHours = pendingConsolidatedHours; + this.taskEndDate = taskEndDate; } public void setValue(BigDecimal value) { @@ -70,24 +65,9 @@ public abstract class ConsolidatedValue extends BaseEntity { return date; } - public void setPendingConsolidatedHours(Set pendingConsolidatedHours) { - this.pendingConsolidatedHours = pendingConsolidatedHours; - } - - public Set getPendingConsolidatedHours() { - return pendingConsolidatedHours; - } - - public static Set createPendingConsolidatedHours( - LocalDate consolidatedDate, - Collection allocations) { - Set pendingConsolidatedHours = new HashSet(); - for (ResourceAllocation allocation : allocations) { - pendingConsolidatedHours - .add(PendingConsolidatedHoursPerResourceAllocation.create( - consolidatedDate, allocation)); - } - return pendingConsolidatedHours; + @NotNull(message = "task end date not specified") + public LocalDate getTaskEndDate() { + return taskEndDate; } } 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 4959fed19..2db78902a 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 @@ -22,6 +22,7 @@ package org.navalplanner.business.planner.entities.consolidations; import java.util.SortedSet; +import org.joda.time.LocalDate; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.planner.entities.Task; @@ -54,4 +55,10 @@ public abstract class Consolidation extends BaseEntity { return task; } + public LocalDate getConsolidatedUntil() { + SortedSet consolidatedValues = getConsolidatedValues(); + return (consolidatedValues.isEmpty()) ? null : consolidatedValues + .last().getDate(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidatedValue.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidatedValue.java index bc03cd5fc..2dfd3d0a2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidatedValue.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidatedValue.java @@ -21,7 +21,6 @@ package org.navalplanner.business.planner.entities.consolidations; import java.math.BigDecimal; -import java.util.Set; import org.joda.time.LocalDate; import org.navalplanner.business.advance.entities.AdvanceMeasurement; @@ -41,32 +40,27 @@ public class NonCalculatedConsolidatedValue extends ConsolidatedValue { } public static NonCalculatedConsolidatedValue create(LocalDate date, - BigDecimal value, - Set pendingConsolidatedHours) { + BigDecimal value, LocalDate taskEndDate) { return create(new NonCalculatedConsolidatedValue(date, value, - pendingConsolidatedHours)); + taskEndDate)); } public static NonCalculatedConsolidatedValue create(LocalDate date, - BigDecimal value, - AdvanceMeasurement advanceMeasurement, - Set pendingConsolidatedHours) { + BigDecimal value, AdvanceMeasurement advanceMeasurement, + LocalDate taskEndDate) { return create(new NonCalculatedConsolidatedValue(date, value, - advanceMeasurement, pendingConsolidatedHours)); + advanceMeasurement, taskEndDate)); } protected NonCalculatedConsolidatedValue(LocalDate date, BigDecimal value, - AdvanceMeasurement advanceMeasurement, - Set pendingConsolidatedHours) { - this(date, value, pendingConsolidatedHours); + AdvanceMeasurement advanceMeasurement, LocalDate taskEndDate) { + this(date, value, taskEndDate); this.advanceMeasurement = advanceMeasurement; } - protected NonCalculatedConsolidatedValue( - LocalDate date, - BigDecimal value, - Set pendingConsolidatedHours) { - super(date, value, pendingConsolidatedHours); + protected NonCalculatedConsolidatedValue(LocalDate date, BigDecimal value, + LocalDate taskEndDate) { + super(date, value, taskEndDate); } protected NonCalculatedConsolidatedValue() { 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 e451c82d2..acffe9957 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 @@ -20,6 +20,7 @@ package org.navalplanner.business.planner.entities.consolidations; +import java.util.Comparator; import java.util.SortedSet; import java.util.TreeSet; @@ -71,7 +72,16 @@ public class NonCalculatedConsolidation extends Consolidation { @Override public SortedSet getConsolidatedValues() { - return new TreeSet(consolidatedValues); + TreeSet result = new TreeSet( + new Comparator() { + @Override + public int compare(ConsolidatedValue arg0, + ConsolidatedValue arg1) { + return arg0.getDate().compareTo(arg1.getDate()); + } + }); + result.addAll(consolidatedValues); + return result; } public SortedSet getNonCalculatedConsolidatedValues() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/PendingConsolidatedHoursPerResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/PendingConsolidatedHoursPerResourceAllocation.java deleted file mode 100644 index bf2b3d81b..000000000 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/PendingConsolidatedHoursPerResourceAllocation.java +++ /dev/null @@ -1,109 +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.business.planner.entities.consolidations; - -import java.util.Collection; - -import org.hibernate.validator.NotNull; -import org.joda.time.LocalDate; -import org.navalplanner.business.common.BaseEntity; -import org.navalplanner.business.planner.entities.DayAssignment; -import org.navalplanner.business.planner.entities.ResourceAllocation; - -/** - * Represents the number of hours per {@link ResourceAllocation} that are not - * consolidated. - * @author Susana Montes Pedreira - */ - -public class PendingConsolidatedHoursPerResourceAllocation extends BaseEntity { - - private Integer pendingConsolidatedHours; - - private ResourceAllocation resourceAllocation; - - public static PendingConsolidatedHoursPerResourceAllocation create( - LocalDate consolidatedDate, - ResourceAllocation resourceAllocation) { - return create(new PendingConsolidatedHoursPerResourceAllocation( - consolidatedDate, - resourceAllocation)); - } - - public static PendingConsolidatedHoursPerResourceAllocation create( - Integer pendingConsolidatedHours, - ResourceAllocation resourceAllocation) { - return create(new PendingConsolidatedHoursPerResourceAllocation( - pendingConsolidatedHours, resourceAllocation)); - } - - protected PendingConsolidatedHoursPerResourceAllocation( - LocalDate consolidatedDate, - ResourceAllocation resourceAllocation) { - this.setPendingConsolidatedHours(calculatePendingConsolidatedHours( - consolidatedDate, resourceAllocation - .getAssignments())); - this.setResourceAllocation(resourceAllocation); - } - - protected PendingConsolidatedHoursPerResourceAllocation( - Integer pendingConsolidatedHours, - ResourceAllocation resourceAllocation) { - this.setPendingConsolidatedHours(pendingConsolidatedHours); - this.setResourceAllocation(resourceAllocation); - } - - protected PendingConsolidatedHoursPerResourceAllocation() { - - } - - private Integer calculatePendingConsolidatedHours(LocalDate consolidatedDate, - Collection assignments) { - int result = 0; - for (DayAssignment dayAssignment : assignments) { - if ((dayAssignment.getDay().toDateTimeAtStartOfDay() - .compareTo(consolidatedDate.toDateTimeAtStartOfDay())) > 0) { - dayAssignment.setConsolidated(true); - result += dayAssignment.getHours(); - } - } - return new Integer(result); - } - - public void setPendingConsolidatedHours(Integer pendingConsolidatedHours) { - this.pendingConsolidatedHours = pendingConsolidatedHours; - } - - @NotNull(message = "pending consolidated hours not specified") - public Integer getPendingConsolidatedHours() { - return pendingConsolidatedHours; - } - - public void setResourceAllocation(ResourceAllocation resourceAllocation) { - this.resourceAllocation = resourceAllocation; - } - - @NotNull(message = "resource allocation not specified") - public ResourceAllocation getResourceAllocation() { - return resourceAllocation; - } - -} 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/limiting/daos/ILimitingResourceQueueDAO.java similarity index 96% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/ILimitingResourceQueueDAO.java index 47b4cfdea..410619088 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/ILimitingResourceQueueDAO.java @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package org.navalplanner.business.planner.daos; +package org.navalplanner.business.planner.limiting.daos; import java.util.List; 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/limiting/daos/ILimitingResourceQueueDependencyDAO.java similarity index 71% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDependencyDAO.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/ILimitingResourceQueueDependencyDAO.java index 121d8f528..9317eed93 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDependencyDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/ILimitingResourceQueueDependencyDAO.java @@ -1,9 +1,9 @@ -package org.navalplanner.business.planner.daos; +package org.navalplanner.business.planner.limiting.daos; import java.util.List; import org.navalplanner.business.common.daos.IGenericDAO; -import org.navalplanner.business.planner.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; /** * Interface for repositories to implement queies related to diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/ILimitingResourceQueueElementDAO.java similarity index 92% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueElementDAO.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/ILimitingResourceQueueElementDAO.java index cd856a097..a7a7d03ce 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueElementDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/ILimitingResourceQueueElementDAO.java @@ -18,12 +18,12 @@ * along with this program. If not, see . */ -package org.navalplanner.business.planner.daos; +package org.navalplanner.business.planner.limiting.daos; import java.util.List; import org.navalplanner.business.common.daos.IGenericDAO; -import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.entities.LimitingResourceQueue; /** 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/limiting/daos/LimitingResourceQueueDAO.java similarity index 97% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/LimitingResourceQueueDAO.java index ad3a1720e..85ab6aa97 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/LimitingResourceQueueDAO.java @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package org.navalplanner.business.planner.daos; +package org.navalplanner.business.planner.limiting.daos; import java.util.List; 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/limiting/daos/LimitingResourceQueueDependencyDAO.java similarity index 81% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDependencyDAO.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/LimitingResourceQueueDependencyDAO.java index 226615763..911de92a9 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDependencyDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/LimitingResourceQueueDependencyDAO.java @@ -1,10 +1,10 @@ -package org.navalplanner.business.planner.daos; +package org.navalplanner.business.planner.limiting.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.limiting.entities.LimitingResourceQueueDependency; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/LimitingResourceQueueElementDAO.java similarity index 94% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueElementDAO.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/LimitingResourceQueueElementDAO.java index 1ec0d5d93..fafe0c17c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueElementDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/daos/LimitingResourceQueueElementDAO.java @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package org.navalplanner.business.planner.daos; +package org.navalplanner.business.planner.limiting.daos; import java.util.List; @@ -26,7 +26,7 @@ import org.hibernate.Criteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.navalplanner.business.common.daos.GenericDAOHibernate; -import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/AllocationOnGap.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/AllocationOnGap.java new file mode 100644 index 000000000..63154a82f --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/AllocationOnGap.java @@ -0,0 +1,182 @@ +/* + * 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.limiting.entities; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.GenericDayAssignment; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.SpecificDayAssignment; +import org.navalplanner.business.planner.entities.SpecificResourceAllocation; +import org.navalplanner.business.resources.entities.Resource; + +public abstract class AllocationOnGap { + + public static AllocationOnGap invalidOn(Gap gap) { + return new InvalidAllocationOnGap(gap); + } + + public static AllocationOnGap validOn(Gap gap, DateAndHour start, + DateAndHour endExclusive, int[] assignableHours) { + return new ValidAllocationOnGap(gap, start, endExclusive, + assignableHours); + } + + private final Gap originalGap; + + protected AllocationOnGap(Gap originalGap) { + Validate.notNull(originalGap); + this.originalGap = originalGap; + } + + public abstract boolean isValid(); + + public abstract List getAssignmentsFor( + ResourceAllocation allocation, Resource resource) + throws IllegalStateException; + + public abstract DateAndHour getStartInclusive() + throws IllegalStateException; + + public abstract DateAndHour getEndExclusive() throws IllegalStateException; + + public Gap getOriginalGap() { + return originalGap; + } +} + +class InvalidAllocationOnGap extends AllocationOnGap { + + private static final String INVALID_ALLOCATION_ON_GAP = "invalid allocation on gap"; + + InvalidAllocationOnGap(Gap originalGap) { + super(originalGap); + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public List getAssignmentsFor( + ResourceAllocation allocation, Resource resource) { + throw new IllegalStateException(INVALID_ALLOCATION_ON_GAP); + } + + @Override + public DateAndHour getEndExclusive() throws IllegalStateException { + throw new IllegalStateException(INVALID_ALLOCATION_ON_GAP); + } + + @Override + public DateAndHour getStartInclusive() throws IllegalStateException { + throw new IllegalStateException(INVALID_ALLOCATION_ON_GAP); + } +} + +class ValidAllocationOnGap extends AllocationOnGap { + + private final DateAndHour start; + private final DateAndHour end; + private final int[] assignableHours; + + public ValidAllocationOnGap(Gap gap, DateAndHour startInclusive, + DateAndHour endExclusive, int[] assignableHours) { + super(gap); + Validate.notNull(startInclusive); + Validate.notNull(endExclusive); + Validate.notNull(assignableHours); + Validate.isTrue(endExclusive.isAfter(startInclusive)); + this.start = startInclusive; + this.end = endExclusive; + Validate.isTrue(assignableHours.length == toFiniteList( + start.daysUntil(end)).size()); + this.assignableHours = assignableHours.clone(); + } + + private List toFiniteList(Iterable daysUntil) { + List result = new ArrayList(); + for (LocalDate each : daysUntil) { + result.add(each); + } + return result; + } + + @Override + public List getAssignmentsFor( + ResourceAllocation allocation, Resource resource) + throws IllegalStateException { + List days = toFiniteList(start.daysUntil(end)); + assert assignableHours.length == days.size(); + if (allocation instanceof SpecificResourceAllocation) { + return createSpecific(days, + (SpecificResourceAllocation) allocation, resource); + } else { + return createGeneric(days, (GenericResourceAllocation) allocation, + resource); + } + } + + private List createSpecific(List days, + SpecificResourceAllocation allocation, Resource resource) { + List result = new ArrayList(); + int i = 0; + for (LocalDate each : days) { + result.add(SpecificDayAssignment.create(each, assignableHours[i], + resource)); + i++; + } + return result; + } + + private List createGeneric(List days, + GenericResourceAllocation allocation, Resource resource) { + List result = new ArrayList(); + int i = 0; + for (LocalDate each : days) { + result.add(GenericDayAssignment.create(each, assignableHours[i], + resource)); + i++; + } + return result; + } + + @Override + public DateAndHour getEndExclusive() throws IllegalStateException { + return end; + } + + @Override + public DateAndHour getStartInclusive() throws IllegalStateException { + return start; + } + + @Override + public boolean isValid() { + return true; + } + +} \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/DateAndHour.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/DateAndHour.java new file mode 100644 index 000000000..d27e0242a --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/DateAndHour.java @@ -0,0 +1,144 @@ +/* + * 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.limiting.entities; + +import java.util.Iterator; + +import org.apache.commons.lang.Validate; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; + +/** + * + * @author Diego Pino Garcia + * + */ +public class DateAndHour implements Comparable { + + public static DateAndHour from(LocalDate date) { + return new DateAndHour(date, 0); + } + + private LocalDate date; + + private Integer hour; + + public DateAndHour(LocalDate date, Integer hour) { + Validate.notNull(date); + this.date = date; + this.hour = hour; + } + + public LocalDate getDate() { + return date; + } + + public Integer getHour() { + return hour; + } + + @Override + public int compareTo(DateAndHour time) { + Validate.notNull(time); + int compareDate = date.compareTo(getDate(time)); + return (compareDate != 0) ? compareDate : compareHour(time + .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 DateTime toDateTime() { + return date.toDateTimeAtStartOfDay().plusHours(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); + } + + public boolean isAfter(DateAndHour dateAndHour) { + return (this.compareTo(dateAndHour) > 0); + } + + public boolean isEquals(DateAndHour dateAndHour) { + return (this.compareTo(dateAndHour) == 0); + } + + public boolean isAfter(LocalDate date) { + return isAfter(DateAndHour.from(date)); + } + + /** + * Creates an {@link Iterable} that returns a lazy iterator. If + * end is null it will not stop and will keep on + * producing days forever + */ + public Iterable daysUntil(final DateAndHour end) { + Validate.isTrue(end == null || end.isAfter(this)); + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + + private LocalDate current = getDate(); + + @Override + public boolean hasNext() { + return end == null || end.isAfter(current); + } + + @Override + public LocalDate next() { + LocalDate result = current; + current = current.plusDays(1); + return result; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/Gap.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/Gap.java new file mode 100644 index 000000000..d21f850bc --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/Gap.java @@ -0,0 +1,309 @@ +/* + * 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.limiting.entities; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine; +import org.navalplanner.business.calendars.entities.BaseCalendar; +import org.navalplanner.business.calendars.entities.ResourceCalendar; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.DatePoint; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.EndOfTime; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.FixedPoint; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.Interval; +import org.navalplanner.business.calendars.entities.AvailabilityTimeLine.StartOfTime; +import org.navalplanner.business.planner.entities.AvailabilityCalculator; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; + +/** + * + * @author Diego Pino Garcia + * + */ +public class Gap implements Comparable { + + public static class GapOnQueue { + + public static List onQueue(LimitingResourceQueue queue, + Collection gaps) { + List result = new ArrayList(); + for (Gap each : gaps) { + result.add(each.onQueue(queue)); + } + return result; + } + + private final LimitingResourceQueue originQueue; + + private final Gap gap; + + GapOnQueue(LimitingResourceQueue originQueue, Gap gap) { + this.originQueue = originQueue; + this.gap = gap; + } + + public LimitingResourceQueue getOriginQueue() { + return originQueue; + } + + public Gap getGap() { + return gap; + } + + public List splitIntoGapsSatisfyingCriteria( + Set criteria) { + return GapOnQueue.onQueue(originQueue, gap + .splitIntoGapsSatisfyingCriteria(originQueue.getResource(), + criteria)); + } + + } + + private DateAndHour startTime; + + private DateAndHour endTime; + + private Integer hoursInGap; + + public Gap(Resource resource, DateAndHour startTime, + DateAndHour endTime) { + this.startTime = startTime; + this.endTime = endTime; + hoursInGap = calculateHoursInGap(resource, startTime, endTime); + } + + public GapOnQueue onQueue(LimitingResourceQueue queue) { + return new GapOnQueue(queue, this); + } + + private Integer calculateHoursInGap(Resource resource, DateAndHour startTime, DateAndHour endTime) { + // TODO remove this method. Use GapRequirements instead + if (endTime == null || startTime == null) { + // startTime is never null when hours in gap is really use + return Integer.MAX_VALUE; + } else { + return 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 List getHoursInGapUntilAllocatingAndGoingToTheEnd( + BaseCalendar calendar, + DateAndHour realStart, DateAndHour allocationEnd, int total) { + DateAndHour gapEnd = getEndTime(); + Validate.isTrue(gapEnd == null || allocationEnd.compareTo(gapEnd) <= 0); + Validate.isTrue(startTime == null + || realStart.compareTo(startTime) >= 0); + List result = new ArrayList(); + Iterator daysUntilEnd = realStart.daysUntil(gapEnd) + .iterator(); + boolean isFirst = true; + while (daysUntilEnd.hasNext()) { + LocalDate each = daysUntilEnd.next(); + final boolean isLast = !daysUntilEnd.hasNext(); + int hoursAtDay = getHoursAtDay(each, calendar, realStart, isFirst, + isLast); + final int hours; + if (total > 0) { + hours = Math.min(hoursAtDay, total); + total -= hours; + } else { + hours = hoursAtDay; + } + if (isFirst) { + isFirst = false; + } + result.add(hours); + if (total == 0 + && DateAndHour.from(each).compareTo(allocationEnd) >= 0) { + break; + } + } + return result; + } + + private int getHoursAtDay(LocalDate day, BaseCalendar calendar, + DateAndHour realStart, boolean isFirst, final boolean isLast) { + final int capacity = calendar.getCapacityAt(day); + if (isLast && isFirst) { + return Math.min(endTime.getHour() - realStart.getHour(), + capacity); + } else if (isFirst) { + return capacity - realStart.getHour(); + } else if (isLast) { + return Math.min(endTime.getHour(), capacity); + } else { + return capacity; + } + } + + public static Gap create(Resource resource, DateAndHour startTime, + DateAndHour endTime) { + return new Gap(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) { + LocalDate startAfter = LocalDate.fromDateFields(candidate + .getEarlierStartDateBecauseOfGantt()); + LocalDate endsAfter = LocalDate.fromDateFields(candidate + .getEarliestEndDateBecauseOfGantt()); + + return canSatisfyStartConstraint(startAfter) + && canSatisfyEndConstraint(endsAfter) + && hoursInGap >= candidate.getIntentedTotalHours(); + } + + private boolean canSatisfyStartConstraint(final LocalDate startsAfter) { + return startsAfter.compareTo(startTime.getDate()) <= 0; + } + + private boolean canSatisfyEndConstraint(LocalDate endsAfter) { + return endTime == null || endsAfter.compareTo(endTime.getDate()) <= 0; + } + + public String toString() { + String result = startTime.getDate() + " - " + startTime.getHour(); + if (endTime != null) { + result += "; " + endTime.getDate() + " - " + endTime.getHour(); + } + return result; + } + + @Override + public int compareTo(Gap other) { + if (other == null) { + return 1; + } + if (this.getStartTime() == null && other.getStartTime() == null) { + return 0; + } else if (this.getStartTime() == null) { + return -1; + } else if (other.getStartTime() == null) { + return 1; + } + return this.getStartTime().compareTo(other.getStartTime()); + } + + public boolean isBefore(Gap gap) { + return (compareTo(gap) < 0); + } + + public List splitIntoGapsSatisfyingCriteria(Resource resource, + Set criteria) { + return splitIntoGapsSatisfyingCriteria(resource, criteria, + getStartTime(), getEndTime()); + } + + /** + * Returns a set of {@link Gap} composed by those gaps which satisfy + * criteria within the period: gapStartTime till + * gapEndTime + * @param resource + * @param criteria + * criteria to be satisfied by resource + * @param gapStartTime + * start time of gap + * @param gapEndTime + * end time of gap + * @return + */ + private static List splitIntoGapsSatisfyingCriteria(Resource resource, + Set criteria, DateAndHour gapStartTime, + DateAndHour gapEndTime) { + AvailabilityTimeLine criterionsAvailability = AvailabilityCalculator + .getCriterionsAvailabilityFor(criteria, resource); + if (gapStartTime != null) { + criterionsAvailability.invalidUntil(gapStartTime.getDate()); + } + if (gapEndTime != null) { + criterionsAvailability.invalidFrom(gapEndTime.getDate()); + } + List validPeriods = criterionsAvailability.getValidPeriods(); + List result = new ArrayList(); + for (Interval each : validPeriods) { + result.add(createGap(resource, each, gapStartTime, gapEndTime)); + } + return result; + } + + private static Gap createGap(Resource resource, Interval interval, + DateAndHour originalGapStartTime, DateAndHour originalGapEndTime) { + DateAndHour start = convert(originalGapStartTime, interval.getStart()); + DateAndHour end = convert(originalGapEndTime, interval.getEnd()); + return Gap.create(resource, start, end); + } + + private static DateAndHour convert(DateAndHour possibleMatch, + DatePoint datePoint) { + if (datePoint instanceof StartOfTime || datePoint instanceof EndOfTime) { + return null; + } + FixedPoint p = (FixedPoint) datePoint; + if (possibleMatch != null + && possibleMatch.getDate().equals(p.getDate())) { + return possibleMatch; + } + return DateAndHour.from(p.getDate()); + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/GapRequirements.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/GapRequirements.java new file mode 100644 index 000000000..34c5c568b --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/GapRequirements.java @@ -0,0 +1,214 @@ +/* + * 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.limiting.entities; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.ListIterator; + +import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; +import org.navalplanner.business.planner.limiting.entities.Gap.GapOnQueue; +import org.navalplanner.business.resources.entities.Resource; + + +/** + * @author Óscar González Fernández + */ +public class GapRequirements { + + private final LimitingResourceQueueElement element; + + private final DateAndHour earliestPossibleStart; + + private final DateAndHour earliestPossibleEnd; + + public static GapRequirements forElement( + LimitingResourceQueueElement element, + List dependenciesAffectingStart, + List dependenciesAffectingEnd) { + return new GapRequirements(element, calculateEarliestPossibleStart( + element, dependenciesAffectingStart), + calculateEarliestPossibleEnd(element, dependenciesAffectingEnd)); + } + + private static DateAndHour calculateEarliestPossibleEnd( + LimitingResourceQueueElement element, + List dependenciesAffectingEnd) { + return DateAndHour.Max(asDateAndHour(element + .getEarliestEndDateBecauseOfGantt()), + max(dependenciesAffectingEnd)); + } + + private static DateAndHour calculateEarliestPossibleStart( + LimitingResourceQueueElement element, + List dependenciesAffectingStart) { + return DateAndHour.Max(asDateAndHour(element + .getEarlierStartDateBecauseOfGantt()), + max(dependenciesAffectingStart)); + } + + private static DateAndHour max( + List dependencies) { + DateAndHour result = null; + for (LimitingResourceQueueDependency each : dependencies) { + assert !each.getHasAsOrigin().isDetached(); + result = DateAndHour.Max(result, each.getDateFromOrigin()); + } + return result; + } + + private static DateAndHour asDateAndHour(Date date) { + return DateAndHour.from(LocalDate.fromDateFields(date)); + } + + private GapRequirements(LimitingResourceQueueElement element, + DateAndHour earliestPossibleStart, DateAndHour earliestPossibleEnd) { + Validate.notNull(element); + Validate.notNull(earliestPossibleStart); + Validate.notNull(earliestPossibleEnd); + this.element = element; + this.earliestPossibleStart = earliestPossibleStart; + this.earliestPossibleEnd = earliestPossibleEnd; + } + + public boolean isPotentiallyValid(Gap gap) { + DateAndHour gapEnd = gap.getEndTime(); + return gapEnd == null + || (earliestPossibleStart.isBefore(gapEnd) && !earliestPossibleEnd + .isAfter(gapEnd)); + } + + public AllocationOnGap guessValidity(GapOnQueue gapOnQueue) { + Gap gap = gapOnQueue.getGap(); + if (!isPotentiallyValid(gap)) { + return AllocationOnGap.invalidOn(gap); + } + DateAndHour realStart = DateAndHour.Max(earliestPossibleStart, gap + .getStartTime()); + Resource resource = gapOnQueue.getOriginQueue().getResource(); + List hours = gap.getHoursInGapUntilAllocatingAndGoingToTheEnd( + resource.getCalendar(), realStart, + earliestPossibleEnd, element.getIntentedTotalHours()); + int total = sum(hours); + if (total < element.getIntentedTotalHours()) { + return AllocationOnGap.invalidOn(gap); + } else if (total == element.getIntentedTotalHours()) { + return validAllocation(gap, realStart, hours); + } else { + assert total > element.getIntentedTotalHours(); + int hoursSurplus = total - element.getIntentedTotalHours(); + StartRemoval result = StartRemoval.removeStartSurplus(realStart, + hours, hoursSurplus); + return validAllocation(gap, result.newStart, result.hours); + } + + } + + private AllocationOnGap validAllocation(Gap gap, DateAndHour realStart, + List hours) { + return AllocationOnGap.validOn(gap, realStart, calculateEnd(realStart, + hours), asArray(hours)); + } + + private DateAndHour calculateEnd(DateAndHour realStart, List hours) { + if (hours.size() == 1) { + return new DateAndHour(realStart.getDate(), hours.get(0) + + realStart.getHour()); + } + return new DateAndHour(realStart.getDate().plusDays(hours.size() - 1), + getLast(hours)); + } + + private int getLast(List hours) { + return hours.get(hours.size() - 1); + } + + private static class StartRemoval { + + /** + * removes the initial assignments so the resulting list has + * hoursSurplus less hours + */ + static StartRemoval removeStartSurplus(DateAndHour start, + List hours, int hoursSurplus) { + int previousSize = hours.size(); + int hoursRemovedAtFirstDayOfNewHours = stripStartAssignments(hours, + hoursSurplus); + int currentSize = hours.size(); + int daysRemoved = previousSize - currentSize; + LocalDate newStartDay = start.getDate().plusDays(daysRemoved); + return new StartRemoval(new DateAndHour(newStartDay, + hoursRemovedAtFirstDayOfNewHours), hours); + } + + /** + * @return the hours reduced in the resulting first assignment + */ + private static int stripStartAssignments(List hours, + int hoursSurplus) { + ListIterator listIterator = hours.listIterator(); + while (listIterator.hasNext() && hoursSurplus > 0) { + Integer current = listIterator.next(); + int hoursTaken = Math.min(hoursSurplus, current); + hoursSurplus -= hoursTaken; + if (hoursTaken == current) { + listIterator.remove(); + } else { + listIterator.set(hoursTaken); + return current - hoursTaken; + } + } + return 0; + } + + private final DateAndHour newStart; + + private final List hours; + + private StartRemoval(DateAndHour newStart, List hours) { + this.newStart = newStart; + this.hours = hours; + } + } + + private static int[] asArray(Collection integers) { + int[] result = new int[integers.size()]; + int i = 0; + for (Integer each : integers) { + result[i++] = each; + } + return result; + } + + private static int sum(List hours) { + int result = 0; + for (int each : hours) { + result += each; + } + return result; + } + + public LimitingResourceQueueElement getElement() { + return element; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceAllocator.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceAllocator.java new file mode 100644 index 000000000..bf633e619 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceAllocator.java @@ -0,0 +1,390 @@ +/* + * 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.limiting.entities; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.navalplanner.business.calendars.entities.ResourceCalendar; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.GenericDayAssignment; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.ResourcesPerDay; +import org.navalplanner.business.planner.entities.SpecificDayAssignment; +import org.navalplanner.business.planner.entities.SpecificResourceAllocation; +import org.navalplanner.business.resources.entities.Criterion; +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 Gap getFirstValidGap( + LimitingResourceQueue queue, LimitingResourceQueueElement element) { + + final Resource resource = queue.getResource(); + final List elements = new LinkedList( + queue.getLimitingResourceQueueElements()); + final int size = elements.size(); + final DateAndHour startTime = getStartTimeBecauseOfGantt(element); + + int pos = 0; + + // Iterate through queue elements + while (pos <= size) { + Gap gap = getGapInQueueAtPosition( + resource, elements, startTime, pos++); + + if (gap != null) { + List subgaps = getFittingSubgaps( + element, gap, resource); + if (!subgaps.isEmpty()) { + return subgaps.get(0); + } + } + } + + // The queue cannot hold this element (queue.resource + // doesn't meet element.criteria) + return null; + } + + private static List getFittingSubgaps( + LimitingResourceQueueElement element, + final Gap gap, final Resource resource) { + + List result = new ArrayList(); + + if (isSpecific(element) && gap.canFit(element)) { + result.add(gap); + } else if (isGeneric(element)) { + final List gaps = gap.splitIntoGapsSatisfyingCriteria( + resource, element.getCriteria()); + for (Gap subgap : gaps) { + if (subgap.canFit(element)) { + result.add(subgap); + } + } + } + return result; + } + + public static Gap getFirstValidGapSince( + LimitingResourceQueueElement element, LimitingResourceQueue queue, + DateAndHour since) { + List gaps = getValidGapsForElementSince(element, queue, since); + return (!gaps.isEmpty()) ? gaps.get(0) : null; + } + + public static List getValidGapsForElementSince( + LimitingResourceQueueElement element, LimitingResourceQueue queue, + DateAndHour since) { + + List result = new ArrayList(); + + final Resource resource = queue.getResource(); + final List elements = new LinkedList( + queue.getLimitingResourceQueueElements()); + final int size = elements.size(); + + int pos = moveUntil(elements, since); + + // Iterate through queue elements + while (pos <= size) { + Gap gap = getGapInQueueAtPosition( + resource, elements, since, pos++); + + // The queue cannot hold this element (queue.resource + // doesn't meet element.criteria) + if (gap != null) { + List subgaps = getFittingSubgaps( + element, gap, resource); + result.addAll(subgaps); + } + } + + return result; + } + + private static int moveUntil(List elements, DateAndHour until) { + int pos = 0; + + if (elements.size() > 0) { + // Space between until and first element start time + LimitingResourceQueueElement first = elements.get(0); + if (until.isBefore(first.getStartTime())) { + return 0; + } + + for (pos = 0; pos < elements.size(); pos++) { + final LimitingResourceQueueElement each = elements.get(pos); + final DateAndHour startTime = each.getStartTime(); + if (until.isAfter(startTime) || until.isEquals(startTime)) { + return pos; + } + } + } + return pos; + } + + private static boolean isGeneric(LimitingResourceQueueElement element) { + return element.getResourceAllocation() instanceof GenericResourceAllocation; + } + + private static boolean isSpecific(LimitingResourceQueueElement element) { + return element.getResourceAllocation() instanceof SpecificResourceAllocation; + } + + public static DateAndHour getFirstElementTime(List dayAssignments) { + final DayAssignment start = dayAssignments.get(0); + return new DateAndHour(start.getDay(), start.getHours()); + } + + public static DateAndHour getLastElementTime(List dayAssignments) { + final DayAssignment end = dayAssignments.get(dayAssignments.size() - 1); + return new DateAndHour(end.getDay(), end.getHours()); + } + + private static Gap getGapInQueueAtPosition( + Resource resource, List elements, + DateAndHour startTimeBecauseOfGantt, int pos) { + + final int size = elements.size(); + + // No elements in queue + if (size == 0) { + return createLastGap(startTimeBecauseOfGantt, null, resource); + } + + // Last element + if (pos == size) { + return createLastGap(startTimeBecauseOfGantt, elements.get(size - 1), resource); + } + + LimitingResourceQueueElement next = elements.get(pos); + + // First element + if (pos == 0 + && startTimeBecauseOfGantt.getDate().isBefore( + next.getStartDate())) { + return Gap.create(resource, + startTimeBecauseOfGantt, next.getStartTime()); + } + + // In the middle of two elements + if (pos > 0) { + LimitingResourceQueueElement previous = elements.get(pos - 1); + return Gap.create(resource, DateAndHour + .Max(previous.getEndTime(), startTimeBecauseOfGantt), next + .getStartTime()); + } + + return null; + } + + private static DateAndHour getStartTimeBecauseOfGantt(LimitingResourceQueueElement element) { + return new DateAndHour(new LocalDate(element.getEarlierStartDateBecauseOfGantt()), 0); + } + + private static Gap createLastGap( + DateAndHour _startTime, LimitingResourceQueueElement lastElement, + Resource resource) { + + final DateAndHour queueEndTime = (lastElement != null) ? lastElement + .getEndTime() : null; + DateAndHour startTime = DateAndHour.Max(_startTime, queueEndTime); + return Gap + .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, DateAndHour endsAfter) { + + List assignments = new LinkedList(); + + LocalDate date = startTime.getDate(); + final int totalHours = resourceAllocation.getIntendedTotalHours(); + int hoursAssigned = 0; + // Generate first day assignment + int hoursCanAllocate = hoursCanWorkOnDay(resource, date, startTime.getHour()); + int hoursToAllocate = Math.min(totalHours, hoursCanAllocate); + DayAssignment dayAssignment = createDayAssignment(resourceAllocation, + resource, date, hoursToAllocate); + hoursAssigned += addDayAssignment(assignments, dayAssignment); + + // Generate rest of day assignments + for (date = date.plusDays(1); hoursAssigned < totalHours + || endsAfter.isAfter(date); date = date.plusDays(1)) { + hoursAssigned += addDayAssignment(assignments, + generateDayAssignment(resourceAllocation, resource, date, + totalHours)); + } + if (hoursAssigned > totalHours) { + stripStartAssignments(assignments, hoursAssigned - totalHours); + } + return new ArrayList(assignments); + } + + private static void stripStartAssignments(List assignments, + int hoursSurplus) { + ListIterator listIterator = assignments.listIterator(); + while (listIterator.hasNext() && hoursSurplus > 0) { + DayAssignment current = listIterator.next(); + int hoursTaken = Math.min(hoursSurplus, current.getHours()); + hoursSurplus -= hoursTaken; + if (hoursTaken == current.getHours()) { + listIterator.remove(); + } else { + listIterator.set(current.withHours(hoursTaken)); + } + } + } + + private static List generateDayAssignmentsStartingFromEnd(ResourceAllocation resourceAllocation, + Resource resource, + DateAndHour endTime) { + + List assignments = new ArrayList(); + + LocalDate date = endTime.getDate(); + int totalHours = resourceAllocation.getIntendedTotalHours(); + + // Generate last day assignment + int hoursCanAllocate = hoursCanWorkOnDay(resource, date, endTime.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.minusDays(1); totalHours > 0; date = date.minusDays(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; + } + + public static DateAndHour startTimeToAllocateStartingFromEnd( + ResourceAllocation resourceAllocation, Resource resource, + Gap gap) { + + // Last element, time is end of last element (gap.starttime) + if (gap.getEndTime() == null) { + return gap.getStartTime(); + } + + final List dayAssignments = LimitingResourceAllocator + .generateDayAssignmentsStartingFromEnd(resourceAllocation, + resource, gap.getEndTime()); + + return LimitingResourceAllocator.getLastElementTime(dayAssignments); + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueDependency.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueDependency.java new file mode 100644 index 000000000..50208ba35 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueDependency.java @@ -0,0 +1,184 @@ +package org.navalplanner.business.planner.limiting.entities; + +import static org.navalplanner.business.i18n.I18nHelper._; + +import java.util.EnumMap; + +import org.apache.commons.lang.Validate; +import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.planner.entities.Dependency; +import org.navalplanner.business.planner.entities.Dependency.Type; + + +/** + * 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(Type.START_START), + END_START(Type.END_START), + END_END(Type.END_END), + START_END(Type.START_END); + + private static EnumMap toQueueDependencyType; + + private static EnumMap toDependencyType; + + static { + toQueueDependencyType = new EnumMap( + Type.class); + toDependencyType = new EnumMap( + QueueDependencyType.class); + for (QueueDependencyType each : QueueDependencyType.values()) { + toQueueDependencyType.put(each.associatedType, each); + toDependencyType.put(each, each.associatedType); + } + } + + public static LimitingResourceQueueDependency.QueueDependencyType toQueueDependencyType( + Type type) { + return toQueueDependencyType.get(type); + } + + public static Type toDependencyType(QueueDependencyType type) { + return toDependencyType.get(type); + } + + private final Type associatedType; + + private QueueDependencyType(Type associatedType) { + this.associatedType = associatedType; + } + + boolean propagatesThrough(QueueDependencyType nextType) { + switch (this) { + case END_START: + case START_START: + return true; + case START_END: + case END_END: + return nextType.comesFromEnd(); + default: + throw new RuntimeException("unknown type: " + this); + } + } + + private boolean comesFromEnd() { + switch (this) { + case START_END: + case START_START: + return false; + case END_START: + case END_END: + return true; + default: + throw new RuntimeException("unknown type: " + this); + } + } + + }; + + public static LimitingResourceQueueDependency.QueueDependencyType toQueueDependencyType( + Dependency.Type type) { + return QueueDependencyType.toQueueDependencyType(type); + } + + 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; + } + + /** + * 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; + } + + public boolean isOriginNotDetached() { + return !hasAsOrigin.isDetached(); + } + + private Dependency.Type getDependencyType() { + return QueueDependencyType.toDependencyType(type); + } + + public boolean modifiesDestinationStart() { + return getDependencyType().modifiesDestinationStart(); + } + + public boolean modifiesDestinationEnd() { + return getDependencyType().modifiesDestinationEnd(); + } + + public DateAndHour getDateFromOrigin() { + if (hasAsOrigin.isDetached()) { + throw new IllegalStateException("origin detached"); + } + switch (type) { + case START_END: + case START_START: + return hasAsOrigin.getStartTime(); + case END_END: + case END_START: + return hasAsOrigin.getEndTime(); + default: + throw new RuntimeException("unknown type: " + type); + } + } + + public boolean propagatesThrough(LimitingResourceQueueDependency transitive) { + return getHasAsDestiny().equals(transitive.getHasAsOrigin()) + && type.propagatesThrough(transitive.getType()); + } +} 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/limiting/entities/LimitingResourceQueueElement.java similarity index 56% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueElement.java index 19c33d5c6..a31bbdbd7 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/LimitingResourceQueueElement.java @@ -18,24 +18,30 @@ * along with this program. If not, see . */ -package org.navalplanner.business.planner.entities; +package org.navalplanner.business.planner.limiting.entities; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; +import java.util.List; 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.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.Dependency; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.SpecificResourceAllocation; +import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.navalplanner.business.resources.entities.Resource; /** - * - * Entity which represents an element in the queue which represents - * the limiting resources. - * + * Entity which represents an element in the queue which represents the limiting + * resources. * @author Diego Pino Garcia * @author Javier Moran Rua */ @@ -47,17 +53,17 @@ public class LimitingResourceQueueElement extends BaseEntity { private Date earlierStartDateBecauseOfGantt; + private Date earliestEndDateBecauseOfGantt; + private QueuePosition startQueuePosition; private QueuePosition endQueuePosition; private long creationTimestamp; - private Set dependenciesAsOrigin = - new HashSet(); + private Set dependenciesAsOrigin = new HashSet(); - private Set dependenciesAsDestiny = - new HashSet(); + private Set dependenciesAsDestiny = new HashSet(); public static LimitingResourceQueueElement create() { return create(new LimitingResourceQueueElement()); @@ -83,7 +89,8 @@ public class LimitingResourceQueueElement extends BaseEntity { return limitingResourceQueue; } - public void setLimitingResourceQueue(LimitingResourceQueue limitingResourceQueue) { + public void setLimitingResourceQueue( + LimitingResourceQueue limitingResourceQueue) { this.limitingResourceQueue = limitingResourceQueue; } @@ -123,9 +130,12 @@ public class LimitingResourceQueueElement extends BaseEntity { return earlierStartDateBecauseOfGantt; } - public void setEarlierStartDateBecauseOfGantt( - Date earlierStartDateBecauseOfGantt) { - this.earlierStartDateBecauseOfGantt = earlierStartDateBecauseOfGantt; + public Date getEarliestEndDateBecauseOfGantt() { + if (earliestEndDateBecauseOfGantt == null) { + // can be null because it's a new column + return earlierStartDateBecauseOfGantt; + } + return earliestEndDateBecauseOfGantt; } public long getCreationTimestamp() { @@ -164,9 +174,10 @@ public class LimitingResourceQueueElement extends BaseEntity { } 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"); + throw new IllegalArgumentException( + "It cannot be added a dependency" + + " in which the current queue element is neither origin" + + " not desinty"); } } @@ -184,4 +195,84 @@ public class LimitingResourceQueueElement extends BaseEntity { public Set getDependenciesAsDestiny() { return Collections.unmodifiableSet(dependenciesAsDestiny); } + + public void updateDates(Date orderInitDate, + Collection incomingDependencies) { + this.earlierStartDateBecauseOfGantt = calculateStartDate(orderInitDate, + incomingDependencies); + this.earliestEndDateBecauseOfGantt = calculateEndDate(orderInitDate, + incomingDependencies); + } + + private Date calculateStartDate(Date orderInitDate, + Collection dependenciesWithThisDestination) { + Date result = orderInitDate; + for (Dependency each : dependenciesWithThisDestination) { + if (!each.isDependencyBetweenLimitedAllocatedTasks() + && each.getType().modifiesDestinationStart()) { + result = bigger(result, each.getDateFromOrigin()); + } + } + return result; + } + + private Date calculateEndDate(Date orderInitDate, + Collection incomingDependencies) { + Date result = orderInitDate; + for (Dependency each : incomingDependencies) { + if (!each.isDependencyBetweenLimitedAllocatedTasks() + && each.getType().modifiesDestinationEnd()) { + result = bigger(result, each.getDateFromOrigin()); + } + } + return result; + } + + private Date bigger(Date one, Date another) { + if (one == null) { + return another; + } + if (another == null) { + return one; + } + return one.compareTo(another) >= 0 ? one : another; + } + + public void detach() { + setLimitingResourceQueue(null); + setStartDate(null); + setStartHour(0); + setEndDate(null); + setEndHour(0); + getResourceAllocation().removeLimitingDayAssignments(); + } + + public boolean isDetached() { + return getStartDate() == null; + } + + public boolean isSpecific() { + return resourceAllocation instanceof SpecificResourceAllocation; + } + + public boolean isGeneric() { + return resourceAllocation instanceof GenericResourceAllocation; + } + + public Set getCriteria() { + if (!isGeneric()) { + throw new IllegalStateException("this is not a generic element"); + } + final ResourceAllocation resourceAllocation = getResourceAllocation(); + return ((GenericResourceAllocation) resourceAllocation).getCriterions(); + } + + public boolean hasDayAssignments() { + return !getResourceAllocation().getAssignments().isEmpty(); + } + + public List getDayAssignments() { + return resourceAllocation.getAssignments(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/QueuePosition.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/QueuePosition.java similarity index 96% rename from navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/QueuePosition.java rename to navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/QueuePosition.java index 1ab54b032..88a82bef6 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/QueuePosition.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/limiting/entities/QueuePosition.java @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package org.navalplanner.business.planner.entities; +package org.navalplanner.business.planner.limiting.entities; import static org.navalplanner.business.i18n.I18nHelper._; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java index 7d8843323..ba97a509c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java @@ -20,12 +20,18 @@ package org.navalplanner.business.resources.entities; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import org.navalplanner.business.common.BaseEntity; -import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.DateAndHour; +import org.navalplanner.business.planner.limiting.entities.Gap; +import org.navalplanner.business.planner.limiting.entities.GapRequirements; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.Gap.GapOnQueue; /** * @@ -39,6 +45,8 @@ public class LimitingResourceQueue extends BaseEntity { private SortedSet limitingResourceQueueElements = new TreeSet(new LimitingResourceQueueElementComparator()); + private List cachedGaps; + public static LimitingResourceQueue create() { return create(new LimitingResourceQueue()); } @@ -58,14 +66,53 @@ public class LimitingResourceQueue extends BaseEntity { public void addLimitingResourceQueueElement(LimitingResourceQueueElement element) { element.setLimitingResourceQueue(this); limitingResourceQueueElements.add(element); + cachedGaps = null; } public void removeLimitingResourceQueueElement(LimitingResourceQueueElement element) { limitingResourceQueueElements.remove(element); + element.detach(); + cachedGaps = null; + } + + public List getGaps() { + if (cachedGaps == null) { + cachedGaps = calculateGaps(); + } + return cachedGaps; + } + + private List calculateGaps() { + List result = new ArrayList(); + DateAndHour previousEnd = null; + for (LimitingResourceQueueElement each : limitingResourceQueueElements) { + DateAndHour startTime = each.getStartTime(); + if (previousEnd == null || startTime.isAfter(previousEnd)) { + result.add(Gap.create(resource, previousEnd, startTime)); + } + previousEnd = each.getEndTime(); + } + result.add(Gap.create(resource, previousEnd, null)); + return GapOnQueue.onQueue(this, result); } public SortedSet getLimitingResourceQueueElements() { return Collections.unmodifiableSortedSet(limitingResourceQueueElements); } + /** + * @return the gaps that could potentially be valid for + * requirements ordered by start date + */ + public List getGapsPotentiallyValidFor( + GapRequirements requirements) { + List result = new ArrayList(); + for (GapOnQueue each : getGaps()) { + if (requirements.isPotentiallyValid(each.getGap())) { + result.add(each); + } + } + return result; + } + } 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 8f25e3989..1b242a9cd 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 @@ -22,7 +22,7 @@ package org.navalplanner.business.resources.entities; import java.util.Comparator; import org.joda.time.LocalDate; -import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; /** * 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 de720cb92..f4c5616ef 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 @@ -25,6 +25,7 @@ import java.util.List; import org.navalplanner.business.common.daos.IIntegrationEntityDAO; import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.workreports.entities.WorkReportLine; /** @@ -44,4 +45,6 @@ public interface IWorkReportLineDAO extends List findFilteredByDate(Date start, Date end); + List findByResources(List resourcesList); + } 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 7a374983f..e77e97cb8 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.Collections; import java.util.Date; import java.util.List; @@ -28,6 +29,7 @@ import org.hibernate.Criteria; import org.hibernate.criterion.Restrictions; import org.navalplanner.business.common.daos.IntegrationEntityDAO; import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.resources.entities.Resource; import org.navalplanner.business.workreports.entities.WorkReportLine; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -88,4 +90,14 @@ public class WorkReportLineDAO extends IntegrationEntityDAO return criteria.list(); } + @Override + @SuppressWarnings("unchecked") + public List findByResources(List resourcesList) { + if (resourcesList.isEmpty()) { + return Collections.emptyList(); + } + return getSession().createCriteria(WorkReportLine.class).add( + Restrictions.in("resource", resourcesList)).list(); + } + } diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml index be7423354..7da23bb9f 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml @@ -33,12 +33,17 @@ + - + + @@ -51,6 +56,7 @@ + - + + + + @@ -108,8 +120,15 @@ + - + + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/calendars/entities/Calendars.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/calendars/entities/Calendars.hbm.xml index 1e9d2b822..8f137c317 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/calendars/entities/Calendars.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/calendars/entities/Calendars.hbm.xml @@ -16,17 +16,20 @@ + + + @@ -56,6 +59,7 @@ + @@ -95,7 +99,9 @@ - + + @@ -117,4 +123,11 @@ + + + CREATE INDEX idx_CalendarException_on_BaseCalendar ON CalendarException (BASE_CALENDAR_ID) + DROP INDEX idx_CalendarException_on_BaseCalendar + + + 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 51a862755..3bfc26ed5 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 @@ -12,6 +12,7 @@ + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml index daa146e65..6cc0e7e65 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/costcategories/entities/CostCategories.hbm.xml @@ -16,6 +16,7 @@ + @@ -40,7 +41,11 @@ - + + @@ -80,9 +85,16 @@ - + + - + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/externalcompanies/entities/ExternalCompanies.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/externalcompanies/entities/ExternalCompanies.hbm.xml index 1e6a8cc6a..849950ba9 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/externalcompanies/entities/ExternalCompanies.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/externalcompanies/entities/ExternalCompanies.hbm.xml @@ -27,6 +27,7 @@ + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/labels/entities/Labels.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/labels/entities/Labels.hbm.xml index 6ad438577..e1ab3b1b0 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/labels/entities/Labels.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/labels/entities/Labels.hbm.xml @@ -14,9 +14,13 @@ - + + + @@ -36,6 +40,7 @@ + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/materials/entities/Materials.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/materials/entities/Materials.hbm.xml index 485e49492..dbc2e57a8 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/materials/entities/Materials.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/materials/entities/Materials.hbm.xml @@ -17,11 +17,16 @@ + - + + @@ -56,13 +61,19 @@ + - + + + @@ -93,7 +104,11 @@ - + + @@ -111,7 +126,11 @@ - + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml index da2d5a311..ffeb9dc8c 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/orders/entities/Orders.hbm.xml @@ -9,42 +9,59 @@ - - - + + + + + + + + - + + + + - + + - + @@ -59,11 +76,15 @@ + + + + @@ -89,8 +110,10 @@ + + @@ -113,6 +136,7 @@ + @@ -143,16 +167,21 @@ + + + class="org.navalplanner.business.orders.entities.OrderLine" + index="idx_hoursgroup_on_parentorderline"/> + + class="org.navalplanner.business.templates.entities.OrderLineTemplate" + index="idx_hoursgroup_on_orderlinetemplate"/> @@ -183,6 +212,7 @@ cascade="none" unique="true" access="field" /> + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml index b8cf8d21e..c66ec8fff 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml @@ -13,22 +13,22 @@ - - - - - - - - + - + + + - + + @@ -46,9 +46,11 @@ + + + + - + + + @@ -29,9 +33,10 @@ + - + @@ -123,7 +128,7 @@ - + 100 @@ -133,36 +138,44 @@ - + + + + - + - + + - + + - + - + 100 @@ -171,20 +184,25 @@ - org.navalplanner.business.planner.entities.LimitingResourceQueueDependency$QueueDependencyType + org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency$QueueDependencyType + + not-null="false" + index="idx_queuedependency_on_originqueue"> + + not-null="false" + index="idx_queuedependency_on_destinyqueue" + > + @@ -275,4 +295,5 @@ + 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 1ce63d858..89f8ae198 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 @@ -14,21 +14,27 @@ + - + + + + + @@ -51,11 +57,13 @@ + + @@ -66,6 +74,8 @@ + + @@ -83,11 +93,19 @@ + + + column="ORIGIN" + index="idx_Dependency_on_TaskElement_origin" /> + + - + + + @@ -48,8 +49,13 @@ - + + + @@ -66,4 +72,22 @@ + + + CREATE INDEX idx_QualityForm_on_QualityFormItems + ON QUALITY_FORM_ITEMS (QUALITY_FORM_ID) + DROP INDEX idx_QualityForm_on_QualityFormItems + + + + + + + CREATE INDEX idx_TaskQualityForm_on_TaskQualityFormItems + ON TASK_QUALITY_FORM_ITEMS (TASK_QUALITY_FORM_ID) + DROP INDEX idx_TaskQualityForm_on_TaskQualityFormItems + + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/requirements/entities/Requirements.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/requirements/entities/Requirements.hbm.xml index 38cf101f2..3a5008fc1 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/requirements/entities/Requirements.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/requirements/entities/Requirements.hbm.xml @@ -14,15 +14,26 @@ - + + - + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml index 00f81e824..ed61a11f4 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml @@ -1,6 +1,7 @@ + @@ -19,20 +20,25 @@ + + + + @@ -64,7 +70,6 @@ - @@ -78,10 +83,11 @@ + - + @@ -99,13 +105,19 @@ - + + + + @@ -130,8 +142,12 @@ + + - + + + @@ -152,6 +168,8 @@ org.navalplanner.business.resources.entities.ResourceEnum + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/templates/entities/Templates.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/templates/entities/Templates.hbm.xml index d3213e038..e690cbf7d 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/templates/entities/Templates.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/templates/entities/Templates.hbm.xml @@ -23,13 +23,19 @@ - + + + + @@ -45,6 +51,7 @@ + @@ -55,11 +62,13 @@ + + @@ -68,6 +77,7 @@ + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml index 911a7a401..0f0c44b20 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/users/entities/Users.hbm.xml @@ -23,6 +23,8 @@ + + @@ -33,6 +35,8 @@ + + + @@ -95,4 +100,28 @@ + + + CREATE INDEX idx_User_on_UserRoles ON USER_ROLES (userId) + DROP INDEX idx_User_on_UserRoles + + + + + + + CREATE INDEX idx_Profile_on_ProfileRoles ON PROFILE_ROLES (profileId) + DROP INDEX idx_Profile_on_ProfileRoles + + + + + + + CREATE INDEX idx_User_on_UserProfiles ON USER_PROFILES (USER_ID) + DROP INDEX idx_User_on_UserProfiles + + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/workreports/entities/WorkReports.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/workreports/entities/WorkReports.hbm.xml index cea8b131b..3d576e45f 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/workreports/entities/WorkReports.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/workreports/entities/WorkReports.hbm.xml @@ -23,11 +23,13 @@ + + @@ -37,6 +39,7 @@ + @@ -63,23 +66,31 @@ + - + + - + + + + + @@ -107,20 +118,34 @@ - + + - + + - + + - + + + + @@ -142,8 +167,10 @@ + + diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/common/ProportionalDistributorTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/common/ProportionalDistributorTest.java index 8230ee1ac..1219e6981 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/common/ProportionalDistributorTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/common/ProportionalDistributorTest.java @@ -19,6 +19,7 @@ */ package org.navalplanner.business.common; +import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import java.util.Arrays; @@ -63,6 +64,23 @@ public class ProportionalDistributorTest { assertThat(distributor.distribute(100), equalToDistribution(50, 50, 0)); } + @Test + public void ifEmptySharesProvidedItDistributesEqually() { + ProportionalDistributor distributor = ProportionalDistributor.create(0, + 0, 0, 0); + assertThat(distributor.distribute(4), equalToDistribution(1, 1, 1, 1)); + assertThat(distributor.distribute(5), equalToDistribution(2, 1, 1, 1)); + assertThat(distributor.distribute(6), equalToDistribution(2, 2, 1, 1)); + } + + @Test + public void noSharesProvidedImpliesItReturnsEmptyDistribution() { + ProportionalDistributor distributor = ProportionalDistributor.create(); + assertThat(distributor.distribute(0).length, equalTo(0)); + assertThat(distributor.distribute(1).length, equalTo(0)); + + } + @Test public void disputedPartGoesToFirstIfEqualWeight() { ProportionalDistributor distributor = ProportionalDistributor.create( diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/orders/entities/AddAdvanceAssignmentsToOrderElementTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/orders/entities/AddAdvanceAssignmentsToOrderElementTest.java index c1c7d2e95..5188c4ee6 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/orders/entities/AddAdvanceAssignmentsToOrderElementTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/orders/entities/AddAdvanceAssignmentsToOrderElementTest.java @@ -370,23 +370,26 @@ public class AddAdvanceAssignmentsToOrderElementTest { @Test(expected = DuplicateAdvanceAssignmentForOrderElementException.class) public void addingAnotherAdvanceAssignmentWithAnEquivalentTypeButDifferentInstance() throws Exception { + final Order order = createValidOrder(); - OrderLine line = createValidLeaf("GranSon", "75757"); + final OrderLine line = createValidLeaf("GranSon", "75757"); order.add(line); orderDao.save(order); AdvanceType type = createAndSaveType("tipoA"); getSession().flush(); - getSession().evict(type); - AdvanceType typeReloaded = reloadType(type); - - DirectAdvanceAssignment assignment = createValidAdvanceAssignment(false); + final DirectAdvanceAssignment assignment = createValidAdvanceAssignment(false); assignment.setAdvanceType(type); - DirectAdvanceAssignment assignmentWithSameType = createValidAdvanceAssignment(false); - assignmentWithSameType.setAdvanceType(typeReloaded); line.addAdvanceAssignment(assignment); + + getSession().evict(type); + AdvanceType typeReloaded = reloadType(type); + + final DirectAdvanceAssignment assignmentWithSameType = createValidAdvanceAssignment(false); + assignmentWithSameType.setAdvanceType(typeReloaded); + line.addAdvanceAssignment(assignmentWithSameType); } @@ -410,10 +413,14 @@ public class AddAdvanceAssignmentsToOrderElementTest { AdvanceType advanceType = createAndSaveType("test"); - DirectAdvanceAssignment advanceAssignment = createValidAdvanceAssignment(true); - advanceAssignment.setAdvanceType(advanceType); + DirectAdvanceAssignment advanceAssignmentA = createValidAdvanceAssignment(true); + advanceAssignmentA.setAdvanceType(advanceType); - orderLineGroup.addAdvanceAssignment(advanceAssignment); + DirectAdvanceAssignment advanceAssignmentB = createValidAdvanceAssignment(true); + advanceAssignmentB.setAdvanceType(advanceType); + + orderLineGroup.addAdvanceAssignment(advanceAssignmentA); + orderLineGroup.addAdvanceAssignment(advanceAssignmentB); } } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/LimitingResourceQueueElementDAOTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/LimitingResourceQueueElementDAOTest.java index f4dd6cb30..3e6d8ba9e 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/LimitingResourceQueueElementDAOTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/LimitingResourceQueueElementDAOTest.java @@ -31,9 +31,9 @@ import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; -import org.navalplanner.business.planner.daos.ILimitingResourceQueueDAO; -import org.navalplanner.business.planner.daos.ILimitingResourceQueueElementDAO; -import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.daos.ILimitingResourceQueueDAO; +import org.navalplanner.business.planner.limiting.daos.ILimitingResourceQueueElementDAO; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/AllocationUntilFillingHoursTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/AllocationUntilFillingHoursTest.java index a5a25cfac..6de17e414 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/AllocationUntilFillingHoursTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/AllocationUntilFillingHoursTest.java @@ -203,6 +203,8 @@ public class AllocationUntilFillingHoursTest { startDate.toDateTimeAtStartOfDay().toDate()).anyTimes(); expect(task.getCriterions()).andReturn( Collections. emptySet()).anyTimes(); + expect(task.getFirstDayNotConsolidated()).andReturn(startDate) + .anyTimes(); replay(task); } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java index d1e505bac..8f48c86c9 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java @@ -95,6 +95,8 @@ public class GenericResourceAllocationTest { .anyTimes(); expect(task.getEndDate()).andReturn(interval.getEnd().toDate()) .anyTimes(); + expect(task.getFirstDayNotConsolidated()).andReturn( + interval.getStart().toLocalDate()).anyTimes(); expect(task.getCalendar()).andReturn(baseCalendar).anyTimes(); replay(task); return this.task = task; diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java index b2ff707f0..e1a09fa84 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java @@ -20,11 +20,16 @@ package org.navalplanner.business.test.planner.entities; +import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.isA; +import static org.easymock.EasyMock.verify; import static org.easymock.classextension.EasyMock.createNiceMock; import static org.easymock.classextension.EasyMock.replay; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.navalplanner.business.test.planner.entities.DayAssignmentMatchers.consecutiveDays; @@ -34,6 +39,7 @@ import static org.navalplanner.business.test.planner.entities.DayAssignmentMatch import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.easymock.IAnswer; @@ -44,8 +50,11 @@ import org.navalplanner.business.calendars.entities.AvailabilityTimeLine; import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.calendars.entities.ResourceCalendar; import org.navalplanner.business.planner.entities.ResourcesPerDay; +import org.navalplanner.business.planner.entities.SpecificDayAssignment; import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.entities.ResourceAllocation.DetachDayAssignmentOnRemoval; +import org.navalplanner.business.planner.entities.ResourceAllocation.IOnDayAssignmentRemoval; import org.navalplanner.business.resources.entities.Worker; public class SpecificResourceAllocationTest { @@ -143,6 +152,7 @@ public class SpecificResourceAllocationTest { start.toDateTimeAtStartOfDay().toDate()).anyTimes(); expect(task.getEndDate()).andReturn( end.toDateTimeAtStartOfDay().toDate()).anyTimes(); + expect(task.getFirstDayNotConsolidated()).andReturn(start).anyTimes(); replay(task); } @@ -244,6 +254,50 @@ public class SpecificResourceAllocationTest { assertThat(specificResourceAllocation.getAssignments(), haveHours(5, 5)); } + @Test + public void canBeNotifiedWhenADayAssignmentIsRemoved() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onInterval(start, start.plusDays(2)) + .allocateHours(10); + List currentAssignments = specificResourceAllocation + .getAssignments(); + IOnDayAssignmentRemoval dayAssignmentRemovalMock = createMock(IOnDayAssignmentRemoval.class); + for (SpecificDayAssignment each : currentAssignments) { + dayAssignmentRemovalMock + .onRemoval(specificResourceAllocation, each); + expectLastCall().once(); + } + specificResourceAllocation + .setOnDayAssignmentRemoval(dayAssignmentRemovalMock); + replay(dayAssignmentRemovalMock); + specificResourceAllocation.onInterval(start, start.plusDays(2)) + .allocateHours(10); + verify(dayAssignmentRemovalMock); + } + + @Test + public void canAutomaticallyDetachDayAssignmentsWhenRemoved() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onInterval(start, start.plusDays(2)) + .allocateHours(10); + List assignments = specificResourceAllocation + .getAssignments(); + for (SpecificDayAssignment each : assignments) { + assertThat(each.getSpecificResourceAllocation(), notNullValue()); + } + + specificResourceAllocation + .setOnDayAssignmentRemoval(new DetachDayAssignmentOnRemoval()); + specificResourceAllocation.onInterval(start, start.plusDays(2)) + .allocateHours(10); + + for (SpecificDayAssignment each : assignments) { + assertThat(each.getSpecificResourceAllocation(), nullValue()); + } + } + @Test public void thePreviousAssignmentsAreReplacedWhenAllocationHoursOnInterval() { givenResourceCalendarAlwaysReturning(3); @@ -299,6 +353,30 @@ public class SpecificResourceAllocationTest { assertThat(specificResourceAllocation.getAssignments(), haveHours(2, 8)); } + @SuppressWarnings("serial") + @Test + public void youCanAllocateHoursPreservingTheCurrentShape() { + final LocalDate start = new LocalDate(2000, 2, 4); + givenResourceCalendar(8, new HashMap() { + { + put(start, 2); + put(start.plusDays(1), 4); + put(start.plusDays(3), 6); + } + }); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onInterval(start, start.plusDays(4)) + .allocateHours(20); + assertThat(specificResourceAllocation.getAssignments(), haveHours(2, 4, + 8, 6)); + + specificResourceAllocation.allocateKeepingProportions(start, start + .plusDays(2), 9); + + assertThat(specificResourceAllocation.getAssignments(), haveHours(3, 6, + 8, 6)); + } + @Test public void theEndDateOfTheAllocationIsExclusive() { LocalDate start = new LocalDate(2000, 2, 4); diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java index 7ff6844bf..a5980f90a 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java @@ -43,10 +43,10 @@ import org.navalplanner.business.orders.entities.OrderLine; import org.navalplanner.business.orders.entities.SchedulingDataForVersion; import org.navalplanner.business.orders.entities.TaskSource; import org.navalplanner.business.planner.daos.ITaskElementDAO; -import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; 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.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.scenarios.entities.OrderVersion; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; diff --git a/navalplanner-webapp/pom.xml b/navalplanner-webapp/pom.xml index 918eadd09..4ce081902 100644 --- a/navalplanner-webapp/pom.xml +++ b/navalplanner-webapp/pom.xml @@ -10,7 +10,132 @@ war NavalPlan Web Client Module - + + + reports + + true + + + + + + + org.codehaus.mojo + jasperreports-maven-plugin + 1.0-beta-2 + + ${project.build.sourceDirectory}/../jasper + ${project.build.sourceDirectory}/../../../target/classes + true + + + + + compile-reports + + compile + + + + + + jasperreports + jasperreports + 3.7.0 + + + + + + + + userguide + + true + + + + + maven-antrun-plugin + 1.1 + + + user-doc-generation + + run + + process-resources + false + + + Executing make html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + user-doc-clean + + run + + clean + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + navalplanner-webapp - - - org.codehaus.mojo - jasperreports-maven-plugin - 1.0-beta-2 - - ${project.build.sourceDirectory}/../jasper - ${project.build.sourceDirectory}/../../../target/classes - true - - - - - compile-reports - - compile - - - - - - jasperreports - jasperreports - 3.7.0 - - - - - maven-antrun-plugin - 1.1 - - - user-doc-generation - - run - - process-resources - false - - - Executing make html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - user-doc-clean - - run - - clean - false - - - - - - - - - - - - - - - - - - - - - - - diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java index 86db2e3f4..dca2e0756 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java @@ -197,8 +197,7 @@ public class CustomMenuController extends Div implements IMenuItemsRegister { subItem(_("Company view"), "/planner/index.zul;company_scheduling","01-introducion.html"), subItem(_("General resource allocation"),"/planner/index.zul;company_load","01-introducion.html#id1"), subItem(_("Orders list"), "/planner/index.zul;orders_list","01-introducion.html#id2"), - // FIX: Temporary hidden in main menu - // subItem(_("Limiting resources"),"/planner/index.zul;limiting_resources","01-introducion.html"), + subItem(_("Limiting resources"),"/planner/index.zul;limiting_resources","01-introducion.html"), subItem(_("Templates list"), "/templates/templates.zul", ""), subItem(_("Subcontracted tasks list"), "/subcontract/subcontractedTasks.zul", ""), subItem(_("Report advances"), "/subcontract/reportAdvances.zul", ""), diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/TemplateModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/TemplateModel.java index 2d794d2db..391b9d5d5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/TemplateModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/TemplateModel.java @@ -329,6 +329,11 @@ public class TemplateModel implements ITemplateModel { task.moveTo(scenario, newStart); } + @Override + public boolean isFixed(TaskElement task) { + return task.isLimitingAndHasDayAssignments(); + } + } @Autowired diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/GapsMergeSort.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/GapsMergeSort.java new file mode 100644 index 000000000..749761b42 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/GapsMergeSort.java @@ -0,0 +1,127 @@ +/* + * 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.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.collections.comparators.BooleanComparator; +import org.navalplanner.business.planner.limiting.entities.Gap.GapOnQueue; + + +/** + * Utility class for doing a merge sort of several ordered list of Gaps
+ * @author Óscar González Fernández + */ +public class GapsMergeSort { + + private GapsMergeSort() { + } + + private static class CurrentGap implements Comparable { + + static List convert( + Collection> iterators) { + List result = new ArrayList(); + for (Iterator iterator : iterators) { + result.add(new CurrentGap(iterator)); + } + return result; + } + + private Iterator iterator; + + private GapOnQueue current; + + private CurrentGap(Iterator iterator) { + this.iterator = iterator; + } + + public GapOnQueue consume() { + GapOnQueue result = getCurrent(); + current = null; + return result; + } + + boolean hasFinished() { + return current == null && !iterator.hasNext(); + } + + private GapOnQueue getCurrent() { + if (hasFinished()) { + throw new IllegalStateException("already finished"); + } + if (current != null) { + return current; + } + return current = iterator.next(); + } + + /** + * Ordering by hasFinished and the gap. An already finished is + * considered bigger than a not finished + */ + @Override + public int compareTo(CurrentGap other) { + int finishComparison = BooleanComparator.getFalseFirstComparator() + .compare(hasFinished(), other.hasFinished()); + if (finishComparison != 0) { + return finishComparison; + } else if (hasFinished()) { + assert other.hasFinished(); + return 0; + } else { + assert !hasFinished() && !other.hasFinished(); + return getCurrent().getGap().compareTo( + other.getCurrent().getGap()); + } + } + } + + public static List sort( + List> orderedListsOfGaps) { + if (orderedListsOfGaps.size() == 1) { + return orderedListsOfGaps.get(0); + } + List result = new ArrayList(); + List currentGaps = CurrentGap.convert(iteratorsFor(orderedListsOfGaps)); + CurrentGap min = null; + do { + min = Collections.min(currentGaps); + if (!min.hasFinished()) { + result.add(min.consume()); + } + } while (!min.hasFinished()); + return result; + } + + private static List> iteratorsFor( + List> orderedListsOfGaps) { + List> result = new ArrayList>(); + for (List each : orderedListsOfGaps) { + result.add(each.iterator()); + } + return result; + } + +} 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 114492aef..fc3e8e56e 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 @@ -24,8 +24,9 @@ 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.planner.limiting.entities.DateAndHour; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.Interval; @@ -54,28 +55,27 @@ import org.zkoss.ganttz.util.Interval; */ public interface ILimitingResourceQueueModel { + boolean nonAppropriativeAllocation( + LimitingResourceQueueElement element, LimitingResourceQueue queue, DateAndHour time); + /** * 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) - * + * {@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 the inserted + * queue elements. More than one can be inserted because inserting + * element can imply to move its sucessors.
+ * 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). In this case an + * empty list is returned * @param element */ - boolean assignLimitingResourceQueueElement(LimitingResourceQueueElement element); + List assignLimitingResourceQueueElement( + LimitingResourceQueueElement element); ZoomLevel calculateInitialZoomLevel(); @@ -84,6 +84,11 @@ public interface ILimitingResourceQueueModel { */ void confirm(); + List getAssignableQueues( + LimitingResourceQueueElement element); + + LimitingResourceQueueElement getLimitingResourceQueueElement(); + /** * Return all {@link LimitingResourceQueue} * @@ -103,20 +108,34 @@ public interface ILimitingResourceQueueModel { Interval getViewInterval(); + void init(LimitingResourceQueueElement element); + /** * Loads {@link LimitingResourceQueue} and unassigned {@link LimitingResourceQueueElement} from DB - * - * @param filterByResources */ - void initGlobalView(boolean filterByResources); + void initGlobalView(); - void initGlobalView(Order filterBy, boolean filterByResources); - - boolean userCanRead(Order order, String loginName); + /** + * Inserts element into queue at a specific time + * + * If it's not possible to insert element at that time because there's no + * room, for instance, then the task that is at that moment occupying that + * space is moved it to the end of the queue. This process is done + * recursively until there's room enough for holding the element to insert + * + * @param element + * @param queue + * @param allocationTime + */ + void appropriativeAllocation(LimitingResourceQueueElement element, LimitingResourceQueue queue, + DateAndHour allocationTime); void unschedule(LimitingResourceQueueElement element); + void removeUnassignedLimitingResourceQueueElement( LimitingResourceQueueElement element); + boolean userCanRead(Order order, String loginName); + } 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 333b5aede..a0283f87e 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 @@ -23,12 +23,10 @@ package org.navalplanner.web.limitingresources; 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.TaskComponent; import org.zkoss.ganttz.data.Dependency; 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 dfdf9a008..6b982feec 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 @@ -21,16 +21,14 @@ package org.navalplanner.web.limitingresources; 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.List; -import java.util.Map; import java.util.Set; import java.util.SortedSet; +import org.apache.commons.lang.Validate; import org.hibernate.Hibernate; import org.hibernate.proxy.HibernateProxy; import org.joda.time.LocalDate; @@ -41,23 +39,26 @@ import org.navalplanner.business.calendars.entities.CalendarException; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; 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.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.planner.limiting.daos.ILimitingResourceQueueDAO; +import org.navalplanner.business.planner.limiting.daos.ILimitingResourceQueueDependencyDAO; +import org.navalplanner.business.planner.limiting.daos.ILimitingResourceQueueElementDAO; +import org.navalplanner.business.planner.limiting.entities.AllocationOnGap; +import org.navalplanner.business.planner.limiting.entities.DateAndHour; +import org.navalplanner.business.planner.limiting.entities.Gap; +import org.navalplanner.business.planner.limiting.entities.GapRequirements; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceAllocator; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.Gap.GapOnQueue; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionSatisfaction; import org.navalplanner.business.resources.entities.LimitingResourceQueue; @@ -102,11 +103,11 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { @Autowired private ILimitingResourceQueueDependencyDAO limitingResourceQueueDependencyDAO; + private QueuesState queuesState; + private Interval viewInterval; - private List limitingResourceQueues = new ArrayList(); - - private List unassignedLimitingResourceQueueElements = new ArrayList(); + private LimitingResourceQueueElement beingEdited; private Set toBeRemoved = new HashSet(); @@ -114,21 +115,25 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { @Override @Transactional(readOnly = true) - public void initGlobalView(boolean filterByResources) { + public void initGlobalView() { doGlobalView(); } - @Override - @Transactional(readOnly = true) - public void initGlobalView(Order filterBy, boolean filterByResources) { - doGlobalView(); - } private void doGlobalView() { - loadUnassignedLimitingResourceQueueElements(); - loadLimitingResourceQueues(); + List unassigned = findUnassignedLimitingResourceQueueElements(); + List queues = loadLimitingResourceQueues(); + queuesState = new QueuesState(queues, unassigned); final Date startingDate = getEarliestDate(); - viewInterval = new Interval(startingDate, plusFiveYears(startingDate)); + Date endDate = (new LocalDate(startingDate)).plusYears(2) + .toDateTimeAtCurrentTime().toDate(); + viewInterval = new Interval(startingDate, endDate); + + Date currentDate = new Date(); + viewInterval = new Interval( + startingDate.after(currentDate) ? currentDate : startingDate, + endDate); + } private Date getEarliestDate() { @@ -140,16 +145,14 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { 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; - } + for (LimitingResourceQueue each : queuesState.getQueues()) { + LimitingResourceQueueElement element = getFirstLimitingResourceQueueElement(each); + if (element == null) { + continue; + } + if (earliestQueueElement == null + || isEarlier(element, earliestQueueElement)) { + earliestQueueElement = element; } } return earliestQueueElement; @@ -170,21 +173,14 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { 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 findUnassignedLimitingResourceQueueElements() { + return initializeLimitingResourceQueueElements(limitingResourceQueueElementDAO + .getUnassigned()); } private List initializeLimitingResourceQueueElements( @@ -216,6 +212,19 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { for (Dependency each: task.getDependenciesWithThisDestination()) { Hibernate.initialize(each); } + initializeRootOrder(task); + } + + // FIXME: Needed to fetch order.name in QueueComponent.composeTooltiptext. + // Try to replace it with a HQL query instead of iterating all the way up + // through order + private void initializeRootOrder(Task task) { + Hibernate.initialize(task.getOrderElement()); + OrderElement order = task.getOrderElement(); + do { + Hibernate.initialize(order.getParent()); + order = order.getParent(); + } while (order.getParent() != null); } private void initializeCalendarIfAny(BaseCalendar calendar) { @@ -274,11 +283,9 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { Hibernate.initialize(criterion.getType()); } - private void loadLimitingResourceQueues() { - limitingResourceQueues.clear(); - limitingResourceQueues - .addAll(initializeLimitingResourceQueues(limitingResourceQueueDAO - .getAll())); + private List loadLimitingResourceQueues() { + return initializeLimitingResourceQueues(limitingResourceQueueDAO + .getAll()); } private List initializeLimitingResourceQueues( @@ -300,11 +307,11 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { private void initializeResourceIfAny(Resource resource) { if (resource != null) { Hibernate.initialize(resource); + initializeCalendarIfAny(resource.getCalendar()); for (CriterionSatisfaction each : resource .getCriterionSatisfactions()) { Hibernate.initialize(each); initializeCriterion(each.getCriterion()); - initializeCalendarIfAny(resource.getCalendar()); } } } @@ -347,12 +354,12 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { @Override public List getLimitingResourceQueues() { - return Collections.unmodifiableList(limitingResourceQueues); + return queuesState.getQueues(); } + @Override public List getUnassignedLimitingResourceQueueElements() { - return Collections - .unmodifiableList(unassignedLimitingResourceQueueElements); + return queuesState.getUnassigned(); } public ZoomLevel calculateInitialZoomLevel() { @@ -362,77 +369,103 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { } @Override - 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; + public List assignLimitingResourceQueueElement( + LimitingResourceQueueElement externalQueueElement) { + List result = new ArrayList(); + for (LimitingResourceQueueElement each : queuesState + .getInsertionsToBeDoneFor(externalQueueElement)) { + GapRequirements requirements = queuesState.getRequirementsFor(each); + boolean inserted = insert(requirements); + if (!inserted) { + break; } - // Select queue and start time - queue = firstGapsForQueues.get(earliestGap); - startTime = earliestGap.getStartTime(); + result.add(requirements.getElement()); } + return result; + } - // Generate day assignments and adjust start and end times for element + private boolean insert(GapRequirements requirements) { + List potentiallyValidGapsFor = queuesState + .getPotentiallyValidGapsFor(requirements); + boolean generic = requirements.getElement().isGeneric(); + for (GapOnQueue each : potentiallyValidGapsFor) { + for (GapOnQueue eachSubGap : getSubGaps(each, requirements + .getElement(), generic)) { + AllocationOnGap allocation = requirements + .guessValidity(eachSubGap); + if (allocation.isValid()) { + doAllocation(requirements, allocation, eachSubGap.getOriginQueue()); + return true; + } + } + } + return false; + } + + private List getSubGaps(GapOnQueue each, + LimitingResourceQueueElement element, boolean generic) { + if (generic) { + return each.splitIntoGapsSatisfyingCriteria(element.getCriteria()); + } + return Collections.singletonList(each); + } + + private void doAllocation(GapRequirements requirements, + AllocationOnGap allocation, LimitingResourceQueue queue) { + Resource resource = queue.getResource(); + ResourceAllocation resourceAllocation = requirements + .getElement().getResourceAllocation(); + List assignments = allocation + .getAssignmentsFor(resourceAllocation, resource); + resourceAllocation + .allocateLimitingDayAssignments(assignments); + updateStartAndEndTimes(requirements.getElement(), + allocation.getStartInclusive(), allocation + .getEndExclusive()); + addLimitingResourceQueueElement(queue, requirements + .getElement()); + markAsModified(requirements.getElement()); + } + + private DateAndHour getEndsAfterBecauseOfGantt( + LimitingResourceQueueElement queueElement) { + return DateAndHour.from(LocalDate.fromDateFields(queueElement + .getEarliestEndDateBecauseOfGantt())); + } + + private boolean assignLimitingResourceQueueElementToQueueAt( + LimitingResourceQueueElement element, LimitingResourceQueue queue, + DateAndHour startTime, DateAndHour endsAfter) { + + // Allocate 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); + .generateDayAssignments(element.getResourceAllocation(), queue + .getResource(), startTime, endsAfter); + element.getResourceAllocation().allocateLimitingDayAssignments( + dayAssignments); + + DateAndHour endTime = LimitingResourceAllocator + .getLastElementTime(dayAssignments); + // the assignments can be generated after the required start + startTime = DateAndHour.Max(startTime, startFor(dayAssignments)); + if (sameDay(startTime, endTime)) { + endTime = new DateAndHour(endTime.getDate(), startTime.getHour() + endTime.getHour()); + } + updateStartAndEndTimes(element, startTime, endTime); // Add element to queue - addLimitingResourceQueueElement(queue, queueElement); - markAsModified(queueElement); + addLimitingResourceQueueElement(queue, element); + markAsModified(element); 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 DateAndHour startFor(List dayAssignments) { + return new DateAndHour(dayAssignments + .get(0).getDay(), 0); } - 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 boolean sameDay(DateAndHour startTime, DateAndHour endTime) { + return startTime.getDate().equals(endTime.getDate()); } private void markAsModified(LimitingResourceQueueElement element) { @@ -441,16 +474,13 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { } } - public LimitingResourceQueueElementGap createGap(Resource resource, DateAndHour startTime, + public Gap createGap(Resource resource, DateAndHour startTime, DateAndHour endTime) { - return LimitingResourceQueueElementGap.create(resource, startTime, endTime); + return Gap.create(resource, startTime, endTime); } private void updateStartAndEndTimes(LimitingResourceQueueElement element, - DateAndHour[] startAndEndTime) { - - final DateAndHour startTime = startAndEndTime[0]; - final DateAndHour endTime = startAndEndTime[1]; + DateAndHour startTime, DateAndHour endTime) { element.setStartDate(startTime.getDate()); element.setStartHour(startTime.getHour()); @@ -476,63 +506,9 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { private void addLimitingResourceQueueElement(LimitingResourceQueue queue, LimitingResourceQueueElement element) { - queue.addLimitingResourceQueueElement(element); - unassignedLimitingResourceQueueElements.remove(element); + queuesState.assignedToQueue(element, queue); } - 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() { @@ -621,21 +597,8 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { * 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); + public void unschedule(LimitingResourceQueueElement queueElement) { + queuesState.unassingFromQueue(queueElement); markAsModified(queueElement); } @@ -647,12 +610,12 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { @Override public void removeUnassignedLimitingResourceQueueElement( LimitingResourceQueueElement element) { - LimitingResourceQueueElement queueElement = retrieveQueueElementFromModel(element); + LimitingResourceQueueElement queueElement = queuesState.getEquivalent(element); queueElement.getResourceAllocation().setLimitingResourceQueueElement(null); queueElement.getResourceAllocation().getTask() .removeAllResourceAllocations(); - unassignedLimitingResourceQueueElements.remove(queueElement); + queuesState.removeUnassigned(queueElement); markAsRemoved(queueElement); } @@ -665,4 +628,110 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { } } + @Override + public List getAssignableQueues( + LimitingResourceQueueElement element) { + return queuesState.getAssignableQueues(element); + } + + @Override + public boolean nonAppropriativeAllocation( + LimitingResourceQueueElement element, + LimitingResourceQueue queue, + DateAndHour startTime) { + + Validate.notNull(element); + Validate.notNull(queue); + Validate.notNull(startTime); + + if (element.getLimitingResourceQueue() != null) { + unschedule(element); + } + return assignLimitingResourceQueueElementToQueueAt(element, queue, + startTime, getEndsAfterBecauseOfGantt(element)); + } + + @Override + public void init(LimitingResourceQueueElement element) { + beingEdited = queuesState.getEquivalent(element); + } + + @Override + public LimitingResourceQueueElement getLimitingResourceQueueElement() { + return beingEdited; + } + + + @Override + public void appropriativeAllocation(LimitingResourceQueueElement _element, LimitingResourceQueue _queue, + DateAndHour allocationTime) { + + LimitingResourceQueue queue = queuesState.getEquivalent(_queue); + LimitingResourceQueueElement element = queuesState.getEquivalent(_element); + + if (element.getLimitingResourceQueue() != null) { + unschedule(element); + } + + List unscheduledElements = new ArrayList(); + + Gap gap; + do { + gap = LimitingResourceAllocator.getFirstValidGapSince(element, queue, allocationTime); + + if (gap != null) { + final LocalDate startDate = gap.getStartTime().getDate(); + + if (startDate.equals(allocationTime.getDate())) { + assignLimitingResourceQueueElementToQueueAt(element, queue, + allocationTime, getEndsAfterBecauseOfGantt(element)); + break; + } else { + LimitingResourceQueueElement elementAtTime = getFirstElementFrom( + queue, allocationTime); + if (elementAtTime != null) { + unschedule(elementAtTime); + unscheduledElements.add(elementAtTime); + } + } + } + } while (gap != null); + + for (LimitingResourceQueueElement each: unscheduledElements) { + gap = LimitingResourceAllocator.getFirstValidGap(queue, each); + assignLimitingResourceQueueElementToQueueAt(each, queue, gap + .getStartTime(), getEndsAfterBecauseOfGantt(element)); + } + + } + + @SuppressWarnings("unchecked") + public LimitingResourceQueueElement getFirstElementFrom(LimitingResourceQueue queue, DateAndHour allocationTime) { + final List elements = new ArrayList(queue.getLimitingResourceQueueElements()); + + // First element + final LimitingResourceQueueElement first = elements.get(0); + if (isAfter(first, allocationTime)) { + return first; + } + + // Rest of elements + for (int i = 0; i < elements.size(); i++) { + final LimitingResourceQueueElement each = elements.get(i); + if (isInTheMiddle(each, allocationTime) || + isAfter(each, allocationTime)) { + return each; + } + } + return null; + } + + private boolean isAfter(LimitingResourceQueueElement element, DateAndHour time) { + return element.getStartTime().isAfter(time); + } + + private boolean isInTheMiddle(LimitingResourceQueueElement element, DateAndHour time) { + return (element.getStartTime().isBefore(time) || element.getStartTime().isEquals(time)) + && (element.getEndTime().isAfter(time) || element.getEndTime().isEquals(time)); + } } 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 2abcb21ff..e26b83a2b 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 @@ -31,13 +31,12 @@ 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.ResourceAllocation; import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.navalplanner.business.resources.entities.Resource; @@ -51,19 +50,22 @@ import org.springframework.stereotype.Component; import org.zkoss.ganttz.resourceload.IFilterChangedListener; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.zoom.SeveralModificators; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; 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.Composer; +import org.zkoss.zk.ui.util.GenericForwardComposer; 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.Listbox; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; +import org.zkoss.zul.Window; +import org.zkoss.zul.api.Rows; /** * Controller for limiting resources view @@ -71,7 +73,7 @@ import org.zkoss.zul.RowRenderer; */ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) -public class LimitingResourcesController implements Composer { +public class LimitingResourcesController extends GenericForwardComposer { @Autowired private ILimitingResourceQueueModel limitingResourceQueueModel; @@ -88,6 +90,10 @@ public class LimitingResourcesController implements Composer { private Grid gridUnassignedLimitingResourceQueueElements; + private Checkbox cbSelectAll; + + private Window manualAllocationWindow; + private final LimitingResourceQueueElementsRenderer limitingResourceQueueElementsRenderer = new LimitingResourceQueueElementsRenderer(); @@ -105,31 +111,32 @@ public class LimitingResourcesController implements Composer { public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { this.parent = comp; reload(); + limitingResourcesPanel.invalidate(); } + private Listbox listAssignableQueues; + + private Listbox listCandidateGaps; + public void reload() { - // by default show the task by resources - boolean filterByResources = true; - reload(filterByResources); - } - - private void reload(boolean filterByResources) { try { - if (filterBy == null) { - limitingResourceQueueModel.initGlobalView(filterByResources); - } else { - limitingResourceQueueModel.initGlobalView(filterBy, - filterByResources); - } + + limitingResourceQueueModel.initGlobalView(); + + // Initialize interval timeTracker = buildTimeTracker(); limitingResourcesPanel = buildLimitingResourcesPanel(); - addListeners(); this.parent.getChildren().clear(); this.parent.appendChild(limitingResourcesPanel); limitingResourcesPanel.afterCompose(); + gridUnassignedLimitingResourceQueueElements = (Grid) limitingResourcesPanel .getFellowIfAny("gridUnassignedLimitingResourceQueueElements"); + cbSelectAll = (Checkbox) limitingResourcesPanel.getFellowIfAny("cbSelectAll"); + + initManualAllocationWindow(); + addCommands(limitingResourcesPanel); } catch (IllegalArgumentException e) { try { @@ -142,20 +149,20 @@ public class LimitingResourcesController implements Composer { } } - private void addListeners() { - filterChangedListener = new IFilterChangedListener() { - - @Override - public void filterChanged(boolean filter) { - onApplyFilter(filter); - } - }; - // this.limitingResourcesPanel.addFilterListener(filterChangedListener); + private void initManualAllocationWindow() { + manualAllocationWindow = (Window) limitingResourcesPanel.getFellowIfAny("manualAllocationWindow"); + ManualAllocationController manualAllocationController = getManualAllocationController(); + manualAllocationController.setLimitingResourcesController(this); + manualAllocationController.setLimitingResourcesPanel(limitingResourcesPanel); } - public void onApplyFilter(boolean filterByResources) { - limitingResourcesPanel.clearComponents(); - reload(filterByResources); + private ManualAllocationController getManualAllocationController() { + return (ManualAllocationController) manualAllocationWindow.getVariable( + "manualAllocationController", true); + } + + public ILimitingResourceQueueModel getLimitingResourceQueueModel() { + return limitingResourceQueueModel; } private void addCommands(LimitingResourcesPanel limitingResourcesPanel) { @@ -164,8 +171,8 @@ public class LimitingResourcesController implements Composer { private TimeTracker buildTimeTracker() { return timeTracker = new TimeTracker(limitingResourceQueueModel - .getViewInterval(), limitingResourceQueueModel - .calculateInitialZoomLevel(), SeveralModificators.create(), + .getViewInterval(), ZoomLevel.DETAIL_THREE, + SeveralModificators.create(), SeveralModificators.create(new BankHolidaysMarker()), parent); } @@ -300,25 +307,41 @@ public class LimitingResourcesController implements Composer { public void render(Row row, Object data) throws Exception { LimitingResourceQueueElementDTO element = (LimitingResourceQueueElementDTO) data; + row.appendChild(automaticQueueing(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 Hbox operations(LimitingResourceQueueElementDTO element) { Hbox hbox = new Hbox(); - hbox.appendChild(assignButton(element)); + hbox.appendChild(automaticButton(element)); + hbox.appendChild(manualButton(element)); hbox.appendChild(removeButton(element)); return hbox; } - private Button removeButton(final LimitingResourceQueueElementDTO element) { + private Button manualButton(final LimitingResourceQueueElementDTO element) { Button result = new Button(); - result.setLabel(_("Remove")); + result.setLabel(_("Manual")); + result.setTooltiptext(_("Assign elemento to queue manually")); + result.addEventListener(Events.ON_CLICK, new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + showManualAllocationWindow(element.getOriginal()); + } + }); + return result; + } + + private Button removeButton(final LimitingResourceQueueElementDTO element) { + Button result = new Button("", "/common/img/ico_borrar1.png"); + result.setHoverImage("/common/img/ico_borrar.png"); + result.setSclass("icono"); result.setTooltiptext(_("Remove limiting resource element")); result.addEventListener(Events.ON_CLICK, new EventListener() { @@ -339,11 +362,11 @@ public class LimitingResourcesController implements Composer { Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); } - private Button assignButton( + private Button automaticButton( final LimitingResourceQueueElementDTO element) { Button result = new Button(); - result.setLabel(_("Assign")); - result.setTooltiptext(_("Assign to queue")); + result.setLabel(_("Automatic")); + result.setTooltiptext(_("Assign element to queue automatically")); result.addEventListener(Events.ON_CLICK, new EventListener() { @Override @@ -358,10 +381,16 @@ public class LimitingResourcesController implements Composer { LimitingResourceQueueElementDTO dto) { LimitingResourceQueueElement element = dto.getOriginal(); - if (limitingResourceQueueModel - .assignLimitingResourceQueueElement(element)) { + List inserted = limitingResourceQueueModel + .assignLimitingResourceQueueElement(element); + if (!inserted.isEmpty()) { Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); - limitingResourcesPanel.appendQueueElementToQueue(element); + for (LimitingResourceQueueElement each : inserted) { + // FIXME visually wrong if an element jumps from a queue to + // another + limitingResourcesPanel.refreshQueue(each + .getLimitingResourceQueue()); + } } else { showErrorMessage(_("Cannot allocate selected element. There is not any queue " + "that matches resource allocation criteria at any interval of time")); @@ -398,4 +427,44 @@ public class LimitingResourcesController implements Composer { Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); } + public boolean moveTask(LimitingResourceQueueElement element) { + showManualAllocationWindow(element); + limitingResourcesPanel.reloadComponent(); + return getManualAllocationWindowStatus() == Messagebox.OK; + } + + private void showManualAllocationWindow(LimitingResourceQueueElement element) { + getManualAllocationController().show(element); + } + + public int getManualAllocationWindowStatus() { + Integer status = getManualAllocationController().getStatus(); + return (status != null) ? status.intValue() : -1; + } + + public void reloadUnassignedLimitingResourceQueueElements() { + Util.reloadBindings(gridUnassignedLimitingResourceQueueElements); + } + + public void selectedAllUnassignedQueueElements() { + final boolean value = cbSelectAll.isChecked(); + + final Rows rows = gridUnassignedLimitingResourceQueueElements.getRows(); + for (Object each: rows.getChildren()) { + final Row row = (Row) each; + Checkbox cbAutoQueueing = getAutoQueueing(row); + cbAutoQueueing.setChecked(value); + } + } + + @SuppressWarnings("unchecked") + private Checkbox getAutoQueueing(Row row) { + List children = row.getChildren(); + return (Checkbox) children.get(0); + } + + public void assignAllSelectedElements() { + + } + } 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 5c8f6e79b..a16f1a7ab 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,21 +20,29 @@ package org.navalplanner.web.limitingresources; -import static org.navalplanner.web.I18nHelper._; - +import java.util.ArrayList; +import java.util.Collection; 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.joda.time.DateTime; +import org.joda.time.Period; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.springframework.beans.factory.annotation.Autowired; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.TimeTrackerComponent; +import org.zkoss.ganttz.timetracker.TimeTracker.IDetailItemFilter; +import org.zkoss.ganttz.timetracker.zoom.DetailItem; +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.Interval; import org.zkoss.ganttz.util.MutableTreeModel; import org.zkoss.ganttz.util.OnZKDesktopRegistry; import org.zkoss.ganttz.util.script.IScriptsRegister; @@ -47,6 +55,7 @@ import org.zkoss.zk.ui.event.Events; import org.zkoss.zul.Button; import org.zkoss.zul.ListModel; import org.zkoss.zul.Listbox; +import org.zkoss.zul.Listitem; import org.zkoss.zul.Separator; import org.zkoss.zul.SimpleListModel; @@ -74,14 +83,39 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { private Listbox listZoomLevels; + private Button paginationDownButton; + + private Button paginationUpButton; + + private Listbox horizontalPagination; + + private Component insertionPointLeftPanel; + private Component insertionPointRightPanel; + private Component insertionPointTimetracker; + + public void paginationDown() { + horizontalPagination.setSelectedIndex(horizontalPagination + .getSelectedIndex() - 1); + goToSelectedHorizontalPage(); + } + + public void paginationUp() { + horizontalPagination.setSelectedIndex(Math.max(1, horizontalPagination + .getSelectedIndex() + 1)); + goToSelectedHorizontalPage(); + } + @Autowired IResourceDAO resourcesDAO; - private static final String filterResources = _("Filter by resources"); - private static final String filterCriterions = _("Filter by criterions"); - private boolean filterbyResources = true; + private LimitingDependencyList dependencyList = new LimitingDependencyList( + this); - private LimitingDependencyList dependencyList = new LimitingDependencyList(this); + private PaginatorFilter paginatorFilter; + + private TimeTrackerComponent timeTrackerHeader; + + private IZoomLevelChangedListener zoomChangedListener; /** * Returns the closest upper {@link LimitingResourcesPanel} instance going @@ -90,7 +124,8 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { * @param comp * @return */ - public static LimitingResourcesPanel getLimitingResourcesPanel(Component comp) { + public static LimitingResourcesPanel getLimitingResourcesPanel( + Component comp) { if (comp == null) { return null; } @@ -100,7 +135,8 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { return getLimitingResourcesPanel(comp.getParent()); } - public LimitingResourcesPanel(LimitingResourcesController limitingResourcesController, + public LimitingResourcesPanel( + LimitingResourcesController limitingResourcesController, TimeTracker timeTracker) { init(limitingResourcesController, timeTracker); } @@ -113,12 +149,12 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { limitingResourcesController, true); treeModel = createModelForTree(); - timeTrackerComponent = timeTrackerForResourcesLoadPanel(timeTracker); - queueListComponent = new QueueListComponent(timeTracker, + + timeTrackerComponent = timeTrackerForLimitingResourcesPanel(timeTracker); + queueListComponent = new QueueListComponent(this, timeTracker, treeModel); - leftPane = new LimitingResourcesLeftPane(treeModel, - queueListComponent); + leftPane = new LimitingResourcesLeftPane(treeModel, queueListComponent); registerNeededScripts(); } @@ -139,25 +175,11 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { return limitingResourcesController.getLimitingResourceQueues(); } - public ListModel getFilters() { - String[] filters = new String[] { filterResources, filterCriterions }; - return new SimpleListModel(filters); - } - - public void setFilter(String filterby) { - if (filterby.equals(filterResources)) { - this.filterbyResources = true; - } else { - this.filterbyResources = false; - } - } - - public boolean getFilter() { - return filterbyResources; - } - public ListModel getZoomLevels() { - return new SimpleListModel(ZoomLevel.values()); + ZoomLevel[] selectableZoomlevels = { ZoomLevel.DETAIL_THREE, + ZoomLevel.DETAIL_FOUR, ZoomLevel.DETAIL_FIVE, + ZoomLevel.DETAIL_SIX }; + return new SimpleListModel(selectableZoomlevels); } public void setZoomLevel(final ZoomLevel zoomLevel) { @@ -220,7 +242,7 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { .retrieve(); } - private TimeTrackerComponent timeTrackerForResourcesLoadPanel( + private TimeTrackerComponent timeTrackerForLimitingResourcesPanel( TimeTracker timeTracker) { return new TimeTrackerComponent(timeTracker) { @Override @@ -236,57 +258,126 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { public void afterCompose() { super.afterCompose(); + paginatorFilter = new PaginatorFilter(); + + initializeBindings(); + + listZoomLevels + .setSelectedIndex(timeTracker.getDetailLevel().ordinal() - 2); + + // Pagination stuff + paginationUpButton.setDisabled(paginatorFilter.isLastPage()); + + paginatorFilter.setInterval(timeTracker.getRealInterval()); + timeTracker.setFilter(paginatorFilter); // Insert leftPane component with limitingresources list - getFellow("insertionPointLeftPanel").appendChild(leftPane); + insertionPointLeftPanel.appendChild(leftPane); leftPane.afterCompose(); - getFellow("insertionPointRightPanel").appendChild(timeTrackerComponent); - getFellow("insertionPointRightPanel").appendChild(queueListComponent); + insertionPointRightPanel.appendChild(timeTrackerComponent); + insertionPointRightPanel.appendChild(queueListComponent); queueListComponent.afterCompose(); dependencyList = generateDependencyComponentsList(); if (dependencyList != null) { dependencyList.afterCompose(); - getFellow("insertionPointRightPanel").appendChild(dependencyList); + insertionPointRightPanel.appendChild(dependencyList); } - // Insert timetracker headers - TimeTrackerComponent timeTrackerHeader = createTimeTrackerHeader(); - getFellow("insertionPointTimetracker").appendChild(timeTrackerHeader); + zoomChangedListener = new IZoomLevelChangedListener() { + @Override + public void zoomLevelChanged(ZoomLevel newDetailLevel) { + rebuildDependencies(); + timeTracker.resetMapper(); + paginatorFilter.setInterval(timeTracker.getRealInterval()); + timeTracker.setFilter(paginatorFilter); + reloadPanelComponents(); + paginatorFilter.populateHorizontalListbox(); + paginatorFilter.goToHorizontalPage(0); + reloadComponent(); + // Reset mapper for first detail + if (newDetailLevel == ZoomLevel.DETAIL_THREE) { + timeTracker.resetMapper(); + queueListComponent.invalidate(); + } + } + + }; + this.timeTracker.addZoomListener(zoomChangedListener); + + // Insert timetracker headers + timeTrackerHeader = createTimeTrackerHeader(); + insertionPointTimetracker.appendChild(timeTrackerHeader); timeTrackerHeader.afterCompose(); timeTrackerComponent.afterCompose(); + + paginatorFilter.populateHorizontalListbox(); + } + + private void rebuildDependencies() { + dependencyList.getChildren().clear(); + insertionPointRightPanel.appendChild(dependencyList); + dependencyList = generateDependencyComponentsList(); + dependencyList.afterCompose(); + } + + private void initializeBindings() { + + // Zoom and pagination listZoomLevels = (Listbox) getFellow("listZoomLevels"); - listZoomLevels.setSelectedIndex(timeTracker.getDetailLevel().ordinal()); + horizontalPagination = (Listbox) getFellow("horizontalPagination"); + paginationUpButton = (Button) getFellow("paginationUpButton"); + paginationDownButton = (Button) getFellow("paginationDownButton"); + + insertionPointLeftPanel = getFellow("insertionPointLeftPanel"); + insertionPointRightPanel = getFellow("insertionPointRightPanel"); + insertionPointTimetracker = getFellow("insertionPointTimetracker"); + } + + private void reloadPanelComponents() { + + timeTrackerComponent.getChildren().clear(); + + // paginatorFilter.setInterval(timeTracker.getRealInterval()); + // timeTracker.setFilter(paginatorFilter); + + if (timeTrackerHeader != null) { + timeTrackerHeader.afterCompose(); + timeTrackerComponent.afterCompose(); + } + dependencyList.invalidate(); } private LimitingDependencyList generateDependencyComponentsList() { - final Map queueElementsMap = queueListComponent + Map queueElementsMap = queueListComponent .getLimitingResourceElementToQueueTaskMap(); for (LimitingResourceQueueElement queueElement : queueElementsMap .keySet()) { for (LimitingResourceQueueDependency dependency : queueElement .getDependenciesAsOrigin()) { - addDependencyComponent(dependencyList, queueElementsMap, dependency); + addDependencyComponent(dependencyList, queueElementsMap, + dependency); } } return dependencyList; } - public void addDependencyComponent(LimitingResourceQueueDependency dependency) { + public void addDependencyComponent( + LimitingResourceQueueDependency dependency) { final Map queueElementsMap = queueListComponent .getLimitingResourceElementToQueueTaskMap(); addDependencyComponent(dependencyList, queueElementsMap, dependency); } - private void addDependencyComponent( - LimitingDependencyList dependencyList, + private void addDependencyComponent(LimitingDependencyList dependencyList, Map queueElementsMap, LimitingResourceQueueDependency dependency) { - LimitingDependencyComponent component = createDependencyComponent(queueElementsMap, dependency); + LimitingDependencyComponent component = createDependencyComponent( + queueElementsMap, dependency); if (component != null) { dependencyList.addDependencyComponent(component); } @@ -334,4 +425,181 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { dependencyList.removeDependencyComponents(task); } + public void moveQueueTask(QueueTask queueTask) { + if (limitingResourcesController.moveTask(queueTask.getLimitingResourceQueueElement())) { + removeQueueTask(queueTask); + } + } + + public void refreshQueue(LimitingResourceQueue queue) { + queueListComponent.refreshQueue(queue); + } + + public void goToSelectedHorizontalPage() { + paginatorFilter.goToHorizontalPage(horizontalPagination + .getSelectedIndex()); + reloadComponent(); + } + + public void reloadComponent() { + timeTrackerHeader.recreate(); + timeTrackerComponent.recreate(); + queueListComponent.invalidate(); + queueListComponent.afterCompose(); + rebuildDependencies(); + } + + private class PaginatorFilter implements IDetailItemFilter { + + private DateTime intervalStart; + private DateTime intervalEnd; + + private DateTime paginatorStart; + private DateTime paginatorEnd; + + @Override + public Interval getCurrentPaginationInterval() { + return new Interval(paginatorStart.toDate(), paginatorEnd.toDate()); + } + + private Period intervalIncrease() { + switch (timeTracker.getDetailLevel()) { + case DETAIL_ONE: + return Period.years(5); + case DETAIL_TWO: + return Period.years(5); + case DETAIL_THREE: + return Period.years(2); + case DETAIL_FOUR: + return Period.months(12); + case DETAIL_FIVE: + return Period.weeks(12); + case DETAIL_SIX: + return Period.weeks(12); + } + // Default month + return Period.years(2); + } + + public void setInterval(Interval realInterval) { + intervalStart = new DateTime(realInterval.getStart()); + intervalEnd = new DateTime(realInterval.getFinish()); + paginatorStart = intervalStart; + paginatorEnd = intervalStart.plus(intervalIncrease()); + if ((paginatorEnd.plus(intervalIncrease()).isAfter(intervalEnd))) { + paginatorEnd = intervalEnd; + } + updatePaginationButtons(); + } + + @Override + public void resetInterval() { + setInterval(timeTracker.getRealInterval()); + } + + public void paginationDown() { + paginatorFilter.goToHorizontalPage(horizontalPagination + .getSelectedIndex() - 1); + reloadComponent(); + } + + public void paginationUp() { + paginatorFilter.goToHorizontalPage(horizontalPagination + .getSelectedIndex() + 1); + reloadComponent(); + } + + @Override + public Collection selectsFirstLevel( + Collection firstLevelDetails) { + ArrayList result = new ArrayList(); + for (DetailItem each : firstLevelDetails) { + if ((each.getStartDate() == null) + || !(each.getStartDate().isBefore(paginatorStart)) + && (each.getStartDate().isBefore(paginatorEnd))) { + result.add(each); + } + } + return result; + } + + @Override + public Collection selectsSecondLevel( + Collection secondLevelDetails) { + ArrayList result = new ArrayList(); + for (DetailItem each : secondLevelDetails) { + if ((each.getStartDate() == null) + || !(each.getStartDate().isBefore(paginatorStart)) + && (each.getStartDate().isBefore(paginatorEnd))) { + result.add(each); + } + } + return result; + } + + public void populateHorizontalListbox() { + horizontalPagination.getItems().clear(); + DateTimeFormatter df = DateTimeFormat.forPattern("dd/MMM/yyyy"); + DateTime intervalStart = new DateTime(timeTracker.getRealInterval() + .getStart()); + if (intervalStart != null) { + DateTime itemStart = intervalStart; + DateTime itemEnd = intervalStart.plus(intervalIncrease()); + while (intervalEnd.isAfter(itemStart)) { + if (intervalEnd.isBefore(itemEnd) + || !intervalEnd.isAfter(itemEnd + .plus(intervalIncrease()))) { + itemEnd = intervalEnd; + } + Listitem item = new Listitem(df.print(itemStart) + " - " + + df.print(itemEnd.minusDays(1))); + horizontalPagination.appendChild(item); + itemStart = itemEnd; + itemEnd = itemEnd.plus(intervalIncrease()); + } + } + horizontalPagination.setDisabled(horizontalPagination.getItems() + .size() < 2); + } + + public void goToHorizontalPage(int interval) { + paginatorStart = intervalStart; + // paginatorStart = new + // DateTime(timeTracker.getRealInterval().getStart()); + paginatorStart = timeTracker.getDetailsFirstLevel().iterator() + .next().getStartDate(); + + for (int i = 0; i < interval; i++) { + paginatorStart = paginatorStart.plus(intervalIncrease()); + } + paginatorEnd = paginatorStart.plus(intervalIncrease()); + if ((paginatorEnd.plus(intervalIncrease()).isAfter(intervalEnd))) { + paginatorEnd = paginatorEnd.plus(intervalIncrease()); + } + timeTracker.resetMapper(); + updatePaginationButtons(); + } + + private void updatePaginationButtons() { + paginationDownButton.setDisabled(isFirstPage()); + paginationUpButton.setDisabled(isLastPage()); + } + + public void previous() { + paginatorStart = paginatorStart.minus(intervalIncrease()); + paginatorEnd = paginatorEnd.minus(intervalIncrease()); + updatePaginationButtons(); + } + + public boolean isFirstPage() { + return (horizontalPagination.getSelectedIndex() <= 0) + || horizontalPagination.isDisabled(); + } + + private boolean isLastPage() { + return (horizontalPagination.getItemCount() == (horizontalPagination + .getSelectedIndex() + 1)) + || horizontalPagination.isDisabled(); + } + } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ManualAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ManualAllocationController.java new file mode 100644 index 000000000..3a7e4ae18 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ManualAllocationController.java @@ -0,0 +1,534 @@ +/* + * 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 static org.navalplanner.web.I18nHelper._; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.limiting.entities.DateAndHour; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceAllocator; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.Gap; +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.Component; +import org.zkoss.zk.ui.SuspendNotAllowedException; +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.SelectEvent; +import org.zkoss.zk.ui.util.Clients; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Checkbox; +import org.zkoss.zul.Datebox; +import org.zkoss.zul.Listbox; +import org.zkoss.zul.Listcell; +import org.zkoss.zul.Listitem; +import org.zkoss.zul.ListitemRenderer; +import org.zkoss.zul.Messagebox; +import org.zkoss.zul.Radio; +import org.zkoss.zul.Radiogroup; +import org.zkoss.zul.SimpleListModel; +import org.zkoss.zul.Window; + +/** + * Controller for manual allocation of queue elements + * + * @author Diego Pino García + */ +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class ManualAllocationController extends GenericForwardComposer { + + private LimitingResourcesController limitingResourcesController; + + private LimitingResourcesPanel limitingResourcesPanel; + + private Radiogroup radioAllocationDate; + + private Radio earliestDate, latestDate, selectStartDate; + + private Datebox startAllocationDate; + + private Listbox listAssignableQueues; + + private Listbox listCandidateGaps; + + private Checkbox cbAllocationType; + + private Map endAllocationDates = new HashMap(); + + private final QueueRenderer queueRenderer = new QueueRenderer(); + + private final CandidateGapRenderer candidateGapRenderer = new CandidateGapRenderer(); + + public ManualAllocationController() { + + } + + @Override + public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { + this.self = comp; + self.setVariable("manualAllocationController", this, true); + listAssignableQueues = (Listbox) self.getFellowIfAny("listAssignableQueues"); + listCandidateGaps = (Listbox) self.getFellowIfAny("listCandidateGaps"); + + radioAllocationDate = (Radiogroup) self.getFellowIfAny("radioAllocationDate"); + earliestDate = (Radio) self.getFellowIfAny("earliestDate"); + latestDate = (Radio) self.getFellowIfAny("latestDate"); + selectStartDate = (Radio) self.getFellowIfAny("selectStartDate"); + + startAllocationDate = (Datebox) self.getFellowIfAny("startAllocationDate"); + cbAllocationType = (Checkbox) self.getFellowIfAny("cbAllocationType"); + } + + public void setLimitingResourcesPanel(LimitingResourcesPanel limitingResourcesPanel) { + this.limitingResourcesPanel = limitingResourcesPanel; + } + + public void setLimitingResourcesController(LimitingResourcesController limitingResourcesController) { + this.limitingResourcesController = limitingResourcesController; + } + + public ILimitingResourceQueueModel getLimitingResourceQueueModel() { + return limitingResourcesController.getLimitingResourceQueueModel(); + } + + private void feedValidGaps(LimitingResourceQueueElement element, LimitingResourceQueue queue) { + feedValidGapsSince(element, queue, getStartDayBecauseOfGantt(element)); + } + + private DateAndHour getStartDayBecauseOfGantt(LimitingResourceQueueElement element) { + return new DateAndHour(new LocalDate(element.getEarlierStartDateBecauseOfGantt()), 0); + } + + private void feedValidGapsSince(LimitingResourceQueueElement element, LimitingResourceQueue queue, DateAndHour since) { + List gaps = LimitingResourceAllocator.getValidGapsForElementSince(element, queue, since); + endAllocationDates = calculateEndAllocationDates(element.getResourceAllocation(), queue.getResource(), gaps); + listCandidateGaps.setModel(new SimpleListModel(gaps)); + + if (!isAppropriative()) { + if (gaps.isEmpty()) { + disable(radioAllocationDate, true); + } else { + listCandidateGaps.setSelectedIndex(0); + setStartAllocationDate(gaps.get(0).getStartTime()); + } + radioAllocationDate.setSelectedIndex(0); + } + enableRadiobuttons(isAppropriative()); + listCandidateGaps.setSelectedIndex(0); + } + + private boolean isAppropriative() { + return cbAllocationType.isChecked(); + } + + private void enableRadiobuttons(boolean isAppropriative) { + final LimitingResourceQueueElement beingEdited = getBeingEditedElement(); + if (isAppropriative) { + listCandidateGaps.setDisabled(true); + + earliestDate.setDisabled(true); + latestDate.setDisabled(true); + selectStartDate.setDisabled(false); + selectStartDate.setSelected(true); + + startAllocationDate.setDisabled(false); + startAllocationDate.setValue(beingEdited.getEarlierStartDateBecauseOfGantt()); + } else { + listCandidateGaps.setDisabled(false); + + earliestDate.setDisabled(false); + earliestDate.setSelected(true); + latestDate.setDisabled(false); + selectStartDate.setDisabled(false); + + startAllocationDate.setDisabled(true); + } + } + + private void setStartAllocationDate(DateAndHour time) { + final Date date = (time != null) ? toDate(time.getDate()) : null; + startAllocationDate.setValue(date); + } + + private Map calculateEndAllocationDates( + ResourceAllocation resourceAllocation, Resource resource, + List gaps) { + + Map result = new HashMap(); + for (Gap each: gaps) { + result.put(each, calculateEndAllocationDate(resourceAllocation, resource, each)); + } + return result; + } + + private DateAndHour calculateEndAllocationDate( + ResourceAllocation resourceAllocation, Resource resource, + Gap gap) { + + if (gap.getEndTime() != null) { + return LimitingResourceAllocator.startTimeToAllocateStartingFromEnd(resourceAllocation, resource, gap); + } + return null; + } + + public void selectRadioAllocationDate(Event event) { + Radiogroup radiogroup = (Radiogroup) event.getTarget().getParent(); + startAllocationDate.setDisabled(radiogroup.getSelectedIndex() != 2); + } + + private void disable(Radiogroup radiogroup, boolean disabled) { + for (Object obj: radiogroup.getChildren()) { + final Radio each = (Radio) obj; + each.setDisabled(disabled); + } + } + + private void setAssignableQueues(final LimitingResourceQueueElement element) { + List queues = getLimitingResourceQueueModel().getAssignableQueues(element); + listAssignableQueues.setModel(new SimpleListModel(queues)); + listAssignableQueues.addEventListener(Events.ON_SELECT, new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + SelectEvent se = (SelectEvent) event; + + LimitingResourceQueue queue = getSelectedQueue(se); + if (queue != null) { + feedValidGaps(element, queue); + } + } + + public LimitingResourceQueue getSelectedQueue(SelectEvent se) { + final Listitem item = (Listitem) se.getSelectedItems().iterator().next(); + return (LimitingResourceQueue) item.getValue(); + } + + }); + listAssignableQueues.setSelectedIndex(0); + feedValidGaps(element, queues.get(0)); + } + + private LimitingResourceQueue getSelectedQueue() { + LimitingResourceQueue result = null; + + final Listitem item = listAssignableQueues.getSelectedItem(); + if (item != null) { + result = (LimitingResourceQueue) item.getValue(); + } + return result; + } + + public void accept(Event e) { + LimitingResourceQueueElement element = getBeingEditedElement(); + LimitingResourceQueue queue = getSelectedQueue(); + DateAndHour time = getSelectedAllocationTime(); + + if (isAppropriative()) { + appropriativeAllocation(element, queue, time); + } else { + nonAppropriativeAllocation(element, queue, time); + } + + limitingResourcesController.reloadUnassignedLimitingResourceQueueElements(); + setStatus(Messagebox.OK); + close(e); + } + + private void nonAppropriativeAllocation(LimitingResourceQueueElement element, LimitingResourceQueue queue, DateAndHour time) { + Validate.notNull(time); + getLimitingResourceQueueModel() + .nonAppropriativeAllocation(element, queue, time); + limitingResourcesPanel.appendQueueElementToQueue(element); + } + + private void appropriativeAllocation(LimitingResourceQueueElement element, LimitingResourceQueue queue, DateAndHour time) { + Validate.notNull(time); + getLimitingResourceQueueModel().appropriativeAllocation(element, queue, time); + limitingResourcesPanel.refreshQueue(queue); + } + + private DateAndHour getSelectedAllocationTime() { + final Gap selectedGap = getSelectedGap(); + int index = radioAllocationDate.getSelectedIndex(); + + // Earliest date + if (index == 0) { + return getEarliestTime(selectedGap); + // Latest date + } else if (index == 1) { + return getLatestTime(selectedGap); + // Select start date + } else if (index == 2) { + final LocalDate selectedDay = new LocalDate(startAllocationDate.getValue()); + if (isAppropriative()) { + LimitingResourceQueueElement beingEdited = getBeingEditedElement(); + if (selectedDay.compareTo(new LocalDate(beingEdited.getEarlierStartDateBecauseOfGantt())) < 0) { + throw new WrongValueException(startAllocationDate, _("Day is not valid")); + } + return new DateAndHour(selectedDay, 0); + } else { + DateAndHour allocationTime = getValidDayInGap(selectedDay, getSelectedGap()); + if (allocationTime == null) { + throw new WrongValueException(startAllocationDate, _("Day is not valid")); + } + return allocationTime; + } + } + return null; + } + + private DateAndHour getEarliestTime(Gap gap) { + Validate.notNull(gap); + return gap.getStartTime(); + } + + private DateAndHour getLatestTime(Gap gap) { + Validate.notNull(gap); + LimitingResourceQueueElement element = getLimitingResourceQueueModel().getLimitingResourceQueueElement(); + LimitingResourceQueue queue = getSelectedQueue(); + return LimitingResourceAllocator.startTimeToAllocateStartingFromEnd( + element.getResourceAllocation(), queue.getResource(), gap); + } + + private Gap getSelectedGap() { + Listitem item = listCandidateGaps.getSelectedItem(); + if (item != null) { + return (Gap) item.getValue(); + } + return null; + } + + /** + * Checks if date is a valid day within gap. A day is valid within a gap if + * it is included between gap.startTime and the last day from which is + * possible to start doing an allocation (endAllocationDate) + * + * If date is valid, returns DateAndHour in gap associated with that date + * + * @param date + * @param gap + * @return + */ + private DateAndHour getValidDayInGap(LocalDate date, Gap gap) { + final DateAndHour endAllocationDate = endAllocationDates.get(gap); + final LocalDate start = gap.getStartTime().getDate(); + final LocalDate end = endAllocationDate != null ? endAllocationDate.getDate() : null; + + if (start.equals(date)) { + return gap.getStartTime(); + } + if (end != null && end.equals(date)) { + return endAllocationDate; + } + if ((start.compareTo(date) <= 0 + && (end == null || end.compareTo(date) >= 0))) { + return new DateAndHour(date, 0); + } + + return null; + } + + public void cancel() { + self.setVisible(false); + setStatus(Messagebox.CANCEL); + } + + public void close(Event e) { + self.setVisible(false); + e.stopPropagation(); + } + + public void setStartAllocationDate(Event event) { + setStartAllocationDate(getSelectedGap().getStartTime()); + } + + public void highlightCalendar(Event event) { + Datebox datebox = (Datebox) event.getTarget(); + if (datebox.getValue() == null) { + final LocalDate startDate = getSelectedGap().getStartTime().getDate(); + datebox.setValue(toDate(startDate)); + } + + if (isAppropriative()) { + final LimitingResourceQueueElement beingEdited = getBeingEditedElement(); + highlightDaysFromDate(datebox.getUuid(), new LocalDate(beingEdited.getEarlierStartDateBecauseOfGantt())); + } else { + highlightDaysInGap(datebox.getUuid(), getSelectedGap()); + } + } + + private LimitingResourceQueueElement getBeingEditedElement() { + return getLimitingResourceQueueModel().getLimitingResourceQueueElement(); + } + + private Date toDate(LocalDate date) { + return date.toDateTimeAtStartOfDay().toDate(); + } + + /** + * Highlight calendar days within gap + * + * @param uuid + * @param gap + */ + public void highlightDaysInGap(String uuid, Gap gap) { + final LocalDate start = gap.getStartTime().getDate(); + final LocalDate end = getEndAllocationDate(gap); + + final String jsCall = "highlightDaysInInterval('" + + uuid + "', '" + + jsonInterval(formatDate(start), formatDate(end)) + "', '" + + jsonHighlightColor() + "');"; + Clients.evalJavaScript(jsCall); + } + + /** + * Highlight calendar days starting from start + * + * @param uuid + * @param start + */ + public void highlightDaysFromDate(String uuid, LocalDate start) { + final String jsCall = "highlightDaysInInterval('" + + uuid + "', '" + + jsonInterval(formatDate(start), null) + "', '" + + jsonHighlightColor() + "');"; + Clients.evalJavaScript(jsCall); + } + + private LocalDate getEndAllocationDate(Gap gap) { + final DateAndHour endTime = endAllocationDates.get(gap); + return endTime != null ? endTime.getDate() : null; + } + + public String formatDate(LocalDate date) { + return (date != null) ? date.toString() : null; + } + + private String jsonInterval(String start, String end) { + StringBuilder result = new StringBuilder(); + + result.append("{\"start\": \"" + start + "\", "); + if (end != null) { + result.append("\"end\": \"" + end + "\""); + } + result.append("}"); + + return result.toString(); + } + + private String jsonHighlightColor() { + return "{\"color\": \"blue\", \"bgcolor\": \"white\"}"; + } + + public CandidateGapRenderer getCandidateGapRenderer() { + return candidateGapRenderer; + } + + private class CandidateGapRenderer implements ListitemRenderer { + + @Override + public void render(Listitem item, Object data) throws Exception { + Gap gap = (Gap) data; + + item.setValue(gap); + item.appendChild(cell(gap.getStartTime())); + item.appendChild(cell(gap.getEndTime())); + } + + public Listcell cell(DateAndHour time) { + return new Listcell(formatTime(time)); + } + + private String formatTime(DateAndHour time) { + return time == null ? _("END") : time.getDate().toString("dd/MM/yyyy") + " - " + time.getHour(); + } + + } + + public void show(LimitingResourceQueueElement element) { + try { + clear(); + setAssignableQueues(element); + getLimitingResourceQueueModel().init(element); + ((Window) self).doModal(); + } catch (SuspendNotAllowedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void clear() { + setStatus(Messagebox.CANCEL); + cbAllocationType.setChecked(false); + } + + public ListitemRenderer getQueueRenderer() { + return queueRenderer; + } + + public Integer getStatus() { + return (Integer) self.getVariable("status", true); + } + + public void setStatus(int status) { + self.setVariable("status", new Integer(status), true); + } + + private class QueueRenderer implements ListitemRenderer { + + @Override + public void render(Listitem item, Object data) throws Exception { + final LimitingResourceQueue queue = (LimitingResourceQueue) data; + item.setValue(queue); + item.appendChild(cell(queue)); + } + + private Listcell cell(LimitingResourceQueue queue) { + Listcell result = new Listcell(); + result.setLabel(queue.getResource().getName()); + return result; + } + + } + + public void onCheckAllocationType(Event e) { + Checkbox checkbox = (Checkbox) e.getTarget(); + enableRadiobuttons(checkbox.isChecked()); + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueComponent.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueComponent.java index 531f331e7..6bc88021e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueComponent.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueComponent.java @@ -23,12 +23,25 @@ package org.navalplanner.web.limitingresources; import static org.navalplanner.web.I18nHelper._; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.SortedSet; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; import org.navalplanner.business.common.exceptions.ValidationException; -import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.orders.entities.OrderElement; +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; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.limiting.entities.DateAndHour; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; +import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.zkoss.ganttz.DatesMapperOnInterval; import org.zkoss.ganttz.IDatesMapper; @@ -43,6 +56,7 @@ import org.zkoss.zk.ui.ext.AfterCompose; import org.zkoss.zul.Div; import org.zkoss.zul.impl.XulElement; + /** * This class wraps ResourceLoad data inside an specific HTML Div component. * @author Lorenzo Tilve Álvaro @@ -50,25 +64,44 @@ import org.zkoss.zul.impl.XulElement; public class QueueComponent extends XulElement implements AfterCompose { - public static QueueComponent create(TimeTracker timeTracker, + private static final int DEADLINE_MARK_HALF_WIDTH = 5; + + public static QueueComponent create( + QueueListComponent queueListComponent, + TimeTracker timeTracker, LimitingResourceQueue limitingResourceQueue) { - return new QueueComponent(timeTracker, + + return new QueueComponent(queueListComponent, timeTracker, limitingResourceQueue); } - private final LimitingResourceQueue limitingResourceQueue; + private final QueueListComponent queueListComponent; + private final TimeTracker timeTracker; + private transient IZoomLevelChangedListener zoomChangedListener; + + private LimitingResourceQueue limitingResourceQueue; + private List queueTasks = new ArrayList(); public List getQueueTasks() { return queueTasks; } - private QueueComponent(final TimeTracker timeTracker, + public void setLimitingResourceQueue(LimitingResourceQueue limitingResourceQueue) { + this.limitingResourceQueue = limitingResourceQueue; + } + + private QueueComponent( + final QueueListComponent queueListComponent, + final TimeTracker timeTracker, final LimitingResourceQueue limitingResourceQueue) { + + this.queueListComponent = queueListComponent; this.limitingResourceQueue = limitingResourceQueue; this.timeTracker = timeTracker; + createChildren(limitingResourceQueue, timeTracker.getMapper()); zoomChangedListener = new IZoomLevelChangedListener() { @@ -76,7 +109,7 @@ public class QueueComponent extends XulElement implements public void zoomLevelChanged(ZoomLevel detailLevel) { getChildren().clear(); createChildren(limitingResourceQueue, timeTracker.getMapper()); - invalidate(); + // invalidate(); } }; this.timeTracker.addZoomListener(zoomChangedListener); @@ -86,9 +119,27 @@ public class QueueComponent extends XulElement implements IDatesMapper mapper) { List queueTasks = createQueueTasks(mapper, limitingResourceQueue.getLimitingResourceQueueElements()); - if (queueTasks != null) { - appendQueueTasks(queueTasks); + appendQueueTasks(queueTasks); + } + + public QueueListComponent getQueueListComponent() { + return queueListComponent; + } + + public LimitingResourcesPanel getLimitingResourcesPanel() { + return queueListComponent.getLimitingResourcePanel(); + } + + public void invalidate() { + removeChildren(); + appendQueueElements(limitingResourceQueue.getLimitingResourceQueueElements()); + } + + private void removeChildren() { + for (QueueTask each: queueTasks) { + removeChild(each); } + queueTasks.clear(); } private void appendQueueTasks(List queueTasks) { @@ -102,13 +153,27 @@ public class QueueComponent extends XulElement implements appendChild(queueTask); } - private static List createQueueTasks( - IDatesMapper datesMapper, + private List createQueueTasks(IDatesMapper datesMapper, Set list) { List result = new ArrayList(); + + org.zkoss.ganttz.util.Interval interval = null; + if (timeTracker.getFilter() != null) { + timeTracker.getFilter().resetInterval(); + interval = timeTracker.getFilter().getCurrentPaginationInterval(); + } for (LimitingResourceQueueElement each : list) { - result.add(createQueueTask(datesMapper, each)); + if (interval != null) { + if (each.getEndDate().toDateMidnight().isAfter( + (new DateTime(interval.getStart())).toDateMidnight()) + && each.getStartDate().toDateMidnight().isBefore( + new DateTime(interval.getFinish()))) { + result.add(createQueueTask(datesMapper, each)); + } + } else { + result.add(createQueueTask(datesMapper, each)); + } } return result; } @@ -118,23 +183,130 @@ public class QueueComponent extends XulElement implements return createDivForElement(datesMapper, element); } + private static OrderElement getRootOrder(Task task) { + OrderElement order = task.getOrderElement(); + while (order.getParent() != null) { + order = order.getParent(); + } + return order; + } + + private static String createTooltiptext(LimitingResourceQueueElement element) { + final Task task = element.getResourceAllocation().getTask(); + final OrderElement order = getRootOrder(task); + + StringBuilder result = new StringBuilder(); + result.append(_("Order: {0} ", order.getName())); + result.append(_("Task: {0} ", task.getName())); + result.append(_("Completed: {0} ", getAdvancementEndDate(element))); + + final ResourceAllocation resourceAllocation = element.getResourceAllocation(); + if (resourceAllocation instanceof SpecificResourceAllocation) { + final SpecificResourceAllocation specific = (SpecificResourceAllocation) resourceAllocation; + result.append(_("Resource: {0} ", specific.getResource().getName())); + } else if (resourceAllocation instanceof GenericResourceAllocation) { + final GenericResourceAllocation generic = (GenericResourceAllocation) resourceAllocation; + result.append(_("Criteria: {0} ", Criterion.getNames(generic.getCriterions()))); + } + result.append("[" + element.getStartDate().toString() + "," + + element.getEndDate().toString() + "]"); + return result.toString(); + } + + private static DateAndHour getAdvancementEndDate(LimitingResourceQueueElement element) { + final Task task = element.getResourceAllocation().getTask(); + + int hoursWorked = 0; + final List dayAssignments = element.getDayAssignments(); + if (element.hasDayAssignments()) { + final int estimatedWorkedHours = estimatedWorkedHours(element.getIntentedTotalHours(), task.getAdvancePercentage()); + + for (DayAssignment each: dayAssignments) { + hoursWorked += each.getHours(); + if (hoursWorked >= estimatedWorkedHours) { + int hourSlot = each.getHours() - (hoursWorked - estimatedWorkedHours); + return new DateAndHour(each.getDay(), hourSlot); + } + } + } + if (hoursWorked != 0) { + DayAssignment lastDayAssignment = dayAssignments.get(dayAssignments.size() - 1); + return new DateAndHour(lastDayAssignment.getDay(), lastDayAssignment.getHours()); + } + return null; + } + + private static int estimatedWorkedHours(Integer totalHours, BigDecimal percentageWorked) { + return (totalHours != null && percentageWorked != null) ? percentageWorked.multiply( + new BigDecimal(totalHours)).intValue() : 0; + } + private static QueueTask createDivForElement(IDatesMapper datesMapper, LimitingResourceQueueElement queueElement) { QueueTask result = new QueueTask(queueElement); - result.setClass("queue-element"); + String cssClass = "queue-element"; - result.setTooltiptext(queueElement.getLimitingResourceQueue() - .getResource().getName()); + result.setTooltiptext(createTooltiptext(queueElement)); - result.setLeft(forCSS(getStartPixels(datesMapper, queueElement))); - result.setWidth(forCSS(getWidthPixels(datesMapper, queueElement))); + int startPixels = getStartPixels(datesMapper, queueElement); + result.setLeft(forCSS(startPixels)); + if (startPixels < 0) { + cssClass += " truncated-start "; + } - result.appendChild(generateNonWorkableShade(datesMapper, queueElement)); + int taskWidth = getWidthPixels(datesMapper, queueElement); + if ((startPixels + taskWidth) > datesMapper.getHorizontalSize()) { + taskWidth = datesMapper.getHorizontalSize() - startPixels; + cssClass += " truncated-end "; + } else { + result.appendChild(generateNonWorkableShade(datesMapper, + queueElement)); + } + result.setWidth(forCSS(taskWidth)); + + Task task = queueElement.getResourceAllocation().getTask(); + if (task.getDeadline() != null) { + int deadlinePosition = getDeadlinePixels(datesMapper, task + .getDeadline()); + if (deadlinePosition < datesMapper.getHorizontalSize()) { + Div deadline = new Div(); + deadline.setSclass("deadline"); + deadline + .setLeft((deadlinePosition - startPixels - DEADLINE_MARK_HALF_WIDTH) + + "px"); + result.appendChild(deadline); + result.appendChild(generateNonWorkableShade(datesMapper, + queueElement)); + } + if (task.getDeadline().isBefore(queueElement.getEndDate())) { + cssClass += " unmet-deadline "; + } + } + + + result.setClass(cssClass); + result.appendChild(generateCompletionShade(datesMapper, queueElement)); + result.appendChild(generateProgressBar(datesMapper, queueElement, task, + startPixels)); return result; } + private static Component generateProgressBar(IDatesMapper datesMapper, + LimitingResourceQueueElement queueElement, Task task, + int startPixels) { + DateAndHour advancementEndDate = getAdvancementEndDate(queueElement); + long millis = (advancementEndDate.toDateTime().getMillis() - queueElement + .getStartTime().toDateTime().getMillis()); + Div progressBar = new Div(); + if (!queueElement.getStartDate().isEqual(advancementEndDate.getDate())) { + progressBar.setWidth(datesMapper.toPixels(millis) + "px"); + progressBar.setSclass("queue-progress-bar"); + } + return progressBar; + } + private static Div generateNonWorkableShade(IDatesMapper datesMapper, LimitingResourceQueueElement queueElement) { @@ -165,6 +337,31 @@ public class QueueComponent extends XulElement implements return notWorkableHoursShade; } + private static Div generateCompletionShade(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.setContext(""); + notWorkableHoursShade.setSclass("limiting-completion"); + + notWorkableHoursShade.setStyle("left: " + shadeLeft + "px; width: " + + shadeWidth + "px;"); + return notWorkableHoursShade; + } + private static int getWidthPixels(IDatesMapper datesMapper, LimitingResourceQueueElement queueElement) { return datesMapper.toPixels(getEndMillis(queueElement) @@ -176,6 +373,14 @@ public class QueueComponent extends XulElement implements + (queueElement.getStartHour() * DatesMapperOnInterval.MILISECONDS_PER_HOUR); } + private static int getDeadlinePixels(IDatesMapper datesMapper, + LocalDate deadlineDate) { + // Deadline date is considered inclusively + return datesMapper.toPixelsAbsolute(deadlineDate.plusDays(1) + .toDateMidnight().getMillis()); + } + + private static long getEndMillis(LimitingResourceQueueElement queueElement) { return queueElement.getEndDate().toDateMidnight().getMillis() + (queueElement.getEndHour() * DatesMapperOnInterval.MILISECONDS_PER_HOUR); @@ -187,16 +392,22 @@ public class QueueComponent extends XulElement implements private static int getStartPixels(IDatesMapper datesMapper, LimitingResourceQueueElement queueElement) { - return datesMapper - .toPixelsAbsolute((queueElement.getStartDate().toDateMidnight() -.getMillis() + queueElement.getStartHour() + return datesMapper.toPixelsAbsolute((queueElement.getStartDate() + .toDateMidnight().getMillis() + queueElement.getStartHour() * DatesMapperOnInterval.MILISECONDS_PER_HOUR)); } + public void appendQueueElements(SortedSet elements) { + for (LimitingResourceQueueElement each : elements) { + appendQueueElement(each); + } + } + public void appendQueueElement(LimitingResourceQueueElement element) { QueueTask queueTask = createQueueTask(element); appendQueueTask(queueTask); appendMenu(queueTask); + addDependenciesInPanel(element); } private QueueTask createQueueTask(LimitingResourceQueueElement element) { @@ -204,6 +415,18 @@ public class QueueComponent extends XulElement implements return createDivForElement(timeTracker.getMapper(), element); } + private void addDependenciesInPanel(LimitingResourceQueueElement element) { + final LimitingResourcesPanel panel = getLimitingResourcesPanel(); + for (LimitingResourceQueueDependency each : element + .getDependenciesAsDestiny()) { + panel.addDependencyComponent(each); + } + for (LimitingResourceQueueDependency each : element + .getDependenciesAsOrigin()) { + panel.addDependencyComponent(each); + } + } + public String getResourceName() { return limitingResourceQueue.getResource().getName(); } @@ -227,14 +450,23 @@ public class QueueComponent extends XulElement implements unnasign(choosen); } }); + menuBuilder.item(_("Move"), "", + new ItemAction() { + @Override + public void onEvent(QueueTask choosen, Event event) { + moveQueueTask(choosen); + } + }); divElement.setContext(menuBuilder.createWithoutSettingContext()); } } + private void moveQueueTask(QueueTask queueTask) { + getLimitingResourcesPanel().moveQueueTask(queueTask); + } + private void unnasign(QueueTask choosen) { - final LimitingResourcesPanel panel = LimitingResourcesPanel - .getLimitingResourcesPanel(choosen.getParent()); - panel.unschedule(choosen); + getLimitingResourcesPanel().unschedule(choosen); } private void appendContextMenus() { @@ -248,4 +480,4 @@ public class QueueComponent extends XulElement implements appendContextMenus(); } -} \ No newline at end of file +} 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 index 5a7297de7..fe9acb173 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueListComponent.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueListComponent.java @@ -25,8 +25,7 @@ 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.planner.limiting.entities.LimitingResourceQueueElement; import org.navalplanner.business.resources.entities.LimitingResourceQueue; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; @@ -45,6 +44,8 @@ import org.zkoss.zk.ui.ext.AfterCompose; public class QueueListComponent extends HtmlMacroComponent implements AfterCompose { + private final LimitingResourcesPanel limitingResourcesPanel; + private final IZoomLevelChangedListener zoomListener; private MutableTreeModel model; @@ -53,9 +54,11 @@ public class QueueListComponent extends HtmlMacroComponent implements private Map fromQueueToComponent = new HashMap(); - public QueueListComponent(TimeTracker timeTracker, + public QueueListComponent(LimitingResourcesPanel limitingResourcesPanel, + TimeTracker timeTracker, MutableTreeModel timelinesTree) { + this.limitingResourcesPanel = limitingResourcesPanel; this.model = timelinesTree; zoomListener = adjustTimeTrackerSizeListener(); @@ -72,7 +75,7 @@ public class QueueListComponent extends HtmlMacroComponent implements } private void insertAsComponent(LimitingResourceQueue queue) { - QueueComponent component = QueueComponent.create(timeTracker, queue); + QueueComponent component = QueueComponent.create(this, timeTracker, queue); this.appendChild(component); fromQueueToComponent.put(queue, component); } @@ -83,7 +86,7 @@ public class QueueListComponent extends HtmlMacroComponent implements public void invalidate() { fromQueueToComponent.clear(); - this.getChildren().clear(); + getChildren().clear(); insertAsComponents(model.asList()); super.invalidate(); } @@ -92,20 +95,12 @@ public class QueueListComponent extends HtmlMacroComponent implements 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); - } + public void refreshQueue(LimitingResourceQueue queue) { + QueueComponent queueComponent = fromQueueToComponent.get(queue); + queueComponent.setLimitingResourceQueue(queue); + queueComponent.invalidate(); } private IZoomLevelChangedListener adjustTimeTrackerSizeListener() { @@ -113,6 +108,9 @@ public class QueueListComponent extends HtmlMacroComponent implements @Override public void zoomLevelChanged(ZoomLevel detailLevel) { + + invalidate(); + afterCompose(); response(null, new AuInvoke(QueueListComponent.this, "adjustTimeTrackerSize")); response(null, new AuInvoke(QueueListComponent.this, @@ -127,6 +125,8 @@ public class QueueListComponent extends HtmlMacroComponent implements for (QueueComponent each : fromQueueToComponent.values()) { each.afterCompose(); } + response(null, new AuInvoke(QueueListComponent.this, + "adjustResourceLoadRows")); } public List getQueueTasks() { @@ -145,4 +145,8 @@ public class QueueListComponent extends HtmlMacroComponent implements return result; } + public LimitingResourcesPanel getLimitingResourcePanel() { + return limitingResourcesPanel; + } + } 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 index 45dc000a4..8f51751d0 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTask.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTask.java @@ -25,7 +25,7 @@ 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.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; @@ -50,11 +50,10 @@ public class QueueTask extends Div { this.start = element.getStartDate(); this.end = element.getEndDate(); this.element = element; - setAction("onmouseover: zkLimitingDependencies.showDependenciesForQueueElement('" + setAction("onmouseover: zkLimitingResourcesList.showRelatedElementsForQueueElement('" + getUuid() - + "');onmouseout: zkLimitingDependencies.hideDependenciesForQueueElement('" + + "');onmouseout: zkLimitingResourcesList.hideRelatedElementsForQueueElement('" + getUuid() + "')"); - final String taskUid = this.getUuid(); this.addEventListener(Events.ON_CLICK, new EventListener() { @Override diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueuesState.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueuesState.java new file mode 100644 index 000000000..6f065406a --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueuesState.java @@ -0,0 +1,369 @@ +/* + * 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.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.Validate; +import org.jgrapht.DirectedGraph; +import org.jgrapht.alg.CycleDetector; +import org.jgrapht.graph.SimpleDirectedGraph; +import org.jgrapht.traverse.TopologicalOrderIterator; +import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.SpecificResourceAllocation; +import org.navalplanner.business.planner.limiting.entities.GapRequirements; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueDependency; +import org.navalplanner.business.planner.limiting.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.limiting.entities.Gap.GapOnQueue; +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; + +/** + * @author Óscar González Fernández + */ +public class QueuesState { + + private final List queues; + + private final List unassignedElements; + + private final DirectedGraph graph; + + private final Map queuesById; + + private final Map elementsById; + + private final Map queuesByResourceId; + + private static Map byId( + Collection entities) { + Map result = new HashMap(); + for (T each : entities) { + result.put(each.getId(), each); + } + return result; + } + + private static Map byResourceId( + Collection limitingResourceQueues) { + Map result = new HashMap(); + for (LimitingResourceQueue each : limitingResourceQueues) { + result.put(each.getResource().getId(), each); + } + return result; + } + + public QueuesState( + List limitingResourceQueues, + List unassignedLimitingResourceQueueElements) { + this.queues = new ArrayList( + limitingResourceQueues); + this.unassignedElements = new ArrayList( + unassignedLimitingResourceQueueElements); + this.queuesById = byId(queues); + this.elementsById = byId(allElements(limitingResourceQueues, + unassignedLimitingResourceQueueElements)); + this.queuesByResourceId = byResourceId(limitingResourceQueues); + this.graph = buildGraph(getAllElements(unassignedElements, queues)); + } + + private static DirectedGraph buildGraph( + List allElements) { + DirectedGraph result = instantiateDirectedGraph(); + for (LimitingResourceQueueElement each : allElements) { + result.addVertex(each); + } + for (LimitingResourceQueueElement each : allElements) { + Set dependenciesAsOrigin = each + .getDependenciesAsOrigin(); + for (LimitingResourceQueueDependency eachDependency : dependenciesAsOrigin) { + addDependency(result, eachDependency); + } + } + return result; + } + + private static SimpleDirectedGraph instantiateDirectedGraph() { + return new SimpleDirectedGraph( + LimitingResourceQueueDependency.class); + } + + private static void addDependency( + DirectedGraph result, + LimitingResourceQueueDependency dependency) { + LimitingResourceQueueElement origin = dependency.getHasAsOrigin(); + LimitingResourceQueueElement destination = dependency.getHasAsDestiny(); + result.addVertex(origin); + result.addVertex(destination); + result.addEdge(origin, destination, dependency); + } + + private static List getAllElements( + List unassigned, + List queues) { + List result = new ArrayList(); + result.addAll(unassigned); + for (LimitingResourceQueue each : queues) { + result.addAll(each.getLimitingResourceQueueElements()); + } + return result; + } + + private List allElements( + List queues, + List unassigned) { + List result = new ArrayList(); + for (LimitingResourceQueue each : queues) { + result.addAll(each.getLimitingResourceQueueElements()); + } + result.addAll(unassigned); + return result; + } + + public List getQueues() { + return Collections.unmodifiableList(queues); + } + + public List getUnassigned() { + return Collections.unmodifiableList(unassignedElements); + } + + public void assignedToQueue(LimitingResourceQueueElement element, + LimitingResourceQueue queue) { + Validate.isTrue(unassignedElements.contains(element)); + queue.addLimitingResourceQueueElement(element); + unassignedElements.remove(element); + } + + public LimitingResourceQueue getEquivalent(LimitingResourceQueue queue) { + return queuesById.get(queue.getId()); + } + + public LimitingResourceQueueElement getEquivalent( + LimitingResourceQueueElement element) { + return elementsById.get(element.getId()); + } + + public void unassingFromQueue(LimitingResourceQueueElement externalElement) { + LimitingResourceQueueElement queueElement = getEquivalent(externalElement); + LimitingResourceQueue queue = queueElement.getLimitingResourceQueue(); + if (queue != null) { + queue.removeLimitingResourceQueueElement(queueElement); + unassignedElements.add(queueElement); + } + } + + public void removeUnassigned(LimitingResourceQueueElement queueElement) { + unassignedElements.remove(queueElement); + } + + public LimitingResourceQueue getQueueFor(Resource resource) { + return queuesByResourceId.get(resource.getId()); + } + + public List getAssignableQueues( + LimitingResourceQueueElement element) { + final ResourceAllocation resourceAllocation = element + .getResourceAllocation(); + if (resourceAllocation instanceof SpecificResourceAllocation) { + LimitingResourceQueue queue = getQueueFor(element + .getResource()); + Validate.notNull(queue); + return Collections.singletonList(queue); + } else if (resourceAllocation instanceof GenericResourceAllocation) { + final GenericResourceAllocation generic = (GenericResourceAllocation) element + .getResourceAllocation(); + return findQueuesMatchingCriteria(generic.getCriterions()); + } + throw new RuntimeException("unexpected type of: " + resourceAllocation); + } + + public GapRequirements getRequirementsFor( + LimitingResourceQueueElement element) { + List dependenciesStart = new ArrayList(); + List dependenciesEnd = new ArrayList(); + fillIncoming(element, dependenciesStart, dependenciesEnd); + return GapRequirements.forElement(getEquivalent(element), + dependenciesStart, dependenciesEnd); + } + + private void fillIncoming(LimitingResourceQueueElement element, + List dependenciesStart, + List dependenciesEnd) { + Set incoming = graph + .incomingEdgesOf(element); + for (LimitingResourceQueueDependency each : incoming) { + List addingTo = each + .modifiesDestinationStart() ? dependenciesStart + : dependenciesEnd; + if (each.isOriginNotDetached()) { + addingTo.add(each); + } else { + fillIncoming(each, addingTo); + } + } + } + + private void fillIncoming(LimitingResourceQueueDependency next, + List result) { + Set incoming = graph + .incomingEdgesOf(next.getHasAsOrigin()); + for (LimitingResourceQueueDependency each : incoming) { + if (each.propagatesThrough(next)) { + if (each.isOriginNotDetached()) { + result.add(each); + } else { + fillIncoming(each, result); + } + } + } + } + + /** + * @return all the gaps that could potentially fit element + * ordered by start date + */ + public List getPotentiallyValidGapsFor( + GapRequirements requirements) { + List assignableQueues = getAssignableQueues(requirements + .getElement()); + List> allGaps = gapsFor(assignableQueues, requirements); + return GapsMergeSort.sort(allGaps); + } + + private List> gapsFor( + List assignableQueues, + GapRequirements requirements) { + List> result = new ArrayList>(); + for (LimitingResourceQueue each : assignableQueues) { + result.add(each.getGapsPotentiallyValidFor(requirements)); + } + return result; + } + + private List findQueuesMatchingCriteria( + Set criteria) { + List result = new ArrayList(); + final ICriterion compositedCriterion = CriterionCompounder.buildAnd( + criteria).getResult(); + for (LimitingResourceQueue each : queues) { + if (compositedCriterion.isSatisfiedBy(each.getResource())) { + result.add(each); + } + } + return result; + } + + /** + * @param externalQueueElement + * the queue element to insert + * @return the list of elements that must be reinserted due to the insertion + * of externalQueueElement + */ + public List getInsertionsToBeDoneFor( + LimitingResourceQueueElement externalQueueElement) { + LimitingResourceQueueElement queueElement = getEquivalent(externalQueueElement); + DirectedGraph subGraph = buildOutgoingGraphFor(queueElement); + CycleDetector cycleDetector = cycleDetector(subGraph); + if (cycleDetector.detectCycles()) { + throw new IllegalStateException("subgraph has cycles"); + } + List result = new ArrayList(); + result.add(queueElement); + result.addAll(getElementsOrderedTopologically(subGraph)); + unassignFromQueues(result); + return result; + } + + private DirectedGraph buildOutgoingGraphFor( + LimitingResourceQueueElement queueElement) { + SimpleDirectedGraph result = instantiateDirectedGraph(); + buildOutgoingGraphFor(result, queueElement); + return result; + } + + private void buildOutgoingGraphFor( + DirectedGraph result, + LimitingResourceQueueElement element) { + Set outgoingEdgesOf = graph + .outgoingEdgesOf(element); + for (LimitingResourceQueueDependency each : outgoingEdgesOf) { + addDependency(result, each); + buildOutgoingGraphFor(result, each.getHasAsDestiny()); + } + } + + private CycleDetector cycleDetector( + DirectedGraph subGraph) { + return new CycleDetector( + subGraph); + } + + private List getElementsOrderedTopologically( + DirectedGraph subGraph) { + return onlyAssigned(toList(topologicalIterator(subGraph))); + } + + private TopologicalOrderIterator topologicalIterator( + DirectedGraph subGraph) { + return new TopologicalOrderIterator( + subGraph); + } + + private static List toList(final Iterator iterator) { + List result = new ArrayList(); + while (iterator.hasNext()) { + result.add(iterator.next()); + } + return result; + } + + private List onlyAssigned( + List list) { + List result = new ArrayList(); + for (LimitingResourceQueueElement each : list) { + if (!each.isDetached()) { + result.add(each); + } + } + return result; + } + + private void unassignFromQueues(List result) { + for (LimitingResourceQueueElement each : result) { + if (!each.isDetached()) { + unassingFromQueue(each); + } + } + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java index 7acbbbe40..2562d25aa 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java @@ -49,7 +49,7 @@ public interface IManageOrderElementAdvancesModel { public void initEdit(OrderElement orderElement); - public void addNewLineAdvaceAssignment(); + public boolean addNewLineAdvaceAssignment(); public void addNewLineAdvaceMeasurement(); @@ -108,4 +108,12 @@ public interface IManageOrderElementAdvancesModel { AdvanceMeasurement advanceMeasurement); public void resetAdvanceAssignment(); + + BigDecimal getMaxValue(AdvanceType advanceType); + + AdvanceAssignment getSpreadAdvance(); + + void createPercentageAdvances(OrderElement orderElement) + throws DuplicateAdvanceAssignmentForOrderElementException, + DuplicateValueTrueReportGlobalAdvanceException; } 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 d54407b0c..b3d27a6d5 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 @@ -62,6 +62,7 @@ import org.zkoss.zul.Comboitem; import org.zkoss.zul.Constraint; import org.zkoss.zul.Datebox; import org.zkoss.zul.Decimalbox; +import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Listbox; import org.zkoss.zul.Listcell; @@ -151,9 +152,16 @@ public class ManageOrderElementAdvancesController extends public void openWindow(IOrderElementModel orderElementModel) { setOrderElementModel(orderElementModel); manageOrderElementAdvancesModel.initEdit(getOrderElement()); - this.indexSelectedItem = -1; selectedAdvances.clear(); createAndLoadBindings(); + selectSpreadAdvanceLine(); + } + + public void openWindow(OrderElement orderElement) { + manageOrderElementAdvancesModel.initEdit(orderElement); + selectedAdvances.clear(); + createAndLoadBindings(); + selectSpreadAdvanceLine(); } public void createAndLoadBindings() { @@ -183,47 +191,79 @@ public class ManageOrderElementAdvancesController extends } } - private void resetAdvancesGrid() { - manageOrderElementAdvancesModel.resetAdvanceAssignment(); - this.indexSelectedItem = -1; - reloadAdvances(); - } - private void reloadAdvances() { Util.reloadBindings(self); resetScreenHeight(); - if (indexSelectedItem > -1) { + setSelectedAdvanceLine(); + } + + private void setSelectedAdvanceLine() { + if ((indexSelectedItem > -1) + && (indexSelectedItem < editAdvances.getItemCount())) { editAdvances.setSelectedItem(editAdvances - .getItemAtIndex(indexSelectedItem)); + .getItemAtIndex(indexSelectedItem)); editAdvances.invalidate(); } } private Listbox editAdvances; - public void prepareEditAdvanceMeasurements(Listitem selectedItem) { - AdvanceAssignment advanceAssignment = (AdvanceAssignment) selectedItem - .getValue(); - if (advanceAssignment.getAdvanceType() != null) { - validateListAdvanceMeasurement(); - manageOrderElementAdvancesModel - .prepareEditAdvanceMeasurements(advanceAssignment); - this.indexSelectedItem = editAdvances.getIndexOfItem(editAdvances - .getSelectedItem()); - reloadAdvances(); + public void selectAdvanceLine(Listitem selectedItem) { + /* + * validate the previous advance line before changing the selected + * advance. + */ + setSelectedAdvanceLine(); + validateListAdvanceMeasurement(); + + /* + * preparation to select the advance line. Set the current selected + * index that will show when the grid reloads. + */ + AdvanceAssignment advance = (AdvanceAssignment) selectedItem.getValue(); + indexSelectedItem = editAdvances.getIndexOfItem(selectedItem); + prepareEditAdvanceMeasurements(advance); + reloadAdvances(); + } + + public void selectAdvanceLine(int index) { + indexSelectedItem = index; + if ((indexSelectedItem >= 0) + && (indexSelectedItem < getAdvanceAssignments().size())) { + prepareEditAdvanceMeasurements(getAdvanceAssignments().get( + indexSelectedItem)); + } + reloadAdvances(); + } + + public void selectSpreadAdvanceLine() { + AdvanceAssignment advance = manageOrderElementAdvancesModel + .getSpreadAdvance(); + if (advance != null) { + indexSelectedItem = getAdvanceAssignments().indexOf(advance); + prepareEditAdvanceMeasurements(advance); } else { - Component comboAdvanceType = selectedItem.getFirstChild() - .getFirstChild(); - if (comboAdvanceType instanceof Combobox) { - throw new WrongValueException(comboAdvanceType, - _("should select a advance type")); - } + selectAdvanceLine(getAdvanceAssignments().size() - 1); + } + reloadAdvances(); + } + + public void prepareEditAdvanceMeasurements(AdvanceAssignment advance) { + if (advance != null && advance.getAdvanceType() != null) { + manageOrderElementAdvancesModel + .prepareEditAdvanceMeasurements(advance); } } public void goToCreateLineAdvanceAssignment() { - manageOrderElementAdvancesModel.addNewLineAdvaceAssignment(); - resetAdvancesGrid(); + validateListAdvanceMeasurement(); + boolean fineResult = manageOrderElementAdvancesModel + .addNewLineAdvaceAssignment(); + if (fineResult) { + selectAdvanceLine(getAdvanceAssignments().size() - 1); + } else { + showMessageNotAddMoreAdvances(); + } } public void goToCreateLineAdvanceMeasurement() { @@ -231,29 +271,39 @@ public class ManageOrderElementAdvancesController extends reloadAdvances(); } - public void goToRemoveLineAdvanceAssignment(){ - Listitem listItem = editAdvances.getItemAtIndex(indexSelectedItem); - if (listItem != null) { - AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem - .getValue(); + public void goToRemoveLineAdvanceAssignment(Listitem listItem) { + AdvanceAssignment advance = (AdvanceAssignment) listItem.getValue(); + if ((editAdvances.getItemCount() > 1) + && (advance.getReportGlobalAdvance())) { + showMessageDeleteSpread(); + } else if (manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advance)) { + showMessagesConsolidation(1); + } else { manageOrderElementAdvancesModel - .removeLineAdvanceAssignment(advanceAssignment); - reloadAdvances(); + .removeLineAdvanceAssignment(advance); + if (indexSelectedItem == editAdvances.getIndexOfItem(listItem)) { + selectSpreadAdvanceLine(); + } else { + if (indexSelectedItem > editAdvances.getIndexOfItem(listItem)) { + selectAdvanceLine(indexSelectedItem - 1); + } else { + reloadAdvances(); + } + } } } private Listbox editAdvancesMeasurement; - public void goToRemoveLineAdvanceMeasurement(){ - Listitem selectedItem = editAdvancesMeasurement.getSelectedItem(); - if(selectedItem != null){ - AdvanceMeasurement advanceMeasurement = (AdvanceMeasurement) selectedItem - .getValue(); - if (advanceMeasurement != null) { - manageOrderElementAdvancesModel - .removeLineAdvanceMeasurement(advanceMeasurement); - reloadAdvances(); - } + public void goToRemoveLineAdvanceMeasurement(Listitem listItem) { + AdvanceMeasurement advance = (AdvanceMeasurement) listItem.getValue(); + if (manageOrderElementAdvancesModel.hasConsolidatedAdvances(advance)) { + showMessagesConsolidation(2); + } else { + manageOrderElementAdvancesModel + .removeLineAdvanceMeasurement(advance); + reloadAdvances(); } } @@ -298,7 +348,7 @@ public class ManageOrderElementAdvancesController extends && ((DirectAdvanceAssignment) advance) .getAdvanceMeasurements().isEmpty() && !isQualityForm) { - appendComboboxAdvancType(listItem); + appendComboboxAdvanceType(listItem); } else { appendLabelAdvanceType(listItem); } @@ -309,16 +359,17 @@ public class ManageOrderElementAdvancesController extends appendRadioSpread(listItem); appendCalculatedCheckbox(listItem); appendChartCheckbox(listItem); - appendRemoveButton(listItem); + appendOperations(listItem); } } - private void appendComboboxAdvancType(final Listitem listItem){ + private void appendComboboxAdvanceType(final Listitem listItem) { final DirectAdvanceAssignment advance = (DirectAdvanceAssignment) listItem .getValue(); final Combobox comboAdvanceTypes = new Combobox(); final List listAdvanceType = manageOrderElementAdvancesModel .getPossibleAdvanceTypes(advance); + for(AdvanceType advanceType : listAdvanceType){ if (!advanceType.getUnitName().equals( PredefinedAdvancedTypes.CHILDREN.getTypeName()) @@ -335,14 +386,15 @@ public class ManageOrderElementAdvancesController extends } } } + comboAdvanceTypes.addEventListener(Events.ON_SELECT, new EventListener() { @Override public void onEvent(Event event) throws Exception { - setMaxValue(listItem,comboAdvanceTypes); + setMaxValue(listItem, comboAdvanceTypes); cleanFields(advance); setPercentage(); - resetAdvancesGrid(); + reloadAdvances(); } }); @@ -359,7 +411,8 @@ public class ManageOrderElementAdvancesController extends (comboItem.getValue() instanceof AdvanceType)){ AdvanceType advanceType = (AdvanceType)comboItem.getValue(); advance.setAdvanceType(advanceType); - advance.setMaxValue(getMaxValue(advanceType)); + advance.setMaxValue(manageOrderElementAdvancesModel + .getMaxValue(advanceType)); } } @@ -369,12 +422,7 @@ public class ManageOrderElementAdvancesController extends listItem.appendChild(listCell); } - private BigDecimal getMaxValue(AdvanceType advanceType) { - if (advanceType != null) { - return advanceType.getDefaultMaxValue(); - } - return BigDecimal.ZERO; - } + private void appendLabelAdvanceType(final Listitem listItem){ final AdvanceAssignment advance = (AdvanceAssignment) listItem.getValue(); @@ -584,7 +632,48 @@ public class ManageOrderElementAdvancesController extends listItem.appendChild(listCell); } - private void appendRemoveButton(final Listitem listItem) { + private void appendOperations(final Listitem listItem) { + Hbox hbox = new Hbox(); + appendAddMeasurement(hbox, listItem); + appendRemoveButton(hbox, listItem); + + Listcell listCell = new Listcell(); + listCell.appendChild(hbox); + listItem.appendChild(listCell); + } + + private void appendAddMeasurement(final Hbox hbox, final Listitem listItem) { + final AdvanceAssignment advance = (AdvanceAssignment) listItem + .getValue(); + final Button addMeasurementButton = createAddMeasurementButton(); + + addMeasurementButton.addEventListener(Events.ON_CLICK, + new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + if (!listItem.equals(editAdvances.getSelectedItem())) { + selectAdvanceLine(listItem); + } + goToCreateLineAdvanceMeasurement(); + } + }); + + if ((advance.getAdvanceType() != null) + && (advance.getAdvanceType().isQualityForm())) { + addMeasurementButton.setDisabled(true); + addMeasurementButton + .setTooltiptext(_("Advances that are reported by quality forms can not be modified")); + } else if (advance instanceof IndirectAdvanceAssignment) { + addMeasurementButton.setDisabled(true); + addMeasurementButton + .setTooltiptext(_("Calculated advances can not be modified")); + } + + hbox.appendChild(addMeasurementButton); + + } + + private void appendRemoveButton(final Hbox hbox, final Listitem listItem) { final AdvanceAssignment advance = (AdvanceAssignment) listItem .getValue(); final Button removeButton = createRemoveButton(); @@ -592,30 +681,22 @@ public class ManageOrderElementAdvancesController extends removeButton.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - if (manageOrderElementAdvancesModel - .hasConsolidatedAdvances(advance)) { - showMessagesConsolidation(1); - } else { - manageOrderElementAdvancesModel - .removeLineAdvanceAssignment(advance); - if (indexSelectedItem == editAdvances - .getIndexOfItem(listItem)) { - indexSelectedItem = -1; - } - reloadAdvances(); - } + goToRemoveLineAdvanceAssignment(listItem); } }); - if (advance instanceof IndirectAdvanceAssignment) { + if ((advance.getAdvanceType() != null) + && (advance.getAdvanceType().isQualityForm())) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("Advances that are reported by quality forms can not be modified")); + } else if (advance instanceof IndirectAdvanceAssignment) { removeButton.setDisabled(true); removeButton .setTooltiptext(_("Calculated advances can not be removed")); } - Listcell listCell = new Listcell(); - listCell.appendChild(removeButton); - listItem.appendChild(listCell); + hbox.appendChild(removeButton); } private void setMaxValue(final Listitem item,Combobox comboAdvanceTypes) { @@ -627,8 +708,10 @@ public class ManageOrderElementAdvancesController extends if(advanceType != null){ DirectAdvanceAssignment advance = (DirectAdvanceAssignment) item .getValue(); - advance.setMaxValue(getMaxValue(advanceType)); - miBox.setValue(getMaxValue(advanceType)); + advance.setMaxValue(manageOrderElementAdvancesModel + .getMaxValue(advanceType)); + miBox.setValue(manageOrderElementAdvancesModel + .getMaxValue(advanceType)); miBox.invalidate(); } } @@ -802,23 +885,26 @@ public class ManageOrderElementAdvancesController extends Listitem listItem = (Listitem) editAdvances.getChildren().get(i); AdvanceAssignment advance = (AdvanceAssignment) listItem .getValue(); - if (advance.getAdvanceType() == null) { - throw new WrongValueException(getComboboxTypeBy(listItem), + if (advance != null) { + if (advance.getAdvanceType() == null) { + throw new WrongValueException( + getComboboxTypeBy(listItem), _("Value is not valid, the type must be not empty")); - } + } - DirectAdvanceAssignment directAdvanceAssignment; - if (advance instanceof IndirectAdvanceAssignment) { - directAdvanceAssignment = manageOrderElementAdvancesModel + DirectAdvanceAssignment directAdvanceAssignment; + if (advance instanceof IndirectAdvanceAssignment) { + directAdvanceAssignment = manageOrderElementAdvancesModel .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advance); - } else { - directAdvanceAssignment = (DirectAdvanceAssignment) advance; - } - if (directAdvanceAssignment != null + } else { + directAdvanceAssignment = (DirectAdvanceAssignment) advance; + } + if (directAdvanceAssignment != null && directAdvanceAssignment.getMaxValue() == null) { - throw new WrongValueException( - getDecimalboxMaxValueBy(listItem), - _("Value is not valid, the current value must be not empty")); + throw new WrongValueException( + getDecimalboxMaxValueBy(listItem), + _("Value is not valid, the current value must be not empty")); + } } } } @@ -876,7 +962,8 @@ public class ManageOrderElementAdvancesController extends AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem .getValue(); existItems = true; - if (advanceAssignment.getReportGlobalAdvance()) { + if ((advanceAssignment != null) + && (advanceAssignment.getReportGlobalAdvance())) { return true; } } @@ -906,7 +993,6 @@ public class ManageOrderElementAdvancesController extends final AdvanceMeasurement advanceMeasurement = (AdvanceMeasurement) listitem .getValue(); final Decimalbox value = new Decimalbox(); - Listcell listcell = new Listcell(); listcell.appendChild(value); listitem.appendChild(listcell); @@ -1101,21 +1187,28 @@ public class ManageOrderElementAdvancesController extends } private void appendRemoveButton(final Listitem listItem) { - final AdvanceMeasurement advance = (AdvanceMeasurement) listItem + + final AdvanceMeasurement measure = (AdvanceMeasurement) listItem .getValue(); final Button removeButton = createRemoveButton(); + DirectAdvanceAssignment advance = (DirectAdvanceAssignment) measure + .getAdvanceAssignment(); + if ((advance.getAdvanceType() != null) + && (advance.getAdvanceType().isQualityForm())) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("Advances measurements that are reported by quality forms can not be removed")); + } else if (advance.isFake()) { + removeButton.setDisabled(true); + removeButton + .setTooltiptext(_("Calculated advances measurement can not be removed")); + } + removeButton.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - if (manageOrderElementAdvancesModel - .hasConsolidatedAdvances(advance)) { - showMessagesConsolidation(2); - } else { - manageOrderElementAdvancesModel - .removeLineAdvanceMeasurement(advance); - reloadAdvances(); - } + goToRemoveLineAdvanceMeasurement(listItem); } }); @@ -1140,18 +1233,45 @@ public class ManageOrderElementAdvancesController extends return removeButton; } + private Button createAddMeasurementButton() { + Button addButton = new Button(); + addButton.setLabel("Add measure"); + addButton.setTooltiptext(_("Add new advnace measurement")); + return addButton; + } + public void refreshChangesFromOrderElement() { manageOrderElementAdvancesModel.refreshChangesFromOrderElement(); } + private void showMessageNotAddMoreAdvances() { + String message = _("All advance types have already been assigned."); + increaseScreenHeight(); + messagesForUser.showMessage(Level.ERROR, message); + } + + public void refreshSelectedAdvance() { + if ((indexSelectedItem < 0) + || (indexSelectedItem >= getAdvanceAssignments().size())) { + selectSpreadAdvanceLine(); + } + selectAdvanceLine(indexSelectedItem); + } + + private void showMessageDeleteSpread() { + String message = _("This advance can not be removed, because is spread. it is necessary to select another advance as spread.."); + increaseScreenHeight(); + messagesForUser.showMessage(Level.ERROR, message); + } + private void showMessagesConsolidation(int opcion) { String message = ""; switch (opcion) { case 1: - message = _("This advance can not be changed or removed, because it has got consolidated advances. It is needed removing the consolidation on all its advances."); + message = _("This advance can not be changed or removed, because it has got consolidated advances. It is needed to remove the consolidation on all its advances."); break; case 2: - message = _("This advance measurement can not be changed or removed, because it is consolidated. It is needed removing its consolidation."); + message = _("This advance measurement can not be changed or removed, because it is consolidated. It is needed to remove its consolidation."); break; } if (!StringUtils.isBlank(message)) { @@ -1160,4 +1280,13 @@ public class ManageOrderElementAdvancesController extends } } + public void createPercentageAdvances(IOrderElementModel orderElementModel) + throws DuplicateAdvanceAssignmentForOrderElementException, + DuplicateValueTrueReportGlobalAdvanceException { + setOrderElementModel(orderElementModel); + manageOrderElementAdvancesModel.initEdit(getOrderElement()); + manageOrderElementAdvancesModel + .createPercentageAdvances(getOrderElement()); + } + } 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 d696d0dc2..d05f0d19b 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 @@ -27,6 +27,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; @@ -52,7 +53,6 @@ import org.navalplanner.business.advance.exceptions.DuplicateValueTrueReportGlob import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.orders.daos.IOrderElementDAO; import org.navalplanner.business.orders.entities.OrderElement; -import org.navalplanner.business.orders.entities.OrderLineGroup; import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidatedValue; import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidation; import org.springframework.beans.factory.annotation.Autowired; @@ -144,7 +144,8 @@ public class ManageOrderElementAdvancesModel implements listAdvanceAssignments); fillVariables(); for (AdvanceAssignment advance : listAdvanceAssignmentsCopy) { - if (!listAdvanceAssignments.contains(advance)) { + if ((!listAdvanceAssignments.contains(advance)) + && (advance instanceof DirectAdvanceAssignment)) { listAdvanceAssignments.add(advance); } } @@ -153,17 +154,60 @@ public class ManageOrderElementAdvancesModel implements @Override public void prepareEditAdvanceMeasurements(AdvanceAssignment assignment) { if (assignment instanceof IndirectAdvanceAssignment) { - this.advanceAssignment = ((OrderLineGroup) this.orderElement) + this.advanceAssignment = orderElement .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) assignment); this.isIndirectAdvanceAssignment = true; } else { - if (assignment instanceof DirectAdvanceAssignment) { - this.advanceAssignment = (DirectAdvanceAssignment) assignment; - this.isIndirectAdvanceAssignment = false; - } + if (assignment instanceof DirectAdvanceAssignment) { + this.advanceAssignment = (DirectAdvanceAssignment) assignment; + this.isIndirectAdvanceAssignment = false; + } } } + public void createPercentageAdvances(OrderElement orderElement) + throws DuplicateAdvanceAssignmentForOrderElementException, + DuplicateValueTrueReportGlobalAdvanceException { + + if (orderElement != null) { + DirectAdvanceAssignment advancePercentage = orderElement + .getAdvanceAssignmentByType(PredefinedAdvancedTypes.PERCENTAGE + .getType()); + + boolean existDirectPercentageAdvance = ((advancePercentage != null) && (!advancePercentage + .isFake())); + + if ((orderElement.isSchedulingPoint()) + && (orderElement.getReportGlobalAdvanceAssignment() == null) + && (!existDirectPercentageAdvance)) { + createPercentageAdvance(orderElement); + + } else if (!existDirectPercentageAdvance) { + + for (OrderElement child : orderElement.getChildren()) { + createPercentageAdvances(child); + } + } + } + } + + private void createPercentageAdvance(OrderElement orderElement) + throws DuplicateAdvanceAssignmentForOrderElementException, + DuplicateValueTrueReportGlobalAdvanceException { + DirectAdvanceAssignment newAdvance = DirectAdvanceAssignment.create(); + newAdvance.setOrderElement(orderElement); + + for (AdvanceType type : this.listAdvanceTypes) { + if (type.getUnitName().equals( + PredefinedAdvancedTypes.PERCENTAGE.getTypeName())) { + newAdvance.setAdvanceType(type); + newAdvance.setMaxValue(getMaxValue(type)); + } + } + newAdvance.setReportGlobalAdvance(true); + orderElement.addAdvanceAssignment(newAdvance); + } + @Override @Transactional(readOnly = true) public void initEdit(OrderElement orderElement) { @@ -182,14 +226,17 @@ public class ManageOrderElementAdvancesModel implements .getDirectAdvanceAssignments()) { forceLoadAdvanceConsolidatedValues(each); each.getNonCalculatedConsolidation().size(); + each.getAdvanceType().getUnitName(); } - if (orderElement instanceof OrderLineGroup) { - ((OrderLineGroup) orderElement).getIndirectAdvanceAssignments().size(); - for (IndirectAdvanceAssignment each : ((OrderLineGroup) orderElement) + + for (IndirectAdvanceAssignment each : orderElement .getIndirectAdvanceAssignments()) { - each.getCalculatedConsolidation().size(); - } + each.getCalculatedConsolidation().size(); + each.getAdvanceType().getUnitName(); + forceLoadAdvanceConsolidatedValues(orderElement + .calculateFakeDirectAdvanceAssignment(each)); } + } private void forceLoadAdvanceConsolidatedValues( @@ -211,23 +258,51 @@ public class ManageOrderElementAdvancesModel implements this.listAdvanceAssignments.add(each); } - if (this.orderElement instanceof OrderLineGroup) { - for (IndirectAdvanceAssignment each : ((OrderLineGroup) this.orderElement) - .getIndirectAdvanceAssignments()) { + for (IndirectAdvanceAssignment each : orderElement + .getIndirectAdvanceAssignments()) { this.listAdvanceAssignments.add(each); } - } } @Override - public void addNewLineAdvaceAssignment() { + public boolean addNewLineAdvaceAssignment() { DirectAdvanceAssignment newAdvance = DirectAdvanceAssignment.create(); newAdvance.setOrderElement(this.orderElement); + /* + * set the first advance type of the list as the default + */ + List listAdvanceType = getPossibleAdvanceTypes(newAdvance); + if (!listAdvanceType.isEmpty()) { + newAdvance.setAdvanceType(listAdvanceType.get(0)); + newAdvance.setMaxValue(getMaxValue(listAdvanceType.get(0))); + } else { + return false; + } + if (listAdvanceAssignments.isEmpty()) { newAdvance.setReportGlobalAdvance(true); } listAdvanceAssignments.add(newAdvance); + return true; + } + + @Override + public BigDecimal getMaxValue(AdvanceType advanceType) { + if (advanceType != null) { + return advanceType.getDefaultMaxValue(); + } + return BigDecimal.ZERO; + } + + @Override + public AdvanceAssignment getSpreadAdvance() { + for(AdvanceAssignment advance : getAdvanceAssignments()){ + if(advance.getReportGlobalAdvance()){ + return advance; + } + } + return null; } @Override @@ -279,6 +354,27 @@ public class ManageOrderElementAdvancesModel implements } advanceTypes.add(advanceType); } + return getSpecificOrder(advanceTypes); + } + + private List getSpecificOrder(List advanceTypes ){ + Collections.sort(advanceTypes, new Comparator(){ + + @Override + public int compare(AdvanceType arg0, AdvanceType arg1) { + if((arg0 == null) || (arg0.getUnitName() == null)){ + return -1; + } + if((arg1 == null) || (arg1.getUnitName() == null) || (arg1.getUnitName().equals(PredefinedAdvancedTypes.PERCENTAGE.getTypeName()))){ + return 1; + } + if (arg0.getUnitName().equals( + PredefinedAdvancedTypes.PERCENTAGE.getTypeName())) { + return -1; + } + return (arg0.getUnitName().compareTo(arg1.getUnitName())); + } + }); return advanceTypes; } @@ -463,15 +559,17 @@ public class ManageOrderElementAdvancesModel implements @Override public void sortListAdvanceMeasurement() { - ArrayList advanceMeasurements = new ArrayList( - this.advanceAssignment.getAdvanceMeasurements()); - Collections.sort(advanceMeasurements, + if (advanceAssignment != null) { + ArrayList advanceMeasurements = new ArrayList( + advanceAssignment.getAdvanceMeasurements()); + Collections.sort(advanceMeasurements, new AdvanceMeasurementComparator()); - TreeSet measurements = new TreeSet( + TreeSet measurements = new TreeSet( new AdvanceMeasurementComparator()); - measurements.addAll(advanceMeasurements); - this.advanceAssignment + measurements.addAll(advanceMeasurements); + this.advanceAssignment .setAdvanceMeasurements(measurements); + } } @Override @@ -485,10 +583,8 @@ public class ManageOrderElementAdvancesModel implements BigDecimal maxValue; if (assignment instanceof IndirectAdvanceAssignment) { - maxValue = ((OrderLineGroup) this.orderElement) - .calculateFakeDirectAdvanceAssignment( - (IndirectAdvanceAssignment) assignment) - .getMaxValue(); + maxValue = orderElement.calculateFakeDirectAdvanceAssignment( + (IndirectAdvanceAssignment) assignment).getMaxValue(); } else { maxValue = ((DirectAdvanceAssignment) assignment) .getMaxValue(); @@ -503,42 +599,35 @@ public class ManageOrderElementAdvancesModel implements return BigDecimal.ZERO; } - return value.setScale(2).divide(maxValue, RoundingMode.DOWN).multiply( - new BigDecimal(100)); + BigDecimal division = value.divide(maxValue, 2, RoundingMode.DOWN); + return (division.multiply(new BigDecimal(100))).setScale(0, + RoundingMode.DOWN); } @Override @Transactional(readOnly = true) public DirectAdvanceAssignment calculateFakeDirectAdvanceAssignment( IndirectAdvanceAssignment indirectAdvanceAssignment) { - if (orderElement == null) { - return null; - } - - if (!(orderElement instanceof OrderLineGroup)) { + if ((orderElement == null) || (orderElement.isLeaf())) { return null; } reattachmentOrderElement(); - return ((OrderLineGroup) orderElement) + return orderElement .calculateFakeDirectAdvanceAssignment(indirectAdvanceAssignment); } @Override @Transactional(readOnly = true) public BigDecimal getAdvancePercentageChildren() { - if (orderElement == null) { - return null; - } - - if (!(orderElement instanceof OrderLineGroup)) { + if ((orderElement == null) || orderElement.isLeaf()) { return null; } reattachmentOrderElement(); - return ((OrderLineGroup) orderElement).getAdvancePercentageChildren(); + return orderElement.getAdvancePercentageChildren(); } @Override @@ -599,7 +688,7 @@ public class ManageOrderElementAdvancesModel implements Iterator iterator = advance .getAdvanceMeasurements().iterator(); while (iterator.hasNext()) { - if (hasConsolidatedAdvances(iterator.next())) { + if (hasConsolidatedAdvances(iterator.next(), advance.isFake())) { return true; } } @@ -607,16 +696,23 @@ public class ManageOrderElementAdvancesModel implements } @Transactional(readOnly = true) - public boolean hasConsolidatedAdvances( - AdvanceMeasurement advanceMeasurement) { + public boolean hasConsolidatedAdvances(AdvanceMeasurement advanceMeasurement) { + return hasConsolidatedAdvances(advanceMeasurement, + isIndirectAdvanceAssignment); + } - if (advanceMeasurement.isNewObject() || isIndirectAdvanceAssignment) { + private boolean hasConsolidatedAdvances( + AdvanceMeasurement advanceMeasurement, + boolean isIndirectAdvanceAssignment) { + + if (isIndirectAdvanceAssignment) { return false; } if (!advanceMeasurement.getNonCalculatedConsolidatedValues().isEmpty()) { return true; } + return findIndirectConsolidation(advanceMeasurement); } @@ -643,6 +739,7 @@ public class ManageOrderElementAdvancesModel implements types.add(PredefinedAdvancedTypes.CHILDREN.getTypeName()); } + orderElementDAO.reattach(orderElement); Set indirects = getSpreadIndirectAdvanceAssignmentWithSameType( orderElement, types); @@ -659,7 +756,6 @@ public class ManageOrderElementAdvancesModel implements private Set getSpreadIndirectAdvanceAssignmentWithSameType( OrderElement orderElement, List types) { - orderElementDAO.reattach(orderElement); Set result = new HashSet(); for (IndirectAdvanceAssignment indirect : orderElement diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java index 09592f704..aac0682d9 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderCRUDController.java @@ -33,6 +33,8 @@ import javax.annotation.Resource; import org.apache.commons.logging.LogFactory; import org.hibernate.validator.InvalidValue; +import org.navalplanner.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException; +import org.navalplanner.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException; import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.externalcompanies.entities.ExternalCompany; @@ -272,6 +274,10 @@ public class OrderCRUDController extends GenericForwardComposer { } public void setupOrderElementTreeController() throws Exception { + if (!confirmLastTab()) + return; + setCurrentTab(); + if (orderElementTreeController == null) { OrderElementController orderElementController = new OrderElementController(); orderElementController.doAfterCompose(self @@ -288,6 +294,23 @@ public class OrderCRUDController extends GenericForwardComposer { } } + /* + * Operations to do before to change the selected tab + */ + private boolean confirmLastTab() { + if (getCurrentTab() != null) { + // Confirm advances tab. + if (getCurrentTab().getId().equals("tabAdvances")) { + if (!manageOrderElementAdvancesController.save()) { + resetSelectedTab(); + selectTab("tabAdvances"); + return false; + } + } + } + return true; + } + private void redraw(Component comp) { Util.createBindingsFor(comp); Util.reloadBindings(comp); @@ -301,6 +324,10 @@ public class OrderCRUDController extends GenericForwardComposer { private AssignedHoursToOrderElementController assignedHoursController; public void setupAssignedHoursToOrderElementController() throws Exception { + if (!confirmLastTab()) + return; + setCurrentTab(); + if (assignedHoursController == null) { Component orderElementHours = editWindow .getFellowIfAny("orderElementHours"); @@ -315,6 +342,10 @@ public class OrderCRUDController extends GenericForwardComposer { private ManageOrderElementAdvancesController manageOrderElementAdvancesController; public void setupManageOrderElementAdvancesController() throws Exception { + if (!confirmLastTab()) + return; + setCurrentTab(); + Component orderElementAdvances = editWindow .getFellowIfAny("orderElementAdvances"); if (manageOrderElementAdvancesController == null) { @@ -325,6 +356,7 @@ public class OrderCRUDController extends GenericForwardComposer { } else { manageOrderElementAdvancesController.refreshChangesFromOrderElement(); manageOrderElementAdvancesController.createAndLoadBindings(); + manageOrderElementAdvancesController.refreshSelectedAdvance(); } } @@ -332,6 +364,10 @@ public class OrderCRUDController extends GenericForwardComposer { public void setupAssignedLabelsToOrderElementController() throws Exception { + if (!confirmLastTab()) + return; + setCurrentTab(); + if (assignedLabelsController == null) { LabelsAssignmentToOrderElementComponent labelsAssignment = (LabelsAssignmentToOrderElementComponent) editWindow .getFellow("orderElementLabels"); @@ -346,6 +382,10 @@ public class OrderCRUDController extends GenericForwardComposer { public void setupAssignedCriterionRequirementsToOrderElementController() throws Exception { + if (!confirmLastTab()) + return; + setCurrentTab(); + if (assignedCriterionRequirementController == null) { Component orderElementCriterionRequirements = editWindow .getFellowIfAny("orderElementCriterionRequirements"); @@ -364,6 +404,10 @@ public class OrderCRUDController extends GenericForwardComposer { public void setupAssignedMaterialsToOrderElementController() throws Exception { + if (!confirmLastTab()) + return; + setCurrentTab(); + if (assignedMaterialsController == null) { OrderElementMaterialAssignmentsComponent assignmentsComponent = (OrderElementMaterialAssignmentsComponent) editWindow .getFellowIfAny("orderElementMaterials"); @@ -378,6 +422,10 @@ public class OrderCRUDController extends GenericForwardComposer { private AssignedTaskQualityFormsToOrderElementController assignedTaskQualityFormController; public void setupAssignedTaskQualityFormsToOrderElementController() throws Exception { + if (!confirmLastTab()) + return; + setCurrentTab(); + Component orderElementTaskQualityForms = editWindow .getFellowIfAny("orderElementTaskQualityForms"); if (assignedTaskQualityFormController == null) { @@ -394,6 +442,10 @@ public class OrderCRUDController extends GenericForwardComposer { private OrderAuthorizationController orderAuthorizationController; public void setupOrderAuthorizationController() { + if (!confirmLastTab()) + return; + setCurrentTab(); + Component orderElementAuthorizations = editWindow .getFellowIfAny("orderElementAuthorizations"); if (orderAuthorizationController == null) { @@ -442,9 +494,10 @@ public class OrderCRUDController extends GenericForwardComposer { } public void saveAndContinue() { - setCurrentTab(); Order order = (Order) orderModel.getOrder(); final boolean isNewObject = order.isNewObject(); + setCurrentTab(); + Tab previousTab = getCurrentTab(); final boolean couldSave = save(); if (couldSave) { if(orderModel.userCanRead(order, SecurityUtils.getSessionUserLoginName())) { @@ -460,13 +513,15 @@ public class OrderCRUDController extends GenericForwardComposer { showWindow(editWindow); // come back to the current tab after initialize all tabs. - selectTab(getCurrentTab().getId()); - Events.sendEvent(new SelectEvent(Events.ON_SELECT, - getCurrentTab(), null)); + resetSelectedTab(); + selectTab(previousTab.getId()); + Events.sendEvent(new SelectEvent(Events.ON_SELECT, previousTab, + null)); if (isNewObject) { this.planningControllerEntryPoints.goToOrderDetails(order); } + } else { try { @@ -492,24 +547,31 @@ public class OrderCRUDController extends GenericForwardComposer { if (manageOrderElementAdvancesController != null) { selectTab("tabAdvances"); if (!manageOrderElementAdvancesController.save()) { + setCurrentTab(); return false; } } if (assignedCriterionRequirementController != null) { selectTab("tabRequirements"); if (!assignedCriterionRequirementController.close()) { + setCurrentTab(); return false; } } if (assignedTaskQualityFormController != null) { selectTab("tabTaskQualityForm"); if (!assignedTaskQualityFormController.confirm()) { - return false; + setCurrentTab(); + return false; } } - // come back to the current tab after validate other tabs. - selectTab(getCurrentTab().getId()); + createPercentageAdvances(); + + // come back to the default tab. + if (getCurrentTab() != null) { + selectTab(getCurrentTab().getId()); + } try { orderModel.save(); @@ -525,15 +587,50 @@ public class OrderCRUDController extends GenericForwardComposer { } catch (ValidationException e) { messagesForUser.showInvalidValues(e, new LabelCreatorForInvalidValues()); } + setCurrentTab(); return false; } Tab tabGeneralData; + private void createPercentageAdvances() { + + try { + if (manageOrderElementAdvancesController == null) { + Component orderElementAdvances = editWindow + .getFellowIfAny("orderElementAdvances"); + manageOrderElementAdvancesController = (ManageOrderElementAdvancesController) orderElementAdvances + .getVariable("manageOrderElementAdvancesController", + true); + } + manageOrderElementAdvancesController + .createPercentageAdvances(getOrderElementModel()); + } catch (DuplicateAdvanceAssignmentForOrderElementException e) { + messagesForUser + .showMessage( + Level.ERROR, + _("cannot include an Advance of the same Advance type twice")); + } catch (DuplicateValueTrueReportGlobalAdvanceException e) { + messagesForUser + .showMessage( + Level.ERROR, + _("spread values are not valid, at least one value should be true")); + } catch (Exception e) { + messagesForUser + .showMessage( + Level.ERROR, + _("incorrect initialization of the advance assignment controller.")); + } + } + private void selectDefaultTab() { selectTab("tabGeneralData"); } + private void resetSelectedTab() { + selectedTab = null; + } + private void setCurrentTab() { Tabbox tabboxOrder = (Tabbox) editWindow.getFellowIfAny("tabboxOrder"); if (tabboxOrder != null) { @@ -566,7 +663,9 @@ public class OrderCRUDController extends GenericForwardComposer { } public void reloadHoursGroupOrder() { - assignedCriterionRequirementController.reload(); + if (getCurrentTab().getId().equals("tabRequirements")) { + assignedCriterionRequirementController.reload(); + } } private void showWindow(Window window) { @@ -697,6 +796,12 @@ public class OrderCRUDController extends GenericForwardComposer { loadCustomerComponent(); } + public void setupOrderDetails() { + confirmLastTab(); + setCurrentTab(); + reloadDefaultTab(); + } + public void reloadDefaultTab() { Tabpanel tabPanel = (Tabpanel) editWindow .getFellow("tabPanelGeneralData"); @@ -1049,14 +1154,14 @@ public class OrderCRUDController extends GenericForwardComposer { orderElementFilter.setVisible(false); } - private void showOrderElementFilter() { + public void showOrderElementFilter() { if (orderFilter != null) orderFilter.setVisible(false); if (orderElementFilter != null) orderElementFilter.setVisible(true); } - private void showCreateButtons(boolean showCreate) { + public void showCreateButtons(boolean showCreate) { createOrderButton.setVisible(showCreate); createOrderFromTemplateButton.setVisible(showCreate); saveOrderAndContinueButton.setVisible(!showCreate); 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 263e2fc7a..13effee4e 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 @@ -131,6 +131,7 @@ public class OrderElementController extends GenericForwardComposer { manageOrderElementAdvancesController .refreshChangesFromOrderElement(); manageOrderElementAdvancesController.createAndLoadBindings(); + manageOrderElementAdvancesController.refreshSelectedAdvance(); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java index 19c43df96..b2e33b3e7 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderModel.java @@ -504,6 +504,20 @@ public class OrderModel implements IOrderModel { order.writeSchedulingDataChanges(); } saveDerivedScenarios(); + calculateAdvancePercentage(order); + for (OrderElement orderElement : order.getAllChildren()) { + calculateAdvancePercentage(orderElement); + } + } + + private void calculateAdvancePercentage(OrderElement orderElement) { + BigDecimal advancePercentage = orderElement.getAdvancePercentage(); + if (orderElement.getTaskSource() != null) { + if (orderElement.getTaskSource().getTask() != null) { + orderElement.getTaskSource().getTask().setAdvancePercentage( + advancePercentage); + } + } } private void createAndSaveNewOrderVersion(Scenario currentScenario, 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 0fc65f681..88eaee976 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 @@ -294,8 +294,7 @@ public class TaskElementAdapter implements ITaskElementAdapter { BigDecimal advancePercentage; Integer hours; if (orderElement != null) { - advancePercentage = orderElement - .getAdvancePercentage(); + advancePercentage = taskElement.getAdvancePercentage(); hours = taskElement.getTotalHoursAssigned(); } else { advancePercentage = new BigDecimal(0); @@ -322,8 +321,8 @@ public class TaskElementAdapter implements ITaskElementAdapter { @Override public BigDecimal getAdvancePercentage() { - if (taskElement.getOrderElement() != null) { - return taskElement.getOrderElement().getAdvancePercentage(); + if (taskElement != null) { + return taskElement.getAdvancePercentage(); } return new BigDecimal(0); } @@ -431,7 +430,6 @@ public class TaskElementAdapter implements ITaskElementAdapter { return new HashSet - LimitingResourcesLeftPane + limitingResourcesLeftPane org.navalplanner.web.limitingresources.LimitingResourcesLeftPane /limitingresources/leftPane.zul diff --git a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml index e346d8e1e..e4d1b15fb 100644 --- a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml +++ b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml @@ -38,6 +38,7 @@ + diff --git a/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependency.js b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependency.js index 0115d4aeb..54f32ef84 100644 --- a/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependency.js +++ b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependency.js @@ -25,112 +25,6 @@ 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 = {}; @@ -152,7 +46,10 @@ zkLimitingDependencies.newdraw = function(arrow, orig, dest, param) { var xend = dest[0]; var yend = dest[1] - zkLimitingDependencies.CORNER; - if (yend < yorig) { + if (yend == yorig) { + yend = yend + zkLimitingDependencies.HEIGHT; + yorig = yorig + zkLimitingDependencies.HEIGHT; + } else if (yend < yorig) { yend = yend + zkLimitingDependencies.HEIGHT; } else { yorig = yorig + zkLimitingDependencies.HEIGHT; diff --git a/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependencylist.js b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependencylist.js index 7bd025148..392fa5104 100644 --- a/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependencylist.js +++ b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingdependencylist.js @@ -22,3 +22,110 @@ zkLimitingDependencylist = {}; zkLimitingDependencylist.init = function (cmp) { } + + +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); + } +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingresourceslist.js b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingresourceslist.js index 60684ca4a..9c354976f 100644 --- a/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingresourceslist.js +++ b/navalplanner-webapp/src/main/resources/web/js/limitingresources/limitingresourceslist.js @@ -20,6 +20,26 @@ zkLimitingResourcesList = addLimitingResourcesListMethods( {}); +zkLimitingResourcesList.showRelatedElementsForQueueElement = function (task) { + zkLimitingDependencies.showDependenciesForQueueElement(task); + zkLimitingResourcesList.SetVisibleDeadlineForQueueElement(task, "inline"); +} + +zkLimitingResourcesList.hideRelatedElementsForQueueElement = function (task) { + zkLimitingDependencies.hideDependenciesForQueueElement(task); + zkLimitingResourcesList.SetVisibleDeadlineForQueueElement(task, "none"); +} + +zkLimitingResourcesList.SetVisibleDeadlineForQueueElement = function(task, visible) { + var deadlines = YAHOO.util.Selector.query('.deadline'); + for ( var i = 0; i < deadlines.length; i++) { + if ((deadlines[i].parentNode.id == task)) { + deadlines[i].style.display = visible; + } + } +} + + function addLimitingResourcesListMethods(object) { var scrollSync; @@ -86,21 +106,29 @@ function addLimitingResourcesListMethods(object) { } object.adjustTimeTrackerSize = function(cmp) { - watermark().style["height"] = cmp.clientHeight + "px"; - timetracker().style["width"] = cmp.clientWidth + "px"; - /* Set watermark width */ - YAHOO.util.Selector.query('.limitingresourceslist')[0].style["width"] = YAHOO.util.Selector - .query('.second_level_')[0].clientWidth - + "px"; - YAHOO.util.Selector.query('.rightpanellayout tr#watermark td')[0].style["height"] = - /* Calculate min : taskspanelgap().clientHeight + 120 + 'px'; ) */ - YAHOO.util.Selector.query('.limitingresourceslist')[0].clientHeight + 120 - + "px"; + var _firstWatarmark = YAHOO.util.Selector.query('.rightpanellayout tr#watermark td')[0]; + + if (watermark() != null) { + watermark().style["height"] = cmp.clientHeight + "px"; + } + + if (timetracker() != null) { + timetracker().style["width"] = cmp.clientWidth + "px"; + + YAHOO.util.Selector.query('.limitingresourceslist')[0].style["width"] = YAHOO.util.Selector + .query('.second_level_')[0].clientWidth + "px"; + + if (_firstWatarmark != undefined ) { + _firstWatarmark.style["height"] =YAHOO.util.Selector.query('.limitingresourceslist')[0].clientHeight + 120 + "px"; + } + + } }; object.adjustResourceLoadRows = function(cmp) { + var width = YAHOO.util.Selector.query('.rightpanellayout #timetracker .z-grid-header')[0].clientWidth + "px"; YAHOO.util.Selector.query('.row_resourceload').each(function(node) { - node.style["width"] = cmp.clientWidth + "px"; + node.style["width"] = width; }); }; diff --git a/navalplanner-webapp/src/main/webapp/calendars/calendars.zul b/navalplanner-webapp/src/main/webapp/calendars/calendars.zul index dc54dc9c7..3ed1c154d 100644 --- a/navalplanner-webapp/src/main/webapp/calendars/calendars.zul +++ b/navalplanner-webapp/src/main/webapp/calendars/calendars.zul @@ -49,7 +49,7 @@ position="center"> - diff --git a/navalplanner-webapp/src/main/webapp/common/concurrent_modification.zul b/navalplanner-webapp/src/main/webapp/common/concurrent_modification.zul index 7613d4752..af4172735 100644 --- a/navalplanner-webapp/src/main/webapp/common/concurrent_modification.zul +++ b/navalplanner-webapp/src/main/webapp/common/concurrent_modification.zul @@ -29,7 +29,7 @@ -
+ + + diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/manualAllocation.zul b/navalplanner-webapp/src/main/webapp/limitingresources/manualAllocation.zul new file mode 100644 index 000000000..21b5f9681 --- /dev/null +++ b/navalplanner-webapp/src/main/webapp/limitingresources/manualAllocation.zul @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + diff --git a/navalplanner-webapp/src/main/webapp/planner/_tabPanelAdvanceConsolidations.zul b/navalplanner-webapp/src/main/webapp/planner/_tabPanelAdvanceConsolidations.zul deleted file mode 100644 index 1f8428717..000000000 --- a/navalplanner-webapp/src/main/webapp/planner/_tabPanelAdvanceConsolidations.zul +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul b/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul index 36cf489f1..3a246da52 100644 --- a/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul +++ b/navalplanner-webapp/src/main/webapp/planner/_tabPanelLimitingResourceAllocation.zul @@ -58,7 +58,7 @@ diff --git a/navalplanner-webapp/src/main/webapp/planner/advance_allocation.zul b/navalplanner-webapp/src/main/webapp/planner/advance_allocation.zul index 3f8f90b77..36492e377 100644 --- a/navalplanner-webapp/src/main/webapp/planner/advance_allocation.zul +++ b/navalplanner-webapp/src/main/webapp/planner/advance_allocation.zul @@ -61,9 +61,8 @@ + + +