org.libreplan.web.expensesheet.ExpenseSheetCRUDController.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.web.expensesheet.ExpenseSheetCRUDController.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2012 WirelessGalicia, 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.expensesheet;

import static org.libreplan.web.I18nHelper._;

import java.math.BigDecimal;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.LogFactory;
import org.joda.time.LocalDate;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.expensesheet.entities.ExpenseSheet;
import org.libreplan.business.expensesheet.entities.ExpenseSheetLine;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.users.entities.UserRole;
import org.libreplan.web.common.BaseCRUDController;
import org.libreplan.web.common.IndexController;
import org.libreplan.web.common.Level;
import org.libreplan.web.common.Util;
import org.libreplan.web.common.components.bandboxsearch.BandboxSearch;
import org.libreplan.web.common.entrypoints.IURLHandlerRegistry;
import org.libreplan.web.common.entrypoints.MatrixParameters;
import org.libreplan.web.security.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Button;
import org.zkoss.zul.Constraint;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Decimalbox;
import org.zkoss.zul.Grid;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Row;
import org.zkoss.zul.RowRenderer;
import org.zkoss.zul.Textbox;

/**
 * Controller for CRUD actions over a {@link ExpenseSheet}
 *
 * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
 */
public class ExpenseSheetCRUDController extends BaseCRUDController<ExpenseSheet>
        implements IExpenseSheetCRUDController {

    private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(ExpenseSheetCRUDController.class);

    @Autowired
    private IExpenseSheetModel expenseSheetModel;

    /*
     * components editWindow
     */

    private Datebox dateboxExpenseDate;

    private Grid gridExpenseLines;

    private BandboxSearch bandboxTasks;

    private Decimalbox dboxValue;

    private BandboxSearch bandboxResource;

    private Textbox tbConcept;

    private ExpenseSheetLineRenderer expenseSheetLineRenderer = new ExpenseSheetLineRenderer();

    private IURLHandlerRegistry URLHandlerRegistry;

    private boolean fromUserDashboard = false;

    private boolean cancel = false;

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);
        checkUserHasProperRoleOrSendForbiddenCode();
        URLHandlerRegistry.getRedirectorFor(IExpenseSheetCRUDController.class).register(this, page);
    }

    private void checkUserHasProperRoleOrSendForbiddenCode() {
        HttpServletRequest request = (HttpServletRequest) Executions.getCurrent().getNativeRequest();
        Map<String, String> matrixParams = MatrixParameters.extract(request);

        // If it doesn't come from a entry point
        if (matrixParams.isEmpty()) {
            if (!SecurityUtils.isSuperuserOrUserInRoles(UserRole.ROLE_EXPENSES)) {
                Util.sendForbiddenStatusCodeInHttpServletResponse();
            }
        }
    }

    @Override
    public void save() throws ValidationException {
        expenseSheetModel.confirmSave();
    }

    @Override
    protected void beforeSaving() throws ValidationException {
        super.beforeSaving();
        expenseSheetModel.generateExpenseSheetLineCodesIfIsNecessary();
    }

    private void loadComponentsEditWindow() {
        tbConcept = (Textbox) editWindow.getFellowIfAny("tbConcept");
        dateboxExpenseDate = (Datebox) editWindow.getFellowIfAny("dateboxExpenseDate");
        dboxValue = (Decimalbox) editWindow.getFellowIfAny("dboxValue");
        gridExpenseLines = (Grid) editWindow.getFellowIfAny("gridExpenseLines");
        bandboxResource = (BandboxSearch) editWindow.getFellowIfAny("bandboxResource");
        bandboxTasks = (BandboxSearch) editWindow.getFellowIfAny("bandboxTasks");
    }

    /*
     * Operations in the list window
     */

    public List<ExpenseSheet> getExpenseSheets() {
        return expenseSheetModel.getExpenseSheets();
    }

    /*
     * Operations in the create window
     */

    public ExpenseSheet getExpenseSheet() {
        return expenseSheetModel.getExpenseSheet();
    }

    public SortedSet<ExpenseSheetLine> getExpenseSheetLines() {
        return expenseSheetModel.getExpenseSheetLines();
    }

    /**
     * Adds a new {@link ExpenseSheetLine} to the list of rows
     *
     * @param rows
     */
    public void addExpenseSheetLine() {
        if (validateNewLine()) {
            expenseSheetModel.addExpenseSheetLine();
            reloadExpenseSheetLines();
            reloadComponentsNewLine();
        }
    }

    private void reloadComponentsNewLine() {
        Util.reloadBindings(bandboxTasks);
        Util.reloadBindings(bandboxResource);
        Util.reloadBindings(tbConcept);
        Util.reloadBindings(dboxValue);
    }

    private boolean validateNewLine() {
        boolean result = true;
        if (expenseSheetModel.getNewExpenseSheetLine().getDate() == null) {
            result = false;
            throw new WrongValueException(this.dateboxExpenseDate, _("cannot be empty"));
        }
        if (expenseSheetModel.getNewExpenseSheetLine().getOrderElement() == null) {
            result = false;
            throw new WrongValueException(this.bandboxTasks, _("cannot be empty"));
        }
        BigDecimal value = expenseSheetModel.getNewExpenseSheetLine().getValue();
        if (value == null || value.compareTo(BigDecimal.ZERO) < 0) {
            result = false;
            throw new WrongValueException(this.dboxValue, _("cannot be empty or less than zero"));
        }
        return result;
    }

    public void confirmRemove(ExpenseSheetLine expenseSheetLine) {
        try {
            int status = Messagebox.show(
                    _("Confirm deleting {0}. Are you sure?", getExpenseSheetLineName(expenseSheetLine)),
                    _("Delete"), Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION);
            if (Messagebox.OK == status) {
                removeExpenseSheetLine(expenseSheetLine);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void removeExpenseSheetLine(ExpenseSheetLine expenseSheetLine) {
        expenseSheetModel.removeExpenseSheetLine(expenseSheetLine);
        reloadExpenseSheetLines();
    }

    private String getExpenseSheetLineName(ExpenseSheetLine expenseSheetLine) {
        if (expenseSheetLine != null) {
            LocalDate date = expenseSheetLine.getDate();
            OrderElement task = expenseSheetLine.getOrderElement();
            if (date != null && task != null) {
                return _("expense line of the ") + task.getName() + " - " + date;
            }
        }
        return _("item");
    }

    private void reloadExpenseSheetLines() {
        if (gridExpenseLines != null) {
            Util.reloadBindings(gridExpenseLines);
        }
    }

    public void onCheckGenerateCode(Event e) {
        CheckEvent ce = (CheckEvent) e;
        if (ce.isChecked()) {
            // we have to auto-generate the code for new objects
            try {
                expenseSheetModel.setCodeAutogenerated(ce.isChecked());
            } catch (ConcurrentModificationException err) {
                messagesForUser.showMessage(Level.ERROR, err.getMessage());
            }
        }
        Util.reloadBindings(editWindow);
        reloadExpenseSheetLines();
    }

    public ExpenseSheetLine getNewExpenseSheetLine() {
        return expenseSheetModel.getNewExpenseSheetLine();
    }

    public List<Order> getOrders() {
        return expenseSheetModel.getOrders();
    }

    public List<OrderElement> getTasks() {
        return expenseSheetModel.getTasks();
    }

    public void setProject(Order project) {
        expenseSheetModel.setSelectedProject(project);
        Util.reloadBindings(bandboxTasks);
    }

    public Order getProject() {
        return expenseSheetModel.getSelectedProject();
    }

    public ExpenseSheetLineRenderer getExpenseSheetLineRenderer() {
        return expenseSheetLineRenderer;
    }

    /**
     * RowRenderer for a @{ExpenseSheetLine} element
     * @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
     */
    public class ExpenseSheetLineRenderer implements RowRenderer {

        @Override
        public void render(Row row, Object data) {
            ExpenseSheetLine expenseSheetLine = (ExpenseSheetLine) data;
            row.setValue(expenseSheetLine);

            appendOrderElementInLines(row);
            appendValueInLines(row);
            appendConceptInLines(row);
            appendDateInLines(row);
            appendResourceInLines(row);
            appendCode(row);
            appendDeleteButton(row);
        }

        private void appendConceptInLines(Row row) {
            final ExpenseSheetLine expenseSheetLine = (ExpenseSheetLine) row.getValue();
            final Textbox txtConcept = new Textbox();
            txtConcept.setWidth("160px");
            Util.bind(txtConcept, new Util.Getter<String>() {

                @Override
                public String get() {
                    if (expenseSheetLine != null) {
                        return expenseSheetLine.getConcept();
                    }
                    return "";
                }

            }, new Util.Setter<String>() {

                @Override
                public void set(String value) {
                    if (expenseSheetLine != null) {
                        expenseSheetLine.setConcept(value);
                    }
                }
            });
            row.appendChild(txtConcept);
        }

        private void appendDateInLines(Row row) {
            final ExpenseSheetLine expenseSheetLine = (ExpenseSheetLine) row.getValue();
            final Datebox dateboxExpense = new Datebox();
            Util.bind(dateboxExpense, new Util.Getter<Date>() {

                @Override
                public Date get() {
                    if (expenseSheetLine != null) {
                        if (expenseSheetLine.getDate() != null) {
                            return expenseSheetLine.getDate().toDateTimeAtStartOfDay().toDate();
                        }
                    }
                    return null;
                }

            }, new Util.Setter<Date>() {

                @Override
                public void set(Date value) {
                    if (expenseSheetLine != null) {
                        LocalDate newDate = null;
                        if (value != null) {
                            newDate = LocalDate.fromDateFields(value);
                        }
                        expenseSheetModel.keepSortedExpenseSheetLines(expenseSheetLine, newDate);
                        reloadExpenseSheetLines();
                    }
                }
            });

            dateboxExpense.setConstraint("no empty:" + _("cannot be empty"));
            row.appendChild(dateboxExpense);
        }

        private void appendResourceInLines(Row row) {
            final ExpenseSheetLine expenseSheetLine = (ExpenseSheetLine) row.getValue();
            final BandboxSearch bandboxSearch = BandboxSearch.create("ResourceInExpenseSheetBandboxFinder");

            bandboxSearch.setSelectedElement(expenseSheetLine.getResource());
            bandboxSearch.setSclass("bandbox-workreport-task");
            bandboxSearch.setListboxWidth("450px");

            EventListener eventListenerUpdateResource = new EventListener() {
                @Override
                public void onEvent(Event event) {
                    Listitem selectedItem = bandboxSearch.getSelectedItem();
                    setResourceInESL(selectedItem, expenseSheetLine);
                }
            };
            bandboxSearch.setListboxEventListener(Events.ON_SELECT, eventListenerUpdateResource);
            bandboxSearch.setListboxEventListener(Events.ON_OK, eventListenerUpdateResource);
            bandboxSearch.setBandboxEventListener(Events.ON_CHANGING, eventListenerUpdateResource);
            row.appendChild(bandboxSearch);
        }

        private void appendCode(final Row row) {
            final ExpenseSheetLine line = (ExpenseSheetLine) row.getValue();
            final Textbox code = new Textbox();
            code.setWidth("170px");
            code.setDisabled(getExpenseSheet().isCodeAutogenerated());
            code.applyProperties();

            if (line.getCode() != null) {
                code.setValue(line.getCode());
            }

            code.addEventListener("onChange", new EventListener() {
                @Override
                public void onEvent(Event event) {
                    final ExpenseSheetLine line = (ExpenseSheetLine) row.getValue();
                    line.setCode(code.getValue());
                }
            });
            code.setConstraint(checkConstraintLineCodes(line));
            row.appendChild(code);
        }

        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, new EventListener() {
                @Override
                public void onEvent(Event event) {
                    confirmRemove((ExpenseSheetLine) row.getValue());
                }
            });
            row.appendChild(delete);
        }

        private void appendValueInLines(Row row) {
            final ExpenseSheetLine expenseSheetLine = (ExpenseSheetLine) row.getValue();
            final Decimalbox dbValue = new Decimalbox();
            dbValue.setScale(2);

            Util.bind(dbValue, new Util.Getter<BigDecimal>() {

                @Override
                public BigDecimal get() {
                    if (expenseSheetLine != null) {
                        return expenseSheetLine.getValue();
                    }
                    return BigDecimal.ZERO.setScale(2);
                }

            }, new Util.Setter<BigDecimal>() {

                @Override
                public void set(BigDecimal value) {
                    if (expenseSheetLine != null) {
                        expenseSheetLine.setValue(value);
                    }
                }
            });

            dbValue.setConstraint(checkConstraintExpenseValue());
            dbValue.setFormat(Util.getMoneyFormat());
            row.appendChild(dbValue);
        }

        /**
         * Append a Bandbox @{link OrderElement} to row
         *
         * @param row
         */
        private void appendOrderElementInLines(Row row) {
            final ExpenseSheetLine expenseSheetLine = (ExpenseSheetLine) row.getValue();
            final BandboxSearch bandboxSearch = BandboxSearch.create("OrderElementInExpenseSheetBandboxFinder");

            bandboxSearch.setSelectedElement(expenseSheetLine.getOrderElement());
            bandboxSearch.setSclass("bandbox-workreport-task");
            bandboxSearch.setListboxWidth("450px");

            EventListener eventListenerUpdateOrderElement = new EventListener() {
                @Override
                public void onEvent(Event event) {
                    Listitem selectedItem = bandboxSearch.getSelectedItem();
                    setOrderElementInESL(selectedItem, expenseSheetLine);
                }
            };
            bandboxSearch.setListboxEventListener(Events.ON_SELECT, eventListenerUpdateOrderElement);
            bandboxSearch.setListboxEventListener(Events.ON_OK, eventListenerUpdateOrderElement);
            bandboxSearch.setBandboxEventListener(Events.ON_CHANGING, eventListenerUpdateOrderElement);
            bandboxSearch.setBandboxConstraint("no empty:" + _("cannot be empty"));
            row.appendChild(bandboxSearch);
        }

        private void setOrderElementInESL(Listitem selectedItem, ExpenseSheetLine line) {
            OrderElement orderElement = (selectedItem == null ? null : (OrderElement) selectedItem.getValue());
            line.setOrderElement(orderElement);
        }

        private void setResourceInESL(Listitem selectedItem, ExpenseSheetLine expenseSheetLine) {
            Resource resource = (selectedItem == null ? null : (Resource) selectedItem.getValue());
            expenseSheetLine.setResource(resource);
        }
    }

    public Constraint checkConstraintExpenseValue() {
        return new Constraint() {
            @Override
            public void validate(Component comp, Object value) throws WrongValueException {
                BigDecimal expenseValue = (BigDecimal) value;
                if (expenseValue == null || expenseValue.compareTo(BigDecimal.ZERO) < 0) {
                    throw new WrongValueException(comp, _("must be greater or equal than 0"));
                }
            }
        };
    }

    public Constraint checkConstraintLineCodes(final ExpenseSheetLine line) {
        return new Constraint() {
            @Override
            public void validate(Component comp, Object value) throws WrongValueException {
                if (!getExpenseSheet().isCodeAutogenerated()) {
                    String code = (String) value;
                    if (code == null || code.isEmpty()) {
                        throw new WrongValueException(comp, _("Code cannot be empty."));
                    } else {
                        String oldCode = line.getCode();
                        line.setCode(code);
                        if (!getExpenseSheet().isNonRepeatedExpenseSheetLinesCodesConstraint()) {
                            line.setCode(oldCode);
                            throw new WrongValueException(comp, _("The code must be unique."));
                        }
                    }
                }
            }
        };
    }

    public Constraint checkConstraintExpendeCode() {
        return new Constraint() {
            @Override
            public void validate(Component comp, Object value) throws WrongValueException {
                if (!getExpenseSheet().isCodeAutogenerated()) {
                    String code = (String) value;
                    if (code == null || code.isEmpty()) {
                        throw new WrongValueException(comp, _("The code cannot be empty and it must be unique."));
                    } else if (!getExpenseSheet().isUniqueCodeConstraint()) {
                        throw new WrongValueException(comp,
                                _("it already exists another expense sheet with the same code."));
                    }
                }
            }
        };
    }

    public Date getExpenseSheetLineDate() {
        if (expenseSheetModel.getNewExpenseSheetLine() != null) {
            return (expenseSheetModel.getNewExpenseSheetLine().getDate() != null)
                    ? expenseSheetModel.getNewExpenseSheetLine().getDate().toDateTimeAtStartOfDay().toDate()
                    : null;
        }
        return null;
    }

    public void setExpenseSheetLineDate(Date date) {
        if (expenseSheetModel.getNewExpenseSheetLine() != null) {
            LocalDate localDate = null;
            if (date != null) {
                localDate = LocalDate.fromDateFields(date);
            }
            expenseSheetModel.getNewExpenseSheetLine().setDate(localDate);
        }
    }

    @Override
    protected void initCreate() {
        initCreate(false);
    }

    @Override
    protected void initEdit(ExpenseSheet expenseSheet) {
        expenseSheetModel.prepareToEdit(expenseSheet);
        loadComponentsEditWindow();
    }

    @Override
    protected ExpenseSheet getEntityBeingEdited() {
        return expenseSheetModel.getExpenseSheet();
    }

    @Override
    public void delete(ExpenseSheet expenseSheet) throws InstanceNotFoundException {
        expenseSheetModel.removeExpenseSheet(expenseSheet);
    }

    @Override
    protected String getEntityType() {
        return _("Expense Sheet");
    }

    @Override
    protected String getPluralEntityType() {
        return _("Expense Sheets");
    }

    public String getCurrencySymbol() {
        return Util.getCurrencySymbol();
    }

    public String getMoneyFormat() {
        return Util.getMoneyFormat();
    }

    @Override
    public void goToCreatePersonalExpenseSheet() {
        if (!SecurityUtils.isUserInRole(UserRole.ROLE_BOUND_USER)) {
            Util.sendForbiddenStatusCodeInHttpServletResponse();
        }

        state = CRUDControllerState.CREATE;
        initCreate(true);
        showEditWindow();
        fromUserDashboard = true;
    }

    private void initCreate(boolean personal) {
        expenseSheetModel.initCreate(personal);
        loadComponentsEditWindow();
    }

    public String getResource() {
        Resource resource = expenseSheetModel.getResource();
        return resource == null ? "" : resource.getShortDescription();
    }

    @Override
    public void goToEditPersonalExpenseSheet(ExpenseSheet expenseSheet) {
        if (!SecurityUtils.isUserInRole(UserRole.ROLE_BOUND_USER)
                || !expenseSheetModel.isPersonalAndBelognsToCurrentUser(expenseSheet)) {
            Util.sendForbiddenStatusCodeInHttpServletResponse();
        }
        goToEditForm(expenseSheet);
        fromUserDashboard = true;
    }

    @Override
    protected void showListWindow() {
        if (fromUserDashboard) {
            String url = IndexController.USER_DASHBOARD_URL;
            if (!cancel) {
                url += "?expense_sheet_saved=" + expenseSheetModel.getExpenseSheet().getCode();
            }
            Executions.getCurrent().sendRedirect(url);
        } else {
            super.showListWindow();
        }
    }

    @Override
    protected void cancel() {
        cancel = true;
    }

    public String getType() {
        return getType(expenseSheetModel.getExpenseSheet());
    }

    private String getType(ExpenseSheet expenseSheet) {
        if (expenseSheet != null && expenseSheet.isPersonal()) {
            return _("Personal");
        }
        return _("Regular");
    }

    public RowRenderer getExpenseSheetsRenderer() {
        return new RowRenderer() {

            @Override
            public void render(Row row, Object data) throws Exception {
                final ExpenseSheet expenseSheet = (ExpenseSheet) data;
                row.setValue(expenseSheet);

                Util.appendLabel(row, expenseSheet.getFirstExpense().toString());
                Util.appendLabel(row, expenseSheet.getLastExpense().toString());
                Util.appendLabel(row, Util.addCurrencySymbol(expenseSheet.getTotal()));
                Util.appendLabel(row, expenseSheet.getCode());
                Util.appendLabel(row, expenseSheet.getDescription());
                Util.appendLabel(row, getType(expenseSheet));

                Util.appendOperationsAndOnClickEvent(row, new EventListener() {
                    @Override
                    public void onEvent(Event event) throws Exception {
                        goToEditForm(expenseSheet);
                    }
                }, new EventListener() {
                    @Override
                    public void onEvent(Event event) throws Exception {
                        confirmDelete(expenseSheet);
                    }
                });

            }
        };
    }

}