<commit_before>/*
 * This file is part of NavalPlan
 *
 * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
 * Desenvolvemento Tecnolóxico de Galicia
 *
 * 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.navalplanner.web.orders;

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

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.navalplanner.business.advance.entities.AdvanceMeasurement;
import org.navalplanner.business.advance.entities.DirectAdvanceAssignment;
import org.navalplanner.business.advance.entities.IndirectAdvanceAssignment;
import org.navalplanner.business.calendars.daos.IBaseCalendarDAO;
import org.navalplanner.business.calendars.entities.BaseCalendar;
import org.navalplanner.business.common.BaseEntity;
import org.navalplanner.business.common.IAdHocTransactionService;
import org.navalplanner.business.common.IOnTransaction;
import org.navalplanner.business.common.daos.IConfigurationDAO;
import org.navalplanner.business.common.daos.IOrderSequenceDAO;
import org.navalplanner.business.common.entities.Configuration;
import org.navalplanner.business.common.entities.OrderSequence;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.externalcompanies.daos.IExternalCompanyDAO;
import org.navalplanner.business.externalcompanies.entities.ExternalCompany;
import org.navalplanner.business.labels.daos.ILabelDAO;
import org.navalplanner.business.labels.entities.Label;
import org.navalplanner.business.orders.daos.IHoursGroupDAO;
import org.navalplanner.business.orders.daos.IOrderDAO;
import org.navalplanner.business.orders.daos.IOrderElementDAO;
import org.navalplanner.business.orders.entities.HoursGroup;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.entities.OrderLineGroup;
import org.navalplanner.business.orders.entities.TaskSource;
import org.navalplanner.business.orders.entities.TaskSource.TaskSourceSynchronization;
import org.navalplanner.business.planner.daos.ITaskElementDAO;
import org.navalplanner.business.planner.daos.ITaskSourceDAO;
import org.navalplanner.business.planner.entities.TaskElement;
import org.navalplanner.business.qualityforms.daos.IQualityFormDAO;
import org.navalplanner.business.qualityforms.entities.QualityForm;
import org.navalplanner.business.requirements.entities.DirectCriterionRequirement;
import org.navalplanner.business.resources.daos.ICriterionDAO;
import org.navalplanner.business.resources.daos.ICriterionTypeDAO;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.business.scenarios.IScenarioManager;
import org.navalplanner.business.scenarios.daos.IOrderVersionDAO;
import org.navalplanner.business.scenarios.daos.IScenarioDAO;
import org.navalplanner.business.scenarios.entities.OrderVersion;
import org.navalplanner.business.scenarios.entities.Scenario;
import org.navalplanner.business.templates.daos.IOrderElementTemplateDAO;
import org.navalplanner.business.templates.entities.OrderElementTemplate;
import org.navalplanner.business.templates.entities.OrderTemplate;
import org.navalplanner.business.users.daos.IOrderAuthorizationDAO;
import org.navalplanner.business.users.daos.IUserDAO;
import org.navalplanner.business.users.entities.OrderAuthorization;
import org.navalplanner.business.users.entities.OrderAuthorizationType;
import org.navalplanner.business.users.entities.User;
import org.navalplanner.business.users.entities.UserRole;
import org.navalplanner.web.orders.labels.LabelsOnConversation;
import org.navalplanner.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;
import org.zkoss.ganttz.IPredicate;
import org.zkoss.zul.Messagebox;

/**
 * Model for UI operations related to {@link Order}. <br />
 *
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 */
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class OrderModel implements IOrderModel {

    @Autowired
    private ICriterionTypeDAO criterionTypeDAO;

    @Autowired
    private IExternalCompanyDAO externalCompanyDAO;

    private final Map<CriterionType, List<Criterion>> mapCriterions = new HashMap<CriterionType, List<Criterion>>();

    private List<ExternalCompany> externalCompanies = new ArrayList<ExternalCompany>();

    @Autowired
    private IOrderDAO orderDAO;

    private Order order;

    private OrderElementTreeModel orderElementTreeModel;

    @Autowired
    private IOrderElementModel orderElementModel;

    @Autowired
    private ICriterionDAO criterionDAO;

    @Autowired
    private ILabelDAO labelDAO;

    @Autowired
    private IQualityFormDAO qualityFormDAO;

    @Autowired
    private IOrderElementDAO orderElementDAO;

    @Autowired
    private IOrderElementTemplateDAO templateDAO;

    @Autowired
    private ITaskSourceDAO taskSourceDAO;

    @Autowired
    private ITaskElementDAO taskElementDAO;

    @Autowired
    private IBaseCalendarDAO baseCalendarDAO;

    @Autowired
    private IConfigurationDAO configurationDAO;

    @Autowired
    private IOrderSequenceDAO orderSequenceDAO;

    @Autowired
    private IUserDAO userDAO;

    @Autowired
    private IOrderAuthorizationDAO orderAuthorizationDAO;

    private List<Order> orderList = new ArrayList<Order>();

    @Autowired
    private IScenarioDAO scenarioDAO;

    @Autowired
    private IScenarioManager scenarioManager;

    @Autowired
    private IAdHocTransactionService transactionService;

    @Autowired
    private IOrderVersionDAO orderVersionDAO;

    @Autowired
    private IHoursGroupDAO hoursGroupDAO;

    @Override
    @Transactional(readOnly = true)
    public List<Label> getLabels() {
        return getLabelsOnConversation().getLabels();
    }

    private LabelsOnConversation labelsOnConversation;


    private LabelsOnConversation getLabelsOnConversation() {
        if (labelsOnConversation == null) {
            labelsOnConversation = new LabelsOnConversation(labelDAO);
        }
        return labelsOnConversation;
    }

    private QualityFormsOnConversation qualityFormsOnConversation;

    private Scenario currentScenario;

    private List<Scenario> derivedScenarios = new ArrayList<Scenario>();

    private boolean isEditing = false;

    private QualityFormsOnConversation getQualityFormsOnConversation() {
        if (qualityFormsOnConversation == null) {
            qualityFormsOnConversation = new QualityFormsOnConversation(
                    qualityFormDAO);
        }
        return qualityFormsOnConversation;
    }

    @Override
    public List<QualityForm> getQualityForms() {
        return getQualityFormsOnConversation().getQualityForms();
    }

    @Override
    public void addLabel(Label label) {
        getLabelsOnConversation().addLabel(label);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Order> getOrders() {

        User user;
        try {
            user = userDAO.findByLoginName(SecurityUtils.getSessionUserLoginName());
        }
        catch(InstanceNotFoundException e) {
            //this case shouldn't happen, because it would mean that there isn't a logged user
            //anyway, if it happenned we return an empty list
            return new ArrayList<Order>();
        }
        getLabelsOnConversation().reattachLabels();
        List<Order> orders = orderDAO.getOrdersByReadAuthorizationByScenario(
                user, scenarioManager.getCurrent());

        initializeOrders(orders);
        return orders;
    }

    private void initializeOrders(List<Order> list) {
        for (Order order : list) {
            orderDAO.reattachUnmodifiedEntity(order);
            if (order.getCustomer() != null) {
                order.getCustomer().getName();
            }
            order.getAdvancePercentage();
            for (Label label : order.getLabels()) {
                label.getName();
            }
            order.getScenarios().size();
        }
        this.orderList = list;
    }

    private void loadCriterions() {
        mapCriterions.clear();
        List<CriterionType> criterionTypes = criterionTypeDAO
                .getCriterionTypes();
        for (CriterionType criterionType : criterionTypes) {
            List<Criterion> criterions = new ArrayList<Criterion>(criterionDAO
                    .findByType(criterionType));

            mapCriterions.put(criterionType, criterions);
        }
    }

    @Override
    public Map<CriterionType, List<Criterion>> getMapCriterions(){
        final Map<CriterionType, List<Criterion>> result =
                new HashMap<CriterionType, List<Criterion>>();
        result.putAll(mapCriterions);
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public void initEdit(Order order) {
        Validate.notNull(order);
        isEditing = true;
        loadNeededDataForConversation();
        this.order = getFromDB(order);
        this.orderElementTreeModel = new OrderElementTreeModel(this.order);
        forceLoadAdvanceAssignmentsAndMeasurements(this.order);
        forceLoadCriterionRequirements(this.order);
        forceLoadCalendar(this.getCalendar());
        forceLoadCustomer(this.order.getCustomer());
        forceLoadLabels(this.order);
        currentScenario = scenarioManager.getCurrent();
        this.order.useSchedulingDataFor(currentScenario);
        loadTasks(this.order);
    }

    private void loadTasks(Order order) {
        TaskSource taskSource = order.getTaskSource();
        if (taskSource == null) {
            return;
        }
        loadTask(taskSource.getTask());
    }

    private void loadTask(TaskElement task) {
        task.getDependenciesWithThisDestination().size();
        task.getDependenciesWithThisOrigin().size();
        if (!task.isLeaf()) {
            for (TaskElement each : task.getChildren()) {
                loadTask(each);
            }
        }
    }

    private void forceLoadLabels(OrderElement orderElement) {
        orderElement.getLabels().size();
        for (OrderElement each : orderElement.getChildren()) {
            forceLoadLabels(each);
        }
    }

    private void forceLoadCustomer(ExternalCompany customer) {
        if (customer != null) {
            customer.getName();
        }
    }

    private void loadNeededDataForConversation() {
        loadCriterions();
        initializeCacheLabels();
        getQualityFormsOnConversation().initialize();
        loadExternalCompaniesAreClient();
    }

    private void reattachNeededDataForConversation() {
        reattachCriterions();
        reattachLabels();
        getQualityFormsOnConversation().reattach();
    }

    private void initializeCacheLabels() {
        getLabelsOnConversation().initializeLabels();
    }

    private static void forceLoadCriterionRequirements(OrderElement orderElement) {
        orderElement.getHoursGroups().size();
        for (HoursGroup hoursGroup : orderElement.getHoursGroups()) {
            attachDirectCriterionRequirement(hoursGroup
                    .getDirectCriterionRequirement());
        }
        attachDirectCriterionRequirement(orderElement
                .getDirectCriterionRequirement());

        for (OrderElement child : orderElement.getChildren()) {
            forceLoadCriterionRequirements(child);
        }
    }

    private static void attachDirectCriterionRequirement(
            Set<DirectCriterionRequirement> requirements) {
        for (DirectCriterionRequirement requirement : requirements) {
            requirement.getChildren().size();
            requirement.getCriterion().getName();
            requirement.getCriterion().getType().getName();
        }
    }

    private void forceLoadAdvanceAssignmentsAndMeasurements(
            OrderElement orderElement) {
        for (DirectAdvanceAssignment directAdvanceAssignment : orderElement
                .getDirectAdvanceAssignments()) {
            directAdvanceAssignment.getAdvanceType().getUnitName();
            for (AdvanceMeasurement advanceMeasurement : directAdvanceAssignment
                    .getAdvanceMeasurements()) {
                advanceMeasurement.getValue();
            }
        }

        if (orderElement instanceof OrderLineGroup) {
            for (IndirectAdvanceAssignment indirectAdvanceAssignment : ((OrderLineGroup) orderElement)
                    .getIndirectAdvanceAssignments()) {
                indirectAdvanceAssignment.getAdvanceType().getUnitName();
            }

            for (OrderElement child : orderElement.getChildren()) {
                forceLoadAdvanceAssignmentsAndMeasurements(child);
            }
        }
    }

    private Order getFromDB(Order order) {
        try {
            return orderDAO.find(order.getId());
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    @Transactional(readOnly = true)
    public void prepareForCreate() throws ConcurrentModificationException {
        loadNeededDataForConversation();
        this.order = Order.create();
        initializeOrder();
        initializeCalendar();
        currentScenario = scenarioManager.getCurrent();
        addOrderToCurrentScenario(this.order);
        this.order.useSchedulingDataFor(currentScenario);
    }

    private OrderVersion addOrderToCurrentScenario(Order order) {
        OrderVersion orderVersion = currentScenario.addOrder(order);
        order.setVersionForScenario(currentScenario, orderVersion);
        derivedScenarios = scenarioDAO.getDerivedScenarios(currentScenario);
        for (Scenario scenario : derivedScenarios) {
            scenario.addOrder(order, orderVersion);
        }
        return orderVersion;
    }

    private void initializeOrder() {
        this.orderElementTreeModel = new OrderElementTreeModel(this.order);
        this.order.setInitDate(new Date());
        setDefaultOrderCode();
        this.order.setCodeAutogenerated(true);
    }

    private void initializeCalendar() {
        this.order.setCalendar(getDefaultCalendar());
    }

    @Override
    @Transactional(readOnly = true)
    public void prepareCreationFrom(OrderTemplate template) {
        loadNeededDataForConversation();
        this.order = createOrderFrom((OrderTemplate) templateDAO
                .findExistingEntity(template.getId()));
        forceLoadAdvanceAssignmentsAndMeasurements(this.order);
        initializeOrder();
    }

    private Order createOrderFrom(OrderTemplate template) {
        return template.createOrder(scenarioManager.getCurrent());
    }

    private OrderElement createOrderElementFrom(OrderLineGroup parent,
            OrderElementTemplate template) {
        Validate.notNull(parent);
        return template.createElement(parent);
    }

    private void setDefaultOrderCode() throws ConcurrentModificationException {
        String code = orderSequenceDAO.getNextOrderCode();
        if (code == null) {
            throw new ConcurrentModificationException(
                    _("Could not get order code, please try again later"));
        }
        this.order.setCode(code);
    }

    @Override
    @Transactional(readOnly = true)
    public OrderElement createFrom(OrderLineGroup parent,
            OrderElementTemplate template) {
        reattachNeededDataForConversation();
        OrderElement result = createOrderElementFrom(parent, templateDAO
                .findExistingEntity(template.getId()));
        if (isCodeAutogenerated()) {
            setAllCodeToNull(result);
        }
        forceLoadAdvanceAssignmentsAndMeasurements(result);
        return result;
    }

    private void setAllCodeToNull(OrderElement result) {
        result.setCode(null);
        for (OrderElement each : result.getChildren()) {
            setAllCodeToNull(each);
        }
    }

    @Override
    public void save() throws ValidationException {
        final boolean newOrderVersionNeeded = isEditing
                && order.hasSchedulingDataBeingModified()
                && !order.isUsingTheOwnerScenario();
        if (!newOrderVersionNeeded || userAcceptsCreateANewOrderVersion()) {
            transactionService.runOnTransaction(new IOnTransaction<Void>() {
                @Override
                public Void execute() {
                    saveOnTransaction(newOrderVersionNeeded);
                    return null;
                }
            });
            dontPoseAsTransientObjectAnymore(order);
        }
    }

    private void dontPoseAsTransientObjectAnymore(Collection<? extends BaseEntity> collection) {
        for(BaseEntity entity : collection) {
            entity.dontPoseAsTransientObjectAnymore();
        }
    }

    private void dontPoseAsTransientObjectAnymore(OrderElement orderElement) {
        orderElement.dontPoseAsTransientObjectAnymore();

        dontPoseAsTransientObjectAnymore(orderElement.getTaskSourcesFromBottomToTop());
        dontPoseAsTransientObjectAnymore(orderElement.getDirectAdvanceAssignments());
        dontPoseAsTransientObjectAnymore(orderElement.getDirectCriterionRequirement());
        dontPoseAsTransientObjectAnymore(orderElement.getLabels());
        dontPoseAsTransientObjectAnymore(orderElement.getTaskElements());
        dontPoseAsTransientObjectAnymore(orderElement.getHoursGroups());
        for(OrderElement child : orderElement.getAllChildren()) {
            dontPoseAsTransientObjectAnymore(child);
        }
    }

    private void saveOnTransaction(boolean newOrderVersionNeeded) {
        Order.checkConstraintOrderUniqueCode(order);
        HoursGroup.checkConstraintHoursGroupUniqueCode(order);<commit_msg>[Bug #696] Fixed bug, preventing the call to order.dontPoseAsTransientObjectAnymore().<commit_after>/*
 * This file is part of NavalPlan
 *
 * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
 * Desenvolvemento Tecnolóxico de Galicia
 *
 * 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.navalplanner.web.orders;

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

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.navalplanner.business.advance.entities.AdvanceMeasurement;
import org.navalplanner.business.advance.entities.DirectAdvanceAssignment;
import org.navalplanner.business.advance.entities.IndirectAdvanceAssignment;
import org.navalplanner.business.calendars.daos.IBaseCalendarDAO;
import org.navalplanner.business.calendars.entities.BaseCalendar;
import org.navalplanner.business.common.BaseEntity;
import org.navalplanner.business.common.IAdHocTransactionService;
import org.navalplanner.business.common.IOnTransaction;
import org.navalplanner.business.common.daos.IConfigurationDAO;
import org.navalplanner.business.common.daos.IOrderSequenceDAO;
import org.navalplanner.business.common.entities.Configuration;
import org.navalplanner.business.common.entities.OrderSequence;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.common.exceptions.ValidationException;
import org.navalplanner.business.externalcompanies.daos.IExternalCompanyDAO;
import org.navalplanner.business.externalcompanies.entities.ExternalCompany;
import org.navalplanner.business.labels.daos.ILabelDAO;
import org.navalplanner.business.labels.entities.Label;
import org.navalplanner.business.orders.daos.IHoursGroupDAO;
import org.navalplanner.business.orders.daos.IOrderDAO;
import org.navalplanner.business.orders.daos.IOrderElementDAO;
import org.navalplanner.business.orders.entities.HoursGroup;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.orders.entities.OrderLineGroup;
import org.navalplanner.business.orders.entities.TaskSource;
import org.navalplanner.business.orders.entities.TaskSource.TaskSourceSynchronization;
import org.navalplanner.business.planner.daos.ITaskElementDAO;
import org.navalplanner.business.planner.daos.ITaskSourceDAO;
import org.navalplanner.business.planner.entities.TaskElement;
import org.navalplanner.business.qualityforms.daos.IQualityFormDAO;
import org.navalplanner.business.qualityforms.entities.QualityForm;
import org.navalplanner.business.requirements.entities.DirectCriterionRequirement;
import org.navalplanner.business.resources.daos.ICriterionDAO;
import org.navalplanner.business.resources.daos.ICriterionTypeDAO;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.business.scenarios.IScenarioManager;
import org.navalplanner.business.scenarios.daos.IOrderVersionDAO;
import org.navalplanner.business.scenarios.daos.IScenarioDAO;
import org.navalplanner.business.scenarios.entities.OrderVersion;
import org.navalplanner.business.scenarios.entities.Scenario;
import org.navalplanner.business.templates.daos.IOrderElementTemplateDAO;
import org.navalplanner.business.templates.entities.OrderElementTemplate;
import org.navalplanner.business.templates.entities.OrderTemplate;
import org.navalplanner.business.users.daos.IOrderAuthorizationDAO;
import org.navalplanner.business.users.daos.IUserDAO;
import org.navalplanner.business.users.entities.OrderAuthorization;
import org.navalplanner.business.users.entities.OrderAuthorizationType;
import org.navalplanner.business.users.entities.User;
import org.navalplanner.business.users.entities.UserRole;
import org.navalplanner.web.orders.labels.LabelsOnConversation;
import org.navalplanner.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;
import org.zkoss.ganttz.IPredicate;
import org.zkoss.zul.Messagebox;

/**
 * Model for UI operations related to {@link Order}. <br />
 *
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 */
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class OrderModel implements IOrderModel {

    @Autowired
    private ICriterionTypeDAO criterionTypeDAO;

    @Autowired
    private IExternalCompanyDAO externalCompanyDAO;

    private final Map<CriterionType, List<Criterion>> mapCriterions = new HashMap<CriterionType, List<Criterion>>();

    private List<ExternalCompany> externalCompanies = new ArrayList<ExternalCompany>();

    @Autowired
    private IOrderDAO orderDAO;

    private Order order;

    private OrderElementTreeModel orderElementTreeModel;

    @Autowired
    private IOrderElementModel orderElementModel;

    @Autowired
    private ICriterionDAO criterionDAO;

    @Autowired
    private ILabelDAO labelDAO;

    @Autowired
    private IQualityFormDAO qualityFormDAO;

    @Autowired
    private IOrderElementDAO orderElementDAO;

    @Autowired
    private IOrderElementTemplateDAO templateDAO;

    @Autowired
    private ITaskSourceDAO taskSourceDAO;

    @Autowired
    private ITaskElementDAO taskElementDAO;

    @Autowired
    private IBaseCalendarDAO baseCalendarDAO;

    @Autowired
    private IConfigurationDAO configurationDAO;

    @Autowired
    private IOrderSequenceDAO orderSequenceDAO;

    @Autowired
    private IUserDAO userDAO;

    @Autowired
    private IOrderAuthorizationDAO orderAuthorizationDAO;

    private List<Order> orderList = new ArrayList<Order>();

    @Autowired
    private IScenarioDAO scenarioDAO;

    @Autowired
    private IScenarioManager scenarioManager;

    @Autowired
    private IAdHocTransactionService transactionService;

    @Autowired
    private IOrderVersionDAO orderVersionDAO;

    @Autowired
    private IHoursGroupDAO hoursGroupDAO;

    @Override
    @Transactional(readOnly = true)
    public List<Label> getLabels() {
        return getLabelsOnConversation().getLabels();
    }

    private LabelsOnConversation labelsOnConversation;


    private LabelsOnConversation getLabelsOnConversation() {
        if (labelsOnConversation == null) {
            labelsOnConversation = new LabelsOnConversation(labelDAO);
        }
        return labelsOnConversation;
    }

    private QualityFormsOnConversation qualityFormsOnConversation;

    private Scenario currentScenario;

    private List<Scenario> derivedScenarios = new ArrayList<Scenario>();

    private boolean isEditing = false;

    private QualityFormsOnConversation getQualityFormsOnConversation() {
        if (qualityFormsOnConversation == null) {
            qualityFormsOnConversation = new QualityFormsOnConversation(
                    qualityFormDAO);
        }
        return qualityFormsOnConversation;
    }

    @Override
    public List<QualityForm> getQualityForms() {
        return getQualityFormsOnConversation().getQualityForms();
    }

    @Override
    public void addLabel(Label label) {
        getLabelsOnConversation().addLabel(label);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Order> getOrders() {

        User user;
        try {
            user = userDAO.findByLoginName(SecurityUtils.getSessionUserLoginName());
        }
        catch(InstanceNotFoundException e) {
            //this case shouldn't happen, because it would mean that there isn't a logged user
            //anyway, if it happenned we return an empty list
            return new ArrayList<Order>();
        }
        getLabelsOnConversation().reattachLabels();
        List<Order> orders = orderDAO.getOrdersByReadAuthorizationByScenario(
                user, scenarioManager.getCurrent());

        initializeOrders(orders);
        return orders;
    }

    private void initializeOrders(List<Order> list) {
        for (Order order : list) {
            orderDAO.reattachUnmodifiedEntity(order);
            if (order.getCustomer() != null) {
                order.getCustomer().getName();
            }
            order.getAdvancePercentage();
            for (Label label : order.getLabels()) {
                label.getName();
            }
            order.getScenarios().size();
        }
        this.orderList = list;
    }

    private void loadCriterions() {
        mapCriterions.clear();
        List<CriterionType> criterionTypes = criterionTypeDAO
                .getCriterionTypes();
        for (CriterionType criterionType : criterionTypes) {
            List<Criterion> criterions = new ArrayList<Criterion>(criterionDAO
                    .findByType(criterionType));

            mapCriterions.put(criterionType, criterions);
        }
    }

    @Override
    public Map<CriterionType, List<Criterion>> getMapCriterions(){
        final Map<CriterionType, List<Criterion>> result =
                new HashMap<CriterionType, List<Criterion>>();
        result.putAll(mapCriterions);
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public void initEdit(Order order) {
        Validate.notNull(order);
        isEditing = true;
        loadNeededDataForConversation();
        this.order = getFromDB(order);
        this.orderElementTreeModel = new OrderElementTreeModel(this.order);
        forceLoadAdvanceAssignmentsAndMeasurements(this.order);
        forceLoadCriterionRequirements(this.order);
        forceLoadCalendar(this.getCalendar());
        forceLoadCustomer(this.order.getCustomer());
        forceLoadLabels(this.order);
        currentScenario = scenarioManager.getCurrent();
        this.order.useSchedulingDataFor(currentScenario);
        loadTasks(this.order);
    }

    private void loadTasks(Order order) {
        TaskSource taskSource = order.getTaskSource();
        if (taskSource == null) {
            return;
        }
        loadTask(taskSource.getTask());
    }

    private void loadTask(TaskElement task) {
        task.getDependenciesWithThisDestination().size();
        task.getDependenciesWithThisOrigin().size();
        if (!task.isLeaf()) {
            for (TaskElement each : task.getChildren()) {
                loadTask(each);
            }
        }
    }

    private void forceLoadLabels(OrderElement orderElement) {
        orderElement.getLabels().size();
        for (OrderElement each : orderElement.getChildren()) {
            forceLoadLabels(each);
        }
    }

    private void forceLoadCustomer(ExternalCompany customer) {
        if (customer != null) {
            customer.getName();
        }
    }

    private void loadNeededDataForConversation() {
        loadCriterions();
        initializeCacheLabels();
        getQualityFormsOnConversation().initialize();
        loadExternalCompaniesAreClient();
    }

    private void reattachNeededDataForConversation() {
        reattachCriterions();
        reattachLabels();
        getQualityFormsOnConversation().reattach();
    }

    private void initializeCacheLabels() {
        getLabelsOnConversation().initializeLabels();
    }

    private static void forceLoadCriterionRequirements(OrderElement orderElement) {
        orderElement.getHoursGroups().size();
        for (HoursGroup hoursGroup : orderElement.getHoursGroups()) {
            attachDirectCriterionRequirement(hoursGroup
                    .getDirectCriterionRequirement());
        }
        attachDirectCriterionRequirement(orderElement
                .getDirectCriterionRequirement());

        for (OrderElement child : orderElement.getChildren()) {
            forceLoadCriterionRequirements(child);
        }
    }

    private static void attachDirectCriterionRequirement(
            Set<DirectCriterionRequirement> requirements) {
        for (DirectCriterionRequirement requirement : requirements) {
            requirement.getChildren().size();
            requirement.getCriterion().getName();
            requirement.getCriterion().getType().getName();
        }
    }

    private void forceLoadAdvanceAssignmentsAndMeasurements(
            OrderElement orderElement) {
        for (DirectAdvanceAssignment directAdvanceAssignment : orderElement
                .getDirectAdvanceAssignments()) {
            directAdvanceAssignment.getAdvanceType().getUnitName();
            for (AdvanceMeasurement advanceMeasurement : directAdvanceAssignment
                    .getAdvanceMeasurements()) {
                advanceMeasurement.getValue();
            }
        }

        if (orderElement instanceof OrderLineGroup) {
            for (IndirectAdvanceAssignment indirectAdvanceAssignment : ((OrderLineGroup) orderElement)
                    .getIndirectAdvanceAssignments()) {
                indirectAdvanceAssignment.getAdvanceType().getUnitName();
            }

            for (OrderElement child : orderElement.getChildren()) {
                forceLoadAdvanceAssignmentsAndMeasurements(child);
            }
        }
    }

    private Order getFromDB(Order order) {
        try {
            return orderDAO.find(order.getId());
        } catch (InstanceNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    @Transactional(readOnly = true)
    public void prepareForCreate() throws ConcurrentModificationException {
        loadNeededDataForConversation();
        this.order = Order.create();
        initializeOrder();
        initializeCalendar();
        currentScenario = scenarioManager.getCurrent();
        addOrderToCurrentScenario(this.order);
        this.order.useSchedulingDataFor(currentScenario);
    }

    private OrderVersion addOrderToCurrentScenario(Order order) {
        OrderVersion orderVersion = currentScenario.addOrder(order);
        order.setVersionForScenario(currentScenario, orderVersion);
        derivedScenarios = scenarioDAO.getDerivedScenarios(currentScenario);
        for (Scenario scenario : derivedScenarios) {
            scenario.addOrder(order, orderVersion);
        }
        return orderVersion;
    }

    private void initializeOrder() {
        this.orderElementTreeModel = new OrderElementTreeModel(this.order);
        this.order.setInitDate(new Date());
        setDefaultOrderCode();
        this.order.setCodeAutogenerated(true);
    }

    private void initializeCalendar() {
        this.order.setCalendar(getDefaultCalendar());
    }

    @Override
    @Transactional(readOnly = true)
    public void prepareCreationFrom(OrderTemplate template) {
        loadNeededDataForConversation();
        this.order = createOrderFrom((OrderTemplate) templateDAO
                .findExistingEntity(template.getId()));
        forceLoadAdvanceAssignmentsAndMeasurements(this.order);
        initializeOrder();
    }

    private Order createOrderFrom(OrderTemplate template) {
        return template.createOrder(scenarioManager.getCurrent());
    }

    private OrderElement createOrderElementFrom(OrderLineGroup parent,
            OrderElementTemplate template) {
        Validate.notNull(parent);
        return template.createElement(parent);
    }

    private void setDefaultOrderCode() throws ConcurrentModificationException {
        String code = orderSequenceDAO.getNextOrderCode();
        if (code == null) {
            throw new ConcurrentModificationException(
                    _("Could not get order code, please try again later"));
        }
        this.order.setCode(code);
    }

    @Override
    @Transactional(readOnly = true)
    public OrderElement createFrom(OrderLineGroup parent,
            OrderElementTemplate template) {
        reattachNeededDataForConversation();
        OrderElement result = createOrderElementFrom(parent, templateDAO
                .findExistingEntity(template.getId()));
        if (isCodeAutogenerated()) {
            setAllCodeToNull(result);
        }
        forceLoadAdvanceAssignmentsAndMeasurements(result);
        return result;
    }

    private void setAllCodeToNull(OrderElement result) {
        result.setCode(null);
        for (OrderElement each : result.getChildren()) {
            setAllCodeToNull(each);
        }
    }

    @Override
    public void save() throws ValidationException {
        final boolean newOrderVersionNeeded = isEditing
                && order.hasSchedulingDataBeingModified()
                && !order.isUsingTheOwnerScenario();
        if (!newOrderVersionNeeded || userAcceptsCreateANewOrderVersion()) {
            transactionService.runOnTransaction(new IOnTransaction<Void>() {
                @Override
                public Void execute() {
                    saveOnTransaction(newOrderVersionNeeded);
                    return null;
                }
            });
            dontPoseAsTransientObjectAnymore(order);
        }
    }

    private void dontPoseAsTransientObjectAnymore(Collection<? extends BaseEntity> collection) {
        for(BaseEntity entity : collection) {
            entity.dontPoseAsTransientObjectAnymore();
        }
    }

    private void dontPoseAsTransientObjectAnymore(OrderElement orderElement) {
        dontPoseAsTransientObjectAnymore(orderElement.getTaskSourcesFromBottomToTop());
        dontPoseAsTransientObjectAnymore(orderElement.getDirectAdvanceAssignments());
        dontPoseAsTransientObjectAnymore(orderElement.getDirectCriterionRequirement());
        dontPoseAsTransientObjectAnymore(orderElement.getLabels());
        dontPoseAsTransientObjectAnymore(orderElement.getTaskElements());
        dontPoseAsTransientObjectAnymore(orderElement.getHoursGroups());
        for(OrderElement child : orderElement.getAllChildren()) {
            child.dontPoseAsTransientObjectAnymore();
            dontPoseAsTransientObjectAnymore(child);
        }
    }

    private void saveOnTransaction(boolean newOrderVersionNeeded) {
        Order.checkConstraintOrderUniqueCode(order);
        HoursGroup.checkConstraintHoursGroupUniqueCode(order);
{'fraction_non_alphanumeric': 0.08266940127790487, 'fraction_numerical': 0.0006573584707212537, 'mean_word_length': 3.4038906901343213, 'pattern_counts': {'":': 0, '<': 81, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7662979', 'n_tokens_mistral': 10160, 'n_tokens_neox': 10141, 'n_words': 1847}