/* * This file is part of LibrePlan * * Copyright (C) 2012 WirelessGalicia, 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.math.BigDecimal; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.hibernate.Hibernate; 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.expensesheet.daos.IExpenseSheetLineDAO; import org.libreplan.business.expensesheet.entities.ExpenseSheetLine; 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.SumExpenses; 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; /** * Contract for {@link SumExpensesDAO}. * * @author Susana Montes Pedreira <smontes@wirelessgalicia.com> */ @Repository @Scope(BeanDefinition.SCOPE_SINGLETON) public class SumExpensesDAO extends GenericDAOHibernate<SumExpenses, Long> implements ISumExpensesDAO { @Autowired private SessionFactory sessionFactory; @Autowired private IExpenseSheetLineDAO expenseSheetLineDAO; @Autowired private IAdHocTransactionService transactionService; @Autowired private IOrderDAO orderDAO; private Map<OrderElement, SumExpenses> mapSumExpenses; @Override public void updateRelatedSumExpensesWithExpenseSheetLineSet(Set<ExpenseSheetLine> expenseSheetLineSet) { resetMapSumExpenses(); for (ExpenseSheetLine expenseSheetLine : expenseSheetLineSet) { updateRelatedSumExpensesWithAddedOrModifiedExpenseSheetLine(expenseSheetLine); } } private void updateRelatedSumExpensesWithAddedOrModifiedExpenseSheetLine(final ExpenseSheetLine expenseSheetLine) { boolean increase = true; BigDecimal value = expenseSheetLine.getValue(); if (!expenseSheetLine.isNewObject()) { BigDecimal previousValue = transactionService.runOnAnotherTransaction(new IOnTransaction<BigDecimal>() { @Override public BigDecimal execute() { try { return expenseSheetLineDAO.find(expenseSheetLine.getId()) .getValue(); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } } }); boolean isTaskDifferent = updateRelatedSumExpensesIfTheAssociatedTaskIsDifferent(previousValue, expenseSheetLine); if (!isTaskDifferent) { if (value.compareTo(previousValue) >= 0) { value = value.subtract(previousValue); } else { increase = false; value = previousValue.subtract(value); } } } if (value != null && value.compareTo(BigDecimal.ZERO) > 0) { if (increase) { addDirectExpenses(expenseSheetLine.getOrderElement(), value); } else { substractDirectExpenses(expenseSheetLine.getOrderElement(), value); } } } private boolean updateRelatedSumExpensesIfTheAssociatedTaskIsDifferent(BigDecimal previousValue, final ExpenseSheetLine expenseSheetLine) { final OrderElement task = expenseSheetLine.getOrderElement(); OrderElement previousTask = transactionService.runOnAnotherTransaction(new IOnTransaction<OrderElement>() { @Override public OrderElement execute() { try { OrderElement previousTask = expenseSheetLineDAO .find(expenseSheetLine.getId()) .getOrderElement(); if (task.getId().compareTo(previousTask.getId()) != 0) { initializeOrderElement(previousTask); } return previousTask; } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } } }); if (task.getId().compareTo(previousTask.getId()) != 0) { substractDirectExpenses(previousTask, previousValue); return true; } return false; } private void initializeOrderElement(OrderElement orderElement) { Hibernate.initialize(orderElement); initializeOrder(orderElement); } private void initializeOrder(OrderElement orderElement) { OrderLineGroup parent = orderElement.getParent(); while (parent != null) { Hibernate.initialize(parent); parent = parent.getParent(); } } private void addDirectExpenses(OrderElement orderElement, BigDecimal value) { SumExpenses sumExpenses = getByOrderElement(orderElement); if (sumExpenses == null) { sumExpenses = SumExpenses.create(orderElement); } sumExpenses.addDirectExpenses(value); save(sumExpenses); addIndirectExpensesRecursively(orderElement.getParent(), value); } private void addIndirectExpensesRecursively(OrderElement orderElement, BigDecimal value) { if (orderElement != null) { SumExpenses sumExpenses = getByOrderElement(orderElement); if (sumExpenses == null) { sumExpenses = SumExpenses.create(orderElement); } sumExpenses.addIndirectExpenses(value); save(sumExpenses); addIndirectExpensesRecursively(orderElement.getParent(), value); } } @Override public void updateRelatedSumExpensesWithDeletedExpenseSheetLineSet(Set<ExpenseSheetLine> expenseSheetLineSet) { resetMapSumExpenses(); for (ExpenseSheetLine expenseSheetLine : expenseSheetLineSet) { updateRelatedSumExpensesWithDeletedExpenseSheetLine(expenseSheetLine); } } private void resetMapSumExpenses() { mapSumExpenses = new HashMap<>(); } private void updateRelatedSumExpensesWithDeletedExpenseSheetLine(ExpenseSheetLine expenseSheetLine) { if (expenseSheetLine.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(expenseSheetLine); substractDirectExpenses(expenseSheetLine.getOrderElement(), expenseSheetLine.getValue()); } private void substractDirectExpenses(OrderElement orderElement, BigDecimal value) { SumExpenses sumExpenses = getByOrderElement(orderElement); sumExpenses.subtractDirectExpenses(value); save(sumExpenses); substractIndirectExpensesRecursively(orderElement.getParent(), value); } private void substractIndirectExpensesRecursively(OrderElement orderElement, BigDecimal value) { if (orderElement != null) { SumExpenses sumExpenses = getByOrderElement(orderElement); sumExpenses.subtractIndirectExpenses(value); save(sumExpenses); substractIndirectExpensesRecursively(orderElement.getParent(), value); } } private SumExpenses getByOrderElement(OrderElement orderElement) { SumExpenses sumExpenses = mapSumExpenses.get(orderElement); if (sumExpenses == null) { sumExpenses = findByOrderElement(orderElement); mapSumExpenses.put(orderElement, sumExpenses); } return sumExpenses; } @Override public SumExpenses findByOrderElement(OrderElement orderElement) { return (SumExpenses) getSession() .createCriteria(getEntityClass()) .add(Restrictions.eq("orderElement", orderElement)) .uniqueResult(); } @Override @Transactional public void recalculateSumExpenses(Long orderId) { try { Order order = orderDAO.find(orderId); resetMapSumExpenses(); resetSumExpenses(order); calculateDirectExpenses(order); } catch (InstanceNotFoundException e) { throw new RuntimeException(e); } } private void resetSumExpenses(OrderElement orderElement) { SumExpenses sumExpenses = getByOrderElement(orderElement); if (sumExpenses == null) { sumExpenses = SumExpenses.create(orderElement); } sumExpenses.reset(); for (OrderElement each : orderElement.getChildren()) { resetSumExpenses(each); } } private void calculateDirectExpenses(OrderElement orderElement) { for (OrderElement each : orderElement.getChildren()) { calculateDirectExpenses(each); } BigDecimal value = BigDecimal.ZERO; for (ExpenseSheetLine line : expenseSheetLineDAO.findByOrderElement(orderElement)) { value = value.add(line.getValue()); } addDirectExpenses(orderElement, value); } }