Связь ролей BPM и Cuba

Но если это делать с экрана контракта, то падает ошибка

Как говорится “не могу не подтвердить, ни опровергнуть”.
В приведено демо-проекте нет возможности запустить процесс с контракта.
image
Собственно, в контроллере ContractBrowse нет рабочего кода который позволил бы запустить процесс с размещенного внизу procActionsFragment при выделенным Contract. А на экране ContractEdit вообще нет procActionsFragment.
Но судя по ошибке - проблема с чтением конкретного экземпляра procInstance по id.
Как вариант - есть ограничения для этой сущности, которые скрывают этот или все экземпляры procInstance от пользователя, выполняющего действие.

Андрей ещё раз спасибо за помощь, нужно выделить сущность, нажать кнопку обновить процесс, и внизу будет активным окно запуска( фрагмент).

Если использовать приложенную тут модель процесса - фрагмент не станет активным :slight_smile:
Можете проверить.

Странно, все работает, из изменения только фрагмент на верх кинул для удобства.

Конечно, у вас работает, потому что модель процесса другая.
Скачайте то, что вы выложили в эту ветку.
Контроллер ContractBrowse

private static final String PROCESS_CODE = "delete";
....
...

procActionsFragment.initializer()
                    .standard()
                    .init(PROCESS_CODE, contractsTable.getSingleSelected());

Открываем вашу модель процесса
image

Ладно, “обработал напильником” тестовый проект и смог под отладчиком вашу ошибку поймать.
Пробовали вот тут ставить точку останова и заглянуть в БД на тему того, есть ли в БД ProcInstance с данным ID?
image
На момент выполнения этого кода, ProcInstance таким ID еще не успел сохраниться в БД.
У вас в модели сразу идет вызов bpmdemo_ProcRolesHelper.fillProcRoles('boss', 'Administrators', bpmProcInstanceId).
В процессной переменной bpmProcInstanceId ID экземпляра уже есть, он СУБД еще не успела сохранить экземпляр.
Попробуйте перенести действия с только что стартовавшим процессом из модели процесса в ProcActionsFragment в слушатель setAfterStartProcessListener().

Спасибо, но не прокатило ))

bpmdemo-test.zip (79.6 КБ) Удаление заявки.json (10.7 КБ)

Ребят, вот итоговая демка и модель. Так и не могу решить как исправить этот эксепшен:

1

Как советовал Андрей выше, я пробывал через слушатель setAfterStartProcessListener(), результат такой: роли подставляются, но не назначаются, я думаю это связано с тем, что процесс уже запущен на момент назначения роли, а потом просто подставляет роль. Хотелось бы решить эту проблему. Так же пробывал сделать этот сервисом, результат такой же как вызывать эти методы таском через модель из бина - экспешен такой же как на скрине.

Спасибо.

В вашем случае проблема следующая: вы стартуете новый процесс - в этот момент открывается новая транзакция БД. В этой транзакции создаётся экземпляр ProcInstance. Затем в этой же транзакции управление переходит к первому элементу процесса (вызов ProcRolesHelper)
Внутри ProcRolesHelper вы используете DataManager для поиска экземпляра ProcInstance, но DataManager создаёт новую транзакцию, а предыдущая транзакция ещё не закоммичена. Поэтому метод load ничего и не возвращает.
Вариант1 - можете попробовать использовать TransactionalDataManager - он не создаёт новую транзакцию, а присоединяется к текущей.
Вариант 2 - если участники процесса у вас заполняются при старте процесса, а не где-то в середине его выполнения, то использовать подход с сервисом, заполняющим роли, смысла не имеет. Вот пример как задать участников процесса программно из контроллера экрана.

2 симпатии

Спасибо, Максим, буду пробывать первый вариант. Второй вариант теряют всю суть - для измениния согласующего нужен разработчик, а не человек с учеткой админа. Еще раз спасибо

Максим, спасибо. Первый вариант сработал отлично. Почти все заработало :handshake: :beers:

Я так понимаю менеджер тразакция не может сейвить контекст, только ентити. Может есть вариант делать коммит после “получения” id процесса, что бы было что грузить из БД.

package com.company.bpmdemo.core;

import com.haulmont.bpm.entity.ProcActor;
import com.haulmont.bpm.entity.ProcInstance;
import com.haulmont.bpm.entity.ProcRole;
import com.haulmont.cuba.core.TransactionalDataManager;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.security.entity.User;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Component(ProcRolesHelper.NAME)
public class ProcRolesHelper {
    public static final String NAME = "bpmdemo_ProcRolesHelper";


    @Inject
    private Logger log;

    @Inject
    private DataManager dataManager;

    @Inject
    private Metadata metadata;

    @Inject
    private TransactionalDataManager transactionalDataManager;

    public void fillProcRoles(String procRoleCode, String secRoleName, UUID bpmProcInstanceId) {


        ProcInstance procInstance = transactionalDataManager.load(ProcInstance.class)
                .id(bpmProcInstanceId)
                .view("procInstance-listener-view")
                .one();
        Optional<ProcRole> procRoleOpt = procInstance.getProcDefinition().getProcRoles().stream()
                .filter(procRole -> procRole.getCode().equals(procRoleCode))
                .findAny();
        if (!procRoleOpt.isPresent()) {
            log.error("ProcRole {} not found", procRoleCode);
            return;
        }
        List<User> usersWithSecRole = findUsersBySecRole(secRoleName);
        List<ProcActor> procActorsToCreate = new ArrayList<>();
        for (User user : usersWithSecRole) {
            ProcActor procActor = metadata.create(ProcActor.class);
            procActor.setUser(user);
            procActor.setProcInstance(procInstance);
            procActor.setProcRole(procRoleOpt.get());
            procActorsToCreate.add(procActor);
            transactionalDataManager.save(procActor);
        }
    }

    private List<User> findUsersBySecRole(String secRoleName) {
        return dataManager.load(User.class)
                .query("select u from sec$User u join u.userRoles ur join ur.role r where r.name = :secRoleName")
                .parameter("secRoleName", secRoleName)
                .list();
    }
}

Максим, если заменяем DataManager на TransactionalDataManager, то в итоге не можем закоммитить вот эту штуку : procActorsToCreate.add(procActor); , раньше это было реализовано через dataManager.commit(new CommitContext(procActorsToCreate)); , как можно выкрутиться из этой ситуации ? Без нее роли конечно назначаются, но только роли и сохраняются, комменты, статус заявки, ничего не меняется, можно бесконечно создавать заявки, фрагмент инициализируется постоянно.

Можете попробовать пойти другим путём.
Можно оставить предыдущий вариант (с обычным DataManager), но отметить в модели элемент Fill approver role как asynchronous
Screenshot 2020-06-04 at 22.05.28

Чтобы флаг заработал, не забудьте в app.properties проставить свойство bpm.activiti.asyncExecutorEnabled в true

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

2 симпатии

Максим, спасибо. Это решило все проблемы, а то мне пришлось делать в обход переопределяя стартовый экран процесса, и в контейнер подсовывать свои роли при инициализации. Теперь стало хорошо :slightly_smiling_face: Еще раз спасибо

Максим добрый день. Подскажите пжл, как можно реализовать вот такую бы схему. Сейчас все работает, но необходимо добавить вот такой “ифчик”. Спасибо

bpm_q

Добрый день. При запуске процесса можете передать процессную переменную, boolean с признаком того, что запущено админом, логин пользователя, в общем что угодно. Затем проанализировать её значение в любом месте процесса:

Screenshot 2020-08-05 at 13.54.37

Выражение может выглядеть, например так: ${myVariable == 'myValue'}

1 симпатия

Спасибо в очередной раз :handshake:

Макс, я нашел инфу как передать процессную переменную в новом аддоне - Bproc, а как это сделал в старом bpm…который уже деприкейтед )))

ProcessRuntimeService.startProcess(...)
Вот тут можно посмотреть пример.

1 симпатия

Макс, а я могу там сделать по типу:
${myVariable == ‘myValue’ || myVariable == ‘myValue2’ || myVariable == ‘myValue3’}

Макс, и еще вопрос. Раньше можно было искать юзеров по ролям таким способом:

private List<User> findUsersBySecRole(String secRoleName) {
        return dataManager.load(User.class)
                .query("select u from sec$User u join u.userRoles ur join ur.role r where r.name = :secRoleName")
                .parameter("secRoleName", secRoleName)
                .list();
    }

Но после перехода на 7.2.x уже не работает данный метод, точнее он работает для созданных мной ролей ( а на 7.1.0 и с куба ролями работало ), а теперь с куба ролями уже не работает. Есть решение для обновленных ролей ?

Проблему с куба ролями решил таким способом.

private List<User> findAdminsBySecRole(String cubaRoleName) {
        return dataManager.load(User.class)
                .query("select u from sec$User u join u.userRoles ur where ur.roleName = :roleName")
                .parameter("roleName", cubaRoleName)
                .list();
    }