Knowledge Base for DX 7 : Demonstration Multiple Approval App For Joget DX


Introduction

Attached is a demonstation app on multiple approvers for Joget DX. This app gives you an example on how to create an approval flow that requires ALL approvers to "Approve" the request and not just one approver. If anyone rejects the request, the whole approval process would be rejected.

Steps:

  1. Click on "Apply (Run Process)" menu and submit. The app has set the approvers by default to "admin" and "cat".

  2. Login as "admin" and "cat" to perform then approval.

  3. In the final activity, you can view the audit trail of all the approvers. Click "Complete" to end the process.

For the process design, we would require two separate workflow processes: "Approve" and "Apply".

The demonstration Multiple Approver app for Joget v6 or earlier will not work in Joget DX if the "Run Process Primary Key" is set to "UUID" ( see General Settings > Run Process Primary Key ). If you are using Joget DX or newer, do download and use the attached app below.



Figure 1: The main process Apply Process


Figure 2: Individual Approval Process flow


Figure 3: Mapping of ApproverGroup

Each of the "Approver" process instances would gather decision from the respective Approver. Then, Update Application will execute to trigger Waiting For Response activity in the parent process ( Apply" Process ) which will then execute the Process Approval tool to evaluate if it is enough to make a final decision and move forward.

Process Tool: BeanShell Code Samples

Generate Approval

import org.joget.workflow.model.service.WorkflowManager;
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.app.service.AppService;
import org.joget.workflow.model.WorkflowAssignment;
import org.joget.workflow.util.WorkflowUtil;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.joget.workflow.model.WorkflowProcess;
import org.joget.workflow.model.WorkflowProcessResult;
 
//constant value
String processDefKey = "approve";
String rowCountVariableName = "approvalCount";
String approverGroupParticipantId = "approverGroup";
String approvalIdsVariableName = "approvalIds";
 
//utility bean
WorkflowManager workflowManager = (WorkflowManager) AppUtil.getApplicationContext().getBean("workflowManager");
AppService appService = (AppService) AppUtil.getApplicationContext().getBean("appService");
 
//get processDefId
WorkflowProcess processDef = appService.getWorkflowProcessForApp(appDef.getId(), appDef.getVersion().toString(), processDefKey);
String processDefId = processDef.getId();
 
//get foreign key
String processId = workflowAssignment.getProcessId();
 
int rowCount = 0;
Collection userList = null;
userList = WorkflowUtil.getAssignmentUsers(appDef.getAppId(), workflowAssignment.getProcessDefId(), workflowAssignment.getProcessId(), workflowAssignment.getProcessVersion(), workflowAssignment.getActivityId(), "", approverGroupParticipantId);
 
String approvalInstanceIds = "";
for(String user : userList){
    System.out.println(user);
    Map variables = new HashMap();
    variables.put("apply_id", processId);
    variables.put("record_id", "#process.recordId#");
    variables.put("approver", user);
    WorkflowProcessResult result = workflowManager.processStart(processDefId, null, variables, "admin", null, false);
    approvalInstanceIds += result.getProcess().getInstanceId() + ",";
    rowCount++;
}
 
//set row count to workflow variable
workflowManager.processVariable(processId, rowCountVariableName, Integer.toString(rowCount));
 
//keep the list of approval instances
workflowManager.processVariable(processId, approvalIdsVariableName, approvalInstanceIds);

Update Application

import java.util.*;
import org.joget.apps.app.service.*;
import org.joget.workflow.model.*;
import org.joget.workflow.model.service.*;

//constant value
String processInstanceId = "#variable.apply_id#";
String activityDefId = "waitingForResponse";

//utility bean
WorkflowManager workflowManager = (WorkflowManager) AppUtil.getApplicationContext().getBean("workflowManager");  
WorkflowUserManager workflowUserManager = (WorkflowUserManager) AppUtil.getApplicationContext().getBean("workflowUserManager");

//get current user username and temporary set current user to roleAnonymous to get the assignment
String username = workflowUserManager.getCurrentUsername();
workflowUserManager.setCurrentThreadUser("roleAnonymous");

//get assignment
Collection assignments = workflowManager.getAssignmentList(null, null, processInstanceId, activityDefId, null, null, null, 1);

if (assignments != null && !assignments.isEmpty()) {
    WorkflowAssignment ass = (WorkflowAssignment) assignments.iterator().next();
    String actId = ass.getActivityId();
    //accept and complete assignment
    workflowManager.assignmentAccept(actId);
    workflowManager.assignmentComplete(actId, null);
}

//set the current user back to original
workflowUserManager.setCurrentThreadUser(username);

Process Approval

import org.joget.apps.app.service.*;
import org.joget.apps.form.dao.*;
import org.joget.apps.form.model.*;
import org.joget.apps.form.service.*;
import org.joget.workflow.model.*;
import org.joget.workflow.model.service.*;

//constant value
//String foreignKey = "customProperties.apply_id";
String foreignKey = "customProperties.record_id";
String formDefId = "approveForm";
String tableName = "multiApproval_approv";
String rowCountVariableName = "approvalCount";
String statusVariableName = "status";
int approvalCount = Integer.parseInt("#variable.approvalCount#");
String approvalIds = "#variable.approvalIds#";

//utility bean
FormDataDao formDataDao = (FormDataDao) AppUtil.getApplicationContext().getBean("formDataDao");   
WorkflowManager workflowManager = (WorkflowManager) AppUtil.getApplicationContext().getBean("workflowManager");  
AppService appService = (AppService) AppUtil.getApplicationContext().getBean("appService");  

//get foreign key
String processId = workflowAssignment.getProcessId();
String recordId = "#process.recordId#";


//build condition
String condition = " WHERE " + foreignKey + " = ?";
Object[] paramsArray = new Object[]{recordId};

//get approval data
FormRowSet rows = new FormRowSet();            
rows = formDataDao.find(formDefId, tableName, condition, paramsArray, "dateCreated", false, null, null);

int rowCount = 0;
String status = "";
String recordId = "";
for (FormRow r : rows) {
    recordId = r.getId();
    String recordStatus = r.get("status");
	
	if(recordStatus.equalsIgnoreCase("Rejected")){
		status = "Rejected";
		break;
	}
    rowCount++;
}

if(status.equalsIgnoreCase("Rejected")){
	workflowManager.processVariable(processId, statusVariableName, "Rejected");
	//terminate any remaining approval instances
	String[] approvalIdsSplit = approvalIds.split(",");
	for(String approvalId : approvalIdsSplit){
		if(!approvalId.equalsIgnoreCase("") && !approvalId.equalsIgnoreCase(recordId)){
			try{
				workflowManager.processAbort(approvalId);
			}catch(Exception e){
			}			
		}
	}
}else if(rowCount == approvalCount){
	workflowManager.processVariable(processId, statusVariableName, "Approved");
}



Figure 4: View of instances after submitting the "Apply" activity with all the "Approve" process spawned and completed


Figure 5: Final view of the Apply process

Download Demo App

APP_multi_approval_dx_kb.jwa


Related Elements


Attachments:

approve.jpg (image/jpeg)
apply.jpg (image/jpeg)
approverGroup.png (image/png)
instances.png (image/png)
trail Of Approval.png (image/png)
APP_multiApproval.zip (application/zip)
image2020-1-13_19-14-4.png (image/png)
image2020-1-13_19-15-23.png (image/png)
image2020-1-13_19-19-34.png (image/png)
APP_multi_approval_dx_kb.jwa (application/octet-stream)
image2020-1-13_19-24-43.png (image/png)