ItEr58S10CUAsignacionRecursosLimitantesItEr57S11: Select day in calendar as the starting day where to allocate a queue element into a queue
* Highlight days in datebox calendar * Fix some other bugs (cancel button, calculating gaps)
This commit is contained in:
parent
80d898c879
commit
f9486de643
5 changed files with 379 additions and 13 deletions
|
|
@ -83,4 +83,11 @@ public class DateAndHour implements Comparable<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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,10 +157,19 @@ public class LimitingResourceAllocator {
|
|||
}
|
||||
|
||||
private static Integer moveUntil(List<LimitingResourceQueueElement> elements, DateAndHour until) {
|
||||
for (int pos = 0; pos < elements.size(); pos++) {
|
||||
final LimitingResourceQueueElement each = elements.get(pos);
|
||||
if (!until.isBefore(each.getStartTime())) {
|
||||
return pos;
|
||||
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 (int 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 null;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ import java.text.SimpleDateFormat;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
|
@ -34,7 +36,6 @@ import org.apache.commons.lang.Validate;
|
|||
import org.joda.time.LocalDate;
|
||||
import org.navalplanner.business.orders.entities.Order;
|
||||
import org.navalplanner.business.planner.entities.DateAndHour;
|
||||
import org.navalplanner.business.planner.entities.DayAssignment;
|
||||
import org.navalplanner.business.planner.entities.GenericResourceAllocation;
|
||||
import org.navalplanner.business.planner.entities.LimitingResourceAllocator;
|
||||
import org.navalplanner.business.planner.entities.LimitingResourceQueueElement;
|
||||
|
|
@ -57,13 +58,16 @@ 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.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.Composer;
|
||||
import org.zkoss.zul.Button;
|
||||
import org.zkoss.zul.Checkbox;
|
||||
import org.zkoss.zul.Datebox;
|
||||
import org.zkoss.zul.Grid;
|
||||
import org.zkoss.zul.Hbox;
|
||||
import org.zkoss.zul.Label;
|
||||
|
|
@ -106,6 +110,10 @@ public class LimitingResourcesController implements Composer {
|
|||
|
||||
private Radiogroup radioAllocationDate;
|
||||
|
||||
private Datebox startAllocationDate;
|
||||
|
||||
private Map<LimitingResourceQueueElementGap, DateAndHour> endAllocationDates = new HashMap<LimitingResourceQueueElementGap, DateAndHour>();
|
||||
|
||||
private final LimitingResourceQueueElementsRenderer limitingResourceQueueElementsRenderer =
|
||||
new LimitingResourceQueueElementsRenderer();
|
||||
|
||||
|
|
@ -160,12 +168,15 @@ public class LimitingResourcesController implements Composer {
|
|||
this.parent.getChildren().clear();
|
||||
this.parent.appendChild(limitingResourcesPanel);
|
||||
limitingResourcesPanel.afterCompose();
|
||||
|
||||
gridUnassignedLimitingResourceQueueElements = (Grid) limitingResourcesPanel
|
||||
.getFellowIfAny("gridUnassignedLimitingResourceQueueElements");
|
||||
manualAllocationWindow = (Window) limitingResourcesPanel.getFellowIfAny("manualAllocationWindow");
|
||||
listAssignableQueues = (Listbox) manualAllocationWindow.getFellowIfAny("listAssignableQueues");
|
||||
listCandidateGaps = (Listbox) manualAllocationWindow.getFellowIfAny("listCandidateGaps");
|
||||
radioAllocationDate = (Radiogroup) manualAllocationWindow.getFellowIfAny("radioAllocationDate");
|
||||
startAllocationDate = (Datebox) manualAllocationWindow.getFellowIfAny("startAllocationDate");
|
||||
|
||||
addCommands(limitingResourcesPanel);
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
|
|
@ -474,13 +485,48 @@ public class LimitingResourcesController implements Composer {
|
|||
|
||||
private void feedValidGapsSince(LimitingResourceQueueElement element, LimitingResourceQueue queue, DateAndHour since) {
|
||||
List<LimitingResourceQueueElementGap> gaps = LimitingResourceAllocator.getValidGapsForElementSince(element, queue, since);
|
||||
endAllocationDates = calculateEndAllocationDates(element.getResourceAllocation(), queue.getResource(), gaps);
|
||||
listCandidateGaps.setModel(new SimpleListModel(gaps));
|
||||
if (!gaps.isEmpty()) {
|
||||
listCandidateGaps.setSelectedIndex(0);
|
||||
setStartAllocationDate(gaps.get(0).getStartTime());
|
||||
startAllocationDate.setDisabled(true);
|
||||
disable(radioAllocationDate, false);
|
||||
} else {
|
||||
disable(radioAllocationDate, true);
|
||||
}
|
||||
radioAllocationDate.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private void setStartAllocationDate(DateAndHour time) {
|
||||
final Date date = (time != null) ? toDate(time.getDate()) : null;
|
||||
startAllocationDate.setValue(date);
|
||||
}
|
||||
|
||||
private Map<LimitingResourceQueueElementGap, DateAndHour> calculateEndAllocationDates(
|
||||
ResourceAllocation<?> resourceAllocation, Resource resource,
|
||||
List<LimitingResourceQueueElementGap> gaps) {
|
||||
|
||||
Map<LimitingResourceQueueElementGap, DateAndHour> result = new HashMap<LimitingResourceQueueElementGap, DateAndHour>();
|
||||
for (LimitingResourceQueueElementGap each: gaps) {
|
||||
result.put(each, calculateEndAllocationDate(resourceAllocation, resource, each));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private DateAndHour calculateEndAllocationDate(
|
||||
ResourceAllocation<?> resourceAllocation, Resource resource,
|
||||
LimitingResourceQueueElementGap 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) {
|
||||
|
|
@ -572,13 +618,15 @@ public class LimitingResourcesController implements Composer {
|
|||
}
|
||||
|
||||
public void accept(Event e) {
|
||||
LimitingResourceQueueElement element = limitingResourceQueueModel
|
||||
.getLimitingResourceQueueElement();
|
||||
LimitingResourceQueue queue = getSelectedQueue();
|
||||
DateAndHour time = getSelectedAllocationTime();
|
||||
final LimitingResourceQueue queue = getSelectedQueue();
|
||||
final DateAndHour time = getSelectedAllocationTime();
|
||||
Validate.notNull(time);
|
||||
limitingResourceQueueModel
|
||||
.assignEditingLimitingResourceQueueElementToQueueAt(queue, time);
|
||||
Util.reloadBindings(gridUnassignedLimitingResourceQueueElements);
|
||||
|
||||
LimitingResourceQueueElement element = limitingResourceQueueModel
|
||||
.getLimitingResourceQueueElement();
|
||||
limitingResourcesPanel.appendQueueElementToQueue(element);
|
||||
closeManualAllocationWindow(e);
|
||||
}
|
||||
|
|
@ -586,12 +634,21 @@ public class LimitingResourcesController implements Composer {
|
|||
private DateAndHour getSelectedAllocationTime() {
|
||||
final LimitingResourceQueueElementGap 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) {
|
||||
|
||||
LocalDate selectedDay = new LocalDate(startAllocationDate.getValue());
|
||||
DateAndHour allocationTime = getValidDayInGap(selectedDay, getSelectedGap());
|
||||
if (allocationTime == null) {
|
||||
throw new WrongValueException(startAllocationDate, _("Day is not valid within selected gap"));
|
||||
}
|
||||
return allocationTime;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -617,8 +674,38 @@ public class LimitingResourcesController implements Composer {
|
|||
return null;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
/**
|
||||
* 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, LimitingResourceQueueElementGap 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() {
|
||||
manualAllocationWindow.setVisible(false);
|
||||
}
|
||||
|
||||
public void closeManualAllocationWindow(Event e) {
|
||||
|
|
@ -626,4 +713,57 @@ public class LimitingResourcesController implements Composer {
|
|||
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));
|
||||
}
|
||||
highlightDaysInGap(datebox.getUuid(), getSelectedGap());
|
||||
}
|
||||
|
||||
private Date toDate(LocalDate date) {
|
||||
return date.toDateTimeAtStartOfDay().toDate();
|
||||
}
|
||||
|
||||
public void highlightDaysInGap(String uuid, LimitingResourceQueueElementGap 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);
|
||||
}
|
||||
|
||||
private LocalDate getEndAllocationDate(LimitingResourceQueueElementGap 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\"}";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var months = {"Jan": 0, "Feb": 1, "Mar": 2, "Apr": 3, "May": 4, "Jun": 5, "Jul": 6, "Aug": 7, "Sep": 8, "Oct": 9, "Nov": 10, "Dec": 11};
|
||||
|
||||
var DEFAULT_COLOR_STYLE = {"color": "blue", "bgcolor": "white"};
|
||||
|
||||
Array.prototype.in_array = function(p_val) {
|
||||
for(var i = 0, l = this.length; i < l; i++) {
|
||||
if(this[i] == p_val) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Date.prototype.compareTo = function(other) {
|
||||
var this_milli = this.getTime();
|
||||
var other_milli = other.getTime();
|
||||
return this_milli - other_milli;
|
||||
}
|
||||
|
||||
Date.prototype.lesserThan = function(other) {
|
||||
return this.compareTo(other) < 0;
|
||||
}
|
||||
|
||||
Date.prototype.greaterThan = function(other) {
|
||||
return this.compareTo(other) > 0;
|
||||
}
|
||||
|
||||
Date.prototype.equals = function(other) {
|
||||
return this.compareTo(other) == 0;
|
||||
}
|
||||
|
||||
Date.prototype.getDaysInMonth = function() {
|
||||
return 32 - new Date(this.getFullYear(), this.getMonth(), 32).getDate();
|
||||
}
|
||||
|
||||
String.prototype.trim = function(string) {
|
||||
return this.replace("^\s+", "").replace("\s+$", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of month: 'Jan' => 0, 'Feb' => 1, etc
|
||||
*/
|
||||
function numberOfMonth(month) {
|
||||
return months[month];
|
||||
}
|
||||
|
||||
function dateAtBeginningOfMonth(monthAndYear) {
|
||||
var arr = monthAndYear.split(",");
|
||||
return new Date(arr[1].trim(), numberOfMonth(arr[0].trim()), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses date to Date(). Expects date in format ISO8601 (yyyy-MM-day)
|
||||
*/
|
||||
function toDate(date) {
|
||||
if (date != undefined) {
|
||||
var arr = date.split("-");
|
||||
|
||||
var year = arr[0];
|
||||
var month = arr[1] - 1;
|
||||
var day = arr[2];
|
||||
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns which days in date.month should be highlighted according to interval
|
||||
*
|
||||
* If interval is open, all days greater than interval.start should highlighted
|
||||
*
|
||||
*/
|
||||
function daysToHighlightInInterval(interval, date) {
|
||||
var start = toDate(interval.start);
|
||||
var end = toDate(interval.end);
|
||||
|
||||
if (sameMonthAndYear(start, date)
|
||||
&& sameMonthAndYear(start, end)) {
|
||||
return daysDelta(start.getDate(), end.getDate());
|
||||
}
|
||||
|
||||
if (sameMonthAndYear(start, date)) {
|
||||
return daysDelta(start.getDate(), date.getDaysInMonth());
|
||||
}
|
||||
|
||||
if (sameMonthAndYear(end, date)) {
|
||||
return daysDelta(1, end.getDate());
|
||||
}
|
||||
|
||||
if (start.lesserThan(date) && (end == null || end.greaterThan(date)) ) {
|
||||
return daysDelta(1, date.getDaysInMonth());
|
||||
}
|
||||
|
||||
return new Array();
|
||||
}
|
||||
|
||||
function sameMonthAndYear(d1, d2) {
|
||||
return (d1 != null && d2 != null
|
||||
&& d1.getFullYear() == d2.getFullYear()
|
||||
&& d1.getMonth() == d2.getMonth());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of days from start to end (both included)
|
||||
*
|
||||
**/
|
||||
function daysDelta(start, end) {
|
||||
var result = new Array();
|
||||
for (var i = start; i <= end; i++) {
|
||||
result.push(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights elements in days array, turns off those days that are not in days
|
||||
*
|
||||
**/
|
||||
function setStyleForDays(nodes, days, colors) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var month = nodes[i].getAttribute("zk_monofs");
|
||||
if (month == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (month == 0) {
|
||||
var day = nodes[i].getAttribute("zk_day");
|
||||
|
||||
if (days.in_array(day)) {
|
||||
nodes[i].setAttribute("style", colorStyleObj(colors));
|
||||
} else {
|
||||
nodes[i].removeAttribute("style");
|
||||
}
|
||||
} else {
|
||||
nodes[i].setAttribute("style", colorStyle('lightgrey', 'white'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function colorStyleObj(obj) {
|
||||
return colorStyle(obj.color, obj.bgcolor, obj.bold);
|
||||
}
|
||||
|
||||
function colorStyle(color, bgcolor, bold) {
|
||||
var cssStyle = "color: " + color + "; background-color: " + bgcolor;
|
||||
if (bold != undefined && bold) {
|
||||
cssStyle += "; font-weight: bold";
|
||||
}
|
||||
return cssStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights those days in a calendar ZUL object that are within interval
|
||||
*
|
||||
* An interval is an object with two attributes:
|
||||
* interval.start: date (year/month/day)
|
||||
* interval.end: date (year/month/day)
|
||||
*
|
||||
* colorStyle is an object with two attributes:
|
||||
* color.color: foreground color
|
||||
* color.bgcolor: background color
|
||||
*
|
||||
* colorStyle is the color used to highlight a day (by default blue over white background)
|
||||
*
|
||||
*/
|
||||
function highlightDaysInInterval(calendarUuid, intervalJSON, colorStyleJSON) {
|
||||
|
||||
var calendar = document.getElementById(calendarUuid + "!pp");
|
||||
if (calendar == null) {
|
||||
return;
|
||||
}
|
||||
var nodes = calendar.getElementsByTagName("td");
|
||||
var title = document.getElementById(calendarUuid + "!title"); // month and year: Jan, 1
|
||||
|
||||
var interval = eval("(" + intervalJSON + ")");
|
||||
var colorStyle = (colorStyleJSON != undefined) ? eval("(" + colorStyleJSON + ")") : DEFAULT_COLOR_STYLE;
|
||||
|
||||
var currentDate = dateAtBeginningOfMonth(title.innerHTML);
|
||||
var days = daysToHighlightInInterval(interval, currentDate);
|
||||
|
||||
setStyleForDays(nodes, days, colorStyle);
|
||||
}
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
<zk xmlns:n="http://www.zkoss.org/2005/zk/native">
|
||||
|
||||
<n:script type="text/javascript" src="/navalplanner-webapp/js/highlightDaysInInterval.js" />
|
||||
|
||||
<zscript><![CDATA[
|
||||
limitingResourcesPanel = self;
|
||||
]]>
|
||||
|
|
@ -132,7 +134,8 @@
|
|||
<panelchildren>
|
||||
<listbox id="listCandidateGaps"
|
||||
itemRenderer="@{limitingResourcesController.candidateGapRenderer}"
|
||||
multiple="false">
|
||||
multiple="false"
|
||||
onSelect="limitingResourcesController.setStartAllocationDate(event)">
|
||||
<listhead>
|
||||
<listheader label="${i18n:_('Start')}" />
|
||||
<listheader label="${i18n:_('End')}" />
|
||||
|
|
@ -146,10 +149,14 @@
|
|||
<vbox>
|
||||
<panel title="${i18n:_('Select date')}" border="normal">
|
||||
<panelchildren>
|
||||
<radiogroup id="radioAllocationDate" orient="vertical">
|
||||
<radiogroup id="radioAllocationDate" orient="vertical"
|
||||
onCheck="limitingResourcesController.selectRadioAllocationDate(event)" >
|
||||
<radio label="${i18n:_('Earliest date')}" selected="true" />
|
||||
<radio label="${i18n:_('Latest date')}" />
|
||||
<radio label="${i18n:_('Select start date')}" />
|
||||
</radiogroup>
|
||||
<datebox id="startAllocationDate"
|
||||
onFocus="limitingResourcesController.highlightCalendar(event)" />
|
||||
</panelchildren>
|
||||
</panel>
|
||||
</vbox>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue