function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Travis McDonaldTravis McDonald 

Future calls limit issues, how to make a batch apex class...

Hey guys, I have a trigger with code:
/*// Trigger runs getLocation() on Accounts with no Geolocation

trigger SetGeolocation on Account (after insert, after update) {
    for (Account a : trigger.new)
    if (a.Location__Latitude__s == null)
          LocationCallouts.getLocation(a.id);
}*/

// Trigger runs getLocation() on Accounts with no Geolocation

trigger SetGeolocation on Account (after insert, after update) {
    for (Account a : trigger.new){
        if(system.isFuture() == FALSE){
          LocationCallouts.getLocation(a.id);
        }
    }
}

This pulls an apex class that has this code:
 
public class LocationCallouts {

     @future (callout=true)  // future method needed to run callouts from Triggers
      static public void getLocation(id accountId){
        // gather account info
        Account a = [SELECT BillingCity,BillingCountry,BillingPostalCode,BillingState,BillingStreet FROM Account WHERE id =: accountId];

        // create an address string
        String address = '';
        if (a.BillingStreet != null)
            address += a.BillingStreet +', ';
        if (a.BillingCity != null)
            address += a.BillingCity +', ';
        if (a.BillingState != null)
            address += a.BillingState +' ';
        if (a.BillingPostalCode != null)
            address += a.BillingPostalCode +', ';
        if (a.BillingCountry != null)
            address += a.BillingCountry;

        address = EncodingUtil.urlEncode(address, 'UTF-8');

        // build callout
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://maps.googleapis.com/maps/api/geocode/json?address='+address+'&sensor=false');
        req.setMethod('GET');
        req.setTimeout(60000);

        try{
            // callout
            HttpResponse res = h.send(req);

            // parse coordinates from response
            JSONParser parser = JSON.createParser(res.getBody());
            double lat = null;
            double lon = null;
            while (parser.nextToken() != null) {
                if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) &&
                    (parser.getText() == 'location')){
                       parser.nextToken(); // object start
                       while (parser.nextToken() != JSONToken.END_OBJECT){
                           String txt = parser.getText();
                           parser.nextToken();
                           if (txt == 'lat')
                               lat = parser.getDoubleValue();
                           else if (txt == 'lng')
                               lon = parser.getDoubleValue();
                       }

                }
            }

            // update coordinates if we get back
            if (lat != null){
                a.Location__Latitude__s = lat;
                a.Location__Longitude__s = lon;
                update a;
            }

        } catch (Exception e) {
        }
    }
}

The problem is, I am going above the 10 max limit of future calls in Apex...

can someone please explain how I would convert my apex class intoa batch apex class so it runs request one at a time? See this threaD:

https://developer.salesforce.com/forums/ForumsMain?id=906F000000091NoIAI
Best Answer chosen by Travis McDonald
ShotShot
Change your class to:
static public void getLocation(List<Id> accountIds){
And in your trigger:
trigger SetGeolocation on Account (after insert, after update) {
List<Id> calloutIds = new List<Id>();    
for (Account a : trigger.new){
        if(system.isFuture() == FALSE){
             calloutIds.add(a.id);
        }
    }

LocationCallouts.getLocation(calloutIds);
}


 

All Answers

ShotShot
Change your class to:
static public void getLocation(List<Id> accountIds){
And in your trigger:
trigger SetGeolocation on Account (after insert, after update) {
List<Id> calloutIds = new List<Id>();    
for (Account a : trigger.new){
        if(system.isFuture() == FALSE){
             calloutIds.add(a.id);
        }
    }

LocationCallouts.getLocation(calloutIds);
}


 
This was selected as the best answer
Travis McDonaldTravis McDonald
Bogdan,

Thank you for your reply. I tried what you suggested, but when I change my class to:
public class LocationCallouts {

     @future (callout=true)  // future method needed to run callouts from Triggers
      static public void getLocation(List<Id> accountIds){
        // gather account info
        Account a = [SELECT BillingCity,BillingCountry,BillingPostalCode,BillingState,BillingStreet FROM Account WHERE id =: accountId];

        // create an address string
        String address = '';
        if (a.BillingStreet != null)
            address += a.BillingStreet +', ';
        if (a.BillingCity != null)
            address += a.BillingCity +', ';
        if (a.BillingState != null)
            address += a.BillingState +' ';
        if (a.BillingPostalCode != null)
            address += a.BillingPostalCode +', ';
        if (a.BillingCountry != null)
            address += a.BillingCountry;

        address = EncodingUtil.urlEncode(address, 'UTF-8');

        // build callout
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://maps.googleapis.com/maps/api/geocode/json?address='+address+'&sensor=false');
        req.setMethod('GET');
        req.setTimeout(60000);

        try{
            // callout
            HttpResponse res = h.send(req);

            // parse coordinates from response
            JSONParser parser = JSON.createParser(res.getBody());
            double lat = null;
            double lon = null;
            while (parser.nextToken() != null) {
                if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) &&
                    (parser.getText() == 'location')){
                       parser.nextToken(); // object start
                       while (parser.nextToken() != JSONToken.END_OBJECT){
                           String txt = parser.getText();
                           parser.nextToken();
                           if (txt == 'lat')
                               lat = parser.getDoubleValue();
                           else if (txt == 'lng')
                               lon = parser.getDoubleValue();
                       }

                }
            }

            // update coordinates if we get back
            if (lat != null){
                a.Location__Latitude__s = lat;
                a.Location__Longitude__s = lon;
                update a;
            }

        } catch (Exception e) {
        }
    }
}

I get a compile error - "[Error] Error: Compile Error: Variable does not exist: accountId at line 6 column 126"

Thoughts? do I need to create a list somewhere? Sorry, I'm just a bit lost here, could you please be more specific?
ShotShot
:) you get List in method, i thought you will add additional logic by yourself:
public class LocationCallouts {

     @future (callout=true)  // future method needed to run callouts from Triggers
      static public void getLocation(List<Id> accountIds){
        // gather account info
        for(Accoount a : [SELECT BillingCity,BillingCountry,BillingPostalCode,BillingState,BillingStreet FROM Account WHERE id in: accountIds]){

        // create an address string
        String address = '';
        if (a.BillingStreet != null)
            address += a.BillingStreet +', ';
        if (a.BillingCity != null)
            address += a.BillingCity +', ';
        if (a.BillingState != null)
            address += a.BillingState +' ';
        if (a.BillingPostalCode != null)
            address += a.BillingPostalCode +', ';
        if (a.BillingCountry != null)
            address += a.BillingCountry;
.....

 
ShotShot
in for loop Account, not Accoount, i made a mistake, fix it :)
Travis McDonaldTravis McDonald
Bogdan thank you for the help, that worked. Issue solved.
ShotShot
You're welcome ;)
BTW, would be better to update accounts in bulky way:
Create before loop account list:
List<Account> accountsList = new List<Account>();
And after loop update this list:
if (lat != null){
                a.Location__Latitude__s = lat;
                a.Location__Longitude__s = lon;
                accountsList.add(a);
            }

        } catch (Exception e) {
        }
    }
    update accountsList;
}

 
Travis McDonaldTravis McDonald
Bogdan,

Turns out I spoke to soon. For some reason when I use your method from earlier, it doesn't write to  a.Location__Latitude__s and  a.Location__Longitude__s...

the whole reverse geocode trigger broke.....but I need to figure out a way to get past the "future calls limit exceeded" I originally had, 
ShotShot
Maybe you missed something, show us the last version of code, please.
Travis McDonaldTravis McDonald
Bogdan,

Thanks for the quick reply. What I currently have is:

Trigger:
 
/*// Trigger runs getLocation() on Accounts with no Geolocation

trigger SetGeolocation on Account (after insert, after update) {
    for (Account a : trigger.new)
    if (a.Location__Latitude__s == null)
          LocationCallouts.getLocation(a.id);
}*/

// Trigger runs getLocation() on Accounts with no Geolocation

trigger SetGeolocation on Account (after insert, after update) {
List<Id> calloutIds = new List<Id>();    
for (Account a : trigger.new){
        if(system.isFuture() == FALSE){
             calloutIds.add(a.id);
        }
    }

LocationCallouts.getLocation(calloutIds);
}

Apex class location callouts:
 
public class LocationCallouts {

     @future (callout=true)  // future method needed to run callouts from Triggers
      static public void getLocation(List<Id> accountIds){
        // gather account info
           for(Account a : [SELECT BillingCity,BillingCountry,BillingPostalCode,BillingState,BillingStreet FROM Account WHERE id in: accountIds]){

        // create an address string
        String address = '';
        if (a.BillingStreet != null)
            address += a.BillingStreet +', ';
        if (a.BillingCity != null)
            address += a.BillingCity +', ';
        if (a.BillingState != null)
            address += a.BillingState +' ';
        if (a.BillingPostalCode != null)
            address += a.BillingPostalCode +', ';
        if (a.BillingCountry != null)
            address += a.BillingCountry;

        address = EncodingUtil.urlEncode(address, 'UTF-8');

        // build callout
        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://maps.googleapis.com/maps/api/geocode/json?address='+address+'&sensor=false');
        req.setMethod('GET');
        req.setTimeout(60000);

        try{
            // callout
            HttpResponse res = h.send(req);

            // parse coordinates from response
            JSONParser parser = JSON.createParser(res.getBody());
            double lat = null;
            double lon = null;
            while (parser.nextToken() != null) {
                if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) &&
                    (parser.getText() == 'location')){
                       parser.nextToken(); // object start
                       while (parser.nextToken() != JSONToken.END_OBJECT){
                           String txt = parser.getText();
                           parser.nextToken();
                           if (txt == 'lat')
                               lat = parser.getDoubleValue();
                           else if (txt == 'lng')
                               lon = parser.getDoubleValue();
                       }

                }
            }

            // update coordinates if we get back
            if (lat != null){
                a.Location__Latitude__s = lat;
                a.Location__Longitude__s = lon;
                update a;
            }

        } catch (Exception e) {
        }
    }
}
}

With this code, no update is made to the custom field "location ( a.Location__Latitude__s, 
a.Location__Longitude__s).

I also started a new thread, as I had taken the code back to what I had before I put in your suggestions and it made it work again. I'm not sure what I am missing/did incorrectly.

The other thread is seen here: https://developer.salesforce.com/forums/#!/feedtype=SINGLE_QUESTION_DETAIL&dc=Apex_Code_Development&criteria=ALLQUESTIONS&id=906F0000000B3icIAC


When I try running the code above, like you had suggested, for some reason it is not updating the custom field with geocode when you save a new address. I found this article as well: https://bryandf11.wordpress.com/2011/08/22/geocoding-in-apex-triggers-callouts-and-future-methods/

It seem that every resource I find online sahys do it as a batch apex, but I am a little lost when I try to do that.

With the code above, I get this error: "Content cannot be displayed: Invalid type or missing latitude value for attribute <apex:mapMarker id="j_id3" position>."

I am trying to pull the map in with apexmap instead of javascript. If I can clarify anything please let me know.



 
Travis McDonaldTravis McDonald
I should clarify that the error I get regarding apex map marker, comes from a visualforce page with this code:
<apex:page standardController="Account" extensions="accountMapExtensionTwo">

  <!--This page must be accessed with an Account Id in the URL. -->
  
  <apex:map width="600px" height="400px" mapType="roadmap">

    <apex:repeat value="{!Accounts}" var="acct">
        <apex:mapMarker title="{!acct.name}" position="{!acct.loc}"/>
    </apex:repeat>

  </apex:map>
  

</apex:page>



 
Kush Patel 5Kush Patel 5
Was this problem ever solved? I am working on a project very similar to this. I know this thread is very old but it is the closest thing that found that can help me with my problem.