org.libreplan.web.workreports.WorkReportCRUDController.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.web.workreports.WorkReportCRUDController.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.workreports;

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

import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.LocalDate;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.common.exceptions.ValidationException.InvalidValue;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.labels.entities.LabelType;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workreports.entities.HoursManagementEnum;
import org.libreplan.business.workreports.entities.WorkReport;
import org.libreplan.business.workreports.entities.WorkReportLabelTypeAssignment;
import org.libreplan.business.workreports.entities.WorkReportLine;
import org.libreplan.business.workreports.entities.WorkReportType;
import org.libreplan.business.workreports.valueobjects.DescriptionField;
import org.libreplan.business.workreports.valueobjects.DescriptionValue;
import org.libreplan.web.common.ConstraintChecker;
import org.libreplan.web.common.IMessagesForUser;
import org.libreplan.web.common.Level;
import org.libreplan.web.common.MessagesForUser;
import org.libreplan.web.common.OnlyOneVisible;
import org.libreplan.web.common.Util;
import org.libreplan.web.common.components.Autocomplete;
import org.libreplan.web.common.components.NewDataSortableColumn;
import org.libreplan.web.common.components.NewDataSortableGrid;
import org.libreplan.web.common.components.bandboxsearch.BandboxSearch;
import org.libreplan.web.common.entrypoints.IURLHandlerRegistry;
import org.libreplan.web.users.dashboard.IPersonalTimesheetController;
import org.zkoss.ganttz.IPredicate;
import org.zkoss.ganttz.util.ComponentsFinder;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.CheckEvent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.SelectEvent;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Button;
import org.zkoss.zul.Checkbox;
import org.zkoss.zul.Column;
import org.zkoss.zul.Columns;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Grid;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Popup;
import org.zkoss.zul.Row;
import org.zkoss.zul.RowRenderer;
import org.zkoss.zul.SimpleListModel;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Timebox;
import org.zkoss.zul.Window;

/**
 * Controller for CRUD actions over a {@link WorkReport}.
 *
 * @author Diego Pino Garca <dpino@igalia.com>
 * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
 * @author Vova Perebykivskyi <vova@libreplan-enterpsire.com>
 * @author Bogdan Bodnarjuk <bogdan@libreplan-enterpsire.com>
 */
public class WorkReportCRUDController extends GenericForwardComposer<Component>
        implements IWorkReportCRUDControllerEntryPoints {

    private static final String MOLD = "paging";

    private static final int PAGING = 10;

    private boolean cameBackList = false;

    private Window createWindow;

    private Window listWindow;

    private IWorkReportModel workReportModel;

    private IURLHandlerRegistry URLHandlerRegistry;

    private OnlyOneVisible visibility;

    private IMessagesForUser messagesForUser;

    private Component messagesContainer;

    private IWorkReportTypeCRUDControllerEntryPoints workReportTypeCRUD;

    private WorkReportListRenderer workReportListRenderer = new WorkReportListRenderer();

    private OrderedFieldsAndLabelsRowRenderer orderedFieldsAndLabelsRowRenderer = new OrderedFieldsAndLabelsRowRenderer();

    private NewDataSortableGrid listWorkReportLines;

    private Grid headingFieldsAndLabels;

    private Autocomplete autocompleteResource;

    private BandboxSearch bandboxSelectOrderElementInHead;

    private ListModel allHoursType;

    private static final String ITEM = "item";

    private static final int EXTRA_FIELD_MIN_WIDTH = 70;

    private static final int EXTRA_FIELD_MAX_WIDTH = 150;

    private static final int EXTRA_FIELD_PX_PER_CHAR = 5;

    private transient IPredicate predicate;

    private Grid listing;

    private Listbox listType;

    private Listbox listTypeToAssign;

    private Datebox filterStartDate;

    private Datebox filterFinishDate;

    private IPersonalTimesheetController personalTimesheetController;

    private Popup personalTimesheetsPopup;

    private Datebox personalTimesheetsDatebox;

    private BandboxSearch personalTimesheetsBandboxSearch;

    private WorkReportType firstType;

    private static final String ASCENDING = "ascending";

    public WorkReportCRUDController() {
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);
        listWorkReportLines = (NewDataSortableGrid) createWindow.getFellowIfAny("listWorkReportLines");
        messagesForUser = new MessagesForUser(messagesContainer);
        showMessageIfPersonalTimesheetWasSaved();

        injectsObjects();

        comp.setAttribute("controller", this);
        goToList();
        if (listType != null) {
            // listType is null in reports -> work report lines
            listType.setSelectedIndex(0);
        }
        initializeHoursType();

        URLHandlerRegistry.getRedirectorFor(IWorkReportCRUDControllerEntryPoints.class).register(this, page);
    }

    private void injectsObjects() {
        workReportModel = (IWorkReportModel) SpringUtil.getBean("workReportModel");
        URLHandlerRegistry = (IURLHandlerRegistry) SpringUtil.getBean("URLHandlerRegistry");

        workReportTypeCRUD = (IWorkReportTypeCRUDControllerEntryPoints) SpringUtil.getBean("workReportTypeCRUD");

        personalTimesheetController = (IPersonalTimesheetController) SpringUtil
                .getBean("personalTimesheetController");
    }

    private void showMessageIfPersonalTimesheetWasSaved() {
        String timesheetSave = Executions.getCurrent().getParameter("timesheet_saved");
        if (!StringUtils.isBlank(timesheetSave)) {
            messagesForUser.showMessage(Level.INFO, _("Personal timesheet saved"));
        }
    }

    private void initializeHoursType() {
        allHoursType = new SimpleListModel<>(workReportModel.getAllHoursType());
    }

    /**
     * Show confirm window for deleting {@link WorkReport}
     *
     * @param workReportDTO
     */
    public void showConfirmDelete(WorkReportDTO workReportDTO) {
        WorkReport workReport = workReportDTO.getWorkReport();

        final String workReportName = formatWorkReportName(workReport);

        int status = Messagebox.show(_("Confirm deleting {0}. Are you sure?", workReportName), "Delete",
                Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION);

        if (Messagebox.OK == status) {
            workReportModel.remove(workReport);
            messagesForUser.showMessage(Level.INFO, _("Timesheet removed successfully"));
            loadComponentslist(listWindow);
            Util.reloadBindings(listWindow);
        }
    }

    private String formatWorkReportName(WorkReport workReport) {
        return workReport.getWorkReportType().getName();
    }

    public List<WorkReportDTO> getWorkReportDTOs() {
        return workReportModel.getWorkReportDTOs();
    }

    private OnlyOneVisible getVisibility() {
        return (visibility == null) ? new OnlyOneVisible(createWindow, listWindow) : visibility;
    }

    public void saveAndExit() {
        if (save()) {
            goToList();
        }
    }

    public void saveAndContinue() {
        if (save()) {
            goToEditForm(getWorkReport());
        }
    }

    public boolean save() {
        ConstraintChecker.isValid(createWindow);
        workReportModel.generateWorkReportLinesIfIsNecessary();
        try {
            workReportModel.confirmSave();
            messagesForUser.showMessage(Level.INFO, _("Timesheet saved"));

            return true;
        } catch (ValidationException e) {
            showInvalidValues(e);
        } catch (Exception e) {
            if (!showInvalidProperty()) {
                throw new RuntimeException(e);
            }
        }

        return false;
    }

    /**
     * Shows invalid values for {@link WorkReport} and {@link WorkReportLine} entities.
     *
     * @param e
     */
    private void showInvalidValues(ValidationException e) {
        for (InvalidValue invalidValue : e.getInvalidValues()) {
            Object value = invalidValue.getRootBean();

            if (value instanceof WorkReport && validateWorkReport())
                messagesForUser.showInvalidValues(e);

            if (value instanceof WorkReportLine) {
                WorkReportLine workReportLine = (WorkReportLine) invalidValue.getRootBean();
                Row row = ComponentsFinder.findRowByValue(listWorkReportLines, workReportLine);
                if (row == null) {
                    messagesForUser.showInvalidValues(e);
                } else {
                    validateWorkReportLine(row, workReportLine);
                }
            }
        }
    }

    private boolean showInvalidProperty() {
        WorkReport workReport = getWorkReport();
        if (workReport != null) {
            if (!validateWorkReport()) {
                return true;
            }
            for (WorkReportLine each : workReport.getWorkReportLines()) {
                if (!validateWorkReportLine(each)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Validates {@link WorkReport} data constraints.
     *
     * @param invalidValue
     */
    private boolean validateWorkReport() {

        if (!getWorkReport().isDateMustBeNotNullIfIsSharedByLinesConstraint()) {
            Datebox datebox = (Datebox) createWindow.getFellowIfAny("date");
            showInvalidMessage(datebox, _("cannot be empty"));

            return false;
        }

        if (!getWorkReport().isResourceMustBeNotNullIfIsSharedByLinesConstraint()) {
            showInvalidMessage(autocompleteResource, _("cannot be empty"));

            return false;
        }

        if (!getWorkReport().isOrderElementMustBeNotNullIfIsSharedByLinesConstraint()) {
            showInvalidMessage(bandboxSelectOrderElementInHead, _("cannot be empty"));

            return false;
        }

        return true;
    }

    private boolean validateWorkReportLine(WorkReportLine workReportLine) {
        Row row = ComponentsFinder.findRowByValue(listWorkReportLines, workReportLine);

        return row != null && validateWorkReportLine(row, workReportLine);
    }

    /**
     * Validates {@link WorkReportLine} data constraints.
     *
     * @param invalidValue
     */
    @SuppressWarnings("unchecked")
    private boolean validateWorkReportLine(Row row, WorkReportLine workReportLine) {

        if (getWorkReportType().getDateIsSharedByLines()) {
            if (!validateWorkReport()) {
                return false;
            }
        } else if (workReportLine.getDate() == null) {
            Datebox date = getDateboxDate(row);
            if (date != null) {
                String message = _("cannot be empty");
                showInvalidMessage(date, message);
            }

            return false;
        }

        if (getWorkReportType().getResourceIsSharedInLines()) {
            if (!validateWorkReport()) {
                return false;
            }
        } else if (workReportLine.getResource() == null) {
            Autocomplete autoResource = getTextboxResource(row);
            if (autoResource != null) {
                String message = _("cannot be empty");
                showInvalidMessage(autoResource, message);
            }

            return false;
        }

        if (getWorkReportType().getOrderElementIsSharedInLines()) {
            if (!validateWorkReport()) {
                return false;
            }
        } else if (workReportLine.getOrderElement() == null) {
            BandboxSearch bandboxOrder = getTextboxOrder(row);

            if (bandboxOrder != null) {
                String message = _("cannot be empty");
                bandboxOrder.clear();
                showInvalidMessage(bandboxOrder, message);
            }

            return false;
        }

        if (!workReportLine.isClockStartMustBeNotNullIfIsCalculatedByClockConstraint()) {
            Timebox timeStart = getTimeboxStart(row);

            if (timeStart != null) {
                String message = _("cannot be empty");
                showInvalidMessage(timeStart, message);
            }

            return false;
        }

        if (!workReportLine.isClockFinishMustBeNotNullIfIsCalculatedByClockConstraint()) {
            Timebox timeFinish = getTimeboxFinish(row);

            if (timeFinish != null) {
                String message = _("cannot be empty");
                showInvalidMessage(timeFinish, message);
            }

            return false;
        }

        if (workReportLine.getEffort() == null) {
            Textbox effort = getEffort(row);

            if (effort == null) {
                String message = _("cannot be empty");
                showInvalidMessage(null, message);
            }

            if (effort != null && EffortDuration.zero()
                    .compareTo(EffortDuration.parseFromFormattedString(effort.getValue())) <= 0) {
                String message = _("Effort must be greater than zero");
                showInvalidMessage(effort, message);
            }

            return false;
        }

        if (!workReportLine.isHoursCalculatedByClockConstraint()) {
            Textbox effort = getEffort(row);

            if (effort != null) {
                String message = _("effort is not properly calculated based on clock");
                showInvalidMessage(effort, message);
            }

            return false;
        }

        if (workReportLine.getTypeOfWorkHours() == null) {
            // Locate TextboxOrder
            Listbox autoTypeOfHours = getTypeOfHours(row);

            if (autoTypeOfHours != null) {

                String message = autoTypeOfHours.getItems().isEmpty()
                        ? _("Hours types are empty. Please, create some hours types before proceeding")
                        : _("cannot be empty");

                showInvalidMessage(autoTypeOfHours, message);
            }

            return false;
        }

        if ((!getWorkReport().isCodeAutogenerated())
                && (workReportLine.getCode() == null || workReportLine.getCode().isEmpty())) {
            // Locate TextboxCode
            Textbox txtCode = getCode(row);
            if (txtCode != null) {
                String message = _("cannot be empty.");
                showInvalidMessage(txtCode, message);
            }

            return false;
        }

        if (!workReportLine.isOrderElementFinishedInAnotherWorkReportConstraint()) {
            Checkbox checkboxFinished = getFinished(row);
            if (checkboxFinished != null) {
                String message = _("task is already marked as finished in another timesheet");
                showInvalidMessage(checkboxFinished, message);
            }

            return false;
        }

        return true;
    }

    private void showInvalidMessage(Component comp, String message) {
        throw new WrongValueException(comp, message);
    }

    /**
     * Locates {@link Timebox} time finish in {@link Row}.
     *
     * @param row
     * @return
     */
    private Timebox getTimeboxFinish(Row row) {
        try {
            int position = row.getChildren().size() - 6;

            return (Timebox) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Timebox} time start in {@link Row}.
     *
     * @param row
     * @return
     */
    private Timebox getTimeboxStart(Row row) {
        try {
            int position = row.getChildren().size() - 7;

            return (Timebox) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Autocomplete} type of work hours in {@link Row}.
     *
     * @param row
     * @return
     */
    private Listbox getTypeOfHours(Row row) {
        try {
            int position = row.getChildren().size() - 4;

            return (Listbox) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Checkbox} finished in {@link Row}.
     *
     * @param row
     * @return
     */
    private Checkbox getFinished(Row row) {
        try {
            int position = row.getChildren().size() - 3;

            return (Checkbox) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Texbox} code in {@link Row}.
     *
     * @param row
     * @return
     */
    private Textbox getCode(Row row) {
        try {
            int position = row.getChildren().size() - 2;

            return (Textbox) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Textbox} effort in {@link Row}.
     *
     * @param row
     * @return
     */
    private Textbox getEffort(Row row) {
        try {
            int position = row.getChildren().size() - 5;

            return (Textbox) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Datebox} date in {@link Row}.
     *
     * @param row
     * @return
     */
    private Datebox getDateboxDate(Row row) {
        try {
            return (Datebox) row.getChildren().get(0);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Textbox} Resource in {@link Row}.
     *
     * @param row
     * @return
     */
    private Autocomplete getTextboxResource(Row row) {
        int position = 0;
        if (!getWorkReportType().getDateIsSharedByLines()) {
            position++;
        }
        try {
            return (Autocomplete) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Locates {@link Textbox} Order in {@link Row}.
     *
     * @param row
     * @return
     */
    private BandboxSearch getTextboxOrder(Row row) {
        int position = 0;
        if (!getWorkReportType().getDateIsSharedByLines()) {
            position++;
        }
        if (!getWorkReportType().getResourceIsSharedInLines()) {
            position++;
        }
        try {
            return (BandboxSearch) row.getChildren().get(position);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public void goToList() {
        getVisibility().showOnly(listWindow);
        loadComponentslist(listWindow);
        Util.reloadBindings(listWindow);
    }

    public void cancel() {
        if (cameBackList || workReportModel.isEditing()) {
            goToList();
        } else {
            workReportTypeCRUD.goToList();
        }
    }

    @Override
    public void goToCreateForm(WorkReportType workReportType) {
        if (workReportType.isPersonalTimesheetsType()) {
            personalTimesheetsPopup.open(listTypeToAssign);
        } else {
            cameBackList = false;
            workReportModel.initCreate(workReportType);
            prepareWorkReportList();
            createWindow.setTitle(_("Create Timesheet"));
            getVisibility().showOnly(createWindow);
            loadComponents(createWindow);
            Util.reloadBindings(createWindow);
        }
    }

    public void goToEditForm(WorkReportDTO workReportDTO) {
        goToEditForm(workReportDTO.getWorkReport());
    }

    @Override
    public void goToEditForm(WorkReport workReport) {
        if (workReportModel.isPersonalTimesheet(workReport)) {
            goToEditPersonalTimeSheet(workReport);
        } else {
            workReportModel.initEdit(workReport);
            createWindow.setTitle(_("Edit Timesheet"));
            loadComponents(createWindow);
            prepareWorkReportList();
            getVisibility().showOnly(createWindow);
            Util.reloadBindings(createWindow);
        }
    }

    private void goToEditPersonalTimeSheet(WorkReport workReport) {
        workReportModel.initEdit(workReport);
        Date date = workReportModel.getFirstWorkReportLine().getDate();
        Resource resource = workReport.getResource();
        personalTimesheetController.goToCreateOrEditFormForResource(LocalDate.fromDateFields(date), resource);
    }

    private void loadComponents(Component window) {
        listWorkReportLines = (NewDataSortableGrid) window.getFellow("listWorkReportLines");
        headingFieldsAndLabels = (Grid) window.getFellow("headingFieldsAndLabels");
        autocompleteResource = (Autocomplete) window.getFellow("autocompleteResource");
        bandboxSelectOrderElementInHead = (BandboxSearch) window.getFellow("bandboxSelectOrderElementInHead");
        bandboxSelectOrderElementInHead.setListboxWidth("750px");

        bandboxSelectOrderElementInHead.setListboxEventListener(Events.ON_SELECT, event -> {
            Listitem selectedItem = (Listitem) ((SelectEvent) event).getSelectedItems().iterator().next();
            OrderElement orderElement = selectedItem.getValue();
            getWorkReport().setOrderElement(orderElement);
        });

        bandboxSelectOrderElementInHead.setListboxEventListener(Events.ON_OK, event -> {
            Listitem selectedItem = bandboxSelectOrderElementInHead.getSelectedItem();
            if ((selectedItem != null) && (getWorkReport() != null)) {
                getWorkReport().setOrderElement(selectedItem.getValue());
            }
            bandboxSelectOrderElementInHead.close();
        });

    }

    private void loadComponentslist(Component window) {
        // Components work report list
        listing = (Grid) window.getFellow("listing");
        listType = (Listbox) window.getFellow("listType");
        listTypeToAssign = (Listbox) window.getFellow("listTypeToAssign");
        filterStartDate = (Datebox) window.getFellow("filterStartDate");
        filterFinishDate = (Datebox) window.getFellow("filterFinishDate");
        personalTimesheetsPopup = (Popup) window.getFellow("personalTimesheetsPopup");
        personalTimesheetsDatebox = (Datebox) window.getFellow("personalTimesheetsDatebox");
        personalTimesheetsBandboxSearch = (BandboxSearch) window.getFellow("personalTimesheetsBandboxSearch");
        clearFilterDates();
    }

    /**
     * {@link WorkReportLine} list is finally constructed dynamically.
     *
     * It seems there are some problems when a list of data is rendered,
     * modified (the data model changes), and it's rendered again.
     * Deleting previous settings and re-establishing the settings again each time the
     * list is rendered, solve those problems.
     */
    private void prepareWorkReportList() {
        /*
         * The only way to clean the listhead, is to clean all its attributes
         * and children The paging component cannot be removed manually.
         * It is removed automatically when changing the mold.
         */
        listWorkReportLines.setMold(null);
        listWorkReportLines.getChildren().clear();

        // Set mold and pagesize
        listWorkReportLines.setMold(MOLD);
        listWorkReportLines.setPageSize(PAGING);

        appendColumns(listWorkReportLines);
        listWorkReportLines
                .setSortedColumn((NewDataSortableColumn) listWorkReportLines.getColumns().getFirstChild());

        listWorkReportLines.setModel(new SimpleListModel<>(getWorkReportLines().toArray()));
    }

    /**
     * Appends list headers to {@link WorkReportLine} list.
     *
     * @param grid
     */
    private void appendColumns(Grid grid) {

        Columns columns = grid.getColumns();

        // Create listhead first time is rendered
        if (columns == null) {
            columns = new Columns();
        }

        // Delete all headers
        columns.getChildren().clear();
        columns.setSizable(true);

        // Add static headers
        if (getWorkReport() != null) {
            if (!getWorkReport().getWorkReportType().getDateIsSharedByLines()) {
                NewDataSortableColumn columnDate = new NewDataSortableColumn();
                columnDate.setLabel(_("Date"));
                columnDate.setSclass("date-column");
                columnDate.setHflex("1");
                Util.setSort(columnDate, "auto=(date)");
                columnDate.setSortDirection(ASCENDING);

                columnDate.addEventListener("onSort", event -> sortWorkReportLines());
                columns.appendChild(columnDate);
            }

            if (!getWorkReport().getWorkReportType().getResourceIsSharedInLines()) {
                NewDataSortableColumn columnResource = new NewDataSortableColumn();
                columnResource.setLabel(_("Resource"));
                columnResource.setHflex("1");
                columnResource.setSclass("resource-column");
                columns.appendChild(columnResource);
            }

            if (!getWorkReport().getWorkReportType().getOrderElementIsSharedInLines()) {
                NewDataSortableColumn columnCode = new NewDataSortableColumn();
                columnCode.setLabel(_("Task"));
                columnCode.setSclass("order-code-column");
                columnCode.setHflex("1");
                columns.appendChild(columnCode);
            }

            for (Object fieldOrLabel : workReportModel.getFieldsAndLabelsLineByDefault()) {
                String columnName;
                int width = EXTRA_FIELD_MIN_WIDTH;

                if (fieldOrLabel instanceof DescriptionField) {
                    columnName = ((DescriptionField) fieldOrLabel).getFieldName();

                    width = Math.max(((DescriptionField) fieldOrLabel).getLength() * EXTRA_FIELD_PX_PER_CHAR,
                            EXTRA_FIELD_MIN_WIDTH);

                    width = Math.min(width, EXTRA_FIELD_MAX_WIDTH);

                } else {
                    columnName = ((WorkReportLabelTypeAssignment) fieldOrLabel).getLabelType().getName();
                }
                NewDataSortableColumn columnFieldOrLabel = new NewDataSortableColumn();
                columnFieldOrLabel.setLabel(_(columnName));
                columnFieldOrLabel.setSclass("columnFieldOrLabel");
                columnFieldOrLabel.setWidth(width + "px");
                columns.appendChild(columnFieldOrLabel);
            }

            if (!getWorkReport().getWorkReportType().getHoursManagement()
                    .equals(HoursManagementEnum.NUMBER_OF_HOURS)) {

                NewDataSortableColumn columnHourStart = new NewDataSortableColumn();
                columnHourStart.setLabel(_("Start hour"));
                columnHourStart.setSclass("column-hour-start");
                columnHourStart.setHflex("min");
                columns.appendChild(columnHourStart);
                NewDataSortableColumn columnHourFinish = new NewDataSortableColumn();
                columnHourFinish.setLabel(_("Finish Hour"));
                columnHourStart.setSclass("column-hour-finish");
                columns.appendChild(columnHourFinish);
            }
        }

        NewDataSortableColumn columnNumHours = new NewDataSortableColumn();
        columnNumHours.setLabel(_("Hours"));
        columnNumHours.setSclass("hours-column");
        columns.appendChild(columnNumHours);
        NewDataSortableColumn columnHoursType = new NewDataSortableColumn();
        columnHoursType.setLabel(_("Hours type"));
        columnHoursType.setSclass("hours-type-column");
        columns.appendChild(columnHoursType);
        NewDataSortableColumn columnFinsihed = new NewDataSortableColumn();
        columnFinsihed.setLabel(_("Done"));
        columnFinsihed.setSclass("finished-column");
        columnFinsihed.setTooltiptext(_("Task finished"));
        NewDataSortableColumn columnCode = new NewDataSortableColumn();
        columns.appendChild(columnFinsihed);
        columnCode.setLabel(_("Code"));
        columnCode.setSclass("code-column");
        columnCode.setHflex("1");
        columns.appendChild(columnCode);
        NewDataSortableColumn columnOperations = new NewDataSortableColumn();
        columnOperations.setLabel(_("Op."));
        columnOperations.setSclass("operations-column");
        columnOperations.setTooltiptext(_("Operations"));
        columnOperations.setHflex("min");
        columns.appendChild(columnOperations);

        columns.setParent(grid);

    }

    private WorkReportType getWorkReportType() {
        return getWorkReport().getWorkReportType();
    }

    public WorkReport getWorkReport() {
        return workReportModel.getWorkReport();
    }

    /**
     * Adds a new {@link WorkReportLine} to the list of rows.
     */
    public void addWorkReportLine() {
        workReportModel.addWorkReportLine();
        reloadWorkReportLines();
    }

    private void removeWorkReportLine(WorkReportLine workReportLine) {
        workReportModel.removeWorkReportLine(workReportLine);
        reloadWorkReportLines();
    }

    public List<WorkReportLine> getWorkReportLines() {
        return workReportModel.getWorkReportLines();
    }

    protected void setClock(WorkReportLine line, Timebox timeStart, Timebox timeFinish) {
        line.setClockStart(timeStart.getValue());
        line.setClockFinish(timeFinish.getValue());
    }

    public void checkCannotBeHigher(Timebox starting, Timebox ending) {
        starting.clearErrorMessage(true);
        ending.clearErrorMessage(true);

        final Date startingDate = starting.getValue();
        final Date endingDate = ending.getValue();

        if (endingDate == null || startingDate == null || startingDate.compareTo(endingDate) > 0) {
            throw new WrongValueException(starting, _("Cannot be higher than finish hour"));
        }
    }

    public void confirmRemove(WorkReportLine workReportLine) {
        int status = Messagebox.show(
                _("Confirm deleting {0}. Are you sure?", getWorkReportLineName(workReportLine)), _("Delete"),
                Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION);

        if (Messagebox.OK == status) {
            removeWorkReportLine(workReportLine);
        }
    }

    private String getWorkReportLineName(WorkReportLine workReportLine) {
        final Resource resource = workReportLine.getResource();
        final OrderElement orderElement = workReportLine.getOrderElement();

        if (resource == null || orderElement == null) {
            return ITEM;
        }

        return resource.getShortDescription() + " - " + orderElement.getCode();
    }

    public WorkReportListRenderer getRenderer() {
        return workReportListRenderer;
    }

    public class WorkReportListRenderer implements RowRenderer {

        /**
         * RowRenderer for a @{WorkReportLine} element.
         *
         * @author Diego Pino Garca <dpino@igalia.com>
         * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
         */
        @Override
        public void render(Row row, Object o, int i) throws Exception {
            WorkReportLine workReportLine = (WorkReportLine) o;

            row.setValue(workReportLine);

            // Create TextBoxes
            if (!getWorkReport().getWorkReportType().getDateIsSharedByLines()) {
                appendDateInLines(row);
            }

            if (!getWorkReport().getWorkReportType().getResourceIsSharedInLines()) {
                appendResourceInLines(row);
            }

            if (!getWorkReport().getWorkReportType().getOrderElementIsSharedInLines()) {
                appendOrderElementInLines(row);
            }

            // Create the fields and labels
            appendFieldsAndLabelsInLines(row);

            NewDataSortableGrid grid = (NewDataSortableGrid) row.getParent().getParent();
            NewDataSortableColumn priorityColumn = (NewDataSortableColumn) grid.getChildren().get(1).getChildren()
                    .get(2);
            priorityColumn.setWidth("110px");

            if (!getWorkReport().getWorkReportType().getHoursManagement()
                    .equals(HoursManagementEnum.NUMBER_OF_HOURS)) {
                appendHoursStartAndFinish(row);
            }

            appendEffortDuration(row);
            appendHoursType(row);
            appendFinished(row);
            appendCode(row);
            appendDeleteButton(row);
        }

        private void setOrderElementInWRL(Listitem selectedItem, WorkReportLine line) {
            OrderElement orderElement = selectedItem.getValue();
            line.setOrderElement(orderElement);
        }

        private void appendFinished(final Row row) {
            final WorkReportLine line = row.getValue();

            Checkbox finished = Util.bind(new Checkbox(), () -> line.isFinished(),
                    value -> line.setFinished(BooleanUtils.isTrue(value)));

            if (!line.isFinished() && workReportModel.isFinished(line.getOrderElement())) {
                finished.setDisabled(true);
            }

            row.appendChild(finished);
        }

        private void appendAutocompleteLabelsByTypeInLine(Row row, final Label currentLabel) {
            final LabelType labelType = currentLabel.getType();
            final WorkReportLine line = row.getValue();
            final Autocomplete comboLabels = createAutocompleteLabels(labelType, currentLabel);
            comboLabels.setParent(row);

            comboLabels.addEventListener(Events.ON_CHANGE, event -> {
                if (comboLabels.getSelectedItem() != null) {
                    Label newLabel = comboLabels.getSelectedItem().getValue();
                    workReportModel.changeLabelInWorkReportLine(currentLabel, newLabel, line);
                }

                reloadWorkReportLines();
            });
        }

        private void appendDateInLines(final Row row) {
            final Datebox date = new Datebox();
            final WorkReportLine line = row.getValue();

            Util.bind(date, () -> {
                if (line != null) {
                    return line.getDate();
                }
                return null;
            }, value -> {
                if (line != null) {
                    line.setDate(value);
                }
            });

            row.appendChild(date);
        }

        /**
         * Append a Autocomplete @{link Resource} to row.
         *
         * @param row
         */
        private void appendResourceInLines(final Row row) {
            final Autocomplete autocomplete = new Autocomplete();
            autocomplete.setWidth("200px");
            autocomplete.setAutodrop(true);
            autocomplete.applyProperties();
            autocomplete.setFinder("ResourceFinder");

            // Getter, show worker selected
            if (getResource(row) != null) {
                autocomplete.setSelectedItem(getResource(row));
            }

            autocomplete.addEventListener("onChange", event -> changeResourceInLines(autocomplete, row));

            row.appendChild(autocomplete);
        }

        private void setHoursType(WorkReportLine workReportLine, Listitem item) {
            TypeOfWorkHours value = item != null ? (TypeOfWorkHours) item.getValue() : null;
            workReportLine.setTypeOfWorkHours(value);
            if (value == null && item != null) {
                throw new WrongValueException(item.getParent(), _("Please, select an item"));
            }
        }

        /**
         * Append a {@link Textbox} effort to {@link Row}.
         *
         * @param row
         */
        private void appendEffortDuration(Row row) {
            WorkReportLine workReportLine = row.getValue();
            Textbox effort = new Textbox();

            effort.setConstraint((comp, value) -> {
                if (!Pattern.matches("(\\d+)(\\s*:\\s*\\d+\\s*)*", (String) value))
                    throw new WrongValueException(comp, _("Please, enter a valid effort"));
            });

            bindEffort(effort, workReportLine);

            if (getWorkReportType().getHoursManagement().equals(HoursManagementEnum.HOURS_CALCULATED_BY_CLOCK)) {
                effort.setDisabled(true);
            }
            row.appendChild(effort);
        }

        private void appendFieldsAndLabelsInLines(final Row row) {
            final WorkReportLine line = row.getValue();
            for (Object fieldOrLabel : getFieldsAndLabelsLine(line)) {
                if (fieldOrLabel instanceof DescriptionValue) {
                    appendNewTextbox(row, (DescriptionValue) fieldOrLabel);
                } else if (fieldOrLabel instanceof Label) {
                    appendAutocompleteLabelsByTypeInLine(row, (Label) fieldOrLabel);
                }
            }
        }

        /**
         * Append Selectbox of @{link TypeOfWorkHours} to row.
         *
         * @param row
         */
        private void appendHoursType(final Row row) {
            final WorkReportLine workReportLine = row.getValue();
            final Listbox lbHoursType = new Listbox();
            lbHoursType.setMold("select");
            lbHoursType.setModel(allHoursType);
            lbHoursType.renderAll();
            lbHoursType.applyProperties();

            if (lbHoursType.getItems().isEmpty()) {
                row.appendChild(lbHoursType);

                return;
            }

            // First time is rendered, select first item
            TypeOfWorkHours type = workReportLine.getTypeOfWorkHours();
            if (workReportLine.isNewObject() && type == null) {
                Listitem item = lbHoursType.getItemAtIndex(0);
                item.setSelected(true);
                setHoursType(workReportLine, item);
            } else {
                // If workReportLine has a type, select item with that type
                Listitem item = ComponentsFinder.findItemByValue(lbHoursType, type);
                if (item != null) {
                    lbHoursType.selectItem(item);
                }
            }

            lbHoursType.addEventListener(Events.ON_SELECT, event -> {
                Listitem item = lbHoursType.getSelectedItem();
                if (item != null) {
                    setHoursType(row.getValue(), item);
                }
            });

            row.appendChild(lbHoursType);
        }

        /**
         * Append a delete {@link Button} to {@link Row}.
         *
         * @param row
         */
        private void appendDeleteButton(final Row row) {
            Button delete = new Button("", "/common/img/ico_borrar1.png");
            delete.setHoverImage("/common/img/ico_borrar.png");
            delete.setSclass("icono");
            delete.setTooltiptext(_("Delete"));
            delete.addEventListener(Events.ON_CLICK, event -> confirmRemove(row.getValue()));

            row.appendChild(delete);
        }

        /**
         * Binds Textbox effort to a {@link WorkReportLine} numHours.
         *
         * @param box
         * @param workReportLine
         */
        private void bindEffort(final Textbox box, final WorkReportLine workReportLine) {
            Util.bind(box,
                    () -> workReportLine.getEffort() != null ? workReportLine.getEffort().toFormattedString()
                            : EffortDuration.zero().toFormattedString(),
                    value -> workReportLine.setEffort(EffortDuration.parseFromFormattedString(value)));
        }

        private void appendCode(final Row row) {
            final WorkReportLine line = row.getValue();
            final Textbox code = new Textbox();
            code.setDisabled(getWorkReport().isCodeAutogenerated());
            code.applyProperties();

            if (line.getCode() != null) {
                code.setValue(line.getCode());
            }

            code.addEventListener("onChange", event -> {
                final WorkReportLine line1 = row.getValue();
                line1.setCode(code.getValue());
            });
            row.appendChild(code);
        }

        private Timebox getNewTimebox() {
            final Timebox timeStart = new Timebox();
            timeStart.setWidth("60px");
            timeStart.setFormat("short");
            timeStart.setButtonVisible(true);

            return timeStart;
        }

        private void updateEffort(final Row row) {
            WorkReportLine line = row.getValue();
            Textbox effort = getEffort(row);
            if (effort != null && line.getEffort() != null) {
                effort.setValue(line.getEffort().toFormattedString());
                effort.invalidate();
            }
        }

        private void appendHoursStartAndFinish(final Row row) {
            final WorkReportLine line = row.getValue();

            final Timebox timeStart = getNewTimebox();
            final Timebox timeFinish = getNewTimebox();

            row.appendChild(timeStart);
            row.appendChild(timeFinish);

            Util.bind(timeStart, () -> {
                if ((line != null) && (line.getClockStart() != null)) {
                    return line.getClockStart().toDateTimeToday().toDate();
                }

                return null;
            }, value -> {
                if (line != null) {
                    checkCannotBeHigher(timeStart, timeFinish);
                    setClock(line, timeStart, timeFinish);
                    updateEffort(row);
                }
            });

            Util.bind(timeFinish, () -> {
                if ((line != null) && (line.getClockStart() != null)) {
                    return line.getClockFinish().toDateTimeToday().toDate();
                }

                return null;
            }, value -> {
                if (line != null) {
                    checkCannotBeHigher(timeStart, timeFinish);
                    setClock(line, timeStart, timeFinish);
                    updateEffort(row);
                }
            });
        }

        /**
         * Append a Textbox @{link Order} to row.
         *
         * @param row
         */
        private void appendOrderElementInLines(Row row) {
            final WorkReportLine workReportLine = row.getValue();

            final BandboxSearch bandboxSearch = BandboxSearch.create("OrderElementBandboxFinder",
                    getOrderElements());

            bandboxSearch.setSelectedElement(workReportLine.getOrderElement());
            bandboxSearch.setSclass("bandbox-workreport-task");
            bandboxSearch.setListboxWidth("750px");

            bandboxSearch.setListboxEventListener(Events.ON_SELECT, event -> {
                Listitem selectedItem = bandboxSearch.getSelectedItem();
                setOrderElementInWRL(selectedItem, workReportLine);
            });

            bandboxSearch.setListboxEventListener(Events.ON_OK, event -> {
                Listitem selectedItem = bandboxSearch.getSelectedItem();
                setOrderElementInWRL(selectedItem, workReportLine);
                bandboxSearch.close();
            });

            row.appendChild(bandboxSearch);
        }

        private Resource getResource(Row listitem) {
            WorkReportLine workReportLine = listitem.getValue();

            return workReportLine.getResource();
        }

        private void changeResourceInLines(final Autocomplete autocomplete, Row row) {
            final WorkReportLine workReportLine = row.getValue();
            final Comboitem comboitem = autocomplete.getSelectedItem();
            if ((comboitem == null) || (comboitem.getValue() == null)) {
                workReportLine.setResource(null);
                throw new WrongValueException(autocomplete, _("Please, select an item"));
            } else {
                workReportLine.setResource(comboitem.getValue());
            }
        }

    }

    public OrderedFieldsAndLabelsRowRenderer getOrderedFieldsAndLabelsRowRenderer() {
        return orderedFieldsAndLabelsRowRenderer;
    }

    public class OrderedFieldsAndLabelsRowRenderer implements RowRenderer {

        @Override
        public void render(Row row, Object o, int i) throws Exception {
            row.setValue(o);

            if (o instanceof DescriptionValue) {
                appendNewLabel(row, ((DescriptionValue) o).getFieldName());
                appendNewTextbox(row, (DescriptionValue) o);
            } else {
                appendNewLabel(row, ((Label) o).getType().getName());
                appendAutocompleteLabelsByType(row, (Label) o);
            }
        }

        private void appendNewLabel(Row row, String label) {
            org.zkoss.zul.Label labelName = new org.zkoss.zul.Label();
            labelName.setParent(row);
            labelName.setValue(label);
        }

        private void appendAutocompleteLabelsByType(Row row, final Label currentLabel) {
            final LabelType labelType = currentLabel.getType();
            final Autocomplete comboLabels = createAutocompleteLabels(labelType, currentLabel);
            comboLabels.setParent(row);

            comboLabels.addEventListener(Events.ON_CHANGE, event -> {
                if (comboLabels.getSelectedItem() != null) {
                    Label newLabel = comboLabels.getSelectedItem().getValue();
                    workReportModel.changeLabelInWorkReport(currentLabel, newLabel);
                }
                Util.reloadBindings(headingFieldsAndLabels);
            });
        }
    }

    private void appendNewTextbox(Row row, final DescriptionValue descriptionValue) {
        Textbox textbox = new Textbox();
        Integer length = workReportModel.getLength(descriptionValue);
        textbox.setCols(length);
        textbox.setParent(row);
        textbox.setTooltiptext(descriptionValue.getValue());

        Util.bind(textbox, descriptionValue::getValue, descriptionValue::setValue);
    }

    private Autocomplete createAutocompleteLabels(LabelType labelType, Label selectedLabel) {
        Autocomplete comboLabels = new Autocomplete();
        comboLabels.setButtonVisible(true);
        comboLabels.setWidth("100px");

        if (labelType != null) {
            final List<Label> listLabel = getMapLabelTypes().get(labelType);

            for (Label label : listLabel) {
                Comboitem comboItem = new Comboitem();
                comboItem.setValue(label);
                comboItem.setLabel(label.getName());
                comboItem.setParent(comboLabels);

                if ((selectedLabel != null) && (selectedLabel.equals(label))) {
                    comboLabels.setSelectedItem(comboItem);
                }
            }
        }

        return comboLabels;
    }

    public List<Object> getFieldsAndLabelsHeading() {
        return workReportModel.getFieldsAndLabelsHeading();
    }

    public List<Object> getFieldsAndLabelsLine(WorkReportLine workReportLine) {
        return workReportModel.getFieldsAndLabelsLine(workReportLine);
    }

    private Map<LabelType, List<Label>> getMapLabelTypes() {
        return workReportModel.getMapAssignedLabelTypes();
    }

    public void changeResource(Comboitem selectedItem) {
        if (selectedItem != null) {
            getWorkReport().setResource(selectedItem.getValue());
        } else {
            getWorkReport().setResource(null);
        }
    }

    private void reloadWorkReportLines() {
        this.prepareWorkReportList();
        Util.reloadBindings(listWorkReportLines);
    }

    private void sortWorkReportLines() {
        listWorkReportLines.setModel(new SimpleListModel<>(getWorkReportLines().toArray()));
    }

    /* It should be public! */
    public void sortWorkReports() {
        Column columnDateStart = (Column) listWindow.getFellow("columnDateStart");
        if (columnDateStart != null) {
            if (columnDateStart.getSortDirection().equals(ASCENDING)) {
                columnDateStart.sort(false, false);
                columnDateStart.setSortDirection(ASCENDING);
            } else if (columnDateStart.getSortDirection().equals("descending")) {
                columnDateStart.sort(true, false);
                columnDateStart.setSortDirection("descending");
            }
        }
    }

    /**
     * It should be public!
     */
    public List<WorkReportType> getFilterWorkReportTypes() {
        List<WorkReportType> result = workReportModel.getWorkReportTypes();

        if (result.isEmpty()) {
            result.add(getDefaultWorkReportType());
        } else {
            result.add(0, getDefaultWorkReportType());
        }

        return result;
    }

    public List<WorkReportType> getWorkReportTypes() {
        List<WorkReportType> result = workReportModel.getWorkReportTypes();

        if (!result.isEmpty()) {
            this.firstType = result.get(2);
        }

        return result;
    }

    public WorkReportType getDefaultWorkReportType() {
        return workReportModel.getDefaultType();
    }

    /**
     * Apply filter to work reports.
     */
    public void onApplyFilter() {
        createPredicate();
        filterByPredicate();
    }

    public Constraint checkConstraintFinishDate() {
        return (comp, value) -> {
            Date finishDate = (Date) value;

            if ((finishDate != null) && (filterStartDate.getValue() != null)
                    && (finishDate.compareTo(filterStartDate.getValue()) < 0)) {

                filterFinishDate.setValue(null);
                throw new WrongValueException(comp, _("must be later than start date"));
            }
        };
    }

    public Constraint checkConstraintStartDate() {
        return (comp, value) -> {
            Date startDate = (Date) value;

            if ((startDate != null) && (filterFinishDate.getValue() != null)
                    && (startDate.compareTo(filterFinishDate.getValue()) > 0)) {

                filterStartDate.setValue(null);
                throw new WrongValueException(comp, _("must be before end date"));
            }
        };
    }

    private void createPredicate() {
        WorkReportType type = getSelectedType();
        Date startDate = filterStartDate.getValue();
        Date finishDate = filterFinishDate.getValue();
        predicate = new WorkReportPredicate(type, startDate, finishDate);
    }

    private WorkReportType getSelectedType() {
        Listitem itemSelected = listType.getSelectedItem();

        if ((itemSelected != null)
                && (!java.util.Objects.equals(itemSelected.getValue(), getDefaultWorkReportType()))) {
            return (WorkReportType) itemSelected.getValue();
        }

        return null;
    }

    private void filterByPredicate() {
        List<WorkReportDTO> filterWorkReports = workReportModel.getFilterWorkReportDTOs(predicate);
        listing.setModel(new SimpleListModel<>(filterWorkReports.toArray()));
        listing.invalidate();
    }

    private void clearFilterDates() {
        filterStartDate.setValue(null);
        filterFinishDate.setValue(null);
    }

    public List<OrderElement> getOrderElements() {
        return workReportModel.getOrderElements();
    }

    /**
     * Methods improved the work report edition and creation.
     * Executed on pressing New work report button Creates a new work report for a type,
     * and added it to the work report list.
     */

    /**
     * It should be public!
     */
    public void onCreateNewWorkReport() {
        Listitem selectedItem = listTypeToAssign.getSelectedItem();
        if (selectedItem == null) {
            throw new WrongValueException(listTypeToAssign, _("please, select a timesheet template type"));
        }

        WorkReportType type = selectedItem.getValue();
        if (type == null) {
            throw new WrongValueException(listTypeToAssign, _("please, select a timesheet template type"));
        }

        goToCreateForm(type);
        listTypeToAssign.clearSelection();
        cameBackList = true;
    }

    /**
     * It should be public!
     */
    public WorkReportType getFirstType() {
        return firstType;
    }

    public void setFirstType(WorkReportType firstType) {
        this.firstType = firstType;
    }

    /**
     * It should be public!
     */
    public void newWorkReportWithSameType() {
        if (save()) {
            goToCreateForm(workReportModel.getWorkReportType());
            cameBackList = true;
        }
    }

    public void onCheckGenerateCode(Event e) {
        CheckEvent ce = (CheckEvent) e;
        if (ce.isChecked()) {
            // We have to auto-generate the code for new objects
            try {
                workReportModel.setCodeAutogenerated(ce.isChecked());
            } catch (ConcurrentModificationException err) {
                messagesForUser.showMessage(Level.ERROR, err.getMessage());
            }
        }
        Util.reloadBindings(createWindow);
        reloadWorkReportLines();
    }

    /**
     * It should be public!
     */
    public List<Worker> getBoundWorkers() {
        return workReportModel.getBoundWorkers();
    }

    /**
     * It should be public!
     */
    public void createOrEditPersonalTimesheet() {
        Date date = personalTimesheetsDatebox.getValue();
        if (date == null) {
            throw new WrongValueException(personalTimesheetsDatebox, _("Please set a date"));
        }
        Resource resource = (Resource) personalTimesheetsBandboxSearch.getSelectedElement();
        if (resource == null) {
            throw new WrongValueException(personalTimesheetsBandboxSearch, _("Please select a worker"));
        }

        personalTimesheetController.goToCreateOrEditFormForResource(LocalDate.fromDateFields(date), resource);
    }

}