2.7.2. Контроллер экрана редактирования договора

Перейдите на вкладку Controller и замените ее содержимое на следующий код:

ContractEdit.java. 

package com.company.demo.gui.contract;

import com.haulmont.bpm.entity.ProcDefinition;
import com.haulmont.bpm.entity.ProcInstance;
import com.haulmont.bpm.gui.action.ProcAction;
import com.haulmont.bpm.gui.procactions.ProcActionsFrame;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.app.core.file.FileDownloadHelper;
import com.haulmont.cuba.gui.components.*;
import com.company.demo.entity.Contract;
import com.haulmont.cuba.gui.components.actions.BaseAction;
import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;

import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.Map;

public class ContractEdit extends AbstractEditor<Contract> {

    private static final String PROCESS_CODE = "contractApproval";

    @Inject
    private DataManager dataManager;

    private ProcDefinition procDefinition;

    private ProcInstance procInstance;

    @Inject
    private ProcActionsFrame procActionsFrame;

    @Inject
    private GroupBoxLayout procActionsBox;

    @Inject
    private ComponentsFactory componentsFactory;

    @Inject
    private Table attachmentsTable;

    @Inject
    private Metadata metadata;

    @Override
    protected void postInit() {
        super.postInit();
        procDefinition = findProcDefinition();
        if (procDefinition != null) {
            procInstance = findProcInstance();
            if (procInstance == null) {
                procInstance = metadata.create(ProcInstance.class);
                procInstance.setProcDefinition(procDefinition);
                procInstance.setEntityName("demo$Contract");
                procInstance.setEntityId(getItem().getId());
            }
            initProcActionsFrame();
        }
        getDsContext().addListener(new DsContext.CommitListenerAdapter() {
            @Override
            public void beforeCommit(CommitContext context) {
                if (procInstance != null && PersistenceHelper.isNew(procInstance)) {
                    context.getCommitInstances().add(procInstance);
                }
            }
        });
        FileDownloadHelper.initGeneratedColumn(attachmentsTable, "file");
    }

    private void initProcActionsFrame() {
        procActionsFrame.setBeforeStartProcessPredicate(new ProcAction.BeforeActionPredicate() {
            @Override
            public boolean evaluate() {
                if (PersistenceHelper.isNew(getItem())) {
                    showNotification(getMessage("saveContract"), NotificationType.WARNING);
                    return false;
                }
                return true;
            }
        });
        procActionsFrame.setAfterStartProcessListener(new ProcAction.AfterActionListener() {
            @Override
            public void actionCompleted() {
                showNotification(getMessage("processStarted"), NotificationType.HUMANIZED);
                close(COMMIT_ACTION_ID);
            }
        });
        procActionsFrame.setBeforeCompleteTaskPredicate(new ProcAction.BeforeActionPredicate() {
            @Override
            public boolean evaluate() {
                return commit();
            }
        });
        procActionsFrame.setAfterCompleteTaskListener(new ProcAction.AfterActionListener() {
            @Override
            public void actionCompleted() {
                showNotification(getMessage("taskCompleted"), NotificationType.HUMANIZED);
                close(COMMIT_ACTION_ID);
            }
        });
        procActionsFrame.setCancelProcessEnabled(false);
        procActionsFrame.init(procInstance);
    }


    @Nullable
    private ProcDefinition findProcDefinition() {
        LoadContext ctx = new LoadContext(ProcDefinition.class);
        ctx.setQueryString("select pd from bpm$ProcDefinition pd where pd.code = :code")
                .setParameter("code", PROCESS_CODE);
        return dataManager.load(ctx);
    }

    @Nullable
    private ProcInstance findProcInstance() {
        LoadContext ctx = new LoadContext(ProcInstance.class).setView("procInstance-start");
        ctx.setQueryString("select pi from bpm$ProcInstance pi where pi.procDefinition.id = :procDefinition and pi.entityId = :entityId")
                .setParameter("procDefinition", procDefinition)
                .setParameter("entityId", getItem());
        return dataManager.load(ctx);
    }
}

Сохраните изменения, нажав кнопку OK.

Рассмотрим код контроллера более подробно.

Чтобы запустить процесс, мы должны создать экземпляр процесса - объект ProcInsntance, связать его с описанием процесса (ProcDefinition) и выполнить запуск. Экземпляр процесса (ProcInstance) может быть запущен как самостоятельно, так и с привязкой к какой-либо сущности проекта. В нашем случае нужна привязка к договору.

В начале метода postInit() производится поиск экземпляра процесса согласования договора. Метод findProcDefinition() по коду contractApproval ищет описание процесса. Далее проверяется нет ли в базе объекта ProcInstance, связанного с текущим договором (метод findProcInstance()). Если экземпляр процесса для данного договора еще не создан, то создаем его, заполняя ссылку на описание процесса, устанавливая имя связанной сущности и ее идентификатор.

if (procInstance == null) {
    procInstance = metadata.create(ProcInstance.class);
    procInstance.setProcDefinition(procDefinition);
    procInstance.setEntityName("demo$Contract");
    procInstance.setEntityId(getItem().getId());
}

CommitListener добавляет в список сущностей, отправляемых на средний слой для коммита, созданный объект ProcInstance.

getDsContext().addListener(new DsContext.CommitListenerAdapter() {
    @Override
    public void beforeCommit(CommitContext context) {
        if (procInstance != null && PersistenceHelper.isNew(procInstance)) {
            context.getCommitInstances().add(procInstance);
        }
    }
});

Далее переходим к методу initProcActionsFrame().

ProcActionsFrame - это стандартный фрейм для отображения кнопок доступных в данный момент процессных действий. ProcActiosnFrame связан с экземпляром ProcInstance. Если процесс не запущен, то фрейм отобразит кнопку запуска процесса, если процесс запущен и для текущего пользователя имеются активные задачи, то он отобразит кнопки завершения текущей задачи в соответствии с определенными в модели процесса выходами из задачи (Task outcomes). Подробнее о ProcActionsFrame см. Раздел 6.1, «ProcActionsFrame».

private void initProcActionsFrame() {
    procActionsFrame.setBeforeStartProcessPredicate(new ProcAction.BeforeActionPredicate() {
        @Override
        public boolean evaluate() {
            if (PersistenceHelper.isNew(getItem())) {
                showNotification(getMessage("saveContract"), NotificationType.WARNING);
                return false;
            }
            return true;
        }
    });
    procActionsFrame.setAfterStartProcessListener(new ProcAction.AfterActionListener() {
        @Override
        public void actionCompleted() {
            showNotification(getMessage("processStarted"), NotificationType.HUMANIZED);
            close(COMMIT_ACTION_ID);
        }
    });
    procActionsFrame.setBeforeCompleteTaskPredicate(new ProcAction.BeforeActionPredicate() {
        @Override
        public boolean evaluate() {
            return commit();
        }
    });
    procActionsFrame.setAfterCompleteTaskListener(new ProcAction.AfterActionListener() {
        @Override
        public void actionCompleted() {
            showNotification(getMessage("taskCompleted"), NotificationType.HUMANIZED);
            close(COMMIT_ACTION_ID);
        }
    });
    procActionsFrame.setCancelProcessEnabled(false);
    procActionsFrame.init(procInstance);
}

Метод procActionsFrame.setBeforeStartProcessPredicate() добавляет проверку, выполняемую перед запуском процесса. Если объект с договором еще не сохранен, то процесс не запустится и будет выведено соответствующее предупреждение.

Метод procActionsFrame.setBeforeCompleteTaskPredicate() вызывает коммит редактора и позволяет завершить процессное действие только если коммит редактора прошел успешно.

Методы setAfterProcessStartListener и setAfterCompleteTaskListener будут вызваны после соответствующего события. Они отобразят уведомление и закроют редактор договора.

После того, как необходимые слушатели и предикаты для procActionsFrame заданы, вызывается инициализация фрейма.

procActionsFrame.init(procInstance);

Во время инициализации и происходит создание необходимых элементов управления внутри фрейма.