ItEr57S11CUAsignacionRecursosLimitantesItEr56S12: Assign unassigned limiting resource queue elements to queues
* Find most suitable gap in queue * Generate day assignments * Modify ganttzk for showing generated elements * Only works for specific resource allocations
This commit is contained in:
parent
ac26b277a2
commit
cdc80f8981
16 changed files with 842 additions and 257 deletions
|
|
@ -64,6 +64,15 @@ public class ResourceCalendar extends BaseCalendar {
|
|||
addNewCalendarAvailability(calendarAvailability);
|
||||
}
|
||||
|
||||
public Integer getCapacity(LocalDate from, LocalDate to) {
|
||||
Integer result = getCapacityAt(to);
|
||||
for (LocalDate date = from; date.isBefore(to);) {
|
||||
result += getCapacityAt(date);
|
||||
date = date.plusDays(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCapacityAt(LocalDate date) {
|
||||
if (!isActive(date)) {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@
|
|||
|
||||
package org.navalplanner.business.planner.daos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.navalplanner.business.common.daos.IGenericDAO;
|
||||
import org.navalplanner.business.resources.entities.LimitingResourceQueue;
|
||||
import org.navalplanner.business.resources.entities.Resource;
|
||||
|
||||
/**
|
||||
* DAO interface for {@link ILimitingResourceQueueDAO}
|
||||
|
|
@ -31,4 +34,8 @@ import org.navalplanner.business.resources.entities.LimitingResourceQueue;
|
|||
public interface ILimitingResourceQueueDAO extends
|
||||
IGenericDAO<LimitingResourceQueue, Long> {
|
||||
|
||||
LimitingResourceQueue findQueueByResource(Resource resource);
|
||||
|
||||
List<LimitingResourceQueue> getAll();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,12 @@
|
|||
|
||||
package org.navalplanner.business.planner.daos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.navalplanner.business.common.daos.GenericDAOHibernate;
|
||||
import org.navalplanner.business.resources.entities.LimitingResourceQueue;
|
||||
import org.navalplanner.business.resources.entities.Resource;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
|
@ -37,4 +41,15 @@ public class LimitingResourceQueueDAO extends
|
|||
GenericDAOHibernate<LimitingResourceQueue, Long> implements
|
||||
ILimitingResourceQueueDAO {
|
||||
|
||||
public LimitingResourceQueue findQueueByResource(Resource resource) {
|
||||
return (LimitingResourceQueue) getSession().createCriteria(
|
||||
LimitingResourceQueue.class).add(
|
||||
Restrictions.eq("resource", resource)).uniqueResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LimitingResourceQueue> getAll() {
|
||||
return list(LimitingResourceQueue.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package org.navalplanner.business.planner.entities;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Diego Pino Garcia <dpino@igalia.com>
|
||||
*
|
||||
*/
|
||||
public class DateAndHour implements Comparable<DateAndHour> {
|
||||
|
||||
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) ? Math.abs(deltaHour) : 0;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return date + "; " + hour;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import java.util.Date;
|
|||
import org.joda.time.LocalDate;
|
||||
import org.navalplanner.business.common.BaseEntity;
|
||||
import org.navalplanner.business.resources.entities.LimitingResourceQueue;
|
||||
import org.navalplanner.business.resources.entities.Resource;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -51,6 +52,10 @@ public class LimitingResourceQueueElement extends BaseEntity {
|
|||
|
||||
protected LimitingResourceQueueElement() {
|
||||
creationTimestamp = (new Date()).getTime();
|
||||
startQueuePosition = new QueuePosition();
|
||||
startQueuePosition.setHour(0);
|
||||
endQueuePosition = new QueuePosition();
|
||||
endQueuePosition.setHour(0);
|
||||
}
|
||||
|
||||
public ResourceAllocation<?> getResourceAllocation() {
|
||||
|
|
@ -118,4 +123,25 @@ public class LimitingResourceQueueElement extends BaseEntity {
|
|||
this.creationTimestamp = creationTimestamp;
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
if (resourceAllocation instanceof SpecificResourceAllocation) {
|
||||
final SpecificResourceAllocation specific = (SpecificResourceAllocation) resourceAllocation;
|
||||
return specific.getResource();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getIntentedTotalHours() {
|
||||
return (getResourceAllocation() != null) ? getResourceAllocation()
|
||||
.getIntendedTotalHours() : null;
|
||||
}
|
||||
|
||||
public DateAndHour getStartTime() {
|
||||
return new DateAndHour(getStartDate(), getStartHour());
|
||||
}
|
||||
|
||||
public DateAndHour getEndTime() {
|
||||
return new DateAndHour(getEndDate(), getEndHour());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
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 <dpino@igalia.com>
|
||||
*
|
||||
*/
|
||||
public class LimitingResourceQueueElementGap {
|
||||
|
||||
private DateAndHour startTime;
|
||||
|
||||
private DateAndHour endTime;
|
||||
|
||||
private Integer hoursInGap;
|
||||
|
||||
public LimitingResourceQueueElementGap(Resource resource, DateAndHour startTime,
|
||||
DateAndHour endTime) {
|
||||
this(resource, startTime.getDate(), startTime.getHour(), endTime.getDate(), endTime.getHour());
|
||||
}
|
||||
|
||||
public LimitingResourceQueueElementGap(Resource resource, LocalDate startDate,
|
||||
int startHour, LocalDate endDate, int endHour) {
|
||||
|
||||
final ResourceCalendar calendar = resource.getCalendar();
|
||||
|
||||
// Calculate hours in range of dates
|
||||
if (startDate.equals(endDate)) {
|
||||
hoursInGap = endHour - startHour;
|
||||
} else {
|
||||
int hoursAtStart = calendar.getCapacityAt(startDate)
|
||||
- startHour;
|
||||
int hoursInBetween = calendar.getWorkableHours(startDate
|
||||
.plusDays(1), endDate.minusDays(1));
|
||||
if (hoursAtStart <= 0) {
|
||||
startDate = startDate.plusDays(1);
|
||||
startHour = 0;
|
||||
}
|
||||
hoursInGap = hoursAtStart + hoursInBetween + endHour;
|
||||
}
|
||||
|
||||
// Set start and end time for gap
|
||||
startTime = new DateAndHour(startDate, startHour);
|
||||
endTime = new DateAndHour(endDate, 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 hours for this gap is big enough for fitting hours
|
||||
*
|
||||
* @param hours
|
||||
* @return
|
||||
*/
|
||||
public boolean canFit(Integer hours) {
|
||||
return hoursInGap.compareTo(hours) >= 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
|
|||
@NotNull
|
||||
private ResourcesPerDay resourcesPerDay;
|
||||
|
||||
private Integer intendedTotalHours;
|
||||
private Integer intendedTotalHours = 0;
|
||||
|
||||
private Set<DerivedAllocation> derivedAllocations = new HashSet<DerivedAllocation>();
|
||||
|
||||
|
|
@ -566,6 +566,11 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
|
|||
setOriginalTotalAssigment(getAssignedHours());
|
||||
}
|
||||
|
||||
public void allocateLimitingDayAssignments(List<T> assignments) {
|
||||
assert isLimiting();
|
||||
resetAssignmentsTo(assignments);
|
||||
}
|
||||
|
||||
protected abstract void addingAssignments(
|
||||
Collection<? extends T> assignments);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ package org.navalplanner.business.resources.entities;
|
|||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.navalplanner.business.planner.entities.LimitingResourceQueueElement;
|
||||
|
||||
/**
|
||||
|
|
@ -34,17 +35,24 @@ public class LimitingResourceQueueElementComparator implements
|
|||
@Override
|
||||
public int compare(LimitingResourceQueueElement arg0,
|
||||
LimitingResourceQueueElement arg1) {
|
||||
final int deltaHour = arg0.getStartHour() - arg1.getStartHour();
|
||||
if (deltaHour != 0) {
|
||||
return deltaHour / Math.abs(deltaHour);
|
||||
}
|
||||
if (arg0.getStartDate() == null) {
|
||||
int compareDates = compare(arg0.getStartDate(), arg1.getStartDate());
|
||||
return (compareDates != 0) ? compareDates : compare(
|
||||
arg0.getStartHour(), arg1.getStartHour());
|
||||
}
|
||||
|
||||
private int compare(LocalDate arg0, LocalDate arg1) {
|
||||
if (arg0 == null) {
|
||||
return -1;
|
||||
}
|
||||
if (arg1.getStartDate() == null) {
|
||||
if (arg1 == null) {
|
||||
return 1;
|
||||
}
|
||||
return arg0.getStartDate().compareTo(arg1.getStartDate());
|
||||
return arg0.compareTo(arg1);
|
||||
}
|
||||
|
||||
private int compare(int arg0, int arg1) {
|
||||
final int deltaHour = arg0 - arg1;
|
||||
return (deltaHour != 0) ? deltaHour / Math.abs(deltaHour) : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
</id>
|
||||
<version name="version" access="property" type="long" />
|
||||
|
||||
<many-to-one name="resourceAllocation" column="RESOURCE_ALLOCATION_ID" not-null="false" unique="true" />
|
||||
<many-to-one name="resourceAllocation" column="RESOURCE_ALLOCATION_ID" cascade="all" not-null="false" unique="true" />
|
||||
|
||||
<many-to-one name="limitingResourceQueue" column="LIMITING_RESOURCE_QUEUE_ID" />
|
||||
|
||||
|
|
|
|||
|
|
@ -23,28 +23,90 @@ package org.navalplanner.web.limitingresources;
|
|||
import java.util.List;
|
||||
|
||||
import org.navalplanner.business.orders.entities.Order;
|
||||
import org.navalplanner.business.planner.entities.DayAssignment;
|
||||
import org.navalplanner.business.planner.entities.LimitingResourceQueueElement;
|
||||
import org.navalplanner.business.planner.entities.TaskElement;
|
||||
import org.navalplanner.business.resources.entities.LimitingResourceQueue;
|
||||
import org.zkoss.ganttz.timetracker.zoom.ZoomLevel;
|
||||
import org.zkoss.ganttz.util.Interval;
|
||||
|
||||
/**
|
||||
* Contains operations for showing {@link LimitingResourceQueue} and its
|
||||
* elements ({@link LimitingResourceQueueElement}), plus showing all
|
||||
* {@link LimitingResourceQueueElement} which are not assigned to any
|
||||
* {@link LimitingResourceQueue}
|
||||
*
|
||||
* <strong>Conversational protocol:</strong>
|
||||
* <ul>
|
||||
* <li>
|
||||
* Initial conversation step: <code>initGlobalView</code></li>
|
||||
* <li>
|
||||
* Intermediate conversation steps:
|
||||
* <code>assignLimitingResourceQueueElement</code>,
|
||||
* <code>getLimitingResourceQueues</code>,
|
||||
* <code>getUnassignedLimitingResourceQueueElements</code></li>
|
||||
* <li>
|
||||
* Final conversation step: <code>confirm</code></li>
|
||||
*
|
||||
*
|
||||
* @author Diego Pino Garcia <dpino@igalia.com>
|
||||
*
|
||||
*/
|
||||
public interface ILimitingResourceQueueModel {
|
||||
|
||||
/**
|
||||
* Assigns a {@link LimitingResourceQueueElement} to its corresponding
|
||||
* {@link LimitingResourceQueue}
|
||||
*
|
||||
* There is one and only one queue for every limiting resource. An element
|
||||
* is assigned to its queue searching by element.resource.
|
||||
*
|
||||
* Allocation within the queue is done by finding the first gap in the queue
|
||||
* that fits the initial intented hours assigned to
|
||||
* element.resourceallocation.
|
||||
*
|
||||
* The method also generates {@link DayAssignment} once ne the allocation is
|
||||
* done
|
||||
*
|
||||
* @param element
|
||||
*/
|
||||
void assignLimitingResourceQueueElement(LimitingResourceQueueElement element);
|
||||
|
||||
ZoomLevel calculateInitialZoomLevel();
|
||||
|
||||
/**
|
||||
* Saves all {@link LimitingResourceQueue}
|
||||
*/
|
||||
void confirm();
|
||||
|
||||
/**
|
||||
* Return all {@link LimitingResourceQueue}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<LimitingResourceQueue> getLimitingResourceQueues();
|
||||
|
||||
Order getOrderByTask(TaskElement task);
|
||||
|
||||
/**
|
||||
* Returns all existing {@link LimitingResourceQueueElement} which are not
|
||||
* assigned to any {@link LimitingResourceQueue}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<LimitingResourceQueueElement> getUnassignedLimitingResourceQueueElements();
|
||||
|
||||
Interval getViewInterval();
|
||||
|
||||
/**
|
||||
* Loads {@link LimitingResourceQueue} and unassigned {@link LimitingResourceQueueElement} from DB
|
||||
*
|
||||
* @param filterByResources
|
||||
*/
|
||||
void initGlobalView(boolean filterByResources);
|
||||
|
||||
void initGlobalView(Order filterBy, boolean filterByResources);
|
||||
|
||||
List<LimitingResourceQueue> getLimitingResourceQueues();
|
||||
|
||||
Interval getViewInterval();
|
||||
|
||||
ZoomLevel calculateInitialZoomLevel();
|
||||
|
||||
Order getOrderByTask(TaskElement task);
|
||||
|
||||
boolean userCanRead(Order order, String loginName);
|
||||
|
||||
List<LimitingResourceQueueElement> getUnassignedLimitingResourceQueueElements();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,33 +20,40 @@
|
|||
|
||||
package org.navalplanner.web.limitingresources;
|
||||
|
||||
import static org.navalplanner.web.I18nHelper._;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.navalplanner.business.common.BaseEntity;
|
||||
import org.navalplanner.business.calendars.entities.BaseCalendar;
|
||||
import org.navalplanner.business.calendars.entities.CalendarAvailability;
|
||||
import org.navalplanner.business.calendars.entities.CalendarData;
|
||||
import org.navalplanner.business.calendars.entities.CalendarException;
|
||||
import org.navalplanner.business.calendars.entities.ResourceCalendar;
|
||||
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
|
||||
import org.navalplanner.business.orders.daos.IOrderDAO;
|
||||
import org.navalplanner.business.orders.daos.IOrderElementDAO;
|
||||
import org.navalplanner.business.orders.entities.Order;
|
||||
import org.navalplanner.business.orders.entities.OrderElement;
|
||||
import org.navalplanner.business.planner.daos.ILimitingResourceQueueDAO;
|
||||
import org.navalplanner.business.planner.daos.ILimitingResourceQueueElementDAO;
|
||||
import org.navalplanner.business.planner.daos.IResourceAllocationDAO;
|
||||
import org.navalplanner.business.planner.daos.ITaskElementDAO;
|
||||
import org.navalplanner.business.planner.entities.DateAndHour;
|
||||
import org.navalplanner.business.planner.entities.DayAssignment;
|
||||
import org.navalplanner.business.planner.entities.GenericResourceAllocation;
|
||||
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.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.TaskElement;
|
||||
import org.navalplanner.business.resources.daos.IResourceDAO;
|
||||
import org.navalplanner.business.resources.entities.Criterion;
|
||||
import org.navalplanner.business.resources.entities.LimitingResourceQueue;
|
||||
import org.navalplanner.business.resources.entities.Resource;
|
||||
import org.navalplanner.business.users.daos.IOrderAuthorizationDAO;
|
||||
|
|
@ -61,7 +68,6 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.zkoss.ganttz.data.resourceload.TimeLineRole;
|
||||
import org.zkoss.ganttz.timetracker.zoom.ZoomLevel;
|
||||
import org.zkoss.ganttz.util.Interval;
|
||||
|
||||
|
|
@ -69,18 +75,12 @@ import org.zkoss.ganttz.util.Interval;
|
|||
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
||||
|
||||
@Autowired
|
||||
private IResourceDAO resourcesDAO;
|
||||
private final ResourcesPerDay ONE_RESOURCE_PER_DAY = ResourcesPerDay
|
||||
.amount(new BigDecimal(1));
|
||||
|
||||
@Autowired
|
||||
private IOrderElementDAO orderElementDAO;
|
||||
|
||||
@Autowired
|
||||
private IOrderDAO orderDAO;
|
||||
|
||||
@Autowired
|
||||
private IResourceAllocationDAO resourceAllocationDAO;
|
||||
|
||||
@Autowired
|
||||
private IUserDAO userDAO;
|
||||
|
||||
|
|
@ -90,30 +90,188 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
@Autowired
|
||||
private ILimitingResourceQueueElementDAO limitingResourceQueueElementDAO;
|
||||
|
||||
private List<LimitingResourceQueue> limitingResourceQueues = new ArrayList<LimitingResourceQueue>();
|
||||
@Autowired
|
||||
private ILimitingResourceQueueDAO limitingResourceQueueDAO;
|
||||
|
||||
@Autowired
|
||||
private IResourceDAO resourceDAO;
|
||||
|
||||
@Autowired
|
||||
private ITaskElementDAO taskDAO;
|
||||
|
||||
private Interval viewInterval;
|
||||
|
||||
private Order filterBy;
|
||||
private List<LimitingResourceQueue> limitingResourceQueues = new ArrayList<LimitingResourceQueue>();
|
||||
|
||||
private boolean filterByResources = true;
|
||||
private List<LimitingResourceQueueElement> unassignedLimitingResourceQueueElements = new ArrayList<LimitingResourceQueueElement>();
|
||||
|
||||
private List<LimitingResourceQueueElement> toBeSaved = new ArrayList<LimitingResourceQueueElement>();
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public void initGlobalView(boolean filterByResources) {
|
||||
filterBy = null;
|
||||
this.filterByResources = filterByResources;
|
||||
doGlobalView();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public void initGlobalView(Order filterBy, boolean filterByResources) {
|
||||
this.filterBy = orderDAO.findExistingEntity(filterBy.getId());
|
||||
this.filterByResources = filterByResources;
|
||||
doGlobalView();
|
||||
}
|
||||
|
||||
private void doGlobalView() {
|
||||
loadUnassignedLimitingResourceQueueElements();
|
||||
loadLimitingResourceQueues();
|
||||
final Date startingDate = getEarliestDate();
|
||||
viewInterval = new Interval(startingDate, plusFiveYears(startingDate));
|
||||
}
|
||||
|
||||
private Date getEarliestDate() {
|
||||
final LimitingResourceQueueElement element = getEarliestQueueElement();
|
||||
return (element != null) ? element.getStartDate()
|
||||
.toDateTimeAtCurrentTime().toDate() : new Date();
|
||||
}
|
||||
|
||||
private LimitingResourceQueueElement getEarliestQueueElement() {
|
||||
LimitingResourceQueueElement earliestQueueElement = null;
|
||||
|
||||
if (!limitingResourceQueues.isEmpty()) {
|
||||
for (LimitingResourceQueue each : limitingResourceQueues) {
|
||||
LimitingResourceQueueElement element = getFirstLimitingResourceQueueElement(each);
|
||||
if (element == null) {
|
||||
continue;
|
||||
}
|
||||
if (earliestQueueElement == null
|
||||
|| isEarlier(element, earliestQueueElement)) {
|
||||
earliestQueueElement = element;
|
||||
}
|
||||
}
|
||||
}
|
||||
return earliestQueueElement;
|
||||
}
|
||||
|
||||
private boolean isEarlier(LimitingResourceQueueElement arg1,
|
||||
LimitingResourceQueueElement arg2) {
|
||||
return (arg1.getStartDate().isBefore(arg2.getStartDate()));
|
||||
}
|
||||
|
||||
private LimitingResourceQueueElement getFirstLimitingResourceQueueElement(
|
||||
LimitingResourceQueue queue) {
|
||||
return getFirstChild(queue.getLimitingResourceQueueElements());
|
||||
}
|
||||
|
||||
private LimitingResourceQueueElement getFirstChild(
|
||||
SortedSet<LimitingResourceQueueElement> elements) {
|
||||
return (elements.isEmpty()) ? null : elements.iterator().next();
|
||||
}
|
||||
|
||||
private Date plusFiveYears(Date date) {
|
||||
return (new LocalDate(date)).plusYears(5).toDateTimeAtCurrentTime()
|
||||
.toDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads unassigned {@link LimitingResourceQueueElement} from DB
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private void loadUnassignedLimitingResourceQueueElements() {
|
||||
unassignedLimitingResourceQueueElements.clear();
|
||||
unassignedLimitingResourceQueueElements
|
||||
.addAll(initializeLimitingResourceQueueElements(limitingResourceQueueElementDAO
|
||||
.getUnassigned()));
|
||||
}
|
||||
|
||||
private List<LimitingResourceQueueElement> initializeLimitingResourceQueueElements(
|
||||
List<LimitingResourceQueueElement> elements) {
|
||||
for (LimitingResourceQueueElement each : elements) {
|
||||
initializeLimitingResourceQueueElement(each);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
private void initializeLimitingResourceQueueElement(
|
||||
LimitingResourceQueueElement element) {
|
||||
ResourceAllocation<?> resourceAllocation = element
|
||||
.getResourceAllocation();
|
||||
resourceAllocation = initializeResourceAllocationIfNecessary(resourceAllocation);
|
||||
element.setResourceAllocation(resourceAllocation);
|
||||
resourceAllocation.getTask().getName();
|
||||
initializeCalendarIfAny(element.getResource());
|
||||
}
|
||||
|
||||
private void initializeCalendarIfAny(Resource resource) {
|
||||
if (resource != null) {
|
||||
resourceDAO.reattach(resource);
|
||||
initializeCalendarIfAny(resource.getCalendar());
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeCalendarIfAny(BaseCalendar calendar) {
|
||||
if (calendar != null) {
|
||||
Hibernate.initialize(calendar);
|
||||
initializeCalendarAvailabilities(calendar);
|
||||
initializeCalendarExceptions(calendar);
|
||||
initializeCalendarDataVersions(calendar);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeCalendarAvailabilities(BaseCalendar calendar) {
|
||||
for (CalendarAvailability each : calendar.getCalendarAvailabilities()) {
|
||||
Hibernate.initialize(each);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeCalendarExceptions(BaseCalendar calendar) {
|
||||
for (CalendarException each : calendar.getExceptions()) {
|
||||
Hibernate.initialize(each);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeCalendarDataVersions(BaseCalendar calendar) {
|
||||
for (CalendarData each : calendar.getCalendarDataVersions()) {
|
||||
Hibernate.initialize(each);
|
||||
Hibernate.initialize(each.getHoursPerDay());
|
||||
initializeCalendarIfAny(each.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceAllocation<?> initializeResourceAllocationIfNecessary(
|
||||
ResourceAllocation<?> resourceAllocation) {
|
||||
if (resourceAllocation instanceof HibernateProxy) {
|
||||
resourceAllocation = (ResourceAllocation<?>) ((HibernateProxy) resourceAllocation)
|
||||
.getHibernateLazyInitializer().getImplementation();
|
||||
if (resourceAllocation instanceof SpecificResourceAllocation) {
|
||||
SpecificResourceAllocation specific = (SpecificResourceAllocation) resourceAllocation;
|
||||
Hibernate.initialize(specific.getAssignments());
|
||||
}
|
||||
}
|
||||
return resourceAllocation;
|
||||
}
|
||||
|
||||
private void loadLimitingResourceQueues() {
|
||||
limitingResourceQueues.clear();
|
||||
limitingResourceQueues
|
||||
.addAll(initializeLimitingResourceQueues(limitingResourceQueueDAO
|
||||
.getAll()));
|
||||
}
|
||||
|
||||
private List<LimitingResourceQueue> initializeLimitingResourceQueues(
|
||||
List<LimitingResourceQueue> queues) {
|
||||
for (LimitingResourceQueue each : queues) {
|
||||
initializeLimitingResourceQueue(each);
|
||||
}
|
||||
return queues;
|
||||
}
|
||||
|
||||
private void initializeLimitingResourceQueue(LimitingResourceQueue queue) {
|
||||
Hibernate.initialize(queue.getResource());
|
||||
for (LimitingResourceQueueElement each : queue
|
||||
.getLimitingResourceQueueElements()) {
|
||||
initializeLimitingResourceQueueElement(each);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Order getOrderByTask(TaskElement task) {
|
||||
|
|
@ -121,6 +279,11 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
.loadOrderAvoidingProxyFor(task.getOrderElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interval getViewInterval() {
|
||||
return viewInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public boolean userCanRead(Order order, String loginName) {
|
||||
|
|
@ -145,142 +308,14 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void doGlobalView() {
|
||||
limitingResourceQueues = calculateLimitingResourceQueues();
|
||||
if (!limitingResourceQueues.isEmpty()) {
|
||||
// Build interval
|
||||
// viewInterval =
|
||||
// LimitingResourceQueue.getIntervalFrom(limitingResourceQueues);
|
||||
viewInterval = new Interval(new Date(), plusFiveYears(new Date()));
|
||||
} else {
|
||||
viewInterval = new Interval(new Date(), plusFiveYears(new Date()));
|
||||
}
|
||||
}
|
||||
|
||||
private Date plusFiveYears(Date date) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.YEAR, 5);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
private List<LimitingResourceQueue> calculateLimitingResourceQueues() {
|
||||
List<LimitingResourceQueue> result = new ArrayList<LimitingResourceQueue>();
|
||||
result.addAll(groupsFor(resourcesToShow()));
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Resource> resourcesToShow() {
|
||||
if (filter()) {
|
||||
return resourcesForActiveTasks();
|
||||
} else {
|
||||
return allLimitingResources();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean filter() {
|
||||
return filterBy != null;
|
||||
}
|
||||
|
||||
private List<Resource> resourcesForActiveTasks() {
|
||||
return Resource.sortByName(resourcesDAO
|
||||
.findResourcesRelatedTo(justTasks(filterBy
|
||||
.getAllChildrenAssociatedTaskElements())));
|
||||
}
|
||||
|
||||
private List<Task> justTasks(Collection<? extends TaskElement> tasks) {
|
||||
List<Task> result = new ArrayList<Task>();
|
||||
for (TaskElement taskElement : tasks) {
|
||||
if (taskElement instanceof Task) {
|
||||
result.add((Task) taskElement);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Resource> allLimitingResources() {
|
||||
List<Resource> result = Resource.sortByName(resourcesDAO
|
||||
.getAllLimitingResources());
|
||||
for (Resource each : result) {
|
||||
each.getLimitingResourceQueue().getLimitingResourceQueueElements()
|
||||
.size();
|
||||
limitingResourceQueues.add(each.getLimitingResourceQueue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private TimeLineRole<BaseEntity> getCurrentTimeLineRole(BaseEntity entity) {
|
||||
return new TimeLineRole<BaseEntity>(entity);
|
||||
}
|
||||
|
||||
private List<LimitingResourceQueue> groupsFor(List<Resource> allResources) {
|
||||
List<LimitingResourceQueue> result = new ArrayList<LimitingResourceQueue>();
|
||||
for (Resource resource : allResources) {
|
||||
LimitingResourceQueue group = resource.getLimitingResourceQueue();
|
||||
result.add(group);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initializeIfNeeded(
|
||||
Map<Order, List<ResourceAllocation<?>>> result, Order order) {
|
||||
if (!result.containsKey(order)) {
|
||||
result.put(order, new ArrayList<ResourceAllocation<?>>());
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Map<Order, List<ResourceAllocation<?>>> byOrder(
|
||||
Collection<ResourceAllocation<?>> allocations) {
|
||||
Map<Order, List<ResourceAllocation<?>>> result = new HashMap<Order, List<ResourceAllocation<?>>>();
|
||||
for (ResourceAllocation<?> resourceAllocation : allocations) {
|
||||
if ((resourceAllocation.isSatisfied())
|
||||
&& (resourceAllocation.getTask() != null)) {
|
||||
OrderElement orderElement = resourceAllocation.getTask()
|
||||
.getOrderElement();
|
||||
Order order = orderElementDAO
|
||||
.loadOrderAvoidingProxyFor(orderElement);
|
||||
initializeIfNeeded(result, order);
|
||||
result.get(order).add(resourceAllocation);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private List<GenericResourceAllocation> onlyGeneric(
|
||||
List<ResourceAllocation<?>> sortedByStartDate) {
|
||||
return ResourceAllocation.getOfType(GenericResourceAllocation.class,
|
||||
sortedByStartDate);
|
||||
}
|
||||
|
||||
public static String getName(Collection<? extends Criterion> criterions,
|
||||
Task task) {
|
||||
String prefix = task.getName();
|
||||
return (prefix + " :: " + getName(criterions));
|
||||
}
|
||||
|
||||
public static String getName(Collection<? extends Criterion> criterions) {
|
||||
if (criterions.isEmpty()) {
|
||||
return _("[generic all workers]");
|
||||
}
|
||||
String[] names = new String[criterions.size()];
|
||||
int i = 0;
|
||||
for (Criterion criterion : criterions) {
|
||||
names[i++] = criterion.getName();
|
||||
}
|
||||
return (Arrays.toString(names));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<LimitingResourceQueue> getLimitingResourceQueues() {
|
||||
return limitingResourceQueues;
|
||||
return Collections.unmodifiableList(limitingResourceQueues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interval getViewInterval() {
|
||||
return viewInterval;
|
||||
public List<LimitingResourceQueueElement> getUnassignedLimitingResourceQueueElements() {
|
||||
return Collections
|
||||
.unmodifiableList(unassignedLimitingResourceQueueElements);
|
||||
}
|
||||
|
||||
public ZoomLevel calculateInitialZoomLevel() {
|
||||
|
|
@ -290,15 +325,256 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly=true)
|
||||
public List<LimitingResourceQueueElement> getUnassignedLimitingResourceQueueElements() {
|
||||
List<LimitingResourceQueueElement> result = limitingResourceQueueElementDAO
|
||||
.getUnassigned();
|
||||
for (LimitingResourceQueueElement each : result) {
|
||||
limitingResourceQueueElementDAO.reattach(each);
|
||||
each.getResourceAllocation().getTask().getName();
|
||||
@Transactional(readOnly = true)
|
||||
public void assignLimitingResourceQueueElement(
|
||||
final LimitingResourceQueueElement element) {
|
||||
|
||||
LimitingResourceQueueElement queueElement = retrieveQueueElementFromModel(element);
|
||||
LimitingResourceQueue queue = retrieveQueueByResourceFromModel(queueElement
|
||||
.getResource());
|
||||
|
||||
DateAndHour startTime = findStartTimeInQueueForQueueElement(queue, queueElement);
|
||||
DateAndHour[] startAndEndTime = allocateDayAssignments(queueElement
|
||||
.getResourceAllocation(), startTime);
|
||||
updateStartAndEndTimes(queueElement, startAndEndTime);
|
||||
addLimitingResourceQueueElement(queue, queueElement);
|
||||
toBeSaved.add(queueElement);
|
||||
}
|
||||
|
||||
private DateAndHour findStartTimeInQueueForQueueElement(
|
||||
LimitingResourceQueue queue, LimitingResourceQueueElement candidate) {
|
||||
|
||||
final SortedSet<LimitingResourceQueueElement> elements = queue.getLimitingResourceQueueElements();
|
||||
if (!elements.isEmpty()) {
|
||||
final List<LimitingResourceQueueElementGap> gapList = buildGapList(candidate, elements);
|
||||
final DateAndHour startTime = findStartTimeInGapList(candidate
|
||||
.getIntentedTotalHours(), gapList);
|
||||
return (startTime != null) ? startTime : afterLastElement(elements);
|
||||
}
|
||||
return new DateAndHour(new LocalDate(candidate.getEarlierStartDateBecauseOfGantt()), 0);
|
||||
}
|
||||
|
||||
private DateAndHour afterLastElement(
|
||||
SortedSet<LimitingResourceQueueElement> elements) {
|
||||
return elements.last().getEndTime();
|
||||
}
|
||||
|
||||
private DateAndHour findStartTimeInGapList(Integer hours,
|
||||
List<LimitingResourceQueueElementGap> gapList) {
|
||||
for (LimitingResourceQueueElementGap each : gapList) {
|
||||
if (each.canFit(hours)) {
|
||||
return each.getStartTime();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<LimitingResourceQueueElementGap> buildGapList(LimitingResourceQueueElement candidate,
|
||||
final SortedSet<LimitingResourceQueueElement> elements) {
|
||||
List<LimitingResourceQueueElementGap> result = new ArrayList<LimitingResourceQueueElementGap>();
|
||||
|
||||
// If start time of candidate element to fit in queue is before first
|
||||
// element, create a gap between candidate and the first element of the
|
||||
// queue
|
||||
DateAndHour candidateTime = new DateAndHour(new LocalDate(candidate
|
||||
.getEarlierStartDateBecauseOfGantt()), 0);
|
||||
final LimitingResourceQueueElement firstElement = elements.first();
|
||||
if (candidateTime.compareTo(firstElement.getStartTime()) <= 0) {
|
||||
result.add(createGap(firstElement.getResource(), candidateTime,
|
||||
firstElement.getStartTime()));
|
||||
}
|
||||
|
||||
// Only include gaps from candidate start time on
|
||||
for (Iterator<LimitingResourceQueueElement> i = elements.iterator(); i
|
||||
.hasNext();) {
|
||||
LimitingResourceQueueElement current = i.next();
|
||||
if (i.hasNext()) {
|
||||
LimitingResourceQueueElement next = i.next();
|
||||
if (candidateTime.compareTo(current.getEndTime()) > 1) {
|
||||
final DateAndHour startTime = current.getEndTime();
|
||||
final DateAndHour endTime = next.getStartTime();
|
||||
result.add(createGap(current.getResource(), startTime, endTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public LimitingResourceQueueElementGap createGap(Resource resource, DateAndHour startTime,
|
||||
DateAndHour endTime) {
|
||||
return LimitingResourceQueueElementGap.create(resource, startTime, endTime);
|
||||
}
|
||||
|
||||
private void updateStartAndEndTimes(LimitingResourceQueueElement element,
|
||||
DateAndHour[] startAndEndTime) {
|
||||
|
||||
final DateAndHour startTime = startAndEndTime[0];
|
||||
final DateAndHour endTime = startAndEndTime[1];
|
||||
|
||||
element.setStartDate(startTime.getDate());
|
||||
element.setStartHour(startTime.getHour());
|
||||
element.setEndDate(endTime.getDate());
|
||||
element.setEndHour(endTime.getHour());
|
||||
|
||||
// Update starting and ending dates for associated Task
|
||||
Task task = element.getResourceAllocation().getTask();
|
||||
updateStartingAndEndingDate(task, startTime.getDate(), endTime
|
||||
.getDate());
|
||||
}
|
||||
|
||||
private void updateStartingAndEndingDate(Task task, LocalDate startDate,
|
||||
LocalDate endDate) {
|
||||
task.setStartDate(toDate(startDate));
|
||||
task.setEndDate(endDate.toDateTimeAtStartOfDay().toDate());
|
||||
task.explicityMoved(toDate(startDate));
|
||||
}
|
||||
|
||||
private Date toDate(LocalDate date) {
|
||||
return date.toDateTimeAtStartOfDay().toDate();
|
||||
}
|
||||
|
||||
private void addLimitingResourceQueueElement(LimitingResourceQueue queue,
|
||||
LimitingResourceQueueElement element) {
|
||||
queue.addLimitingResourceQueueElement(element);
|
||||
unassignedLimitingResourceQueueElements.remove(element);
|
||||
}
|
||||
|
||||
private LimitingResourceQueue retrieveQueueByResourceFromModel(Resource resource) {
|
||||
return findQueueByResource(limitingResourceQueues, resource);
|
||||
}
|
||||
|
||||
private LimitingResourceQueue findQueueByResource(
|
||||
List<LimitingResourceQueue> queues, Resource resource) {
|
||||
for (LimitingResourceQueue each : queues) {
|
||||
if (each.getResource().getId().equals(resource.getId())) {
|
||||
return each;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private LimitingResourceQueueElement retrieveQueueElementFromModel(
|
||||
LimitingResourceQueueElement element) {
|
||||
return findQueueElement(unassignedLimitingResourceQueueElements,
|
||||
element);
|
||||
}
|
||||
|
||||
private LimitingResourceQueueElement findQueueElement(
|
||||
List<LimitingResourceQueueElement> elements,
|
||||
LimitingResourceQueueElement element) {
|
||||
for (LimitingResourceQueueElement each : elements) {
|
||||
if (each.getId().equals(element.getId())) {
|
||||
return each;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private DateAndHour[] allocateDayAssignments(
|
||||
ResourceAllocation<?> resourceAllocation, DateAndHour startingTime) {
|
||||
if (resourceAllocation instanceof SpecificResourceAllocation) {
|
||||
return allocateDayAssignments(
|
||||
(SpecificResourceAllocation) resourceAllocation,
|
||||
startingTime);
|
||||
}
|
||||
if (resourceAllocation instanceof GenericResourceAllocation) {
|
||||
// TODO: Generate day assignments for generic resource allocation
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private DateAndHour[] allocateDayAssignments(
|
||||
SpecificResourceAllocation resourceAllocation,
|
||||
DateAndHour startTime) {
|
||||
|
||||
List<SpecificDayAssignment> assignments = new ArrayList<SpecificDayAssignment>();
|
||||
|
||||
DateAndHour newStartTime = startTime;
|
||||
|
||||
LocalDate date = startTime.getDate();
|
||||
int totalHours = resourceAllocation.getIntendedTotalHours();
|
||||
|
||||
// Generate first day assignment
|
||||
int hoursToAllocate = hoursCanWorkOnDay(resourceAllocation, date, startTime.getHour());
|
||||
if (hoursToAllocate != 0) {
|
||||
SpecificDayAssignment dayAssignment = SpecificDayAssignment.create(
|
||||
date, hoursToAllocate, resourceAllocation.getResource());
|
||||
totalHours -= addDayAssignment(assignments, dayAssignment);
|
||||
} else {
|
||||
newStartTime = new DateAndHour(date.plusDays(1), 0);
|
||||
}
|
||||
|
||||
// Generate rest of day assignments
|
||||
for (date = date.plusDays(1); totalHours > 0; date = date.plusDays(1)) {
|
||||
totalHours -= addDayAssignment(assignments, generateDayAssignment(
|
||||
resourceAllocation, date, totalHours));
|
||||
}
|
||||
resourceAllocation.allocateLimitingDayAssignments(assignments);
|
||||
|
||||
DateAndHour newEndTime = new DateAndHour(date, getEndingTime(assignments));
|
||||
DateAndHour[] startAndEndTime = {newStartTime, newEndTime};
|
||||
return startAndEndTime;
|
||||
}
|
||||
|
||||
private DayAssignment getLastDayAssignment(List<SpecificDayAssignment> dayAssignments) {
|
||||
return dayAssignments.get(dayAssignments.size() - 1);
|
||||
}
|
||||
|
||||
private int getEndingTime(List<SpecificDayAssignment> dayAssignments) {
|
||||
return (dayAssignments.isEmpty()) ? 0 : getLastDayAssignment(dayAssignments).getHours();
|
||||
}
|
||||
|
||||
private int addDayAssignment(List<SpecificDayAssignment> list, SpecificDayAssignment dayAssignment) {
|
||||
if (dayAssignment != null) {
|
||||
list.add(dayAssignment);
|
||||
return dayAssignment.getHours();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int hoursCanWorkOnDay(final SpecificResourceAllocation resourceAllocation,
|
||||
final LocalDate date, int alreadyWorked) {
|
||||
final ResourceCalendar calendar = resourceAllocation.getResource()
|
||||
.getCalendar();
|
||||
int hoursCanAllocate = calendar.toHours(date, ONE_RESOURCE_PER_DAY);
|
||||
return hoursCanAllocate - alreadyWorked;
|
||||
}
|
||||
|
||||
private SpecificDayAssignment generateDayAssignment(
|
||||
final SpecificResourceAllocation resourceAllocation,
|
||||
final LocalDate date, int intentedHours) {
|
||||
|
||||
final ResourceCalendar calendar = resourceAllocation.getResource()
|
||||
.getCalendar();
|
||||
|
||||
int hoursCanAllocate = calendar.toHours(date, ONE_RESOURCE_PER_DAY);
|
||||
if (hoursCanAllocate > 0) {
|
||||
int hoursToAllocate = Math.min(intentedHours, hoursCanAllocate);
|
||||
return SpecificDayAssignment.create(date, hoursToAllocate,
|
||||
resourceAllocation.getResource());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void confirm() {
|
||||
saveModifiedQueueElements();
|
||||
loadLimitingResourceQueues();
|
||||
loadUnassignedLimitingResourceQueueElements();
|
||||
}
|
||||
|
||||
private void saveModifiedQueueElements() {
|
||||
for (LimitingResourceQueueElement each : toBeSaved) {
|
||||
limitingResourceQueueElementDAO.save(each);
|
||||
saveAssociatedTask(each);
|
||||
}
|
||||
toBeSaved.clear();
|
||||
}
|
||||
|
||||
private void saveAssociatedTask(LimitingResourceQueueElement element) {
|
||||
Task task = element.getResourceAllocation().getTask();
|
||||
taskDAO.save(task);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ public class LimitingResourcesComponent extends XulElement implements
|
|||
private static void validateQueueElement(
|
||||
LimitingResourceQueueElement queueElement) {
|
||||
if ((queueElement.getStartDate() == null)
|
||||
|| (queueElement.getStartDate() == null)) {
|
||||
|| (queueElement.getEndDate() == null)) {
|
||||
throw new ValidationException(_("Invalid queue element"));
|
||||
}
|
||||
}
|
||||
|
|
@ -181,4 +181,4 @@ public class LimitingResourcesComponent extends XulElement implements
|
|||
appendContextMenus();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import org.apache.commons.lang.Validate;
|
|||
import org.navalplanner.business.orders.entities.Order;
|
||||
import org.navalplanner.business.planner.entities.LimitingResourceQueueElement;
|
||||
import org.navalplanner.business.planner.entities.TaskElement;
|
||||
import org.navalplanner.web.common.Util;
|
||||
import org.navalplanner.web.limitingresources.LimitingResourcesPanel.IToolbarCommand;
|
||||
import org.navalplanner.web.planner.order.BankHolidaysMarker;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -45,6 +46,7 @@ import org.zkoss.zk.ui.event.Events;
|
|||
import org.zkoss.zk.ui.util.Composer;
|
||||
import org.zkoss.zul.Button;
|
||||
import org.zkoss.zul.Checkbox;
|
||||
import org.zkoss.zul.Grid;
|
||||
import org.zkoss.zul.Label;
|
||||
import org.zkoss.zul.Messagebox;
|
||||
import org.zkoss.zul.Row;
|
||||
|
|
@ -71,6 +73,8 @@ public class LimitingResourcesController implements Composer {
|
|||
|
||||
private TimeTracker timeTracker;
|
||||
|
||||
private Grid gridUnassignedLimitingResourceQueueElements;
|
||||
|
||||
private final LimitingResourceQueueElementsRenderer limitingResourceQueueElementsRenderer =
|
||||
new LimitingResourceQueueElementsRenderer();
|
||||
|
||||
|
|
@ -111,6 +115,8 @@ public class LimitingResourcesController implements Composer {
|
|||
this.parent.getChildren().clear();
|
||||
this.parent.appendChild(limitingResourcesPanel);
|
||||
limitingResourcesPanel.afterCompose();
|
||||
gridUnassignedLimitingResourceQueueElements = (Grid) limitingResourcesPanel
|
||||
.getFellowIfAny("gridUnassignedLimitingResourceQueueElements");
|
||||
addCommands(limitingResourcesPanel);
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
|
|
@ -150,6 +156,13 @@ public class LimitingResourcesController implements Composer {
|
|||
SeveralModificators.create(new BankHolidaysMarker()), parent);
|
||||
}
|
||||
|
||||
private void updateLimitingResourceQueues() {
|
||||
limitingResourcesPanel
|
||||
.resetLimitingResourceQueues(limitingResourceQueueModel
|
||||
.getLimitingResourceQueues());
|
||||
limitingResourcesPanel.reloadLimitingResourcesList();
|
||||
}
|
||||
|
||||
private LimitingResourcesPanel buildLimitingResourcesPanel() {
|
||||
LimitingResourcesPanel result = new LimitingResourcesPanel(
|
||||
limitingResourceQueueModel.getLimitingResourceQueues(),
|
||||
|
|
@ -166,6 +179,20 @@ public class LimitingResourcesController implements Composer {
|
|||
this.filterBy = order;
|
||||
}
|
||||
|
||||
public void saveQueues() {
|
||||
limitingResourceQueueModel.confirm();
|
||||
notifyUserThatSavingIsDone();
|
||||
}
|
||||
|
||||
private void notifyUserThatSavingIsDone() {
|
||||
try {
|
||||
Messagebox.show(_("Scheduling saved"), _("Information"), Messagebox.OK,
|
||||
Messagebox.INFORMATION);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public LimitingResourceQueueElementsRenderer getLimitingResourceQueueElementsRenderer() {
|
||||
return limitingResourceQueueElementsRenderer;
|
||||
}
|
||||
|
|
@ -184,18 +211,24 @@ public class LimitingResourcesController implements Composer {
|
|||
|
||||
private Button assignButton(final LimitingResourceQueueElement element) {
|
||||
Button result = new Button();
|
||||
result.setLabel("Assign");
|
||||
result.setLabel(_("Assign"));
|
||||
result.setTooltiptext(_("Assign to queue"));
|
||||
result.addEventListener(Events.ON_CLICK, new EventListener() {
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) throws Exception {
|
||||
// FIXME: assign element to queue
|
||||
assignLimitingResourceQueueElement(element);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private void assignLimitingResourceQueueElement(LimitingResourceQueueElement element) {
|
||||
limitingResourceQueueModel.assignLimitingResourceQueueElement(element);
|
||||
Util.reloadBindings(gridUnassignedLimitingResourceQueueElements);
|
||||
updateLimitingResourceQueues();
|
||||
}
|
||||
|
||||
private Checkbox automaticQueueing(final LimitingResourceQueueElement element) {
|
||||
Checkbox result = new Checkbox();
|
||||
result.setTooltiptext(_("Select for automatic queuing"));
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@ import org.zkoss.zk.ui.HtmlMacroComponent;
|
|||
import org.zkoss.zk.ui.ext.AfterCompose;
|
||||
|
||||
/**
|
||||
* Component to include a list of ResourceLoads inside the ResourcesLoadPanel.
|
||||
* Component to include a list of {@link LimitingResourceQueue} inside the {@link LimitingResourcesPanel}
|
||||
*
|
||||
* @author Lorenzo Tilve Álvaro <ltilve@igalia.com>
|
||||
* @author Diego Pino Garcia <dpino@igalia.com>
|
||||
*/
|
||||
public class LimitingResourcesList extends HtmlMacroComponent implements
|
||||
AfterCompose {
|
||||
|
|
@ -45,31 +47,44 @@ public class LimitingResourcesList extends HtmlMacroComponent implements
|
|||
|
||||
private Map<LimitingResourceQueue, LimitingResourcesComponent> fromTimeLineToComponent = new HashMap<LimitingResourceQueue, LimitingResourcesComponent>();
|
||||
|
||||
private final MutableTreeModel<LimitingResourceQueue> timelinesTree;
|
||||
private MutableTreeModel<LimitingResourceQueue> model;
|
||||
|
||||
private TimeTracker timeTracker;
|
||||
|
||||
private List<LimitingResourcesComponent> limitingResourcesComponents = new ArrayList<LimitingResourcesComponent>();
|
||||
|
||||
public LimitingResourcesList(TimeTracker timeTracker,
|
||||
MutableTreeModel<LimitingResourceQueue> timelinesTree) {
|
||||
this.timelinesTree = timelinesTree;
|
||||
|
||||
this.model = timelinesTree;
|
||||
|
||||
zoomListener = adjustTimeTrackerSizeListener();
|
||||
timeTracker.addZoomListener(zoomListener);
|
||||
LimitingResourceQueue current = timelinesTree.getRoot();
|
||||
List<LimitingResourceQueue> toInsert = new ArrayList<LimitingResourceQueue>();
|
||||
fill(timelinesTree, current, toInsert);
|
||||
insertAsComponents(timeTracker, toInsert);
|
||||
this.timeTracker = timeTracker;
|
||||
|
||||
insertAsComponents(timelinesTree.asList());
|
||||
}
|
||||
|
||||
private void fill(MutableTreeModel<LimitingResourceQueue> timelinesTree,
|
||||
LimitingResourceQueue current, List<LimitingResourceQueue> result) {
|
||||
final int length = timelinesTree.getChildCount(current);
|
||||
for (int i = 0; i < length; i++) {
|
||||
LimitingResourceQueue child = timelinesTree.getChild(current, i);
|
||||
result.add(child);
|
||||
fill(timelinesTree, child, result);
|
||||
private void insertAsComponents(List<LimitingResourceQueue> children) {
|
||||
for (LimitingResourceQueue each : children) {
|
||||
LimitingResourcesComponent component = LimitingResourcesComponent
|
||||
.create(timeTracker, each);
|
||||
this.appendChild(component);
|
||||
fromTimeLineToComponent.put(each, component);
|
||||
}
|
||||
}
|
||||
|
||||
public void setModel(MutableTreeModel<LimitingResourceQueue> model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
fromTimeLineToComponent.clear();
|
||||
this.getChildren().clear();
|
||||
insertAsComponents(model.asList());
|
||||
super.invalidate();
|
||||
}
|
||||
|
||||
private IZoomLevelChangedListener adjustTimeTrackerSizeListener() {
|
||||
return new IZoomLevelChangedListener() {
|
||||
|
||||
|
|
@ -83,24 +98,8 @@ public class LimitingResourcesList extends HtmlMacroComponent implements
|
|||
};
|
||||
}
|
||||
|
||||
private void insertAsComponents(TimeTracker timetracker,
|
||||
List<LimitingResourceQueue> children) {
|
||||
for (LimitingResourceQueue LimitingResourceQueue : children) {
|
||||
LimitingResourcesComponent component = LimitingResourcesComponent
|
||||
.create(timetracker, LimitingResourceQueue);
|
||||
limitingResourcesComponents.add(component);
|
||||
appendChild(component);
|
||||
fromTimeLineToComponent.put(LimitingResourceQueue, component);
|
||||
}
|
||||
}
|
||||
|
||||
public void collapse(LimitingResourceQueue line) {
|
||||
}
|
||||
|
||||
private LimitingResourcesComponent getComponentFor(LimitingResourceQueue l) {
|
||||
LimitingResourcesComponent resourceLoadComponent = fromTimeLineToComponent
|
||||
.get(l);
|
||||
return resourceLoadComponent;
|
||||
}
|
||||
|
||||
public void expand(LimitingResourceQueue line,
|
||||
|
|
|
|||
|
|
@ -88,11 +88,10 @@ public class LimitingResourcesPanel extends HtmlMacroComponent {
|
|||
public LimitingResourcesPanel(List<LimitingResourceQueue> groups,
|
||||
TimeTracker timeTracker) {
|
||||
init(groups, timeTracker);
|
||||
|
||||
}
|
||||
|
||||
public void init(List<LimitingResourceQueue> groups, TimeTracker timeTracker) {
|
||||
this.limitingResourceQueues = groups;
|
||||
limitingResourceQueues.addAll(groups);
|
||||
this.timeTracker = timeTracker;
|
||||
treeModel = createModelForTree();
|
||||
timeTrackerComponent = timeTrackerForResourcesLoadPanel(timeTracker);
|
||||
|
|
@ -103,6 +102,16 @@ public class LimitingResourcesPanel extends HtmlMacroComponent {
|
|||
registerNeededScripts();
|
||||
}
|
||||
|
||||
public void resetLimitingResourceQueues(List<LimitingResourceQueue> queues) {
|
||||
limitingResourceQueues = new ArrayList<LimitingResourceQueue>();
|
||||
limitingResourceQueues.addAll(queues);
|
||||
}
|
||||
|
||||
public void reloadLimitingResourcesList() {
|
||||
limitingResourcesList.setModel(createModelForTree());
|
||||
limitingResourcesList.invalidate();
|
||||
}
|
||||
|
||||
public ListModel getFilters() {
|
||||
String[] filters = new String[] { filterResources, filterCriterions };
|
||||
return new SimpleListModel(filters);
|
||||
|
|
@ -233,6 +242,8 @@ public class LimitingResourcesPanel extends HtmlMacroComponent {
|
|||
|
||||
dependencyList.afterCompose();
|
||||
|
||||
limitingResourcesList.invalidate();
|
||||
|
||||
// Insert timetracker headers
|
||||
TimeTrackerComponent timeTrackerHeader = createTimeTrackerHeader();
|
||||
getFellow("insertionPointTimetracker").appendChild(timeTrackerHeader);
|
||||
|
|
@ -250,12 +261,11 @@ public class LimitingResourcesPanel extends HtmlMacroComponent {
|
|||
}
|
||||
|
||||
private TimeTrackerComponent createTimeTrackerHeader() {
|
||||
return new TimeTrackerComponent(
|
||||
timeTracker) {
|
||||
return new TimeTrackerComponent(timeTracker) {
|
||||
|
||||
@Override
|
||||
protected void scrollHorizontalPercentage(int pixelsDisplacement) {
|
||||
}
|
||||
@Override
|
||||
protected void scrollHorizontalPercentage(int pixelsDisplacement) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,40 +26,52 @@ resourcesLoadPanel = self;
|
|||
]]>
|
||||
</zscript>
|
||||
|
||||
|
||||
<borderlayout sclass="resourcesloadlayout" width="auto">
|
||||
<north height="30px" border="0" sclass="toolbar-box">
|
||||
<hbox align="center" id="toolbar">
|
||||
<separator/>
|
||||
<label>${i18n:_('Zoom')}:</label>
|
||||
<listbox id="listZoomLevels" mold="select" rows="1"
|
||||
model="${resourcesLoadPanel.zoomLevels}"
|
||||
onSelect="resourcesLoadPanel.setZoomLevel(self.selectedItem.value);" >
|
||||
</listbox>
|
||||
|
||||
<separator/>
|
||||
<label>${i18n:_('Pagination')}:</label>
|
||||
<button tooltiptext="Page down" image="/common/img/ico_left.png" id="paginationDownButton"
|
||||
onClick="advancedAllocationController.paginationDown();" disabled="true" />
|
||||
<listbox mold="select" rows="1" visible="false" id="advancedAllocationHorizontalPagination"
|
||||
onSelect="advancedAllocationController.goToSelectedHorizontalPage();" />
|
||||
<button tooltiptext="Page up" image="/common/img/ico_right.png" id="paginationUpButton"
|
||||
onClick="advancedAllocationController.paginationUp();" />
|
||||
<!-- Top control button bar -->
|
||||
<north height="30px" border="0" sclass="toolbar-box">
|
||||
<hbox align="center" id="toolbar">
|
||||
|
||||
<separator/>
|
||||
|
||||
<separator/>
|
||||
Filter:
|
||||
<listbox id="listFilters" mold="select" rows="1" width="200px"
|
||||
model="${resourcesLoadPanel.filters}"
|
||||
onSelect="resourcesLoadPanel.setFilter(self.selectedItem.value);" >
|
||||
</listbox>
|
||||
<button image="/common/img/ico_filter.png" style="margin-top: -4px"
|
||||
tooltiptext="${i18n:_('Apply filtering to resource load satisfying required criteria')}"
|
||||
onClick="resourcesLoadPanel.onApplyFilter()"/>
|
||||
<button image="/common/img/ico_save.png" style="margin-top: -4px"
|
||||
tooltiptext="${i18n:_('Save')}"
|
||||
onClick="limitingResourcesController.saveQueues()"/>
|
||||
|
||||
</hbox>
|
||||
</north>
|
||||
<separator/>
|
||||
|
||||
<!-- Zoom -->
|
||||
<label>${i18n:_('Zoom')}:</label>
|
||||
<listbox id="listZoomLevels" mold="select" rows="1"
|
||||
model="${resourcesLoadPanel.zoomLevels}"
|
||||
onSelect="resourcesLoadPanel.setZoomLevel(self.selectedItem.value);" >
|
||||
</listbox>
|
||||
|
||||
<separator/>
|
||||
|
||||
<!-- Pagination -->
|
||||
<label>${i18n:_('Pagination')}:</label>
|
||||
<button tooltiptext="Page down" image="/common/img/ico_left.png" id="paginationDownButton"
|
||||
onClick="advancedAllocationController.paginationDown();" disabled="true" />
|
||||
<listbox mold="select" rows="1" visible="false" id="advancedAllocationHorizontalPagination"
|
||||
onSelect="advancedAllocationController.goToSelectedHorizontalPage();" />
|
||||
<button tooltiptext="Page up" image="/common/img/ico_right.png" id="paginationUpButton"
|
||||
onClick="advancedAllocationController.paginationUp();" />
|
||||
|
||||
<separator/>
|
||||
|
||||
<!-- Filter -->
|
||||
<label>${i18n:_('Filter')}:</label>
|
||||
<listbox id="listFilters" mold="select" rows="1" width="200px"
|
||||
model="${resourcesLoadPanel.filters}"
|
||||
onSelect="resourcesLoadPanel.setFilter(self.selectedItem.value);" />
|
||||
<button image="/common/img/ico_filter.png" style="margin-top: -4px"
|
||||
tooltiptext="${i18n:_('Apply filtering to resource load satisfying required criteria')}"
|
||||
onClick="resourcesLoadPanel.onApplyFilter()"/>
|
||||
</hbox>
|
||||
</north>
|
||||
|
||||
<!-- List of queues -->
|
||||
<center border="0">
|
||||
<borderlayout sclass="resourcesload limitingresources">
|
||||
<west size="250px" flex="true" collapsible="true"
|
||||
|
|
@ -89,13 +101,14 @@ resourcesLoadPanel = self;
|
|||
<div id="insertionPointRightPanel" sclass="taskspanelgap"></div>
|
||||
</center>
|
||||
</borderlayout>
|
||||
|
||||
</center>
|
||||
</borderlayout>
|
||||
</center>
|
||||
|
||||
<!-- List of unassigned queue elements -->
|
||||
<south height="170px" collapsible="true" title="Tasks input buffer" sclass="limiting-resources-buffer">
|
||||
<grid model="@{limitingResourcesController.unassignedLimitingResourceQueueElements}"
|
||||
<grid id="gridUnassignedLimitingResourceQueueElements"
|
||||
model="@{limitingResourcesController.unassignedLimitingResourceQueueElements}"
|
||||
rowRenderer="@{limitingResourcesController.limitingResourceQueueElementsRenderer}"
|
||||
fixedLayout="true"
|
||||
style="margin: 10px" >
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue