WorkItem Creation During remove operation in forms

Hi Everyone,

I am working on a QuickLink where it will be updating a custom object (add and remove) operations.
Now If the remove operation is performed - instead of directly removing the entry or selected data I want to create a workitem and if approved remove the data if not just end the flow without making any changes. Can anyone guide me with this issue.

below is the flow I have without the workitem creation

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Workflow PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Workflow created="1758885015689" explicitTransitions="true" id="c0a8380199851b45819985b770890036" modified="1758894454691" name="CustomObject_Add_Preventative_SOD_Flow_Updated" type="">
  <Variable editable="true" name="identityModel"/>
  <Variable name="workItemFormBasePath"/>
  <Variable initializer="false" name="identityAttributesVisible"/>
  <Variable editable="true" name="policyModel"/>
  <Variable editable="true" name="removeSelection"/>
  <Variable editable="true" name="userAction"/>
  <Variable input="true" name="policyTemplates" output="true" type="List"/>
  <Variable initializer="spadmin" input="true" name="fallbackApprover">
    <Description>A String that specifies the name of the Identity that will 
      be assigned any approvals where the owner of the approver 
      can&amp;#39;t be resolved. Example if the scheme is &amp;quot;owner&amp;quot; and the 
      application doesn&amp;#39;t specify and owner.</Description>
  </Variable>
  <Variable input="true" name="workItemPriority">
    <Description>String version of WorkItem.level that will be used
      to set the priority of any generated approval
      workitems.</Description>
  </Variable>
  <Variable input="true" name="approverElectronicSignature">
    <Description>The name of the electronic signature object that should be used when workitems
      are completed by the batch approver when performing batch operations.</Description>
  </Variable>
  <Variable initializer="serial" input="true" name="approvalMode">
    <Description>A string that specifies how we should handle the approvals.
  By default this is serial since most of these request with
  the exception of manager transfers will have only one approver.

  parallel
  Approvals are processed concurrently and there must be consensus,
  we wait for all approvers to approve.  The first approver that
  rejects terminates the entire approval.

  parallelPoll
  Approvals are processed concurrently but consensus is not required.
  All approvals will be processed, we don&amp;#39;t stop if there are any
  rejections.

  serial
  Approvals are processed one at a time and there must be consensus.
  The first approver that rejects terminates the entire approval.

  serialPoll
  Approvals are processed in order but consensus is not required.
  All approvals will be processed, we don&amp;#39;t stop if there are any
  rejections.  In effect we are &amp;quot;taking a poll&amp;quot; of the approvers.

  any
  Approvals are processed concurrently, the first approver to
  respond makes the decision for the group.</Description>
</Variable>
  <Variable name="approvalForm">
    <Description>The generated form that is displayed to the user during the approval.</Description>
  </Variable>
  <Variable input="true" name="fallbackApprover">
    <Description>A String that specifies the name of the Identity that will
      be assigned any manual actions  where the owner of the approver
      can&amp;#39;t be resolved.</Description>
  </Variable>
  <Variable input="true" name="approvalSet" output="true">
    <Description>This variable includes all ApprovalItems that are part of
      the request process and is updated during the AfterScript
      of the approval process by assimilating the decisions
      and comments from the Approvals copy of the ApprovalItem.</Description>
  </Variable>
  <Variable input="true" name="approvers">
    <Description>Owners that should recieve the approval.</Description>
  </Variable>
  <Variable input="true" name="approvalScheme">
    <Description>A String that specifies how approvals should be generated for
      this workflow there are five built-in modes
  none - disabled approvals

  manager - The user&amp;#39;s current manager will get approvals

  newManager - The newly assigned manager will get approvals when
  manager transfers occur. Otherwise the user&amp;#39;s manager
  current manager will be the approver.

  securityOfficer - The identity in the securityOfficerName variable
  will get approvals.</Description>
</Variable>
  <Step icon="Start" name="Start" posX="50" posY="50">
    <Transition to="Policy_Email_Selection dummy"/>
  </Step>
  <Step icon="Default" name="Policy_Email_Selection dummy" posX="160" posY="222">
    <Approval name="Policy_Email_Selection_Form" owner="spadmin" return="policyModel,userAction" send="">
      <Form name="Policy_Email_Selection_Form">
        <Attributes>
          <Map>
            <entry key="hideIncompleteFields">
              <value>
                <Boolean></Boolean>
              </value>
            </entry>
            <entry key="includeHiddenFields">
              <value>
                <Boolean></Boolean>
              </value>
            </entry>
            <entry key="pageTitle" value="Policy_Email_Selection_Form"/>
          </Map>
        </Attributes>
        <Section label="Existing Policies" name="ExistingPoliciesSection" type="text">
          <Field>
            <Script>
              <Source>
                import sailpoint.object.Custom;
                import java.util.*;
                System.out.println("Entered the table page");
            StringBuilder sb = new StringBuilder("");
            Custom customObj = context.getObjectByName(Custom.class, "New_PreventativeSOD_Policies");

            sb.append("&lt;html>");
            sb.append("&lt;div style='overflow:auto; max-height:400px;'>");
            sb.append("&lt;table width='100%' border='1' cellpadding='6' cellspacing='0' style='border-collapse:collapse;'>");

            sb.append("&lt;tr style='background-color:#024069; color:white;'>");
            sb.append("&lt;th style='border:1px solid black;'>Policy Name&lt;/th>");
            sb.append("&lt;th style='border:1px solid black;'>Email Template&lt;/th>");
            sb.append("&lt;/tr>");

            if (customObj != null) {
            Map attrs = customObj.getAttributes();
            if (attrs != null) {
            Map policies = (Map) attrs.get("policies");
            if (policies != null &amp;&amp; !policies.isEmpty()) {
            for (Object key : policies.keySet()) {
            String policyName = key.toString();
            Object emailTemplate = policies.get(key);

            sb.append("&lt;tr style='background-color:#FFFFFF; color:black;'>");
            sb.append("&lt;td style='border:1px solid black;'>").append(policyName).append("&lt;/td>");
            sb.append("&lt;td style='border:1px solid black;'>")
            .append(emailTemplate != null ? emailTemplate.toString() : "")
            .append("&lt;/td>");
            sb.append("&lt;/tr>");
            }
            } else {
            sb.append("&lt;tr>&lt;td colspan='2'>No existing policies found.&lt;/td>&lt;/tr>");
            }
            }
            } else {
            sb.append("&lt;tr>&lt;td colspan='2'>No existing policies found.&lt;/td>&lt;/tr>");
            }

            sb.append("&lt;/table>");
            sb.append("&lt;/div>");
            sb.append("&lt;/html>");

            System.out.println("Exit the table page");
            return sb.toString();
          </Source>
        </Script>
      </Field>
    </Section>
    <Button action="next" label="Add" parameter="userAction" value="add"/>
    <Button action="cancel" label="Cancel" parameter="userAction" value="cancel"/>
    <Button action="next" label="Remove" parameter="userAction" value="remove"/>
  </Form>
</Approval>
<Transition to="Policy_Email_Selection">
  <Script>
    <Source>
      if (userAction != null &amp;&amp; userAction.toString().equalsIgnoreCase("add")) {
      System.out.println("userAction first: " + userAction);
      return true;
      }
    </Source>
  </Script>
</Transition>
<Transition to="Remove_CustomObject"/>
</Step>
  <Step icon="Default" name="Policy_Email_Selection" posX="302" posY="68">
    <Approval name="Policy_Email_Selection_Form_Add" owner="spadmin" return="policyModel,userAction" send="">
      <Form name="Policy_Email_Selection_Form_Add">
        <Attributes>
          <Map>
            <entry key="pageTitle" value="Policy_Email_Selection_Form_Add"/>
          </Map>
        </Attributes>
        <Section name="Policy Details">
          <Field displayName="Policy Name" name="policyName" required="true" type="string">
            <AllowedValuesDefinition>
              <Script>
                <Source>
                  import sailpoint.object.Policy;
                  import sailpoint.object.Custom;
                  import java.util.*;
              List policyNames = new ArrayList();
              List policies = context.getObjects(Policy.class);
              for (Policy p : policies) {
              policyNames.add(p.getName());
              }

              Custom customObj = context.getObjectByName(Custom.class, "New_PreventativeSOD_Policies");
              Set existingPolicies = new HashSet();
              if (customObj != null) {
              Map attributes = customObj.getAttributes();
              if (attributes != null) {
              Map policiesMap = (Map) attributes.get("policies");
              if (policiesMap != null) {
              existingPolicies.addAll(policiesMap.keySet());
              }
              }
              }

              policyNames.removeAll(existingPolicies);
              Collections.sort(policyNames);
              return policyNames;
            </Source>
          </Script>
        </AllowedValuesDefinition>
      </Field>
      <Field displayName="Email Template" name="emailTemplate" required="true" type="string">
        <AllowedValuesDefinition>
          <Script>
            <Source>
              import sailpoint.object.EmailTemplate;
              import java.util.*;

              List emailTemplates = new ArrayList();
              List templates = context.getObjects(EmailTemplate.class);
              for (EmailTemplate t : templates) {
              emailTemplates.add(t.getName());
              }
              emailTemplates.add("None of the above");
              Collections.sort(emailTemplates);
              return emailTemplates;
            </Source>
          </Script>
        </AllowedValuesDefinition>
      </Field>
    </Section>
    <Button action="next" label="Submit"/>
    <Button action="cancel" label="Cancel" value="cancel"/>
  </Form>
</Approval>
<Arg name="workItemFormBasePath" value="policyModel"/>
<Transition to="Create_CustomObject"/>
</Step>
  <Step icon="Default" name="Create_CustomObject" posX="476" posY="47">
    <Script>
      <Source>
        import sailpoint.object.Custom;
        import java.util.*;
    try {
    String policyName = policyModel.get("policyName");
    String emailTemplate = policyModel.get("emailTemplate");

    Custom customObj = context.getObjectByName(Custom.class, "New_PreventativeSOD_Policies");
    if (customObj == null) {
    customObj = new Custom();
    customObj.setName("New_PreventativeSOD_Policies");
    }

    Map attributes = customObj.getAttributes();
    if (attributes == null) attributes = new HashMap();

    Map policiesMap = (Map) attributes.get("policies");
    if (policiesMap == null) policiesMap = new HashMap();

    policiesMap.put(policyName, emailTemplate);
    attributes.put("policies", policiesMap);

    customObj.setAttributes(new sailpoint.object.Attributes(attributes));
    context.saveObject(customObj);
    context.commitTransaction();

    return "Custom Object updated successfully with policy: " + policyName;
    } catch (Exception e) {
    return "Error: " + e.getMessage();
    }
  </Source>
</Script>
<Transition to="Audit_Log"/>
</Step>
  <Step icon="Default" name="Remove_CustomObject" posX="302" posY="316">
    <Approval name="Remove_Policy_Form" owner="spadmin" return="removeSelection,userAction" send="">
      <Form name="Remove_Policy_Form">
        <Attributes>
          <Map>
            <entry key="pageTitle" value="Remove_Policy_Form"/>
          </Map>
        </Attributes>
        <Section name="Remove Policies">
          <Field displayName="Select Policies to Remove" multi="true" name="removeSelection" required="true" type="string">
            <Script>
              <Source>
                import sailpoint.object.Custom;
                import java.util.*;
            List policiesList = new ArrayList();
            Custom customObj = context.getObjectByName(Custom.class, "New_PreventativeSOD_Policies");

            if (customObj != null) {
            Map attrs = customObj.getAttributes();
            if (attrs != null) {
            Map policies = (Map) attrs.get("policies");
            if (policies != null) {
            for (Object key : policies.keySet()) {
            policiesList.add(key.toString());
            }
            }
            }
            }
            return policiesList;
          </Source>
        </Script>
      </Field>
    </Section>
    <Button action="next" label="Submit" value="submit"/>
    <Button action="cancel" label="Cancel"/>
  </Form>
</Approval>
<Transition to="Process_Remove"/>
</Step>
  <Step icon="Default" name="Process_Remove" posX="577" posY="225">
    <Script>
      <Source>
        import sailpoint.object.Custom;
        import java.util.*;
    System.out.println("Entered Process_Remove");

    try {
    List removeList = (List) removeSelection;
    System.out.println("Requested removeList: " + removeList);

    Custom customObj = context.getObjectByName(Custom.class, "New_PreventativeSOD_Policies");
    if (customObj != null) {
    Map attrs = customObj.getAttributes();
    if (attrs != null) {
    Map policies = (Map) attrs.get("policies");
    if (policies != null) {
    System.out.println("Original policies: " + policies.keySet());

    Map selectedPolicies = new HashMap();
    for (Object selectedKeyObj : removeList) {
    String selectedKey = selectedKeyObj.toString().trim();
    if (policies.containsKey(selectedKey)) {
    selectedPolicies.put(selectedKey, policies.get(selectedKey));
    System.out.println("Keeping selected policy: " + selectedKey);
    } else {
    System.out.println("Selected policy not found in custom object: " + selectedKey);
    }
    }

    System.out.println("Final policies saved to custom object: " + selectedPolicies.keySet());

    attrs.put("policies", selectedPolicies);
    customObj.setAttributes(new sailpoint.object.Attributes(attrs));
    context.saveObject(customObj);
    context.commitTransaction();
    }
    }
    }
    return "Custom Object now contains only selected policies: " + removeList;
    } catch (Exception e) {
    System.out.println("Error in Process_Remove: " + e.getMessage());
    e.printStackTrace();
    return "Error removing: " + e.getMessage();
    }
  </Source>
</Script>
<Transition to="Audit_Log"/>
</Step>
  <Step icon="Default" name="Audit_Log" posX="753" posY="140">
    <Script>
      <Source>
        import sailpoint.object.AuditEvent;
        import sailpoint.server.Auditor;
        import java.text.SimpleDateFormat;
        import java.util.Date;
    System.out.println("Entered Audit_Log step");

    try {
    String action = "PreventativeSODPolicyUpdate";
    String source = "SH Lifecycle Event - Rehire Workflow";
    String target = "New_PreventativeSOD_Policies";
    String actor  = context.getUserName();

    String arg1 = "User Action: " + (userAction != null ? userAction.toString() : "unknown");
    String arg2 = (policyModel != null) 
    ? "Policy Added: " + policyModel.get("policyName") + ", Template: " + policyModel.get("emailTemplate") 
    : (removeSelection != null ? "Policies Removed: " + removeSelection.toString() : "No details");

    SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
    String currentDate = formatter.format(new Date());

    AuditEvent event = new AuditEvent();
    event.setAction(action);
    event.setSource(source);
    event.setTarget(target);
    event.setAttributeName("Operation Status");
    event.setAttributeValue("Success");
    event.setAttribute("Actor", actor);
    event.setAttribute("Details1", arg1);
    event.setAttribute("Details2", arg2);
    event.setAttribute("Creation Date", currentDate);

    Auditor.log(event);
    context.commitTransaction();

    System.out.println("Audit_Log step completed successfully");
    } catch (Exception e) {
    System.out.println("Error in Audit_Log step: " + e.getMessage());
    e.printStackTrace();
    }
    System.out.println("Exited Audit_Log step");
  </Source>
</Script>
<Transition to="Stop"/>
</Step>
  <Step icon="Stop" name="Stop" posX="991" posY="135"/>
</Workflow>

Hi @Yashwanti,
Below is basic example for Approval Step:

image

The workitem will be looks like below:


Thanks,
Harikrishna.

Hi @Yashwanti ,

Requirement is not clear , you have mentioned that you have created one quicklink and from there you are updating one custom file .

I am assuming there you are putting entitlement etc where for a certain operation workitem should open .

Now Remove operation can be placed from various sources . So exactly from where this remove operation is coming from . In LCM you want to generate the workitem or anywhere else .

Can you please elaborate little bit . First we need to identify the approach then we can come back to code and get it done or fixed.

Thanks.

I Have a custom object where I add Policies and email templates corresponding to them
Now I created a quicklink to operate that customobject. (Adding and removing entries from the custom object)
Adding into the custom object has no need for a peer review but for removing an entry from the custom object needs a peer review.

That is after selecting what to remove from the custom object, i want to create a workitem (to approve / reject that remove operation)
if that is approved then the entry is remove from custom object if not it will be the same

Hi @Yashwanti , Ok , Now its clear . So you need approval workitem when from quicklink someone try to remove entry from custom object .

What issue you are getting ?

In your quicklink workflow , create a step and approval workitem from there .

<Step name="Test" posX="782" posY="219">
    <Approval name="Test Review Form" owner="testUser" return="buttonActionNext,buttonActionReject,reviwerComments" send="identityModel,identityName">
  <Arg name="workItemDescription">
    <Script>
      <Source>
		return "Test";
      </Source>
	</Script>  
  </Arg>
  <Arg name="workItemNotificationTemplate" value="Test Email Template"/>
  <Form name="Test Form">
    <Attributes>
      <Map>
        <entry key="pageTitle" value="Test Form"/>
      </Map>
    </Attributes>
    <FormRef name="Test Form"/>
  </Form>
  <Transition to="Test Step"/>
  </Step>
</Approval>

Hi @Yashwanti

Use the ‘Add Approval’ option within the generic step to include custom approvals. In the following step, you can validate the action using ‘approve’ and design the workflow accordingly. Please refer to the screenshot below for guidance.

Thank you @harsh_gupta4 @vijaysuddala @Harikrishna_06 for your immediate response.
The solution worked here is the step I created

  <Step name="Work Item creation" posX="454" posY="289">
    <Approval name="Work Item Form" return="userAction,reviewerComments" send="removeSelection">
      <Form name="Work Item Form">
        <Attributes>
          <Map>
            <entry key="pageTitle" value="Approval to remove the policies from Auto Reject Custom Object"/>
          </Map>
        </Attributes>
        <Section name="ReviewSection">
          <Field displayName="Policies Selected for Removal" displayOnly="true" name="removeSelection" type="string">
            <Attributes>
              <Map>
                <entry key="readOnly" value="true"/>
              </Map>
            </Attributes>
          </Field>
          <Field displayName="Reviewer Comments" name="reviewerComments" type="String"/>
        </Section>
        <Button action="next" label="Approve" parameter="userAction" value="approved"/>
        <Button action="next" label="Reject" parameter="userAction" value="rejected"/>
      </Form>
      <OwnerScript>
        <Source>
          import sailpoint.object.Identity;
		  
          Identity policyApprovers = context.getObjectByName(Identity.class, "PolicyApprovers");
          return policyApprovers;
        </Source>
      </OwnerScript>
    </Approval>
    <Transition to="Process_Remove">
      <Script>
        <Source>
          if (userAction != null &amp;&amp; userAction.toString().equalsIgnoreCase("approved")) {
          return true;
          }        
		  </Source>
      </Script>
    </Transition>
    <Transition to="Audit_Log"/>
  </Step>

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.