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

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

ContractEdit.java
package com.company.demo.web.contract;

import com.company.demo.entity.Contract;
import com.haulmont.bpm.entity.ProcDefinition;
import com.haulmont.bpm.entity.ProcInstance;
import com.haulmont.bpm.gui.procactions.ProcActionsFrame;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.core.global.PersistenceHelper;
import com.haulmont.cuba.gui.app.core.file.FileDownloadHelper;
import com.haulmont.cuba.gui.components.AbstractEditor;
import com.haulmont.cuba.gui.components.Table;

import javax.annotation.Nullable;
import javax.inject.Inject;

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 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().addBeforeCommitListener(context -> {
            if (procInstance != null && PersistenceHelper.isNew(procInstance)) {
                context.getCommitInstances().add(procInstance);
            }
        });
        FileDownloadHelper.initGeneratedColumn(attachmentsTable, "file");
    }

    private void initProcActionsFrame() {
        procActionsFrame.setBeforeStartProcessPredicate(() -> {
            if (PersistenceHelper.isNew(getItem())) {
                showNotification(getMessage("saveContract"), NotificationType.WARNING);
                return false;
            }
            return true;
        });
        procActionsFrame.setAfterStartProcessListener(() -> {
            showNotification(getMessage("processStarted"), NotificationType.HUMANIZED);
            close(COMMIT_ACTION_ID);
        });
        procActionsFrame.setBeforeCompleteTaskPredicate(this::commit);
        procActionsFrame.setAfterCompleteTaskListener(() -> {
            showNotification(getMessage("taskCompleted"), NotificationType.HUMANIZED);
            close(COMMIT_ACTION_ID);
        });
        procActionsFrame.setCancelProcessEnabled(false);
        procActionsFrame.init(procInstance);
    }


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

    @Nullable
    private ProcInstance findProcInstance() {
        LoadContext ctx = LoadContext.create(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 (ProcInstance) 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().addBeforeCommitListener(context -> {
    if (procInstance != null && PersistenceHelper.isNew(procInstance)) {
        context.getCommitInstances().add(procInstance);
    }
});

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

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

private void initProcActionsFrame() {
    procActionsFrame.setBeforeStartProcessPredicate(() -> {
        if (PersistenceHelper.isNew(getItem())) {
            showNotification(getMessage("saveContract"), NotificationType.WARNING);
            return false;
        }
        return true;
    });
    procActionsFrame.setAfterStartProcessListener(() -> {
        showNotification(getMessage("processStarted"), NotificationType.HUMANIZED);
        close(COMMIT_ACTION_ID);
    });
    procActionsFrame.setBeforeCompleteTaskPredicate(this::commit);
    procActionsFrame.setAfterCompleteTaskListener(() -> {
        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);

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