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