org.libreplan.web.orders.ManageOrderElementAdvancesModel.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.web.orders.ManageOrderElementAdvancesModel.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e
 *                         Desenvolvemento Tecnolxico de Galicia
 * Copyright (C) 2010-2011 Igalia, S.L.
 *
 * Copyright (C) 2011-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.web.orders;

import static org.libreplan.web.I18nHelper._;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.LocalDate;
import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes;
import org.libreplan.business.advance.daos.IAdvanceMeasurementDAO;
import org.libreplan.business.advance.daos.IAdvanceTypeDAO;
import org.libreplan.business.advance.entities.AdvanceAssignment;
import org.libreplan.business.advance.entities.AdvanceMeasurement;
import org.libreplan.business.advance.entities.AdvanceMeasurementComparator;
import org.libreplan.business.advance.entities.AdvanceType;
import org.libreplan.business.advance.entities.DirectAdvanceAssignment;
import org.libreplan.business.advance.entities.IndirectAdvanceAssignment;
import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException;
import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
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.TaskElement;
import org.libreplan.business.planner.entities.consolidations.CalculatedConsolidatedValue;
import org.libreplan.business.planner.entities.consolidations.CalculatedConsolidation;
import org.libreplan.business.planner.entities.consolidations.Consolidation;
import org.libreplan.business.planner.entities.consolidations.NonCalculatedConsolidatedValue;
import org.libreplan.business.planner.entities.consolidations.NonCalculatedConsolidation;
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;
import org.zkoss.zul.SimpleXYModel;
import org.zkoss.zul.XYModel;

/**
 * Service to manage the advance of a selected order element.
 *
 * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
 * @author Diego Pino Garca <dpino@igalia.com>
 */
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ManageOrderElementAdvancesModel implements IManageOrderElementAdvancesModel {

    private static final Log LOG = LogFactory.getLog(ManageOrderElementAdvancesModel.class);

    @Autowired
    private final IAdvanceTypeDAO advanceTypeDAO;

    @Autowired
    private final IOrderElementDAO orderElementDAO;

    private OrderElement orderElement;

    private DirectAdvanceAssignment advanceAssignment;

    private boolean isIndirectAdvanceAssignment = false;

    private List<AdvanceAssignment> listAdvanceAssignments;

    private List<AdvanceType> listAdvanceTypes;

    @Autowired
    public ManageOrderElementAdvancesModel(IAdvanceMeasurementDAO advanceMeasurementDAO,
            IAdvanceTypeDAO advanceTypeDAO, IOrderElementDAO orderElementDAO) {

        Validate.notNull(advanceMeasurementDAO);
        this.advanceTypeDAO = advanceTypeDAO;
        this.orderElementDAO = orderElementDAO;
    }

    @Override
    public String getInfoAdvanceAssignment() {
        if (this.advanceAssignment == null || this.orderElement == null) {
            return "";
        }
        return getInfoAdvanceAssignment(this.advanceAssignment);
    }

    private String getInfoAdvanceAssignment(DirectAdvanceAssignment assignment) {
        if (assignment == null) {
            return "";
        }
        if ((assignment.getAdvanceType() == null) || assignment.getMaxValue() == null) {
            return "";
        }
        return _("{0} (max: {1})", assignment.getAdvanceType().getUnitName(), assignment.getMaxValue());
    }

    @Override
    @Transactional(readOnly = true)
    public List<AdvanceMeasurement> getAdvanceMeasurements() {
        if (this.advanceAssignment == null || this.orderElement == null) {
            return new ArrayList<>();
        }
        return new ArrayList<>(this.advanceAssignment.getAdvanceMeasurements());
    }

    @Override
    @Transactional(readOnly = true)
    public List<AdvanceAssignment> getAdvanceAssignments() {
        if (orderElement == null) {
            return new ArrayList<>();
        }
        return listAdvanceAssignments;
    }

    @Override
    public void refreshChangesFromOrderElement() {
        List<AdvanceAssignment> listAdvanceAssignmentsCopy = new ArrayList<>(listAdvanceAssignments);

        fillAdvanceAssignmentList();
        for (AdvanceAssignment advance : listAdvanceAssignmentsCopy) {

            if ((!listAdvanceAssignments.contains(advance)) && (advance instanceof DirectAdvanceAssignment)
                    && (!advance.getAdvanceType().isQualityForm()) && (!advance.getAdvanceType().isReadOnly())) {

                listAdvanceAssignments.add(advance);
            }
        }

    }

    @Override
    public void prepareEditAdvanceMeasurements(AdvanceAssignment assignment) {
        if (assignment instanceof IndirectAdvanceAssignment) {
            this.advanceAssignment = orderElement
                    .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) assignment);
            this.isIndirectAdvanceAssignment = true;
        } else {
            if (assignment instanceof DirectAdvanceAssignment) {
                this.advanceAssignment = (DirectAdvanceAssignment) assignment;
                this.isIndirectAdvanceAssignment = false;
            }
        }
    }

    @Override
    @Transactional(readOnly = true)
    public void initEdit(OrderElement orderElement) {
        this.orderElement = orderElement;
        this.advanceAssignment = null;
        if (orderElement != null) {
            loadAdvanceTypes();
            reattachmentOrderElement();
            forceLoadAdvanceAssignmentsAndMeasurements();
            fillAdvanceAssignmentList();
        }
    }

    private void forceLoadAdvanceAssignmentsAndMeasurements() {
        for (DirectAdvanceAssignment each : orderElement.getDirectAdvanceAssignments()) {
            forceLoadAdvanceConsolidatedValues(each);
            each.getNonCalculatedConsolidation().size();
            each.getAdvanceType().getUnitName();
        }

        for (IndirectAdvanceAssignment each : orderElement.getIndirectAdvanceAssignments()) {
            each.getCalculatedConsolidation().size();
            each.getAdvanceType().getUnitName();
            DirectAdvanceAssignment fakedDirect = orderElement.calculateFakeDirectAdvanceAssignment(each);
            if (fakedDirect != null) {
                forceLoadAdvanceConsolidatedValues(fakedDirect);
            } else {
                LOG.warn("Fake direct advance assignment shouldn't be NULL for type '"
                        + each.getAdvanceType().getUnitName() + "'");
            }
        }

    }

    private void forceLoadAdvanceConsolidatedValues(DirectAdvanceAssignment advance) {
        for (AdvanceMeasurement measurement : advance.getAdvanceMeasurements()) {
            for (NonCalculatedConsolidatedValue each : measurement.getNonCalculatedConsolidatedValues()) {
                each.getConsolidation().getConsolidatedUntil();
            }
        }
    }

    public void reattachmentOrderElement() {
        orderElementDAO.reattach(orderElement);
    }

    private void fillAdvanceAssignmentList() {
        listAdvanceAssignments = new ArrayList<>();

        for (DirectAdvanceAssignment each : orderElement.getDirectAdvanceAssignments()) {
            listAdvanceAssignments.add(each);
        }
        for (IndirectAdvanceAssignment each : orderElement.getIndirectAdvanceAssignments()) {
            listAdvanceAssignments.add(each);
        }
    }

    @Override
    public boolean addNewLineAdvanceAssignment() {
        DirectAdvanceAssignment newAdvance = DirectAdvanceAssignment.create();
        newAdvance.setOrderElement(orderElement);

        // Set first advance type of the list as default
        List<AdvanceType> listAdvanceType = getPossibleAdvanceTypes(newAdvance);
        if (listAdvanceType.isEmpty()) {
            return false;
        }

        // Create new progress type and add it to list
        final AdvanceType progressType = first(listAdvanceType);
        newAdvance.setAdvanceType(progressType);
        newAdvance.setMaxValue(getMaxValue(progressType));
        newAdvance.setReportGlobalAdvance(listAdvanceAssignments.isEmpty());
        listAdvanceAssignments.add(newAdvance);

        return true;
    }

    private AdvanceType first(List<AdvanceType> listAdvanceType) {
        return listAdvanceType.get(0);
    }

    @Override
    public BigDecimal getMaxValue(AdvanceType advanceType) {
        if (advanceType != null) {
            return advanceType.getDefaultMaxValue();
        }
        return BigDecimal.ZERO;
    }

    @Override
    public AdvanceAssignment getSpreadAdvance() {
        for (AdvanceAssignment advance : getAdvanceAssignments()) {
            if (advance.getReportGlobalAdvance()) {
                return advance;
            }
        }
        return null;
    }

    @Override
    public AdvanceMeasurement addNewLineAdvanceMeasurement() {
        if (advanceAssignment != null) {
            AdvanceMeasurement newMeasurement = createMeasurement();
            addMeasurement(newMeasurement);
            return newMeasurement;
        }
        return null;
    }

    private AdvanceMeasurement createMeasurement() {
        AdvanceMeasurement result = AdvanceMeasurement.create();
        result.setDate(new LocalDate());
        result.setAdvanceAssignment(advanceAssignment);
        return result;
    }

    private void addMeasurement(AdvanceMeasurement measurement) {
        if (!advanceAssignment.addAdvanceMeasurements(measurement)) {
            measurement.setDate(null);
            advanceAssignment.addAdvanceMeasurements(measurement);
        }
    }

    @Override
    public void removeLineAdvanceAssignment(AdvanceAssignment advance) {
        advance.setOrderElement(null);
        this.listAdvanceAssignments.remove(advance);
        orderElement.removeAdvanceAssignment(advance);
        this.advanceAssignment = null;
    }

    @Override
    public void removeLineAdvanceMeasurement(AdvanceMeasurement measurement) {
        this.advanceAssignment.removeAdvanceMeasurement(measurement);
    }

    @Override
    public List<AdvanceType> getPossibleAdvanceTypes(DirectAdvanceAssignment directAdvanceAssignment) {
        if (orderElement == null) {
            return new ArrayList<>();
        }
        List<AdvanceType> advanceTypes = new ArrayList<>();
        for (AdvanceType advanceType : this.listAdvanceTypes) {

            if ((advanceType.getUnitName().equals(PredefinedAdvancedTypes.CHILDREN.getTypeName()))
                    || (advanceType.isQualityForm()) || advanceType.isReadOnly()) {

                continue;
            }

            if (existsAdvanceTypeAlreadyInThisOrderElement(advanceType)) {

                if ((directAdvanceAssignment.getAdvanceType() == null) || (!directAdvanceAssignment.getAdvanceType()
                        .getUnitName().equals(advanceType.getUnitName()))) {

                    continue;
                }
            }
            // If the associated task of the order element or of some its children is subcontracted
            // the advance type subcontractor is not permitted
            if (advanceType.getUnitName().equals(PredefinedAdvancedTypes.SUBCONTRACTOR.getTypeName())
                    && hasAnySubcontractedTaskOnChildren()) {

                continue;
            }
            advanceTypes.add(advanceType);
        }
        return getSpecificOrder(advanceTypes);
    }

    private List<AdvanceType> getSpecificOrder(List<AdvanceType> advanceTypes) {
        Collections.sort(advanceTypes, new Comparator<AdvanceType>() {

            @Override
            public int compare(AdvanceType arg0, AdvanceType arg1) {
                if ((arg0 == null) || (arg0.getUnitName() == null)) {
                    return -1;
                }
                if ((arg1 == null) || (arg1.getUnitName() == null)
                        || (arg1.getUnitName().equals(PredefinedAdvancedTypes.PERCENTAGE.getTypeName()))) {

                    return 1;
                }
                if (arg0.getUnitName().equals(PredefinedAdvancedTypes.PERCENTAGE.getTypeName())) {
                    return -1;
                }
                return (arg0.getUnitName().compareTo(arg1.getUnitName()));
            }
        });
        return advanceTypes;
    }

    private boolean existsAdvanceTypeAlreadyInThisOrderElement(AdvanceType advanceType) {
        if (listAdvanceAssignments != null) {
            for (AdvanceAssignment each : listAdvanceAssignments) {

                if ((each.getAdvanceType() != null)
                        && (each.getAdvanceType().getUnitName().equals(advanceType.getUnitName()))) {

                    return true;
                }
            }
        }
        return false;
    }

    @Transactional(readOnly = true)
    private void loadAdvanceTypes() {
        this.listAdvanceTypes = this.advanceTypeDAO.findActivesAdvanceTypes();
    }

    @Override
    public boolean isReadOnlyAdvanceMeasurements() {
        if (this.advanceAssignment == null) {
            return true;
        }

        AdvanceType advanceType = this.advanceAssignment.getAdvanceType();
        if (advanceType != null) {
            if (advanceType.isQualityForm()) {
                return true;
            }
            if (advanceType.isReadOnly()) {
                return true;
            }
        }

        return isIndirectAdvanceAssignment || isSubcontractedAdvanceTypeAndSubcontractedTask(advanceAssignment);

    }

    @Override
    public boolean isSubcontractedAdvanceTypeAndSubcontractedTask(AdvanceAssignment advance) {
        return (isSubcontractedAdvanceType(advance) && hasSubcontractedAssociatedTask(advance.getOrderElement()));
    }

    @Override
    public boolean isSubcontractedAdvanceType(AdvanceAssignment advance) {
        AdvanceType advanceType = advance.getAdvanceType();
        if (advanceType != null) {
            if (advanceType.getUnitName().equals(PredefinedAdvancedTypes.SUBCONTRACTOR.getTypeName())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void cleanAdvance(DirectAdvanceAssignment advanceAssignment) {
        if (advanceAssignment != null) {
            advanceAssignment.clearAdvanceMeasurements();
        }
    }

    @Override
    public void resetAdvanceAssignment() {
        this.advanceAssignment = null;
    }

    @Override
    @Transactional(readOnly = true)
    public void confirmSave() throws InstanceNotFoundException, DuplicateAdvanceAssignmentForOrderElementException,
            DuplicateValueTrueReportGlobalAdvanceException {

        reattachmentOrderElement();
        orderElement.updateAdvancePercentageTaskElement();
        validateBasicData();
    }

    private void validateBasicData() throws InstanceNotFoundException,
            DuplicateAdvanceAssignmentForOrderElementException, DuplicateValueTrueReportGlobalAdvanceException {

        updateRemoveAdvances();
        for (AdvanceAssignment each : this.listAdvanceAssignments) {
            if (each instanceof DirectAdvanceAssignment) {
                validateBasicData((DirectAdvanceAssignment) each);
            }
        }
    }

    private void updateRemoveAdvances() {
        for (AdvanceAssignment each : this.listAdvanceAssignments) {
            AdvanceAssignment advance = yetExistAdvanceAssignment(each);
            if (advance == null) {
                removeAdvanceAssignment(each);
            }
        }
    }

    private void validateBasicData(DirectAdvanceAssignment directAdvanceAssignment)
            throws InstanceNotFoundException, DuplicateAdvanceAssignmentForOrderElementException,
            DuplicateValueTrueReportGlobalAdvanceException {

        if (directAdvanceAssignment.getVersion() == null) {
            addAdvanceAssignment(directAdvanceAssignment);
        }
    }

    private AdvanceAssignment yetExistAdvanceAssignment(AdvanceAssignment assignment) {
        for (AdvanceAssignment advance : this.orderElement.getDirectAdvanceAssignments()) {
            if (advance.getVersion() != null && advance.getId().equals(assignment.getId())) {
                return advance;
            }
        }
        return null;
    }

    @Transactional(readOnly = true)
    private void addAdvanceAssignment(DirectAdvanceAssignment newAdvanceAssignment)
            throws DuplicateAdvanceAssignmentForOrderElementException,
            DuplicateValueTrueReportGlobalAdvanceException {

        if (newAdvanceAssignment.getReportGlobalAdvance()) {
            this.orderElement.removeReportGlobalAdvanceAssignment();
        }
        this.orderElement.addAdvanceAssignment(newAdvanceAssignment);
    }

    private void removeAdvanceAssignment(AdvanceAssignment assignment) {
        if (assignment != null) {
            orderElement.removeAdvanceAssignment(assignment);
        }
    }

    @Override
    public boolean isPrecisionValid(AdvanceMeasurement advanceMeasurement) {
        if ((this.advanceAssignment != null) && (this.advanceAssignment.getAdvanceType() != null)) {
            return advanceMeasurement.isValidPrecisionConstraint();
        }
        return true;
    }

    @Override
    public boolean greatThanMaxValue(AdvanceMeasurement advanceMeasurement) {
        if (this.advanceAssignment == null || this.advanceAssignment.getMaxValue() == null) {
            return false;
        }
        return !(advanceMeasurement.isValueIsLessThanMaxValueConstraint());
    }

    @Override
    public boolean lessThanPreviousMeasurements() {
        if (this.advanceAssignment == null) {
            return false;
        }
        return !(this.advanceAssignment.isValidAdvanceMeasurementsConstraint());
    }

    @Override
    public boolean isDistinctValidDate(LocalDate value, AdvanceMeasurement newAdvanceMeasurement) {
        if (this.advanceAssignment == null) {
            return true;
        }
        for (AdvanceMeasurement advanceMeasurement : advanceAssignment.getAdvanceMeasurements()) {
            LocalDate oldDate = advanceMeasurement.getDate();
            if (oldDate != null && !newAdvanceMeasurement.equals(advanceMeasurement)
                    && oldDate.compareTo(new LocalDate(value)) == 0) {
                return false;
            }
        }
        return true;
    }

    @Override
    public BigDecimal getUnitPrecision() {
        if (this.advanceAssignment == null) {
            return BigDecimal.ZERO;
        }
        return this.advanceAssignment.getAdvanceType().getUnitPrecision();
    }

    @Override
    @Transactional(readOnly = true)
    public AdvanceMeasurement getLastAdvanceMeasurement(DirectAdvanceAssignment assignment) {
        if (assignment != null) {
            SortedSet<AdvanceMeasurement> advanceMeasurements = assignment.getAdvanceMeasurements();
            if (advanceMeasurements.size() > 0) {
                return advanceMeasurements.first();
            }
        }
        return null;
    }

    @Override
    public void sortListAdvanceMeasurement() {
        if (advanceAssignment != null) {
            ArrayList<AdvanceMeasurement> advanceMeasurements = new ArrayList<>(
                    advanceAssignment.getAdvanceMeasurements());
            Collections.sort(advanceMeasurements, new AdvanceMeasurementComparator());
            TreeSet<AdvanceMeasurement> measurements = new TreeSet<>(new AdvanceMeasurementComparator());
            measurements.addAll(advanceMeasurements);
            this.advanceAssignment.setAdvanceMeasurements(measurements);
        }
    }

    @Override
    public BigDecimal getPercentageAdvanceMeasurement(AdvanceMeasurement advanceMeasurement) {
        AdvanceAssignment assignment = advanceMeasurement.getAdvanceAssignment();
        if (assignment == null) {
            return BigDecimal.ZERO;
        }

        BigDecimal maxValue;
        if (assignment instanceof IndirectAdvanceAssignment) {

            maxValue = orderElement.calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) assignment)
                    .getMaxValue();
        } else {
            maxValue = ((DirectAdvanceAssignment) assignment).getMaxValue();
        }

        if (maxValue.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }

        BigDecimal value = advanceMeasurement.getValue();
        if (value == null) {
            return BigDecimal.ZERO;
        }

        BigDecimal division = value.divide(maxValue.setScale(2), 4, RoundingMode.DOWN);
        return (division.multiply(new BigDecimal(100))).setScale(2, RoundingMode.DOWN);

    }

    @Override
    @Transactional(readOnly = true)
    public DirectAdvanceAssignment calculateFakeDirectAdvanceAssignment(
            IndirectAdvanceAssignment indirectAdvanceAssignment) {
        if ((orderElement == null) || (orderElement.isLeaf())) {
            return null;
        }

        reattachmentOrderElement();

        return orderElement.calculateFakeDirectAdvanceAssignment(indirectAdvanceAssignment);
    }

    @Override
    @Transactional(readOnly = true)
    public BigDecimal getAdvancePercentageChildren() {
        if ((orderElement == null) || orderElement.isLeaf()) {
            return null;
        }

        reattachmentOrderElement();

        return orderElement.getAdvancePercentageChildren();
    }

    @Override
    @Transactional(readOnly = true)
    public XYModel getChartData(Set<AdvanceAssignment> selectedAdvances) {
        XYModel xyModel = new SimpleXYModel();

        for (AdvanceAssignment each : selectedAdvances) {
            DirectAdvanceAssignment directAdvanceAssignment;
            if (each instanceof DirectAdvanceAssignment) {
                directAdvanceAssignment = (DirectAdvanceAssignment) each;
            } else {
                directAdvanceAssignment = calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) each);
            }
            if (directAdvanceAssignment != null) {
                String title = getInfoAdvanceAssignment(directAdvanceAssignment);
                SortedSet<AdvanceMeasurement> listAdvanceMeasurements = directAdvanceAssignment
                        .getAdvanceMeasurements();
                if (listAdvanceMeasurements.size() > 1) {
                    for (AdvanceMeasurement advanceMeasurement : listAdvanceMeasurements) {
                        BigDecimal value = advanceMeasurement.getValue();
                        if ((selectedAdvances.size() > 1) && (value != null)
                                && (value.compareTo(BigDecimal.ZERO) > 0)) {
                            BigDecimal maxValue = directAdvanceAssignment.getMaxValue();
                            value = value.setScale(2).divide(maxValue, RoundingMode.DOWN);
                        }
                        LocalDate date = advanceMeasurement.getDate();
                        if ((value != null) && (date != null)) {
                            xyModel.addValue(title, date.toDateTimeAtStartOfDay().getMillis(), value);
                        }
                    }
                }
            }
        }

        return xyModel;
    }

    @Override
    @Transactional(readOnly = true)
    public boolean hasConsolidatedAdvances(AdvanceAssignment advance) {
        if (advance instanceof DirectAdvanceAssignment) {

            if ((advance.getReportGlobalAdvance())
                    && (!((DirectAdvanceAssignment) advance).getNonCalculatedConsolidation().isEmpty())) {

                return true;
            }

            return ((!((DirectAdvanceAssignment) advance).isFake())
                    && (!canBeRemovedAllAdvanceMeasurements((DirectAdvanceAssignment) advance)));

        } else {
            return ((advance.getReportGlobalAdvance())
                    && (!((IndirectAdvanceAssignment) advance).getCalculatedConsolidation().isEmpty()));
        }
    }

    private boolean canBeRemovedAllAdvanceMeasurements(DirectAdvanceAssignment advance) {
        Iterator<AdvanceMeasurement> iterator = advance.getAdvanceMeasurements().iterator();
        while (iterator.hasNext()) {
            if (!canRemoveOrChange(iterator.next())) {
                return false;
            }
        }
        return true;
    }

    @Transactional(readOnly = true)
    public boolean canRemoveOrChange(AdvanceMeasurement advanceMeasurement) {
        return (!hasConsolidatedAdvances(advanceMeasurement) && !isAlreadyReportedProgress(advanceMeasurement));
    }

    @Transactional(readOnly = true)
    public boolean hasConsolidatedAdvances(AdvanceMeasurement advanceMeasurement) {
        return hasConsolidatedAdvances(advanceMeasurement, isIndirectAdvanceAssignment);
    }

    private boolean hasConsolidatedAdvances(AdvanceMeasurement advanceMeasurement,
            boolean isIndirectAdvanceAssignment) {

        if (isIndirectAdvanceAssignment) {
            return false;
        }

        if (!advanceMeasurement.getNonCalculatedConsolidatedValues().isEmpty()) {
            return true;
        }

        return findIndirectConsolidation(advanceMeasurement);
    }

    @Override
    @Transactional(readOnly = true)
    public boolean isQualityForm(AdvanceAssignment advance) {
        AdvanceType advanceType = advance.getAdvanceType();
        advanceTypeDAO.reattach(advanceType);
        return advanceType.isQualityForm();
    }

    @Override
    @Transactional(readOnly = true)
    public boolean isReadOnly(AdvanceAssignment advance) {
        AdvanceType advanceType = advance.getAdvanceType();
        advanceTypeDAO.reattach(advanceType);
        return advanceType.isReadOnly();
    }

    @Override
    @Transactional(readOnly = true)
    public boolean findIndirectConsolidation(AdvanceMeasurement advanceMeasurement) {
        AdvanceAssignment advance = advanceMeasurement.getAdvanceAssignment();
        if ((orderElement != null) && (orderElement.getParent() != null)
                && (advance instanceof DirectAdvanceAssignment)) {
            orderElementDAO.reattach(orderElement);
            Set<IndirectAdvanceAssignment> indirects = getSpreadIndirectAdvanceAssignmentWithSameType(orderElement,
                    advance);

            for (IndirectAdvanceAssignment indirect : indirects) {
                if (findConsolidatedAdvance(indirect.getCalculatedConsolidation(), advanceMeasurement)) {
                    return true;
                }
            }
        }
        return false;
    }

    private Set<IndirectAdvanceAssignment> getSpreadIndirectAdvanceAssignmentWithSameType(OrderElement orderElement,
            AdvanceAssignment advance) {
        List<String> types = new ArrayList<>();

        types.add(advance.getAdvanceType().getUnitName());
        if (advance.getReportGlobalAdvance()) {
            types.add(PredefinedAdvancedTypes.CHILDREN.getTypeName());
        }

        return getSpreadIndirectAdvanceAssignmentWithSameType(orderElement, types);
    }

    private Set<IndirectAdvanceAssignment> getSpreadIndirectAdvanceAssignmentWithSameType(OrderElement orderElement,
            List<String> types) {
        Set<IndirectAdvanceAssignment> result = new HashSet<>();

        for (IndirectAdvanceAssignment indirect : orderElement.getIndirectAdvanceAssignments()) {
            if ((indirect.getReportGlobalAdvance()) && (types.contains(indirect.getAdvanceType().getUnitName()))) {
                result.add(indirect);
            }
        }

        OrderElement parent = orderElement.getParent();
        if (parent != null) {
            result.addAll(getSpreadIndirectAdvanceAssignmentWithSameType(parent, types));
        }

        return result;
    }

    private boolean findConsolidatedAdvance(Set<CalculatedConsolidation> consolidations,
            AdvanceMeasurement advance) {
        for (CalculatedConsolidation consolidation : consolidations) {
            for (CalculatedConsolidatedValue value : consolidation.getCalculatedConsolidatedValues()) {

                if ((value.getDate() != null) && (advance.getDate() != null)
                        && (value.getDate().compareTo(advance.getDate()) == 0)) {

                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public LocalDate getLastConsolidatedMeasurementDate(AdvanceAssignment advance) {
        List<Consolidation> consolidations = new ArrayList<>();

        Set<NonCalculatedConsolidation> nonCalculatedConsolidations = ((DirectAdvanceAssignment) advance)
                .getNonCalculatedConsolidation();

        consolidations.addAll(nonCalculatedConsolidations);

        if (consolidations.isEmpty()) {

            Set<IndirectAdvanceAssignment> indirects = getSpreadIndirectAdvanceAssignmentWithSameType(orderElement,
                    advance);

            for (IndirectAdvanceAssignment indirect : indirects) {
                consolidations.addAll(indirect.getCalculatedConsolidation());
            }
        }

        if (consolidations.isEmpty()) {
            return null;
        }

        Collections.sort(consolidations, new Comparator<Consolidation>() {
            @Override
            public int compare(Consolidation o1, Consolidation o2) {
                return o1.getConsolidatedUntil().compareTo(o2.getConsolidatedUntil());
            }
        });

        return consolidations.get(consolidations.size() - 1).getConsolidatedUntil();
    }

    @Override
    public boolean hasAnyConsolidatedAdvanceCurrentOrderElement() {
        return orderElement != null && orderElement.hasAnyConsolidatedAdvance();
    }

    @Override
    public boolean hasAnySubcontractedTaskOnChildren() {
        List<OrderElement> list = new ArrayList<>();
        list.add(orderElement);
        list.addAll(orderElement.getAllChildren());
        for (OrderElement child : list) {
            if (hasSubcontractedAssociatedTask(child)) {
                return true;
            }
        }
        return false;
    }

    public boolean hasSubcontractedAssociatedTask(OrderElement orderElement) {
        for (TaskElement task : orderElement.getTaskElements()) {
            if (task.isSubcontracted()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Boolean isAlreadyReportedProgress(AdvanceMeasurement measure) {
        if (measure != null && !measure.isNewObject()) {
            return isAlreadyReportedProgressWith(measure.getDate());
        }
        return false;
    }

    @Override
    public Boolean isAlreadyReportedProgressWith(LocalDate date) {
        if (isSubcontractedByCustomer() && isSubcontractedAdvanceType(advanceAssignment)) {
            LocalDate lastReported = getLastReportedProgressToCustomer();
            return (date != null && lastReported != null && date.compareTo(lastReported) <= 0);
        }
        return false;
    }

    private LocalDate getLastReportedProgressToCustomer() {
        return getLastReportedProgressToCustomerOf(advanceAssignment);
    }

    private LocalDate getLastReportedProgressToCustomerOf(DirectAdvanceAssignment advanceAssignment) {
        if (advanceAssignment != null && isSubcontractedByCustomer()) {
            AdvanceMeasurement lastAdvanceMeasurementReported = null;

            for (AdvanceMeasurement advanceMeasurement : advanceAssignment.getAdvanceMeasurements()) {
                if (advanceMeasurement.getCommunicationDate() != null) {
                    if (lastAdvanceMeasurementReported == null) {
                        lastAdvanceMeasurementReported = advanceMeasurement;
                    } else {
                        if (advanceMeasurement.getCommunicationDate()
                                .compareTo(lastAdvanceMeasurementReported.getCommunicationDate()) > 0) {

                            lastAdvanceMeasurementReported = advanceMeasurement;
                        }
                    }
                }
            }
            return (lastAdvanceMeasurementReported != null) ? lastAdvanceMeasurementReported.getDate() : null;
        }
        return null;
    }

    private Boolean isSubcontractedByCustomer() {
        if (orderElement != null) {
            Order order = orderElement.getOrder();
            if (order != null && order.getExternalCode() != null) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean hasReportedProgress(AdvanceAssignment advance) {
        return advance instanceof DirectAdvanceAssignment && isSubcontractedByCustomer()
                && isSubcontractedAdvanceType(advance)
                && (getLastReportedProgressToCustomerOf((DirectAdvanceAssignment) advance) != null);
    }

}