ApprovalAssignment rule logic

Which IIQ version are you inquiring about?

8.5

Share all details about your problem, including any error messages you may have received.

We have this ApprovalAssignment rule in LCM - Provisioning workflow. We are seeing an issue where if a manager submits multiple requests in one access request (that go to separate owners), the first owner to approve ends up canceling the other request.

I have tried other iterations of childApproval.setMode(“parallelPoll”); to set the approvalMode, but it doesn’t seem to change it or override the initialized approvalMode.

Curious if anyone sees something I might be missing in the code below:

    import sailpoint.object.ApprovalItem;
    import sailpoint.object.Filter;
    import sailpoint.object.Identity;
    import sailpoint.object.QueryOptions;
    import sailpoint.object.Workflow;
    import sailpoint.object.WorkItem;
    import sailpoint.object.ApprovalSet;
    import java.util.Date; 
    import java.util.List;
    import java.util.ArrayList;
    import java.util.Iterator;

    List finalApprovals = new ArrayList();
    List approvalItems = approvalSet.getItems();

    Identity launcherIdentity = context.getObjectByName(Identity.class, launcher);

    // Process each approval item individually
    for (ApprovalItem approvalItem : approvalItems) {
      String approvalItemOperation = approvalItem.getOperation(); 
      String owner = approvalItem.getOwner();

      if ("Remove".equals(approvalItemOperation)) {
        approvalItem.approve();
        approvalItem.setState(WorkItem.State.Finished);
        approvalItem.setOwner("spadmin");
        finalApprovals.add(approvalItem);    
      } else { 
        // launcherIsMember logic
        QueryOptions q = new QueryOptions();
        q.add(Filter.eq("workgroups.name", owner));

        boolean launcherIsMember = false;
        List members = new ArrayList();

        Iterator i = context.search(Identity.class, q, "name");
        while (i.hasNext()) {
          String member = i.next()[0];
          members.add(member);
          if (member.equalsIgnoreCase(launcherIdentity.getName())) {
            launcherIsMember = true;
          }
        }

        ApprovalSet singleApprovalSet = new ApprovalSet();
        singleApprovalSet.add(approvalItem);

        if (launcherIsMember) {
          for (String memberName : members) {
            if (memberName.equalsIgnoreCase(launcherIdentity.getName()))
              continue;

            Workflow.Approval childApproval = new Workflow.Approval();
            childApproval.setOwner(memberName);
            childApproval.setDescription("Owner Approval - Account Changes for User: " + identityDisplayName);
            childApproval.addArg("workItemTargetClass", "sailpoint.object.Identity");
            childApproval.addArg("workItemTargetName", identityName);
            childApproval.addArg("workItemNotificationTemplate", identityEmailTemplate);
            childApproval.setApprovalSet(singleApprovalSet);
            //childApproval.setMode(Workflow.ApprovalModeAny);
            //childApproval.setMode(Workflow.ApprovalModeParallelPoll); //testing
            //childApproval.setMode("any");
            childApproval.setMode("parallelPoll");

            finalApprovals.add(childApproval);
          }
        } else {
          Workflow.Approval ownerApproval = new Workflow.Approval();
          ownerApproval.setOwner(owner);
          ownerApproval.setDescription("Owner Approval - Account Changes for User: " + identityDisplayName);
          ownerApproval.addArg("workItemTargetClass", "sailpoint.object.Identity");
          ownerApproval.addArg("workItemTargetName", identityName);
          ownerApproval.addArg("workItemNotificationTemplate", identityEmailTemplate);
          //ownerApproval.setMode(Workflow.ApprovalModeParallelPoll); 
          ownerApproval.setMode("parallelPoll");
          ownerApproval.setApprovalSet(singleApprovalSet);

          finalApprovals.add(ownerApproval);
        }
      }
    }

    return finalApprovals;

Thanks!

You might need to wrap these final approvals in another parent approval.

        parentApproval = new Approval();
        parentApproval.setChildren(finalApprovals);
        parentApproval.setMode("parallelPoll");
        newApprovalsFinal = new ArrayList();
        newApprovalsFinal.add(parentApproval);
        return newApprovalsFinal ;

Regards
Ankush

@supakij The root cause here is that setMode(“parallelPoll”) on each individual child approval controls how that approval’s own children are evaluated — it doesn’t control how the workflow engine handles the list you return from the ApprovalAssignment rule. When you return a flat List of Workflow.Approval objects, the LCM Provisioning workflow iterates through them sequentially by default, so the first approval completing triggers completion logic that cancels the rest.

The fix is to wrap all your finalApprovals in a single parent approval with parallelPoll mode, exactly as @shirbhatea suggested. Here’s the full picture of why and how:

Why setMode on the children doesn’t work

Each child approval’s mode only governs how its own sub-children are polled. The parent container (the list you return) has no mode set, so the workflow treats the returned list as a sequential approval chain. The first owner to act completes the parent, which cascades a cancellation to the remaining siblings.

The fix — wrap in a parent approval

After your loop builds finalApprovals, do this before returning:

Workflow.Approval parentApproval = new Workflow.Approval();
parentApproval.setMode("parallelPoll");
parentApproval.setChildren(finalApprovals);

List wrappedApprovals = new ArrayList();
wrappedApprovals.add(parentApproval);
return wrappedApprovals;

This tells the workflow engine to evaluate all child approvals in parallel — each owner acts independently, and one approval completing does not cancel the others.

One additional thing to verify

Make sure the LCM Provisioning workflow step that calls your ApprovalAssignment rule has its own approval mode set to “parallelPoll” as well (check the Approval step’s approvalMode variable in the workflow XML). If the step-level mode is hardcoded to “serial” or “any”, it will override what your rule returns regardless of parent wrapping.

Hope that resolves it!

@supafongboon Try setting approvalMode to true and enable split provisioning. In that way as soon as first level is approved, IIQ will create multiple independent work items for 2nd level approval, which can go for provisioning as soon as they are approved and decision of one workitem, will not impact the other workitem.