/*
* 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.workreports;
import static org.libreplan.web.I18nHelper._;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.users.entities.User;
import org.libreplan.business.users.entities.UserRole;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workreports.entities.WorkReport;
import org.libreplan.business.workreports.entities.WorkReportLine;
import org.libreplan.web.UserUtil;
import org.libreplan.web.common.IMessagesForUser;
import org.libreplan.web.common.Level;
import org.libreplan.web.common.MessagesForUser;
import org.libreplan.web.common.components.Autocomplete;
import org.libreplan.web.common.components.bandboxsearch.BandboxSearch;
import org.libreplan.web.security.SecurityUtils;
import org.libreplan.web.users.dashboard.IPersonalTimesheetController;
import org.zkoss.ganttz.IPredicate;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zkplus.spring.SpringUtil;
import org.zkoss.zul.Column;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Grid;
import org.zkoss.zul.Label;
import org.zkoss.zul.SimpleListModel;
import org.zkoss.zul.Window;
/**
* Controller for workReportQuery.zul. A kind of report to filter and look for the work report lines.
* <br />
* Previously this was part of {@link WorkReportCRUDController}, however it has been moved to a separate controller.
*
* @author Diego Pino García <dpino@igalia.com>
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
* @author Manuel Rego Casasnovas <rego@igalia.com>
*/
@SuppressWarnings("serial")
public class WorkReportQueryController extends GenericForwardComposer {
private IWorkReportModel workReportModel;
private Datebox filterStartDateLine;
private Datebox filterFinishDateLine;
private BandboxSearch bandboxFilterOrderElement;
private Combobox filterType;
private Autocomplete filterResource;
private Autocomplete filterHoursType;
private Set<IPredicate> predicates = new HashSet<>();
private List<WorkReportLine> filterWorkReportLines = new ArrayList<>();
private Grid gridListQuery;
private Label gridSummary;
private Window listQueryWindow;
private Component messagesContainer;
private IMessagesForUser messagesForUser;
private IWorkReportCRUDControllerEntryPoints workReportCRUD;
private IPersonalTimesheetController personalTimesheetController;
public WorkReportQueryController(){
workReportModel = (IWorkReportModel) SpringUtil.getBean("workReportModel");
workReportCRUD = (IWorkReportCRUDControllerEntryPoints) SpringUtil.getBean("workReportCRUD");
personalTimesheetController = (IPersonalTimesheetController) SpringUtil.getBean("personalTimesheetController");
}
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
comp.setAttribute("controller", this);
messagesForUser = new MessagesForUser(messagesContainer);
}
public List<OrderElement> getOrderElements() {
return workReportModel.getOrderElements();
}
public Constraint checkConstraintFinishDateLine() {
return new Constraint() {
@Override
public void validate(Component comp, Object value)
throws WrongValueException {
Date finishDateLine = (Date) value;
if ((finishDateLine != null) && (filterStartDateLine.getValue() != null) &&
(finishDateLine.compareTo(filterStartDateLine.getValue()) < 0)) {
filterFinishDateLine.setValue(null);
throw new WrongValueException(comp, _("must be after start date"));
}
}
};
}
public Constraint checkConstraintStartDateLine() {
return new Constraint() {
@Override
public void validate(Component comp, Object value)
throws WrongValueException {
Date startDateLine = (Date) value;
if ((startDateLine != null) && (filterFinishDateLine.getValue() != null) &&
(startDateLine.compareTo(filterFinishDateLine.getValue()) > 0)) {
filterStartDateLine.setValue(null);
throw new WrongValueException(comp, _("must be lower than end date"));
}
}
};
}
/**
* Apply filter on all workReportLines
*
* First, all workReportLines are retrieved. Then a series of predicates are
* created containing the conditions of the filter.
*
* In the case that there's no order filtered, the predicate filter contains
* the order of the retrieved workReportLines so it can apply the rest of
* the parameters of the filter
*
* @param event
*/
public void onApplyFilterWorkReportLines(Event event) {
OrderElement selectedOrder = getSelectedOrderElement();
List<WorkReportLine> workReportLines = workReportModel.getAllWorkReportLines();
if (selectedOrder == null) {
createPredicateLines(filterOrderElements(workReportLines));
} else {
createPredicateLines(Collections.singletonList(selectedOrder));
}
filterByPredicateLines();
updateSummary();
}
private void updateSummary() {
updateSummary(filterWorkReportLines);
}
private void updateSummary(List<WorkReportLine> workReportLines) {
WorkReportLineSummary summary = new WorkReportLineSummary(
totalTasks(workReportLines), totalHours(workReportLines));
gridSummary.setValue(summary.toString());
}
private Integer totalTasks(List<WorkReportLine> workReportLines) {
return workReportLines.size();
}
private EffortDuration totalHours(List<WorkReportLine> workReportLines) {
EffortDuration result = EffortDuration.zero();
for (WorkReportLine each : workReportLines) {
result = result.plus(each.getEffort());
}
return result;
}
private void filterByPredicateLines() {
filterWorkReportLines.clear();
for (IPredicate each : predicates) {
filterWorkReportLines.addAll(workReportModel
.getFilterWorkReportLines(each));
}
gridListQuery.setModel(new SimpleListModel<>(filterWorkReportLines.toArray()));
gridListQuery.invalidate();
}
private Collection<OrderElement> filterOrderElements(
List<WorkReportLine> workReportLines) {
Collection<OrderElement> result = new HashSet<>();
for (WorkReportLine each : workReportLines) {
result.add(each.getOrderElement());
}
return result;
}
private void createPredicateLines(Collection<OrderElement> orderElements) {
String type = filterType.getValue();
Resource resource = getSelectedResource();
Date startDate = filterStartDateLine.getValue();
Date finishDate = filterFinishDateLine.getValue();
TypeOfWorkHours hoursType = getSelectedHoursType();
predicates.clear();
for (OrderElement each : orderElements) {
predicates.addAll(createWRLPredicates(type, resource, startDate,
finishDate, each, hoursType));
}
}
private Collection<? extends WorkReportLinePredicate> createWRLPredicates(String type,
Resource resource,
Date startDate,
Date finishDate,
OrderElement orderElement,
TypeOfWorkHours hoursType) {
Set<WorkReportLinePredicate> result = new HashSet<>();
if (type.equals(_("All"))) {
result.add(new WorkReportLinePredicate(resource, startDate, finishDate, orderElement, hoursType));
if (orderElement != null) {
for (OrderElement each : orderElement.getChildren()) {
result.add(new WorkReportLinePredicate(resource, startDate, finishDate, each, hoursType));
}
}
} else if (type.equals(_("Direct"))) {
result.add(new WorkReportLinePredicate(resource, startDate, finishDate, orderElement, hoursType));
} else if (type.equals(_("Indirect")) && orderElement != null) {
for (OrderElement each : orderElement.getChildren()) {
result.add(new WorkReportLinePredicate(resource, startDate, finishDate, each, hoursType));
}
}
return result;
}
private Resource getSelectedResource() {
Comboitem itemSelected = filterResource.getSelectedItem();
if (itemSelected != null && itemSelected.getValue() != null) {
return (Resource) itemSelected.getValue();
}
return null;
}
private OrderElement getSelectedOrderElement() {
OrderElement orderElement = (OrderElement) this.bandboxFilterOrderElement.getSelectedElement();
if ((orderElement != null) && ((orderElement.getCode() != null) && (!orderElement.getCode().isEmpty()))) {
try {
return workReportModel.findOrderElement(orderElement.getCode());
} catch (InstanceNotFoundException e) {
throw new WrongValueException(bandboxFilterOrderElement, _("Task not found"));
}
}
return null;
}
private TypeOfWorkHours getSelectedHoursType() {
Comboitem itemSelected = filterHoursType.getSelectedItem();
return (itemSelected != null) && ((itemSelected.getValue()) != null)
? (TypeOfWorkHours) itemSelected.getValue()
: null;
}
/**
* Method to manage the query work report lines
*/
public List<WorkReportLine> getQueryWorkReportLines() {
List<WorkReportLine> result = workReportModel.getAllWorkReportLines();
updateSummary(result);
return result;
}
public void sortQueryWorkReportLines() {
Column columnDateLine = (Column) listQueryWindow.getFellow("date");
if (columnDateLine != null) {
if (columnDateLine.getSortDirection().equals("ascending")) {
columnDateLine.sort(false, false);
columnDateLine.setSortDirection("ascending");
} else if (columnDateLine.getSortDirection().equals("descending")) {
columnDateLine.sort(true, false);
columnDateLine.setSortDirection("descending");
}
}
}
/* Should be public! */
public void goToEditFormQuery(WorkReportLine line) {
WorkReport workReport = line.getWorkReport();
if (SecurityUtils.isSuperuserOrUserInRoles(UserRole.ROLE_TIMESHEETS)) {
workReportCRUD.goToEditForm(workReport);
} else if (SecurityUtils.isUserInRole(UserRole.ROLE_BOUND_USER) &&
workReportModel.isPersonalTimesheet(workReport) &&
belongsToCurrentUser(line)) {
personalTimesheetController.goToCreateOrEditForm(line.getLocalDate());
} else {
messagesForUser.showMessage(Level.WARNING, _("You do not have permissions to edit this timesheet"));
}
}
private boolean belongsToCurrentUser(WorkReportLine line) {
User user = UserUtil.getUserFromSession();
assert user != null;
return line.getResource().getId().equals(user.getWorker().getId());
}
/**
*
* @author Diego Pino García <dpino@igalia.com>
*
*/
private class WorkReportLineSummary {
private Integer totalTasks;
private EffortDuration totalHours;
private WorkReportLineSummary(Integer totalTasks, EffortDuration totalHours) {
this.totalTasks = totalTasks;
this.totalHours = totalHours;
}
String getTotalTasks() {
return totalTasks.toString();
}
public String getTotalHours() {
return totalHours.toFormattedString();
}
public String toString() {
return _("Tasks") + " " + getTotalTasks() + ". " + _("Total hours") + " " + getTotalHours() + ".";
}
}
}