diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java index 99abba5c8..d5ffa96a4 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/ResourceCalendar.java @@ -64,6 +64,15 @@ public class ResourceCalendar extends BaseCalendar { addNewCalendarAvailability(calendarAvailability); } + public Integer getCapacity(LocalDate from, LocalDate to) { + Integer result = getCapacityAt(to); + for (LocalDate date = from; date.isBefore(to);) { + result += getCapacityAt(date); + date = date.plusDays(1); + } + return result; + } + @Override public Integer getCapacityAt(LocalDate date) { if (!isActive(date)) { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java index d08cfd66d..47b4cfdea 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java @@ -20,8 +20,11 @@ package org.navalplanner.business.planner.daos; +import java.util.List; + import org.navalplanner.business.common.daos.IGenericDAO; import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; /** * DAO interface for {@link ILimitingResourceQueueDAO} @@ -31,4 +34,8 @@ import org.navalplanner.business.resources.entities.LimitingResourceQueue; public interface ILimitingResourceQueueDAO extends IGenericDAO { + LimitingResourceQueue findQueueByResource(Resource resource); + + List getAll(); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java index f64eb94ef..ad3a1720e 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java @@ -20,8 +20,12 @@ package org.navalplanner.business.planner.daos; +import java.util.List; + +import org.hibernate.criterion.Restrictions; import org.navalplanner.business.common.daos.GenericDAOHibernate; import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; @@ -37,4 +41,15 @@ public class LimitingResourceQueueDAO extends GenericDAOHibernate implements ILimitingResourceQueueDAO { + public LimitingResourceQueue findQueueByResource(Resource resource) { + return (LimitingResourceQueue) getSession().createCriteria( + LimitingResourceQueue.class).add( + Restrictions.eq("resource", resource)).uniqueResult(); + } + + @Override + public List getAll() { + return list(LimitingResourceQueue.class); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DateAndHour.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DateAndHour.java new file mode 100644 index 000000000..c3577d06f --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DateAndHour.java @@ -0,0 +1,49 @@ +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) ? Math.abs(deltaHour) : 0; + } + + public String toString() { + return date + "; " + hour; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java index f60349695..00da2b5a2 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java @@ -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()); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java new file mode 100644 index 000000000..512f49e60 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElementGap.java @@ -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 + * + */ +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; + } + +} 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 25f9ad2e5..6f2d71022 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 @@ -295,7 +295,7 @@ public abstract class ResourceAllocation extends @NotNull private ResourcesPerDay resourcesPerDay; - private Integer intendedTotalHours; + private Integer intendedTotalHours = 0; private Set derivedAllocations = new HashSet(); @@ -566,6 +566,11 @@ public abstract class ResourceAllocation extends setOriginalTotalAssigment(getAssignedHours()); } + public void allocateLimitingDayAssignments(List assignments) { + assert isLimiting(); + resetAssignmentsTo(assignments); + } + protected abstract void addingAssignments( Collection assignments); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java index fdc3a4332..8f25e3989 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java @@ -21,6 +21,7 @@ package org.navalplanner.business.resources.entities; import java.util.Comparator; +import org.joda.time.LocalDate; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; /** @@ -34,17 +35,24 @@ public class LimitingResourceQueueElementComparator implements @Override public int compare(LimitingResourceQueueElement arg0, LimitingResourceQueueElement arg1) { - final int deltaHour = arg0.getStartHour() - arg1.getStartHour(); - if (deltaHour != 0) { - return deltaHour / Math.abs(deltaHour); - } - if (arg0.getStartDate() == null) { + int compareDates = compare(arg0.getStartDate(), arg1.getStartDate()); + return (compareDates != 0) ? compareDates : compare( + arg0.getStartHour(), arg1.getStartHour()); + } + + private int compare(LocalDate arg0, LocalDate arg1) { + if (arg0 == null) { return -1; } - if (arg1.getStartDate() == null) { + if (arg1 == null) { return 1; } - return arg0.getStartDate().compareTo(arg1.getStartDate()); + return arg0.compareTo(arg1); + } + + private int compare(int arg0, int arg1) { + final int deltaHour = arg0 - arg1; + return (deltaHour != 0) ? deltaHour / Math.abs(deltaHour) : 0; } } diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml index cae5cf815..f0c05d29a 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml @@ -71,7 +71,7 @@ - + diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java index 4fe25b5f1..b66d24e07 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java @@ -23,28 +23,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} + * + * Conversational protocol: + *
    + *
  • + * Initial conversation step: initGlobalView
  • + *
  • + * Intermediate conversation steps: + * assignLimitingResourceQueueElement, + * getLimitingResourceQueues, + * getUnassignedLimitingResourceQueueElements
  • + *
  • + * Final conversation step: confirm
  • + * + * + * @author Diego Pino Garcia + * + */ public interface ILimitingResourceQueueModel { + /** + * Assigns a {@link LimitingResourceQueueElement} to its corresponding + * {@link LimitingResourceQueue} + * + * There is one and only one queue for every limiting resource. An element + * is assigned to its queue searching by element.resource. + * + * Allocation within the queue is done by finding the first gap in the queue + * that fits the initial intented hours assigned to + * element.resourceallocation. + * + * The method also generates {@link DayAssignment} once 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 getLimitingResourceQueues(); + + Order getOrderByTask(TaskElement task); + + /** + * Returns all existing {@link LimitingResourceQueueElement} which are not + * assigned to any {@link LimitingResourceQueue} + * + * @return + */ + List getUnassignedLimitingResourceQueueElements(); + + Interval getViewInterval(); + + /** + * Loads {@link LimitingResourceQueue} and unassigned {@link LimitingResourceQueueElement} from DB + * + * @param filterByResources + */ void initGlobalView(boolean filterByResources); void initGlobalView(Order filterBy, boolean filterByResources); - List getLimitingResourceQueues(); - - Interval getViewInterval(); - - ZoomLevel calculateInitialZoomLevel(); - - Order getOrderByTask(TaskElement task); - boolean userCanRead(Order order, String loginName); - List getUnassignedLimitingResourceQueueElements(); - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java index 423c1ccff..89db9e8b8 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java @@ -20,33 +20,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 limitingResourceQueues = new ArrayList(); + @Autowired + private ILimitingResourceQueueDAO limitingResourceQueueDAO; + + @Autowired + private IResourceDAO resourceDAO; + + @Autowired + private ITaskElementDAO taskDAO; private Interval viewInterval; - private Order filterBy; + private List limitingResourceQueues = new ArrayList(); - private boolean filterByResources = true; + private List unassignedLimitingResourceQueueElements = new ArrayList(); + + private List toBeSaved = new ArrayList(); @Override @Transactional(readOnly = true) public void initGlobalView(boolean filterByResources) { - filterBy = null; - this.filterByResources = filterByResources; doGlobalView(); } @Override @Transactional(readOnly = true) public void initGlobalView(Order filterBy, boolean filterByResources) { - this.filterBy = orderDAO.findExistingEntity(filterBy.getId()); - this.filterByResources = filterByResources; doGlobalView(); } + private void doGlobalView() { + loadUnassignedLimitingResourceQueueElements(); + loadLimitingResourceQueues(); + final Date startingDate = getEarliestDate(); + viewInterval = new Interval(startingDate, plusFiveYears(startingDate)); + } + + private Date getEarliestDate() { + final LimitingResourceQueueElement element = getEarliestQueueElement(); + return (element != null) ? element.getStartDate() + .toDateTimeAtCurrentTime().toDate() : new Date(); + } + + private LimitingResourceQueueElement getEarliestQueueElement() { + LimitingResourceQueueElement earliestQueueElement = null; + + if (!limitingResourceQueues.isEmpty()) { + for (LimitingResourceQueue each : limitingResourceQueues) { + LimitingResourceQueueElement element = getFirstLimitingResourceQueueElement(each); + if (element == null) { + continue; + } + if (earliestQueueElement == null + || isEarlier(element, earliestQueueElement)) { + earliestQueueElement = element; + } + } + } + return earliestQueueElement; + } + + private boolean isEarlier(LimitingResourceQueueElement arg1, + LimitingResourceQueueElement arg2) { + return (arg1.getStartDate().isBefore(arg2.getStartDate())); + } + + private LimitingResourceQueueElement getFirstLimitingResourceQueueElement( + LimitingResourceQueue queue) { + return getFirstChild(queue.getLimitingResourceQueueElements()); + } + + private LimitingResourceQueueElement getFirstChild( + SortedSet elements) { + return (elements.isEmpty()) ? null : elements.iterator().next(); + } + + private Date plusFiveYears(Date date) { + return (new LocalDate(date)).plusYears(5).toDateTimeAtCurrentTime() + .toDate(); + } + + /** + * Loads unassigned {@link LimitingResourceQueueElement} from DB + * + * @return + */ + private void loadUnassignedLimitingResourceQueueElements() { + unassignedLimitingResourceQueueElements.clear(); + unassignedLimitingResourceQueueElements + .addAll(initializeLimitingResourceQueueElements(limitingResourceQueueElementDAO + .getUnassigned())); + } + + private List initializeLimitingResourceQueueElements( + List elements) { + for (LimitingResourceQueueElement each : elements) { + initializeLimitingResourceQueueElement(each); + } + return elements; + } + + private void initializeLimitingResourceQueueElement( + LimitingResourceQueueElement element) { + ResourceAllocation resourceAllocation = element + .getResourceAllocation(); + resourceAllocation = initializeResourceAllocationIfNecessary(resourceAllocation); + element.setResourceAllocation(resourceAllocation); + 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 initializeLimitingResourceQueues( + List 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 calculateLimitingResourceQueues() { - List result = new ArrayList(); - result.addAll(groupsFor(resourcesToShow())); - return result; - } - - private List resourcesToShow() { - if (filter()) { - return resourcesForActiveTasks(); - } else { - return allLimitingResources(); - } - } - - private boolean filter() { - return filterBy != null; - } - - private List resourcesForActiveTasks() { - return Resource.sortByName(resourcesDAO - .findResourcesRelatedTo(justTasks(filterBy - .getAllChildrenAssociatedTaskElements()))); - } - - private List justTasks(Collection tasks) { - List result = new ArrayList(); - for (TaskElement taskElement : tasks) { - if (taskElement instanceof Task) { - result.add((Task) taskElement); - } - } - return result; - } - - private List allLimitingResources() { - List result = Resource.sortByName(resourcesDAO - .getAllLimitingResources()); - for (Resource each : result) { - each.getLimitingResourceQueue().getLimitingResourceQueueElements() - .size(); - limitingResourceQueues.add(each.getLimitingResourceQueue()); - } - return result; - } - - private TimeLineRole getCurrentTimeLineRole(BaseEntity entity) { - return new TimeLineRole(entity); - } - - private List groupsFor(List allResources) { - List result = new ArrayList(); - for (Resource resource : allResources) { - LimitingResourceQueue group = resource.getLimitingResourceQueue(); - result.add(group); - } - return result; - } - - private void initializeIfNeeded( - Map>> result, Order order) { - if (!result.containsKey(order)) { - result.put(order, new ArrayList>()); - } - } - - @Transactional(readOnly = true) - public Map>> byOrder( - Collection> allocations) { - Map>> result = new HashMap>>(); - for (ResourceAllocation resourceAllocation : allocations) { - if ((resourceAllocation.isSatisfied()) - && (resourceAllocation.getTask() != null)) { - OrderElement orderElement = resourceAllocation.getTask() - .getOrderElement(); - Order order = orderElementDAO - .loadOrderAvoidingProxyFor(orderElement); - initializeIfNeeded(result, order); - result.get(order).add(resourceAllocation); - } - } - return result; - } - - - private List onlyGeneric( - List> sortedByStartDate) { - return ResourceAllocation.getOfType(GenericResourceAllocation.class, - sortedByStartDate); - } - - public static String getName(Collection criterions, - Task task) { - String prefix = task.getName(); - return (prefix + " :: " + getName(criterions)); - } - - public static String getName(Collection criterions) { - if (criterions.isEmpty()) { - return _("[generic all workers]"); - } - String[] names = new String[criterions.size()]; - int i = 0; - for (Criterion criterion : criterions) { - names[i++] = criterion.getName(); - } - return (Arrays.toString(names)); - } - - @Override public List getLimitingResourceQueues() { - return limitingResourceQueues; + return Collections.unmodifiableList(limitingResourceQueues); } - @Override - public Interval getViewInterval() { - return viewInterval; + public List getUnassignedLimitingResourceQueueElements() { + return Collections + .unmodifiableList(unassignedLimitingResourceQueueElements); } public ZoomLevel calculateInitialZoomLevel() { @@ -290,15 +325,256 @@ public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { } @Override - @Transactional(readOnly=true) - public List getUnassignedLimitingResourceQueueElements() { - List result = limitingResourceQueueElementDAO - .getUnassigned(); - for (LimitingResourceQueueElement each : result) { - limitingResourceQueueElementDAO.reattach(each); - each.getResourceAllocation().getTask().getName(); + @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 elements = queue.getLimitingResourceQueueElements(); + if (!elements.isEmpty()) { + final List 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 elements) { + return elements.last().getEndTime(); + } + + private DateAndHour findStartTimeInGapList(Integer hours, + List gapList) { + for (LimitingResourceQueueElementGap each : gapList) { + if (each.canFit(hours)) { + return each.getStartTime(); + } + } + return null; + } + + private List buildGapList(LimitingResourceQueueElement candidate, + final SortedSet elements) { + List result = new ArrayList(); + + // 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 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 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 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 assignments = new ArrayList(); + + 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 dayAssignments) { + return dayAssignments.get(dayAssignments.size() - 1); + } + + private int getEndingTime(List dayAssignments) { + return (dayAssignments.isEmpty()) ? 0 : getLastDayAssignment(dayAssignments).getHours(); + } + + private int addDayAssignment(List 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); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java index b762ab899..567e5d050 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java @@ -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(); } -} \ No newline at end of file +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java index 806a6e5d2..4be439bed 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 @@ -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")); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java index 80d34ac0c..9d22f79d9 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java @@ -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 + * @author Diego Pino Garcia */ public class LimitingResourcesList extends HtmlMacroComponent implements AfterCompose { @@ -45,31 +47,44 @@ public class LimitingResourcesList extends HtmlMacroComponent implements private Map fromTimeLineToComponent = new HashMap(); - private final MutableTreeModel timelinesTree; + private MutableTreeModel model; + + private TimeTracker timeTracker; private List limitingResourcesComponents = new ArrayList(); public LimitingResourcesList(TimeTracker timeTracker, MutableTreeModel timelinesTree) { - this.timelinesTree = timelinesTree; + + this.model = timelinesTree; + zoomListener = adjustTimeTrackerSizeListener(); timeTracker.addZoomListener(zoomListener); - LimitingResourceQueue current = timelinesTree.getRoot(); - List toInsert = new ArrayList(); - fill(timelinesTree, current, toInsert); - insertAsComponents(timeTracker, toInsert); + this.timeTracker = timeTracker; + + insertAsComponents(timelinesTree.asList()); } - private void fill(MutableTreeModel timelinesTree, - LimitingResourceQueue current, List result) { - final int length = timelinesTree.getChildCount(current); - for (int i = 0; i < length; i++) { - LimitingResourceQueue child = timelinesTree.getChild(current, i); - result.add(child); - fill(timelinesTree, child, result); + private void insertAsComponents(List children) { + for (LimitingResourceQueue each : children) { + LimitingResourcesComponent component = LimitingResourcesComponent + .create(timeTracker, each); + this.appendChild(component); + fromTimeLineToComponent.put(each, component); } } + public void setModel(MutableTreeModel 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 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, diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java index 4fc6e9a5b..7e1ee9b06 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 @@ -88,11 +88,10 @@ public class LimitingResourcesPanel extends HtmlMacroComponent { public LimitingResourcesPanel(List groups, TimeTracker timeTracker) { init(groups, timeTracker); - } public void init(List 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 queues) { + limitingResourceQueues = new ArrayList(); + 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) { + } }; } diff --git a/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul b/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul index a3fec3107..780100a77 100644 --- a/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul +++ b/navalplanner-webapp/src/main/webapp/limitingresources/limitingResourcesLayout.zul @@ -26,40 +26,52 @@ resourcesLoadPanel = self; ]]> - - - - - - - - - -