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) {
|
for (CommandContextualized<?> c : contextualizedGlobalCommands) {
|
||||||
// Comparison through icon as name is internationalized
|
// Comparison through icon as name is internationalized
|
||||||
if (c.getCommand().getImage()
|
if (c.getCommand().isPlannerCommand()) {
|
||||||
.equals("/common/img/ico_reassign.png")) {
|
// FIXME Avoid hard-coding the number of planner commands
|
||||||
if (plannerToolbar.getChildren().isEmpty()) {
|
// At this moment we have 2 planner commands: reassign and adapt
|
||||||
|
// planning
|
||||||
|
if (plannerToolbar.getChildren().size() < 2) {
|
||||||
plannerToolbar.appendChild(c.toButton());
|
plannerToolbar.appendChild(c.toButton());
|
||||||
}
|
}
|
||||||
} else {
|
} 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.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.commons.lang.Validate;
|
import org.apache.commons.lang.Validate;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.Days;
|
||||||
|
import org.joda.time.Duration;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.zkoss.ganttz.adapters.IDisabilityConfiguration;
|
import org.zkoss.ganttz.adapters.IDisabilityConfiguration;
|
||||||
import org.zkoss.ganttz.data.GanttDate;
|
import org.zkoss.ganttz.data.GanttDate;
|
||||||
|
|
@ -517,8 +520,34 @@ public class TaskComponent extends Div implements AfterCompose {
|
||||||
this.task.getHoursAdvanceEndDate()) + "px";
|
this.task.getHoursAdvanceEndDate()) + "px";
|
||||||
response(null, new AuInvoke(this, "resizeCompletionAdvance",
|
response(null, new AuInvoke(this, "resizeCompletionAdvance",
|
||||||
widthHoursAdvancePercentage));
|
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 {
|
} else {
|
||||||
response(null, new AuInvoke(this, "resizeCompletionAdvance", "0px"));
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPlannerCommand() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NullCommandOnTask<T> implements ICommandOnTask<T> {
|
private static class NullCommandOnTask<T> implements ICommandOnTask<T> {
|
||||||
|
|
|
||||||
|
|
@ -308,4 +308,19 @@ public class DefaultFundamentalProperties implements ITaskFundamentalProperties
|
||||||
return false;
|
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();
|
public boolean isRoot();
|
||||||
|
|
||||||
|
boolean isUpdatedFromTimesheets();
|
||||||
|
|
||||||
|
Date getFirstTimesheetDate();
|
||||||
|
|
||||||
|
Date getLastTimesheetDate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -548,6 +548,21 @@ public abstract class Task implements ITaskFundamentalProperties {
|
||||||
getBeginDate());
|
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() {
|
public void firePropertyChangeForTaskDates() {
|
||||||
fundamentalPropertiesListeners.firePropertyChange("beginDate", null,
|
fundamentalPropertiesListeners.firePropertyChange("beginDate", null,
|
||||||
getBeginDate());
|
getBeginDate());
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ public class TaskLeaf extends Task {
|
||||||
@Override
|
@Override
|
||||||
public boolean canBeExplicitlyMoved() {
|
public boolean canBeExplicitlyMoved() {
|
||||||
return !(isSubcontracted() || isLimitingAndHasDayAssignments()
|
return !(isSubcontracted() || isLimitingAndHasDayAssignments()
|
||||||
|| hasConsolidations() || isManualAnyAllocation());
|
|| hasConsolidations() || isManualAnyAllocation() || isUpdatedFromTimesheets());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,10 @@ public interface ICommand<T> {
|
||||||
|
|
||||||
boolean isDisabled();
|
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){
|
resizeCompletionAdvance : function(width){
|
||||||
jq('#' + this.uuid + ' .completion:first').css('width', 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){
|
resizeCompletion2Advance : function(width){
|
||||||
jq('#' + this.uuid + ' .completion2:first').css('width', 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="completionMoneyCostBar"></div>');
|
||||||
out.push('<div class="completion"></div>');
|
out.push('<div class="completion"></div>');
|
||||||
out.push('<div class="completion2"></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">',
|
out.push('<div id="tasktooltip', this.uuid,'" class="task_tooltip">',
|
||||||
this.getTooltipText(),
|
this.getTooltipText(),
|
||||||
|
|
|
||||||
|
|
@ -36,16 +36,26 @@ public enum PredefinedAdvancedTypes {
|
||||||
UNITS("units", new BigDecimal(Integer.MAX_VALUE),
|
UNITS("units", new BigDecimal(Integer.MAX_VALUE),
|
||||||
new BigDecimal(1), false, false),
|
new BigDecimal(1), false, false),
|
||||||
SUBCONTRACTOR("subcontractor",
|
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,
|
private PredefinedAdvancedTypes(String name, BigDecimal defaultMaxValue,
|
||||||
BigDecimal precision, boolean percentage, boolean qualityForm) {
|
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.name = name;
|
||||||
this.defaultMaxValue = defaultMaxValue.setScale(4,
|
this.defaultMaxValue = defaultMaxValue.setScale(4,
|
||||||
BigDecimal.ROUND_HALF_UP);
|
BigDecimal.ROUND_HALF_UP);
|
||||||
this.unitPrecision = precision.setScale(4, BigDecimal.ROUND_HALF_UP);
|
this.unitPrecision = precision.setScale(4, BigDecimal.ROUND_HALF_UP);
|
||||||
this.percentage = percentage;
|
this.percentage = percentage;
|
||||||
this.qualityForm = qualityForm;
|
this.qualityForm = qualityForm;
|
||||||
|
this.readOnly = readOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
@ -58,9 +68,13 @@ public enum PredefinedAdvancedTypes {
|
||||||
|
|
||||||
private final boolean qualityForm;
|
private final boolean qualityForm;
|
||||||
|
|
||||||
|
private final boolean readOnly;
|
||||||
|
|
||||||
public AdvanceType createType() {
|
public AdvanceType createType() {
|
||||||
return AdvanceType.create(name, defaultMaxValue, false, unitPrecision,
|
AdvanceType advanceType = AdvanceType.create(name, defaultMaxValue,
|
||||||
true, percentage, qualityForm);
|
false, unitPrecision, true, percentage, qualityForm);
|
||||||
|
advanceType.setReadOnly(readOnly);
|
||||||
|
return advanceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTypeName() {
|
public String getTypeName() {
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,8 @@ public class AdvanceType extends BaseEntity implements IHumanIdentifiable{
|
||||||
|
|
||||||
private IAdvanceTypeDAO avanceTypeDAO = Registry.getAdvanceTypeDao();
|
private IAdvanceTypeDAO avanceTypeDAO = Registry.getAdvanceTypeDao();
|
||||||
|
|
||||||
|
private boolean readOnly = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for hibernate. Do not use!
|
* Constructor for hibernate. Do not use!
|
||||||
*/
|
*/
|
||||||
|
|
@ -271,4 +273,12 @@ public class AdvanceType extends BaseEntity implements IHumanIdentifiable{
|
||||||
return true;
|
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();
|
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);
|
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;
|
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.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -66,6 +70,9 @@ public class SumChargedEffortDAO extends
|
||||||
@Autowired
|
@Autowired
|
||||||
private IOrderDAO orderDAO;
|
private IOrderDAO orderDAO;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IOrderElementDAO orderElementDAO;
|
||||||
|
|
||||||
private Map<OrderElement, SumChargedEffort> mapSumChargedEfforts;
|
private Map<OrderElement, SumChargedEffort> mapSumChargedEfforts;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -114,6 +121,7 @@ public class SumChargedEffortDAO extends
|
||||||
forceLoadParents(parent);
|
forceLoadParents(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
previousEffort = previous.getFirst();
|
previousEffort = previous.getFirst();
|
||||||
|
|
@ -149,9 +157,6 @@ public class SumChargedEffortDAO extends
|
||||||
private void addDirectChargedEffort(OrderElement orderElement,
|
private void addDirectChargedEffort(OrderElement orderElement,
|
||||||
EffortDuration effort) {
|
EffortDuration effort) {
|
||||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||||
if (sumChargedEffort == null) {
|
|
||||||
sumChargedEffort = SumChargedEffort.create(orderElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
sumChargedEffort.addDirectChargedEffort(effort);
|
sumChargedEffort.addDirectChargedEffort(effort);
|
||||||
save(sumChargedEffort);
|
save(sumChargedEffort);
|
||||||
|
|
@ -163,9 +168,6 @@ public class SumChargedEffortDAO extends
|
||||||
EffortDuration effort) {
|
EffortDuration effort) {
|
||||||
if (orderElement != null) {
|
if (orderElement != null) {
|
||||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||||
if (sumChargedEffort == null) {
|
|
||||||
sumChargedEffort = SumChargedEffort.create(orderElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
sumChargedEffort.addIndirectChargedEffort(effort);
|
sumChargedEffort.addIndirectChargedEffort(effort);
|
||||||
save(sumChargedEffort);
|
save(sumChargedEffort);
|
||||||
|
|
@ -231,6 +233,9 @@ public class SumChargedEffortDAO extends
|
||||||
.get(orderElement);
|
.get(orderElement);
|
||||||
if (sumChargedEffort == null) {
|
if (sumChargedEffort == null) {
|
||||||
sumChargedEffort = findByOrderElement(orderElement);
|
sumChargedEffort = findByOrderElement(orderElement);
|
||||||
|
if (sumChargedEffort == null) {
|
||||||
|
sumChargedEffort = SumChargedEffort.create(orderElement);
|
||||||
|
}
|
||||||
mapSumChargedEfforts.put(orderElement, sumChargedEffort);
|
mapSumChargedEfforts.put(orderElement, sumChargedEffort);
|
||||||
}
|
}
|
||||||
return sumChargedEffort;
|
return sumChargedEffort;
|
||||||
|
|
@ -251,6 +256,7 @@ public class SumChargedEffortDAO extends
|
||||||
resetMapSumChargedEfforts();
|
resetMapSumChargedEfforts();
|
||||||
resetSumChargedEffort(order);
|
resetSumChargedEffort(order);
|
||||||
calculateDirectChargedEffort(order);
|
calculateDirectChargedEffort(order);
|
||||||
|
calculateTimesheetData(order);
|
||||||
} catch (InstanceNotFoundException e) {
|
} catch (InstanceNotFoundException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
@ -258,9 +264,6 @@ public class SumChargedEffortDAO extends
|
||||||
|
|
||||||
private void resetSumChargedEffort(OrderElement orderElement) {
|
private void resetSumChargedEffort(OrderElement orderElement) {
|
||||||
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
SumChargedEffort sumChargedEffort = getByOrderElement(orderElement);
|
||||||
if (sumChargedEffort == null) {
|
|
||||||
sumChargedEffort = SumChargedEffort.create(orderElement);
|
|
||||||
}
|
|
||||||
sumChargedEffort.reset();
|
sumChargedEffort.reset();
|
||||||
|
|
||||||
for (OrderElement each : orderElement.getChildren()) {
|
for (OrderElement each : orderElement.getChildren()) {
|
||||||
|
|
@ -281,4 +284,159 @@ public class SumChargedEffortDAO extends
|
||||||
addDirectChargedEffort(orderElement, effort);
|
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();
|
return getCurrentSchedulingData().getTaskSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TaskElement getTaskElement() {
|
||||||
|
TaskSource taskSource = getTaskSource();
|
||||||
|
if (taskSource == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return taskSource.getTask();
|
||||||
|
}
|
||||||
|
|
||||||
public Set<TaskElement> getTaskElements() {
|
public Set<TaskElement> getTaskElements() {
|
||||||
if (getTaskSource() == null) {
|
if (getTaskSource() == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
|
|
@ -1586,6 +1594,44 @@ public abstract class OrderElement extends IntegrationEntity implements
|
||||||
return false;
|
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() {
|
public void detachFromParent() {
|
||||||
parent = null;
|
parent = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,11 @@ public class OrderLineGroup extends OrderElement implements
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUpdatedFromTimesheets() {
|
||||||
|
return getThis().isUpdatedFromTimesheets();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OrderLineGroup create() {
|
public static OrderLineGroup create() {
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,12 @@
|
||||||
|
|
||||||
package org.libreplan.business.orders.entities;
|
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.common.BaseEntity;
|
||||||
import org.libreplan.business.workingday.EffortDuration;
|
import org.libreplan.business.workingday.EffortDuration;
|
||||||
|
import org.libreplan.business.workreports.entities.WorkReportLine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It represents the efforts charged to an {@link OrderElement}, avoiding the
|
* 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 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() {}
|
protected SumChargedEffort() {}
|
||||||
|
|
||||||
private SumChargedEffort(OrderElement orderElement) {
|
private SumChargedEffort(OrderElement orderElement) {
|
||||||
|
|
@ -93,6 +108,38 @@ public class SumChargedEffort extends BaseEntity {
|
||||||
public void reset() {
|
public void reset() {
|
||||||
directChargedEffort = EffortDuration.zero();
|
directChargedEffort = EffortDuration.zero();
|
||||||
indirectChargedEffort = 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();
|
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.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.commons.lang.Validate;
|
import org.apache.commons.lang.Validate;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
@ -182,6 +183,8 @@ public abstract class TaskElement extends BaseEntity {
|
||||||
|
|
||||||
private Boolean simplifiedAssignedStatusCalculationEnabled = false;
|
private Boolean simplifiedAssignedStatusCalculationEnabled = false;
|
||||||
|
|
||||||
|
private Boolean updatedFromTimesheets = false;
|
||||||
|
|
||||||
public void initializeDatesIfNeeded() {
|
public void initializeDatesIfNeeded() {
|
||||||
if (getIntraDayEndDate() == null || getIntraDayStartDate() == null) {
|
if (getIntraDayEndDate() == null || getIntraDayStartDate() == null) {
|
||||||
initializeDates();
|
initializeDates();
|
||||||
|
|
@ -837,4 +840,12 @@ public abstract class TaskElement extends BaseEntity {
|
||||||
return result;
|
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();
|
public abstract boolean isOrderTemplate();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUpdatedFromTimesheets() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,11 @@ public class OrderLineGroupTemplate extends OrderElementTemplate implements
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUpdatedFromTimesheets() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OrderLineGroupTemplate createNew() {
|
public static OrderLineGroupTemplate createNew() {
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,6 @@ public interface ITreeNode<T extends ITreeNode<T>> {
|
||||||
*/
|
*/
|
||||||
boolean isEmptyLeaf();
|
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.orders.entities.OrderElement;
|
||||||
import org.libreplan.business.reports.dtos.WorkReportLineDTO;
|
import org.libreplan.business.reports.dtos.WorkReportLineDTO;
|
||||||
import org.libreplan.business.resources.entities.Resource;
|
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.WorkReport;
|
||||||
import org.libreplan.business.workreports.entities.WorkReportLine;
|
import org.libreplan.business.workreports.entities.WorkReportLine;
|
||||||
|
|
||||||
|
|
@ -65,6 +66,14 @@ public interface IWorkReportLineDAO extends
|
||||||
List<WorkReportLine> findByResourceFilteredByDateNotInWorkReport(
|
List<WorkReportLine> findByResourceFilteredByDateNotInWorkReport(
|
||||||
Resource resource, Date start, Date end, WorkReport workReport);
|
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(
|
List<WorkReportLine> findByOrderElementAndWorkReports(
|
||||||
OrderElement orderElement, List<WorkReport> workReports);
|
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.orders.entities.OrderElement;
|
||||||
import org.libreplan.business.reports.dtos.WorkReportLineDTO;
|
import org.libreplan.business.reports.dtos.WorkReportLineDTO;
|
||||||
import org.libreplan.business.resources.entities.Resource;
|
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.WorkReport;
|
||||||
import org.libreplan.business.workreports.entities.WorkReportLine;
|
import org.libreplan.business.workreports.entities.WorkReportLine;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -146,6 +148,57 @@ public class WorkReportLineDAO extends IntegrationEntityDAO<WorkReportLine>
|
||||||
return criteria.list();
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public List<WorkReportLine> findByOrderElementAndWorkReports(
|
public List<WorkReportLine> findByOrderElementAndWorkReports(
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import org.hibernate.validator.Valid;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.libreplan.business.common.IntegrationEntity;
|
import org.libreplan.business.common.IntegrationEntity;
|
||||||
import org.libreplan.business.common.Registry;
|
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.EntitySequence;
|
||||||
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
|
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
|
||||||
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
||||||
|
|
@ -538,4 +539,20 @@ public class WorkReport extends IntegrationEntity implements
|
||||||
return false;
|
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.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
@ -78,6 +79,8 @@ public class WorkReportLine extends IntegrationEntity implements Comparable,
|
||||||
|
|
||||||
private TypeOfWorkHours typeOfWorkHours;
|
private TypeOfWorkHours typeOfWorkHours;
|
||||||
|
|
||||||
|
private Boolean finished = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for hibernate. Do not use!
|
* Constructor for hibernate. Do not use!
|
||||||
*/
|
*/
|
||||||
|
|
@ -552,4 +555,31 @@ public class WorkReportLine extends IntegrationEntity implements Comparable,
|
||||||
: false;
|
: 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>
|
</update>
|
||||||
</changeSet>
|
</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">
|
<changeSet id="update-status-values-in-order_table" author="mrego">
|
||||||
<comment>Updating status values in order_table</comment>
|
<comment>Updating status values in order_table</comment>
|
||||||
<update tableName="order_table">
|
<update tableName="order_table">
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
<property name="percentage" access="field"/>
|
<property name="percentage" access="field"/>
|
||||||
<property name="qualityForm" access="field"
|
<property name="qualityForm" access="field"
|
||||||
column="quality_form" />
|
column="quality_form" />
|
||||||
|
<property name="readOnly" column="read_only" />
|
||||||
|
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,14 @@
|
||||||
<property name="indirectChargedEffort" access="field"
|
<property name="indirectChargedEffort" access="field"
|
||||||
column="indirect_charged_effort"
|
column="indirect_charged_effort"
|
||||||
type="org.libreplan.business.workingday.hibernate.EffortDurationType" />
|
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>
|
||||||
|
|
||||||
<class name="SumExpenses" table="sum_expenses">
|
<class name="SumExpenses" table="sum_expenses">
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,9 @@
|
||||||
<many-to-one name="calendar" column="base_calendar_id" cascade="none"
|
<many-to-one name="calendar" column="base_calendar_id" cascade="none"
|
||||||
class="org.libreplan.business.calendars.entities.BaseCalendar"/>
|
class="org.libreplan.business.calendars.entities.BaseCalendar"/>
|
||||||
|
|
||||||
|
<property name="updatedFromTimesheets"
|
||||||
|
column="updated_from_timesheets" />
|
||||||
|
|
||||||
<joined-subclass name="Task" table="task">
|
<joined-subclass name="Task" table="task">
|
||||||
<key column="task_element_id"></key>
|
<key column="task_element_id"></key>
|
||||||
<property name="calculatedValue" column="calculated_value">
|
<property name="calculatedValue" column="calculated_value">
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,8 @@
|
||||||
</composite-element>
|
</composite-element>
|
||||||
</set>
|
</set>
|
||||||
|
|
||||||
|
<property name="finished" />
|
||||||
|
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
<class name="WorkReportLabelTypeAssigment" table="work_report_label_type_assigment">
|
<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.zkplus.databind.DataBinder;
|
||||||
import org.zkoss.zul.Bandbox;
|
import org.zkoss.zul.Bandbox;
|
||||||
import org.zkoss.zul.Button;
|
import org.zkoss.zul.Button;
|
||||||
|
import org.zkoss.zul.Checkbox;
|
||||||
import org.zkoss.zul.Combobox;
|
import org.zkoss.zul.Combobox;
|
||||||
import org.zkoss.zul.Comboitem;
|
import org.zkoss.zul.Comboitem;
|
||||||
import org.zkoss.zul.Datebox;
|
import org.zkoss.zul.Datebox;
|
||||||
|
|
@ -67,7 +68,6 @@ import org.zkoss.zul.Radio;
|
||||||
import org.zkoss.zul.Row;
|
import org.zkoss.zul.Row;
|
||||||
import org.zkoss.zul.Textbox;
|
import org.zkoss.zul.Textbox;
|
||||||
import org.zkoss.zul.Timebox;
|
import org.zkoss.zul.Timebox;
|
||||||
import org.zkoss.zul.api.Checkbox;
|
|
||||||
import org.zkoss.zul.api.Column;
|
import org.zkoss.zul.api.Column;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -446,7 +446,7 @@ public class Util {
|
||||||
* The {@link Setter} interface that will implement a set method.
|
* The {@link Setter} interface that will implement a set method.
|
||||||
* @return The {@link Checkbox} bound
|
* @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) {
|
final Getter<Boolean> getter, final Setter<Boolean> setter) {
|
||||||
checkBox.setChecked(getter.get());
|
checkBox.setChecked(getter.get());
|
||||||
checkBox.addEventListener(Events.ON_CHECK, new EventListener() {
|
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;
|
||||||
import org.libreplan.web.common.Util.Getter;
|
import org.libreplan.web.common.Util.Getter;
|
||||||
import org.libreplan.web.common.Util.Setter;
|
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
|
* 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 isQualityForm(AdvanceAssignment advance);
|
||||||
|
|
||||||
|
public boolean isReadOnly(AdvanceAssignment advance);
|
||||||
|
|
||||||
public boolean lessThanPreviousMeasurements();
|
public boolean lessThanPreviousMeasurements();
|
||||||
|
|
||||||
public boolean hasConsolidatedAdvances(AdvanceAssignment advance);
|
public boolean hasConsolidatedAdvances(AdvanceAssignment advance);
|
||||||
|
|
@ -134,4 +136,5 @@ public interface IManageOrderElementAdvancesModel {
|
||||||
|
|
||||||
Boolean isAlreadyReportedProgressWith(LocalDate date);
|
Boolean isAlreadyReportedProgressWith(LocalDate date);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,10 @@ public class ManageOrderElementAdvancesController extends
|
||||||
if (advance.getAdvanceType() != null) {
|
if (advance.getAdvanceType() != null) {
|
||||||
isQualityForm = manageOrderElementAdvancesModel
|
isQualityForm = manageOrderElementAdvancesModel
|
||||||
.isQualityForm(advance);
|
.isQualityForm(advance);
|
||||||
if (manageOrderElementAdvancesModel
|
readOnlyAdvance = manageOrderElementAdvancesModel
|
||||||
|
.isReadOnly(advance);
|
||||||
|
if (!readOnlyAdvance
|
||||||
|
&& manageOrderElementAdvancesModel
|
||||||
.isSubcontratedAdvanceTypeAndSubcontratedTask(advance)) {
|
.isSubcontratedAdvanceTypeAndSubcontratedTask(advance)) {
|
||||||
readOnlyAdvance = true;
|
readOnlyAdvance = true;
|
||||||
}
|
}
|
||||||
|
|
@ -375,12 +378,12 @@ public class ManageOrderElementAdvancesController extends
|
||||||
if ((advance instanceof DirectAdvanceAssignment)
|
if ((advance instanceof DirectAdvanceAssignment)
|
||||||
&& ((DirectAdvanceAssignment) advance)
|
&& ((DirectAdvanceAssignment) advance)
|
||||||
.getAdvanceMeasurements().isEmpty()
|
.getAdvanceMeasurements().isEmpty()
|
||||||
&& !isQualityForm) {
|
&& !isQualityForm && !readOnlyAdvance) {
|
||||||
appendComboboxAdvanceType(listItem);
|
appendComboboxAdvanceType(listItem);
|
||||||
} else {
|
} else {
|
||||||
appendLabelAdvanceType(listItem);
|
appendLabelAdvanceType(listItem);
|
||||||
}
|
}
|
||||||
appendDecimalBoxMaxValue(listItem, isQualityForm);
|
appendDecimalBoxMaxValue(listItem, isQualityForm || readOnlyAdvance);
|
||||||
appendDecimalBoxValue(listItem);
|
appendDecimalBoxValue(listItem);
|
||||||
appendLabelPercentage(listItem);
|
appendLabelPercentage(listItem);
|
||||||
appendDateBoxDate(listItem);
|
appendDateBoxDate(listItem);
|
||||||
|
|
@ -401,7 +404,8 @@ public class ManageOrderElementAdvancesController extends
|
||||||
for(AdvanceType advanceType : listAdvanceType){
|
for(AdvanceType advanceType : listAdvanceType){
|
||||||
if (!advanceType.getUnitName().equals(
|
if (!advanceType.getUnitName().equals(
|
||||||
PredefinedAdvancedTypes.CHILDREN.getTypeName())
|
PredefinedAdvancedTypes.CHILDREN.getTypeName())
|
||||||
&& !advanceType.isQualityForm()) {
|
&& !advanceType.isQualityForm()
|
||||||
|
&& !advanceType.isReadOnly()) {
|
||||||
Comboitem comboItem = new Comboitem();
|
Comboitem comboItem = new Comboitem();
|
||||||
comboItem.setValue(advanceType);
|
comboItem.setValue(advanceType);
|
||||||
comboItem.setLabel(advanceType.getUnitName());
|
comboItem.setLabel(advanceType.getUnitName());
|
||||||
|
|
@ -461,7 +465,7 @@ public class ManageOrderElementAdvancesController extends
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendDecimalBoxMaxValue(final Listitem listItem,
|
private void appendDecimalBoxMaxValue(final Listitem listItem,
|
||||||
boolean isQualityForm) {
|
boolean isQualityFormOrReadOnly) {
|
||||||
final AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem
|
final AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem
|
||||||
.getValue();
|
.getValue();
|
||||||
final Decimalbox maxValue = new Decimalbox();
|
final Decimalbox maxValue = new Decimalbox();
|
||||||
|
|
@ -469,7 +473,7 @@ public class ManageOrderElementAdvancesController extends
|
||||||
|
|
||||||
final DirectAdvanceAssignment directAdvanceAssignment;
|
final DirectAdvanceAssignment directAdvanceAssignment;
|
||||||
if ((advanceAssignment instanceof IndirectAdvanceAssignment)
|
if ((advanceAssignment instanceof IndirectAdvanceAssignment)
|
||||||
|| isQualityForm
|
|| isQualityFormOrReadOnly
|
||||||
|| (advanceAssignment.getAdvanceType() != null && advanceAssignment
|
|| (advanceAssignment.getAdvanceType() != null && advanceAssignment
|
||||||
.getAdvanceType().getPercentage())
|
.getAdvanceType().getPercentage())
|
||||||
|| manageOrderElementAdvancesModel
|
|| manageOrderElementAdvancesModel
|
||||||
|
|
@ -708,6 +712,11 @@ public class ManageOrderElementAdvancesController extends
|
||||||
addMeasurementButton.setDisabled(true);
|
addMeasurementButton.setDisabled(true);
|
||||||
addMeasurementButton
|
addMeasurementButton
|
||||||
.setTooltiptext(_("Progress that are reported by quality forms can not be modified"));
|
.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) {
|
} else if (advance instanceof IndirectAdvanceAssignment) {
|
||||||
addMeasurementButton.setDisabled(true);
|
addMeasurementButton.setDisabled(true);
|
||||||
addMeasurementButton
|
addMeasurementButton
|
||||||
|
|
@ -739,6 +748,11 @@ public class ManageOrderElementAdvancesController extends
|
||||||
removeButton.setDisabled(true);
|
removeButton.setDisabled(true);
|
||||||
removeButton
|
removeButton
|
||||||
.setTooltiptext(_("Progress that are reported by quality forms cannot be modified"));
|
.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) {
|
} else if (advance instanceof IndirectAdvanceAssignment) {
|
||||||
removeButton.setDisabled(true);
|
removeButton.setDisabled(true);
|
||||||
removeButton
|
removeButton
|
||||||
|
|
@ -1219,6 +1233,11 @@ public class ManageOrderElementAdvancesController extends
|
||||||
removeButton.setDisabled(true);
|
removeButton.setDisabled(true);
|
||||||
removeButton
|
removeButton
|
||||||
.setTooltiptext(_("Progress measurements that are reported by quality forms cannot be removed"));
|
.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()) {
|
} else if (advance.isFake()) {
|
||||||
removeButton.setDisabled(true);
|
removeButton.setDisabled(true);
|
||||||
removeButton
|
removeButton
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,8 @@ public class ManageOrderElementAdvancesModel implements
|
||||||
for (AdvanceAssignment advance : listAdvanceAssignmentsCopy) {
|
for (AdvanceAssignment advance : listAdvanceAssignmentsCopy) {
|
||||||
if ((!listAdvanceAssignments.contains(advance))
|
if ((!listAdvanceAssignments.contains(advance))
|
||||||
&& (advance instanceof DirectAdvanceAssignment)
|
&& (advance instanceof DirectAdvanceAssignment)
|
||||||
&& (!advance.getAdvanceType().isQualityForm())) {
|
&& (!advance.getAdvanceType().isQualityForm())
|
||||||
|
&& (!advance.getAdvanceType().isReadOnly())) {
|
||||||
listAdvanceAssignments.add(advance);
|
listAdvanceAssignments.add(advance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +338,8 @@ public class ManageOrderElementAdvancesModel implements
|
||||||
for (AdvanceType advanceType : this.listAdvanceTypes) {
|
for (AdvanceType advanceType : this.listAdvanceTypes) {
|
||||||
if ((advanceType.getUnitName()
|
if ((advanceType.getUnitName()
|
||||||
.equals(PredefinedAdvancedTypes.CHILDREN.getTypeName()))
|
.equals(PredefinedAdvancedTypes.CHILDREN.getTypeName()))
|
||||||
|| (advanceType.isQualityForm())) {
|
|| (advanceType.isQualityForm())
|
||||||
|
|| advanceType.isReadOnly()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (existsAdvanceTypeAlreadyInThisOrderElement(advanceType)) {
|
if (existsAdvanceTypeAlreadyInThisOrderElement(advanceType)) {
|
||||||
|
|
@ -412,6 +414,9 @@ public class ManageOrderElementAdvancesModel implements
|
||||||
if (advanceType.isQualityForm()) {
|
if (advanceType.isQualityForm()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (advanceType.isReadOnly()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isIndirectAdvanceAssignment){
|
if(isIndirectAdvanceAssignment){
|
||||||
|
|
@ -764,6 +769,14 @@ public class ManageOrderElementAdvancesModel implements
|
||||||
return advanceType.isQualityForm();
|
return advanceType.isQualityForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public boolean isReadOnly(AdvanceAssignment advance) {
|
||||||
|
AdvanceType advanceType = advance.getAdvanceType();
|
||||||
|
advanceTypeDAO.reattach(advanceType);
|
||||||
|
return advanceType.isReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public boolean findIndirectConsolidation(
|
public boolean findIndirectConsolidation(
|
||||||
|
|
|
||||||
|
|
@ -1203,7 +1203,8 @@ _(
|
||||||
@Override
|
@Override
|
||||||
public boolean isFixed() {
|
public boolean isFixed() {
|
||||||
return taskElement.isLimitingAndHasDayAssignments()
|
return taskElement.isLimitingAndHasDayAssignments()
|
||||||
|| taskElement.hasConsolidations();
|
|| taskElement.hasConsolidations()
|
||||||
|
|| taskElement.isUpdatedFromTimesheets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1222,6 +1223,29 @@ _(
|
||||||
return taskElement.isRoot();
|
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
|
@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() {
|
private EffortDurationBox buildSumAllEffort() {
|
||||||
EffortDurationBox box = (isGroupingRow() || isLimiting) ? EffortDurationBox
|
EffortDurationBox box = isEffortDurationBoxDisabled() ? EffortDurationBox
|
||||||
.notEditable() : new EffortDurationBox();
|
.notEditable() : new EffortDurationBox();
|
||||||
box.setWidth("40px");
|
box.setWidth("40px");
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addListenerIfNeeded(Component allEffortComponent) {
|
private void addListenerIfNeeded(Component allEffortComponent) {
|
||||||
if (isGroupingRow() || isLimiting) {
|
if (isEffortDurationBoxDisabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final EffortDurationBox effortDurationBox = (EffortDurationBox) allEffortComponent;
|
final EffortDurationBox effortDurationBox = (EffortDurationBox) allEffortComponent;
|
||||||
|
|
@ -1167,6 +1167,10 @@ class Row {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isEffortDurationBoxDisabled() {
|
||||||
|
return isGroupingRow() || isLimiting || task.isUpdatedFromTimesheets();
|
||||||
|
}
|
||||||
|
|
||||||
private void reloadEffortsSameRowForDetailItems() {
|
private void reloadEffortsSameRowForDetailItems() {
|
||||||
for (Entry<DetailItem, Component> entry : componentsByDetailItem
|
for (Entry<DetailItem, Component> entry : componentsByDetailItem
|
||||||
.entrySet()) {
|
.entrySet()) {
|
||||||
|
|
@ -1181,7 +1185,7 @@ class Row {
|
||||||
EffortDuration allEffort = aggregate.getTotalEffort();
|
EffortDuration allEffort = aggregate.getTotalEffort();
|
||||||
allEffortInput.setValue(allEffort);
|
allEffortInput.setValue(allEffort);
|
||||||
Clients.closeErrorBox(allEffortInput);
|
Clients.closeErrorBox(allEffortInput);
|
||||||
if (isLimiting) {
|
if (isEffortDurationBoxDisabled()) {
|
||||||
allEffortInput.setDisabled(true);
|
allEffortInput.setDisabled(true);
|
||||||
}
|
}
|
||||||
if (restriction.isInvalidTotalEffort(allEffort)) {
|
if (restriction.isInvalidTotalEffort(allEffort)) {
|
||||||
|
|
@ -1215,6 +1219,11 @@ class Row {
|
||||||
hboxAssigmentFunctionsCombo.appendChild(assignmentFunctionsCombo);
|
hboxAssigmentFunctionsCombo.appendChild(assignmentFunctionsCombo);
|
||||||
assignmentFunctionsConfigureButton = getAssignmentFunctionsConfigureButton(assignmentFunctionsCombo);
|
assignmentFunctionsConfigureButton = getAssignmentFunctionsConfigureButton(assignmentFunctionsCombo);
|
||||||
hboxAssigmentFunctionsCombo.appendChild(assignmentFunctionsConfigureButton);
|
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) {
|
private boolean cannotBeEdited(DetailItem item) {
|
||||||
return isGroupingRow() || doesNotIntersectWithTask(item)
|
return isGroupingRow() || doesNotIntersectWithTask(item)
|
||||||
|| isBeforeLatestConsolidation(item);
|
|| isBeforeLatestConsolidation(item)
|
||||||
|
|| task.isUpdatedFromTimesheets();
|
||||||
}
|
}
|
||||||
|
|
||||||
private EffortDurationBox disableIfNeeded(DetailItem item,
|
private EffortDurationBox disableIfNeeded(DetailItem item,
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,8 @@ public class AllocationConfiguration extends HtmlMacroComponent {
|
||||||
.getCalculatedValue())) {
|
.getCalculatedValue())) {
|
||||||
radio.setChecked(true);
|
radio.setChecked(true);
|
||||||
}
|
}
|
||||||
radio.setDisabled(formBinder.isAnyManual());
|
radio.setDisabled(formBinder.isAnyManual()
|
||||||
|
|| formBinder.isTaskUpdatedFromTimesheets());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -423,4 +423,9 @@ public class AllocationRowsHandler {
|
||||||
private ArrayList<AllocationRow> copyOfCurrentRowsToAvoidConcurrentModification() {
|
private ArrayList<AllocationRow> copyOfCurrentRowsToAvoidConcurrentModification() {
|
||||||
return new ArrayList<AllocationRow>(currentRows);
|
return new ArrayList<AllocationRow>(currentRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTaskUpdatedFromTimesheets() {
|
||||||
|
return task.isUpdatedFromTimesheets();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +217,7 @@ public class FormBinder {
|
||||||
boolean disabled = rows.isEmpty()
|
boolean disabled = rows.isEmpty()
|
||||||
|| (CalculatedValue.NUMBER_OF_HOURS == c)
|
|| (CalculatedValue.NUMBER_OF_HOURS == c)
|
||||||
|| (c == CalculatedValue.RESOURCES_PER_DAY && !recommendedAllocation)
|
|| (c == CalculatedValue.RESOURCES_PER_DAY && !recommendedAllocation)
|
||||||
|| isAnyManual();
|
|| isAnyManual() || isTaskUpdatedFromTimesheets();
|
||||||
this.effortInput.setDisabled(disabled);
|
this.effortInput.setDisabled(disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,13 +245,14 @@ public class FormBinder {
|
||||||
allResourcesPerDayVisibilityRule();
|
allResourcesPerDayVisibilityRule();
|
||||||
applyDisabledRulesOnRows();
|
applyDisabledRulesOnRows();
|
||||||
this.btnRecommendedAllocation.setDisabled(recommendedAllocation
|
this.btnRecommendedAllocation.setDisabled(recommendedAllocation
|
||||||
|| isAnyManual());
|
|| isAnyManual() || isTaskUpdatedFromTimesheets());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyDisabledRulesOnRows() {
|
private void applyDisabledRulesOnRows() {
|
||||||
for (AllocationRow each : rows) {
|
for (AllocationRow each : rows) {
|
||||||
each.applyDisabledRules(getCalculatedValue(),
|
each.applyDisabledRules(getCalculatedValue(),
|
||||||
recommendedAllocation, isAnyManual());
|
recommendedAllocation, isAnyManual()
|
||||||
|
|| isTaskUpdatedFromTimesheets());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,7 +362,7 @@ public class FormBinder {
|
||||||
void applyDisabledRules() {
|
void applyDisabledRules() {
|
||||||
this.taskWorkableDays.setDisabled(allocationRowsHandler
|
this.taskWorkableDays.setDisabled(allocationRowsHandler
|
||||||
.getCalculatedValue() == CalculatedValue.END_DATE
|
.getCalculatedValue() == CalculatedValue.END_DATE
|
||||||
|| isAnyManual());
|
|| isAnyManual() || isTaskUpdatedFromTimesheets());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeDateAndDurationFieldsFromTaskOriginalValues() {
|
private void initializeDateAndDurationFieldsFromTaskOriginalValues() {
|
||||||
|
|
@ -465,7 +466,8 @@ public class FormBinder {
|
||||||
CalculatedValue c = allocationRowsHandler.getCalculatedValue();
|
CalculatedValue c = allocationRowsHandler.getCalculatedValue();
|
||||||
this.allResourcesPerDay.setDisabled(rows.isEmpty()
|
this.allResourcesPerDay.setDisabled(rows.isEmpty()
|
||||||
|| c == CalculatedValue.RESOURCES_PER_DAY
|
|| c == CalculatedValue.RESOURCES_PER_DAY
|
||||||
|| !recommendedAllocation || isAnyManual());
|
|| !recommendedAllocation || isAnyManual()
|
||||||
|
|| isTaskUpdatedFromTimesheets());
|
||||||
this.allResourcesPerDay
|
this.allResourcesPerDay
|
||||||
.setConstraint(constraintForAllResourcesPerDay());
|
.setConstraint(constraintForAllResourcesPerDay());
|
||||||
}
|
}
|
||||||
|
|
@ -522,6 +524,10 @@ public class FormBinder {
|
||||||
* exit the edition form
|
* exit the edition form
|
||||||
*/
|
*/
|
||||||
public boolean accept() {
|
public boolean accept() {
|
||||||
|
if (isTaskUpdatedFromTimesheets()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Flagged<AllocationResult, Warnings> result = resourceAllocationModel
|
Flagged<AllocationResult, Warnings> result = resourceAllocationModel
|
||||||
.accept();
|
.accept();
|
||||||
|
|
||||||
|
|
@ -707,7 +713,8 @@ public class FormBinder {
|
||||||
|
|
||||||
public void setRecommendedAllocation(Button recommendedAllocation) {
|
public void setRecommendedAllocation(Button recommendedAllocation) {
|
||||||
this.btnRecommendedAllocation = recommendedAllocation;
|
this.btnRecommendedAllocation = recommendedAllocation;
|
||||||
this.btnRecommendedAllocation.setDisabled(isAnyManual());
|
this.btnRecommendedAllocation.setDisabled(isAnyManual()
|
||||||
|
|| isTaskUpdatedFromTimesheets());
|
||||||
Util.ensureUniqueListener(recommendedAllocation, Events.ON_CLICK,
|
Util.ensureUniqueListener(recommendedAllocation, Events.ON_CLICK,
|
||||||
new EventListener() {
|
new EventListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -939,4 +946,8 @@ public class FormBinder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTaskUpdatedFromTimesheets() {
|
||||||
|
return allocationRowsHandler.isTaskUpdatedFromTimesheets();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -602,7 +602,7 @@ public class ResourceAllocationController extends GenericForwardComposer {
|
||||||
|
|
||||||
// On click delete button
|
// On click delete button
|
||||||
Button deleteButton = appendDeleteButton(row);
|
Button deleteButton = appendDeleteButton(row);
|
||||||
deleteButton.setDisabled(isAnyManual());
|
deleteButton.setDisabled(isAnyManualOrTaskUpdatedFromTimesheets());
|
||||||
formBinder.setDeleteButtonFor(data, deleteButton);
|
formBinder.setDeleteButtonFor(data, deleteButton);
|
||||||
deleteButton.addEventListener("onClick", new EventListener() {
|
deleteButton.addEventListener("onClick", new EventListener() {
|
||||||
|
|
||||||
|
|
@ -671,8 +671,12 @@ public class ResourceAllocationController extends GenericForwardComposer {
|
||||||
return formBinder != null && formBinder.isAnyNotFlat();
|
return formBinder != null && formBinder.isAnyNotFlat();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAnyManual() {
|
public boolean isAnyManualOrTaskUpdatedFromTimesheets() {
|
||||||
return formBinder != null && formBinder.isAnyManual();
|
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.business.workingday.EffortDuration;
|
||||||
import org.libreplan.web.calendars.BaseCalendarModel;
|
import org.libreplan.web.calendars.BaseCalendarModel;
|
||||||
import org.libreplan.web.common.ViewSwitcher;
|
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.AdvanceAssignmentPlanningController;
|
||||||
import org.libreplan.web.planner.advances.IAdvanceAssignmentPlanningCommand;
|
import org.libreplan.web.planner.advances.IAdvanceAssignmentPlanningCommand;
|
||||||
import org.libreplan.web.planner.allocation.IAdvancedAllocationCommand;
|
import org.libreplan.web.planner.allocation.IAdvancedAllocationCommand;
|
||||||
|
|
@ -222,6 +223,9 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IReassignCommand reassignCommand;
|
private IReassignCommand reassignCommand;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IAdaptPlanningCommand adaptPlanningCommand;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IResourceAllocationCommand resourceAllocationCommand;
|
private IResourceAllocationCommand resourceAllocationCommand;
|
||||||
|
|
||||||
|
|
@ -332,6 +336,7 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
||||||
|
|
||||||
configuration.addGlobalCommand(buildReassigningCommand());
|
configuration.addGlobalCommand(buildReassigningCommand());
|
||||||
configuration.addGlobalCommand(buildCancelEditionCommand());
|
configuration.addGlobalCommand(buildCancelEditionCommand());
|
||||||
|
configuration.addGlobalCommand(buildAdaptPlanningCommand());
|
||||||
|
|
||||||
NullSeparatorCommandOnTask<TaskElement> separator = new NullSeparatorCommandOnTask<TaskElement>();
|
NullSeparatorCommandOnTask<TaskElement> separator = new NullSeparatorCommandOnTask<TaskElement>();
|
||||||
|
|
||||||
|
|
@ -1055,6 +1060,11 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
||||||
return reassignCommand;
|
return reassignCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ICommand<TaskElement> buildAdaptPlanningCommand() {
|
||||||
|
adaptPlanningCommand.setState(planningState);
|
||||||
|
return adaptPlanningCommand;
|
||||||
|
}
|
||||||
|
|
||||||
private ICommand<TaskElement> buildCancelEditionCommand() {
|
private ICommand<TaskElement> buildCancelEditionCommand() {
|
||||||
return new ICommand<TaskElement>() {
|
return new ICommand<TaskElement>() {
|
||||||
|
|
||||||
|
|
@ -1097,6 +1107,11 @@ public class OrderPlanningModel implements IOrderPlanningModel {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPlannerCommand() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1038,6 +1038,11 @@ public class SaveCommandBuilder {
|
||||||
return disabled;
|
return disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPlannerCommand() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class LabelCreatorForInvalidValues implements
|
private static final class LabelCreatorForInvalidValues implements
|
||||||
|
|
|
||||||
|
|
@ -375,4 +375,9 @@ public class ReassignCommand implements IReassignCommand {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPlannerCommand() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,9 @@ public class ReassignConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isChoosenForReassignation(Task each) {
|
private boolean isChoosenForReassignation(Task each) {
|
||||||
|
if (each.isUpdatedFromTimesheets()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return type == Type.ALL || isAfterDate(each);
|
return type == Type.ALL || isAfterDate(each);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,8 @@ public class TaskPropertiesController extends GenericForwardComposer {
|
||||||
|
|
||||||
disabledConstraintsAndAllocations = currentTaskElement
|
disabledConstraintsAndAllocations = currentTaskElement
|
||||||
.isSubcontractedAndWasAlreadySent()
|
.isSubcontractedAndWasAlreadySent()
|
||||||
|| currentTaskElement.isLimitingAndHasDayAssignments();
|
|| currentTaskElement.isLimitingAndHasDayAssignments()
|
||||||
|
|| currentTaskElement.isUpdatedFromTimesheets();
|
||||||
if (!disabledConstraintsAndAllocations && (currentTaskElement.isTask())) {
|
if (!disabledConstraintsAndAllocations && (currentTaskElement.isTask())) {
|
||||||
disabledConstraintsAndAllocations = ((Task) currentTaskElement)
|
disabledConstraintsAndAllocations = ((Task) currentTaskElement)
|
||||||
.isManualAnyAllocation();
|
.isManualAnyAllocation();
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||||
getModel().indent(element);
|
getModel().indent(element);
|
||||||
filterByPredicateIfAny();
|
filterByPredicateIfAny();
|
||||||
|
updateControlButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TreeModel getTreeModel() {
|
public TreeModel getTreeModel() {
|
||||||
|
|
@ -140,6 +141,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||||
getModel().unindent(element);
|
getModel().unindent(element);
|
||||||
filterByPredicateIfAny();
|
filterByPredicateIfAny();
|
||||||
|
updateControlButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void up() {
|
public void up() {
|
||||||
|
|
@ -153,6 +155,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||||
getModel().up(element);
|
getModel().up(element);
|
||||||
filterByPredicateIfAny();
|
filterByPredicateIfAny();
|
||||||
|
updateControlButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void down() {
|
public void down() {
|
||||||
|
|
@ -165,6 +168,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||||
getModel().down(element);
|
getModel().down(element);
|
||||||
filterByPredicateIfAny();
|
filterByPredicateIfAny();
|
||||||
|
updateControlButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getSelectedNode() {
|
public T getSelectedNode() {
|
||||||
|
|
@ -184,8 +188,15 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
|
|
||||||
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
viewStateSnapshot = TreeViewStateSnapshot.takeSnapshot(tree);
|
||||||
|
|
||||||
Treerow from = (Treerow) dragged;
|
T fromNode = null;
|
||||||
T fromNode = type.cast(((Treeitem) from.getParent()).getValue());
|
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) {
|
if (dropedIn instanceof Tree) {
|
||||||
getModel().moveToRoot(fromNode);
|
getModel().moveToRoot(fromNode);
|
||||||
}
|
}
|
||||||
|
|
@ -354,6 +365,15 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
|
|
||||||
private List<Column> columns;
|
private List<Column> columns;
|
||||||
|
|
||||||
|
private Button btnNewFromTemplate;
|
||||||
|
|
||||||
|
private Button downButton;
|
||||||
|
|
||||||
|
private Button upButton;
|
||||||
|
|
||||||
|
private Button leftButton;
|
||||||
|
|
||||||
|
private Button rightButton;
|
||||||
|
|
||||||
protected TreeViewStateSnapshot getSnapshotOfOpenedNodes() {
|
protected TreeViewStateSnapshot getSnapshotOfOpenedNodes() {
|
||||||
return viewStateSnapshot;
|
return viewStateSnapshot;
|
||||||
|
|
@ -361,6 +381,13 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
|
|
||||||
private void resetControlButtons() {
|
private void resetControlButtons() {
|
||||||
btnNew.setDisabled(isNewButtonDisabled());
|
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();
|
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) {
|
public void render(final Treeitem item, Object data) {
|
||||||
item.setValue(data);
|
item.setValue(data);
|
||||||
applySnapshot(item);
|
applySnapshot(item);
|
||||||
currentTreeRow = getTreeRowWithoutChildrenFor(item);
|
|
||||||
final T currentElement = type.cast(data);
|
final T currentElement = type.cast(data);
|
||||||
|
currentTreeRow = getTreeRowWithoutChildrenFor(item, currentElement);
|
||||||
createCells(item, currentElement);
|
createCells(item, currentElement);
|
||||||
onDropMoveFromDraggedToTarget();
|
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);
|
Treerow result = createOrRetrieveFor(item);
|
||||||
// Attach treecells to treerow
|
// 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();
|
result.getChildren().clear();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -766,7 +800,8 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
final SchedulingState schedulingState = getSchedulingStateFrom(currentElement);
|
final SchedulingState schedulingState = getSchedulingStateFrom(currentElement);
|
||||||
SchedulingStateToggler schedulingStateToggler = new SchedulingStateToggler(
|
SchedulingStateToggler schedulingStateToggler = new SchedulingStateToggler(
|
||||||
schedulingState);
|
schedulingState);
|
||||||
schedulingStateToggler.setReadOnly(readOnly);
|
schedulingStateToggler.setReadOnly(readOnly
|
||||||
|
|| currentElement.isUpdatedFromTimesheets());
|
||||||
final Treecell cell = addCell(
|
final Treecell cell = addCell(
|
||||||
getDecorationFromState(getSchedulingStateFrom(currentElement)),
|
getDecorationFromState(getSchedulingStateFrom(currentElement)),
|
||||||
schedulingStateToggler);
|
schedulingStateToggler);
|
||||||
|
|
@ -796,7 +831,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
schedulingStateToggler.afterCompose();
|
schedulingStateToggler.afterCompose();
|
||||||
cell.setDraggable("true");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract SchedulingState getSchedulingStateFrom(
|
protected abstract SchedulingState getSchedulingStateFrom(
|
||||||
|
|
@ -1105,92 +1140,6 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
protected abstract void onDoubleClickForSchedulingStateCell(
|
protected abstract void onDoubleClickForSchedulingStateCell(
|
||||||
T currentElement);
|
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) {
|
protected Button createRemoveButton(final T currentElement) {
|
||||||
EventListener removeListener = new EventListener() {
|
EventListener removeListener = new EventListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1239,6 +1188,7 @@ public abstract class TreeController<T extends ITreeNode<T>> extends
|
||||||
public void doTry() {
|
public void doTry() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColumns(List<Column> columns) {
|
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)
|
* Disable control buttons (new, up, down, indent, unindent, delete)
|
||||||
*/
|
*/
|
||||||
public void updateControlButtons(Event event) {
|
public void updateControlButtons() {
|
||||||
updateControlButtons((Tree) event.getTarget());
|
T element = getSelectedNode();
|
||||||
}
|
if (element == null) {
|
||||||
|
|
||||||
public void updateControlButtons(Tree tree) {
|
|
||||||
final Treeitem item = tree.getSelectedItem();
|
|
||||||
if (item == null) {
|
|
||||||
resetControlButtons();
|
resetControlButtons();
|
||||||
return;
|
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();
|
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
|
// saved as it will not be possible to find it later with
|
||||||
// WorkReportDAO.getPersonalTimesheetWorkReport() method.
|
// WorkReportDAO.getPersonalTimesheetWorkReport() method.
|
||||||
} else {
|
} else {
|
||||||
|
Set<WorkReportLine> deletedWorkReportLinesSet = removeWorkReportLinesWithEffortZero();
|
||||||
|
|
||||||
|
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||||
|
.getOrderElementsToRecalculateTimsheetDates(
|
||||||
|
workReport.getWorkReportLines(),
|
||||||
|
deletedWorkReportLinesSet);
|
||||||
|
sumChargedEffortDAO
|
||||||
|
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(deletedWorkReportLinesSet);
|
||||||
sumChargedEffortDAO
|
sumChargedEffortDAO
|
||||||
.updateRelatedSumChargedEffortWithWorkReportLineSet(workReport
|
.updateRelatedSumChargedEffortWithWorkReportLineSet(workReport
|
||||||
.getWorkReportLines());
|
.getWorkReportLines());
|
||||||
workReport.generateWorkReportLineCodes(entitySequenceDAO
|
workReport.generateWorkReportLineCodes(entitySequenceDAO
|
||||||
.getNumberOfDigitsCode(EntityNameEnum.WORK_REPORT));
|
.getNumberOfDigitsCode(EntityNameEnum.WORK_REPORT));
|
||||||
workReportDAO.save(workReport);
|
workReportDAO.save(workReport);
|
||||||
|
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetModifiedFields();
|
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() {
|
private void resetModifiedFields() {
|
||||||
modified = false;
|
modified = false;
|
||||||
modifiedMap = new HashMap<OrderElement, Set<LocalDate>>();
|
modifiedMap = new HashMap<OrderElement, Set<LocalDate>>();
|
||||||
|
|
|
||||||
|
|
@ -245,4 +245,10 @@ public interface IWorkReportModel extends IIntegrationEntityModel {
|
||||||
|
|
||||||
WorkReportLine getFirstWorkReportLine();
|
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.Map;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.BooleanUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.hibernate.validator.InvalidValue;
|
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.MessagesForUser;
|
||||||
import org.libreplan.web.common.OnlyOneVisible;
|
import org.libreplan.web.common.OnlyOneVisible;
|
||||||
import org.libreplan.web.common.Util;
|
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.Autocomplete;
|
||||||
import org.libreplan.web.common.components.NewDataSortableColumn;
|
import org.libreplan.web.common.components.NewDataSortableColumn;
|
||||||
import org.libreplan.web.common.components.NewDataSortableGrid;
|
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.event.SelectEvent;
|
||||||
import org.zkoss.zk.ui.util.GenericForwardComposer;
|
import org.zkoss.zk.ui.util.GenericForwardComposer;
|
||||||
import org.zkoss.zul.Button;
|
import org.zkoss.zul.Button;
|
||||||
|
import org.zkoss.zul.Checkbox;
|
||||||
import org.zkoss.zul.Column;
|
import org.zkoss.zul.Column;
|
||||||
import org.zkoss.zul.Columns;
|
import org.zkoss.zul.Columns;
|
||||||
import org.zkoss.zul.Comboitem;
|
import org.zkoss.zul.Comboitem;
|
||||||
|
|
@ -273,7 +277,9 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
for (InvalidValue invalidValue : e.getInvalidValues()) {
|
for (InvalidValue invalidValue : e.getInvalidValues()) {
|
||||||
Object value = invalidValue.getBean();
|
Object value = invalidValue.getBean();
|
||||||
if (value instanceof WorkReport) {
|
if (value instanceof WorkReport) {
|
||||||
validateWorkReport();
|
if (validateWorkReport()) {
|
||||||
|
messagesForUser.showInvalidValues(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (value instanceof WorkReportLine) {
|
if (value instanceof WorkReportLine) {
|
||||||
WorkReportLine workReportLine = (WorkReportLine) invalidValue.getBean();
|
WorkReportLine workReportLine = (WorkReportLine) invalidValue.getBean();
|
||||||
|
|
@ -328,6 +334,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
_("cannot be empty"));
|
_("cannot be empty"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,6 +459,16 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
}
|
}
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -466,7 +483,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
*/
|
*/
|
||||||
private Timebox getTimeboxFinish(Row row) {
|
private Timebox getTimeboxFinish(Row row) {
|
||||||
try {
|
try {
|
||||||
int position = row.getChildren().size() - 5;
|
int position = row.getChildren().size() - 6;
|
||||||
return (Timebox) row.getChildren().get(position);
|
return (Timebox) row.getChildren().get(position);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -480,7 +497,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
*/
|
*/
|
||||||
private Timebox getTimeboxStart(Row row) {
|
private Timebox getTimeboxStart(Row row) {
|
||||||
try {
|
try {
|
||||||
int position = row.getChildren().size() - 6;
|
int position = row.getChildren().size() - 7;
|
||||||
return (Timebox) row.getChildren().get(position);
|
return (Timebox) row.getChildren().get(position);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -494,13 +511,28 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
*/
|
*/
|
||||||
private Listbox getTypeOfHours(Row row) {
|
private Listbox getTypeOfHours(Row row) {
|
||||||
try {
|
try {
|
||||||
int position = row.getChildren().size() - 3;
|
int position = row.getChildren().size() - 4;
|
||||||
return (Listbox) row.getChildren().get(position);
|
return (Listbox) row.getChildren().get(position);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
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}
|
* Locates {@link Texbox} code in {@link Row}
|
||||||
* @param row
|
* @param row
|
||||||
|
|
@ -523,7 +555,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
*/
|
*/
|
||||||
private Textbox getEffort(Row row) {
|
private Textbox getEffort(Row row) {
|
||||||
try {
|
try {
|
||||||
int position = row.getChildren().size() - 4;
|
int position = row.getChildren().size() - 5;
|
||||||
return (Textbox) row.getChildren().get(position);
|
return (Textbox) row.getChildren().get(position);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -815,7 +847,12 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
columnHoursType.setLabel(_("Hours type"));
|
columnHoursType.setLabel(_("Hours type"));
|
||||||
columnHoursType.setSclass("hours-type-column");
|
columnHoursType.setSclass("hours-type-column");
|
||||||
columns.appendChild(columnHoursType);
|
columns.appendChild(columnHoursType);
|
||||||
|
NewDataSortableColumn columnFinsihed = new NewDataSortableColumn();
|
||||||
|
columnFinsihed.setLabel(_("Done"));
|
||||||
|
columnFinsihed.setSclass("finished-column");
|
||||||
|
columnFinsihed.setTooltiptext(_("Task finished"));
|
||||||
NewDataSortableColumn columnCode = new NewDataSortableColumn();
|
NewDataSortableColumn columnCode = new NewDataSortableColumn();
|
||||||
|
columns.appendChild(columnFinsihed);
|
||||||
columnCode.setLabel(_("Code"));
|
columnCode.setLabel(_("Code"));
|
||||||
columnCode.setSclass("code-column");
|
columnCode.setSclass("code-column");
|
||||||
columns.appendChild(columnCode);
|
columns.appendChild(columnCode);
|
||||||
|
|
@ -1197,6 +1234,29 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
row.appendChild(code);
|
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}
|
* Append a delete {@link Button} to {@link Row}
|
||||||
*
|
*
|
||||||
|
|
@ -1306,6 +1366,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
|
||||||
|
|
||||||
appendEffortDuration(row);
|
appendEffortDuration(row);
|
||||||
appendHoursType(row);
|
appendHoursType(row);
|
||||||
|
appendFinished(row);
|
||||||
appendCode(row);
|
appendCode(row);
|
||||||
appendDeleteButton(row);
|
appendDeleteButton(row);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import java.util.Set;
|
||||||
import org.apache.commons.lang.Validate;
|
import org.apache.commons.lang.Validate;
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.libreplan.business.common.IntegrationEntity;
|
import org.libreplan.business.common.IntegrationEntity;
|
||||||
|
import org.libreplan.business.common.Util;
|
||||||
import org.libreplan.business.common.daos.IConfigurationDAO;
|
import org.libreplan.business.common.daos.IConfigurationDAO;
|
||||||
import org.libreplan.business.common.entities.EntityNameEnum;
|
import org.libreplan.business.common.entities.EntityNameEnum;
|
||||||
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
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.Resource;
|
||||||
import org.libreplan.business.resources.entities.Worker;
|
import org.libreplan.business.resources.entities.Worker;
|
||||||
import org.libreplan.business.workreports.daos.IWorkReportDAO;
|
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.daos.IWorkReportTypeDAO;
|
||||||
import org.libreplan.business.workreports.entities.WorkReport;
|
import org.libreplan.business.workreports.entities.WorkReport;
|
||||||
import org.libreplan.business.workreports.entities.WorkReportLabelTypeAssigment;
|
import org.libreplan.business.workreports.entities.WorkReportLabelTypeAssigment;
|
||||||
|
|
@ -86,6 +88,9 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
||||||
@Autowired
|
@Autowired
|
||||||
private IWorkReportDAO workReportDAO;
|
private IWorkReportDAO workReportDAO;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IWorkReportLineDAO workReportLineDAO;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IOrderElementDAO orderElementDAO;
|
private IOrderElementDAO orderElementDAO;
|
||||||
|
|
||||||
|
|
@ -274,12 +279,17 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void confirmSave() throws ValidationException {
|
public void confirmSave() throws ValidationException {
|
||||||
|
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||||
|
.getOrderElementsToRecalculateTimsheetDates(
|
||||||
|
workReport.getWorkReportLines(),
|
||||||
|
deletedWorkReportLinesSet);
|
||||||
sumChargedEffortDAO.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(deletedWorkReportLinesSet);
|
sumChargedEffortDAO.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(deletedWorkReportLinesSet);
|
||||||
sumChargedEffortDAO
|
sumChargedEffortDAO
|
||||||
.updateRelatedSumChargedEffortWithWorkReportLineSet(workReport
|
.updateRelatedSumChargedEffortWithWorkReportLineSet(workReport
|
||||||
.getWorkReportLines());
|
.getWorkReportLines());
|
||||||
|
|
||||||
workReportDAO.save(workReport);
|
workReportDAO.save(workReport);
|
||||||
|
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -412,10 +422,14 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
||||||
//before deleting the report, update OrderElement.SumChargedHours
|
//before deleting the report, update OrderElement.SumChargedHours
|
||||||
try {
|
try {
|
||||||
workReportDAO.reattach(workReport);
|
workReportDAO.reattach(workReport);
|
||||||
|
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||||
|
.getOrderElementsToRecalculateTimsheetDates(null,
|
||||||
|
workReport.getWorkReportLines());
|
||||||
sumChargedEffortDAO
|
sumChargedEffortDAO
|
||||||
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport
|
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport
|
||||||
.getWorkReportLines());
|
.getWorkReportLines());
|
||||||
workReportDAO.remove(workReport.getId());
|
workReportDAO.remove(workReport.getId());
|
||||||
|
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||||
} catch (InstanceNotFoundException e) {
|
} catch (InstanceNotFoundException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
@ -644,4 +658,25 @@ public class WorkReportModel extends IntegrationEntityModel implements
|
||||||
return workReport.getWorkReportLines().iterator().next();
|
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();
|
entity.validate();
|
||||||
beforeSaving(entity);
|
beforeSaving(entity);
|
||||||
entityDAO.saveWithoutValidating(entity);
|
entityDAO.saveWithoutValidating(entity);
|
||||||
|
afterSaving(entity);
|
||||||
|
|
||||||
return null;
|
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) {
|
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.
|
* It creates an entity from a DTO.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@
|
||||||
package org.libreplan.ws.workreports.impl;
|
package org.libreplan.ws.workreports.impl;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
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.daos.IIntegrationEntityDAO;
|
||||||
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
|
||||||
import org.libreplan.business.common.exceptions.ValidationException;
|
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.daos.ISumChargedEffortDAO;
|
||||||
|
import org.libreplan.business.orders.entities.OrderElement;
|
||||||
import org.libreplan.business.workreports.daos.IWorkReportDAO;
|
import org.libreplan.business.workreports.daos.IWorkReportDAO;
|
||||||
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
|
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
|
||||||
import org.libreplan.business.workreports.entities.WorkReport;
|
import org.libreplan.business.workreports.entities.WorkReport;
|
||||||
|
|
@ -64,15 +66,14 @@ public class WorkReportServiceREST extends
|
||||||
GenericRESTService<WorkReport, WorkReportDTO> implements
|
GenericRESTService<WorkReport, WorkReportDTO> implements
|
||||||
IWorkReportService {
|
IWorkReportService {
|
||||||
|
|
||||||
|
private Set<OrderElement> orderElements;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IWorkReportDAO workReportDAO;
|
private IWorkReportDAO workReportDAO;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IWorkReportLineDAO workReportLineDAO;
|
private IWorkReportLineDAO workReportLineDAO;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IOrderElementDAO orderElementDAO;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISumChargedEffortDAO sumChargedEffortDAO;
|
private ISumChargedEffortDAO sumChargedEffortDAO;
|
||||||
|
|
||||||
|
|
@ -120,11 +121,19 @@ public class WorkReportServiceREST extends
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void beforeSaving(WorkReport entity) {
|
protected void beforeSaving(WorkReport entity) {
|
||||||
|
orderElements = sumChargedEffortDAO
|
||||||
|
.getOrderElementsToRecalculateTimsheetDates(
|
||||||
|
entity.getWorkReportLines(), null);
|
||||||
sumChargedEffortDAO
|
sumChargedEffortDAO
|
||||||
.updateRelatedSumChargedEffortWithWorkReportLineSet(entity
|
.updateRelatedSumChargedEffortWithWorkReportLineSet(entity
|
||||||
.getWorkReportLines());
|
.getWorkReportLines());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterSaving(WorkReport entity) {
|
||||||
|
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@GET
|
@GET
|
||||||
@Path("/{code}/")
|
@Path("/{code}/")
|
||||||
|
|
@ -140,10 +149,14 @@ public class WorkReportServiceREST extends
|
||||||
public Response removeWorkReport(@PathParam("code") String code) {
|
public Response removeWorkReport(@PathParam("code") String code) {
|
||||||
try {
|
try {
|
||||||
WorkReport workReport = workReportDAO.findByCode(code);
|
WorkReport workReport = workReportDAO.findByCode(code);
|
||||||
|
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||||
|
.getOrderElementsToRecalculateTimsheetDates(null,
|
||||||
|
workReport.getWorkReportLines());
|
||||||
sumChargedEffortDAO
|
sumChargedEffortDAO
|
||||||
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport
|
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport
|
||||||
.getWorkReportLines());
|
.getWorkReportLines());
|
||||||
workReportDAO.remove(workReport.getId());
|
workReportDAO.remove(workReport.getId());
|
||||||
|
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||||
return Response.ok().build();
|
return Response.ok().build();
|
||||||
} catch (InstanceNotFoundException e) {
|
} catch (InstanceNotFoundException e) {
|
||||||
return Response.status(Status.NOT_FOUND).build();
|
return Response.status(Status.NOT_FOUND).build();
|
||||||
|
|
@ -157,10 +170,14 @@ public class WorkReportServiceREST extends
|
||||||
public Response removeWorkReportLine(@PathParam("code") String code) {
|
public Response removeWorkReportLine(@PathParam("code") String code) {
|
||||||
try {
|
try {
|
||||||
WorkReportLine workReportLine = workReportLineDAO.findByCode(code);
|
WorkReportLine workReportLine = workReportLineDAO.findByCode(code);
|
||||||
|
Set<OrderElement> orderElements = sumChargedEffortDAO
|
||||||
|
.getOrderElementsToRecalculateTimsheetDates(null,
|
||||||
|
Collections.singleton(workReportLine));
|
||||||
sumChargedEffortDAO
|
sumChargedEffortDAO
|
||||||
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(new HashSet<WorkReportLine>(
|
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(new HashSet<WorkReportLine>(
|
||||||
Arrays.asList(workReportLine)));
|
Arrays.asList(workReportLine)));
|
||||||
workReportLineDAO.remove(workReportLine.getId());
|
workReportLineDAO.remove(workReportLine.getId());
|
||||||
|
sumChargedEffortDAO.recalculateTimesheetData(orderElements);
|
||||||
return Response.ok().build();
|
return Response.ok().build();
|
||||||
} catch (InstanceNotFoundException e) {
|
} catch (InstanceNotFoundException e) {
|
||||||
return Response.status(Status.NOT_FOUND).build();
|
return Response.status(Status.NOT_FOUND).build();
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 830 B |
|
|
@ -82,7 +82,7 @@
|
||||||
<vbox width="100%">
|
<vbox width="100%">
|
||||||
<tree id="tree" hflex="1" multiple="false" droppable="true"
|
<tree id="tree" hflex="1" multiple="false" droppable="true"
|
||||||
onDrop="treeController.move(self, event.dragged)"
|
onDrop="treeController.move(self, event.dragged)"
|
||||||
onSelect="treeController.updateControlButtons(event)"
|
onSelect="treeController.updateControlButtons()"
|
||||||
mold="paging" pageSize="25"
|
mold="paging" pageSize="25"
|
||||||
sclass="orderTree"
|
sclass="orderTree"
|
||||||
zclass="z-dottree">
|
zclass="z-dottree">
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,24 @@ div.box.limiting-unassigned {
|
||||||
margin-top: 1px;
|
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 {
|
.completion2 {
|
||||||
width: 0%;
|
width: 0%;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@
|
||||||
<hbox align="bottom" sclass="add-resources-or-criteria">
|
<hbox align="bottom" sclass="add-resources-or-criteria">
|
||||||
<newAllocationSelectorCombo id="newAllocationSelectorCombo" behaviour="NON_LIMITING"/>
|
<newAllocationSelectorCombo id="newAllocationSelectorCombo" behaviour="NON_LIMITING"/>
|
||||||
<button label="${i18n:_('Add')}" onClick="allocationController.onSelectWorkers(newAllocationSelectorCombo)"
|
<button label="${i18n:_('Add')}" onClick="allocationController.onSelectWorkers(newAllocationSelectorCombo)"
|
||||||
disabled="@{allocationController.anyManual}" />
|
disabled="@{allocationController.isAnyManualOrTaskUpdatedFromTimesheets}" />
|
||||||
<button id="advancedSearchButton" label="${i18n:_('Advanced search')}" onClick="allocationController.goToAdvancedSearch()" />
|
<button id="advancedSearchButton" label="${i18n:_('Advanced search')}" onClick="allocationController.goToAdvancedSearch()" />
|
||||||
<checkbox id="extendedViewCheckbox" label="${i18n:_('Extended view')}"
|
<checkbox id="extendedViewCheckbox" label="${i18n:_('Extended view')}"
|
||||||
onCheck="allocationController.onCheckExtendedView()"/>
|
onCheck="allocationController.onCheckExtendedView()"/>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.listWorkReportLines .operations-column {
|
.listWorkReportLines .operations-column {
|
||||||
width: 40px;
|
width: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listWorkReportLines .resource-column {
|
.listWorkReportLines .resource-column {
|
||||||
|
|
@ -25,6 +25,10 @@
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.listWorkReportLines .finished-column {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.listWorkReportLines .order-code-column {
|
.listWorkReportLines .order-code-column {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue