1.7.2. Контроллер экрана редактирования договора
Перейдите на вкладку Controller и замените ее содержимое на следующий код:
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);
Во время инициализации и происходит создание необходимых элементов управления внутри фрейма.