diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/Util.java b/libreplan-business/src/main/java/org/libreplan/business/common/Util.java
new file mode 100644
index 000000000..769ad097a
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/Util.java
@@ -0,0 +1,40 @@
+/*
+ * 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 .
+ */
+
+package org.libreplan.business.common;
+
+import java.util.Collection;
+
+/**
+ * Utilities class.
+ * @author Manuel Rego Casasnovas
+ */
+public class Util {
+
+ public static boolean contains(Collection extends BaseEntity> collection,
+ BaseEntity entity) {
+ for (BaseEntity each : collection) {
+ if (each.getId().equals(entity.getId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java
index 5af312036..9e22fcab8 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java
@@ -69,4 +69,7 @@ public interface IWorkReportLineDAO extends
Pair findMinAndMaxDatesByOrderElement(
OrderElement orderElement);
+ List findByOrderElementNotInWorkReportAnotherTransaction(
+ OrderElement orderElement, WorkReport workReport);
+
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java
index 27036e0fc..ebafb9ee2 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java
@@ -40,6 +40,7 @@ import org.libreplan.business.workreports.entities.WorkReportLine;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
@@ -168,4 +169,24 @@ public class WorkReportLineDAO extends IntegrationEntityDAO
return Pair.create(min, max);
}
+ @Override
+ @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ public List findByOrderElementNotInWorkReportAnotherTransaction(
+ OrderElement orderElement, WorkReport workReport) {
+ return findByOrderElementNotInWorkReport(orderElement, workReport);
+ }
+
+ @SuppressWarnings("unchecked")
+ private List 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) criteria.list();
+ }
+
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java
index b1d501fba..caca48337 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReport.java
@@ -36,6 +36,7 @@ import org.hibernate.validator.Valid;
import org.joda.time.LocalDate;
import org.libreplan.business.common.IntegrationEntity;
import org.libreplan.business.common.Registry;
+import org.libreplan.business.common.Util;
import org.libreplan.business.common.entities.EntitySequence;
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
@@ -538,4 +539,20 @@ public class WorkReport extends IntegrationEntity implements
return false;
}
+ @AssertTrue(message = "the same task is marked as finished by more than one timesheet line")
+ public boolean checkConstraintSameOrderElementFinishedBySeveralWorkReportLines() {
+ Set finishedOrderElements = new HashSet();
+
+ for (WorkReportLine line : workReportLines) {
+ if (line.isFinished()) {
+ if (Util.contains(finishedOrderElements, line.getOrderElement())) {
+ return false;
+ }
+ finishedOrderElements.add(line.getOrderElement());
+ }
+ }
+
+ return true;
+ }
+
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java
index a33db71ab..c291fecc6 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/entities/WorkReportLine.java
@@ -23,6 +23,7 @@ package org.libreplan.business.workreports.entities;
import java.util.Date;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
@@ -563,4 +564,22 @@ public class WorkReportLine extends IntegrationEntity implements Comparable,
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 lines = Registry.getWorkReportLineDAO()
+ .findByOrderElementNotInWorkReportAnotherTransaction(
+ orderElement, workReport);
+ for (WorkReportLine line : lines) {
+ if (line.isFinished()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java
index 38144517c..15ee26fe8 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/web/workreports/WorkReportCRUDController.java
@@ -277,7 +277,9 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
for (InvalidValue invalidValue : e.getInvalidValues()) {
Object value = invalidValue.getBean();
if (value instanceof WorkReport) {
- validateWorkReport();
+ if (validateWorkReport()) {
+ messagesForUser.showInvalidValues(e);
+ }
}
if (value instanceof WorkReportLine) {
WorkReportLine workReportLine = (WorkReportLine) invalidValue.getBean();
@@ -332,6 +334,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
_("cannot be empty"));
return false;
}
+
return true;
}
@@ -456,6 +459,16 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
}
return false;
}
+
+ if (!workReportLine.checkConstraintOrderElementFinishedInAnotherWorkReport()) {
+ Checkbox checkboxFinished = getFinished(row);
+ if (checkboxFinished != null) {
+ String message = _("task is already marked as finished in another timesheet");
+ showInvalidMessage(checkboxFinished, message);
+ }
+ return false;
+ }
+
return true;
}
@@ -470,7 +483,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
*/
private Timebox getTimeboxFinish(Row row) {
try {
- int position = row.getChildren().size() - 5;
+ int position = row.getChildren().size() - 6;
return (Timebox) row.getChildren().get(position);
} catch (Exception e) {
return null;
@@ -484,7 +497,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
*/
private Timebox getTimeboxStart(Row row) {
try {
- int position = row.getChildren().size() - 6;
+ int position = row.getChildren().size() - 7;
return (Timebox) row.getChildren().get(position);
} catch (Exception e) {
return null;
@@ -498,13 +511,28 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
*/
private Listbox getTypeOfHours(Row row) {
try {
- int position = row.getChildren().size() - 3;
+ int position = row.getChildren().size() - 4;
return (Listbox) row.getChildren().get(position);
} catch (Exception e) {
return null;
}
}
+
+ /**
+ * Locates {@link Checkbox} finished in {@link Row}
+ * @param row
+ * @return
+ */
+ private Checkbox getFinished(Row row) {
+ try {
+ int position = row.getChildren().size() - 3;
+ return (Checkbox) row.getChildren().get(position);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
/**
* Locates {@link Texbox} code in {@link Row}
* @param row
@@ -527,7 +555,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
*/
private Textbox getEffort(Row row) {
try {
- int position = row.getChildren().size() - 4;
+ int position = row.getChildren().size() - 5;
return (Textbox) row.getChildren().get(position);
} catch (Exception e) {
return null;