4

I've got some records that were accidentally entered into an approval process that I want to remove - I keep running into governor limits when doing so. The approach I'm taking currently doesn't feel right, so I'm wondering if there is an easier way to get the process work items of records currently in an approval flow.

List<Approval.ProcessWorkitemRequest> requests = new List<Approval.ProcessWorkitemRequest> ();

// get all records that need to be removed from approval process
for(Opportunity o : [Select Id from Opportunity where StageName = 'Lost' and Deal_Number__c like 'SLE%']){

    // find the process instances
    List<ProcessInstance> p = [Select Id from ProcessInstance where TargetObjectId = :o.Id];

    if(p.size() > 0){

        // if record has process instance, find processInstanceWorkItems
        List<ProcessInstanceWorkItem> pp = [Select Id from ProcessInstanceWorkItem where ProcessInstanceId = :p[0].Id];

        if(pp.size() > 0){

            // if there's a work item, set the action to 'removed' and execute
            Approval.ProcessWorkitemRequest req2 = new Approval.ProcessWorkitemRequest();
            req2.setAction('Removed');
            req2.setWorkitemId(pp[0].Id);
            requests.add(req2);

        }
    } 
}

Approval.ProcessResult[] processResults = null;
processResults = Approval.process(requests, true);
  • Are your governor limits related to any processing inside the for loop or right at the end at Approval.process? Also, can you post any limits errors you are getting? – Caspar Harmer Dec 21 '15 at 17:03
5

Liberal use of Maps should reduce it to this:

List<Approval.ProcessWorkitemRequest> requests = new List<Approval.ProcessWorkitemRequest> ();

//Get Opps
Map<ID, Opportunity> opps : New Map<ID,Opportunity>([Select Id from Opportunity where StageName = 'Lost' and Deal_Number__c like 'SLE%']);

//Get ProcessInstance Items
Map<ID,ProcessInstance> piMap = New Map<ID,ProcessInstance>([Select Id from ProcessInstance where TargetObjectId IN :opps.keySet()]);


for(ProcessInstanceWorkItem wi : [Select Id from ProcessInstanceWorkItem where ProcessInstanceId IN :piMap.keySet()]){


    // if there's a work item, set the action to 'removed' and execute
    Approval.ProcessWorkitemRequest req2 = new Approval.ProcessWorkitemRequest();
    req2.setAction('Removed');
    req2.setWorkitemId(pp[0].Id);
    requests.add(req2);
}

Approval.ProcessResult[] processResults = null;
processResults = Approval.process(requests, true);

I did not see any criteria in your code so this will process all requests meeting the Opportunity Query as the start point

3

You might try this to remove records from approval process.

  1. Go to Setup --> Data Management --> Mass Transfer Approval Requests.

enter image description here

  1. Enter the search conditions that the approval requests you are transferring or removing must match.
  2. Click “Find” button to search for approval requests that match.
  3. Optionally, enter any comments to be stored with the approval requests. The comments you enter are displayed on the Approval History related list if your administrator has customized it to include the Comments field. Choose an option: • “Mass remove records from an approval process” unlocks the records and removes them from any approval process so they no longer appear on the approver's list of pending approval requests.

    • “Mass transfer outstanding approval requests to a new user” allows you to select a user and assign that user as the approver for all approval requests selected. 5.Select the checkbox next to each approval request you want to transfer or remove from the approval process, depending on the option you selected previously. 6.Click Transfer or Remove, depending on the option you selected previously.

enter image description here

  • this would be great, but unfortunately it looks like you can't apply filters to the object, only to the approval request. filtering out Opportunities with a particular StageName is very important for my use case. – logeyg Dec 21 '15 at 20:16
1

A few key points:

  • avoid SOQL in FOR Loops
  • use Object Relations to your benefits (e.g. Select Id, (Select id from WorkItems) from ...)

Id do it somewhat like this (Beware: probably typos around :) ):

    List<Approval.ProcessWorkitemRequest> requests = new List<Approval.ProcessWorkitemRequest> ();
// get all records that need to be removed from approval process
Map<Id, Opportunity> mapRelatedOpps = new Map<Id, Opportunity>([Select Id from Opportunity where StageName = 'Lost' and Deal_Number__c like 'SLE%']);

If (mapRelatedOpps.keyset().size()>0){
    List<Id> listOppIds = mapRelatedOpps.keyset();
    List<ProcessInstance> listProcessInstancesWithWorkItems = [Select Id, (Select Id From WorkItems) from ProcessInstance where TargetObjectId in : listOppIds];
    For ( ProcessInstance instance : listProcessInstancesWithWorkItems){
        For(ProcessInstanceWorkItem item : instance.WorkItems){
            Approval.ProcessWorkitemRequest req2 = new Approval.ProcessWorkitemRequest();
                req2.setAction('Removed');
                req2.setWorkitemId(item.Id);
                requests.add(req2);
        }
    }    
}
Approval.ProcessResult[] processResults = null;
processResults = Approval.process(requests, true);
0

I am sure you would run into 101 SOQL governor limit since you have two queries inside your for loop. You need to bulkify your code so that it won't hit 101 SOQL error.

  1. Get all the Opportunity Id into a set inside a SOQL for loop.
  2. Outside of that for loop, do another SOQL for loop querying ProcessInstance using TargetObjectId in set of Opportunity Id and assign the Id to a set.
  3. Outside of the above for loop, do another SOQL for loop querying ProcessInstanceItem and updating it status as removed and add it to a list.
  4. Update the list outside of above for loop.

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.