Merge branch 'adapt-planning-according-timesheets'
This commit is contained in:
commit
b218f380a7
63 changed files with 1405 additions and 157 deletions
|
|
@ -509,9 +509,11 @@ public class Planner extends HtmlMacroComponent {
|
|||
}
|
||||
for (CommandContextualized<?> c : contextualizedGlobalCommands) {
|
||||
// Comparison through icon as name is internationalized
|
||||
if (c.getCommand().getImage()
|
||||
.equals("/common/img/ico_reassign.png")) {
|
||||
if (plannerToolbar.getChildren().isEmpty()) {
|
||||
if (c.getCommand().isPlannerCommand()) {
|
||||
// FIXME Avoid hard-coding the number of planner commands
|
||||
// At this moment we have 2 planner commands: reassign and adapt
|
||||
// planning
|
||||
if (plannerToolbar.getChildren().size() < 2) {
|
||||
plannerToolbar.appendChild(c.toButton());
|
||||
}
|
||||
} else {
|
||||
|
|
@ -942,4 +944,18 @@ public class Planner extends HtmlMacroComponent {
|
|||
}
|
||||
}
|
||||
|
||||
public TaskComponent getTaskComponentRelatedTo(
|
||||
org.zkoss.ganttz.data.Task task) {
|
||||
TaskList taskList = getTaskList();
|
||||
if (taskList != null) {
|
||||
for (TaskComponent each : taskList.getTaskComponents()) {
|
||||
if (each.getTask().equals(task)) {
|
||||
return each;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,14 @@ package org.zkoss.ganttz;
|
|||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.zkoss.ganttz.adapters.IDisabilityConfiguration;
|
||||
import org.zkoss.ganttz.data.GanttDate;
|
||||
|
|
@ -517,8 +520,34 @@ public class TaskComponent extends Div implements AfterCompose {
|
|||
this.task.getHoursAdvanceEndDate()) + "px";
|
||||
response(null, new AuInvoke(this, "resizeCompletionAdvance",
|
||||
widthHoursAdvancePercentage));
|
||||
|
||||
Date firstTimesheetDate = task.getFirstTimesheetDate();
|
||||
Date lastTimesheetDate = task.getLastTimesheetDate();
|
||||
if (firstTimesheetDate != null && lastTimesheetDate != null) {
|
||||
Duration firstDuration = Days.daysBetween(
|
||||
task.getBeginDateAsLocalDate(),
|
||||
LocalDate.fromDateFields(firstTimesheetDate))
|
||||
.toStandardDuration();
|
||||
int pixelsFirst = getMapper().toPixels(firstDuration);
|
||||
String positionFirst = pixelsFirst + "px";
|
||||
|
||||
Duration lastDuration = Days
|
||||
.daysBetween(
|
||||
task.getBeginDateAsLocalDate(),
|
||||
LocalDate.fromDateFields(lastTimesheetDate)
|
||||
.plusDays(1)).toStandardDuration();
|
||||
int pixelsLast = getMapper().toPixels(lastDuration);
|
||||
String positionLast = pixelsLast + "px";
|
||||
|
||||
response(null, new AuInvoke(this, "showTimsheetDateMarks",
|
||||
positionFirst, positionLast));
|
||||
} else {
|
||||
response(null, new AuInvoke(this, "hideTimsheetDateMarks"));
|
||||
}
|
||||
|
||||
} else {
|
||||
response(null, new AuInvoke(this, "resizeCompletionAdvance", "0px"));
|
||||
response(null, new AuInvoke(this, "hideTimsheetDateMarks"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,11 @@ public class PlannerConfiguration<T> implements IDisabilityConfiguration {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlannerCommand() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class NullCommandOnTask<T> implements ICommandOnTask<T> {
|
||||
|
|
|
|||
|
|
@ -308,4 +308,19 @@ public class DefaultFundamentalProperties implements ITaskFundamentalProperties
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdatedFromTimesheets() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getFirstTimesheetDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastTimesheetDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,4 +125,10 @@ public interface ITaskFundamentalProperties {
|
|||
|
||||
public boolean isRoot();
|
||||
|
||||
boolean isUpdatedFromTimesheets();
|
||||
|
||||
Date getFirstTimesheetDate();
|
||||
|
||||
Date getLastTimesheetDate();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -548,6 +548,21 @@ public abstract class Task implements ITaskFundamentalProperties {
|
|||
getBeginDate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdatedFromTimesheets() {
|
||||
return fundamentalProperties.isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getFirstTimesheetDate() {
|
||||
return fundamentalProperties.getFirstTimesheetDate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastTimesheetDate() {
|
||||
return fundamentalProperties.getLastTimesheetDate();
|
||||
}
|
||||
|
||||
public void firePropertyChangeForTaskDates() {
|
||||
fundamentalPropertiesListeners.firePropertyChange("beginDate", null,
|
||||
getBeginDate());
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public class TaskLeaf extends Task {
|
|||
@Override
|
||||
public boolean canBeExplicitlyMoved() {
|
||||
return !(isSubcontracted() || isLimitingAndHasDayAssignments()
|
||||
|| hasConsolidations() || isManualAnyAllocation());
|
||||
|| hasConsolidations() || isManualAnyAllocation() || isUpdatedFromTimesheets());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,4 +39,10 @@ public interface ICommand<T> {
|
|||
|
||||
boolean isDisabled();
|
||||
|
||||
/**
|
||||
* Describes if a command is for the planner toolbar. Otherwise it'll be
|
||||
* inserted in the common toolbar.
|
||||
*/
|
||||
boolean isPlannerCommand();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,6 +221,18 @@ ganttz.TaskComponent = zk.$extends(zul.Widget, {
|
|||
resizeCompletionAdvance : function(width){
|
||||
jq('#' + this.uuid + ' .completion:first').css('width', width);
|
||||
},
|
||||
showTimsheetDateMarks : function(positionFirst, postionLast) {
|
||||
var firstTimesheetDateMark = jq('#' + this.uuid + ' .first-timesheet-date');
|
||||
var lastTimesheetDateMark = jq('#' + this.uuid + ' .last-timesheet-date');
|
||||
firstTimesheetDateMark.css('left', positionFirst);
|
||||
lastTimesheetDateMark.css('left', postionLast);
|
||||
firstTimesheetDateMark.show();
|
||||
lastTimesheetDateMark.show();
|
||||
},
|
||||
hideTimsheetDateMarks : function() {
|
||||
jq('#' + this.uuid + ' .first-timesheet-date').hide();
|
||||
jq('#' + this.uuid + ' .last-timesheet-date').hide();
|
||||
},
|
||||
resizeCompletion2Advance : function(width){
|
||||
jq('#' + this.uuid + ' .completion2:first').css('width', width);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ function(out){
|
|||
out.push('<div class="completionMoneyCostBar"></div>');
|
||||
out.push('<div class="completion"></div>');
|
||||
out.push('<div class="completion2"></div>');
|
||||
out.push('<div class="timesheet-date-mark first-timesheet-date">|</div>');
|
||||
out.push('<div class="timesheet-date-mark last-timesheet-date">|</div>');
|
||||
|
||||
out.push('<div id="tasktooltip', this.uuid,'" class="task_tooltip">',
|
||||
this.getTooltipText(),
|
||||
|
|
|
|||
|
|
@ -36,16 +36,26 @@ public enum PredefinedAdvancedTypes {
|
|||
UNITS("units", new BigDecimal(Integer.MAX_VALUE),
|
||||
new BigDecimal(1), false, false),
|
||||
SUBCONTRACTOR("subcontractor",
|
||||
new BigDecimal(100), new BigDecimal(0.01), true, false);
|
||||
new BigDecimal(100), new BigDecimal(0.01), true, false),
|
||||
TIMESHEETS("timesheets",
|
||||
new BigDecimal(100), new BigDecimal(0.01), true, false, true);
|
||||
|
||||
|
||||
private PredefinedAdvancedTypes(String name, BigDecimal defaultMaxValue,
|
||||
BigDecimal precision, boolean percentage, boolean qualityForm) {
|
||||
this(name, defaultMaxValue, precision, percentage, qualityForm, false);
|
||||
}
|
||||
|
||||
private PredefinedAdvancedTypes(String name, BigDecimal defaultMaxValue,
|
||||
BigDecimal precision, boolean percentage, boolean qualityForm,
|
||||
boolean readOnly) {
|
||||
this.name = name;
|
||||
this.defaultMaxValue = defaultMaxValue.setScale(4,
|
||||
BigDecimal.ROUND_HALF_UP);
|
||||
this.unitPrecision = precision.setScale(4, BigDecimal.ROUND_HALF_UP);
|
||||
this.percentage = percentage;
|
||||
this.qualityForm = qualityForm;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
private final String name;
|
||||
|
|
@ -58,9 +68,13 @@ public enum PredefinedAdvancedTypes {
|
|||
|
||||
private final boolean qualityForm;
|
||||
|
||||
private final boolean readOnly;
|
||||
|
||||
public AdvanceType createType() {
|
||||
return AdvanceType.create(name, defaultMaxValue, false, unitPrecision,
|
||||
true, percentage, qualityForm);
|
||||
AdvanceType advanceType = AdvanceType.create(name, defaultMaxValue,
|
||||
false, unitPrecision, true, percentage, qualityForm);
|
||||
advanceType.setReadOnly(readOnly);
|
||||
return advanceType;
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ public class AdvanceType extends BaseEntity implements IHumanIdentifiable{
|
|||
|
||||
private IAdvanceTypeDAO avanceTypeDAO = Registry.getAdvanceTypeDao();
|
||||
|
||||
private boolean readOnly = false;
|
||||
|
||||
/**
|
||||
* Constructor for hibernate. Do not use!
|
||||
*/
|
||||
|
|
@ -271,4 +273,12 @@ public class AdvanceType extends BaseEntity implements IHumanIdentifiable{
|
|||
return true;
|
||||
}
|
||||
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,4 +263,9 @@ public class DirectAdvanceAssignment extends AdvanceAssignment {
|
|||
return !nonCalculatedConsolidations.isEmpty();
|
||||
}
|
||||
|
||||
public void resetAdvanceMeasurements(AdvanceMeasurement advanceMeasurement) {
|
||||
advanceMeasurements.clear();
|
||||
addAdvanceMeasurements(advanceMeasurement);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* This file is part of LibrePlan
|
||||
*
|
||||
* Copyright (C) 2012 Igalia, S.L.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.libreplan.business.common;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Utilities class. <br />
|
||||
* @author Manuel Rego Casasnovas <mrego@igalia.com>
|
||||
*/
|
||||
public class Util {
|
||||
|
||||
public static boolean equals(BaseEntity entity1, BaseEntity entity2) {
|
||||
if (entity1 == null || entity2 == null) {
|
||||
return false;
|
||||
}
|
||||
if (entity1.getId() == null || entity2.getId() == null) {
|
||||
return false;
|
||||
}
|
||||
return entity1.getId().equals(entity2.getId());
|
||||
}
|
||||
|
||||
public static boolean contains(Collection<? extends BaseEntity> collection,
|
||||
BaseEntity entity) {
|
||||
for (BaseEntity each : collection) {
|
||||
if (each.getId().equals(entity.getId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -74,4 +74,26 @@ public interface ISumChargedEffortDAO extends
|
|||
*/
|
||||
void recalculateSumChargedEfforts(Long orderId);
|
||||
|
||||
/**
|
||||
* Returns a {@link Set} of {@link OrderElement OrderElements} affected by
|
||||
* any change taking into account the lines in the report and the ones to be
|
||||
* removed.
|
||||
*
|
||||
* Usually you call this method to get the set before saving the work
|
||||
* report. After saving the work report you call
|
||||
* {@link ISumChargedEffortDAO#recalculateTimesheetData(Set)} with the
|
||||
* result of this method.
|
||||
*
|
||||
* You can pass <code>null</code> as param if you only have one of the sets.
|
||||
*/
|
||||
Set<OrderElement> getOrderElementsToRecalculateTimsheetDates(
|
||||
Set<WorkReportLine> workReportLines,
|
||||
Set<WorkReportLine> deletedWorkReportLinesSet);
|
||||
|
||||
/**
|
||||
* Recalulates the first and last timesheets dates for each
|
||||
* {@link OrderElement} in the {@link Set}.
|
||||
*/
|
||||
void recalculateTimesheetData(Set<OrderElement> orderElements);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,11 @@
|
|||
|
||||
package org.libreplan.business.orders.daos;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
|
@ -66,6 +70,9 @@ public class SumChargedEffortDAO extends
|
|||
@Autowired
|
||||
private IOrderDAO orderDAO;
|
||||
|
||||
@Autowired
|
||||
private IOrderElementDAO orderElementDAO;
|
||||
|
||||
private Map<OrderElement, SumChargedEffort> mapSumChargedEfforts;
|
||||
|
||||
@Override
|
||||
|
|
@ -114,6 +121,7 @@ public class SumChargedEffortDAO extends
|
|||
forceLoadParents(parent);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
previousEffort = previous.getFirst();
|
||||
|
|
@ -149,9 +157,6 @@ public class SumChargedEffortDAO extends
|
|||
private void addDirectChargedEffort(OrderElement orderElement,
|
||||
EffortDuration effort) {
|
||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||
if (sumChargedEffort == null) {
|
||||
sumChargedEffort = SumChargedEffort.create(orderElement);
|
||||
}
|
||||
|
||||
sumChargedEffort.addDirectChargedEffort(effort);
|
||||
save(sumChargedEffort);
|
||||
|
|
@ -163,9 +168,6 @@ public class SumChargedEffortDAO extends
|
|||
EffortDuration effort) {
|
||||
if (orderElement != null) {
|
||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||
if (sumChargedEffort == null) {
|
||||
sumChargedEffort = SumChargedEffort.create(orderElement);
|
||||
}
|
||||
|
||||
sumChargedEffort.addIndirectChargedEffort(effort);
|
||||
save(sumChargedEffort);
|
||||
|
|
@ -231,6 +233,9 @@ public class SumChargedEffortDAO extends
|
|||
.get(orderElement);
|
||||
if (sumChargedEffort == null) {
|
||||
sumChargedEffort = findByOrderElement(orderElement);
|
||||
if (sumChargedEffort == null) {
|
||||
sumChargedEffort = SumChargedEffort.create(orderElement);
|
||||
}
|
||||
mapSumChargedEfforts.put(orderElement, sumChargedEffort);
|
||||
}
|
||||
return sumChargedEffort;
|
||||
|
|
@ -251,6 +256,7 @@ public class SumChargedEffortDAO extends
|
|||
resetMapSumChargedEfforts();
|
||||
resetSumChargedEffort(order);
|
||||
calculateDirectChargedEffort(order);
|
||||
calculateTimesheetData(order);
|
||||
} catch (InstanceNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -258,9 +264,6 @@ public class SumChargedEffortDAO extends
|
|||
|
||||
private void resetSumChargedEffort(OrderElement orderElement) {
|
||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||
if (sumChargedEffort == null) {
|
||||
sumChargedEffort = SumChargedEffort.create(orderElement);
|
||||
}
|
||||
sumChargedEffort.reset();
|
||||
|
||||
for (OrderElement each : orderElement.getChildren()) {
|
||||
|
|
@ -281,4 +284,159 @@ public class SumChargedEffortDAO extends
|
|||
addDirectChargedEffort(orderElement, effort);
|
||||
}
|
||||
|
||||
private void calculateTimesheetData(OrderElement orderElement) {
|
||||
calculateTimesheetDatesAndChildren(orderElement);
|
||||
calculateFinishedTimesheetsAndChildren(orderElement);
|
||||
}
|
||||
|
||||
private Pair<Date, Date> calculateTimesheetDatesAndChildren(
|
||||
OrderElement orderElement) {
|
||||
Pair<Date, Date> minMax = workReportLineDAO
|
||||
.findMinAndMaxDatesByOrderElement(orderElement);
|
||||
|
||||
Set<Date> minDates = new HashSet<Date>();
|
||||
Set<Date> maxDates = new HashSet<Date>();
|
||||
|
||||
addIfNotNull(minDates, minMax.getFirst());
|
||||
addIfNotNull(maxDates, minMax.getSecond());
|
||||
|
||||
for (OrderElement child : orderElement.getChildren()) {
|
||||
Pair<Date, Date> minMaxChild = calculateTimesheetDatesAndChildren(child);
|
||||
addIfNotNull(minDates, minMaxChild.getFirst());
|
||||
addIfNotNull(maxDates, minMaxChild.getSecond());
|
||||
}
|
||||
|
||||
Pair<Date, Date> result = Pair.create(
|
||||
minDates.isEmpty() ? null : Collections.min(minDates),
|
||||
maxDates.isEmpty() ? null : Collections.max(maxDates));
|
||||
|
||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||
sumChargedEffort.setTimesheetDates(result.getFirst(),
|
||||
result.getSecond());
|
||||
save(sumChargedEffort);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addIfNotNull(Collection<Date> list, Date date) {
|
||||
if (date != null) {
|
||||
list.add(date);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateFinishedTimesheetsAndChildren(
|
||||
OrderElement orderElement) {
|
||||
calculateFinishedTimesheets(orderElement);
|
||||
|
||||
for (OrderElement child : orderElement.getChildren()) {
|
||||
calculateFinishedTimesheetsAndChildren(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateFinishedTimesheets(OrderElement orderElement) {
|
||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||
sumChargedEffort.setFinishedTimesheets(workReportLineDAO
|
||||
.isFinished(orderElement));
|
||||
save(sumChargedEffort);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Set<OrderElement> getOrderElementsToRecalculateTimsheetDates(
|
||||
Set<WorkReportLine> workReportLines,
|
||||
Set<WorkReportLine> deletedWorkReportLines) {
|
||||
Set<OrderElement> orderElements = new HashSet<OrderElement>();
|
||||
|
||||
if (workReportLines != null) {
|
||||
for (final WorkReportLine workReportLine : workReportLines) {
|
||||
if (!workReportLine.isNewObject()) {
|
||||
OrderElement previousOrderElement = transactionService
|
||||
.runOnAnotherTransaction(new IOnTransaction<OrderElement>() {
|
||||
@Override
|
||||
public OrderElement execute() {
|
||||
try {
|
||||
WorkReportLine line = workReportLineDAO
|
||||
.find(workReportLine.getId());
|
||||
|
||||
return line.getOrderElement();
|
||||
} catch (InstanceNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
orderElements.add(previousOrderElement);
|
||||
}
|
||||
orderElements.add(workReportLine.getOrderElement());
|
||||
}
|
||||
}
|
||||
|
||||
if (deletedWorkReportLines != null) {
|
||||
for (WorkReportLine workReportLine : deletedWorkReportLines) {
|
||||
if (workReportLine.isNewObject()) {
|
||||
// If the line hasn't been saved, we don't take it into
|
||||
// account
|
||||
continue;
|
||||
}
|
||||
|
||||
// Refresh data from database, because of changes not saved are
|
||||
// not
|
||||
// useful for the following operations
|
||||
sessionFactory.getCurrentSession().refresh(workReportLine);
|
||||
|
||||
orderElements.add(workReportLine.getOrderElement());
|
||||
}
|
||||
}
|
||||
|
||||
return orderElements;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void recalculateTimesheetData(Set<OrderElement> orderElements) {
|
||||
try {
|
||||
for (OrderElement orderElement : orderElements) {
|
||||
saveTimesheetDatesRecursively(orderElementDAO.find(orderElement
|
||||
.getId()));
|
||||
calculateFinishedTimesheets(orderElementDAO.find(orderElement
|
||||
.getId()));
|
||||
}
|
||||
} catch (InstanceNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveTimesheetDatesRecursively(OrderElement orderElement) {
|
||||
if (orderElement != null) {
|
||||
saveTimesheetDates(orderElement);
|
||||
saveTimesheetDatesRecursively(orderElement.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveTimesheetDates(OrderElement orderElement) {
|
||||
Pair<Date, Date> minMax = workReportLineDAO
|
||||
.findMinAndMaxDatesByOrderElement(orderElement);
|
||||
|
||||
Set<Date> minDates = new HashSet<Date>();
|
||||
Set<Date> maxDates = new HashSet<Date>();
|
||||
|
||||
addIfNotNull(minDates, minMax.getFirst());
|
||||
addIfNotNull(maxDates, minMax.getSecond());
|
||||
|
||||
for (OrderElement child : orderElement.getChildren()) {
|
||||
SumChargedEffort childSumChargedEffort = getByOrderElement(child);
|
||||
addIfNotNull(minDates,
|
||||
childSumChargedEffort.getFirstTimesheetDate());
|
||||
addIfNotNull(maxDates, childSumChargedEffort.getLastTimesheetDate());
|
||||
}
|
||||
|
||||
Pair<Date, Date> result = Pair.create(minDates.isEmpty() ? null
|
||||
: Collections.min(minDates), maxDates.isEmpty() ? null
|
||||
: Collections.max(maxDates));
|
||||
|
||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||
sumChargedEffort.setTimesheetDates(result.getFirst(),
|
||||
result.getSecond());
|
||||
save(sumChargedEffort);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1014,6 +1014,14 @@ public abstract class OrderElement extends IntegrationEntity implements
|
|||
return getCurrentSchedulingData().getTaskSource();
|
||||
}
|
||||
|
||||
public TaskElement getTaskElement() {
|
||||
TaskSource taskSource = getTaskSource();
|
||||
if (taskSource == null) {
|
||||
return null;
|
||||
}
|
||||
return taskSource.getTask();
|
||||
}
|
||||
|
||||
public Set<TaskElement> getTaskElements() {
|
||||
if (getTaskSource() == null) {
|
||||
return Collections.emptySet();
|
||||
|
|
@ -1586,6 +1594,44 @@ public abstract class OrderElement extends IntegrationEntity implements
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean hasTimesheetsReportingHours() {
|
||||
if (sumChargedEffort == null) {
|
||||
return false;
|
||||
}
|
||||
return sumChargedEffort.getFirstTimesheetDate() != null;
|
||||
}
|
||||
|
||||
public boolean isFinishedTimesheets() {
|
||||
if (sumChargedEffort == null) {
|
||||
return false;
|
||||
}
|
||||
return sumChargedEffort.isFinishedTimesheets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdatedFromTimesheets() {
|
||||
TaskElement taskElement = getTaskElement();
|
||||
if (taskElement == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return taskElement.isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
public Date getFirstTimesheetDate() {
|
||||
if (sumChargedEffort == null) {
|
||||
return null;
|
||||
}
|
||||
return sumChargedEffort.getFirstTimesheetDate();
|
||||
}
|
||||
|
||||
public Date getLastTimesheetDate() {
|
||||
if (sumChargedEffort == null) {
|
||||
return null;
|
||||
}
|
||||
return sumChargedEffort.getLastTimesheetDate();
|
||||
}
|
||||
|
||||
public void detachFromParent() {
|
||||
parent = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,6 +158,11 @@ public class OrderLineGroup extends OrderElement implements
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdatedFromTimesheets() {
|
||||
return getThis().isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static OrderLineGroup create() {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,12 @@
|
|||
|
||||
package org.libreplan.business.orders.entities;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.libreplan.business.common.BaseEntity;
|
||||
import org.libreplan.business.workingday.EffortDuration;
|
||||
import org.libreplan.business.workreports.entities.WorkReportLine;
|
||||
|
||||
/**
|
||||
* It represents the efforts charged to an {@link OrderElement}, avoiding the
|
||||
|
|
@ -39,6 +43,17 @@ public class SumChargedEffort extends BaseEntity {
|
|||
|
||||
private EffortDuration indirectChargedEffort = EffortDuration.zero();
|
||||
|
||||
private Date firstTimesheetDate;
|
||||
|
||||
private Date lastTimesheetDate;
|
||||
|
||||
/**
|
||||
* Finished according to timesheets. If <code>true</code> it means that
|
||||
* there's a {@link WorkReportLine} marking as finished this
|
||||
* {@link OrderElement}.
|
||||
*/
|
||||
private Boolean finishedTimesheets = false;
|
||||
|
||||
protected SumChargedEffort() {}
|
||||
|
||||
private SumChargedEffort(OrderElement orderElement) {
|
||||
|
|
@ -93,6 +108,38 @@ public class SumChargedEffort extends BaseEntity {
|
|||
public void reset() {
|
||||
directChargedEffort = EffortDuration.zero();
|
||||
indirectChargedEffort = EffortDuration.zero();
|
||||
firstTimesheetDate = null;
|
||||
lastTimesheetDate = null;
|
||||
}
|
||||
|
||||
public Date getFirstTimesheetDate() {
|
||||
return firstTimesheetDate;
|
||||
}
|
||||
|
||||
public void setFirstTimesheetDate(Date firstTimesheetDate) {
|
||||
this.firstTimesheetDate = firstTimesheetDate;
|
||||
}
|
||||
|
||||
public Date getLastTimesheetDate() {
|
||||
return lastTimesheetDate;
|
||||
}
|
||||
|
||||
public void setLastTimesheetDate(Date lastTimesheetDate) {
|
||||
this.lastTimesheetDate = lastTimesheetDate;
|
||||
}
|
||||
|
||||
public void setTimesheetDates(Date firstTimesheetDate,
|
||||
Date lastTimesheetDate) {
|
||||
setFirstTimesheetDate(firstTimesheetDate);
|
||||
setLastTimesheetDate(lastTimesheetDate);
|
||||
}
|
||||
|
||||
public Boolean isFinishedTimesheets() {
|
||||
return finishedTimesheets;
|
||||
}
|
||||
|
||||
public void setFinishedTimesheets(Boolean finishedTimesheets) {
|
||||
this.finishedTimesheets = BooleanUtils.isTrue(finishedTimesheets);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2253,4 +2253,17 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
|
|||
intendedResourcesPerDay = getNonConsolidatedResourcePerDay();
|
||||
}
|
||||
|
||||
public void removeDayAssignmentsBeyondDate(LocalDate date) {
|
||||
List<T> toRemove = new ArrayList<T>();
|
||||
|
||||
for (T t : getAssignments()) {
|
||||
if (t.getDay().compareTo(date) >= 0) {
|
||||
toRemove.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
setOnDayAssignmentRemoval(new DetachDayAssignmentOnRemoval());
|
||||
getDayAssignmentsState().removingAssignments(toRemove);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import java.util.Set;
|
|||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
|
@ -182,6 +183,8 @@ public abstract class TaskElement extends BaseEntity {
|
|||
|
||||
private Boolean simplifiedAssignedStatusCalculationEnabled = false;
|
||||
|
||||
private Boolean updatedFromTimesheets = false;
|
||||
|
||||
public void initializeDatesIfNeeded() {
|
||||
if (getIntraDayEndDate() == null || getIntraDayStartDate() == null) {
|
||||
initializeDates();
|
||||
|
|
@ -837,4 +840,12 @@ public abstract class TaskElement extends BaseEntity {
|
|||
return result;
|
||||
}
|
||||
|
||||
public Boolean isUpdatedFromTimesheets() {
|
||||
return updatedFromTimesheets;
|
||||
}
|
||||
|
||||
public void setUpdatedFromTimesheets(Boolean updatedFromTimesheets) {
|
||||
this.updatedFromTimesheets = BooleanUtils.isTrue(updatedFromTimesheets);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -620,4 +620,9 @@ public abstract class OrderElementTemplate extends BaseEntity implements
|
|||
|
||||
public abstract boolean isOrderTemplate();
|
||||
|
||||
@Override
|
||||
public boolean isUpdatedFromTimesheets() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ public class OrderLineGroupTemplate extends OrderElementTemplate implements
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdatedFromTimesheets() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static OrderLineGroupTemplate createNew() {
|
||||
|
|
|
|||
|
|
@ -60,4 +60,6 @@ public interface ITreeNode<T extends ITreeNode<T>> {
|
|||
*/
|
||||
boolean isEmptyLeaf();
|
||||
|
||||
boolean isUpdatedFromTimesheets();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.libreplan.business.common.daos.IIntegrationEntityDAO;
|
|||
import org.libreplan.business.orders.entities.OrderElement;
|
||||
import org.libreplan.business.reports.dtos.WorkReportLineDTO;
|
||||
import org.libreplan.business.resources.entities.Resource;
|
||||
import org.libreplan.business.util.Pair;
|
||||
import org.libreplan.business.workreports.entities.WorkReport;
|
||||
import org.libreplan.business.workreports.entities.WorkReportLine;
|
||||
|
||||
|
|
@ -65,6 +66,14 @@ public interface IWorkReportLineDAO extends
|
|||
List<WorkReportLine> findByResourceFilteredByDateNotInWorkReport(
|
||||
Resource resource, Date start, Date end, WorkReport workReport);
|
||||
|
||||
Pair<Date, Date> findMinAndMaxDatesByOrderElement(
|
||||
OrderElement orderElement);
|
||||
|
||||
List<WorkReportLine> findByOrderElementNotInWorkReportAnotherTransaction(
|
||||
OrderElement orderElement, WorkReport workReport);
|
||||
|
||||
Boolean isFinished(OrderElement orderElement);
|
||||
|
||||
List<WorkReportLine> findByOrderElementAndWorkReports(
|
||||
OrderElement orderElement, List<WorkReport> workReports);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,11 +34,13 @@ import org.libreplan.business.common.daos.IntegrationEntityDAO;
|
|||
import org.libreplan.business.orders.entities.OrderElement;
|
||||
import org.libreplan.business.reports.dtos.WorkReportLineDTO;
|
||||
import org.libreplan.business.resources.entities.Resource;
|
||||
import org.libreplan.business.util.Pair;
|
||||
import org.libreplan.business.workreports.entities.WorkReport;
|
||||
import org.libreplan.business.workreports.entities.WorkReportLine;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
|
|
@ -146,6 +148,57 @@ public class WorkReportLineDAO extends IntegrationEntityDAO<WorkReportLine>
|
|||
return criteria.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Date, Date> findMinAndMaxDatesByOrderElement(
|
||||
OrderElement orderElement) {
|
||||
|
||||
String strQuery = "SELECT MIN(date) AS min, MAX(date) AS max "
|
||||
+ "FROM WorkReportLine " + "WHERE orderElement = :orderElement";
|
||||
|
||||
Query query = getSession().createQuery(strQuery);
|
||||
query.setParameter("orderElement", orderElement);
|
||||
|
||||
Object[] result = (Object[]) query.uniqueResult();
|
||||
|
||||
Date min = null;
|
||||
Date max = null;
|
||||
if (result != null) {
|
||||
min = (Date) result[0];
|
||||
max = (Date) result[1];
|
||||
}
|
||||
return Pair.create(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
|
||||
public List<WorkReportLine> findByOrderElementNotInWorkReportAnotherTransaction(
|
||||
OrderElement orderElement, WorkReport workReport) {
|
||||
return findByOrderElementNotInWorkReport(orderElement, workReport);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<WorkReportLine> findByOrderElementNotInWorkReport(
|
||||
OrderElement orderElement, WorkReport workReport) {
|
||||
Criteria criteria = getSession().createCriteria(WorkReportLine.class);
|
||||
|
||||
criteria.add(Restrictions.eq("orderElement", orderElement));
|
||||
if (!workReport.isNewObject()) {
|
||||
criteria.add(Restrictions.ne("workReport", workReport));
|
||||
}
|
||||
|
||||
return (List<WorkReportLine>) criteria.list();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isFinished(OrderElement orderElement) {
|
||||
Criteria criteria = getSession().createCriteria(WorkReportLine.class);
|
||||
|
||||
criteria.add(Restrictions.eq("orderElement", orderElement));
|
||||
criteria.add(Restrictions.eq("finished", true));
|
||||
|
||||
return criteria.uniqueResult() != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public List<WorkReportLine> findByOrderElementAndWorkReports(
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.hibernate.validator.Valid;
|
|||
import org.joda.time.LocalDate;
|
||||
import org.libreplan.business.common.IntegrationEntity;
|
||||
import org.libreplan.business.common.Registry;
|
||||
import org.libreplan.business.common.Util;
|
||||
import org.libreplan.business.common.entities.EntitySequence;
|
||||
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
|
||||
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
||||
|
|
@ -538,4 +539,20 @@ public class WorkReport extends IntegrationEntity implements
|
|||
return false;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "the same task is marked as finished by more than one timesheet line")
|
||||
public boolean checkConstraintSameOrderElementFinishedBySeveralWorkReportLines() {
|
||||
Set<OrderElement> finishedOrderElements = new HashSet<OrderElement>();
|
||||
|
||||
for (WorkReportLine line : workReportLines) {
|
||||
if (line.isFinished()) {
|
||||
if (Util.contains(finishedOrderElements, line.getOrderElement())) {
|
||||
return false;
|
||||
}
|
||||
finishedOrderElements.add(line.getOrderElement());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ package org.libreplan.business.workreports.entities;
|
|||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
|
@ -78,6 +79,8 @@ public class WorkReportLine extends IntegrationEntity implements Comparable,
|
|||
|
||||
private TypeOfWorkHours typeOfWorkHours;
|
||||
|
||||
private Boolean finished = false;
|
||||
|
||||
/**
|
||||
* Constructor for hibernate. Do not use!
|
||||
*/
|
||||
|
|
@ -552,4 +555,31 @@ public class WorkReportLine extends IntegrationEntity implements Comparable,
|
|||
: false;
|
||||
}
|
||||
|
||||
@NotNull(message = "finished not specified")
|
||||
public Boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public void setFinished(Boolean finished) {
|
||||
this.finished = finished;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "there is a timesheet line in another work report marking as finished the same task")
|
||||
public boolean checkConstraintOrderElementFinishedInAnotherWorkReport() {
|
||||
if (!finished) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<WorkReportLine> lines = Registry.getWorkReportLineDAO()
|
||||
.findByOrderElementNotInWorkReportAnotherTransaction(
|
||||
orderElement, workReport);
|
||||
for (WorkReportLine line : lines) {
|
||||
if (line.isFinished()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,88 @@
|
|||
</update>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="add-columns-first-and-last-timesheet_date-to-sum_charged_effort"
|
||||
author="mrego">
|
||||
<comment>
|
||||
Add columns first_timesheet_date and last_timesheet_date to
|
||||
sum_charged_effort table
|
||||
</comment>
|
||||
<addColumn tableName="sum_charged_effort">
|
||||
<column name="first_timesheet_date" type="DATETIME" />
|
||||
</addColumn>
|
||||
<addColumn tableName="sum_charged_effort">
|
||||
<column name="last_timesheet_date" type="DATETIME" />
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="add-new-column-read_only-to-advance_type" author="mrego">
|
||||
<comment>
|
||||
Add new column read_only with default value FALSE to advance_type
|
||||
table.
|
||||
</comment>
|
||||
<addColumn tableName="advance_type">
|
||||
<column name="read_only" type="BOOLEAN" />
|
||||
</addColumn>
|
||||
<addDefaultValue tableName="advance_type" columnName="read_only"
|
||||
defaultValueBoolean="FALSE" />
|
||||
<addNotNullConstraint tableName="advance_type"
|
||||
columnName="read_only"
|
||||
defaultNullValue="FALSE"
|
||||
columnDataType="BOOLEAN" />
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="add-new-column-finished-to-work_report_line" author="mrego">
|
||||
<comment>
|
||||
Add new column finished with default value FALSE to
|
||||
work_report_line table.
|
||||
</comment>
|
||||
<addColumn tableName="work_report_line">
|
||||
<column name="finished" type="BOOLEAN" />
|
||||
</addColumn>
|
||||
<addDefaultValue tableName="work_report_line" columnName="finished"
|
||||
defaultValueBoolean="FALSE" />
|
||||
<addNotNullConstraint tableName="work_report_line"
|
||||
columnName="finished"
|
||||
defaultNullValue="FALSE"
|
||||
columnDataType="BOOLEAN" />
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="add-new-column-finished_timesheets-to-sum_charged_effort"
|
||||
author="mrego">
|
||||
<comment>
|
||||
Add new column finished_timesheets with default value FALSE to
|
||||
sum_charged_effort table.
|
||||
</comment>
|
||||
<addColumn tableName="sum_charged_effort">
|
||||
<column name="finished_timesheets" type="BOOLEAN" />
|
||||
</addColumn>
|
||||
<addDefaultValue tableName="sum_charged_effort"
|
||||
columnName="finished_timesheets"
|
||||
defaultValueBoolean="FALSE" />
|
||||
<addNotNullConstraint tableName="sum_charged_effort"
|
||||
columnName="finished_timesheets"
|
||||
defaultNullValue="FALSE"
|
||||
columnDataType="BOOLEAN" />
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="add-new-column-updated_from_timesheets-to-task_element"
|
||||
author="mrego">
|
||||
<comment>
|
||||
Add new column updated_from_timesheets with default value FALSE to
|
||||
task_element table.
|
||||
</comment>
|
||||
<addColumn tableName="task_element">
|
||||
<column name="updated_from_timesheets" type="BOOLEAN" />
|
||||
</addColumn>
|
||||
<addDefaultValue tableName="task_element"
|
||||
columnName="updated_from_timesheets"
|
||||
defaultValueBoolean="FALSE" />
|
||||
<addNotNullConstraint tableName="task_element"
|
||||
columnName="updated_from_timesheets"
|
||||
defaultNullValue="FALSE"
|
||||
columnDataType="BOOLEAN" />
|
||||
</changeSet>
|
||||
|
||||
<changeSet id="update-status-values-in-order_table" author="mrego">
|
||||
<comment>Updating status values in order_table</comment>
|
||||
<update tableName="order_table">
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
<property name="percentage" access="field"/>
|
||||
<property name="qualityForm" access="field"
|
||||
column="quality_form" />
|
||||
<property name="readOnly" column="read_only" />
|
||||
|
||||
</class>
|
||||
|
||||
|
|
|
|||
|
|
@ -272,6 +272,14 @@
|
|||
<property name="indirectChargedEffort" access="field"
|
||||
column="indirect_charged_effort"
|
||||
type="org.libreplan.business.workingday.hibernate.EffortDurationType" />
|
||||
|
||||
<property name="firstTimesheetDate" access="field"
|
||||
column="first_timesheet_date" />
|
||||
<property name="lastTimesheetDate" access="field"
|
||||
column="last_timesheet_date" />
|
||||
<property name="finishedTimesheets"
|
||||
column="finished_timesheets" />
|
||||
|
||||
</class>
|
||||
|
||||
<class name="SumExpenses" table="sum_expenses">
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@
|
|||
<many-to-one name="calendar" column="base_calendar_id" cascade="none"
|
||||
class="org.libreplan.business.calendars.entities.BaseCalendar"/>
|
||||
|
||||
<property name="updatedFromTimesheets"
|
||||
column="updated_from_timesheets" />
|
||||
|
||||
<joined-subclass name="Task" table="task">
|
||||
<key column="task_element_id"></key>
|
||||
<property name="calculatedValue" column="calculated_value">
|
||||
|
|
|
|||
|
|
@ -162,6 +162,8 @@
|
|||
</composite-element>
|
||||
</set>
|
||||
|
||||
<property name="finished" />
|
||||
|
||||
</class>
|
||||
|
||||
<class name="WorkReportLabelTypeAssigment" table="work_report_label_type_assigment">
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import org.zkoss.zkplus.databind.AnnotateDataBinder;
|
|||
import org.zkoss.zkplus.databind.DataBinder;
|
||||
import org.zkoss.zul.Bandbox;
|
||||
import org.zkoss.zul.Button;
|
||||
import org.zkoss.zul.Checkbox;
|
||||
import org.zkoss.zul.Combobox;
|
||||
import org.zkoss.zul.Comboitem;
|
||||
import org.zkoss.zul.Datebox;
|
||||
|
|
@ -67,7 +68,6 @@ import org.zkoss.zul.Radio;
|
|||
import org.zkoss.zul.Row;
|
||||
import org.zkoss.zul.Textbox;
|
||||
import org.zkoss.zul.Timebox;
|
||||
import org.zkoss.zul.api.Checkbox;
|
||||
import org.zkoss.zul.api.Column;
|
||||
|
||||
/**
|
||||
|
|
@ -446,7 +446,7 @@ public class Util {
|
|||
* The {@link Setter} interface that will implement a set method.
|
||||
* @return The {@link Checkbox} bound
|
||||
*/
|
||||
public static <C extends Checkbox> C bind(final C checkBox,
|
||||
public static Checkbox bind(final Checkbox checkBox,
|
||||
final Getter<Boolean> getter, final Setter<Boolean> setter) {
|
||||
checkBox.setChecked(getter.get());
|
||||
checkBox.addEventListener(Events.ON_CHECK, new EventListener() {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import org.libreplan.business.workingday.EffortDuration;
|
|||
import org.libreplan.web.common.Util;
|
||||
import org.libreplan.web.common.Util.Getter;
|
||||
import org.libreplan.web.common.Util.Setter;
|
||||
import org.zkoss.zul.api.Checkbox;
|
||||
import org.zkoss.zul.Checkbox;
|
||||
|
||||
/**
|
||||
* It configures some ZK components to work together and edit a Capacity object
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ public interface IManageOrderElementAdvancesModel {
|
|||
|
||||
public boolean isQualityForm(AdvanceAssignment advance);
|
||||
|
||||
public boolean isReadOnly(AdvanceAssignment advance);
|
||||
|
||||
public boolean lessThanPreviousMeasurements();
|
||||
|
||||
public boolean hasConsolidatedAdvances(AdvanceAssignment advance);
|
||||
|
|
@ -134,4 +136,5 @@ public interface IManageOrderElementAdvancesModel {
|
|||
|
||||
Boolean isAlreadyReportedProgressWith(LocalDate date);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -366,7 +366,10 @@ public class ManageOrderElementAdvancesController extends
|
|||
if (advance.getAdvanceType() != null) {
|
||||
isQualityForm = manageOrderElementAdvancesModel
|
||||
.isQualityForm(advance);
|
||||
if (manageOrderElementAdvancesModel
|
||||
readOnlyAdvance = manageOrderElementAdvancesModel
|
||||
.isReadOnly(advance);
|
||||
if (!readOnlyAdvance
|
||||
&& manageOrderElementAdvancesModel
|
||||
.isSubcontratedAdvanceTypeAndSubcontratedTask(advance)) {
|
||||
readOnlyAdvance = true;
|
||||
}
|
||||
|
|
@ -375,12 +378,12 @@ public class ManageOrderElementAdvancesController extends
|
|||
if ((advance instanceof DirectAdvanceAssignment)
|
||||
&& ((DirectAdvanceAssignment) advance)
|
||||
.getAdvanceMeasurements().isEmpty()
|
||||
&& !isQualityForm) {
|
||||
&& !isQualityForm && !readOnlyAdvance) {
|
||||
appendComboboxAdvanceType(listItem);
|
||||
} else {
|
||||
appendLabelAdvanceType(listItem);
|
||||
}
|
||||
appendDecimalBoxMaxValue(listItem, isQualityForm);
|
||||
appendDecimalBoxMaxValue(listItem, isQualityForm || readOnlyAdvance);
|
||||
appendDecimalBoxValue(listItem);
|
||||
appendLabelPercentage(listItem);
|
||||
appendDateBoxDate(listItem);
|
||||
|
|
@ -401,7 +404,8 @@ public class ManageOrderElementAdvancesController extends
|
|||
for(AdvanceType advanceType : listAdvanceType){
|
||||
if (!advanceType.getUnitName().equals(
|
||||
PredefinedAdvancedTypes.CHILDREN.getTypeName())
|
||||
&& !advanceType.isQualityForm()) {
|
||||
&& !advanceType.isQualityForm()
|
||||
&& !advanceType.isReadOnly()) {
|
||||
Comboitem comboItem = new Comboitem();
|
||||
comboItem.setValue(advanceType);
|
||||
comboItem.setLabel(advanceType.getUnitName());
|
||||
|
|
@ -461,7 +465,7 @@ public class ManageOrderElementAdvancesController extends
|
|||
}
|
||||
|
||||
private void appendDecimalBoxMaxValue(final Listitem listItem,
|
||||
boolean isQualityForm) {
|
||||
boolean isQualityFormOrReadOnly) {
|
||||
final AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem
|
||||
.getValue();
|
||||
final Decimalbox maxValue = new Decimalbox();
|
||||
|
|
@ -469,7 +473,7 @@ public class ManageOrderElementAdvancesController extends
|
|||
|
||||
final DirectAdvanceAssignment directAdvanceAssignment;
|
||||
if ((advanceAssignment instanceof IndirectAdvanceAssignment)
|
||||
|| isQualityForm
|
||||
|| isQualityFormOrReadOnly
|
||||
|| (advanceAssignment.getAdvanceType() != null && advanceAssignment
|
||||
.getAdvanceType().getPercentage())
|
||||
|| manageOrderElementAdvancesModel
|
||||
|
|
@ -708,6 +712,11 @@ public class ManageOrderElementAdvancesController extends
|
|||
addMeasurementButton.setDisabled(true);
|
||||
addMeasurementButton
|
||||
.setTooltiptext(_("Progress that are reported by quality forms can not be modified"));
|
||||
} else if ((advance.getAdvanceType() != null)
|
||||
&& (advance.getAdvanceType().isReadOnly())) {
|
||||
addMeasurementButton.setDisabled(true);
|
||||
addMeasurementButton
|
||||
.setTooltiptext(_("This progress type cannot be modified"));
|
||||
} else if (advance instanceof IndirectAdvanceAssignment) {
|
||||
addMeasurementButton.setDisabled(true);
|
||||
addMeasurementButton
|
||||
|
|
@ -739,6 +748,11 @@ public class ManageOrderElementAdvancesController extends
|
|||
removeButton.setDisabled(true);
|
||||
removeButton
|
||||
.setTooltiptext(_("Progress that are reported by quality forms cannot be modified"));
|
||||
} else if ((advance.getAdvanceType() != null)
|
||||
&& (advance.getAdvanceType().isReadOnly())) {
|
||||
removeButton.setDisabled(true);
|
||||
removeButton
|
||||
.setTooltiptext(_("This progress type cannot be modified"));
|
||||
} else if (advance instanceof IndirectAdvanceAssignment) {
|
||||
removeButton.setDisabled(true);
|
||||
removeButton
|
||||
|
|
@ -1219,6 +1233,11 @@ public class ManageOrderElementAdvancesController extends
|
|||
removeButton.setDisabled(true);
|
||||
removeButton
|
||||
.setTooltiptext(_("Progress measurements that are reported by quality forms cannot be removed"));
|
||||
} else if ((advance.getAdvanceType() != null)
|
||||
&& (advance.getAdvanceType().isReadOnly())) {
|
||||
removeButton.setDisabled(true);
|
||||
removeButton
|
||||
.setTooltiptext(_("This progress type cannot cannot be removed"));
|
||||
} else if (advance.isFake()) {
|
||||
removeButton.setDisabled(true);
|
||||
removeButton
|
||||
|
|
|
|||
|
|
@ -161,7 +161,8 @@ public class ManageOrderElementAdvancesModel implements
|
|||
for (AdvanceAssignment advance : listAdvanceAssignmentsCopy) {
|
||||
if ((!listAdvanceAssignments.contains(advance))
|
||||
&& (advance instanceof DirectAdvanceAssignment)
|
||||
&& (!advance.getAdvanceType().isQualityForm())) {
|
||||
&& (!advance.getAdvanceType().isQualityForm())
|
||||
&& (!advance.getAdvanceType().isReadOnly())) {
|
||||
listAdvanceAssignments.add(advance);
|
||||
}
|
||||
}
|
||||
|
|
@ -337,7 +338,8 @@ public class ManageOrderElementAdvancesModel implements
|
|||
for (AdvanceType advanceType : this.listAdvanceTypes) {
|
||||
if ((advanceType.getUnitName()
|
||||
.equals(PredefinedAdvancedTypes.CHILDREN.getTypeName()))
|
||||
|| (advanceType.isQualityForm())) {
|
||||
|| (advanceType.isQualityForm())
|
||||
|| advanceType.isReadOnly()) {
|
||||
continue;
|
||||
}
|
||||
if (existsAdvanceTypeAlreadyInThisOrderElement(advanceType)) {
|
||||
|
|
@ -412,6 +414,9 @@ public class ManageOrderElementAdvancesModel implements
|
|||
if (advanceType.isQualityForm()) {
|
||||
return true;
|
||||
}
|
||||
if (advanceType.isReadOnly()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(isIndirectAdvanceAssignment){
|
||||
|
|
@ -764,6 +769,14 @@ public class ManageOrderElementAdvancesModel implements
|
|||
return advanceType.isQualityForm();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public boolean isReadOnly(AdvanceAssignment advance) {
|
||||
AdvanceType advanceType = advance.getAdvanceType();
|
||||
advanceTypeDAO.reattach(advanceType);
|
||||
return advanceType.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public boolean findIndirectConsolidation(
|
||||
|
|
|
|||
|
|
@ -1203,7 +1203,8 @@ _(
|
|||
@Override
|
||||
public boolean isFixed() {
|
||||
return taskElement.isLimitingAndHasDayAssignments()
|
||||
|| taskElement.hasConsolidations();
|
||||
|| taskElement.hasConsolidations()
|
||||
|| taskElement.isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1222,6 +1223,29 @@ _(
|
|||
return taskElement.isRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdatedFromTimesheets() {
|
||||
return taskElement.isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getFirstTimesheetDate() {
|
||||
OrderElement orderElement = taskElement.getOrderElement();
|
||||
if (orderElement != null) {
|
||||
return orderElement.getFirstTimesheetDate();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getLastTimesheetDate() {
|
||||
OrderElement orderElement = taskElement.getOrderElement();
|
||||
if (orderElement != null) {
|
||||
return orderElement.getLastTimesheetDate();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* This file is part of LibrePlan
|
||||
*
|
||||
* Copyright (C) 2012 Igalia, S.L.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.libreplan.web.planner.adaptplanning;
|
||||
|
||||
import static org.libreplan.web.I18nHelper._;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes;
|
||||
import org.libreplan.business.advance.entities.AdvanceMeasurement;
|
||||
import org.libreplan.business.advance.entities.AdvanceType;
|
||||
import org.libreplan.business.advance.entities.DirectAdvanceAssignment;
|
||||
import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException;
|
||||
import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException;
|
||||
import org.libreplan.business.orders.entities.OrderElement;
|
||||
import org.libreplan.business.planner.entities.PositionConstraintType;
|
||||
import org.libreplan.business.planner.entities.ResourceAllocation;
|
||||
import org.libreplan.business.planner.entities.Task;
|
||||
import org.libreplan.business.planner.entities.TaskElement;
|
||||
import org.libreplan.business.workingday.IntraDayDate;
|
||||
import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.zkoss.ganttz.Planner;
|
||||
import org.zkoss.ganttz.TaskComponent;
|
||||
import org.zkoss.ganttz.extensions.IContext;
|
||||
import org.zkoss.ganttz.util.LongOperationFeedback;
|
||||
import org.zkoss.ganttz.util.LongOperationFeedback.ILongOperation;
|
||||
|
||||
/**
|
||||
* @author Manuel Rego Casasnovas <rego@igalia.com>
|
||||
*/
|
||||
@Component
|
||||
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
|
||||
public class AdaptPlanningCommand implements IAdaptPlanningCommand {
|
||||
|
||||
private PlanningState planningState;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return _("Adapt planning acording to timesheets");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAction(final IContext<TaskElement> context) {
|
||||
LongOperationFeedback.execute(context.getRelativeTo(),
|
||||
new ILongOperation() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return _("Adapting planning according to timesheets");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAction() throws Exception {
|
||||
adaptPlanning(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void adaptPlanning(IContext<TaskElement> context) {
|
||||
List<TaskElement> taskElements = planningState.getRootTask()
|
||||
.getAllChildren();
|
||||
for (TaskElement taskElement : taskElements) {
|
||||
// Only adapt task leafs
|
||||
if (!taskElement.isLeaf()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OrderElement orderElement = taskElement.getOrderElement();
|
||||
// Reset status to allow move the task if needed while adapting the
|
||||
// planning
|
||||
taskElement.setUpdatedFromTimesheets(false);
|
||||
|
||||
if (orderElement.hasTimesheetsReportingHours()) {
|
||||
setStartDateAndConstraint(taskElement,
|
||||
orderElement.getFirstTimesheetDate());
|
||||
Date lastTimesheetDate = orderElement.getLastTimesheetDate();
|
||||
setEndDateIfNeeded(taskElement, lastTimesheetDate);
|
||||
|
||||
if (orderElement.isFinishedTimesheets()) {
|
||||
setEndDate(taskElement, lastTimesheetDate);
|
||||
addTimesheetsProgress(orderElement, lastTimesheetDate);
|
||||
removeResourceAllocationsBeyondEndDate(taskElement);
|
||||
} else {
|
||||
removeTimesheetsProgressIfAny(orderElement);
|
||||
}
|
||||
|
||||
taskElement.setUpdatedFromTimesheets(true);
|
||||
}
|
||||
}
|
||||
for (TaskElement taskElement : taskElements) {
|
||||
if (taskElement.isUpdatedFromTimesheets()) {
|
||||
updateTask(context, taskElement);
|
||||
}
|
||||
}
|
||||
|
||||
((Planner) context.getRelativeTo()).invalidate();
|
||||
context.reloadCharts();
|
||||
}
|
||||
|
||||
private void removeResourceAllocationsBeyondEndDate(TaskElement taskElement) {
|
||||
LocalDate endDate = taskElement.getEndAsLocalDate();
|
||||
|
||||
for (ResourceAllocation<?> resourceAllocation : taskElement
|
||||
.getAllResourceAllocations()) {
|
||||
resourceAllocation.removeDayAssignmentsBeyondDate(endDate);
|
||||
}
|
||||
}
|
||||
|
||||
private void setStartDateAndConstraint(TaskElement taskElement,
|
||||
Date startDate) {
|
||||
taskElement.setStartDate(startDate);
|
||||
setStartInFixedDateConstarint(taskElement, startDate);
|
||||
}
|
||||
|
||||
private void setStartInFixedDateConstarint(TaskElement taskElement,
|
||||
Date startDate) {
|
||||
if (taskElement.isTask()) {
|
||||
Task task = (Task) taskElement;
|
||||
task.getPositionConstraint()
|
||||
.update(PositionConstraintType.START_IN_FIXED_DATE,
|
||||
IntraDayDate.startOfDay(LocalDate
|
||||
.fromDateFields(startDate)));
|
||||
}
|
||||
}
|
||||
|
||||
private void setEndDateIfNeeded(TaskElement taskElement, Date endDate) {
|
||||
if (taskElement.getEndDate().compareTo(endDate) <= 0) {
|
||||
setEndDate(taskElement, endDate);
|
||||
}
|
||||
}
|
||||
|
||||
private void setEndDate(TaskElement taskElement, Date endDate) {
|
||||
taskElement.setEndDate(LocalDate.fromDateFields(endDate).plusDays(1)
|
||||
.toDateTimeAtStartOfDay().toDate());
|
||||
}
|
||||
|
||||
private void addTimesheetsProgress(OrderElement orderElement,
|
||||
Date progressDate) {
|
||||
AdvanceType timesheetsAdvanceType = getTimesheetsAdvanceType();
|
||||
|
||||
DirectAdvanceAssignment timesheetsAdvanceAssignment = orderElement
|
||||
.getDirectAdvanceAssignmentByType(timesheetsAdvanceType);
|
||||
|
||||
if (timesheetsAdvanceAssignment == null) {
|
||||
timesheetsAdvanceAssignment = DirectAdvanceAssignment.create(false,
|
||||
timesheetsAdvanceType.getDefaultMaxValue());
|
||||
timesheetsAdvanceAssignment.setAdvanceType(timesheetsAdvanceType);
|
||||
try {
|
||||
orderElement.addAdvanceAssignment(timesheetsAdvanceAssignment);
|
||||
} catch (DuplicateValueTrueReportGlobalAdvanceException e) {
|
||||
// This shouldn't happen as the new advanceAssignment is not
|
||||
// marked as spread yet
|
||||
throw new RuntimeException(e);
|
||||
} catch (DuplicateAdvanceAssignmentForOrderElementException e) {
|
||||
// If the same type already exists in other element we don't do
|
||||
// anything
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DirectAdvanceAssignment spreadAdvanceAssignment = orderElement
|
||||
.getReportGlobalAdvanceAssignment();
|
||||
if (spreadAdvanceAssignment != null) {
|
||||
spreadAdvanceAssignment.setReportGlobalAdvance(false);
|
||||
}
|
||||
|
||||
timesheetsAdvanceAssignment.setReportGlobalAdvance(true);
|
||||
timesheetsAdvanceAssignment.resetAdvanceMeasurements(AdvanceMeasurement
|
||||
.create(LocalDate.fromDateFields(progressDate),
|
||||
timesheetsAdvanceType.getDefaultMaxValue()));
|
||||
}
|
||||
|
||||
private AdvanceType getTimesheetsAdvanceType() {
|
||||
return PredefinedAdvancedTypes.TIMESHEETS.getType();
|
||||
}
|
||||
|
||||
private void removeTimesheetsProgressIfAny(OrderElement orderElement) {
|
||||
DirectAdvanceAssignment timesheetsAdvanceAssignment = orderElement
|
||||
.getDirectAdvanceAssignmentByType(getTimesheetsAdvanceType());
|
||||
if (timesheetsAdvanceAssignment != null) {
|
||||
orderElement.removeAdvanceAssignment(timesheetsAdvanceAssignment);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTask(IContext<TaskElement> context,
|
||||
TaskElement taskElement) {
|
||||
taskElement.updateAdvancePercentageFromOrderElement();
|
||||
|
||||
Planner planner = (Planner) context.getRelativeTo();
|
||||
TaskComponent taskComponent = planner.getTaskComponentRelatedTo(context
|
||||
.getMapper().findAssociatedBean(taskElement));
|
||||
if (taskComponent != null) {
|
||||
taskComponent.updateTooltipText();
|
||||
taskComponent.updateProperties();
|
||||
taskComponent.invalidate();
|
||||
}
|
||||
|
||||
context.recalculatePosition(taskElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImage() {
|
||||
return "/common/img/ico_adapt_planning.png";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(PlanningState planningState) {
|
||||
this.planningState = planningState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlannerCommand() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* This file is part of LibrePlan
|
||||
*
|
||||
* Copyright (C) 2012 Igalia, S.L.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.libreplan.web.planner.adaptplanning;
|
||||
|
||||
import org.libreplan.business.planner.entities.TaskElement;
|
||||
import org.libreplan.web.planner.order.PlanningStateCreator.PlanningState;
|
||||
import org.zkoss.ganttz.extensions.ICommand;
|
||||
|
||||
/**
|
||||
* Command to adapt planning of a project taking into account information from
|
||||
* the timesheets.
|
||||
*
|
||||
* @author Manuel Rego Casasnovas <rego@igalia.com>
|
||||
*/
|
||||
public interface IAdaptPlanningCommand extends ICommand<TaskElement> {
|
||||
|
||||
public void setState(PlanningState planningState);
|
||||
|
||||
}
|
||||
|
|
@ -1129,14 +1129,14 @@ class Row {
|
|||
}
|
||||
|
||||
private EffortDurationBox buildSumAllEffort() {
|
||||
EffortDurationBox box = (isGroupingRow() || isLimiting) ? EffortDurationBox
|
||||
EffortDurationBox box = isEffortDurationBoxDisabled() ? EffortDurationBox
|
||||
.notEditable() : new EffortDurationBox();
|
||||
box.setWidth("40px");
|
||||
return box;
|
||||
}
|
||||
|
||||
private void addListenerIfNeeded(Component allEffortComponent) {
|
||||
if (isGroupingRow() || isLimiting) {
|
||||
if (isEffortDurationBoxDisabled()) {
|
||||
return;
|
||||
}
|
||||
final EffortDurationBox effortDurationBox = (EffortDurationBox) allEffortComponent;
|
||||
|
|
@ -1167,6 +1167,10 @@ class Row {
|
|||
});
|
||||
}
|
||||
|
||||
private boolean isEffortDurationBoxDisabled() {
|
||||
return isGroupingRow() || isLimiting || task.isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
private void reloadEffortsSameRowForDetailItems() {
|
||||
for (Entry<DetailItem, Component> entry : componentsByDetailItem
|
||||
.entrySet()) {
|
||||
|
|
@ -1181,7 +1185,7 @@ class Row {
|
|||
EffortDuration allEffort = aggregate.getTotalEffort();
|
||||
allEffortInput.setValue(allEffort);
|
||||
Clients.closeErrorBox(allEffortInput);
|
||||
if (isLimiting) {
|
||||
if (isEffortDurationBoxDisabled()) {
|
||||
allEffortInput.setDisabled(true);
|
||||
}
|
||||
if (restriction.isInvalidTotalEffort(allEffort)) {
|
||||
|
|
@ -1215,6 +1219,11 @@ class Row {
|
|||
hboxAssigmentFunctionsCombo.appendChild(assignmentFunctionsCombo);
|
||||
assignmentFunctionsConfigureButton = getAssignmentFunctionsConfigureButton(assignmentFunctionsCombo);
|
||||
hboxAssigmentFunctionsCombo.appendChild(assignmentFunctionsConfigureButton);
|
||||
|
||||
// Disable if task is updated from timesheets
|
||||
assignmentFunctionsCombo.setDisabled(task.isUpdatedFromTimesheets());
|
||||
assignmentFunctionsConfigureButton.setDisabled(task
|
||||
.isUpdatedFromTimesheets());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1621,7 +1630,8 @@ class Row {
|
|||
|
||||
private boolean cannotBeEdited(DetailItem item) {
|
||||
return isGroupingRow() || doesNotIntersectWithTask(item)
|
||||
|| isBeforeLatestConsolidation(item);
|
||||
|| isBeforeLatestConsolidation(item)
|
||||
|| task.isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
private EffortDurationBox disableIfNeeded(DetailItem item,
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@ public class AllocationConfiguration extends HtmlMacroComponent {
|
|||
.getCalculatedValue())) {
|
||||
radio.setChecked(true);
|
||||
}
|
||||
radio.setDisabled(formBinder.isAnyManual());
|
||||
radio.setDisabled(formBinder.isAnyManual()
|
||||
|| formBinder.isTaskUpdatedFromTimesheets());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -423,4 +423,9 @@ public class AllocationRowsHandler {
|
|||
private ArrayList<AllocationRow> copyOfCurrentRowsToAvoidConcurrentModification() {
|
||||
return new ArrayList<AllocationRow>(currentRows);
|
||||
}
|
||||
|
||||
public boolean isTaskUpdatedFromTimesheets() {
|
||||
return task.isUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -217,7 +217,7 @@ public class FormBinder {
|
|||
boolean disabled = rows.isEmpty()
|
||||
|| (CalculatedValue.NUMBER_OF_HOURS == c)
|
||||
|| (c == CalculatedValue.RESOURCES_PER_DAY && !recommendedAllocation)
|
||||
|| isAnyManual();
|
||||
|| isAnyManual() || isTaskUpdatedFromTimesheets();
|
||||
this.effortInput.setDisabled(disabled);
|
||||
}
|
||||
|
||||
|
|
@ -245,13 +245,14 @@ public class FormBinder {
|
|||
allResourcesPerDayVisibilityRule();
|
||||
applyDisabledRulesOnRows();
|
||||
this.btnRecommendedAllocation.setDisabled(recommendedAllocation
|
||||
|| isAnyManual());
|
||||
|| isAnyManual() || isTaskUpdatedFromTimesheets());
|
||||
}
|
||||
|
||||
private void applyDisabledRulesOnRows() {
|
||||
for (AllocationRow each : rows) {
|
||||
each.applyDisabledRules(getCalculatedValue(),
|
||||
recommendedAllocation, isAnyManual());
|
||||
recommendedAllocation, isAnyManual()
|
||||
|| isTaskUpdatedFromTimesheets());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +362,7 @@ public class FormBinder {
|
|||
void applyDisabledRules() {
|
||||
this.taskWorkableDays.setDisabled(allocationRowsHandler
|
||||
.getCalculatedValue() == CalculatedValue.END_DATE
|
||||
|| isAnyManual());
|
||||
|| isAnyManual() || isTaskUpdatedFromTimesheets());
|
||||
}
|
||||
|
||||
private void initializeDateAndDurationFieldsFromTaskOriginalValues() {
|
||||
|
|
@ -465,7 +466,8 @@ public class FormBinder {
|
|||
CalculatedValue c = allocationRowsHandler.getCalculatedValue();
|
||||
this.allResourcesPerDay.setDisabled(rows.isEmpty()
|
||||
|| c == CalculatedValue.RESOURCES_PER_DAY
|
||||
|| !recommendedAllocation || isAnyManual());
|
||||
|| !recommendedAllocation || isAnyManual()
|
||||
|| isTaskUpdatedFromTimesheets());
|
||||
this.allResourcesPerDay
|
||||
.setConstraint(constraintForAllResourcesPerDay());
|
||||
}
|
||||
|
|
@ -522,6 +524,10 @@ public class FormBinder {
|
|||
* exit the edition form
|
||||
*/
|
||||
public boolean accept() {
|
||||
if (isTaskUpdatedFromTimesheets()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Flagged<AllocationResult, Warnings> result = resourceAllocationModel
|
||||
.accept();
|
||||
|
||||
|
|
@ -707,7 +713,8 @@ public class FormBinder {
|
|||
|
||||
public void setRecommendedAllocation(Button recommendedAllocation) {
|
||||
this.btnRecommendedAllocation = recommendedAllocation;
|
||||
this.btnRecommendedAllocation.setDisabled(isAnyManual());
|
||||
this.btnRecommendedAllocation.setDisabled(isAnyManual()
|
||||
|| isTaskUpdatedFromTimesheets());
|
||||
Util.ensureUniqueListener(recommendedAllocation, Events.ON_CLICK,
|
||||
new EventListener() {
|
||||
@Override
|
||||
|
|
@ -939,4 +946,8 @@ public class FormBinder {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isTaskUpdatedFromTimesheets() {
|
||||
return allocationRowsHandler.isTaskUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -602,7 +602,7 @@ public class ResourceAllocationController extends GenericForwardComposer {
|
|||
|
||||
// On click delete button
|
||||
Button deleteButton = appendDeleteButton(row);
|
||||
deleteButton.setDisabled(isAnyManual());
|
||||
deleteButton.setDisabled(isAnyManualOrTaskUpdatedFromTimesheets());
|
||||
formBinder.setDeleteButtonFor(data, deleteButton);
|
||||
deleteButton.addEventListener("onClick", new EventListener() {
|
||||
|
||||
|
|
@ -671,8 +671,12 @@ public class ResourceAllocationController extends GenericForwardComposer {
|
|||
return formBinder != null && formBinder.isAnyNotFlat();
|
||||
}
|
||||
|
||||
public boolean isAnyManual() {
|
||||
return formBinder != null && formBinder.isAnyManual();
|
||||
public boolean isAnyManualOrTaskUpdatedFromTimesheets() {
|
||||
if (formBinder == null) {
|
||||
return false;
|
||||
}
|
||||
return formBinder.isAnyManual()
|
||||
|| formBinder.isTaskUpdatedFromTimesheets();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ import org.libreplan.business.users.entities.UserRole;
|
|||
import org.libreplan.business.workingday.EffortDuration;
|
||||
import org.libreplan.web.calendars.BaseCalendarModel;
|
||||
import org.libreplan.web.common.ViewSwitcher;
|
||||
import org.libreplan.web.planner.adaptplanning.IAdaptPlanningCommand;
|
||||
import org.libreplan.web.planner.advances.AdvanceAssignmentPlanningController;
|
||||
import org.libreplan.web.planner.advances.IAdvanceAssignmentPlanningCommand;
|
||||
import org.libreplan.web.planner.allocation.IAdvancedAllocationCommand;
|
||||
|
|
@ -222,6 +223,9 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
|||
@Autowired
|
||||
private IReassignCommand reassignCommand;
|
||||
|
||||
@Autowired
|
||||
private IAdaptPlanningCommand adaptPlanningCommand;
|
||||
|
||||
@Autowired
|
||||
private IResourceAllocationCommand resourceAllocationCommand;
|
||||
|
||||
|
|
@ -332,6 +336,7 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
|||
|
||||
configuration.addGlobalCommand(buildReassigningCommand());
|
||||
configuration.addGlobalCommand(buildCancelEditionCommand());
|
||||
configuration.addGlobalCommand(buildAdaptPlanningCommand());
|
||||
|
||||
NullSeparatorCommandOnTask<TaskElement> separator = new NullSeparatorCommandOnTask<TaskElement>();
|
||||
|
||||
|
|
@ -1055,6 +1060,11 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
|||
return reassignCommand;
|
||||
}
|
||||
|
||||
private ICommand<TaskElement> buildAdaptPlanningCommand() {
|
||||
adaptPlanningCommand.setState(planningState);
|
||||
return adaptPlanningCommand;
|
||||
}
|
||||
|
||||
private ICommand<TaskElement> buildCancelEditionCommand() {
|
||||
return new ICommand<TaskElement>() {
|
||||
|
||||
|
|
@ -1097,6 +1107,11 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlannerCommand() {
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1038,6 +1038,11 @@ public class SaveCommandBuilder {
|
|||
return disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlannerCommand() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class LabelCreatorForInvalidValues implements
|
||||
|
|
|
|||
|
|
@ -375,4 +375,9 @@ public class ReassignCommand implements IReassignCommand {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlannerCommand() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ public class ReassignConfiguration {
|
|||
}
|
||||
|
||||
private boolean isChoosenForReassignation(Task each) {
|
||||
if (each.isUpdatedFromTimesheets()) {
|
||||
return false;
|
||||
}
|
||||
return type == Type.ALL || isAfterDate(each);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,7 +138,8 @@ public class TaskPropertiesController extends GenericForwardComposer {
|
|||
|
||||
disabledConstraintsAndAllocations = currentTaskElement
|
||||
.isSubcontractedAndWasAlreadySent()
|
||||
|| currentTaskElement.isLimitingAndHasDayAssignments();
|
||||
|| currentTaskElement.isLimitingAndHasDayAssignments()
|
||||
|| currentTaskElement.isUpdatedFromTimesheets();
|
||||
if (!disabledConstraintsAndAllocations && (currentTaskElement.isTask())) {
|
||||
disabledConstraintsAndAllocations = ((Task) currentTaskElement)
|
||||
.isManualAnyAllocation();
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||
getModel().indent(element);
|
||||
filterByPredicateIfAny();
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
public TreeModel getTreeModel() {
|
||||
|
|
@ -140,6 +141,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||
getModel().unindent(element);
|
||||
filterByPredicateIfAny();
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
public void up() {
|
||||
|
|
@ -153,6 +155,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||
getModel().up(element);
|
||||
filterByPredicateIfAny();
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
public void down() {
|
||||
|
|
@ -165,6 +168,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||
getModel().down(element);
|
||||
filterByPredicateIfAny();
|
||||
updateControlButtons();
|
||||
}
|
||||
|
||||
public T getSelectedNode() {
|
||||
|
|
@ -184,8 +188,15 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
|
||||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||
|
||||
Treerow from = (Treerow) dragged;
|
||||
T fromNode = type.cast(((Treeitem) from.getParent()).getValue());
|
||||
T fromNode = null;
|
||||
if (dragged instanceof Treerow) {
|
||||
Treerow from = (Treerow) dragged;
|
||||
fromNode = type.cast(((Treeitem) from.getParent()).getValue());
|
||||
}
|
||||
if (dragged instanceof Treeitem) {
|
||||
Treeitem from = (Treeitem) dragged;
|
||||
fromNode = type.cast(from.getValue());
|
||||
}
|
||||
if (dropedIn instanceof Tree) {
|
||||
getModel().moveToRoot(fromNode);
|
||||
}
|
||||
|
|
@ -354,6 +365,15 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
|
||||
private List<Column> columns;
|
||||
|
||||
private Button btnNewFromTemplate;
|
||||
|
||||
private Button downButton;
|
||||
|
||||
private Button upButton;
|
||||
|
||||
private Button leftButton;
|
||||
|
||||
private Button rightButton;
|
||||
|
||||
protected TreeViewStateSnapshot getSnapshotOfOpenedNodes() {
|
||||
return viewStateSnapshot;
|
||||
|
|
@ -361,6 +381,13 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
|
||||
private void resetControlButtons() {
|
||||
btnNew.setDisabled(isNewButtonDisabled());
|
||||
btnNewFromTemplate.setDisabled(isNewButtonDisabled());
|
||||
|
||||
boolean disabled = readOnly || isPredicateApplied();
|
||||
downButton.setDisabled(disabled);
|
||||
upButton.setDisabled(disabled);
|
||||
leftButton.setDisabled(disabled);
|
||||
rightButton.setDisabled(disabled);
|
||||
}
|
||||
|
||||
protected abstract boolean isNewButtonDisabled();
|
||||
|
|
@ -692,8 +719,8 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
public void render(final Treeitem item, Object data) {
|
||||
item.setValue(data);
|
||||
applySnapshot(item);
|
||||
currentTreeRow = getTreeRowWithoutChildrenFor(item);
|
||||
final T currentElement = type.cast(data);
|
||||
currentTreeRow = getTreeRowWithoutChildrenFor(item, currentElement);
|
||||
createCells(item, currentElement);
|
||||
onDropMoveFromDraggedToTarget();
|
||||
}
|
||||
|
|
@ -722,10 +749,17 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
}
|
||||
}
|
||||
|
||||
private Treerow getTreeRowWithoutChildrenFor(final Treeitem item) {
|
||||
private Treerow getTreeRowWithoutChildrenFor(final Treeitem item,
|
||||
T element) {
|
||||
Treerow result = createOrRetrieveFor(item);
|
||||
// Attach treecells to treerow
|
||||
result.setDroppable("true");
|
||||
if (element.isUpdatedFromTimesheets()) {
|
||||
result.setDraggable("false");
|
||||
result.setDroppable("false");
|
||||
} else {
|
||||
result.setDraggable("true");
|
||||
result.setDroppable("true");
|
||||
}
|
||||
result.getChildren().clear();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -766,7 +800,8 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
final SchedulingState schedulingState = getSchedulingStateFrom(currentElement);
|
||||
SchedulingStateToggler schedulingStateToggler = new SchedulingStateToggler(
|
||||
schedulingState);
|
||||
schedulingStateToggler.setReadOnly(readOnly);
|
||||
schedulingStateToggler.setReadOnly(readOnly
|
||||
|| currentElement.isUpdatedFromTimesheets());
|
||||
final Treecell cell = addCell(
|
||||
getDecorationFromState(getSchedulingStateFrom(currentElement)),
|
||||
schedulingStateToggler);
|
||||
|
|
@ -796,7 +831,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
}
|
||||
});
|
||||
schedulingStateToggler.afterCompose();
|
||||
cell.setDraggable("true");
|
||||
|
||||
}
|
||||
|
||||
protected abstract SchedulingState getSchedulingStateFrom(
|
||||
|
|
@ -1105,92 +1140,6 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
protected abstract void onDoubleClickForSchedulingStateCell(
|
||||
T currentElement);
|
||||
|
||||
protected Button createDownButton(final Treeitem item,
|
||||
final T currentElement) {
|
||||
EventListener downButtonListener = new EventListener() {
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
down(currentElement);
|
||||
}
|
||||
};
|
||||
Button result;
|
||||
if (isPredicateApplied() || isLastItem(currentElement) || readOnly) {
|
||||
result = createButton("/common/img/ico_bajar_out.png", "",
|
||||
"/common/img/ico_bajar_out.png", "icono",
|
||||
downButtonListener);
|
||||
result.setDisabled(true);
|
||||
} else {
|
||||
result = createButton("/common/img/ico_bajar1.png",
|
||||
_("Move down"), "/common/img/ico_bajar.png", "icono",
|
||||
downButtonListener);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Button createUpButton(final Treeitem item, final T element) {
|
||||
EventListener upButtonListener = new EventListener() {
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
up(element);
|
||||
}
|
||||
};
|
||||
Button result;
|
||||
if (isPredicateApplied() || isFirstItem(element) || readOnly) {
|
||||
result = createButton("/common/img/ico_subir_out.png", "",
|
||||
"/common/img/ico_subir_out.png", "icono",
|
||||
upButtonListener);
|
||||
result.setDisabled(true);
|
||||
} else {
|
||||
result = createButton("/common/img/ico_subir1.png",
|
||||
_("Move up"), "/common/img/ico_subir.png", "icono",
|
||||
upButtonListener);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Button createUnindentButton(final Treeitem item,
|
||||
final T element) {
|
||||
EventListener unindentListener = new EventListener() {
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
unindent(element);
|
||||
}
|
||||
};
|
||||
final Button result;
|
||||
if (isPredicateApplied() || isFirstLevelElement(item) || readOnly) {
|
||||
result = createButton("/common/img/ico_izq_out.png", "",
|
||||
"/common/img/ico_izq_out.png", "icono",
|
||||
unindentListener);
|
||||
result.setDisabled(true);
|
||||
} else {
|
||||
result = createButton("/common/img/ico_izq1.png",
|
||||
_("Unindent"), "/common/img/ico_izq.png", "icono",
|
||||
unindentListener);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Button createIndentButton(final Treeitem item, final T element) {
|
||||
EventListener indentListener = new EventListener() {
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
indent(element);
|
||||
}
|
||||
};
|
||||
final Button result;
|
||||
if (isPredicateApplied() || isFirstItem(element) || readOnly) {
|
||||
result = createButton("/common/img/ico_derecha_out.png", "",
|
||||
"/common/img/ico_derecha_out.png", "icono",
|
||||
indentListener);
|
||||
result.setDisabled(true);
|
||||
} else {
|
||||
result = createButton("/common/img/ico_derecha1.png",
|
||||
_("Indent"), "/common/img/ico_derecha.png", "icono",
|
||||
indentListener);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Button createRemoveButton(final T currentElement) {
|
||||
EventListener removeListener = new EventListener() {
|
||||
@Override
|
||||
|
|
@ -1239,6 +1188,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
public void doTry() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setColumns(List<Column> columns) {
|
||||
|
|
@ -1286,17 +1236,40 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
|||
/**
|
||||
* Disable control buttons (new, up, down, indent, unindent, delete)
|
||||
*/
|
||||
public void updateControlButtons(Event event) {
|
||||
updateControlButtons((Tree) event.getTarget());
|
||||
}
|
||||
|
||||
public void updateControlButtons(Tree tree) {
|
||||
final Treeitem item = tree.getSelectedItem();
|
||||
if (item == null) {
|
||||
public void updateControlButtons() {
|
||||
T element = getSelectedNode();
|
||||
if (element == null) {
|
||||
resetControlButtons();
|
||||
return;
|
||||
}
|
||||
btnNew.setDisabled(false);
|
||||
Treeitem item = tree.getSelectedItem();
|
||||
|
||||
btnNew.setDisabled(isNewButtonDisabled()
|
||||
|| element.isUpdatedFromTimesheets());
|
||||
btnNewFromTemplate.setDisabled(isNewButtonDisabled()
|
||||
|| element.isUpdatedFromTimesheets());
|
||||
|
||||
boolean disabled = readOnly || isPredicateApplied();
|
||||
downButton.setDisabled(disabled || isLastItem(element));
|
||||
upButton.setDisabled(disabled || isFirstItem(element));
|
||||
|
||||
disabled |= element.isUpdatedFromTimesheets();
|
||||
leftButton.setDisabled(disabled
|
||||
|| isFirstLevelElement(item)
|
||||
|| element.getParent().isUpdatedFromTimesheets());
|
||||
|
||||
boolean previousSiblingIsUpdatedFromTimesheets = false;
|
||||
try {
|
||||
Treeitem previousItem = (Treeitem) item.getParent()
|
||||
.getChildren().get(item.getIndex() - 1);
|
||||
T previousSibling = type.cast(previousItem.getValue());
|
||||
previousSiblingIsUpdatedFromTimesheets = previousSibling
|
||||
.isUpdatedFromTimesheets();
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// Do nothing
|
||||
}
|
||||
rightButton.setDisabled(disabled || isFirstItem(element)
|
||||
|| previousSiblingIsUpdatedFromTimesheets);
|
||||
}
|
||||
|
||||
protected abstract boolean isPredicateApplied();
|
||||
|
|
|
|||
|
|
@ -431,17 +431,39 @@ public class PersonalTimesheetModel implements IPersonalTimesheetModel {
|
|||
// saved as it will not be possible to find it later with
|
||||
// WorkReportDAO.getPersonalTimesheetWorkReport() method.
|
||||
} else {
|
||||
Set<WorkReportLine> deletedWorkReportLinesSet = removeWorkReportLinesWithEffortZero();
|
||||
|
||||
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||
.getOrderElementsToRecalculateTimsheetDates(
|
||||
workReport.getWorkReportLines(),
|
||||
deletedWorkReportLinesSet);
|
||||
sumChargedEffortDAO
|
||||
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(deletedWorkReportLinesSet);
|
||||
sumChargedEffortDAO
|
||||
.updateRelatedSumChargedEffortWithWorkReportLineSet(workReport
|
||||
.getWorkReportLines());
|
||||
workReport.generateWorkReportLineCodes(entitySequenceDAO
|
||||
.getNumberOfDigitsCode(EntityNameEnum.WORK_REPORT));
|
||||
workReportDAO.save(workReport);
|
||||
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||
}
|
||||
|
||||
resetModifiedFields();
|
||||
}
|
||||
|
||||
private Set<WorkReportLine> removeWorkReportLinesWithEffortZero() {
|
||||
Set<WorkReportLine> toRemove = new HashSet<WorkReportLine>();
|
||||
for (WorkReportLine line : workReport.getWorkReportLines()) {
|
||||
if (line.getEffort().isZero()) {
|
||||
toRemove.add(line);
|
||||
}
|
||||
}
|
||||
for (WorkReportLine line : toRemove) {
|
||||
workReport.removeWorkReportLine(line);
|
||||
}
|
||||
return toRemove;
|
||||
}
|
||||
|
||||
private void resetModifiedFields() {
|
||||
modified = false;
|
||||
modifiedMap = new HashMap<OrderElement, Set<LocalDate>>();
|
||||
|
|
|
|||
|
|
@ -245,4 +245,10 @@ public interface IWorkReportModel extends IIntegrationEntityModel {
|
|||
|
||||
WorkReportLine getFirstWorkReportLine();
|
||||
|
||||
/**
|
||||
* Checks if an {@link OrderElement} is finished or not in any
|
||||
* {@link WorkReportLine} of this report or other report.
|
||||
*/
|
||||
boolean isFinished(OrderElement orderElement);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.validator.InvalidValue;
|
||||
|
|
@ -54,6 +55,8 @@ import org.libreplan.web.common.Level;
|
|||
import org.libreplan.web.common.MessagesForUser;
|
||||
import org.libreplan.web.common.OnlyOneVisible;
|
||||
import org.libreplan.web.common.Util;
|
||||
import org.libreplan.web.common.Util.Getter;
|
||||
import org.libreplan.web.common.Util.Setter;
|
||||
import org.libreplan.web.common.components.Autocomplete;
|
||||
import org.libreplan.web.common.components.NewDataSortableColumn;
|
||||
import org.libreplan.web.common.components.NewDataSortableGrid;
|
||||
|
|
@ -73,6 +76,7 @@ import org.zkoss.zk.ui.event.Events;
|
|||
import org.zkoss.zk.ui.event.SelectEvent;
|
||||
import org.zkoss.zk.ui.util.GenericForwardComposer;
|
||||
import org.zkoss.zul.Button;
|
||||
import org.zkoss.zul.Checkbox;
|
||||
import org.zkoss.zul.Column;
|
||||
import org.zkoss.zul.Columns;
|
||||
import org.zkoss.zul.Comboitem;
|
||||
|
|
@ -273,7 +277,9 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
for (InvalidValue invalidValue : e.getInvalidValues()) {
|
||||
Object value = invalidValue.getBean();
|
||||
if (value instanceof WorkReport) {
|
||||
validateWorkReport();
|
||||
if (validateWorkReport()) {
|
||||
messagesForUser.showInvalidValues(e);
|
||||
}
|
||||
}
|
||||
if (value instanceof WorkReportLine) {
|
||||
WorkReportLine workReportLine = (WorkReportLine) invalidValue.getBean();
|
||||
|
|
@ -328,6 +334,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
_("cannot be empty"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +459,16 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!workReportLine.checkConstraintOrderElementFinishedInAnotherWorkReport()) {
|
||||
Checkbox checkboxFinished = getFinished(row);
|
||||
if (checkboxFinished != null) {
|
||||
String message = _("task is already marked as finished in another timesheet");
|
||||
showInvalidMessage(checkboxFinished, message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -466,7 +483,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
*/
|
||||
private Timebox getTimeboxFinish(Row row) {
|
||||
try {
|
||||
int position = row.getChildren().size() - 5;
|
||||
int position = row.getChildren().size() - 6;
|
||||
return (Timebox) row.getChildren().get(position);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
|
|
@ -480,7 +497,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
*/
|
||||
private Timebox getTimeboxStart(Row row) {
|
||||
try {
|
||||
int position = row.getChildren().size() - 6;
|
||||
int position = row.getChildren().size() - 7;
|
||||
return (Timebox) row.getChildren().get(position);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
|
|
@ -494,13 +511,28 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
*/
|
||||
private Listbox getTypeOfHours(Row row) {
|
||||
try {
|
||||
int position = row.getChildren().size() - 3;
|
||||
int position = row.getChildren().size() - 4;
|
||||
return (Listbox) row.getChildren().get(position);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locates {@link Checkbox} finished in {@link Row}
|
||||
* @param row
|
||||
* @return
|
||||
*/
|
||||
private Checkbox getFinished(Row row) {
|
||||
try {
|
||||
int position = row.getChildren().size() - 3;
|
||||
return (Checkbox) row.getChildren().get(position);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates {@link Texbox} code in {@link Row}
|
||||
* @param row
|
||||
|
|
@ -523,7 +555,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
*/
|
||||
private Textbox getEffort(Row row) {
|
||||
try {
|
||||
int position = row.getChildren().size() - 4;
|
||||
int position = row.getChildren().size() - 5;
|
||||
return (Textbox) row.getChildren().get(position);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
|
|
@ -815,7 +847,12 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
columnHoursType.setLabel(_("Hours type"));
|
||||
columnHoursType.setSclass("hours-type-column");
|
||||
columns.appendChild(columnHoursType);
|
||||
NewDataSortableColumn columnFinsihed = new NewDataSortableColumn();
|
||||
columnFinsihed.setLabel(_("Done"));
|
||||
columnFinsihed.setSclass("finished-column");
|
||||
columnFinsihed.setTooltiptext(_("Task finished"));
|
||||
NewDataSortableColumn columnCode = new NewDataSortableColumn();
|
||||
columns.appendChild(columnFinsihed);
|
||||
columnCode.setLabel(_("Code"));
|
||||
columnCode.setSclass("code-column");
|
||||
columns.appendChild(columnCode);
|
||||
|
|
@ -1197,6 +1234,29 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
row.appendChild(code);
|
||||
}
|
||||
|
||||
private void appendFinished(final Row row) {
|
||||
final WorkReportLine line = (WorkReportLine) row.getValue();
|
||||
|
||||
Checkbox finished = Util.bind(new Checkbox(), new Getter<Boolean>() {
|
||||
@Override
|
||||
public Boolean get() {
|
||||
return line.isFinished();
|
||||
}
|
||||
}, new Setter<Boolean>() {
|
||||
@Override
|
||||
public void set(Boolean value) {
|
||||
line.setFinished(BooleanUtils.isTrue(value));
|
||||
}
|
||||
});
|
||||
|
||||
if (!line.isFinished()
|
||||
&& workReportModel.isFinished(line.getOrderElement())) {
|
||||
finished.setDisabled(true);
|
||||
}
|
||||
|
||||
row.appendChild(finished);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a delete {@link Button} to {@link Row}
|
||||
*
|
||||
|
|
@ -1306,6 +1366,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
|||
|
||||
appendEffortDuration(row);
|
||||
appendHoursType(row);
|
||||
appendFinished(row);
|
||||
appendCode(row);
|
||||
appendDeleteButton(row);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import java.util.Set;
|
|||
import org.apache.commons.lang.Validate;
|
||||
import org.hibernate.Hibernate;
|
||||
import org.libreplan.business.common.IntegrationEntity;
|
||||
import org.libreplan.business.common.Util;
|
||||
import org.libreplan.business.common.daos.IConfigurationDAO;
|
||||
import org.libreplan.business.common.entities.EntityNameEnum;
|
||||
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
||||
|
|
@ -51,6 +52,7 @@ import org.libreplan.business.resources.daos.IWorkerDAO;
|
|||
import org.libreplan.business.resources.entities.Resource;
|
||||
import org.libreplan.business.resources.entities.Worker;
|
||||
import org.libreplan.business.workreports.daos.IWorkReportDAO;
|
||||
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
|
||||
import org.libreplan.business.workreports.daos.IWorkReportTypeDAO;
|
||||
import org.libreplan.business.workreports.entities.WorkReport;
|
||||
import org.libreplan.business.workreports.entities.WorkReportLabelTypeAssigment;
|
||||
|
|
@ -86,6 +88,9 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
|||
@Autowired
|
||||
private IWorkReportDAO workReportDAO;
|
||||
|
||||
@Autowired
|
||||
private IWorkReportLineDAO workReportLineDAO;
|
||||
|
||||
@Autowired
|
||||
private IOrderElementDAO orderElementDAO;
|
||||
|
||||
|
|
@ -274,12 +279,17 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
|||
@Override
|
||||
@Transactional
|
||||
public void confirmSave() throws ValidationException {
|
||||
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||
.getOrderElementsToRecalculateTimsheetDates(
|
||||
workReport.getWorkReportLines(),
|
||||
deletedWorkReportLinesSet);
|
||||
sumChargedEffortDAO.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(deletedWorkReportLinesSet);
|
||||
sumChargedEffortDAO
|
||||
.updateRelatedSumChargedEffortWithWorkReportLineSet(workReport
|
||||
.getWorkReportLines());
|
||||
|
||||
workReportDAO.save(workReport);
|
||||
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -412,10 +422,14 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
|||
//before deleting the report, update OrderElement.SumChargedHours
|
||||
try {
|
||||
workReportDAO.reattach(workReport);
|
||||
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||
.getOrderElementsToRecalculateTimsheetDates(null,
|
||||
workReport.getWorkReportLines());
|
||||
sumChargedEffortDAO
|
||||
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport
|
||||
.getWorkReportLines());
|
||||
workReportDAO.remove(workReport.getId());
|
||||
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||
} catch (InstanceNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -644,4 +658,25 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
|||
return workReport.getWorkReportLines().iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public boolean isFinished(OrderElement orderElement) {
|
||||
for (WorkReportLine line : workReport.getWorkReportLines()) {
|
||||
if (line.isFinished()
|
||||
&& Util.equals(line.getOrderElement(), orderElement)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
List<WorkReportLine> lines = workReportLineDAO
|
||||
.findByOrderElementNotInWorkReportAnotherTransaction(
|
||||
orderElement, workReport);
|
||||
for (WorkReportLine line : lines) {
|
||||
if (line.isFinished()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ public abstract class GenericRESTService<E extends IntegrationEntity,
|
|||
entity.validate();
|
||||
beforeSaving(entity);
|
||||
entityDAO.saveWithoutValidating(entity);
|
||||
afterSaving(entity);
|
||||
|
||||
return null;
|
||||
|
||||
|
|
@ -155,12 +156,23 @@ public abstract class GenericRESTService<E extends IntegrationEntity,
|
|||
}
|
||||
|
||||
/**
|
||||
* it adds operations that must be done before saving.
|
||||
* It allows to add operations that must be done before saving.
|
||||
*
|
||||
* Default implementation is empty.
|
||||
*/
|
||||
protected void beforeSaving(E entity) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* It allows to add operations that must be done after saving.
|
||||
*
|
||||
* Default implementation is empty.
|
||||
*/
|
||||
protected void afterSaving(E entity) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* It creates an entity from a DTO.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@
|
|||
package org.libreplan.ws.workreports.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
|
|
@ -37,8 +39,8 @@ import javax.ws.rs.core.Response.Status;
|
|||
import org.libreplan.business.common.daos.IIntegrationEntityDAO;
|
||||
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
||||
import org.libreplan.business.common.exceptions.ValidationException;
|
||||
import org.libreplan.business.orders.daos.IOrderElementDAO;
|
||||
import org.libreplan.business.orders.daos.ISumChargedEffortDAO;
|
||||
import org.libreplan.business.orders.entities.OrderElement;
|
||||
import org.libreplan.business.workreports.daos.IWorkReportDAO;
|
||||
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
|
||||
import org.libreplan.business.workreports.entities.WorkReport;
|
||||
|
|
@ -64,15 +66,14 @@ public class WorkReportServiceREST extends
|
|||
GenericRESTService<WorkReport, WorkReportDTO> implements
|
||||
IWorkReportService {
|
||||
|
||||
private Set<OrderElement> orderElements;
|
||||
|
||||
@Autowired
|
||||
private IWorkReportDAO workReportDAO;
|
||||
|
||||
@Autowired
|
||||
private IWorkReportLineDAO workReportLineDAO;
|
||||
|
||||
@Autowired
|
||||
private IOrderElementDAO orderElementDAO;
|
||||
|
||||
@Autowired
|
||||
private ISumChargedEffortDAO sumChargedEffortDAO;
|
||||
|
||||
|
|
@ -120,11 +121,19 @@ public class WorkReportServiceREST extends
|
|||
|
||||
@Override
|
||||
protected void beforeSaving(WorkReport entity) {
|
||||
orderElements = sumChargedEffortDAO
|
||||
.getOrderElementsToRecalculateTimsheetDates(
|
||||
entity.getWorkReportLines(), null);
|
||||
sumChargedEffortDAO
|
||||
.updateRelatedSumChargedEffortWithWorkReportLineSet(entity
|
||||
.getWorkReportLines());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterSaving(WorkReport entity) {
|
||||
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||
}
|
||||
|
||||
@Override
|
||||
@GET
|
||||
@Path("/{code}/")
|
||||
|
|
@ -140,10 +149,14 @@ public class WorkReportServiceREST extends
|
|||
public Response removeWorkReport(@PathParam("code") String code) {
|
||||
try {
|
||||
WorkReport workReport = workReportDAO.findByCode(code);
|
||||
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||
.getOrderElementsToRecalculateTimsheetDates(null,
|
||||
workReport.getWorkReportLines());
|
||||
sumChargedEffortDAO
|
||||
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport
|
||||
.getWorkReportLines());
|
||||
workReportDAO.remove(workReport.getId());
|
||||
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||
return Response.ok().build();
|
||||
} catch (InstanceNotFoundException e) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
|
@ -157,10 +170,14 @@ public class WorkReportServiceREST extends
|
|||
public Response removeWorkReportLine(@PathParam("code") String code) {
|
||||
try {
|
||||
WorkReportLine workReportLine = workReportLineDAO.findByCode(code);
|
||||
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||
.getOrderElementsToRecalculateTimsheetDates(null,
|
||||
Collections.singleton(workReportLine));
|
||||
sumChargedEffortDAO
|
||||
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(new HashSet<WorkReportLine>(
|
||||
Arrays.asList(workReportLine)));
|
||||
workReportLineDAO.remove(workReportLine.getId());
|
||||
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||
return Response.ok().build();
|
||||
} catch (InstanceNotFoundException e) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 830 B |
|
|
@ -82,7 +82,7 @@
|
|||
<vbox width="100%">
|
||||
<tree id="tree" hflex="1" multiple="false" droppable="true"
|
||||
onDrop="treeController.move(self, event.dragged)"
|
||||
onSelect="treeController.updateControlButtons(event)"
|
||||
onSelect="treeController.updateControlButtons()"
|
||||
mold="paging" pageSize="25"
|
||||
sclass="orderTree"
|
||||
zclass="z-dottree">
|
||||
|
|
|
|||
|
|
@ -274,6 +274,24 @@ div.box.limiting-unassigned {
|
|||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.timesheet-date-mark {
|
||||
color: #F21CFF;
|
||||
font-size: 8px;
|
||||
font-weight: bold;
|
||||
/* By default marks are hidden */
|
||||
display: none;
|
||||
}
|
||||
|
||||
.first-timesheet-date {
|
||||
position: absolute;
|
||||
margin-top: -19px;
|
||||
}
|
||||
|
||||
.last-timesheet-date {
|
||||
position: absolute;
|
||||
margin-top: -19px;
|
||||
}
|
||||
|
||||
.completion2 {
|
||||
width: 0%;
|
||||
height: 6px;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
<hbox align="bottom" sclass="add-resources-or-criteria">
|
||||
<newAllocationSelectorCombo id="newAllocationSelectorCombo" behaviour="NON_LIMITING"/>
|
||||
<button label="${i18n:_('Add')}" onClick="allocationController.onSelectWorkers(newAllocationSelectorCombo)"
|
||||
disabled="@{allocationController.anyManual}" />
|
||||
disabled="@{allocationController.isAnyManualOrTaskUpdatedFromTimesheets}" />
|
||||
<button id="advancedSearchButton" label="${i18n:_('Advanced search')}" onClick="allocationController.goToAdvancedSearch()" />
|
||||
<checkbox id="extendedViewCheckbox" label="${i18n:_('Extended view')}"
|
||||
onCheck="allocationController.onCheckExtendedView()"/>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
|
||||
.listWorkReportLines .operations-column {
|
||||
width: 40px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.listWorkReportLines .resource-column {
|
||||
|
|
@ -25,6 +25,10 @@
|
|||
width: 150px;
|
||||
}
|
||||
|
||||
.listWorkReportLines .finished-column {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.listWorkReportLines .order-code-column {
|
||||
width: 180px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue