com.haulmont.timesheets.gui.approve.ApproveScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.timesheets.gui.approve.ApproveScreen.java

Source

/*
 * Copyright (c) 2016 Haulmont
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.haulmont.timesheets.gui.approve;

import com.haulmont.bali.util.ParamsMap;
import com.haulmont.cuba.core.global.CommitContext;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.global.TimeSource;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.actions.RefreshAction;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.CollectionDatasource.Operation;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import com.haulmont.cuba.security.entity.User;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.timesheets.entity.*;
import com.haulmont.timesheets.global.DateTimeUtils;
import com.haulmont.timesheets.global.StringFormatHelper;
import com.haulmont.timesheets.global.ValidationTools;
import com.haulmont.timesheets.global.WeeklyReportConverter;
import com.haulmont.timesheets.gui.rejection.RejectionReason;
import com.haulmont.timesheets.gui.timeentry.TimeEntryEdit;
import com.haulmont.timesheets.gui.util.ComponentsHelper;
import com.haulmont.timesheets.gui.util.SecurityAssistant;
import com.haulmont.timesheets.gui.util.WeeklyReportEntryAggregation;
import com.haulmont.timesheets.gui.weeklytimesheets.TotalColumnAggregation;
import com.haulmont.timesheets.service.ProjectsService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;

import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.*;
import java.util.Calendar;

/**
 * @author gorelov
 */
@SuppressWarnings("WeakerAccess")
public class ApproveScreen extends AbstractWindow {

    protected static final String COLUMN_SUFFIX = "Column";
    protected static final String TOTAL_COLUMN_ID = "totalColumn";

    public interface Companion {
        void initTable(Table table);
    }

    @Inject
    private CheckBox hasTimeEntries;
    @Inject
    protected Table<ExtUser> usersTable;
    @Inject
    protected Table<WeeklyReportEntry> weeklyReportsTable;
    @Inject
    protected DateField dateField;
    @Inject
    protected Label weekCaption;
    @Inject
    protected OptionsGroup statusOption;
    @Inject
    protected OptionsGroup typeOption;
    @Inject
    protected ComponentsFactory componentsFactory;
    @Inject
    protected CollectionDatasource<ExtUser, UUID> usersDs;
    @Inject
    protected CollectionDatasource<WeeklyReportEntry, UUID> weeklyEntriesDs;
    @Inject
    protected Messages messages;
    @Inject
    protected UserSession userSession;
    @Inject
    protected ProjectsService projectsService;
    @Inject
    protected TimeSource timeSource;
    @Inject
    protected WeeklyReportConverter reportConverterBean;
    @Inject
    protected Companion companion;
    @Inject
    protected ValidationTools validationTools;
    @Inject
    protected SecurityAssistant securityAssistant;

    @Inject
    private LookupField user;

    @Inject
    private LookupField status;

    @Inject
    private Button refresh;

    @Inject
    private LookupField project;

    @Inject
    private LookupField task;

    protected Map<String, Label> totalLabelsMap = new HashMap<>();

    protected Date firstDayOfWeek;
    protected Date lastDayOfWeek;
    protected List<Project> managedProjects;
    protected List<User> managedUsers;

    @Override
    public void init(Map<String, Object> params) {
        if (companion != null) {
            companion.initTable(weeklyReportsTable);
        }

        setWeekRange(DateTimeUtils.getFirstDayOfWeek(DateUtils.addWeeks(timeSource.currentTimestamp(), -1)));
        User currentOrSubstitutedUser = userSession.getCurrentOrSubstitutedUser();
        managedProjects = projectsService.getActiveManagedProjectsForUser(currentOrSubstitutedUser, View.LOCAL);
        managedUsers = projectsService.getManagedUsers(currentOrSubstitutedUser, View.LOCAL);

        initUsersTable();
        initUserReportsTable();
        initDateField();
        initStatusOption();
        initTypeOptions();

        updateWeek();

        project.addValueChangeListener(e -> task.setValue(null));
        status.setOptionsList(Arrays.asList(TimeEntryStatus.values()));
        refresh.setAction(new RefreshAction(usersTable));
        user.setOptionsList(
                projectsService.getManagedUsers(userSession.getCurrentOrSubstitutedUser(), View.MINIMAL));
    }

    protected void initUsersTable() {
        final String actionsColumnId = "actions";
        usersTable.addGeneratedColumn(actionsColumnId, entity -> {
            UserChangeStatusActionsProvider provider = new UserChangeStatusActionsProvider(entity);
            return getApproveControls(provider.getApproveAction(), provider.getRejectAction(),
                    provider.getCloseAction());
        });
        usersTable.setColumnWidth(actionsColumnId, 100);
        usersTable.setColumnCaption(actionsColumnId, messages.getMessage(getClass(), actionsColumnId));

        usersDs.addItemChangeListener(e -> {
            updateReportTableItems();
            updateStatusOption(e.getItem());
        });

        hasTimeEntries.setValue(true);
        hasTimeEntries.addValueChangeListener(e -> updateUsersTable());

    }

    protected void initUserReportsTable() {
        weeklyReportsTable.setSettingsEnabled(false);

        initProjectColumn();
        initTaskColumn();
        initDaysColumns();
        initTotalColumn();
        initActionsColumn();

        weeklyEntriesDs.addCollectionChangeListener(e -> {
            if (Operation.REMOVE.equals(e.getOperation()) || Operation.CLEAR.equals(e.getOperation())) {
                for (WeeklyReportEntry entry : e.getItems()) {
                    totalLabelsMap.remove(ComponentsHelper.getCacheKeyForEntity(entry, TOTAL_COLUMN_ID));
                }
            }
        });

        weeklyReportsTable.setStyleProvider((entity, property) -> {
            String id = null;
            if (property != null && property.endsWith(COLUMN_SUFFIX)) {
                id = property.replace(COLUMN_SUFFIX, "");
            }
            DayOfWeek day = DayOfWeek.fromId(id != null ? id : property);
            if (entity == null && usersTable.getSingleSelected() != null) {
                if (day != null) {
                    return validationTools.isWorkTimeMatchToPlanForDay(
                            DateTimeUtils.getSpecificDayOfWeek(firstDayOfWeek, day.getJavaCalendarDay()),
                            usersTable.<User>getSingleSelected()) ? null : "overtime";
                } else if (TOTAL_COLUMN_ID.equals(property)) {
                    return validationTools.isWorkTimeMatchToPlanForWeek(firstDayOfWeek,
                            usersTable.<User>getSingleSelected()) ? null : "overtime";
                }
            } else if (entity != null && day != null) {
                List<TimeEntry> timeEntries = entity.getDayOfWeekTimeEntries(day);
                if (CollectionUtils.isNotEmpty(timeEntries)) {
                    return ComponentsHelper.getTimeEntryStatusStyleBg(timeEntries);
                }
            }
            return null;
        });
    }

    protected void initProjectColumn() {
        final String projectColumnId = "project";
        weeklyReportsTable.addGeneratedColumn(projectColumnId, entity -> {
            Label label = componentsFactory.createComponent(Label.class);
            label.setValue(entity.getProject().getName());
            return label;
        });
    }

    protected void initTaskColumn() {
        final String taskColumnId = "task";
        weeklyReportsTable.addGeneratedColumn(taskColumnId, entity -> {
            Label label = componentsFactory.createComponent(Label.class);
            String caption;
            if (entity.getActivityType() == null) {
                caption = entity.getTask().getName();
            } else {
                caption = String.format("%s (%s)", entity.getTask().getName(),
                        entity.getActivityType().getInstanceName());
            }
            label.setValue(caption);
            return label;
        });
    }

    protected void initDaysColumns() {
        for (Date current = firstDayOfWeek; current.getTime() <= lastDayOfWeek.getTime(); current = DateUtils
                .addDays(current, 1)) {
            final DayOfWeek day = DayOfWeek
                    .fromCalendarDay(DateUtils.toCalendar(current).get(Calendar.DAY_OF_WEEK));
            final String columnId = day.getId() + COLUMN_SUFFIX;
            weeklyReportsTable.addGeneratedColumn(columnId, new Table.ColumnGenerator<WeeklyReportEntry>() {
                @Override
                public Component generateCell(final WeeklyReportEntry entity) {
                    List<TimeEntry> timeEntries = entity.getDayOfWeekTimeEntries(day);
                    if (CollectionUtils.isNotEmpty(timeEntries)) {
                        if (timeEntries.size() == 1) {
                            return createLinkToSingleTimeEntry(entity, timeEntries);
                        } else {
                            return createLinkToMultipleTimeEntries(entity, timeEntries.get(0).getDate());
                        }
                    }
                    return null;
                }

                private Component createLinkToMultipleTimeEntries(final WeeklyReportEntry reportEntry,
                        final Date date) {
                    final LinkButton linkButton = componentsFactory.createComponent(LinkButton.class);
                    linkButton.setCaption(StringFormatHelper.getDayHoursString(reportEntry.getTotalForDay(day)));
                    linkButton.setAction(new AbstractAction("edit") {

                        @Override
                        public void actionPerform(Component component) {
                            User user = usersTable.getSingleSelected();
                            if (user != null) {
                                openLookup("ts$TimeEntry.lookup", items -> {
                                    if (CollectionUtils.isNotEmpty(items)) {
                                        TimeEntry timeEntry = (TimeEntry) items.iterator().next();
                                        openTimeEntryEditor(timeEntry);
                                    }
                                }, WindowManager.OpenType.DIALOG,
                                        ParamsMap.of("date", date, "task", reportEntry.getTask(), "activityType",
                                                reportEntry.getActivityType(), "user", user.getId()));
                            }
                        }
                    });
                    return linkButton;
                }

                private Component createLinkToSingleTimeEntry(WeeklyReportEntry reportEntry,
                        List<TimeEntry> timeEntries) {
                    final TimeEntry timeEntry = timeEntries.get(0);
                    final LinkButton linkButton = componentsFactory.createComponent(LinkButton.class);
                    linkButton.setCaption(StringFormatHelper.getDayHoursString(reportEntry.getTotalForDay(day)));
                    linkButton.setAction(new AbstractAction("edit") {
                        @Override
                        public void actionPerform(Component component) {
                            openTimeEntryEditor(timeEntry);
                        }
                    });
                    return linkButton;
                }
            });
            weeklyReportsTable.setColumnWidth(columnId, 80);

            Table.Column column = weeklyReportsTable.getColumn(columnId);
            column.setAggregation(ComponentsHelper.createAggregationInfo(
                    projectsService.getEntityMetaPropertyPath(WeeklyReportEntry.class, day.getId()),
                    new WeeklyReportEntryAggregation()));
        }
    }

    protected void initTotalColumn() {
        weeklyReportsTable.addGeneratedColumn(TOTAL_COLUMN_ID, entity -> {
            Label label = componentsFactory.createComponent(Label.class);
            label.setValue(entity.getTotal());
            totalLabelsMap.put(ComponentsHelper.getCacheKeyForEntity(entity, TOTAL_COLUMN_ID), label);
            return label;
        });
        weeklyReportsTable.setColumnWidth(TOTAL_COLUMN_ID, 80);
        weeklyReportsTable.setColumnCaption(TOTAL_COLUMN_ID, messages.getMessage(getClass(), "total"));

        Table.Column column = weeklyReportsTable.getColumn(TOTAL_COLUMN_ID);
        column.setAggregation(ComponentsHelper.createAggregationInfo(
                projectsService.getEntityMetaPropertyPath(WeeklyReportEntry.class, "total"),
                new TotalColumnAggregation()));
    }

    protected void initActionsColumn() {
        final String actionsColumnId = "actions";
        weeklyReportsTable.addGeneratedColumn(actionsColumnId, entity -> {
            User user1 = usersTable.getSingleSelected();
            WeeklyReportChangeStatusActionProvider provider = new WeeklyReportChangeStatusActionProvider(user1,
                    entity);
            return getApproveControls(provider.getApproveAction(), provider.getRejectAction(),
                    provider.getCloseAction());
        });
        weeklyReportsTable.setColumnWidth(actionsColumnId, 100);
        weeklyReportsTable.setColumnCaption(actionsColumnId, messages.getMessage(getClass(), actionsColumnId));
    }

    protected void openTimeEntryEditor(TimeEntry timeEntry) {
        final TimeEntryEdit editor = (TimeEntryEdit) openEditor("ts$TimeEntry.edit", timeEntry,
                WindowManager.OpenType.DIALOG);
        editor.addListener(actionId -> {
            if (COMMIT_ACTION_ID.equals(actionId)) {
                updateReportTableItems();
            }
        });
    }

    protected Component getApproveControls(@Nullable Action approveAction, @Nullable Action rejectAction,
            @Nullable Action closeAction) {
        HBoxLayout hBoxLayout = componentsFactory.createComponent(HBoxLayout.class);
        hBoxLayout.setSpacing(true);
        hBoxLayout.setWidth("100%");

        if (approveAction != null) {
            hBoxLayout.add(ComponentsHelper.createCaptionlessLinkButton("icons/ok.png",
                    messages.getMessage(getClass(), AbstractChangeStatusAction.APPROVE_ACTION_ID), approveAction));
        }
        if (rejectAction != null) {
            hBoxLayout.add(ComponentsHelper.createCaptionlessLinkButton("icons/remove.png",
                    messages.getMessage(getClass(), AbstractChangeStatusAction.REJECT_ACTION_ID), rejectAction));
        }
        if (closeAction != null) {
            hBoxLayout.add(ComponentsHelper.createCaptionlessLinkButton("font-icon:LOCK",
                    messages.getMessage(getClass(), AbstractChangeStatusAction.CLOSE_ACTION_ID), closeAction));
        }

        return hBoxLayout;
    }

    protected void initDateField() {
        dateField.addValueChangeListener(e -> {
            setWeekRange(DateTimeUtils.getFirstDayOfWeek((Date) e.getValue()));
            updateWeek();
        });
    }

    protected void initStatusOption() {
        statusOption.setOptionsList(Arrays.asList(TimeEntryStatus.values()));
        statusOption.addValueChangeListener(e -> updateReportTableItems());
    }

    protected void initTypeOptions() {
        String all = messages.getMessage(getClass(), "all");
        typeOption.setOptionsList(Arrays.asList(messages.getMessage(getClass(), "approvable"), all));
        typeOption.setValue(all);

        typeOption.addValueChangeListener(e -> updateReportTableItems());
    }

    protected void setWeekRange(Date start) {
        firstDayOfWeek = start;
        lastDayOfWeek = DateTimeUtils.getLastDayOfWeek(firstDayOfWeek);
    }

    public void setToday() {
        setWeekRange(DateTimeUtils.getFirstDayOfWeek(timeSource.currentTimestamp()));
        updateWeek();
    }

    public void showPreviousWeek() {
        setWeekRange(DateUtils.addWeeks(firstDayOfWeek, -1));
        updateWeek();
    }

    public void showNextWeek() {
        setWeekRange(DateUtils.addWeeks(firstDayOfWeek, 1));
        updateWeek();
    }

    protected void updateWeek() {
        updateWeekCaption();
        updateUsersTable();
        updateReportTableItems();
        updateDayColumnsCaptions();
    }

    protected void updateUsersTable() {
        Map<String, Object> params = new HashMap<>();
        params.put("from", firstDayOfWeek);
        params.put("to", lastDayOfWeek);
        params.put("hasTimeEntries", Boolean.TRUE.equals(hasTimeEntries.getValue()));
        usersDs.refresh(params);
    }

    protected void updateReportTableItems() {
        weeklyEntriesDs.clear();
        User user = usersTable.getSingleSelected();
        if (user != null) {
            fillExistingTimeEntries(user);
            weeklyReportsTable.repaint();
        }
    }

    protected void updateWeekCaption() {
        weekCaption.setValue(String.format("%s - %s", DateTimeUtils.getDateFormat().format(firstDayOfWeek),
                DateTimeUtils.getDateFormat().format(lastDayOfWeek)));
    }

    protected void updateDayColumnsCaptions() {
        for (Date current = firstDayOfWeek; current.getTime() <= lastDayOfWeek.getTime(); current = DateUtils
                .addDays(current, 1)) {
            DayOfWeek day = DayOfWeek.fromCalendarDay(DateTimeUtils.getCalendarDayOfWeek(current));
            String columnId = day.getId() + COLUMN_SUFFIX;
            weeklyReportsTable.setColumnCaption(columnId, ComponentsHelper.getColumnCaption(day.getId(), current));
        }
    }

    protected void updateStatusOption(User user) {
        List<TimeEntryStatus> values = new ArrayList<>(statusOption.getValue());
        if (securityAssistant.isSuperUser() || managedUsers.contains(user)) {
            if (!values.contains(TimeEntryStatus.NEW)) {
                values.add(TimeEntryStatus.NEW);
            }
        }
        if (isUserCanClose()) {
            if (!values.contains(TimeEntryStatus.APPROVED)) {
                values.add(TimeEntryStatus.APPROVED);
            }
        }
        statusOption.setValue(values);
    }

    protected boolean showApprovable() {
        return StringUtils.equals(messages.getMessage(getClass(), "approvable"), typeOption.getValue());
    }

    protected void fillExistingTimeEntries(User user) {
        List<TimeEntry> timeEntries = new TimeEntriesProvider(user).setApprovable(showApprovable())
                .getUserTimeEntries();
        List<WeeklyReportEntry> reportEntries = reportConverterBean.convertFromTimeEntries(timeEntries);
        for (WeeklyReportEntry entry : reportEntries) {
            weeklyEntriesDs.addItem(entry);
        }
    }

    protected boolean isUserCanClose() {
        return securityAssistant.isSuperUser() || securityAssistant.isUserCloser();
    }

    protected abstract class AbstractChangeStatusAction extends AbstractAction {

        public static final String APPROVE_ACTION_ID = "approve";
        public static final String REJECT_ACTION_ID = "reject";
        public static final String CLOSE_ACTION_ID = "close";

        protected final User user;
        protected final TimeEntryStatus status;
        protected String rejectReason;

        protected AbstractChangeStatusAction(String id, User user, TimeEntryStatus status) {
            super(id);
            this.user = user;
            this.status = status;
        }

        @Override
        public void actionPerform(Component component) {
            rejectReason = null;
            final List<TimeEntry> timeEntries = getTimeEntries();
            if (timeEntries.isEmpty()) {
                return;
            }
            if (TimeEntryStatus.REJECTED.equals(status)) {
                final RejectionReason rejectionReasonWindow = (RejectionReason) openWindow("rejection-reason",
                        WindowManager.OpenType.DIALOG);
                rejectionReasonWindow.addListener(actionId -> {
                    if (RejectionReason.CONFIRM_ACTION_AD.equals(actionId)) {
                        rejectReason = rejectionReasonWindow.getRejectionReason();
                        commitTimeEntries(timeEntries);
                    }
                });
            } else {
                commitTimeEntries(timeEntries);
            }
        }

        protected void commitTimeEntries(List<TimeEntry> timeEntries) {
            CommitContext commitContext = new CommitContext();
            for (TimeEntry entry : timeEntries) {
                entry.setStatus(status);
                entry.setRejectionReason(rejectReason);
                commitContext.getCommitInstances().add(entry);
            }
            getDsContext().getDataSupplier().commit(commitContext);

            updateReportTableItems();
        }

        protected abstract List<TimeEntry> getTimeEntries();

        @Override
        public String getCaption() {
            return null;
        }
    }

    protected class UserChangeStatusAction extends AbstractChangeStatusAction {

        protected UserChangeStatusAction(String id, User user, TimeEntryStatus status) {
            super(id, user, status);
        }

        @Override
        protected List<TimeEntry> getTimeEntries() {
            TimeEntriesProvider provider = new TimeEntriesProvider(user);
            if (CLOSE_ACTION_ID.equals(id)) {
                provider.setSpecificStatus(TimeEntryStatus.APPROVED);
            } else {
                if (!securityAssistant.isSuperUser()) {
                    provider.setApprovable(true);
                }
                provider.addExcludeStatus(TimeEntryStatus.CLOSED);
                if (REJECT_ACTION_ID.equals(id)) {
                    provider.addExcludeStatus(TimeEntryStatus.REJECTED);
                }
            }
            return provider.getUserTimeEntries();
        }
    }

    protected class WeeklyReportChangeStatusAction extends AbstractChangeStatusAction {

        protected final WeeklyReportEntry weeklyReportEntry;

        protected WeeklyReportChangeStatusAction(String id, User user, WeeklyReportEntry weeklyReportEntry,
                TimeEntryStatus status) {
            super(id, user, status);
            this.weeklyReportEntry = weeklyReportEntry;
        }

        @Override
        protected List<TimeEntry> getTimeEntries() {
            List<TimeEntry> exist = weeklyReportEntry.getExistTimeEntries();
            if (exist.isEmpty()) {
                return Collections.emptyList();
            }
            TimeEntriesProvider provider = new TimeEntriesProvider(exist);
            if (CLOSE_ACTION_ID.equals(id)) {
                provider.setSpecificStatus(TimeEntryStatus.APPROVED);
            } else {
                provider.addExcludeStatus(TimeEntryStatus.CLOSED);
                if (REJECT_ACTION_ID.equals(id)) {
                    provider.addExcludeStatus(TimeEntryStatus.REJECTED);
                }
            }
            return provider.getUserTimeEntries();
        }
    }

    @SuppressWarnings("unused")
    protected abstract class AbstractChangeStatusActionsProvider {
        protected User user;

        protected AbstractChangeStatusActionsProvider(User user) {
            this.user = user;
        }

        @Nullable
        public abstract AbstractChangeStatusAction getApproveAction();

        @Nullable
        public abstract AbstractChangeStatusAction getRejectAction();

        @Nullable
        public abstract AbstractChangeStatusAction getCloseAction();
    }

    protected class UserChangeStatusActionsProvider extends AbstractChangeStatusActionsProvider {

        public UserChangeStatusActionsProvider(User user) {
            super(user);
        }

        @Nullable
        @Override
        public AbstractChangeStatusAction getApproveAction() {
            return securityAssistant.isSuperUser() || isApprovableUser()
                    ? new UserChangeStatusAction(AbstractChangeStatusAction.APPROVE_ACTION_ID, user,
                            TimeEntryStatus.APPROVED)
                    : null;
        }

        @Nullable
        @Override
        public AbstractChangeStatusAction getRejectAction() {
            return securityAssistant.isSuperUser() || isApprovableUser()
                    ? new UserChangeStatusAction(AbstractChangeStatusAction.REJECT_ACTION_ID, user,
                            TimeEntryStatus.REJECTED)
                    : null;
        }

        @Nullable
        @Override
        public AbstractChangeStatusAction getCloseAction() {
            return isUserCanClose()
                    ? new UserChangeStatusAction(AbstractChangeStatusAction.CLOSE_ACTION_ID, user,
                            TimeEntryStatus.CLOSED)
                    : null;
        }

        protected boolean isApprovableUser() {
            return managedUsers.contains(user);
        }
    }

    protected class WeeklyReportChangeStatusActionProvider extends AbstractChangeStatusActionsProvider {

        protected WeeklyReportEntry reportEntry;

        public WeeklyReportChangeStatusActionProvider(User user, WeeklyReportEntry reportEntry) {
            super(user);
            this.reportEntry = reportEntry;
        }

        @Nullable
        @Override
        public AbstractChangeStatusAction getApproveAction() {
            return securityAssistant.isSuperUser() || isApprovableEntry(reportEntry)
                    ? new WeeklyReportChangeStatusAction(AbstractChangeStatusAction.APPROVE_ACTION_ID, user,
                            reportEntry, TimeEntryStatus.APPROVED)
                    : null;
        }

        @Nullable
        @Override
        public AbstractChangeStatusAction getRejectAction() {
            return securityAssistant.isSuperUser() || isApprovableEntry(reportEntry)
                    ? new WeeklyReportChangeStatusAction(AbstractChangeStatusAction.REJECT_ACTION_ID, user,
                            reportEntry, TimeEntryStatus.REJECTED)
                    : null;
        }

        @Nullable
        @Override
        public AbstractChangeStatusAction getCloseAction() {
            return isUserCanClose()
                    ? new WeeklyReportChangeStatusAction(AbstractChangeStatusAction.CLOSE_ACTION_ID, user,
                            reportEntry, TimeEntryStatus.CLOSED)
                    : null;
        }

        protected boolean isApprovableEntry(WeeklyReportEntry reportEntry) {
            return managedProjects.contains(reportEntry.getProject());
        }
    }

    protected class TimeEntriesProvider {

        protected User user;
        protected boolean isApprovable = false;
        protected TimeEntryStatus specificStatus = null;
        protected List<TimeEntryStatus> excludeStatuses = null;
        protected List<TimeEntry> fixedTimeEntries = null;

        public TimeEntriesProvider(User user) {
            this.user = user;
        }

        public TimeEntriesProvider(List<TimeEntry> timeEntries) {
            this.fixedTimeEntries = timeEntries;
        }

        public TimeEntriesProvider setApprovable(boolean isApprovable) {
            this.isApprovable = isApprovable;
            return this;
        }

        public TimeEntriesProvider setSpecificStatus(TimeEntryStatus specificStatus) {
            this.specificStatus = specificStatus;
            return this;
        }

        public TimeEntriesProvider addExcludeStatus(TimeEntryStatus excludeStatus) {
            if (this.excludeStatuses == null) {
                this.excludeStatuses = new ArrayList<>();
            }
            this.excludeStatuses.add(excludeStatus);
            return this;
        }

        public List<TimeEntry> getUserTimeEntries() {
            if (statusOption.getValue() == null) {
                return Collections.emptyList();
            }

            List<TimeEntryStatus> statuses = new ArrayList<>(statusOption.getValue());
            if (specificStatus != null) {
                if (statuses.contains(specificStatus)) {
                    statuses = Collections.singletonList(specificStatus);
                } else {
                    return Collections.emptyList();
                }
            }

            if (excludeStatuses != null) {
                for (TimeEntryStatus excludeStatus : excludeStatuses) {
                    statuses.remove(excludeStatus);
                }
            }
            List<TimeEntry> timeEntries = new ArrayList<>();
            if (fixedTimeEntries == null) {
                for (TimeEntryStatus status : statuses) {
                    timeEntries.addAll(getTimeEntriesForPeriod(firstDayOfWeek, lastDayOfWeek, status));
                }
            } else {
                for (TimeEntry timeEntry : fixedTimeEntries) {
                    if (statuses.contains(timeEntry.getStatus())) {
                        timeEntries.add(timeEntry);
                    }
                }
            }
            return timeEntries;
        }

        protected Collection<? extends TimeEntry> getTimeEntriesForPeriod(Date start, Date end,
                TimeEntryStatus status) {
            if (isApprovable) {
                return projectsService.getApprovableTimeEntriesForPeriod(start, end,
                        userSession.getCurrentOrSubstitutedUser(), user, status, "timeEntry-full");
            } else {
                return projectsService.getTimeEntriesForPeriod(start, end, user, status, "timeEntry-full");
            }
        }
    }
}