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:
Click on "Apply (Run Process)" menu and submit. The app has set the approvers by default to "admin" and "cat".
Login as "admin" and "cat" to perform then approval.
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
Related Elements
Attachments:










