Экран Jmix

Если вам нужна процессная форма со сложной компоновкой и поведением, вы можете использовать существующий экран UI Jmix вместо диалоговых форм ввода. Контроллер экрана должен быть помечен аннотацией @ProcessForm, чтобы использоваться в качестве процессной формы.

Аннотация @ProcessForm указывает, что экран должен появиться в поле со списком процессных форм моделера.

Переменные процесса

Аннотацией @ProcessVariable можно помечать инжектированные компоненты UI или обычные поля класса.

Она указывает, что значение переменной процесса будет записано в это поле при открытии процессной формы. В случае компонента UI значением переменной процесса будет компонент UI.

@ProcessVariable
private Date date;

@ViewComponent
@ProcessVariable(name = "order")
private EntityPicker<Order> orderEntityPicker;

Если настроить ProcessFormContext с помощью метода saveInjectedProcessVariables(), то значения аннотированных полей будут сохранены как переменные процесса при запуске процесса или завершении пользовательской задачи.

Аннотация @ProcessVariable имеет необязательный атрибут name. Значением этого атрибута является имя переменной процесса. Если атрибут name отсутствует, то в качестве имени переменной процесса используется имя поля.

ProcessFormContext

Объект ProcessFormContext содержит информацию об определении запускаемого процесса (когда для запуска процесса используется форма) или пользовательской задачи, которую необходимо выполнить.

ProcessFormContext можно использовать, если процессная форма открыта из экранов Start process и My tasks. Если вам нужно открыть процессную форму с инжектированным ProcessFormContext программно, используйте бин ProcessFormScreens.

Объект ProcessFormContext также содержит методы для запуска процесса и завершения задачи.

Пример запуска процесса:

@ProcessVariable
private Date date;

@ViewComponent
@ProcessVariable(name = "order")
private EntityPicker<Order> orderEntityPicker;

@Autowired
private ProcessFormContext processFormContext;

@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
    processFormContext.processStarting() (1)
            .withBusinessKey("order-1") (2)
            .addProcessVariable("date", date)
            .addProcessVariable("order", orderEntityPicker.getValue()) (3)
            .start(); (4)
    closeWithDefaultAction(); (5)
}
1 Создание экземпляра ProcessStarting.
2 Установка бизнес-ключа для экземпляра процесса.
3 Добавление переменной процесса.
4 Запуск процесса.
5 Закрытие открытого окна.

Пример завершения пользовательской задачи:

@Autowired
private ProcessFormContext processFormContext;

@Subscribe("rejectBtn")
protected void onRejectBtnClick(ClickEvent<JmixButton> event) {
    processFormContext.taskCompletion() (1)
            .withOutcome("reject") (2)
            .saveInjectedProcessVariables() (3)
            .complete(); (4)
    closeWithDefaultAction(); (5)
}
1 Создание экземпляра TaskCompletion.
2 Установка выхода задачи.
3 Указывает, что значения полей класса, аннотированные символом, @ProcessVariables, должны быть собраны и сохранены как переменные процесса.
4 Завершение задачи.
5 Закрытие открытого окна.

Объявление выходов задачи

В моделере можно определить условие для элемента потока, выбрав пользовательскую задачу и ее выход из выпадающего списка. Чтобы заполнить этот список для пользовательской задачи, которая использует процессную форму экрана Jmix, вы можете объявить список возможных выходов в контроллере формы. Используйте для этого атрибут outcomes аннотации @ProcessForm.

@ProcessForm(
        outcomes = { (1)
                @Outcome(id = "approve"),
                @Outcome(id = "reject")
        }
)
public class OrderApprovalTaskForm extends StandardView {

Параметры процессных форм

Процессные формы экранов Jmix могут принимать внешние параметры, определенные в моделере. Параметры, используемые формой, определяются в атрибуте params аннотации @ProcessForm:

@ProcessForm(
        params = {
                @Param(name = "variableName"),
                @Param(name = "entityPickerCaption")
        }
)

Эти параметры считываются моделером, и их можно увидеть после выбора экрана.

form params

Вы можете редактировать параметры и указать прямое значение параметра или использовать в качестве значения одну из существующих переменных процесса.

form params edit

Внутри контроллера процессной формы используйте аннотацию @ProcessFormParam на полях класса, чтобы получить значения параметров.

@ProcessFormParam
private String variableName;

@ProcessFormParam
private String entityPickerCaption;

Полный список параметров процессной формы также можно получить из объекта ProcessFormContext:

List<FormParam> formParams = processFormContext.getFormData().getFormParams();

Как и аннотация @ProcessVariable, @ProcessFormParam поддерживает необязательный атрибут name. Если атрибут не определен, то в качестве имени параметра используется имя поля.

См. пример процессной формы с параметрами.

Выходные переменные

При моделировании процесса может быть полезно знать, какие переменные задаются процессной формой экрана Jmix, чтобы позже использовать их повторно в модели процесса. Для этого используйте атрибут outputVariabes аннотации @ProcessForm.

@ProcessForm(
        outputVariables = {
                @OutputVariable(name = "order", type = Order.class),
                @OutputVariable(name = "comment", type = String.class)
        }
)

Часто бывают случаи, когда переменная процесса устанавливается только тогда, когда задача выполнена с использованием определенного выхода. Для объявления этого поместите атрибут аннотации outputVariables в аннотацию @Outcome.

@ProcessForm(
   outcomes = {
      @Outcome(
         id = "approve",
         outputVariables = {
            @OutputVariable(name = "nextActor", type = User.class) (1)
         }
      ),
      @Outcome(
        id = "reject",
           outputVariables = {
              @OutputVariable(name = "rejectionReason", type = String.class) (2)
        }
      )
   },
   outputVariables = {
      @OutputVariable(name = "comment", type = String.class) (3)
   }
)
1 Переменная nextActor может быть установлена, когда задача завершена с выходом approve.
2 Переменная rejectionReason может быть установлена, когда задача завершена с выходом reject.
3 Переменная comment может быть установлена в любом случае.

Информация о выходных переменных отображается в соответствующем разделе панели свойств при выборе процессной формы.

output variables

Ограничение использования процессных форм

По умолчанию все процессные формы экранов доступны в рамках любой модели процесса. Если вы хотите использовать какой-либо экран только в определенных процессах, нужно указать ключи процессов в атрибуте allowedProcessKeys аннотации @ProcessForm.

@ProcessForm(allowedProcessKeys = {"process-1", "process-2"})

Форма будет доступна только для процессов с идентификаторами process-1 и process-2 в моделере.

Программное открытие форм

Вы можете использовать службу ProcessFormScreens для создания процессных форм запуска и процессных форм задач, определенных в моделере.

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

@Autowired
private RepositoryService repositoryService;

@Autowired
protected ProcessFormViews processFormViews;

@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(final ClickEvent<JmixButton> event) {
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() (1)
            .processDefinitionKey("order-process")
            .latestVersion()
            .singleResult();

    processFormViews.openStartProcessForm(processDefinition, this); (2)
}
1 Получение определения процесса с ключом order-process.
2 Отображение процессной формы запуска с полученным определением процесса.

Процессная форма запуска может выглядеть как в примере в разделе ProcessFormContext.

Для создания формы задачи используйте метод createTaskProcessForm:

@Autowired
private TaskService taskService;

@Autowired
private ProcessFormViews processFormViews;

@Subscribe("openTaskBtn")
public void onOpenTaskBtnClick(ClickEvent<JmixButton> event) {

    Task task = taskService.createTaskQuery()
            .processDefinitionKey("approve-order-process")
            .taskAssignee("admin")
            .active()
            .orderByTaskCreateTime()
            .list()
            .get(0);

    processFormViews.openTaskProcessForm(task, this);
}

Процессная форма задачи может выглядеть как пример в разделе ProcessFormContext.

Примеры

Процессная форма запуска

Давайте рассмотрим пример процессной формы, которая используется в качестве стартовой. В форме отображаются два поля:

  • текстовое поле для ввода номера заказа.

  • выпадающий список пользователей для выбора менеджера. Менеджер может быть следующим актором процесса.

XML-дескриптор экрана:

<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://com.company.bpmex1.view.forms/startProcessForm.title">
    <layout>
        <formLayout>
            <textField id="orderNumber"
                       label="msg://com.company.bpmex1.view.forms/orderNumber"
                       datatype="string"/>
            <entityPicker id="managerEntityPicker"
                          metaClass="User"
                          label="msg://managerEntityPicker.caption">
                <actions>
                    <action id="lookup" type="entity_lookup"/>
                    <action id="clear" type="entity_clear"/>
                </actions>
            </entityPicker>
        </formLayout>
        <hbox id="actionsPanel" spacing="true">
            <button id="startProcessBtn"
                    icon="CHECK"
                    text="msg://com.company.bpmex1.view.forms/startProcessBtn.text"/>
        </hbox>
    </layout>
</view>

Контроллер экрана:

@ViewController("OrderApprovalStartForm")
@ViewDescriptor("order-approval-start-form.xml")
@ProcessForm (1)
public class OrderApprovalStartForm extends StandardView {

    @ViewComponent
    @ProcessVariable (2)
    private TypedTextField<String> orderNumber;

    @ViewComponent
    @ProcessVariable(name = "manager") (3)
    private EntityPicker<User> managerEntityPicker;

    @Autowired
    private ProcessFormContext processFormContext; (4)

    @Subscribe("startProcessBtn")
    public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.processStarting()
                .withBusinessKey(orderNumber.getValue()) (5)
                .saveInjectedProcessVariables() (6)
                .start();
        closeWithDefaultAction();
    }
}
1 Аннотация @ProcessForm указывает, что этот экран является процессной формой и будет доступен в моделере.
2 Мы объявляем, что инжектированный компонент UI orderNumber является переменной процесса. Поскольку мы разрабатываем процессную форму запуска, переменная еще не имеет значения, но аннотация будет использоваться при запуске процесса.
3 То же, что и 2, но здесь имя manager переменной процесса отличается от имени поля managerEntityPicker.
4 ProcessFormContext – это объект, который мы используем для запуска процесса.
5 При запуске процесса мы можем передать необязательный бизнес-ключ экземпляра процесса. Здесь мы использовали orderNumber.
6 saveInjectedProcessVariables() указывает, что значения полей с аннотацией @ProcessVariables должны быть сохранены как переменные процесса при запуске процесса.

Вместо того, что бы использовать метод saveInjectedProcessVariables(), вы можете задать переменные процесса явно:

@Subscribe("startProcessBtn")
public void onStartProcessBtnClick(ClickEvent<JmixButton> event) {
    processFormContext.processStarting()
            .withBusinessKey(orderNumber.getValue())
            .addProcessVariable("orderNumber", orderNumber.getValue())
            .addProcessVariable("manager",managerEntityPicker.getValue())
            .start();
    closeWithDefaultAction();
}

Процессная форма задачи

Рассмотрим пример процессной формы задачи с двумя полями:

  • Первый отобразит значение существующей переменной процесса – orderNumber.

  • Второе поле будет использоваться для новой переменной процесса – comment.

Кнопки Approve и Reject завершают пользовательскую задачу с соответствующим выходом.

XML-дескриптор экрана:

<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://orderApprovalTaskForm.title">
    <layout>
        <formLayout>
            <textField id="orderNumber" readOnly="true"
                       label="msg://orderNumber"/>
            <textField id="commentField" label="msg://comment"/>
        </formLayout>
        <hbox id="actionsPanel" spacing="true">
            <button id="approveBtn" icon="CHECK" text="msg://approveBtn.text"/>
            <button id="rejectBtn" icon="BAN" text="msg://rejectBtn.text"/>
        </hbox>
    </layout>
</view>

Контроллер экрана:

@ViewController("OrderApprovalTaskForm")
@ViewDescriptor("order-approval-task-form.xml")
@ProcessForm(
        outcomes = { (1)
                @Outcome(id = "approve"),
                @Outcome(id = "reject")
        }
)
public class OrderApprovalTaskForm extends StandardView {

    @ViewComponent
    @ProcessVariable (2)
    private TypedTextField<String> orderNumber;

    @ViewComponent
    @ProcessVariable(name = "comment") (3)
    private TypedTextField<String> commentField;

    @Autowired
    private ProcessFormContext processFormContext;

    @Subscribe("approveBtn")
    protected void onApproveBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.taskCompletion()
                .withOutcome("approve")
                .saveInjectedProcessVariables() (4)
                .complete();
        closeWithDefaultAction();
    }

    @Subscribe("rejectBtn")
    protected void onRejectBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.taskCompletion()
                .withOutcome("reject")
                .addProcessVariable("comment", commentField.getValue()) (5)
                .complete();
        closeWithDefaultAction();
    }
}
1 Форма определяет два возможных выхода, которые можно использовать в условии узла потока в моделере. Эта информация используется только моделером.
2 Переменная orderNumber уже была установлена при запуске процесса. Из-за аннотации @ProcessVariable значение переменных процесса orderNumber будет установлено в текстовом поле orderNumber при отображении формы.
3 Переменная comment еще не задана, но аннотация @ProcessVariable будет учтена слушателем нажатия кнопки при выполнении задачи.
4 Значения всех полей с аннотацией @ProcessVariable будут сохранены как переменные процесса по завершении задачи.
5 Альтернативный способ определения переменных процесса. Вместо того, что бы использовать метод saveInjectedProcessVariables(), вы можете определить переменные процесса напрямую.

Процессная форма с параметрами

Предположим, вам нужна форма для выбора следующего актора процесса. Форма должна отображать поле EntityPicker с пользователями и сохранять результат в переменной процесса. Мы хотим использовать форму для выбора разных акторов на разных этапах процесса, поэтому форма должна иметь два параметра:

  • variableName

  • entityPickerCaption

XML-дескриптор экрана:

<view xmlns="http://jmix.io/schema/flowui/view"
      title="msg://com.company.bpmex1.view.forms/actorSelectionForm.title">
    <layout spacing="true">
        <formLayout width="20em">
            <entityPicker id="userEntityPicker"
                          metaClass="User"
                          property="username">
                <actions>
                    <action id="lookup" type="entity_lookup"/>
                    <action id="clear" type="entity_clear"/>
                </actions>
            </entityPicker>
        </formLayout>
        <hbox spacing="true">
            <button id="completeTaskBtn" icon="CHECK" text="msg://completeTask"/>
        </hbox>
    </layout>
</view>

Контроллер экрана:

@ProcessForm(
        params = {
                @Param(name = "variableName"),
                @Param(name = "entityPickerCaption")
        }
)
public class ActorSelectionForm extends StandardView {

    @Autowired
    private ProcessFormContext processFormContext;

    @ViewComponent
    private EntityPicker<String> userEntityPicker;

    @ProcessFormParam
    private String variableName;

    @ProcessFormParam
    private String entityPickerCaption;

    @Subscribe
    private void onBeforeShow(BeforeShowEvent event) {
        userEntityPicker.setLabel(entityPickerCaption);
    }

    @Subscribe("completeTaskBtn")
    private void onCompleteTaskBtnClick(ClickEvent<JmixButton> event) {
        processFormContext.taskCompletion()
                .addProcessVariable(variableName, userEntityPicker.getValue())
                .complete();
        closeWithDefaultAction();
    }
}