0

I am a pretty novice coder and am struggling with an Apex invocable action. This code takes two inputs (min and max numbers) and returns a Double (basically a random number generator) - response can be a decimal if 0/1 is the start min/max, or an integer if it is.

It works fine for single records, but I know it will fail if multiple records are edited at the same time due to bulkification by Salesforce.

I know I need to return a list of lists but I just cannot wrap my head around how to achieve this. I've spent hours trying various things and I know it shouldn't be that hard(!). Could somebody please show what I need to do? Thanks!

global with sharing class GetRandomNumber {
    
    @InvocableMethod
    global static List<Results> getRandomNumberFromRange(List<Requests> requests){
           
        Results curResult = new Results();
        curResult.randomNumber = generateRandomFromRange(requests[0].startNumber, requests[0].endNumber);
           
        List<Results> resultsList = new List<results>();
        resultsList.add(curResult);
        return resultsList;
    }
  
    global static Double generateRandomFromRange(Integer startNum, Integer endNum) {
        
        if(startNum == 0 && endNum == 1)
            return Math.random();
        
        Integer returnValue;
        //Logic- first we will generate random boolean value which will decide if we want to
        //add the random number from startNum or will subtract random number from endNum
        Integer randomNumber = Integer.valueof((math.random() * 10));
        boolean addSubFlag= math.mod(randomNumber,2) == 0 ? true : false;
        
        integer diffInRange = endNum-startNum;
        //Now find random number staring from 0 to diffInRange
        Integer randomNum = Math.mod(Math.round(Math.random()*diffInRange+1),diffInRange);
        
        //If addSubFlag is true, we will add random number generated between 0 and diffInRange from start number
        //else we will subtract random number from end number
        if(addSubFlag){
            if(diffInRange > 1)
                returnValue = startNum + randomNum;
            else
                returnValue = startNum;
        }else{
            if(diffInRange > 1)
                returnValue = endNum - randomNum;
            else
                returnValue = endNum;
        }

        return returnValue;
    }
    
    global class Requests {
        @InvocableVariable(required=true)
        global Integer startNumber;
        
        @InvocableVariable(required=true)
        global Integer endNumber;
    }
    
    global class Results {
        @InvocableVariable
        global Double randomNumber;
    }
}
3
  • 1
    "Bulkification" is mostly just short for "make sure you're working on collections of data, and don't have queries or DML inside of loops". Since I don't see any queries or DML (things like insert records; and update records;) here, this doesn't look like it should be a problem to me.
    – Derek F
    Aug 5, 2023 at 21:59
  • 2
    Also, while we don't absolutely need curly braces if an if or else only has one line below it, but I'd say it's a good habit to use them anyways. Otherwise, it can make debugging harder (especially if you aren't careful with indentation). Being consistent will make your code easier to read in the end (and also 6+ months down the line when you've worked on other things and have forgotten exactly what a piece of older code does).
    – Derek F
    Aug 5, 2023 at 22:01
  • @DerekF - thanks a lot for your input. The issue I was having wasn't with DML governor, but editing multiple records at once. Since this triggers multiple flows, but Salesforce bulks the records and passes a single request to my code, it was failing since number of outputs didn't match number of instances. As per the response below seems I just needed to have a loop to create matched input and output. Thanks for your input - appreciated!
    – Ben
    Aug 6, 2023 at 16:07

1 Answer 1

1

Even if in the flow builder you handle a single value, your method takes a list as input due to flow bulkification. It shoud be an hint telling that the method could get multiple values in input, not just a single Requests instance, therefore you must return a random number for each list's entry, while currently you're returning just one value no matter the size of the list.

Keep in minid that the size of the output list should always be equal to the size of input list.
Just loop over requests and create an instance of Results for each item:

@InvocableMethod
global static List<Results> getRandomNumberFromRange(List<Requests> requests){
    List<Results> resultsList = new List<results>();
    for (Requests request : requests) {
        Results curResult = new Results();
        curResult.randomNumber = generateRandomFromRange(request.startNumber, request.endNumber);
        resultsList.add(curResult);
    }

    return resultsList;
}
2
  • Thanks so much for your help! I had the loop in there like you had, but I was trying to return a list of lists... so creating a list in each loop then adding that to a master list and returning that. I think I was overcomplicating it as actually the single list (but with multiple entries based on input) works a treat. Thanks again for your quick response!
    – Ben
    Aug 6, 2023 at 16:05
  • @Ben You're welcome. Glad I could help!
    – RubenDG
    Aug 6, 2023 at 16:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .