/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2012 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.calendars; import static org.libreplan.web.I18nHelper._; import static org.libreplan.web.common.Util.findOrCreate; import java.text.DateFormatSymbols; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.joda.time.LocalDate; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.calendars.entities.CalendarAvailability; import org.libreplan.business.calendars.entities.CalendarData; import org.libreplan.business.calendars.entities.CalendarData.Days; import org.libreplan.business.calendars.entities.CalendarException; import org.libreplan.business.calendars.entities.CalendarExceptionType; import org.libreplan.business.calendars.entities.Capacity; import org.libreplan.business.calendars.entities.ResourceCalendar; import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.workingday.EffortDuration; import org.libreplan.business.workingday.EffortDuration.Granularity; import org.libreplan.business.workingday.IntraDayDate.PartialDay; import org.libreplan.web.common.IMessagesForUser; import org.libreplan.web.common.Level; import org.libreplan.web.common.Util; import org.libreplan.web.common.components.CapacityPicker; import org.libreplan.web.common.components.EffortDurationPicker; import org.zkoss.util.Locales; import org.zkoss.zk.au.out.AuInvoke; import org.zkoss.zk.ui.Component; 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.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.SelectEvent; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.Button; import org.zkoss.zul.Checkbox; import org.zkoss.zul.Combobox; import org.zkoss.zul.Comboitem; import org.zkoss.zul.ComboitemRenderer; import org.zkoss.zul.Datebox; import org.zkoss.zul.Hbox; import org.zkoss.zul.Label; import org.zkoss.zul.Listbox; import org.zkoss.zul.Listcell; import org.zkoss.zul.Listitem; import org.zkoss.zul.ListitemRenderer; import org.zkoss.zul.Textbox; import org.zkoss.zul.Calendar; import org.zkoss.zul.Window; /** * Controller for edit and create one {@link BaseCalendar}. * It's separated of {@link BaseCalendarCRUDController} to be used from other parts of the application. * * @author Manuel Rego Casasnovas <mrego@igalia.com> * @author Susana Montes Pedreira <smontes@wirelessgalicia.com> */ public abstract class BaseCalendarEditionController extends GenericForwardComposer { private static final String TEXT_COLOR_HIGHLIGHTED_DAY = "white"; private static final String BACKGROUND_COLOR_ZERO_HOURS_DAY = "lightgrey"; private IBaseCalendarModel baseCalendarModel; private Window window; private Window createNewVersionWindow; private HoursPerDayRenderer hoursPerDayRenderer = new HoursPerDayRenderer(); private HistoryVersionsRenderer historyVersionsRenderer = new HistoryVersionsRenderer(); private ParentCalendarsComboitemRenderer parentCalendarsComboitemRenderer = new ParentCalendarsComboitemRenderer(); private CalendarExceptionRenderer calendarExceptionRenderer = new CalendarExceptionRenderer(); private CalendarAvailabilityRenderer calendarAvailabilityRenderer = new CalendarAvailabilityRenderer(); private boolean creatingNewVersion = false; private Date newWorkWeekStartDate; private Date newWorkWeekExpiringDate; private CapacityPicker capacityPicker; private IMessagesForUser messagesForUser; private Combobox exceptionTypes; public BaseCalendarEditionController(IBaseCalendarModel baseCalendarModel, Window window, Window createNewVersionWindow, IMessagesForUser messagesForUser) { this.baseCalendarModel = baseCalendarModel; this.window = window; this.createNewVersionWindow = createNewVersionWindow; this.messagesForUser = messagesForUser; } private static String asString(Capacity capacity) { String extraEffortString = capacity.isOverAssignableWithoutLimit() ? _("unl") : asString(capacity.getAllowedExtraEffort()); return asString(capacity.getStandardEffort()) + " [" + extraEffortString + "]"; } private static String asString(EffortDuration duration) { if ( duration == null ) { return ""; } EnumMap<Granularity, Integer> decomposed = duration.decompose(); String result = decomposed.get(Granularity.HOURS) + ":"; result += decomposed.get(Granularity.MINUTES); if ( decomposed.get(Granularity.SECONDS) > 0 ) { result += " " + decomposed.get(Granularity.SECONDS) + "s"; } return result; } public abstract void goToList(); public abstract void save(); public abstract void saveAndContinue(); public abstract void cancel(); @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); prepareExceptionTypeCombo(); capacityPicker = addEffortDurationPickerAtWorkableTimeRow(comp); updateWithCapacityFrom(getSelectedExceptionType()); } private CapacityPicker addEffortDurationPickerAtWorkableTimeRow(Component comp) { Component normalEffortRow = comp.getFellow("exceptionDayNormalEffortRow"); Component extraEffortRow = comp.getFellow("exceptionDayExtraEffortBox"); EffortDurationPicker normalDuration = findOrCreateDurationPicker(normalEffortRow); normalDuration.initializeFor24HoursAnd0Minutes(); EffortDurationPicker extraDuration = findOrCreateDurationPicker(extraEffortRow); Checkbox checkbox = findOrCreateUnlimitedCheckbox(extraEffortRow); return CapacityPicker.workWith(checkbox, normalDuration, extraDuration, Capacity.create(EffortDuration.zero())); } private EffortDurationPicker findOrCreateDurationPicker(Component parent) { return findOrCreate(parent, EffortDurationPicker.class, parent1 -> { EffortDurationPicker normalDuration = new EffortDurationPicker(); parent1.appendChild(normalDuration); return normalDuration; }); } private Checkbox findOrCreateUnlimitedCheckbox(Component parent) { return findOrCreate(parent, Checkbox.class, parent1 -> { Checkbox result = createUnlimitedCheckbox(); parent1.appendChild(result); return result; }); } private Checkbox createUnlimitedCheckbox() { Checkbox unlimited = new Checkbox(); unlimited.setLabel(_("Unlimited")); unlimited.setTooltiptext(_("Infinitely Over Assignable")); return unlimited; } private void updateWithCapacityFrom(CalendarExceptionType exceptionType) { capacityPicker.setValue(exceptionType != null ? exceptionType.getCapacity() : Capacity.create(EffortDuration.zero())); } private CalendarExceptionType getSelectedExceptionType() { Comboitem selectedItem = exceptionTypes.getSelectedItem(); return selectedItem != null ? selectedItem.getValue() : null; } private void prepareExceptionTypeCombo() { exceptionTypes = (Combobox) window.getFellow("exceptionTypes"); fillExceptionTypesComboAndMarkSelectedItem(exceptionTypes); addSelectListener(exceptionTypes); } private void addSelectListener(final Combobox exceptionTypes) { exceptionTypes.addEventListener(Events.ON_SELECT, new EventListener() { @Override public void onEvent(Event event) { Comboitem selectedItem = getSelectedItem((SelectEvent) event); if ( selectedItem != null ) { updateWithCapacityFrom(getValue(selectedItem)); } } private Comboitem getSelectedItem(SelectEvent event) { return (Comboitem) event.getSelectedItems().iterator().next(); } private CalendarExceptionType getValue(Comboitem item) { return (CalendarExceptionType) item.getValue(); } }); } private void fillExceptionTypesComboAndMarkSelectedItem(Combobox exceptionTypes) { exceptionTypes.getChildren().clear(); CalendarExceptionType type = baseCalendarModel.getCalendarExceptionType(); Comboitem defaultItem = new Comboitem("NO_EXCEPTION"); exceptionTypes.appendChild(defaultItem); if ( type == null ) { exceptionTypes.setSelectedItem(defaultItem); } for (CalendarExceptionType calendarExceptionType : baseCalendarModel.getCalendarExceptionTypes()) { Comboitem item = new Comboitem(calendarExceptionType.getName()); item.setValue(calendarExceptionType); exceptionTypes.appendChild(item); if ( (type != null) && (type.getName().equals(calendarExceptionType.getName())) ) { exceptionTypes.setSelectedItem(item); } } } public boolean isEditing() { return baseCalendarModel.isEditing(); } public BaseCalendar getBaseCalendar() { return baseCalendarModel.getBaseCalendar(); } /** Should be public! */ public String getCalendarType() { if ( baseCalendarModel.isDerived() ) { String currentStartDate = this.getCurrentStartDateLabel(); String currentExpiringDate = this.getCurrentExpiringDateLabel(); return _("Derived of calendar {0}", getNameParentCalendar()) + currentStartDate + currentExpiringDate; } return _("Root calendar"); } private String getCurrentExpiringDateLabel() { Date date = baseCalendarModel.getCurrentExpiringDate(); String label = ""; if ( date != null ) { label = " " + _("to {0}", Util.formatDate(date)); } return label; } private String getCurrentStartDateLabel() { Date date = baseCalendarModel.getCurrentStartDate(); String label = ""; if ( date != null ) { label = " " + _("from {0}", Util.formatDate(date)); } return label; } public boolean isDerived() { return baseCalendarModel.isDerived(); } public boolean isNotDerived() { return (!isDerived()); } public List<Days> getHoursPerDay() { return Arrays.asList(Days.values()); } public HoursPerDayRenderer getHoursPerDayRenderer() { return hoursPerDayRenderer; } public class HoursPerDayRenderer implements ListitemRenderer { @Override public void render(Listitem item, Object data, int i) { final Days day = (Days) data; addLabelCell(item, day); EffortDurationPicker normalDurationPicker = new EffortDurationPicker(); normalDurationPicker.initializeFor24HoursAnd0Minutes(); EffortDurationPicker extraDurationPicker = new EffortDurationPicker(); Checkbox unlimitedCheckbox = createUnlimitedCheckbox(); addNormalDurationCell(item, normalDurationPicker); addExtraEffortCell(item, extraDurationPicker, unlimitedCheckbox); CapacityPicker capacityPicker = CapacityPicker.workWith( unlimitedCheckbox, normalDurationPicker, extraDurationPicker, () -> baseCalendarModel.getCapacityAt(day), value -> { baseCalendarModel.setCapacityAt(day, value); reloadDayInformation(); }); capacityPicker.setDisabled(baseCalendarModel.isDerived() && baseCalendarModel.isDefault(day)); Listcell inheritedListcell = new Listcell(); if ( baseCalendarModel.isDerived() ) { Checkbox inheritedCheckbox = Util.bind( new Checkbox(), () -> baseCalendarModel.isDefault(day), value -> { if ( value ) { baseCalendarModel.setDefault(day); } else { baseCalendarModel.unsetDefault(day); } }); inheritedCheckbox.addEventListener(Events.ON_CHECK, (EventListener) event -> reloadCurrentWindow()); inheritedListcell.appendChild(inheritedCheckbox); } item.appendChild(inheritedListcell); } private void addLabelCell(Listitem item, final Days day) { String days[] = DateFormatSymbols.getInstance(Locales.getCurrent()).getWeekdays(); Listcell labelListcell = new Listcell(); labelListcell.appendChild(new Label(days[day.getIndex()])); item.appendChild(labelListcell); } private void addNormalDurationCell(Listitem item, EffortDurationPicker normalDurationPicker) { Listcell normalEffortCell = new Listcell(); normalEffortCell.appendChild(normalDurationPicker); item.appendChild(normalEffortCell); } private void addExtraEffortCell(Listitem item, EffortDurationPicker extraDurationPicker, Checkbox checkbox) { Listcell extraEffortCell = new Listcell(); Hbox hbox = new Hbox(); hbox.setSclass("extra effort cell"); hbox.appendChild(extraDurationPicker); hbox.appendChild(checkbox); extraEffortCell.appendChild(hbox); item.appendChild(extraEffortCell); } } private void reloadCurrentWindow() { Util.reloadBindings(window); highlightDaysOnCalendar(); } private void reloadDayInformation() { Util.reloadBindings(window.getFellow("dayInformation")); Util.reloadBindings(window.getFellow("exceptionInformation")); reloadWorkWeeksList(); reloadTypeDatesAndDuration(); highlightDaysOnCalendar(); } private void reloadSelectDayInformation() { Util.reloadBindings(window.getFellow("dayInformation")); Util.reloadBindings(window.getFellow("exceptionInformation")); Util.reloadBindings(window.getFellow("hoursPerDay")); reloadTypeDatesAndDuration(); highlightDaysOnCalendar(); } private void reloadWorkWeeksList() { Util.reloadBindings(window.getFellow("historyInformation")); Util.reloadBindings(window.getFellow("calendarTypeLabel")); } private void reloadTypeDatesAndDuration() { LocalDate selectedDay = baseCalendarModel.getSelectedDay(); CalendarExceptionType type = baseCalendarModel.getCalendarExceptionType(new LocalDate(selectedDay)); Combobox exceptionTypes = (Combobox) window.getFellow("exceptionTypes"); @SuppressWarnings("unchecked") List<Comboitem> items = exceptionTypes.getItems(); for (Comboitem item : items) { CalendarExceptionType value = item.getValue(); if ( (value == null) && (type == null) ) { exceptionTypes.setSelectedItem(item); break; } if ( (value != null) && (type != null) && (value.getName().equals(type.getName())) ) { exceptionTypes.setSelectedItem(item); break; } } Datebox dateboxStartDate = (Datebox) window.getFellow("exceptionStartDate"); dateboxStartDate.setValue(toDate(selectedDay)); Datebox dateboxEndDate = (Datebox) window.getFellow("exceptionEndDate"); dateboxEndDate.setValue(toDate(selectedDay)); capacityPicker.setValue(baseCalendarModel.getWorkableCapacity()); } private Map<String, List<Integer>> getDaysCurrentMonthByColor() { LocalDate currentDate = baseCalendarModel.getSelectedDay(); LocalDate minDate = currentDate.dayOfMonth().withMinimumValue(); LocalDate maxDate = currentDate.dayOfMonth().withMaximumValue(); Map<String, List<Integer>> colorsMap = new HashMap<>(); BaseCalendar calendar = baseCalendarModel.getBaseCalendar(); if ( calendar == null ) { return colorsMap; } for (LocalDate date = minDate; date.compareTo(maxDate) <= 0; date = date.plusDays(1)) { CalendarExceptionType calendarExceptionType = calendar.getExceptionType(date); if ( calendarExceptionType != null ) { if (calendar.getOwnExceptionDay(date) != null) { addDayToColor( colorsMap, calendarExceptionType.getColor().getColorOwnException(), date.getDayOfMonth()); } else { addDayToColor( colorsMap, calendarExceptionType.getColor().getColorDerivedException(), date.getDayOfMonth()); } } else { if ( calendar.getCapacityOn(PartialDay.wholeDay(date)).isZero() ) { addDayToColor(colorsMap, BACKGROUND_COLOR_ZERO_HOURS_DAY, date.getDayOfMonth()); } } } return colorsMap; } private void addDayToColor(Map<String, List<Integer>> colorsMap, String color, int day) { if ( colorsMap.get(color) == null) colorsMap.put(color, new ArrayList<>()); colorsMap.get(color).add(day); } public void highlightDaysOnCalendar() { Calendar calendar = (Calendar) window.getFellow("calendarWidget"); Clients.response(new AuInvoke(calendar, "resetHighlightedDates")); Map<String, List<Integer>> daysByColor = getDaysCurrentMonthByColor(); for (String color : daysByColor.keySet()) { Clients.response(new AuInvoke( calendar, "highlightDates", daysByColor.get(color).toArray(), TEXT_COLOR_HIGHLIGHTED_DAY, color)); } } public Date getSelectedDay() { Date selectedDay = toDate(baseCalendarModel.getSelectedDay()); return selectedDay == null ? new Date() : selectedDay; } private static Date toDate(LocalDate date) { return date == null ? null : date.toDateTimeAtStartOfDay().toDate(); } private static LocalDate toLocalDate(Date date) { return date == null ? null : LocalDate.fromDateFields(date); } public void setSelectedDay(LocalDate date) { baseCalendarModel.setSelectedDay(date); reloadSelectDayInformation(); } public String getTypeOfDay() { BaseCalendar calendar = baseCalendarModel.getBaseCalendar(); if ( calendar == null ) { return ""; } LocalDate date = baseCalendarModel.getSelectedDay(); CalendarException exceptionDay = calendar.getExceptionDay(date); if ( exceptionDay != null ) { if ( calendar.getOwnExceptionDay(date) != null ) { return _("Exception: {0}", exceptionDay.getType().getName()); } else { return _("Exception: {0} (Inh)", exceptionDay.getType().getName()); } } if ( calendar.getCapacityOn(PartialDay.wholeDay(date)).isZero() ) { return _("Not workable day"); } return _("Normal"); } public String getWorkableTime() { return asString(baseCalendarModel.getWorkableTime()); } public void createException() { Combobox exceptionTypes = (Combobox) window.getFellow("exceptionTypes"); CalendarExceptionType type = exceptionTypes.getSelectedItem().getValue(); if ( type == null ) { throw new WrongValueException(exceptionTypes, _("Please, select type of exception")); } else { Clients.clearWrongValue(exceptionTypes); } Datebox dateboxStartDate = (Datebox) window.getFellow("exceptionStartDate"); Date startDate = dateboxStartDate.getValue(); if ( startDate == null ) { throw new WrongValueException(dateboxStartDate, _("You should select a start date for the exception")); } else { Clients.clearWrongValue(dateboxStartDate); } Datebox dateboxEndDate = (Datebox) window.getFellow("exceptionEndDate"); Date endDate = dateboxEndDate.getValue(); if ( endDate == null ) { throw new WrongValueException(dateboxEndDate, _("Please, select an End Date for the Exception")); } else { Clients.clearWrongValue(dateboxEndDate); } if ( new LocalDate(startDate).compareTo(new LocalDate(endDate)) > 0 ) { throw new WrongValueException(dateboxEndDate, _("Exception end date should be greater or equals than start date")); } else { Clients.clearWrongValue(dateboxEndDate); } Capacity capacity = capacityPicker.getValue(); baseCalendarModel.createException( type, LocalDate.fromDateFields(startDate), LocalDate.fromDateFields(endDate), capacity); reloadDayInformation(); } public boolean isNotExceptional() { return !baseCalendarModel.isExceptional(); } public void removeException() { baseCalendarModel.removeException(); reloadDayInformation(); } public List<CalendarData> getHistoryVersions() { return baseCalendarModel.getHistoryVersions(); } public HistoryVersionsRenderer getHistoryVersionsRenderer() { return historyVersionsRenderer; } public class HistoryVersionsRenderer implements ListitemRenderer { @Override public void render(Listitem listitem, Object o, int i) throws Exception { CalendarData calendarData = (CalendarData) o; listitem.setValue(calendarData); if ( isDerived() ) { appendParentCombobox(listitem, calendarData); } else { appendParentLabel(listitem, calendarData); } appendStartDatebox(listitem, calendarData); appendExpiringDatebox(listitem, calendarData); appendSummaryLabel(listitem, calendarData); appendOperationsListcell(listitem, calendarData); markAsSelected(listitem, calendarData); addEventListener(listitem); } private void appendSummaryLabel(final Listitem listItem, final CalendarData version) { BaseCalendar parent = version.getParent(); List<String> summary = new ArrayList<>(); for (Days day : Days.values()) { if ( version.isDefault(day) ) { if ( parent == null ) { summary.add("0"); } else { summary.add(_("Inh")); } } else { summary.add(asString(version.getCapacityOn(day))); } } Label summaryLabel = new Label(StringUtils.join(summary, " - ")); Listcell listCell = new Listcell(); listCell.appendChild(summaryLabel); listItem.appendChild(listCell); } private void appendStartDatebox(final Listitem listItem, final CalendarData version) { Datebox datebox = new Datebox(); final LocalDate dateValidFrom = baseCalendarModel.getValidFrom(version); if ( dateValidFrom != null ) { Util.bind( datebox, () -> dateValidFrom.toDateTimeAtStartOfDay().toDate()); datebox.setDisabled(false); } else { datebox.setDisabled(true); } datebox.addEventListener(Events.ON_CHANGE, (EventListener) event -> reloadWorkWeeksList()); datebox.setConstraint((comp, value) -> { try { baseCalendarModel.checkAndChangeStartDate(version, ((Date) value)); } catch (ValidationException e) { throw new WrongValueException(comp, e.getMessage()); } }); Listcell listCell = new Listcell(); listCell.appendChild(datebox); listItem.appendChild(listCell); } private void appendExpiringDatebox(final Listitem listItem, final CalendarData version) { Datebox datebox = new Datebox(); final LocalDate expiringDate = version.getExpiringDate(); if ( expiringDate != null ) { datebox.setDisabled(false); } else { datebox.setDisabled(true); } Util.bind( datebox, () -> { LocalDate expiringDate1 = version.getExpiringDate(); if ( expiringDate1 != null ) { return expiringDate1.minusDays(1).toDateTimeAtStartOfDay().toDate(); } return null; }, value -> { LocalDate expiringDate1 = null; if ( value != null ) { expiringDate1 = new LocalDate(value).plusDays(1); } version.setExpiringDate(expiringDate1); }); datebox.addEventListener(Events.ON_CHANGE, (EventListener) event -> reloadWorkWeeksList()); datebox.setConstraint((comp, value) -> { Date date = ((Date) value); try { baseCalendarModel.checkChangeExpiringDate(version, date); } catch (ValidationException e) { throw new WrongValueException(comp, e.getMessage()); } }); Listcell listCell = new Listcell(); listCell.appendChild(datebox); listItem.appendChild(listCell); } private void appendParentLabel(Listitem listItem, CalendarData version) { final Label labelParent = new Label(); if ( version.getParent() != null ) { labelParent.setValue(version.getParent().getName()); } Listcell listCell = new Listcell(); listCell.appendChild(labelParent); listItem.appendChild(listCell); } private void appendParentCombobox(final Listitem listItem, final CalendarData version) { final Combobox comboParents = new Combobox(); final List<BaseCalendar> listParents = getParentCalendars(); for (BaseCalendar parent : listParents) { Comboitem comboItem = new Comboitem(); comboItem.setValue(parent); comboItem.setLabel(parent.getName()); comboItem.setParent(comboParents); if ( (version.getParent() ) != null && (parent.getId().equals(version.getParent().getId())) ) { comboParents.setSelectedItem(comboItem); } } comboParents.addEventListener(Events.ON_SELECT, (EventListener) event -> { if ( comboParents.getSelectedItem() != null ) { BaseCalendar parent = comboParents.getSelectedItem().getValue(); version.setParent(parent); } }); Util.bind( comboParents, () -> comboParents.getSelectedItem(), comboItem -> { if ( (comboItem != null) && (comboItem.getValue() != null ) && (comboItem.getValue() instanceof BaseCalendar) ) { BaseCalendar parent = comboItem.getValue(); version.setParent(parent); } }); Listcell listCell = new Listcell(); listCell.appendChild(comboParents); listItem.appendChild(listCell); } private void markAsSelected(Listitem item, CalendarData calendarData) { CalendarData selected = baseCalendarModel.getCalendarData(); if ( (selected != null) && (calendarData.equals(selected)) ) { item.setSelected(true); } } private void appendOperationsListcell(Listitem item, CalendarData calendarData) { Listcell listcell = new Listcell(); listcell.appendChild(createRemoveButton(calendarData)); item.appendChild(listcell); } private Button createRemoveButton(final CalendarData calendarData) { Button result = createButton( "/common/img/ico_borrar1.png", _("Delete"), "/common/img/ico_borrar.png", "icono", event -> { baseCalendarModel.removeCalendarData(calendarData); reloadWorkWeeksList(); }); if ( baseCalendarModel.getBaseCalendar() == null || baseCalendarModel.getBaseCalendar().getCalendarDataVersions().size() == 1 ) { result.setDisabled(true); } else { result.setDisabled(false); } return result; } private Button createButton(String image, String tooltip, String hoverImage, String styleClass, EventListener eventListener) { Button result = new Button("", image); result.setHoverImage(hoverImage); result.setSclass(styleClass); result.setTooltiptext(tooltip); result.addEventListener(Events.ON_CLICK, eventListener); return result; } private void addEventListener(Listitem item) { item.addEventListener(Events.ON_CLICK, (EventListener) event -> { Listitem item1 = (Listitem) event.getTarget(); CalendarData calendarData = item1.getValue(); LocalDate dateValidFrom = baseCalendarModel.getValidFrom(calendarData); LocalDate expiringDate = calendarData.getExpiringDate(); if ( dateValidFrom != null ) { goToDate(dateValidFrom.toDateTimeAtStartOfDay().toDate()); } else if ( expiringDate != null ) { goToDate(expiringDate.minusDays(1).toDateTimeAtStartOfDay().toDate()); } else { goToDate(new Date()); } }); } } public boolean isLastVersion(LocalDate selectedDate) { return baseCalendarModel.isLastVersion(selectedDate); } public boolean isFirstVersion(LocalDate selectedDate) { return baseCalendarModel.isFirstVersion(selectedDate); } public void goToDate(Date date) { setSelectedDay(toLocalDate(date)); } public boolean isCreatingNewVersion() { return creatingNewVersion; } public void createNewVersion() { creatingNewVersion = true; initDatesToCreateNewVersion(); try { Util.reloadBindings(createNewVersionWindow); createNewVersionWindow.doModal(); } catch (Exception e) { throw new RuntimeException(e); } } private void initDatesToCreateNewVersion() { this.newWorkWeekStartDate = (new LocalDate()).plusDays(1).toDateTimeAtStartOfDay().toDate(); this.newWorkWeekExpiringDate = (new LocalDate()).plusDays(2).toDateTimeAtStartOfDay().toDate(); } public void acceptCreateNewVersion() { Component compStartDate = createNewVersionWindow.getFellow("startDateValidFromNewVersion"); LocalDate startDate = getLocalDateFrom((Datebox) compStartDate); Component compExpiringDate = createNewVersionWindow.getFellow("expiringDateValidFromNewVersion"); LocalDate expiringDate = getLocalDateFrom((Datebox) compExpiringDate); BaseCalendar selected = null; if ( isDerived() ) { Combobox parentCalendars = (Combobox) createNewVersionWindow.getFellow("parentCalendars"); if ( parentCalendars.getSelectedItem() == null ) { throw new WrongValueException(parentCalendars, _("cannot be empty")); } selected = parentCalendars.getSelectedItem().getValue(); } try { baseCalendarModel.createNewVersion(startDate, expiringDate, selected); } catch (IllegalArgumentException e) { if ( e.getMessage().contains("Wrong expiring date") ) { throw new WrongValueException(compExpiringDate, _(e.getMessage())); } else { throw new WrongValueException(compStartDate, _(e.getMessage())); } } Clients.clearWrongValue(compStartDate); Clients.clearWrongValue(compExpiringDate); creatingNewVersion = false; Util.reloadBindings(createNewVersionWindow); setSelectedDay(startDate); if ( (startDate == null) && (expiringDate != null) ) { setSelectedDay(expiringDate.minusDays(1)); } reloadCurrentWindow(); } private static LocalDate getLocalDateFrom(Datebox datebox) { Date value = datebox.getValue(); return value == null ? null : LocalDate.fromDateFields(value); } public void cancelNewVersion() { creatingNewVersion = false; Util.reloadBindings(createNewVersionWindow); } public Date getDateValidFromNewVersion() { return newWorkWeekStartDate; } public void setDateValidFromNewVersion(Date date) { this.newWorkWeekStartDate = date; } public Date getDateValidToNewVersion() { return newWorkWeekExpiringDate; } public void setDateValidToNewVersion(Date date) { this.newWorkWeekExpiringDate = date; } public List<BaseCalendar> getParentCalendars() { return baseCalendarModel.getPossibleParentCalendars(); } public List<CalendarException> getCalendarExceptions() { List<CalendarException> calendarExceptions = new ArrayList<>(baseCalendarModel.getCalendarExceptions()); Collections.sort(calendarExceptions, (o1, o2) -> o1.getDate().compareTo(o2.getDate())); return calendarExceptions; } public ParentCalendarsComboitemRenderer getParentCalendarsComboitemRenderer() { return parentCalendarsComboitemRenderer; } public class ParentCalendarsComboitemRenderer implements ComboitemRenderer { @Override public void render(Comboitem comboitem, Object o, int i) throws Exception { BaseCalendar calendar = (BaseCalendar) o; comboitem.setLabel(calendar.getName()); comboitem.setValue(calendar); Combobox combobox = (Combobox) comboitem.getParent(); if ( combobox.getSelectedIndex() != 0 ) { combobox.setSelectedIndex(0); } } } public CalendarExceptionRenderer getCalendarExceptionRenderer() { return calendarExceptionRenderer; } public class CalendarExceptionRenderer implements ListitemRenderer { @Override public void render(Listitem listitem, Object o, int i) throws Exception { CalendarException calendarException = (CalendarException) o; listitem.setValue(calendarException); appendDayListcell(listitem, calendarException); appendExceptionTypeListcell(listitem, calendarException); appendStandardEffortListcell(listitem, calendarException.getCapacity()); appendExtraEffortListcell(listitem, calendarException.getCapacity()); appendCodeListcell(listitem, calendarException); appendOriginListcell(listitem, calendarException); appendOperationsListcell(listitem, calendarException); markAsSelected(listitem, calendarException); } private void markAsSelected(Listitem item, CalendarException calendarException) { LocalDate selectedDay = baseCalendarModel.getSelectedDay(); if ( selectedDay != null && calendarException.getDate().equals(selectedDay) ) item.setSelected(true); } } private void appendDayListcell(Listitem item, CalendarException calendarException) { Listcell listcell = new Listcell(); listcell.appendChild(new Label(calendarException.getDate().toString())); item.appendChild(listcell); } private void appendStandardEffortListcell(Listitem item, Capacity capacity) { Listcell listcell = new Listcell(); listcell.appendChild(new Label(_(capacity.getStandardEffortString()))); item.appendChild(listcell); } private void appendExtraEffortListcell(Listitem item, Capacity capacity) { Listcell listcell = new Listcell(); listcell.appendChild(new Label(_(capacity.getExtraEffortString()))); item.appendChild(listcell); } private void appendExceptionTypeListcell(Listitem item, CalendarException calendarException) { Listcell listcell = new Listcell(); String type = ""; if ( calendarException.getType() != null ) { type = calendarException.getType().getName(); } listcell.appendChild(new Label(type)); item.appendChild(listcell); } private void appendCodeListcell(final Listitem item, final CalendarException calendarException) { item.setValue(calendarException); Listcell listcell = new Listcell(); final Textbox code = new Textbox(); if ( getBaseCalendar() != null ) { code.setDisabled(getBaseCalendar().isCodeAutogenerated()); } Util.bind( code, () -> calendarException.getCode(), value -> { try { calendarException.setCode(value); } catch (IllegalArgumentException e) { throw new WrongValueException(code, e.getMessage()); } }); code.setConstraint("no empty:" + _("cannot be empty")); listcell.appendChild(code); item.appendChild(listcell); } private void appendOriginListcell(Listitem item, CalendarException calendarException) { Listcell listcell = new Listcell(); String origin = _("Inherited"); if ( baseCalendarModel.isOwnException(calendarException) ) origin = _("Direct"); listcell.appendChild(new Label(origin)); item.appendChild(listcell); } private void appendOperationsListcell(Listitem item, CalendarException calendarException) { Listcell listcell = new Listcell(); listcell.appendChild(createRemoveButton(calendarException)); item.appendChild(listcell); } private Button createRemoveButton(final CalendarException calendarException) { Button result = createButton( "/common/img/ico_borrar1.png", _("Delete"), "/common/img/ico_borrar.png", "icono", event -> { baseCalendarModel.removeException(calendarException.getDate()); reloadDayInformation(); }); if ( !baseCalendarModel.isOwnException(calendarException) ) { result.setDisabled(true); result.setTooltiptext(_("inherited exception can not be removed")); } return result; } private Button createButton(String image, String tooltip, String hoverImage, String styleClass, EventListener eventListener) { Button result = new Button("", image); result.setHoverImage(hoverImage); result.setSclass(styleClass); result.setTooltiptext(tooltip); result.addEventListener(Events.ON_CLICK, eventListener); return result; } public boolean isOwnExceptionDay() { return baseCalendarModel.isOwnExceptionDay(); } public boolean isNotOwnExceptionDay() { return !isOwnExceptionDay(); } public void updateException() { Combobox exceptionTypes = (Combobox) window.getFellow("exceptionTypes"); CalendarExceptionType type = exceptionTypes.getSelectedItem().getValue(); Datebox dateboxStartDate = (Datebox) window.getFellow("exceptionStartDate"); Date startDate = dateboxStartDate.getValue(); if ( startDate == null ) { throw new WrongValueException(dateboxStartDate, _("You should select a start date for the exception")); } else { Clients.clearWrongValue(dateboxStartDate); } Datebox dateboxEndDate = (Datebox) window.getFellow("exceptionEndDate"); Date endDate = dateboxEndDate.getValue(); if ( endDate == null ) { throw new WrongValueException(dateboxEndDate, _("Please, select an End Date for the Exception")); } else { Clients.clearWrongValue(dateboxEndDate); } if ( startDate.compareTo(endDate) > 0 ) { throw new WrongValueException(dateboxEndDate, _("Exception end date should be greater or equals than start date")); } else { Clients.clearWrongValue(dateboxEndDate); } Capacity capacity = capacityPicker.getValue(); baseCalendarModel.updateException( type, LocalDate.fromDateFields(startDate), LocalDate.fromDateFields(endDate), capacity); reloadDayInformation(); } public String getNameParentCalendar() { BaseCalendar parent = baseCalendarModel.getCurrentParent(); return parent != null ? parent.getName() : ""; } public boolean isResourceCalendar() { return baseCalendarModel.isResourceCalendar(); } public List<CalendarAvailability> getCalendarAvailabilities() { return baseCalendarModel.getCalendarAvailabilities(); } public CalendarAvailabilityRenderer getCalendarAvailabilityRenderer() { return calendarAvailabilityRenderer; } public class CalendarAvailabilityRenderer implements ListitemRenderer { @Override public void render(Listitem listitem, Object o, int i) throws Exception { CalendarAvailability calendarAvailability = (CalendarAvailability) o; listitem.setValue(calendarAvailability); appendValidFromListcell(listitem, calendarAvailability); appendExpirationDateListcell(listitem, calendarAvailability); appendAvailabilityCodeListcell(listitem, calendarAvailability); appendOperationsListcell(listitem, calendarAvailability); } private void appendValidFromListcell(Listitem item, final CalendarAvailability calendarAvailability) { Listcell listcell = new Listcell(); final Datebox datebox = new Datebox(); Datebox dateboxValidFrom = Util.bind( datebox, () -> { LocalDate startDate = calendarAvailability.getStartDate(); return startDate != null ? startDate.toDateTimeAtStartOfDay().toDate() : null; }, value -> { LocalDate startDate = new LocalDate(value); try { baseCalendarModel.setStartDate(calendarAvailability, startDate); } catch (IllegalArgumentException e) { throw new WrongValueException(datebox, e.getMessage()); } }); listcell.appendChild(dateboxValidFrom); item.appendChild(listcell); } private void appendExpirationDateListcell(Listitem item, final CalendarAvailability calendarAvailability) { Listcell listcell = new Listcell(); final Datebox datebox = new Datebox(); Datebox dateboxExpirationDate = Util.bind( datebox, () -> { LocalDate endDate = calendarAvailability.getEndDate(); return endDate != null ? endDate.toDateTimeAtStartOfDay().toDate() : null; }, value -> { try { LocalDate endDate = getAppropiateEndDate(calendarAvailability, value); baseCalendarModel.setEndDate(calendarAvailability, endDate); } catch (IllegalArgumentException e) { throw new WrongValueException(datebox, e.getMessage()); } }); listcell.appendChild(dateboxExpirationDate); item.appendChild(listcell); } private LocalDate getAppropiateEndDate(CalendarAvailability calendarAvailability, Date endDate) { if ( endDate == null ) { if (baseCalendarModel.isLastActivationPeriod(calendarAvailability)) { return null; } else { throw new IllegalArgumentException(_("End date can only be deleted in the the last activation")); } } return new LocalDate(endDate); } private void appendAvailabilityCodeListcell(Listitem item, final CalendarAvailability availability) { item.setValue(availability); Listcell listcell = new Listcell(); final Textbox code = new Textbox(); if ( getBaseCalendar() != null ) { code.setDisabled(getBaseCalendar().isCodeAutogenerated()); } Util.bind( code, () -> availability.getCode(), value -> { try { availability.setCode(value); } catch (IllegalArgumentException e) { throw new WrongValueException(code, e.getMessage()); } }); code.setConstraint("no empty:" + _("cannot be empty")); listcell.appendChild(code); item.appendChild(listcell); } private void appendOperationsListcell(Listitem item, CalendarAvailability calendarAvailability) { Listcell listcell = new Listcell(); listcell.appendChild(createRemoveButton(calendarAvailability)); item.appendChild(listcell); } private Button createRemoveButton(final CalendarAvailability calendarAvailability) { return createButton( "/common/img/ico_borrar1.png", _("Delete"), "/common/img/ico_borrar.png", "icono", event -> { baseCalendarModel.removeCalendarAvailability(calendarAvailability); reloadDayInformation(); reloadActivationPeriods(); }); } private Button createButton(String image, String tooltip, String hoverImage, String styleClass, EventListener eventListener) { Button result = new Button("", image); result.setHoverImage(hoverImage); result.setSclass(styleClass); result.setTooltiptext(tooltip); result.addEventListener(Events.ON_CLICK, eventListener); return result; } } public void createCalendarAvailability() { baseCalendarModel.createCalendarAvailability(); reloadDayInformation(); reloadActivationPeriods(); } private void reloadActivationPeriods() { Util.reloadBindings(window.getFellow("calendarAvailabilities")); } public void reloadExceptionsList() { Util.reloadBindings(window.getFellow("exceptionsList")); } public boolean isNotResourceCalendar() { BaseCalendar baseCalendar = baseCalendarModel.getBaseCalendar(); return !((baseCalendar == null) || (baseCalendar instanceof ResourceCalendar)); } public void validateCalendarExceptionCodes(){ Listbox listbox = (Listbox) window.getFellow("exceptionsList"); if ( listbox != null ) { for (int i = 0; i < listbox.getItemCount(); i++) { Listitem item = listbox.getItems().get(i); if ( item.getChildren().size() == 5) { Textbox code = (Textbox) (item.getChildren().get(3)).getFirstChild(); if ( code != null && !code.isDisabled() && code.getValue().isEmpty() ) { throw new WrongValueException(code, _("It cannot be empty")); } } } } } public void onCheckGenerateCode(Event e) { CheckEvent ce = (CheckEvent) e; if (ce.isChecked()) { try { baseCalendarModel.setCodeAutogenerated(ce.isChecked()); } catch (ConcurrentModificationException err) { messagesForUser.showMessage(Level.ERROR, err.getMessage()); } } reloadCodeInformation(); } private void reloadCodeInformation() { Util.reloadBindings(window.getFellow("txtCode")); reloadExceptionsList(); reloadActivationPeriods(); } public void onSelectException(Event event) { Listbox listBox = (Listbox) event.getTarget(); Listitem item = listBox.getSelectedItem(); if ( item != null ) { CalendarException calendarException = item.getValue(); setSelectedDay(calendarException.getDate()); reloadDayInformation(); } } public boolean isVirtualWorker() { return baseCalendarModel.isVirtualWorker(); } public Integer getCapacity() { return baseCalendarModel.getCapacity(); } public void setCapacity(Integer capacity) { baseCalendarModel.setCapacity(capacity); } }