/*
* 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.web.reports;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.libreplan.business.labels.daos.ILabelDAO;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.orders.daos.IOrderDAO;
import org.libreplan.business.orders.daos.IOrderElementDAO;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.planner.entities.IMoneyCostCalculator;
import org.libreplan.business.reports.dtos.ProjectStatusReportDTO;
import org.libreplan.business.requirements.entities.IndirectCriterionRequirement;
import org.libreplan.business.resources.daos.ICriterionDAO;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.scenarios.IScenarioManager;
import org.libreplan.business.users.daos.IOrderAuthorizationDAO;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.web.security.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Model for Project Status report.
*
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ProjectStatusReportModel implements IProjectStatusReportModel {
@Autowired
private IOrderDAO orderDAO;
@Autowired
private ILabelDAO labelDAO;
@Autowired
private ICriterionDAO criterionDAO;
@Autowired
private IOrderElementDAO orderElementDAO;
@Autowired
private IOrderAuthorizationDAO orderAuthorizationDAO;
@Autowired
private IScenarioManager scenarioManager;
@Autowired
private IMoneyCostCalculator moneyCostCalculator;
private Set<Label> selectedLabels = new HashSet<Label>();
private Set<Criterion> selectedCriteria = new HashSet<Criterion>();
private ProjectStatusReportDTO totalDTO;
@SuppressWarnings("unchecked")
@Override
@Transactional(readOnly = true)
public List<Order> getOrders() {
List<Order> result = orderDAO.getOrdersByReadAuthorizationByScenario(
SecurityUtils.getSessionUserLoginName(),
scenarioManager.getCurrent());
Collections.sort(result);
return result;
}
@Override
@Transactional(readOnly = true)
public List<ProjectStatusReportDTO> getProjectStatusReportDTOs(Order order) {
moneyCostCalculator.resetMoneyCostMap();
List<OrderElement> orderElements;
if (order != null) {
orderDAO.reattach(order);
order.useSchedulingDataFor(scenarioManager.getCurrent());
orderElements = order.getAllChildren();
orderElements = filterBySelectedLabels(orderElements);
orderElements = filterBySelectedCriteria(orderElements);
} else {
orderElements = orderElementDAO.findByLabelsAndCriteria(
selectedLabels, selectedCriteria);
for (OrderElement each : orderElements) {
each.useSchedulingDataFor(orderDAO.loadOrderAvoidingProxyFor(
each).getOrderVersionFor(
scenarioManager.getCurrent()));
}
orderElements = filterByOrderAuthorizations(orderElements);
}
List<ProjectStatusReportDTO> dtos = new ArrayList<ProjectStatusReportDTO>();
for (OrderElement element : orderElements) {
dtos.add(calculateDTO(element, order == null));
}
calculateTotalDTO(order, dtos);
return dtos;
}
private ProjectStatusReportDTO calculateDTO(OrderElement orderElement, boolean appendProjectInName) {
ProjectStatusReportDTO dto = new ProjectStatusReportDTO(orderElement,
appendProjectInName ? orderDAO
.loadOrderAvoidingProxyFor(orderElement) : null);
dto.setHoursCost(moneyCostCalculator.getHoursMoneyCost(orderElement));
dto.setExpensesCost(moneyCostCalculator
.getExpensesMoneyCost(orderElement));
dto.setTotalCost(moneyCostCalculator.getTotalMoneyCost(orderElement));
if (!isNotFilteringByCriteria()) {
dto = discountChildrenWithInvalidatedCriteria(orderElement, dto);
}
dto.calculateMarks();
return dto;
}
private ProjectStatusReportDTO discountChildrenWithInvalidatedCriteria(
OrderElement orderElement, ProjectStatusReportDTO dto) {
List<ProjectStatusReportDTO> dtosToDiscount = new ArrayList<ProjectStatusReportDTO>();
for (OrderElement child : orderElement.getChildren()) {
for (IndirectCriterionRequirement criterionRequirement : child
.getIndirectCriterionRequirement()) {
if (isCriterionSelected(criterionRequirement.getCriterion()
.getCode())) {
if (!criterionRequirement.isValid()) {
dtosToDiscount.add(calculateDTO(child, false));
}
}
}
}
return discount(dto, dtosToDiscount);
}
private ProjectStatusReportDTO discount(ProjectStatusReportDTO originalDto,
List<ProjectStatusReportDTO> toDiscount) {
if (toDiscount.isEmpty()) {
return originalDto;
}
EffortDuration estimatedHours = originalDto.getEstimatedHoursAsEffortDuration();
EffortDuration plannedHours = originalDto.getPlannedHoursAsEffortDuration();
EffortDuration imputedHours = originalDto.getImputedHoursAsEffortDuration();
BigDecimal budget = originalDto.getBudget();
BigDecimal resourcesBudget = originalDto.getResourcesBudget();
BigDecimal expensesBudget = originalDto.getExpensesBudget();
BigDecimal hoursCost = originalDto.getHoursCost();
BigDecimal expensesCost = originalDto.getExpensesCost();
BigDecimal totalCost = originalDto.getTotalCost();
for (ProjectStatusReportDTO each : toDiscount) {
estimatedHours = subtractIfNotNull(estimatedHours,
each.getEstimatedHoursAsEffortDuration());
plannedHours = subtractIfNotNull(plannedHours,
each.getPlannedHoursAsEffortDuration());
imputedHours = subtractIfNotNull(imputedHours,
each.getImputedHoursAsEffortDuration());
resourcesBudget = subtractIfNotNull(budget,
each.getResourcesBudget());
expensesBudget = subtractIfNotNull(budget, each.getExpensesBudget());
budget = subtractIfNotNull(budget, each.getBudget());
hoursCost = subtractIfNotNull(hoursCost, each.getHoursCost());
expensesCost = subtractIfNotNull(expensesCost,
each.getExpensesCost());
totalCost = subtractIfNotNull(totalCost, each.getTotalCost());
}
ProjectStatusReportDTO projectStatusReportDTO = new ProjectStatusReportDTO(
originalDto.getCode(), originalDto.getName(), estimatedHours,
plannedHours, imputedHours, resourcesBudget, expensesBudget,
budget, hoursCost, expensesCost,
totalCost);
return projectStatusReportDTO;
}
public boolean isCriterionSelected(String code) {
for (Criterion criterion : selectedCriteria) {
if (criterion.getCode().equals(code)) {
return true;
}
}
return false;
}
private void calculateTotalDTO(Order order,
List<ProjectStatusReportDTO> dtos) {
if (isNotFiltering()) {
totalDTO = calculateDTO(order, false);
} else {
EffortDuration estimatedHours = EffortDuration.zero();
EffortDuration plannedHours = EffortDuration.zero();
EffortDuration imputedHours = EffortDuration.zero();
BigDecimal budget = BigDecimal.ZERO.setScale(2);
BigDecimal hoursBudget = BigDecimal.ZERO.setScale(2);
BigDecimal expensesBudget = BigDecimal.ZERO.setScale(2);
BigDecimal hoursCost = BigDecimal.ZERO.setScale(2);
BigDecimal expensesCost = BigDecimal.ZERO.setScale(2);
BigDecimal totalCost = BigDecimal.ZERO.setScale(2);
for (ProjectStatusReportDTO dto : dtos) {
estimatedHours = addIfNotNull(estimatedHours,
dto.getEstimatedHoursAsEffortDuration());
plannedHours = addIfNotNull(plannedHours,
dto.getPlannedHoursAsEffortDuration());
imputedHours = addIfNotNull(imputedHours,
dto.getImputedHoursAsEffortDuration());
hoursBudget = addIfNotNull(budget, dto.getResourcesBudget());
expensesBudget = addIfNotNull(budget, dto.getExpensesBudget());
budget = addIfNotNull(budget, dto.getBudget());
hoursCost = addIfNotNull(hoursCost, dto.getHoursCost());
expensesCost = addIfNotNull(expensesCost, dto.getExpensesCost());
totalCost = addIfNotNull(totalCost, dto.getTotalCost());
}
totalDTO = new ProjectStatusReportDTO(estimatedHours, plannedHours,
imputedHours, hoursBudget, expensesBudget, budget,
hoursCost, expensesCost, totalCost);
totalDTO.calculateMarks();
}
}
@Override
public boolean isNotFiltering() {
return isNotFilteringByLabels() && isNotFilteringByCriteria();
}
private boolean isNotFilteringByLabels() {
return selectedLabels.isEmpty();
}
private boolean isNotFilteringByCriteria() {
return selectedCriteria.isEmpty();
}
private List<OrderElement> filterBySelectedLabels(
List<OrderElement> orderElements) {
if (isNotFilteringByLabels()) {
return orderElements;
}
List<OrderElement> result = new ArrayList<OrderElement>();
for (OrderElement orderElement : orderElements) {
if (orderElement.containsLabels(selectedLabels)) {
result.add(orderElement);
}
}
return result;
}
private List<OrderElement> filterBySelectedCriteria(
List<OrderElement> orderElements) {
if (isNotFilteringByCriteria()) {
return orderElements;
}
List<OrderElement> result = new ArrayList<OrderElement>();
for (OrderElement orderElement : orderElements) {
if (orderElement.containsCriteria(selectedCriteria)) {
result.add(orderElement);
}
}
return result;
}
private List<OrderElement> filterByOrderAuthorizations(
List<OrderElement> orderElements) {
List<Order> orders = getOrders();
List<OrderElement> result = new ArrayList<OrderElement>();
for (OrderElement each : orderElements) {
if (orders.contains(orderDAO.loadOrderAvoidingProxyFor(each))) {
result.add(each);
}
}
return result;
}
private EffortDuration addIfNotNull(EffortDuration total,
EffortDuration other) {
if (other == null) {
return total;
}
return total.plus(other);
}
private EffortDuration subtractIfNotNull(EffortDuration total,
EffortDuration other) {
if (other == null) {
return total;
}
return total.minus(other);
}
private BigDecimal addIfNotNull(BigDecimal total, BigDecimal other) {
if (other == null) {
return total;
}
return total.add(other);
}
private BigDecimal subtractIfNotNull(BigDecimal total, BigDecimal other) {
if (other == null) {
return total;
}
return total.subtract(other);
}
@Override
@Transactional(readOnly = true)
public BigDecimal getHoursCost(Order order) {
return moneyCostCalculator.getHoursMoneyCost(order);
}
@Override
@Transactional(readOnly = true)
public BigDecimal getExpensesCost(Order order) {
return moneyCostCalculator.getExpensesMoneyCost(order);
}
@Override
@Transactional(readOnly = true)
public BigDecimal getTotalCost(Order order) {
return moneyCostCalculator.getTotalMoneyCost(order);
}
@Override
@Transactional(readOnly = true)
public List<Label> getAllLabels() {
List<Label> labels = labelDAO.findAll();
for (Label label : labels) {
forceLoadLabelType(label);
}
return labels;
}
private void forceLoadLabelType(Label label) {
label.getType().getName();
}
@Override
public void addSelectedLabel(Label label) {
selectedLabels.add(label);
}
@Override
public void removeSelectedLabel(Label label) {
selectedLabels.remove(label);
}
@Override
public Set<Label> getSelectedLabels() {
return Collections.unmodifiableSet(selectedLabels);
}
@Override
public ProjectStatusReportDTO getTotalDTO() {
return totalDTO;
}
@Override
@Transactional(readOnly = true)
public List<Criterion> getAllCriteria() {
List<Criterion> criteria = criterionDAO.findAll();
for (Criterion criterion : criteria) {
forceLoadCriterionType(criterion);
}
return criteria;
}
private void forceLoadCriterionType(Criterion criterion) {
criterion.getType().getName();
}
@Override
public void addSelectedCriterion(Criterion criterion) {
selectedCriteria.add(criterion);
}
@Override
public void removeSelectedCriterion(Criterion criterion) {
selectedCriteria.remove(criterion);
}
@Override
public Set<Criterion> getSelectedCriteria() {
return Collections.unmodifiableSet(selectedCriteria);
}
}