Wednesday, July 2, 2014

How to achieve reverse navigation flow within user Tasks in aciviti BPM



Achieving reverse workflow movement is tricky in BPM workflow. I tried such use case. I am providing potential approach for same in below write-up.

Reverse flow for sequential workflow




Reverse flow for Parallel workflow




Reverse flow can be achieved in  activiti task  using adding conditional links with appropriate source references.

e.g flow can be directed from Pay back to First task using conditions like

${sourceEvent == 'XXX'} etc.

flow can be directed from Xtask to approve using appropriate conditional statements.

Parallel gateway have behavior of  fork and join.

So extra logic need to build to keep taking of waiting and completed tasks.


Please see attached sample BPMN and Test case to run this logic.

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="SampleReverse" name="SampleReverse" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="FirstLevel" name="FirstLevel"></userTask>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow1" sourceRef="FirstLevel" targetRef="exclusivegateway1"></sequenceFlow>
    <userTask id="SecondLevel" name="SecondLevel"></userTask>
    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow2" sourceRef="SecondLevel" targetRef="exclusivegateway2"></sequenceFlow>
    <userTask id="ThirdLevel" name="ThirdLevel"></userTask>
    <sequenceFlow id="flow3" sourceRef="exclusivegateway2" targetRef="ThirdLevel">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${backButton == 'false'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow4" sourceRef="exclusivegateway1" targetRef="SecondLevel"></sequenceFlow>
    <sequenceFlow id="flow6" sourceRef="startevent1" targetRef="FirstLevel"></sequenceFlow>
    <sequenceFlow id="flow7" sourceRef="exclusivegateway2" targetRef="FirstLevel">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${backButton == 'false'}]]></conditionExpression>
    </sequenceFlow>
    <exclusiveGateway id="exclusivegateway3" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow8" sourceRef="ThirdLevel" targetRef="exclusivegateway3"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow9" sourceRef="exclusivegateway3" targetRef="endevent1">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${backButton == 'false'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow10" sourceRef="exclusivegateway3" targetRef="SecondLevel">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${backButton == 'false'}]]></conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="70.0" y="111.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="FirstLevel" id="BPMNShape_FirstLevel">
        <omgdc:Bounds height="55.0" width="105.0" x="150.0" y="100.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="287.0" y="107.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="SecondLevel" id="BPMNShape_SecondLevel">
        <omgdc:Bounds height="55.0" width="105.0" x="350.0" y="100.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
        <omgdc:Bounds height="40.0" width="40.0" x="488.0" y="107.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="ThirdLevel" id="BPMNShape_ThirdLevel">
        <omgdc:Bounds height="55.0" width="105.0" x="563.0" y="100.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway3" id="BPMNShape_exclusivegateway3">
        <omgdc:Bounds height="40.0" width="40.0" x="699.0" y="107.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="780.0" y="110.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="455.0" y="127.0"></omgdi:waypoint>
        <omgdi:waypoint x="488.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="528.0" y="127.0"></omgdi:waypoint>
        <omgdi:waypoint x="563.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="327.0" y="127.0"></omgdi:waypoint>
        <omgdi:waypoint x="350.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
        <omgdi:waypoint x="105.0" y="128.0"></omgdi:waypoint>
        <omgdi:waypoint x="150.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
        <omgdi:waypoint x="508.0" y="147.0"></omgdi:waypoint>
        <omgdi:waypoint x="507.0" y="204.0"></omgdi:waypoint>
        <omgdi:waypoint x="115.0" y="204.0"></omgdi:waypoint>
        <omgdi:waypoint x="115.0" y="128.0"></omgdi:waypoint>
        <omgdi:waypoint x="150.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
        <omgdi:waypoint x="668.0" y="127.0"></omgdi:waypoint>
        <omgdi:waypoint x="699.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
        <omgdi:waypoint x="739.0" y="127.0"></omgdi:waypoint>
        <omgdi:waypoint x="780.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
        <omgdi:waypoint x="719.0" y="147.0"></omgdi:waypoint>
        <omgdi:waypoint x="718.0" y="187.0"></omgdi:waypoint>
        <omgdi:waypoint x="321.0" y="187.0"></omgdi:waypoint>
        <omgdi:waypoint x="321.0" y="129.0"></omgdi:waypoint>
        <omgdi:waypoint x="350.0" y="127.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>



Test case:

package org.activiti.designer.test;

import static org.junit.Assert.assertNotNull;

import java.io.FileInputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMyProcessReverse {

private String filename = "D:/Development/TestProject/TestBPM/src/main/resources/diagrams/MyProcessReverse.bpmn";


@Test
public void startProcess() {

try{

ClassPathXmlApplicationContext applicationContext = 
new ClassPathXmlApplicationContext("spring-test-config.xml");

// review : shiv,sunita
// verify : soumya
// approve : vishal

RepositoryService repositoryService = (RepositoryService) applicationContext.getBean("repositoryService");
repositoryService.createDeployment().addInputStream(filename,
new FileInputStream(filename)).deploy();
RuntimeService runtimeService = (RuntimeService) applicationContext.getBean("runtimeService");
TaskService taskService = (TaskService) applicationContext.getBean("taskService");

Map<String, Object> variableMap = new HashMap<String, Object>();
variableMap.put("fromBackButton", "false");
variableMap.put("invoiceId", "10");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcessReverse","10", variableMap);
assertNotNull(processInstance.getId());
System.out.println("id " + processInstance.getId() + " "
+ processInstance.getProcessDefinitionId());

List<Task> taskList = taskService.createTaskQuery().includeProcessVariables().list();
List<Task> invoiceUserList = taskService.createTaskQuery().taskCandidateUser("shiv").includeTaskLocalVariables().includeProcessVariables().list();
System.out.println("invoiceUserList:fisrtLevel:shiv::"+invoiceUserList);

for(Task task : invoiceUserList ){
       
System.out.println("Taskid for first level:"+task.getId());
variableMap.put("fromBackButton", "false");
taskService.complete(task.getId(),variableMap);
        System.out.println("First TASK COMPLETED");
}

taskList = taskService.createTaskQuery().includeProcessVariables().list();
invoiceUserList = taskService.createTaskQuery().taskCandidateUser("soumya").includeTaskLocalVariables().includeProcessVariables().list();
System.out.println("invoiceUserList:fisrtLevel:soumya::"+invoiceUserList);

for(Task task : invoiceUserList ){
   
System.out.println("Taskid for second level"+task.getId());
variableMap.put("fromBackButton", "true");
taskService.complete(task.getId(),variableMap);
    System.out.println("First TASK Reversed");
}

taskList = taskService.createTaskQuery().includeProcessVariables().list();
invoiceUserList = taskService.createTaskQuery().taskCandidateUser("soumya").includeTaskLocalVariables().includeProcessVariables().list();
System.out.println("I am moving to next level");



for(Task task : invoiceUserList ){
       
System.out.println("Taskid moving to second level again:"+task.getId());
variableMap.put("fromBackButton", "false");
taskService.complete(task.getId(),variableMap);
        System.out.println("Taskid moved to second level again");
        invoiceUserList = taskService.createTaskQuery().taskCandidateUser("soumya").includeTaskLocalVariables().includeProcessVariables().list();
}

} catch (Throwable e){
e.printStackTrace();
}
}
}








No comments:

Post a Comment