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

Java tutorial

Introduction

Here is the source code for org.libreplan.web.orders.ManageOrderElementAdvancesController.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.
 *
 * 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.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

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.entities.AdvanceAssignment;
import org.libreplan.business.advance.entities.AdvanceMeasurement;
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.entities.OrderElement;
import org.libreplan.web.common.IMessagesForUser;
import org.libreplan.web.common.Level;
import org.libreplan.web.common.MessagesForUser;
import org.libreplan.web.common.Util;
import org.zkoss.util.IllegalSyntaxException;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Button;
import org.zkoss.zul.Chart;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Decimalbox;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Radio;
import org.zkoss.zul.Tabbox;
import org.zkoss.zul.XYModel;

/**
 * Controller for show the advances of the selected order element.
 * <br />
 *
 * @author Susana Montes Pedreria <smontes@wirelessgalicia.com>
 */

public class ManageOrderElementAdvancesController extends GenericForwardComposer {

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

    private IMessagesForUser messagesForUser;

    private int indexSelectedItem = -1;

    private IManageOrderElementAdvancesModel manageOrderElementAdvancesModel;

    private AdvanceTypeListRenderer advanceTypeListRenderer = new AdvanceTypeListRenderer();

    private AdvanceMeasurementRenderer advanceMeasurementRenderer = new AdvanceMeasurementRenderer();

    private Set<AdvanceAssignment> selectedAdvances = new HashSet<>();

    private Component messagesContainerAdvances;

    private Tabbox tabboxOrderElement;

    private IOrderElementModel orderElementModel;

    private Listbox editAdvances;

    private Listbox editAdvancesMeasurement;

    private Chart chart;

    public ManageOrderElementAdvancesController() {
        manageOrderElementAdvancesModel = (IManageOrderElementAdvancesModel) SpringUtil
                .getBean("manageOrderElementAdvancesModel");
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);
        comp.setAttribute("manageOrderElementAdvancesController", this, true);
        messagesForUser = new MessagesForUser(messagesContainerAdvances);
    }

    public List<AdvanceMeasurement> getAdvanceMeasurements() {
        List<AdvanceMeasurement> measurements = manageOrderElementAdvancesModel.getAdvanceMeasurements();
        Collections.reverse(measurements);

        return measurements;
    }

    public List<AdvanceAssignment> getAdvanceAssignments() {
        return manageOrderElementAdvancesModel.getAdvanceAssignments();
    }

    public boolean close() {
        return save();
    }

    private void validate() {
        if (!validateDataForm())
            throw new IllegalSyntaxException(_("values are not valid, the values must not be null"));

        if (!validateReportGlobalAdvance())
            throw new IllegalSyntaxException(_("Invalid Spread values. At least one value should be true"));
    }

    public boolean save() {
        try {
            validate();
            manageOrderElementAdvancesModel.confirmSave();

            return true;
        } catch (DuplicateAdvanceAssignmentForOrderElementException e) {
            messagesForUser.showMessage(Level.ERROR, _("Cannot create another progress of the same type"));
        } catch (DuplicateValueTrueReportGlobalAdvanceException e) {
            messagesForUser.showMessage(Level.ERROR, _("Invalid Spread values. At least one value should be true"));
        } catch (IllegalSyntaxException e) {
            messagesForUser.showMessage(Level.ERROR, e.getMessage());
        } catch (InstanceNotFoundException e) {
            messagesForUser.showMessage(Level.ERROR, e.getMessage());
            LOG.error("Couldn't find element: " + e.getKey(), e);
        }
        increaseScreenHeight();

        return false;
    }

    public void openWindow(IOrderElementModel orderElementModel) {
        setOrderElementModel(orderElementModel);
        manageOrderElementAdvancesModel.initEdit(getOrderElement());
        selectedAdvances.clear();
        selectedAdvances.addAll(getAdvanceAssignments());
        createAndLoadBindings();
        selectSpreadAdvanceLine();
    }

    public void openWindow(OrderElement orderElement) {
        manageOrderElementAdvancesModel.initEdit(orderElement);
        selectedAdvances.clear();
        selectedAdvances.addAll(getAdvanceAssignments());
        createAndLoadBindings();
        selectSpreadAdvanceLine();
    }

    void createAndLoadBindings() {
        Util.createBindingsFor(self);
        Util.reloadBindings(self);
    }

    public void setOrderElementModel(IOrderElementModel orderElementModel) {
        this.orderElementModel = orderElementModel;
    }

    private OrderElement getOrderElement() {
        return orderElementModel.getOrderElement();
    }

    private void increaseScreenHeight() {
        if ((tabboxOrderElement != null)
                && (tabboxOrderElement.getHeight() == null || !"680px".equals(tabboxOrderElement.getHeight()))) {

            tabboxOrderElement.setHeight("680px");
            tabboxOrderElement.invalidate();
        }
    }

    private void reloadAdvances() {
        Util.reloadBindings(self);
        setSelectedAdvanceLine();
    }

    private void setSelectedAdvanceLine() {
        if ((indexSelectedItem > -1) && (indexSelectedItem < editAdvances.getItemCount())) {
            editAdvances.setSelectedItem(editAdvances.getItemAtIndex(indexSelectedItem));
            editAdvances.invalidate();
        }
    }

    public void selectAdvanceLine(Listitem selectedItem) {
        /* Validate the previous advance line before changing the selected advance */
        setSelectedAdvanceLine();
        findErrorsInMeasurements();

        /*
         * Preparation to select the advance line.
         * Set the current selected index that will show when the grid reloads.
         */
        if (selectedItem != null) {
            AdvanceAssignment advance = selectedItem.getValue();
            indexSelectedItem = editAdvances.getIndexOfItem(selectedItem);
            showInfoAbout(advance);
            prepareEditAdvanceMeasurements(advance);
            reloadAdvances();
        }
    }

    public void selectAdvanceLine(int index) {
        indexSelectedItem = index;
        if ((indexSelectedItem >= 0) && (indexSelectedItem < getAdvanceAssignments().size())) {
            prepareEditAdvanceMeasurements(getAdvanceAssignments().get(indexSelectedItem));
        }
        reloadAdvances();
    }

    public void selectSpreadAdvanceLine() {
        AdvanceAssignment advance = manageOrderElementAdvancesModel.getSpreadAdvance();
        if (advance != null) {
            indexSelectedItem = getAdvanceAssignments().indexOf(advance);
            showInfoAbout(advance);
            prepareEditAdvanceMeasurements(advance);
        } else {
            selectAdvanceLine(getAdvanceAssignments().size() - 1);
        }
        reloadAdvances();
    }

    private void showInfoAbout(AdvanceAssignment advance) {
        if (manageOrderElementAdvancesModel.isSubcontractedAdvanceTypeAndSubcontractedTask(advance))
            showErrorMessage(_("Subcontractor values are read only "
                    + "because they were reported by the subcontractor company."));

    }

    public void prepareEditAdvanceMeasurements(AdvanceAssignment advance) {
        if (advance != null && advance.getAdvanceType() != null) {
            manageOrderElementAdvancesModel.prepareEditAdvanceMeasurements(advance);
        }
    }

    /** It should be public! */
    public void goToCreateLineAdvanceAssignment() {
        findErrorsInMeasurements();
        boolean fineResult = manageOrderElementAdvancesModel.addNewLineAdvanceAssignment();
        if (fineResult) {
            int position = getAdvanceAssignments().size() - 1;
            selectAdvanceLine(position);
            selectedAdvances.add(getAdvanceAssignments().get(position));
        } else {
            showMessageNotAddMoreAdvances();
        }
    }

    public void goToCreateLineAdvanceMeasurement() {
        AdvanceMeasurement newMeasure = manageOrderElementAdvancesModel.addNewLineAdvanceMeasurement();
        if ((newMeasure != null)
                && (manageOrderElementAdvancesModel.hasConsolidatedAdvances(newMeasure.getAdvanceAssignment())))
            newMeasure.setDate(null);

        reloadAdvances();
    }

    public void goToRemoveLineAdvanceAssignment(Listitem listItem) {
        AdvanceAssignment advance = listItem.getValue();

        if ((editAdvances.getItemCount() > 1) && (advance.getReportGlobalAdvance()))
            showMessageDeleteSpread();

        else if (manageOrderElementAdvancesModel.hasConsolidatedAdvances(advance))
            showErrorMessage(_("Progress Assignment cannot be deleted or changed. "
                    + "Progress Assignment contains Progress Consolidations values"));
        else {
            manageOrderElementAdvancesModel.removeLineAdvanceAssignment(advance);
            selectedAdvances.remove(advance);

            if (indexSelectedItem == editAdvances.getIndexOfItem(listItem))
                selectSpreadAdvanceLine();
            else if (indexSelectedItem > editAdvances.getIndexOfItem(listItem))
                selectAdvanceLine(indexSelectedItem - 1);
            else {
                prepareEditAdvanceMeasurements(getAdvanceAssignments().get(indexSelectedItem));
                reloadAdvances();
            }

        }
    }

    public void goToRemoveLineAdvanceMeasurement(Listitem listItem) {
        AdvanceMeasurement advance = listItem.getValue();
        if (manageOrderElementAdvancesModel.canRemoveOrChange(advance)) {
            manageOrderElementAdvancesModel.removeLineAdvanceMeasurement(advance);
            reloadAdvances();
        } else
            showErrorMessage(
                    _("Progress Measurement cannot be deleted. Progress Measurement already consolidated"));
    }

    /** It should be public! */
    public String getInfoAdvance() {
        String infoAdvanceAssignment = manageOrderElementAdvancesModel.getInfoAdvanceAssignment();

        return infoAdvanceAssignment.isEmpty() ? _("Progress measurements")
                : _("Progress measurements") + ": " + infoAdvanceAssignment;
    }

    public boolean isReadOnlyAdvanceMeasurements() {
        return manageOrderElementAdvancesModel.isReadOnlyAdvanceMeasurements();
    }

    /** It should be public! */
    public AdvanceTypeListRenderer getAdvancesRenderer() {
        return advanceTypeListRenderer;
    }

    public void updatesValue() {
        this.setPercentage();
        this.setCurrentValue();
    }

    public class AdvanceTypeListRenderer implements ListitemRenderer {

        @Override
        public void render(Listitem listitem, Object o, int i) throws Exception {
            final AdvanceAssignment advance = (AdvanceAssignment) o;
            listitem.setValue(advance);

            Boolean readOnlyAdvance = false;
            boolean isQualityForm = false;

            if (advance.getAdvanceType() != null) {
                isQualityForm = manageOrderElementAdvancesModel.isQualityForm(advance);
                readOnlyAdvance = manageOrderElementAdvancesModel.isReadOnly(advance);

                if (!readOnlyAdvance
                        && manageOrderElementAdvancesModel.isSubcontractedAdvanceTypeAndSubcontractedTask(advance))

                    readOnlyAdvance = true;

            }

            if ((advance instanceof DirectAdvanceAssignment)
                    && ((DirectAdvanceAssignment) advance).getAdvanceMeasurements().isEmpty() && !isQualityForm
                    && !readOnlyAdvance)

                appendComboboxAdvanceType(listitem);
            else
                appendLabelAdvanceType(listitem);

            appendDecimalBoxMaxValue(listitem, isQualityForm || readOnlyAdvance);
            appendDecimalBoxValue(listitem);
            appendLabelPercentage(listitem);
            appendDateBoxDate(listitem);
            appendRadioSpread(listitem);
            appendCalculatedCheckbox(listitem);
            appendChartCheckbox(listitem);
            appendOperations(listitem, readOnlyAdvance);
        }

        private void appendDecimalBoxMaxValue(final Listitem listItem, boolean isQualityFormOrReadOnly) {
            final AdvanceAssignment advanceAssignment = listItem.getValue();
            final Decimalbox maxValue = new Decimalbox();
            maxValue.setScale(2);
            maxValue.setSclass("decimal-max-value");

            final DirectAdvanceAssignment directAdvanceAssignment;

            boolean isAdvanceAssignmentEquals = advanceAssignment.getAdvanceType() != null
                    && advanceAssignment.getAdvanceType().getPercentage();

            if ((advanceAssignment instanceof IndirectAdvanceAssignment) || isQualityFormOrReadOnly
                    || isAdvanceAssignmentEquals
                    || manageOrderElementAdvancesModel.hasConsolidatedAdvances(advanceAssignment)) {

                maxValue.setDisabled(true);
            }

            if (advanceAssignment instanceof IndirectAdvanceAssignment) {

                directAdvanceAssignment = manageOrderElementAdvancesModel
                        .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advanceAssignment);
            } else {
                directAdvanceAssignment = (DirectAdvanceAssignment) advanceAssignment;
            }

            Util.bind(maxValue, () -> directAdvanceAssignment.getMaxValue(), value -> {
                if (!manageOrderElementAdvancesModel.hasConsolidatedAdvances(advanceAssignment))
                    directAdvanceAssignment.setMaxValue(value);
            });

            maxValue.addEventListener(Events.ON_CHANGE, (EventListener) event -> {
                if (manageOrderElementAdvancesModel.hasConsolidatedAdvances(advanceAssignment))
                    throw new WrongValueException(maxValue, _("Progress Assignment cannot be deleted or changed. "
                            + "Progress Assignment contains Progress Consolidations values"));
                else {
                    setPercentage();
                    reloadAdvances();
                }
            });

            Listcell listCell = new Listcell();
            listCell.appendChild(maxValue);
            listItem.appendChild(listCell);
            maxValue.setConstraint(checkMaxValue());
        }

        private void appendComboboxAdvanceType(final Listitem listItem) {
            final DirectAdvanceAssignment advance = listItem.getValue();
            final Combobox comboAdvanceTypes = new Combobox();
            final List<AdvanceType> listAdvanceType = manageOrderElementAdvancesModel
                    .getPossibleAdvanceTypes(advance);

            for (AdvanceType advanceType : listAdvanceType) {

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

                    Comboitem comboItem = new Comboitem();
                    comboItem.setValue(advanceType);
                    comboItem.setLabel(advanceType.getUnitName());
                    comboItem.setParent(comboAdvanceTypes);

                    if ((advance.getAdvanceType() != null)
                            && (advance.getAdvanceType().getId().equals(advanceType.getId()))) {

                        comboAdvanceTypes.setSelectedItem(comboItem);
                    }
                }
            }

            comboAdvanceTypes.addEventListener(Events.ON_SELECT, (EventListener) event -> {
                setMaxValue(listItem, comboAdvanceTypes);
                cleanFields(advance);
                setPercentage();
                reloadAdvances();
            });

            Util.bind(comboAdvanceTypes, () -> comboAdvanceTypes.getSelectedItem(), comboItem -> {
                if ((comboItem != null) && (comboItem.getValue() != null)
                        && (comboItem.getValue() instanceof AdvanceType)) {

                    AdvanceType advanceType = comboItem.getValue();
                    advance.setAdvanceType(advanceType);
                    advance.setMaxValue(manageOrderElementAdvancesModel.getMaxValue(advanceType));
                }
            });

            Listcell listCell = new Listcell();
            listCell.appendChild(comboAdvanceTypes);
            listItem.appendChild(listCell);
        }

        private void appendLabelAdvanceType(final Listitem listItem) {
            final AdvanceAssignment advance = listItem.getValue();
            Label unitName = new Label(advance.getAdvanceType().getUnitName());
            Listcell listCell = new Listcell();
            listCell.appendChild(unitName);
            listItem.appendChild(listCell);
        }

        private void appendDecimalBoxValue(final Listitem listItem) {
            final AdvanceAssignment advanceAssignment = listItem.getValue();
            Decimalbox value = new Decimalbox();
            value.setScale(2);
            value.setDisabled(true);

            DirectAdvanceAssignment directAdvanceAssignment;

            if (advanceAssignment instanceof IndirectAdvanceAssignment) {

                directAdvanceAssignment = manageOrderElementAdvancesModel
                        .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advanceAssignment);
            } else {
                directAdvanceAssignment = (DirectAdvanceAssignment) advanceAssignment;
            }

            final AdvanceMeasurement advanceMeasurement = manageOrderElementAdvancesModel
                    .getLastAdvanceMeasurement(directAdvanceAssignment);

            if (advanceMeasurement != null) {
                Util.bind(value, () -> advanceMeasurement.getValue());
            }

            Listcell listCell = new Listcell();
            listCell.appendChild(value);
            listItem.appendChild(listCell);
        }

        private void appendLabelPercentage(final Listitem listItem) {
            final AdvanceAssignment advanceAssignment = listItem.getValue();
            Label percentage = new Label();

            DirectAdvanceAssignment directAdvanceAssignment;

            if (advanceAssignment instanceof IndirectAdvanceAssignment) {

                directAdvanceAssignment = manageOrderElementAdvancesModel
                        .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advanceAssignment);
            } else {
                directAdvanceAssignment = (DirectAdvanceAssignment) advanceAssignment;
            }

            final AdvanceMeasurement advanceMeasurement = manageOrderElementAdvancesModel
                    .getLastAdvanceMeasurement(directAdvanceAssignment);

            if (advanceMeasurement != null) {
                percentage.setValue(manageOrderElementAdvancesModel
                        .getPercentageAdvanceMeasurement(advanceMeasurement).toString() + " %");
            }

            Listcell listCell = new Listcell();
            listCell.appendChild(percentage);
            listItem.appendChild(listCell);
        }

        private void appendDateBoxDate(final Listitem listItem) {
            final AdvanceAssignment advanceAssignment = listItem.getValue();
            Datebox date = new Datebox();
            date.setDisabled(true);

            DirectAdvanceAssignment directAdvanceAssignment;

            if (advanceAssignment instanceof IndirectAdvanceAssignment) {
                directAdvanceAssignment = manageOrderElementAdvancesModel
                        .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advanceAssignment);
            } else {
                directAdvanceAssignment = (DirectAdvanceAssignment) advanceAssignment;
            }

            final AdvanceMeasurement advanceMeasurement = manageOrderElementAdvancesModel
                    .getLastAdvanceMeasurement(directAdvanceAssignment);

            if (advanceMeasurement != null) {
                Util.bind(date, () -> advanceMeasurement.getDate() == null ? null
                        : advanceMeasurement.getDate().toDateTimeAtStartOfDay().toDate());
            }

            Listcell listCell = new Listcell();
            listCell.appendChild(date);
            listItem.appendChild(listCell);
        }

        private void appendRadioSpread(final Listitem listItem) {
            final AdvanceAssignment advanceAssignment = listItem.getValue();

            final Radio reportGlobalAdvance = Util.bind(new Radio(),
                    () -> advanceAssignment.getReportGlobalAdvance(), value -> {
                        advanceAssignment.setReportGlobalAdvance(value);
                        setReportGlobalAdvance(listItem);
                    });

            Listcell listCell = new Listcell();
            listCell.appendChild(reportGlobalAdvance);
            listItem.appendChild(listCell);

            if (((AdvanceAssignment) listItem.getValue()).getReportGlobalAdvance()) {
                reportGlobalAdvance.getRadiogroup().setSelectedItem(reportGlobalAdvance);
                reportGlobalAdvance.getRadiogroup().invalidate();
            }
        }

        private void appendCalculatedCheckbox(final Listitem listItem) {
            final AdvanceAssignment advance = listItem.getValue();
            Checkbox calculated = new Checkbox();
            boolean isCalculated = advance instanceof IndirectAdvanceAssignment;
            calculated.setChecked(isCalculated);
            calculated.setDisabled(true);

            Listcell listCell = new Listcell();
            listCell.appendChild(calculated);
            listItem.appendChild(listCell);
        }

        private void appendChartCheckbox(final Listitem listItem) {
            final AdvanceAssignment advance = listItem.getValue();
            final Checkbox chartCheckbox = new Checkbox();

            chartCheckbox.setChecked(selectedAdvances.contains(advance));

            chartCheckbox.addEventListener(Events.ON_CHECK, (EventListener) event -> {
                if (chartCheckbox.isChecked())
                    selectedAdvances.add(advance);
                else
                    selectedAdvances.remove(advance);

                reloadAdvances();
            });

            Listcell listCell = new Listcell();
            listCell.appendChild(chartCheckbox);
            listItem.appendChild(listCell);
        }

        private void appendOperations(final Listitem listItem, Boolean readOnly) {
            Hbox hbox = new Hbox();
            appendAddMeasurement(hbox, listItem, readOnly);
            appendRemoveButton(hbox, listItem, readOnly);

            Listcell listCell = new Listcell();
            listCell.appendChild(hbox);
            listItem.appendChild(listCell);
        }

        private void appendAddMeasurement(final Hbox hbox, final Listitem listItem, Boolean readOnly) {
            final AdvanceAssignment advance = listItem.getValue();
            final Button addMeasurementButton = createAddMeasurementButton();

            addMeasurementButton.addEventListener(Events.ON_CLICK, (EventListener) event -> {
                if (!listItem.equals(editAdvances.getSelectedItem()))
                    selectAdvanceLine(listItem);

                goToCreateLineAdvanceMeasurement();
            });

            if ((advance.getAdvanceType() != null) && (advance.getAdvanceType().isQualityForm())) {
                addMeasurementButton.setDisabled(true);
                addMeasurementButton
                        .setTooltiptext(_("Progress that are reported by quality forms can not be modified"));

            } else if ((advance.getAdvanceType() != null) && (advance.getAdvanceType().isReadOnly())) {
                addMeasurementButton.setDisabled(true);
                addMeasurementButton.setTooltiptext(_("This progress type cannot be modified"));

            } else if (advance instanceof IndirectAdvanceAssignment) {
                addMeasurementButton.setDisabled(true);
                addMeasurementButton.setTooltiptext(_("Calculated progress can not be modified"));

            } else if (readOnly) {
                addMeasurementButton.setDisabled(true);
                addMeasurementButton.setTooltiptext(_("Subcontractor values are read only "
                        + "because they were reported by the subcontractor company."));
            }

            hbox.appendChild(addMeasurementButton);

        }

        private void appendRemoveButton(final Hbox hbox, final Listitem listItem, Boolean readOnly) {
            final AdvanceAssignment advance = listItem.getValue();
            final Button removeButton = createRemoveButton();

            removeButton.addEventListener(Events.ON_CLICK, event -> goToRemoveLineAdvanceAssignment(listItem));

            if ((advance.getAdvanceType() != null) && (advance.getAdvanceType().isQualityForm())) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Progress that are reported by quality forms cannot be modified"));

            } else if ((advance.getAdvanceType() != null) && (advance.getAdvanceType().isReadOnly())) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("This progress type cannot be modified"));

            } else if (advance instanceof IndirectAdvanceAssignment) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Calculated progress cannot be removed"));

            } else if (manageOrderElementAdvancesModel.hasConsolidatedAdvances(advance)) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Consolidated progress cannot be removed"));

            } else if (readOnly) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Subcontractor values are read only "
                        + "because they were reported by the subcontractor company"));

            } else if (manageOrderElementAdvancesModel.hasReportedProgress(advance)) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Advance assignment cannot be removed as "
                        + "it has advance measures that have already been reported to the customer"));
            }

            hbox.appendChild(removeButton);
        }

        private void setReportGlobalAdvance(final Listitem item) {
            boolean spread = true;

            if (manageOrderElementAdvancesModel.hasAnyConsolidatedAdvanceCurrentOrderElement()) {
                showErrorMessage(_("Spread progress cannot be changed "
                        + "if there is a consolidation in any progress assignment from root task"));
                spread = false;
            } else if (!radioSpreadIsConsolidated())
                for (AdvanceAssignment advance : getAdvanceAssignments()) {
                    advance.setReportGlobalAdvance(false);
                }
            else {
                spread = false;
            }

            ((AdvanceAssignment) item.getValue()).setReportGlobalAdvance(spread);
            Util.reloadBindings(editAdvances);
            setSelectedAdvanceLine();
        }

        private boolean radioSpreadIsConsolidated() {
            for (AdvanceAssignment advance : getAdvanceAssignments())

                if ((advance.getReportGlobalAdvance())
                        && (manageOrderElementAdvancesModel.hasConsolidatedAdvances(advance))) {

                    showErrorMessage(_("Spread progress cannot be changed "
                            + "if there is a consolidation in any progress assignment"));

                    return true;
                }

            return false;
        }

    }

    private void setMaxValue(final Listitem item, Combobox comboAdvanceTypes) {
        Listcell listCell = (Listcell) item.getChildren().get(1);
        Decimalbox miBox = (Decimalbox) listCell.getFirstChild();
        Comboitem selectedItem = comboAdvanceTypes.getSelectedItem();

        if (selectedItem != null) {
            AdvanceType advanceType = selectedItem.getValue();

            if (advanceType != null) {
                DirectAdvanceAssignment advance = item.getValue();
                advance.setMaxValue(manageOrderElementAdvancesModel.getMaxValue(advanceType));
                miBox.setValue(manageOrderElementAdvancesModel.getMaxValue(advanceType));
                miBox.invalidate();
            }
        }
    }

    private Constraint checkMaxValue() {
        return (comp, value) -> {
            Listitem item = (Listitem) comp.getParent().getParent();
            DirectAdvanceAssignment advance = item.getValue();
            if (!manageOrderElementAdvancesModel.hasConsolidatedAdvances(advance)
                    && (value == null || (BigDecimal.ZERO.compareTo((BigDecimal) value) >= 0))) {
                ((Decimalbox) comp).setValue(advance.getAdvanceType().getDefaultMaxValue());
                (comp).invalidate();

                throw new WrongValueException(comp, _("The max value must be greater than 0"));
            }
        };
    }

    private void setPercentage() {
        if ((this.indexSelectedItem < editAdvances.getItemCount()) && (this.indexSelectedItem >= 0)) {

            Listitem selectedItem = editAdvances.getItemAtIndex(indexSelectedItem);
            AdvanceAssignment advanceAssignment = selectedItem.getValue();

            DirectAdvanceAssignment directAdvanceAssignment;

            if (advanceAssignment instanceof IndirectAdvanceAssignment)

                directAdvanceAssignment = manageOrderElementAdvancesModel
                        .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advanceAssignment);
            else
                directAdvanceAssignment = (DirectAdvanceAssignment) advanceAssignment;

            final AdvanceMeasurement greatAdvanceMeasurement = this.manageOrderElementAdvancesModel
                    .getLastAdvanceMeasurement(directAdvanceAssignment);

            if (greatAdvanceMeasurement != null) {
                Listcell percentage = (Listcell) selectedItem.getChildren().get(3);

                ((Label) percentage.getFirstChild()).setValue(this.manageOrderElementAdvancesModel
                        .getPercentageAdvanceMeasurement(greatAdvanceMeasurement).toString() + " %");

                (percentage.getFirstChild()).invalidate();
            }
        }
    }

    private void setCurrentValue() {
        if (this.indexSelectedItem >= 0) {

            Listitem selectedItem = editAdvances.getItemAtIndex(indexSelectedItem);
            AdvanceAssignment advanceAssignment = selectedItem.getValue();

            DirectAdvanceAssignment directAdvanceAssignment;
            if (advanceAssignment instanceof IndirectAdvanceAssignment)

                directAdvanceAssignment = manageOrderElementAdvancesModel
                        .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advanceAssignment);

            else
                directAdvanceAssignment = (DirectAdvanceAssignment) advanceAssignment;

            final AdvanceMeasurement greatAdvanceMeasurement = this.manageOrderElementAdvancesModel
                    .getLastAdvanceMeasurement(directAdvanceAssignment);

            if (greatAdvanceMeasurement != null) {
                Listcell value = (Listcell) selectedItem.getChildren().get(2);
                ((Decimalbox) value.getFirstChild()).setValue(greatAdvanceMeasurement.getValue());
                (value.getFirstChild()).invalidate();
            }
        }
    }

    public void setCurrentDate() {
        this.manageOrderElementAdvancesModel.sortListAdvanceMeasurement();
        Util.reloadBindings(editAdvancesMeasurement);

        this.setCurrentDate();
        this.setPercentage();
        this.setCurrentValue();
        Util.reloadBindings(chart);
    }

    private void cleanFields(DirectAdvanceAssignment advance) {
        this.manageOrderElementAdvancesModel.cleanAdvance(advance);
    }

    private boolean validateDataForm() {
        return (validateListAdvanceAssignment()) && (validateListAdvanceMeasurement());
    }

    private boolean validateListAdvanceAssignment() {
        for (int i = 0; i < editAdvances.getChildren().size(); i++)

            if (editAdvances.getChildren().get(i) instanceof Listitem) {
                Listitem listItem = (Listitem) editAdvances.getChildren().get(i);
                AdvanceAssignment advance = listItem.getValue();

                if (advance != null) {

                    if (advance.getAdvanceType() == null)
                        throw new WrongValueException(getComboboxTypeBy(listItem), _("cannot be empty"));

                    DirectAdvanceAssignment directAdvanceAssignment;

                    if (advance instanceof IndirectAdvanceAssignment) {

                        directAdvanceAssignment = manageOrderElementAdvancesModel
                                .calculateFakeDirectAdvanceAssignment((IndirectAdvanceAssignment) advance);
                    } else
                        directAdvanceAssignment = (DirectAdvanceAssignment) advance;

                    if (directAdvanceAssignment != null && directAdvanceAssignment.getMaxValue() == null)
                        throw new WrongValueException(getDecimalboxMaxValueBy(listItem), _("cannot be empty"));
                }
            }

        return true;
    }

    private boolean validateListAdvanceMeasurement() {
        for (int i = 0; i < editAdvancesMeasurement.getChildren().size(); i++)

            if (editAdvancesMeasurement.getChildren().get(i) instanceof Listitem) {
                Listitem listItem = (Listitem) editAdvancesMeasurement.getChildren().get(i);
                AdvanceMeasurement advance = listItem.getValue();

                if (advance != null) {
                    // Validate the value of the advance measurement
                    Decimalbox valueBox = getDecimalboxBy(listItem);
                    validateMeasurementValue(valueBox, advance.getValue());

                    // Validate the date of the advance measurement
                    Datebox dateBox = getDateboxBy(listItem);

                    if (advance.getDate() == null) {
                        validateMeasurementDate(dateBox, null);
                    } else {
                        validateMeasurementDate(dateBox, advance.getDate().toDateTimeAtStartOfDay().toDate());
                    }
                }
            }

        return true;
    }

    private boolean validateAdvanceMeasurement(AdvanceMeasurement advance) {
        boolean result = true;
        // Validate the value of advance measurement
        if (advance.getValue() == null)
            result = false;
        else {
            String errorMessage = validateValueAdvanceMeasurement(advance);
            if (errorMessage != null)
                result = false;
        }

        // Validate the date of advance measurement
        if (advance.getDate() == null)
            result = false;
        else {
            String errorMessage = validateDateAdvanceMeasurement(advance.getDate(), advance);
            if (errorMessage != null)
                result = false;
        }

        return result;
    }

    private Combobox getComboboxTypeBy(Listitem item) {
        return (Combobox) (item.getChildren().get(0)).getFirstChild();
    }

    private Combobox getDecimalboxMaxValueBy(Listitem item) {
        return (Combobox) (item.getChildren().get(1)).getFirstChild();
    }

    private Decimalbox getDecimalboxBy(Listitem item) {
        return (Decimalbox) (item.getChildren().get(0)).getFirstChild();
    }

    private Datebox getDateboxBy(Listitem item) {
        return (Datebox) (item.getChildren().get(2)).getFirstChild();
    }

    private boolean validateReportGlobalAdvance() {
        boolean existItems = false;
        for (AdvanceAssignment advance : this.getAdvanceAssignments()) {
            existItems = true;
            if (advance.getReportGlobalAdvance())
                return true;
        }

        return !existItems;
    }

    /** It should be public! */
    public AdvanceMeasurementRenderer getAdvanceMeasurementRenderer() {
        return advanceMeasurementRenderer;
    }

    private class AdvanceMeasurementRenderer implements ListitemRenderer {

        @Override
        public void render(Listitem listitem, Object o, int i) throws Exception {
            AdvanceMeasurement advanceMeasurement = (AdvanceMeasurement) o;
            listitem.setValue(advanceMeasurement);

            appendDecimalBoxValue(listitem);
            appendLabelPercentage(listitem);
            appendDateboxDate(listitem);
            appendRemoveButton(listitem);
        }

        private void appendDecimalBoxValue(final Listitem listitem) {
            final AdvanceMeasurement advanceMeasurement = listitem.getValue();
            final Decimalbox decimalbox = new Decimalbox();
            Listcell listcell = new Listcell();
            listcell.appendChild(decimalbox);
            listitem.appendChild(listcell);

            decimalbox.setScale(calculateScale(advanceMeasurement));

            decimalbox.setDisabled(isReadOnlyAdvanceMeasurements()
                    || manageOrderElementAdvancesModel.hasConsolidatedAdvances(advanceMeasurement)
                    || manageOrderElementAdvancesModel.isAlreadyReportedProgress(advanceMeasurement));

            decimalbox.addEventListener(Events.ON_CHANGE, (EventListener) event -> {
                if (manageOrderElementAdvancesModel.canRemoveOrChange(advanceMeasurement)) {
                    updatesValue();
                    validateMeasurementValue(decimalbox, decimalbox.getValue());
                } else
                    throw new WrongValueException(decimalbox, _("Progress Measurement cannot be deleted."
                            + " Progress Measurement already consolidated"));
            });

            Util.bind(decimalbox, advanceMeasurement::getValue, value -> {
                if (manageOrderElementAdvancesModel.canRemoveOrChange(advanceMeasurement)) {
                    advanceMeasurement.setValue(value);
                    reloadAdvances();
                }
            });

            decimalbox.focus();
        }

        private void appendLabelPercentage(final Listitem listitem) {
            final AdvanceMeasurement advanceMeasurement = listitem.getValue();

            BigDecimal percentage = manageOrderElementAdvancesModel
                    .getPercentageAdvanceMeasurement(advanceMeasurement);
            Label percentageLabel = new Label(percentage.toString() + " %");

            Listcell listcell = new Listcell();
            listcell.appendChild(percentageLabel);
            listitem.appendChild(listcell);
        }

        private void appendDateboxDate(final Listitem listitem) {
            final AdvanceMeasurement advanceMeasurement = listitem.getValue();
            final Datebox date = new Datebox();

            Listcell listcell = new Listcell();
            listcell.appendChild(date);
            listitem.appendChild(listcell);

            date.setDisabled(isReadOnlyAdvanceMeasurements()
                    || manageOrderElementAdvancesModel.hasConsolidatedAdvances(advanceMeasurement)
                    || manageOrderElementAdvancesModel.isAlreadyReportedProgress(advanceMeasurement));

            date.addEventListener(Events.ON_CHANGE, (EventListener) event -> {
                if (manageOrderElementAdvancesModel.canRemoveOrChange(advanceMeasurement)) {
                    validateMeasurementDate(date, date.getValue());
                    setCurrentDate();
                } else
                    throw new WrongValueException(date, _("Progress Measurement cannot be deleted."
                            + " Progress Measurement already consolidated"));
            });

            Util.bind(date, () -> advanceMeasurement.getDate() == null ? null
                    : advanceMeasurement.getDate().toDateTimeAtStartOfDay().toDate(), value -> {
                        if (manageOrderElementAdvancesModel.canRemoveOrChange(advanceMeasurement)) {

                            LocalDate oldDate = advanceMeasurement.getDate();
                            advanceMeasurement.setDate(new LocalDate(value));

                            if (manageOrderElementAdvancesModel.hasConsolidatedAdvances(advanceMeasurement)) {
                                showMessagesConsolidation(new LocalDate(value));
                                advanceMeasurement.setDate(oldDate);
                            }

                            manageOrderElementAdvancesModel.sortListAdvanceMeasurement();
                            reloadAdvances();
                        }
                    });
        }

        private void appendRemoveButton(final Listitem listItem) {

            final AdvanceMeasurement measure = listItem.getValue();
            final Button removeButton = createRemoveButton();

            DirectAdvanceAssignment advance = (DirectAdvanceAssignment) measure.getAdvanceAssignment();

            if ((advance.getAdvanceType() != null) && (advance.getAdvanceType().isQualityForm())) {
                removeButton.setDisabled(true);

                removeButton.setTooltiptext(
                        _("Progress measurements that are reported " + "by quality forms cannot be removed"));

            } else if ((advance.getAdvanceType() != null) && (advance.getAdvanceType().isReadOnly())) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("This progress type cannot cannot be removed"));

            } else if (advance.isFake()) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Calculated progress measurements cannot be removed"));

            } else if (manageOrderElementAdvancesModel.hasConsolidatedAdvances(measure)) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Consolidated progress measurement cannot be removed"));

            } else if (manageOrderElementAdvancesModel.isAlreadyReportedProgress(measure)) {
                removeButton.setDisabled(true);
                removeButton.setTooltiptext(_("Values already sent to the customer. Values cannot be changed "));

            } else if (isReadOnlyAdvanceMeasurements()) {
                removeButton.setDisabled(isReadOnlyAdvanceMeasurements());

                removeButton.setTooltiptext(_("Subcontractor values are read only"
                        + " because they were reported by the subcontractor company."));
            }

            removeButton.addEventListener(Events.ON_CLICK, event -> goToRemoveLineAdvanceMeasurement(listItem));

            Listcell listCell = new Listcell();
            listCell.appendChild(removeButton);
            listItem.appendChild(listCell);
        }

        private int calculateScale(AdvanceMeasurement advanceMeasurement) {
            return advanceMeasurement.getAdvanceAssignment().getAdvanceType().getUnitPrecision()
                    .stripTrailingZeros().scale();
        }

        private void showMessagesConsolidation(LocalDate date) {
            String message = _("Progress measurement cannot be canged to {0}, because it is consolidated", date);
            showErrorMessage(message);
        }

    }

    private Button createAddMeasurementButton() {
        Button addButton = new Button();
        addButton.setLabel(_("Add measure"));
        addButton.setClass("add-button");
        addButton.setTooltiptext(_("Add new progress measurement"));

        return addButton;
    }

    /** It should be public! */
    public XYModel getChartData() {
        return this.manageOrderElementAdvancesModel.getChartData(selectedAdvances);
    }

    private Button createRemoveButton() {
        Button removeButton = new Button();
        removeButton.setSclass("icono");
        removeButton.setImage("/common/img/ico_borrar1.png");
        removeButton.setHoverImage("/common/img/ico_borrar.png");
        removeButton.setTooltiptext(_("Delete"));

        return removeButton;
    }

    public void refreshChangesFromOrderElement() {
        manageOrderElementAdvancesModel.refreshChangesFromOrderElement();
    }

    private void showMessageNotAddMoreAdvances() {
        String message = _("All progress types have already been assigned.");
        increaseScreenHeight();
        messagesForUser.showMessage(Level.ERROR, message);
    }

    public void refreshSelectedAdvance() {
        if ((indexSelectedItem < 0) || (indexSelectedItem >= getAdvanceAssignments().size()))
            selectSpreadAdvanceLine();

        selectAdvanceLine(indexSelectedItem);
    }

    private void showMessageDeleteSpread() {
        String message = _("Spread progress cannot be removed. Please select another progress as spread.");
        showErrorMessage(message);
    }

    private void showErrorMessage(String message) {
        increaseScreenHeight();
        messagesForUser.showMessage(Level.ERROR, message);
    }

    private String validateValueAdvanceMeasurement(AdvanceMeasurement measurement) {
        if (manageOrderElementAdvancesModel.greatThanMaxValue(measurement))
            return _("Value is not valid. It must be smaller than max value");

        if (!manageOrderElementAdvancesModel.isPrecisionValid(measurement))
            return _("Value must be a multiple of the precision value of the progress type: {0}",
                    manageOrderElementAdvancesModel.getUnitPrecision().stripTrailingZeros().toPlainString());

        if (manageOrderElementAdvancesModel.lessThanPreviousMeasurements())
            return _("Invalid value. Value must be greater than the value of previous progress.");

        return null;
    }

    private String validateDateAdvanceMeasurement(LocalDate value, AdvanceMeasurement measurement) {
        LocalDate oldDate = measurement.getDate();
        measurement.setDate(value);

        if (!manageOrderElementAdvancesModel.isDistinctValidDate(value, measurement))
            return _("Invalid date. Date must be unique for this Progress Assignment");

        if (manageOrderElementAdvancesModel.hasConsolidatedAdvances(measurement))
            measurement.setDate(oldDate);
        else {
            manageOrderElementAdvancesModel.sortListAdvanceMeasurement();
            if (manageOrderElementAdvancesModel.lessThanPreviousMeasurements())
                return _("Invalid value. Value must be greater than the value of previous progress.");
        }

        if (!isReadOnlyAdvanceMeasurements()) {

            LocalDate consolidatedUntil = manageOrderElementAdvancesModel
                    .getLastConsolidatedMeasurementDate(measurement.getAdvanceAssignment());

            if (consolidatedUntil != null && consolidatedUntil.compareTo(measurement.getDate()) >= 0) {
                return _("Date is not valid, it must be later than the last progress consolidation");
            }
            if (manageOrderElementAdvancesModel.isAlreadyReportedProgressWith(value)) {
                return _("Date is not valid, it must be later than the last progress reported to the customer");
            }
        }

        return null;
    }

    public boolean findErrorsInMeasurements() {
        boolean result = findPageWithError();
        validateListAdvanceMeasurement();

        return result;
    }

    private boolean findPageWithError() {
        int currentPage = editAdvancesMeasurement.getActivePage();
        int i = 0;
        int page = 0;
        changePage(page);

        for (Listitem item : editAdvancesMeasurement.getItems()) {
            AdvanceMeasurement advance = item.getValue();

            if (advance != null) {

                if (!validateAdvanceMeasurement(advance))
                    return true;

                i++;
                if (i == editAdvancesMeasurement.getPageSize()) {
                    i = 0;
                    changePage(++page);
                }
            }
        }
        changePage(currentPage);

        return false;
    }

    private void changePage(int page) {
        if ((page >= 0) && (page < editAdvancesMeasurement.getPageCount())) {
            editAdvancesMeasurement.setActivePage(page);
            editAdvancesMeasurement.invalidate();
        }
    }

    /** It should be public! */
    public void onPagingMeasurement() {
        validateListAdvanceMeasurement();
    }

    public void validateMeasurementDate(Component comp, Date value) {
        AdvanceMeasurement advanceMeasurement = getAdvanceMeasurementByComponent(comp);
        if (manageOrderElementAdvancesModel.canRemoveOrChange(advanceMeasurement)) {

            if (value == null && advanceMeasurement != null) {
                advanceMeasurement.setDate(null);
                ((Datebox) comp).setValue(null);
                throw new WrongValueException(comp, _("cannot be empty"));

            } else {
                String errorMessage = validateDateAdvanceMeasurement(new LocalDate(value), advanceMeasurement);
                LocalDate date = null;

                if (advanceMeasurement != null)
                    date = advanceMeasurement.getDate();

                if (date != null)
                    ((Datebox) comp).setValue(date.toDateTimeAtStartOfDay().toDate());

                if (errorMessage != null)
                    throw new WrongValueException(comp, errorMessage);
            }
        }
    }

    public void validateMeasurementValue(Component comp, Object value) {
        AdvanceMeasurement advanceMeasurement = getAdvanceMeasurementByComponent(comp);
        if ((advanceMeasurement != null)
                && (manageOrderElementAdvancesModel.canRemoveOrChange(advanceMeasurement))) {

            advanceMeasurement.setValue((BigDecimal) value);
            ((Decimalbox) comp).setValue((BigDecimal) value);

            if ((value) == null)
                throw new WrongValueException(comp, _("cannot be empty"));
            else {
                String errorMessage = validateValueAdvanceMeasurement(advanceMeasurement);
                if (errorMessage != null)
                    throw new WrongValueException(comp, errorMessage);
            }
        }
    }

    private AdvanceMeasurement getAdvanceMeasurementByComponent(Component comp) {
        try {
            Listitem item = (Listitem) comp.getParent().getParent();

            return (AdvanceMeasurement) item.getValue();
        } catch (Exception e) {
            return null;
        }
    }
}