1
votes

I am creating a Apex trigger on a custom object in SF. Right now I have the object Appointment that I would like to trigger a callout when the a new record is saved. Due to our internal processes, we do not need to worry about updates. Only new records.

That being said, I have the basics of the trigger and class created and they work. The trigger runs the class that is labeled as Future to run async. However, the class is where I get lost.

I want to pass some variables into the code from the record in the Appointment object that was being created. My code sends a HTTP POST off to a service that sends a SMS to the customer. I want to use the phone number, customer name and a message that has the appointment date and time in the message. The fields this data is stored in are:

i360__Prospect_Phone__c

i360__Correspondence_Name__c

i360__Start__c

i360__Start_Time__c

So for example, I need phone number and name to transfer into the class code below. As far as the message, I would like to to be sent out in a string with the variables in it. Example: "Hello i360__Correspondence_Name__c, your appointment with COMPANY has been scheduled on i360__Start__c at i360__start_Time__c. Please respond or call if you have any questions."

Here is my trigger code:

trigger sendtext on i360__Appointment__c (after insert) {
  System.debug('Making future call to update account');
  for (i360__Appointment__c app : Trigger.New) {


    PodiumText.updateAccount(app.Id, app.Name);
  }
}

Here is my Class code:

public class PodiumText {
//Future annotation to mark the method as async.
  @Future(callout=true)
  public static void updateAccount(String id, String name) {

 Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://api.podium.com/api/v2/conversations');
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json');
        request.setHeader('Accept', 'application/json');
        request.setHeader('Authorization', 'IDSTRING');
        request.setBody('{"customerPhoneNumber":"PHONENUMBER","message":"Testing","locationId":"49257","customerName":"CORRESPONDENCENAME"}');
        HttpResponse response = http.send(request);
        // Parse the JSON response
        if (response.getStatusCode() != 201) {
            System.debug('The status code returned was not expected: ' +
                response.getStatusCode() + ' ' + response.getStatus());
        } else {
            System.debug(response.getBody());
        }

  }
}

Any help would be great.

1

1 Answers

0
votes

Ideally you would not call this @future method one account at a time. There's limit of 50 calls per transaction (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm look for "Maximum number of methods with the future annotation". So what you have now will work perfectly when you insert 1 record from UI but will fail horribly if you ever do a mass data load.

So you could call it once but pass a List (array) with your data. We can pass just record ids and then in the method itself we could query fields you need. Or you could build text of your message in the trigger and pass to @future method a List of strings. It's bit of personal preference, I think it's cleaner to just pass record IDs. That way trigger doesn't care what exactly happens, it just goes "yo, send whatever message you need based on this data". And the actual callout code is kept together (the endpoint and message building) so if you ever want to change something you do it in just 1 file.

Something like that (does order of messages matter? if it doesn't it could even be bit simpler)

trigger sendtext on i360__Appointment__c (after insert) {
  List<Id> ids = new List<Id>();
  for (i360__Appointment__c app : trigger.New) {
     ids.add(app.Id);
   }

  System.debug('Making future call to update account');
  PodiumText.updateAccounts(ids);
}

and then

@future(callout=true)
public static void updateAccounts(List<Id> ids){
    Map<Id, i360__Appointment__c> appointments = new Map<Id, i360__Appointment__c>([SELECT Id, Name, 
            i360__Prospect_Phone__c, i360__Correspondence_Name__c, i360__Start__c, i360__Start_Time__c
        FROM i360__Appointment__c
        WHERE Id IN :ids]);

    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint('https://api.podium.com/api/v2/conversations');
    request.setMethod('POST');
    request.setHeader('Content-Type', 'application/json');
    request.setHeader('Accept', 'application/json');
    request.setHeader('Authorization', 'IDSTRING');

    for(Id i : ids){
        i360__Appointment__c appointment = appointments.get(i);

        Map<String, String> message = new Map<String, String>{
            'customerPhoneNumber' => appointment.i360__Prospect_Phone__c,
            'message' => 'Testing',
            'locationId' => '49257',
            'customerName' => appointment.i360__Correspondence_Name__c
        };
        String messageJson = JSON.serialize(message);
        System.debug(messageJson);

        request.setBody(messageJson);
        HttpResponse response = http.send(request);
        // Parse the JSON response
        if (response.getStatusCode() != 201) {
            System.debug('The status code returned was not expected: ' +
                response.getStatusCode() + ' ' + response.getStatus());
        } else {
            System.debug(response.getBody());
        }
    }
}

If this works (I haven't checked if it'd compile and I don't have this app installed) then the last part would be to put real message instead of "Testing". This part depends a bit what exactly these fields are (is start time a text/picklist field? a time field?). There's interesting String.format() method but we can keep it simple (feels like I'm exposing you to a lot already, queries, Maps, JSON serialisation...)

String message = 'Hello ' + appointment.i360__Correspondence_Name__c + ', your appointment with COMPANY has been scheduled on ' + appointment.i360__Start__c + ' at ' + appointment.i360__start_Time__c + '. Please respond or call if you have any questions.';

(actually what i gave you isn't 100% perfect either. There's a "Total number of callouts" limit, 100. So if you do mass data loading you'd need to rework this code to maybe make 2 future calls 100 records each or 10 * 20...)