Hello,
I have a requirement to assign more than one UserTask for roles at one time. But standart implementation of procActionsFrame and even procInstance-edit frame doesn’t seems to support this functionality.
+
Any chance that someone already tried to implement it in their projects? What would be a better way to implement it?
I cant find a better solution than write my own “MultiTaskActionsFrame” and ProcActionsFrame extention.
If someone would be interested : I zipped test project below.
Code is far from ideal but it is test solution that i can easily share. Further development will be in my main proj so it is hard to track changes here.
Warning: Creating and cancelling process not allowed. If you yousing AfterCompliteTaskListener
don’t forget to call method MultiTaskFrame.reload()
ExtProcActionsFrame
package com.company.bpmmultitask.web.screens;
import com.haulmont.bpm.entity.ProcTask;
import com.haulmont.bpm.gui.procactions.ProcActionsFrame;
import com.haulmont.cuba.core.global.LoadContext;
import java.util.List;
public class ExtProcActionsFrame extends ProcActionsFrame {
protected int taskNum = 0; // task-count starts from zero
public void setTaskNum(int num){
this.taskNum = num;
}
public int getTaskNum() {
return taskNum;
}
@Override
protected ProcTask findCurrentUserProcTask() {
LoadContext<ProcTask> ctx = new LoadContext<>(ProcTask.class);
ctx.setQueryString("select pt from bpm$ProcTask pt left join pt.procActor pa left join pa.user pau " +
"where pt.procInstance.id = :procInstance and (pau.id = :userId or " +
"(pa is null and exists(select pt2 from bpm$ProcTask pt2 join pt2.candidateUsers cu where pt2.id = pt.id and cu.id = :userId))) " +
"and pt.endDate is null")
.setParameter("procInstance", procInstance)
.setParameter("userId", userSession.getCurrentOrSubstitutedUser());
ctx.setView("procTask-complete");
List<ProcTask> result = dataManager.loadList(ctx);
return result.isEmpty() || result.size()<taskNum ? null : result.get(taskNum);
}
}
MultiTaskFrame
package com.company.bpmmultitask.web.screens;
import com.haulmont.bpm.entity.ProcDefinition;
import com.haulmont.bpm.entity.ProcInstance;
import com.haulmont.bpm.entity.ProcTask;
import com.haulmont.bpm.gui.action.ProcAction;
import com.haulmont.bpm.gui.procactions.ProcActionsFrame;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.ReferenceToEntitySupport;
import com.haulmont.cuba.gui.components.AbstractFrame;
import com.haulmont.cuba.gui.components.Frame;
import com.haulmont.cuba.gui.components.VBoxLayout;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import com.haulmont.cuba.security.global.UserSession;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
public class MultiTaskFrame extends AbstractFrame {
@Inject
protected ComponentsFactory componentsFactory;
@Inject
protected ReferenceToEntitySupport referenceToEntitySupport;
@Inject
protected UserSession userSession;
@Inject
protected DataManager dataManager;
@Inject
protected VBoxLayout frameBox;
protected List<ExtProcActionsFrame> openedFrames = new ArrayList<>();
protected ProcInstance procInstance = null;
protected ProcAction.BeforeActionPredicate beforeStartProcessPredicate;
protected ProcAction.BeforeActionPredicate beforeCompleteTaskPredicate;
protected ProcAction.BeforeActionPredicate beforeClaimTaskPredicate;
protected ProcAction.BeforeActionPredicate beforeCancelProcessPredicate;
protected ProcAction.AfterActionListener afterStartProcessListener;
protected ProcAction.AfterActionListener afterCompleteTaskListener;
protected ProcAction.AfterActionListener afterClaimTaskListener;
protected ProcAction.AfterActionListener afterCancelProcessListener;
protected boolean startProcessEnabled = true;
protected boolean cancelProcessEnabled = false;
protected boolean completeTaskEnabled = true;
protected boolean claimTaskEnabled = true;
protected boolean taskInfoEnabled = true;
protected String buttonWidth = "150px";
protected static String extFrameAllias = "procActionsFrame.ext";
public void init(ProcInstance procInstance){
if (procInstance == null){ //TODO starting new process NOT supported
return;
}else{
this.procInstance = procInstance;
for(Long i = 0l; i<getTaskCount();i++){
Frame currFrameComponent = componentsFactory.createComponent(Frame.class);
ExtProcActionsFrame currFrame = (ExtProcActionsFrame)openFrame(currFrameComponent,extFrameAllias);
currFrame.setTaskNum(i.intValue());
ProcActionsFrame.Initializer currInitializer = currFrame.initializer();
copyFields(currInitializer);
currInitializer.init(procInstance);
openedFrames.add(currFrame);
frameBox.add(currFrameComponent);
}
}
}
public void init(String procCode, Entity entity){
ProcDefinition procDefinition = findProcDefinition(procCode);
if (procDefinition == null){
return;
}else{
init(findProcInstance(procDefinition, entity));
}
}
/**
* Clears (//hides in fact) opened task-frames and reloads new ones <br>
* Called by default (if null) in AfterCompleteTaskListener <br>
*/
public void reload(){
for(ExtProcActionsFrame frame : openedFrames){
frame.getParent().setVisible(false);
}
openedFrames.clear();
init(this.procInstance);
}
/**
* @return number of tasks for that (or substituted) user for input procInstance
*/
private Long getTaskCount() {
if (this.procInstance == null){
return null;
}else {
LoadContext<ProcTask> lc = new LoadContext<>(ProcTask.class);
lc.setQueryString("select pt from bpm$ProcTask pt left join pt.procActor pa left join pa.user pau " +
"where pt.procInstance.id = :procInstance and (pau.id = :userId or " +
"(pa is null and exists(select pt2 from bpm$ProcTask pt2 join pt2.candidateUsers cu where pt2.id = pt.id and cu.id = :userId))) " +
"and pt.endDate is null")
.setParameter("procInstance", procInstance)
.setParameter("userId", userSession.getCurrentOrSubstitutedUser());
return dataManager.getCount(lc);
}
}
/**
* Copying setted parameters to opened task-frame
* @param initializer Initializer of ExtProcActionsFrame
*/
protected void copyFields(ProcActionsFrame.Initializer initializer){
initializer.setAfterCancelProcessListener(afterCancelProcessListener);
initializer.setAfterClaimTaskListener(afterClaimTaskListener);
initializer.setAfterCompleteTaskListener(afterCompleteTaskListener == null ? this::reload : afterCompleteTaskListener);
initializer.setAfterStartProcessListener(afterStartProcessListener);
initializer.setBeforeCancelProcessPredicate(beforeCancelProcessPredicate);
initializer.setBeforeClaimTaskPredicate(beforeClaimTaskPredicate);
initializer.setBeforeCompleteTaskPredicate(beforeCompleteTaskPredicate);
initializer.setBeforeStartProcessPredicate(beforeStartProcessPredicate);
initializer.setStartProcessEnabled(startProcessEnabled);
initializer.setCancelProcessEnabled(cancelProcessEnabled);
initializer.setCompleteTaskEnabled(completeTaskEnabled);
initializer.setClaimTaskEnabled(claimTaskEnabled);
initializer.setTaskInfoEnabled(taskInfoEnabled);
initializer.setButtonWidth(buttonWidth);
}
@Nullable //ctrl+c ctfl+v from ProcActionsFrame
protected ProcDefinition findProcDefinition(String processCode) {
LoadContext ctx = LoadContext.create(ProcDefinition.class);
ctx.setQueryString("select pd from bpm$ProcDefinition pd where pd.code = :code")
.setParameter("code", processCode);
return (ProcDefinition) dataManager.load(ctx);
}
@Nullable //ctrl+c ctfl+v from ProcActionsFrame
protected ProcInstance findProcInstance(ProcDefinition procDefinition, Entity entity) {
String referenceIdPropertyName = referenceToEntitySupport.getReferenceIdPropertyName(entity.getMetaClass());
LoadContext<ProcInstance> ctx = LoadContext.create(ProcInstance.class).setView("procInstance-start");
ctx.setQueryString("select pi from bpm$ProcInstance pi where pi.procDefinition.id = :procDefinition and " +
"pi.entity." + referenceIdPropertyName + " = :entityId order by pi.createTs desc")
.setParameter("procDefinition", procDefinition)
.setParameter("entityId", referenceToEntitySupport.getReferenceId(entity));
List<ProcInstance> list = dataManager.loadList(ctx);
return list.isEmpty() ? null : list.get(0); //TODO MultiProcSupport
}
/*
* Options and hooks methods
*/
public MultiTaskFrame setBeforeStartProcessPredicate(ProcAction.BeforeActionPredicate predicate) {
this.beforeStartProcessPredicate = predicate;
return this;
}
public MultiTaskFrame setBeforeCompleteTaskPredicate(ProcAction.BeforeActionPredicate beforeCompleteTaskPredicate) {
this.beforeCompleteTaskPredicate = beforeCompleteTaskPredicate;
return this;
}
public MultiTaskFrame setBeforeClaimTaskPredicate(ProcAction.BeforeActionPredicate beforeClaimTaskPredicate) {
this.beforeClaimTaskPredicate = beforeClaimTaskPredicate;
return this;
}
public MultiTaskFrame setBeforeCancelProcessPredicate(ProcAction.BeforeActionPredicate beforeCancelProcessPredicate) {
this.beforeCancelProcessPredicate = beforeCancelProcessPredicate;
return this;
}
public MultiTaskFrame setAfterStartProcessListener(ProcAction.AfterActionListener afterStartProcessListener) {
this.afterStartProcessListener = afterStartProcessListener;
return this;
}
public MultiTaskFrame setAfterCompleteTaskListener(ProcAction.AfterActionListener afterCompleteTaskListener) {
this.afterCompleteTaskListener = afterCompleteTaskListener;
return this;
}
public MultiTaskFrame setAfterClaimTaskListener(ProcAction.AfterActionListener afterClaimTaskListener) {
this.afterClaimTaskListener = afterClaimTaskListener;
return this;
}
public MultiTaskFrame setAfterCancelProcessListener(ProcAction.AfterActionListener afterCancelProcessListener) {
this.afterCancelProcessListener = afterCancelProcessListener;
return this;
}
public MultiTaskFrame setStartProcessEnabled(boolean startProcessEnabled) {
this.startProcessEnabled = startProcessEnabled;
return this;
}
public MultiTaskFrame setCancelProcessEnabled(boolean cancelProcessEnabled) {
this.cancelProcessEnabled = cancelProcessEnabled;
return this;
}
public MultiTaskFrame setCompleteTaskEnabled(boolean completeTaskEnabled) {
this.completeTaskEnabled = completeTaskEnabled;
return this;
}
public MultiTaskFrame setClaimTaskEnabled(boolean claimTaskEnabled) {
this.claimTaskEnabled = claimTaskEnabled;
return this;
}
public MultiTaskFrame setTaskInfoEnabled(boolean taskInfoEnabled) {
this.taskInfoEnabled = taskInfoEnabled;
return this;
}
public MultiTaskFrame setButtonWidth(String buttonWidth) {
this.buttonWidth = buttonWidth;
return this;
}
}
Screen
test project (158.6 KB)
1 Like
Since from 6.9.* there is no findCurrentUserProcTask
method in ProcActionsFrame
so i made some workaround if someone would be interested:
ExtProcActionsFrame
public class CustomTaskProcActionsFrame extends ProcActionsFrame {
@Inject
protected BpmEntitiesService baseBpmEntitiesService;
@Inject
protected UserSession userSession;
protected int taskNum = 0;
public void setTaskNum(int num){
this.taskNum = num;
}
public int getTaskNum() {
return taskNum;
}
@Override
public void init(Map<String, Object> params){
bpmEntitiesService = new BpmEntitiesService() {
@Nullable
@Override
public ProcDefinition findProcDefinitionByCode(String procDefinitionCode, String viewName) {
return baseBpmEntitiesService.findProcDefinitionByCode(procDefinitionCode, viewName);
}
@Override
public List<ProcInstance> findActiveProcInstancesForEntity(String procDefinitionCode, Entity entity, String viewName) {
return baseBpmEntitiesService.findActiveProcInstancesForEntity(procDefinitionCode, entity, viewName);
}
@Override
public List<ProcTask> findActiveProcTasks(ProcInstance procInstance, User user, String viewName) {
return baseBpmEntitiesService.findActiveProcTasks(procInstance, user, viewName);
}
@Nullable
@Override
public ProcRole findProcRole(String procDefinitionCode, String procRoleCode, String viewName) {
return baseBpmEntitiesService.findProcRole(procDefinitionCode, procRoleCode, viewName);
}
@Override
public List<ProcTask> findActiveProcTasksForCurrentUser(ProcInstance procInstance, String viewName) {
List<ProcTask> result = new ArrayList<>();
ProcTask procTask = findCurrentUserProcTask();
if(procTask != null) result.add(procTask);
return result;
}
/**
* Creates a new not-persisted ProcInstance according to the information from the {@code procInstanceDetails}
*
* @param procInstanceDetails
*/
@Override
public ProcInstance createProcInstance(ProcInstanceDetails procInstanceDetails) {
return baseBpmEntitiesService.createProcInstance(procInstanceDetails);
}
};
super.init(params);
}
/**
* Finds a task with a proper index number
* @return indexed procTask or null if there is no such index
*/
protected ProcTask findCurrentUserProcTask() {
List<ProcTask> result = baseBpmEntitiesService.findActiveProcTasksForCurrentUser(procInstance, BpmConstants.Views.PROC_TASK_COMPLETE);
return result.isEmpty() || result.size() < taskNum ? null : result.get(taskNum);
}
}