From 6c3a915b8d0c6ef4e001657e182ced7cbd4149a5 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Fri, 2 Nov 2012 09:31:32 +0100 Subject: [PATCH] Implement constraint to check that only one WorkReportLine per task is finished FEA: ItEr77S12AdaptPlanningAccordingTimesheets --- .../org/libreplan/business/common/Util.java | 40 +++++++++++++++++++ .../workreports/daos/IWorkReportLineDAO.java | 3 ++ .../workreports/daos/WorkReportLineDAO.java | 21 ++++++++++ .../workreports/entities/WorkReport.java | 17 ++++++++ .../workreports/entities/WorkReportLine.java | 19 +++++++++ .../workreports/WorkReportCRUDController.java | 38 +++++++++++++++--- 6 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 libreplan-business/src/main/java/org/libreplan/business/common/Util.java 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 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;