3

I have a process builder that executes just fine when there's a single record update. But not updating all records when there are multiple updates at the same time.

PB calls a flow, flow makes the updates. Works perfect on a single record, even on multiple records (always leaving a record without processing), if I make the updates from the object List View, runs the whole process (PB->Flow) for all records but seems like is leaving one record without processing.

Any ideas?

Use Case:

We are using a managed package called Aprika Mission Control, what we are trying to is that whenever a Time Log is approved and has not been posted (flag to prevent to count same Time Log twice), roll the time consumed up to the related Opportunity Product.

This is the ERD.

enter image description here

Time Log <-m:1- Action <-m:1- Milestone <-m:1- Project <-m:n- Oppty <-m:n-> OLI

Basically, it works for one record, but when doing bulk updates it's not rolling up the value up to the OLI for all records, just for the newest record.


Edit #1

Ran some tests, and all records go through the PB, but only one (the newest record) is going through the flow.



Edit #2

Changed flow type to Flow trigger, got rid of PB, now everything happens in the Flow.

Records actually go through the flow, but they are not updating each OLI.


Screen Shots

PB.

Object and process Criteria Action - Flow Action - Update Field

Flow.

Flow Get Record that fired the PB

Update from List view:

Update from List view

11
  • 1
    Without giving any detail info on what/how the process builder/flow are updating the records, it is not really possible to know exactly what had happened. But it sounds like a bulkification issue to me. Please edit your question and add more relevant info. – Hengky Ilawan Aug 1 '20 at 6:00
  • Sorry, you're right, it was pretty vague. I hope it's fixed now, can you take a look? thanks @HengkyIlawan – AjFmO Aug 2 '20 at 20:32
  • In the first screenshot, the Time Log node in advanced - is the recursive checkbox marked true or not? – Brian Miller Aug 5 '20 at 0:32
  • Hi, @BrianMiller no it wasn't checked, I checked and tested and got the same result. – AjFmO Aug 5 '20 at 16:44
  • I'm thinking the records are getting passed from process builder in bulk, so you might need to add a loop variable in your flow to iterate over the records being passed to the flow. – Zach Aug 7 '20 at 0:54
2
+50

So the way Process Builder "bulkifies" the records is that it actually runs X interviews each for the loaded records. So if you load 100 records, PB is running 100 interviews with 1 record each and NOT 1 interview with the 100 records.

  1. This is why you see 1 record in the Log. It has always been an issue that the Debug Logs are pretty much useless in this scenario because it will either jumble everything up so there is no way you can see a linear flow OR it drops all the records and shows you just 1.

  2. Since you mentioned something about rolling up the values it sounds like you need to have the 100 records go through together as in a Collection variable. You can do this 1 of 2 ways.

A. Instead of having the PB actually fire the flow for the loaded records, you would have it flag a field in each of the records "Update Me". Then you have a scheduled flow execute that finds all the "Update Me" records, puts them into an Sobject Collection, then loops through and does what you want it to do. You'd want your DML updates at the very end to update all records at once. You CANNOT do this in your current process. There is no way to collect the records from the PB and pass them to the flow in an Sobject Collection this way.

B. Use an Invocable Apex Class at the start of the Flow. This is one of the areas where the flow "stops" and bulkifies the records again. So in the case where the 100 interviews are running, you can actually stop them all using the Invocable Apex, pass them to the Apex, put them into your own List variable, and pass that variable back to the Flow as an Sobject Collection of 100 records, then run as in A.

*Note: I have used option B for 2 yrs now to resolve this issue I put an example of the Invocable for the Case object below. You can change the object OR now that flow accepts dynamic Apex to some degree you can modify this to accept any object.

public with sharing class CaseBulkifyClass {

@InvocableMethod(label='Collect Bulkified Records')
public static List<List<Case>> gatherRequests(Request[] requests) {

    // Gather bulkified records from Flow
    Map<Id, Case> parents = new Map<Id, Case>();

    for(Request request : requests) {
        parents.put(request.recordId, null);
    }

    parents = new Map<Id, Case>([
        SELECT Id, hdone__Identifier__c, Status, ClosedDate, hdone__hidden_CaseEdit__c, hdone__Flow_Status__c
        FROM Case
        WHERE Id IN :parents.keySet()
    ]);
    
    // Flows must return List<List> for Sobject Collections. This is returned as Sobject Collection to Flow
    List<List<Case>> responseColl = new List<List<Case>>();
    
    // Add all of the values to an initial List
    List<Case> responseList = new List<Case>();
    responseList.addAll(parents.values());
    
    System.debug('Size of ResponseList ' + responseList.size());
    
    // Adds actual values we will use to the List of List
    responseColl.add(responseList);
    System.debug('Return responseColl size ' + responseColl.size());
    
    // Initialize a new list which will return empty to ensure return same number of interviews back to Flow
    List<Case> emptyList = new List<Case>();

    // Iterate through number of interviews - 1 and assign to empty list
    for (Integer i=0; i<responseList.size()-1; i++){
        responseColl.add(emptyList);
        }
    return responseColl;
}

public with sharing class Request {
    @InvocableVariable(label='Record ID' required=true)
    public Id recordId;
    
    @InvocableVariable(label='Object API Name' required=false)
    public String objectName;

    @InvocableVariable(label='Parent ID' required=false)
    public Id parentId;
}

}

One more caveat is that if you leave this as is you will get an error "# of Flow Interviews does not match....". This is because the Flow started with 100 interviews, Apex collected them and passed back 1 variable with 100 records...so where are the other 99 interviews? Apex actually will pass back 1 with 100 records and 99 with 0 records. So you need a decision element after the Apex class that says "If collection variable is null, exit flow, else continue." Then you'll be set.

4
  • I'm summarizing the process when I say that PB runs 100 interviews with 1 record each since there is still only 1 Debug Log and not 100. Behind the scenes its more complicated but as far as a user is concerned this is what is happening. – ddeve Aug 10 '20 at 2:18
  • Hi, I had an idea, I actually called the Flow from a trigger, inside a for, for each time log that meets the criteria and was after the update, this allowed me to solve the issue, I'm not sure if your answer fits the solution I found and could solve the issue I had. Do you think your solution would work? – AjFmO Aug 12 '20 at 4:00
  • That may work, for now, but that's a pretty ugly solution unfortunately. In 8 yrs of flow, I've never heard of someone calling a flow with a Trigger since some of reason for flow and PB is to avoid the Trigger. Calling anything inside a loop isn't a scalable method. I posted my solution because yes it works. If you are calling from a Trigger though you should be able to pass in a List to the flow and just do it once. – ddeve Aug 12 '20 at 12:32
  • Thank @ddeve I'll adapt your solution, and keep you posted, I accepted your answer as correct, Thank you. – AjFmO Aug 12 '20 at 15:47
0

The solution I found was to call the Flow from a trigger (Trigger Handler actually). I called the flow inside for each record and worked like a charm.

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.