/* * 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.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; 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.Order; import org.libreplan.business.orders.entities.OrderElement; import org.libreplan.business.orders.entities.OrderLineGroup; import org.libreplan.business.orders.entities.SumChargedEffort; import org.libreplan.business.util.Pair; 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; import org.springframework.transaction.annotation.Transactional; /** * 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; @Autowired private IOrderDAO orderDAO; @Autowired private IOrderElementDAO orderElementDAO; private Map<OrderElement, SumChargedEffort> mapSumChargedEfforts; @Override public void updateRelatedSumChargedEffortWithWorkReportLineSet( Set<WorkReportLine> workReportLineSet) { resetMapSumChargedEfforts(); for (WorkReportLine workReportLine : workReportLineSet) { updateRelatedSumChargedEffortWithAddedOrModifiedWorkReportLine(workReportLine); } } private void updateRelatedSumChargedEffortWithAddedOrModifiedWorkReportLine( final WorkReportLine workReportLine) { boolean increase = true; boolean sameOrderElement = true; EffortDuration effort = workReportLine.getEffort(); OrderElement orderElement = workReportLine.getOrderElement(); EffortDuration previousEffort = null; OrderElement previousOrderElement = null; if (!workReportLine.isNewObject()) { Pair<EffortDuration, OrderElement> previous = transactionService .runOnAnotherTransaction(new IOnTransaction<Pair<EffortDuration, OrderElement>>() { @Override public Pair<EffortDuration, OrderElement> execute() { try { WorkReportLine line = workReportLineDAO .find(workReportLine.getId()); OrderElement orderElement = line .getOrderElement(); forceLoadParents(orderElement); return Pair.create(line.getEffort(), orderElement); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } } private void forceLoadParents(OrderElement orderElement) { OrderLineGroup parent = orderElement.getParent(); if (parent != null) { forceLoadParents(parent); } } }); previousEffort = previous.getFirst(); previousOrderElement = previous.getSecond(); sameOrderElement = orderElement.getId().equals( previousOrderElement.getId()); } if (sameOrderElement) { if (previousEffort != null) { if (effort.compareTo(previousEffort) >= 0) { effort = effort.minus(previousEffort); } else { increase = false; effort = previousEffort.minus(effort); } } if (!effort.isZero()) { if (increase) { addDirectChargedEffort(orderElement, effort); } else { substractDirectChargedEffort(orderElement, effort); } } } else { substractDirectChargedEffort(previousOrderElement, previousEffort); addDirectChargedEffort(orderElement, effort); } } private void addDirectChargedEffort(OrderElement orderElement, EffortDuration effort) { SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); sumChargedEffort.addDirectChargedEffort(effort); save(sumChargedEffort); addInirectChargedEffortRecursively(orderElement.getParent(), effort); } private void addInirectChargedEffortRecursively(OrderElement orderElement, EffortDuration effort) { if (orderElement != null) { SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); sumChargedEffort.addIndirectChargedEffort(effort); save(sumChargedEffort); addInirectChargedEffortRecursively(orderElement.getParent(), effort); } } @Override public void updateRelatedSumChargedEffortWithDeletedWorkReportLineSet( Set<WorkReportLine> workReportLineSet) { resetMapSumChargedEfforts(); for (WorkReportLine workReportLine : workReportLineSet) { updateRelatedSumChargedEffortWithDeletedWorkReportLine(workReportLine); } } private void resetMapSumChargedEfforts() { mapSumChargedEfforts = new HashMap<OrderElement, SumChargedEffort>(); } 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 = getByOrderElement(orderElement); sumChargedEffort.subtractDirectChargedEffort(effort); save(sumChargedEffort); substractInirectChargedEffortRecursively(orderElement.getParent(), effort); } private void substractInirectChargedEffortRecursively(OrderElement orderElement, EffortDuration effort) { if (orderElement != null) { SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); sumChargedEffort.subtractIndirectChargedEffort(effort); save(sumChargedEffort); substractInirectChargedEffortRecursively(orderElement.getParent(), effort); } } private SumChargedEffort getByOrderElement(OrderElement orderElement) { SumChargedEffort sumChargedEffort = mapSumChargedEfforts .get(orderElement); if (sumChargedEffort == null) { sumChargedEffort = findByOrderElement(orderElement); if (sumChargedEffort == null) { sumChargedEffort = SumChargedEffort.create(orderElement); } mapSumChargedEfforts.put(orderElement, sumChargedEffort); } return sumChargedEffort; } @Override public SumChargedEffort findByOrderElement(OrderElement orderElement) { return (SumChargedEffort) getSession().createCriteria(getEntityClass()) .add(Restrictions.eq("orderElement", orderElement)) .uniqueResult(); } @Override @Transactional public void recalculateSumChargedEfforts(Long orderId) { try { Order order = orderDAO.find(orderId); resetMapSumChargedEfforts(); resetSumChargedEffort(order); calculateDirectChargedEffort(order); calculateTimesheetData(order); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } } private void resetSumChargedEffort(OrderElement orderElement) { SumChargedEffort sumChargedEffort = getByOrderElement(orderElement); sumChargedEffort.reset(); for (OrderElement each : orderElement.getChildren()) { resetSumChargedEffort(each); } } private void calculateDirectChargedEffort(OrderElement orderElement) { for (OrderElement each : orderElement.getChildren()) { calculateDirectChargedEffort(each); } EffortDuration effort = EffortDuration.zero(); for (WorkReportLine line : workReportLineDAO .findByOrderElement(orderElement)) { effort = effort.plus(line.getEffort()); } 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); } }