/* * 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-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.resources.machine; import static org.libreplan.web.I18nHelper._; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.List; import java.util.Set; import org.joda.time.LocalDate; import org.libreplan.business.calendars.entities.BaseCalendar; import org.libreplan.business.calendars.entities.ResourceCalendar; import org.libreplan.business.common.entities.Limits; import org.libreplan.business.common.exceptions.InstanceNotFoundException; import org.libreplan.business.common.exceptions.ValidationException; import org.libreplan.business.resources.daos.IResourceDAO; import org.libreplan.business.resources.entities.Machine; import org.libreplan.web.calendars.BaseCalendarEditionController; import org.libreplan.web.calendars.IBaseCalendarModel; import org.libreplan.web.common.BaseCRUDController; import org.libreplan.web.common.ConstraintChecker; import org.libreplan.web.common.Level; import org.libreplan.web.common.Util; import org.libreplan.web.common.ILimitsModel; import org.libreplan.web.common.components.bandboxsearch.BandboxMultipleSearch; import org.libreplan.web.costcategories.ResourcesCostCategoryAssignmentController; import org.libreplan.web.resources.search.ResourcePredicate; import org.libreplan.web.resources.worker.CriterionsController; import org.libreplan.web.resources.worker.CriterionsMachineController; import org.libreplan.web.resources.worker.WorkerCRUDController.LimitingResourceEnum; 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.Events; import org.zkoss.zkplus.spring.SpringUtil; import org.zkoss.zul.Combobox; import org.zkoss.zul.Comboitem; import org.zkoss.zul.ComboitemRenderer; import org.zkoss.zul.Constraint; import org.zkoss.zul.Datebox; import org.zkoss.zul.Grid; 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.RowRenderer; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.Tab; import org.zkoss.zul.Textbox; import org.zkoss.zul.Window; /** * Controller for {@link Machine} resource. * <br /> * @author Diego Pino Garcia <dpino@igalia.com> * @author Lorenzo Tilve Álvaro <ltilve@igalia.com> * @author Vova Perebykivskyi <vova@libreplan-enterprise.com> */ public class MachineCRUDController extends BaseCRUDController<Machine> { private ILimitsModel limitsModel; private IMachineModel machineModel; private IBaseCalendarModel resourceCalendarModel; private IResourceDAO resourceDAO; private Component configurationUnits; private CriterionsMachineController criterionsController; private MachineConfigurationController configurationController; private ResourcesCostCategoryAssignmentController resourcesCostCategoryAssignmentController; private Grid listing; private Datebox filterStartDate; private Datebox filterFinishDate; private Listbox filterLimitingResource; private Textbox txtfilter; private BandboxMultipleSearch bdFilters; private BaseCalendarsComboitemRenderer baseCalendarsComboitemRenderer = new BaseCalendarsComboitemRenderer(); public List<Machine> getMachines() { return machineModel.getMachines(); } public Machine getMachine() { return machineModel.getMachine(); } @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); machineModel = (IMachineModel) SpringUtil.getBean("machineModel"); limitsModel = (ILimitsModel) SpringUtil.getBean("limitsModel"); resourceDAO = (IResourceDAO) SpringUtil.getBean("resourceDAO"); resourceCalendarModel = (IBaseCalendarModel) SpringUtil.getBean("resourceCalendarModel"); setupCriterionsController(); setupConfigurationController(); setupResourcesCostCategoryAssignmentController(); showListWindow(); initFilterComponent(); setupFilterLimitingResourceListbox(); } private void initFilterComponent() { this.filterFinishDate = (Datebox) listWindow.getFellowIfAny("filterFinishDate"); this.filterStartDate = (Datebox) listWindow.getFellowIfAny("filterStartDate"); this.filterLimitingResource = (Listbox) listWindow.getFellowIfAny("filterLimitingResource"); this.bdFilters = (BandboxMultipleSearch) listWindow.getFellowIfAny("bdFilters"); this.txtfilter = (Textbox) listWindow.getFellowIfAny("txtfilter"); this.listing = (Grid) listWindow.getFellowIfAny("listing"); clearFilterDates(); } private void setupCriterionsController() throws Exception { final Component comp = editWindow.getFellowIfAny("criterionsContainer"); criterionsController = new CriterionsMachineController(); criterionsController.doAfterCompose(comp); } private void setupConfigurationController() { configurationUnits = editWindow.getFellow("configurationUnits"); configurationController = (MachineConfigurationController) configurationUnits.getAttribute("configurationController", true); } private void setupResourcesCostCategoryAssignmentController() { Component costCategoryAssignmentContainer = editWindow.getFellowIfAny("costCategoryAssignmentContainer"); resourcesCostCategoryAssignmentController = (ResourcesCostCategoryAssignmentController) costCategoryAssignmentContainer.getAttribute("assignmentController", true); } @Override protected void initCreate() { machineModel.initCreate(); criterionsController.prepareForCreate(machineModel.getMachine()); configurationController.initConfigurationController(machineModel); resourcesCostCategoryAssignmentController.setResource(machineModel.getMachine()); selectMachineDataTab(); resourceCalendarModel.cancel(); } @Override protected void initEdit(Machine machine) { machineModel.initEdit(machine); prepareCriterionsForEdit(); prepareCalendarForEdit(); selectMachineDataTab(); configurationController.initConfigurationController(machineModel); resourcesCostCategoryAssignmentController.setResource(machineModel.getMachine()); } private void selectMachineDataTab() { Tab tabMachineData = (Tab) editWindow.getFellow("tbMachineData"); tabMachineData.setSelected(true); } private void prepareCriterionsForEdit() { criterionsController.prepareForEdit(machineModel.getMachine()); } private void prepareCalendarForEdit() { if (isCalendarNull()) { return; } updateCalendarController(); resourceCalendarModel.initEdit(machineModel.getCalendarOfMachine()); try { baseCalendarEditionController.doAfterCompose(editCalendarWindow); } catch (Exception e) { throw new RuntimeException(e); } baseCalendarEditionController.setSelectedDay(new LocalDate()); Util.reloadBindings(editCalendarWindow); Util.reloadBindings(createNewVersionWindow); } @Override protected void beforeSaving() throws ValidationException { validateConstraints(); } @Override protected void save() throws ValidationException { saveCalendar(); if (!confirmCriterions()) { return; } machineModel.confirmSave(); } private void validateConstraints() { Tab tab = (Tab) editWindow.getFellowIfAny("tbMachineData"); try { validateMachineDataTab(); tab = (Tab) editWindow.getFellowIfAny("assignedCriteriaTab"); criterionsController.validateConstraints(); tab = (Tab) editWindow.getFellowIfAny("costCategoryAssignmentTab"); resourcesCostCategoryAssignmentController.validateConstraints(); // TODO: check 'calendar' tab } catch (WrongValueException e) { tab.setSelected(true); throw e; } } private void validateMachineDataTab() { ConstraintChecker.isValid(editWindow.getFellowIfAny("machineDataTabpanel")); } private void saveCalendar() throws ValidationException { if (baseCalendarEditionController != null) { baseCalendarEditionController.save(); } if (machineModel.getCalendar() == null) { createCalendar(); } } private boolean confirmCriterions() throws ValidationException { if (criterionsController != null) { if (!criterionsController.validate()) { return false; } criterionsController.save(); } return true; } public List<BaseCalendar> getBaseCalendars() { return machineModel.getBaseCalendars(); } private void createCalendar() { Combobox combobox = (Combobox) editWindow.getFellow("createDerivedCalendar"); Comboitem selectedItem = combobox.getSelectedItem(); if (selectedItem == null) { throw new WrongValueException(combobox, _("Please, select a calendar")); } BaseCalendar parentCalendar = combobox.getSelectedItem().getValue(); if (parentCalendar == null) { parentCalendar = machineModel.getDefaultCalendar(); } resourceCalendarModel.initCreateDerived(parentCalendar); resourceCalendarModel.generateCalendarCodes(); machineModel.setCalendar((ResourceCalendar) resourceCalendarModel.getBaseCalendar()); } private Window editCalendarWindow; private Window createNewVersionWindow; private BaseCalendarEditionController baseCalendarEditionController; private void updateCalendarController() { editCalendarWindow = (Window) editWindow.getFellowIfAny("editCalendarWindow"); createNewVersionWindow = (Window) editWindow.getFellowIfAny("createNewVersion"); createNewVersionWindow.setVisible(true); createNewVersionWindow.setVisible(false); baseCalendarEditionController = new BaseCalendarEditionController( resourceCalendarModel, editCalendarWindow, createNewVersionWindow, messagesForUser) { @Override public void goToList() { machineModel.setCalendarOfMachine((ResourceCalendar) resourceCalendarModel.getBaseCalendar()); reloadWindow(); } @Override public void cancel() { machineModel.removeCalendar(); resourceCalendarModel.cancel(); reloadWindow(); } @Override public void save() { validateCalendarExceptionCodes(); ResourceCalendar calendar = (ResourceCalendar) resourceCalendarModel.getBaseCalendar(); if (calendar != null) { resourceCalendarModel.generateCalendarCodes(); machineModel.setCalendarOfMachine(calendar); } reloadWindow(); } @Override public void saveAndContinue() { save(); } }; editCalendarWindow.setAttribute("calendarController", this, true); createNewVersionWindow.setAttribute("calendarController", this, true); } private void reloadWindow() { Util.reloadBindings(editWindow); } public boolean isCalendarNull() { return (machineModel.getCalendarOfMachine() == null); } public boolean isCalendarNotNull() { return !isCalendarNull(); } public BaseCalendarEditionController getEditionController() { return baseCalendarEditionController; } @SuppressWarnings("unused") private CriterionsController getCriterionsController() { return (CriterionsController) editWindow.getFellow("criterionsContainer").getAttribute("assignedCriterionsController"); } public MachineConfigurationController getConfigurationController() { return configurationController; } public BaseCalendarsComboitemRenderer getBaseCalendarsComboitemRenderer() { return baseCalendarsComboitemRenderer; } private class BaseCalendarsComboitemRenderer 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); if (isDefaultCalendar(calendar)) { Combobox combobox = (Combobox) comboitem.getParent(); combobox.setSelectedItem(comboitem); } } private boolean isDefaultCalendar(BaseCalendar calendar) { BaseCalendar defaultCalendar = machineModel.getDefaultCalendar(); return defaultCalendar.getId().equals(calendar.getId()); } } /** * Operations to filter the machines by multiple filters. */ public Constraint checkConstraintFinishDate() { return new Constraint() { @Override public void validate(Component comp, Object value) throws WrongValueException { Date finishDate = (Date) value; if ((finishDate != null) && (filterStartDate.getValue() != null) && (finishDate.compareTo(filterStartDate.getValue()) < 0)) { filterFinishDate.setValue(null); throw new WrongValueException(comp, _("must be after start date")); } } }; } public Constraint checkConstraintStartDate() { return new Constraint() { @Override public void validate(Component comp, Object value) throws WrongValueException { Date startDate = (Date) value; if ((startDate != null) && (filterFinishDate.getValue() != null) && (startDate.compareTo(filterFinishDate.getValue()) > 0)) { filterStartDate.setValue(null); throw new WrongValueException(comp, _("must be lower than end date")); } } }; } public void onApplyFilter() { ResourcePredicate predicate = createPredicate(); if (predicate != null) { filterByPredicate(predicate); } else { showAllMachines(); } } private ResourcePredicate createPredicate() { List listFilters = bdFilters.getSelectedElements(); String personalFilter = txtfilter.getValue(); // Get the dates filter LocalDate startDate = null; LocalDate finishDate = null; if (filterStartDate.getValue() != null) { startDate = LocalDate.fromDateFields(filterStartDate.getValue()); } if (filterFinishDate.getValue() != null) { finishDate = LocalDate.fromDateFields(filterFinishDate.getValue()); } final Listitem item = filterLimitingResource.getSelectedItem(); Boolean isLimitingResource = (item != null) ? LimitingResourceEnum.valueOf((LimitingResourceEnum) item.getValue()) : null; if (listFilters.isEmpty() && (personalFilter == null || personalFilter.isEmpty()) && startDate == null && finishDate == null && isLimitingResource == null) { return null; } return new ResourcePredicate(listFilters, personalFilter, startDate, finishDate, isLimitingResource); } private void filterByPredicate(ResourcePredicate predicate) { List<Machine> filteredResources = machineModel.getFilteredMachines(predicate); listing.setModel(new SimpleListModel<>(filteredResources.toArray())); listing.invalidate(); } private void clearFilterDates() { filterStartDate.setValue(null); filterFinishDate.setValue(null); } public void showAllMachines() { listing.setModel(new SimpleListModel(machineModel.getAllMachines().toArray())); listing.invalidate(); } private void setupFilterLimitingResourceListbox() { for(LimitingResourceEnum resourceEnum : LimitingResourceEnum.getLimitingResourceFilterOptionList()) { Listitem item = new Listitem(); item.setParent(filterLimitingResource); item.setValue(resourceEnum); item.appendChild(new Listcell(resourceEnum.toString())); filterLimitingResource.appendChild(item); } filterLimitingResource.setSelectedIndex(0); } public Set<LimitingResourceEnum> getLimitingResourceOptionList() { return LimitingResourceEnum.getLimitingResourceOptionList(); } public Object getLimitingResource() { final Machine machine = getMachine(); return (machine != null) ? LimitingResourceEnum.valueOf(machine.isLimitingResource()) : LimitingResourceEnum.NON_LIMITING_RESOURCE; // Default option } public void setLimitingResource(LimitingResourceEnum option) { Machine machine = getMachine(); if (machine != null) { machine.setResourceType(LimitingResourceEnum.toResourceType(option)); } } public boolean isEditing() { return (getMachine() != null && !getMachine().isNewObject()); } public void onCheckGenerateCode(Event e) { CheckEvent ce = (CheckEvent) e; if (ce.isChecked()) { // We have to auto-generate the code if it's unsaved try { machineModel.setCodeAutogenerated(ce.isChecked()); } catch (ConcurrentModificationException err) { messagesForUser.showMessage(Level.ERROR, err.getMessage()); } Util.reloadBindings(editWindow); } } @Override protected boolean beforeDeleting(Machine machine) { if (!machineModel.canRemove(machine)) { messagesForUser.showMessage( Level.WARNING, _("Machine cannot be deleted. Machine is allocated to a project or contains imputed hours")); return false; } return true; } @Override protected void delete(Machine machine) { try { machineModel.confirmRemove(machine); } catch (InstanceNotFoundException e) { messagesForUser.showMessage(Level.INFO, _("Machine was already removed")); } } public RowRenderer getMachinesRenderer() { return (row, data, i) -> { final Machine machine = (Machine) data; row.setValue(machine); row.addEventListener(Events.ON_CLICK, event -> goToEditForm(machine)); row.appendChild(new Label(machine.getName())); row.appendChild(new Label(machine.getDescription())); row.appendChild(new Label(machine.getCode())); row.appendChild(new Label((Boolean.TRUE.equals(machine.isLimitingResource())) ? _("yes") : _("no"))); Hbox hbox = new Hbox(); hbox.appendChild(Util.createEditButton(event -> goToEditForm(machine))); hbox.appendChild(Util.createRemoveButton(event -> confirmDelete(machine))); row.appendChild(hbox); }; } @Override protected String getEntityType() { return _("Machine"); } @Override protected String getPluralEntityType() { return _("Machines"); } @Override protected Machine getEntityBeingEdited() { return machineModel.getMachine(); } /** * Should be public! * Used in _listMachines.zul */ public boolean isCreateButtonDisabled() { Limits resourcesTypeLimit = limitsModel.getResourcesType(); if (isNullOrZeroValue(resourcesTypeLimit)) { return false; } else { Integer resources = resourceDAO.getRowCount().intValue(); return resources >= resourcesTypeLimit.getValue(); } } /** * Should be public! * Used in _listMachines.zul */ public String getShowCreateFormLabel(){ Limits resourcesTypeLimit = limitsModel.getResourcesType(); if (isNullOrZeroValue(resourcesTypeLimit)) { return _("Create"); } Integer resources = resourceDAO.getRowCount().intValue(); int resourcesLeft = resourcesTypeLimit.getValue() - resources; return resources >= resourcesTypeLimit.getValue() ? _("Machines limit reached") : _("Create") + " ( " + resourcesLeft + " " + _("left") + " )"; } private boolean isNullOrZeroValue (Limits resourcesTypeLimit) { return resourcesTypeLimit == null || resourcesTypeLimit.getValue() == null || resourcesTypeLimit.getValue().equals(0); } }