Create SumChargedEffort while saving the work reports

FEA: ItEr76S14ConcurrencyProblemWorkReports
This commit is contained in:
Manuel Rego Casasnovas 2012-03-30 16:01:10 +02:00
parent 7f5184b78d
commit abc0e0fe66
7 changed files with 283 additions and 190 deletions

View file

@ -30,7 +30,6 @@ import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.templates.entities.OrderElementTemplate;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workreports.entities.WorkReportLine;
/**
* Contract for {@link OrderElementDAO}
@ -110,12 +109,6 @@ public interface IOrderElementDAO extends IIntegrationEntityDAO<OrderElement> {
boolean isAlreadyInUseThisOrAnyOfItsChildren(OrderElement orderElement);
void updateRelatedSumChargedEffortWithWorkReportLineSet(
Set<WorkReportLine> workReportLineSet) throws InstanceNotFoundException;
void updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(
Set<WorkReportLine> workReportLineSet) throws InstanceNotFoundException;
/**
* Returns codes in DB searching in all order elements but excluding orderElements
*

View file

@ -0,0 +1,68 @@
/*
* 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.orders.daos;
import java.util.Set;
import org.libreplan.business.common.daos.IGenericDAO;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.SumChargedEffort;
import org.libreplan.business.workreports.entities.WorkReportLine;
/**
* Contract for {@link SumChargedEffortDAO}
*
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
public interface ISumChargedEffortDAO extends
IGenericDAO<SumChargedEffort, Long> {
/**
* Update the {@link SumChargedEffort} objects with the changes in the
* {@link WorkReportLine} set passed as argument. <br />
*
* If the {@link WorkReportLine} is new, the effort is added to the
* corresponding {@link SumChargedEffort}. Otherwise, the difference of
* effort is added or subtracted as required. <br />
*
* If there is not {@link SumChargedEffort} associated to the
* {@link OrderElement} yet, it is created on demand.
*
* @param workReportLineSet
*/
void updateRelatedSumChargedEffortWithWorkReportLineSet(
Set<WorkReportLine> workReportLineSet);
/**
* Update the {@link SumChargedEffort} objects removing the values from the
* {@link WorkReportLine} set passed as argument. <br />
*
* If the {@link WorkReportLine} is new, nothing is substracted. Otherwise,
* the actual value saved in the database is substracted and not the one
* coming in the objects passed.
*
* @param workReportLineSet
*/
void updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(
Set<WorkReportLine> workReportLineSet);
SumChargedEffort findByOrderElement(OrderElement orderElement);
}

View file

@ -27,7 +27,6 @@ import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -39,7 +38,6 @@ import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.IOnTransaction;
import org.libreplan.business.common.daos.IntegrationEntityDAO;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.orders.entities.OrderElement;
@ -422,149 +420,6 @@ public class OrderElementDAO extends IntegrationEntityDAO<OrderElement>
return false;
}
private void updateRelatedSumChargedEffortWithWorkReportLine(
final WorkReportLine workReportLine,
Map<Long, EffortDuration> relationOrderElementIdAndIndirectChargedEffort)
throws InstanceNotFoundException {
OrderElement orderElement = find(workReportLine.getOrderElement().getId());
EffortDuration effort = workReportLine.getEffort();
EffortDuration differenceOfEffort;
boolean mustBeAdded = true;
if(workReportLine.isNewObject()) {
differenceOfEffort = effort;
}
else {
//the line already exists, we have to get the old value for numHours
EffortDuration oldEffort = transactionService
.runOnAnotherTransaction(new IOnTransaction<EffortDuration>() {
@Override
public EffortDuration execute() {
try {
return workReportLineDAO.find(
workReportLine.getId()).getEffort();
} catch (InstanceNotFoundException e) {
// this shouldn't happen, as workReportLine.isNewObject()
// returns false, indicating this object already exists
return workReportLine.getEffort();
}
}
});
BigDecimal differenceEffortNumeric = effort
.toHoursAsDecimalWithScale(2).subtract(
oldEffort.toHoursAsDecimalWithScale(2));
mustBeAdded = differenceEffortNumeric.compareTo(BigDecimal.ZERO) >= 0;
if (mustBeAdded)
differenceOfEffort = effort.minus(oldEffort);
else
differenceOfEffort = oldEffort.minus(effort);
}
if (mustBeAdded)
orderElement.getSumChargedEffort().addDirectChargedEffort(
differenceOfEffort);
else
orderElement.getSumChargedEffort().subtractDirectChargedEffort(
differenceOfEffort);
save(orderElement);
updateIndirectChargedEffortRecursively(orderElement.getParent(),
differenceOfEffort,
relationOrderElementIdAndIndirectChargedEffort);
workReportLineDAO.save(workReportLine);
}
private void updateRelatedSumChargedEffortWithDeletedWorkReportLine(
final WorkReportLine workReportLine,
Map<Long, EffortDuration> relationOrderElementIdAndIndirectChargedHours)
throws InstanceNotFoundException {
if(workReportLine.isNewObject()) {
//if the line hadn't been saved, we have nothing to update
return;
}
// Refresh data from database, because of changes not saved are not
// useful for the following operations
sessionFactory.getCurrentSession().refresh(workReportLine);
OrderElement orderElement = find(workReportLine.getOrderElement().getId());
EffortDuration effort = workReportLine.getEffort();
orderElement.getSumChargedEffort().subtractDirectChargedEffort(effort);
save(orderElement);
updateIndirectChargedEffortRecursively(orderElement.getParent(),
effort,
relationOrderElementIdAndIndirectChargedHours);
}
private void updateIndirectChargedEffortRecursively(
OrderElement orderElement,
EffortDuration effortDelta,
Map<Long, EffortDuration> relationOrderElementIdAndIndirectChargedEffort) {
if(orderElement != null) {
Long id = orderElement.getId();
if (relationOrderElementIdAndIndirectChargedEffort.containsKey(id)) {
EffortDuration previous = relationOrderElementIdAndIndirectChargedEffort
.get(id);
relationOrderElementIdAndIndirectChargedEffort.put(id,
previous.plus(effortDelta));
}
else {
relationOrderElementIdAndIndirectChargedEffort.put(id,
effortDelta);
}
updateIndirectChargedEffortRecursively(orderElement.getParent(),
effortDelta, relationOrderElementIdAndIndirectChargedEffort);
}
}
private void updateIndirectChargedEffortWithMap(
Map<Long, EffortDuration> relationOrderElementIdAndIndirectChargedEffort,
boolean sum)
throws InstanceNotFoundException {
for (Long id : relationOrderElementIdAndIndirectChargedEffort.keySet()) {
OrderElement orderElement = find(id);
if (sum) {
orderElement.getSumChargedEffort().addIndirectChargedEffort(
relationOrderElementIdAndIndirectChargedEffort.get(id));
} else {
orderElement.getSumChargedEffort()
.subtractIndirectChargedEffort(
relationOrderElementIdAndIndirectChargedEffort
.get(id));
}
save(orderElement);
}
}
@Override
@Transactional
public void updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(
Set<WorkReportLine> workReportLineSet) throws InstanceNotFoundException {
Map<Long, EffortDuration> relationOrderElementIdAndIndirectChargedEffort = new Hashtable<Long, EffortDuration>();
for(WorkReportLine line : workReportLineSet) {
updateRelatedSumChargedEffortWithDeletedWorkReportLine(line,
relationOrderElementIdAndIndirectChargedEffort);
}
updateIndirectChargedEffortWithMap(
relationOrderElementIdAndIndirectChargedEffort, false);
}
@Override
@Transactional
public void updateRelatedSumChargedEffortWithWorkReportLineSet(
Set<WorkReportLine> workReportLineSet) throws InstanceNotFoundException {
Map<Long, EffortDuration> relationOrderElementIdAndIndirectChargedEffort = new Hashtable<Long, EffortDuration>();
for(WorkReportLine line : workReportLineSet) {
updateRelatedSumChargedEffortWithWorkReportLine(line,
relationOrderElementIdAndIndirectChargedEffort);
}
updateIndirectChargedEffortWithMap(
relationOrderElementIdAndIndirectChargedEffort, true);
}
@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true)

View file

@ -0,0 +1,186 @@
/*
* 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.orders.daos;
import java.util.Set;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.IOnTransaction;
import org.libreplan.business.common.daos.GenericDAOHibernate;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.SumChargedEffort;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
import org.libreplan.business.workreports.entities.WorkReportLine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
/**
* DAO for {@link SumChargedEffort}.
*
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
@Repository
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class SumChargedEffortDAO extends
GenericDAOHibernate<SumChargedEffort, Long> implements
ISumChargedEffortDAO {
@Autowired
private SessionFactory sessionFactory;
@Autowired
private IWorkReportLineDAO workReportLineDAO;
@Autowired
private IAdHocTransactionService transactionService;
@Override
public void updateRelatedSumChargedEffortWithWorkReportLineSet(
Set<WorkReportLine> workReportLineSet) {
for (WorkReportLine workReportLine : workReportLineSet) {
updateRelatedSumChargedEffortWithAddedOrModifiedWorkReportLine(workReportLine);
}
}
private void updateRelatedSumChargedEffortWithAddedOrModifiedWorkReportLine(
final WorkReportLine workReportLine) {
boolean increase = true;
EffortDuration effort = workReportLine.getEffort();
if (!workReportLine.isNewObject()) {
EffortDuration previousEffort = transactionService
.runOnAnotherTransaction(new IOnTransaction<EffortDuration>() {
@Override
public EffortDuration execute() {
try {
return workReportLineDAO.find(
workReportLine.getId()).getEffort();
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
}
});
if (effort.compareTo(previousEffort) >= 0) {
effort = effort.minus(previousEffort);
} else {
increase = false;
effort = previousEffort.minus(effort);
}
}
if (!effort.isZero()) {
if (increase) {
addDirectChargedEffort(workReportLine.getOrderElement(), effort);
} else {
substractDirectChargedEffort(workReportLine.getOrderElement(),
effort);
}
}
}
private void addDirectChargedEffort(OrderElement orderElement,
EffortDuration effort) {
SumChargedEffort sumChargedEffort = findByOrderElement(orderElement);
if (sumChargedEffort == null) {
sumChargedEffort = SumChargedEffort.create(orderElement);
}
sumChargedEffort.addDirectChargedEffort(effort);
save(sumChargedEffort);
addInirectChargedEffortRecursively(orderElement.getParent(), effort);
}
private void addInirectChargedEffortRecursively(OrderElement orderElement,
EffortDuration effort) {
if (orderElement != null) {
SumChargedEffort sumChargedEffort = findByOrderElement(orderElement);
if (sumChargedEffort == null) {
sumChargedEffort = SumChargedEffort.create(orderElement);
}
sumChargedEffort.addIndirectChargedEffort(effort);
save(sumChargedEffort);
addInirectChargedEffortRecursively(orderElement.getParent(), effort);
}
}
@Override
public void updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(
Set<WorkReportLine> workReportLineSet) {
for (WorkReportLine workReportLine : workReportLineSet) {
updateRelatedSumChargedEffortWithDeletedWorkReportLine(workReportLine);
}
}
private void updateRelatedSumChargedEffortWithDeletedWorkReportLine(
WorkReportLine workReportLine) {
if (workReportLine.isNewObject()) {
// If the line hasn't been saved, we have nothing to update
return;
}
// Refresh data from database, because of changes not saved are not
// useful for the following operations
sessionFactory.getCurrentSession().refresh(workReportLine);
substractDirectChargedEffort(workReportLine.getOrderElement(),
workReportLine.getEffort());
}
private void substractDirectChargedEffort(OrderElement orderElement,
EffortDuration effort) {
SumChargedEffort sumChargedEffort = findByOrderElement(orderElement);
sumChargedEffort.subtractDirectChargedEffort(effort);
save(sumChargedEffort);
substractInirectChargedEffortRecursively(orderElement.getParent(),
effort);
}
private void substractInirectChargedEffortRecursively(OrderElement orderElement,
EffortDuration effort) {
if (orderElement != null) {
SumChargedEffort sumChargedEffort = findByOrderElement(orderElement);
sumChargedEffort.subtractIndirectChargedEffort(effort);
save(sumChargedEffort);
substractInirectChargedEffortRecursively(orderElement.getParent(), effort);
}
}
@Override
public SumChargedEffort findByOrderElement(OrderElement orderElement) {
return (SumChargedEffort) getSession().createCriteria(getEntityClass())
.add(Restrictions.eq("orderElement", orderElement))
.uniqueResult();
}
}

View file

@ -41,8 +41,12 @@ public class SumChargedEffort extends BaseEntity {
protected SumChargedEffort() {}
public static SumChargedEffort create() {
return create(new SumChargedEffort());
private SumChargedEffort(OrderElement orderElement) {
this.orderElement = orderElement;
}
public static SumChargedEffort create(OrderElement orderElement) {
return create(new SumChargedEffort(orderElement));
}
public OrderElement getOrderElement() {

View file

@ -3,7 +3,7 @@
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
* Copyright (C) 2010-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
@ -43,14 +43,13 @@ import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.labels.daos.ILabelDAO;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.labels.entities.LabelType;
import org.libreplan.business.orders.daos.IOrderDAO;
import org.libreplan.business.orders.daos.IOrderElementDAO;
import org.libreplan.business.orders.daos.ISumChargedEffortDAO;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.OrderLineGroup;
import org.libreplan.business.resources.daos.IWorkerDAO;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.scenarios.IScenarioManager;
import org.libreplan.business.workreports.daos.IWorkReportDAO;
import org.libreplan.business.workreports.daos.IWorkReportTypeDAO;
import org.libreplan.business.workreports.entities.WorkReport;
@ -70,8 +69,10 @@ import org.zkoss.ganttz.IPredicate;
/**
* Model for UI operations related to {@link WorkReport}.
*
* @author Diego Pino García <dpino@igalia.com>
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@ -88,24 +89,21 @@ public class WorkReportModel extends IntegrationEntityModel implements
@Autowired
private IOrderElementDAO orderElementDAO;
@Autowired
private IOrderDAO orderDAO;
@Autowired
private IWorkerDAO workerDAO;
@Autowired
private ILabelDAO labelDAO;
@Autowired
private IScenarioManager scenarioManager;
@Autowired
private IConfigurationDAO configurationDAO;
@Autowired
private ITypeOfWorkHoursDAO typeOfWorkHoursDAO;
@Autowired
private ISumChargedEffortDAO sumChargedEffortDAO;
private WorkReportType workReportType;
private WorkReport workReport;
@ -114,10 +112,6 @@ public class WorkReportModel extends IntegrationEntityModel implements
private boolean listingQuery = false;
private Map<DescriptionValue, DescriptionField> mapDescriptonValues = new HashMap<DescriptionValue, DescriptionField>();
private Map<Label, Integer> mapLabels = new HashMap<Label, Integer>();
private static final Map<LabelType, List<Label>> mapLabelTypes = new HashMap<LabelType, List<Label>>();
private List<WorkReportDTO> listWorkReportDTOs = new ArrayList<WorkReportDTO>();
@ -281,16 +275,11 @@ public class WorkReportModel extends IntegrationEntityModel implements
@Override
@Transactional
public void confirmSave() throws ValidationException {
try {
orderElementDAO
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(
deletedWorkReportLinesSet);
orderElementDAO
.updateRelatedSumChargedEffortWithWorkReportLineSet(
workReport.getWorkReportLines());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);
}
sumChargedEffortDAO.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(deletedWorkReportLinesSet);
sumChargedEffortDAO
.updateRelatedSumChargedEffortWithWorkReportLineSet(workReport
.getWorkReportLines());
workReportDAO.save(workReport);
}
@ -423,9 +412,9 @@ public class WorkReportModel extends IntegrationEntityModel implements
public void remove(WorkReport workReport) {
//before deleting the report, update OrderElement.SumChargedHours
try {
orderElementDAO
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(
workReport.getWorkReportLines());
sumChargedEffortDAO
.updateRelatedSumChargedEffortWithDeletedWorkReportLineSet(workReport
.getWorkReportLines());
workReportDAO.remove(workReport.getId());
} catch (InstanceNotFoundException e) {
throw new RuntimeException(e);

View file

@ -3,7 +3,7 @@
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
* Copyright (C) 2010-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
@ -34,6 +34,7 @@ import org.libreplan.business.common.daos.IIntegrationEntityDAO;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.orders.daos.IOrderElementDAO;
import org.libreplan.business.orders.daos.ISumChargedEffortDAO;
import org.libreplan.business.workreports.daos.IWorkReportDAO;
import org.libreplan.business.workreports.entities.WorkReport;
import org.libreplan.ws.common.api.InstanceConstraintViolationsListDTO;
@ -64,6 +65,9 @@ public class WorkReportServiceREST extends
@Autowired
private IOrderElementDAO orderElementDAO;
@Autowired
private ISumChargedEffortDAO sumChargedEffortDAO;
@Override
@GET
@Transactional(readOnly = true)
@ -143,15 +147,9 @@ public class WorkReportServiceREST extends
* Validate and save (insert or update) the entity.
*/
entity.validate();
try {
orderElementDAO
.updateRelatedSumChargedEffortWithWorkReportLineSet(
entity.getWorkReportLines());
} catch (InstanceNotFoundException e) {
//should never happen, because the entity has been
//validated before, so we are sure the lines refer
//to existing order elements
}
sumChargedEffortDAO
.updateRelatedSumChargedEffortWithWorkReportLineSet(entity
.getWorkReportLines());
entityDAO.saveWithoutValidating(entity);
return null;