Implement constraint to check that only one WorkReportLine per task is finished

FEA: ItEr77S12AdaptPlanningAccordingTimesheets
This commit is contained in:
Manuel Rego Casasnovas 2012-11-02 09:31:32 +01:00
parent a8ba46e505
commit 6c3a915b8d
6 changed files with 133 additions and 5 deletions

View file

@ -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 <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 contains(Collection<? extends BaseEntity> collection,
BaseEntity entity) {
for (BaseEntity each : collection) {
if (each.getId().equals(entity.getId())) {
return true;
}
}
return false;
}
}

View file

@ -69,4 +69,7 @@ public interface IWorkReportLineDAO extends
Pair<Date, Date> findMinAndMaxDatesByOrderElement( Pair<Date, Date> findMinAndMaxDatesByOrderElement(
OrderElement orderElement); OrderElement orderElement);
List<WorkReportLine> findByOrderElementNotInWorkReportAnotherTransaction(
OrderElement orderElement, WorkReport workReport);
} }

View file

@ -40,6 +40,7 @@ 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;
/** /**
@ -168,4 +169,24 @@ public class WorkReportLineDAO extends IntegrationEntityDAO<WorkReportLine>
return Pair.create(min, max); 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();
}
} }

View file

@ -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;
}
} }

View file

@ -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;
@ -563,4 +564,22 @@ public class WorkReportLine extends IntegrationEntity implements Comparable,
this.finished = 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;
}
} }

View file

@ -277,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();
@ -332,6 +334,7 @@ public class WorkReportCRUDController extends GenericForwardComposer implements
_("cannot be empty")); _("cannot be empty"));
return false; return false;
} }
return true; return true;
} }
@ -456,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;
} }
@ -470,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;
@ -484,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;
@ -498,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
@ -527,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;