Java tutorial
/* * 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); } }