Tuesday, August 18, 2015

Bulk Data load integration across two different organizations

I tried the code which was posted in salesforce blogs
https://developer.salesforce.com/blogs/developer-relations/2015/07/using-apex-to-integrate-salesforce.html
I made some minor changes to trigger and the class to bulk load data between two different organizations.

Create Trigger on account object.
trigger SendAccount on Account(after insert)
{
 String jsonstr = JSON.serializePretty(Trigger.new);
 SendAccountUsingRESTAPI.callcreateAcc(jsonstr);
}

SendAccountUsingRESTAPI Apex Class

public class SendAccountUsingRESTAPI {
  private final String clientId = 'Client Id From Connected App';
   private final String clientSecret = 'clientSecret From Connected App';
   private final String username = 'User Name';
   private final String password = 'Password';
  public class deserializeResponse
   {
      public String id;
      public String access_token;
   }
  public String ReturnAccessToken (SendAccountUsingRESTAPI acount)
   {
      String reqbody = 'grant_type=password&client_id='+clientId+'&client_secret='+clientSecret+'&username='+username+'&password='+password;
     Http h = new Http();
      HttpRequest req = new HttpRequest();
      req.setBody(reqbody);
      req.setMethod('POST');
      req.setEndpoint('https://login.salesforce.com/services/oauth2/token');
      HttpResponse res = h.send(req);
      System.debug('\n Body Response:'+res.getbody());
      deserializeResponse resp1 = (deserializeResponse)JSON.deserialize(res.getbody(),deserializeResponse.class);
      return resp1.access_token;
   }
   
   @future(callout=true)
   public static void callcreateAcc(String jsonstr)
   {
      SendAccountUsingRESTAPI acount1 = new SendAccountUsingRESTAPI();
      String accessToken;
      accessToken = acount1.ReturnAccessToken (acount1);

       if(accessToken != null){
          String endPoint = 'https://ap1.salesforce.com/services/apexrest/v1/sampledemo/';

           Http h2 = new Http();
           HttpRequest req1 = new HttpRequest();
           req1.setHeader('Authorization','Bearer ' + accessToken);
           req1.setHeader('Content-Type','application/json');
           req1.setHeader('accept','application/json');
           req1.setBody(jsonstr);
           req1.setMethod('POST');
           req1.setEndpoint(endPoint);
           HttpResponse res1 = h2.send(req1);
        
           String trimmedResponse = res1.getBody().unescapeCsv().remove('\\');
           
           List<Account> accountsDeserialized = (List<Account>) JSON.deserialize(trimmedResponse, List<Account>.class);
           List<Account> lstAccount=new List<Account>();
           for(Account acc:accountsDeserialized){
               Account ac=new Account(Id=acc.Customer_ID__c);
               ac.Customer_ID__c=acc.Id;
               lstAccount.add(ac);
           }
           
           if(!lstAccount.isEmpty()){
               update lstAccount;
           }
        }
   }
}
In the Other Salesforce Org we need to created a rest web service. I create a sampledemo class
@RestResource(urlMapping='/v1/sampledemo/*')
global with sharing class sampledemo {

    @HttpPost
    global static String createAccount(){
        String requestBody = RestContext.request.requestBody.toString();
       // List<Account> accountsDeserialized = (List<Account>) JSON.deserialize(requestBody, List<Account>.class);
        //insert accountsDeserialized;
        List<Account> accountsDeserialized=new List<Account>();
        List<Object> m = (List<Object>)JSON.deserializeUntyped(requestBody);
        for(Object obj:m){
            Map<String,Object> requests = (Map<String,Object>)obj;
            Account acc=new Account();
            acc.Name=(String)requests.get('Name');
            acc.Customer_Id__c=(Id)requests.get('Id');
            accountsDeserialized.add(acc);
        }
        
        if(!accountsDeserialized.isEmpty()){
            insert accountsDeserialized;
        }
  
        String returnResponse = JSON.serialize(accountsDeserialized);
        return returnResponse;
    }
}
I tried the below code to load 3 accounts. It made 2 callouts(one for authentication and the other for data sync).
List<Account> lstAccount=new List<Account>();
for(Integer i=0;i<3;i++){
 Account acc=new Account();
 acc.Name= 'Test Sample'+i;
 lstAccount.add(acc);
}
insert lstAccount;
The response we get from the External org will be in JSON format. The response we are parsing and updating the customer id field.

Friday, August 7, 2015

Resolution for Too many future calls limit in Apex

In Sales force we do have many limits one among them is @future calls(asynchronous). Salesforce has a limit of 50 future calls for a single transaction.

Salesforce has introducted Queueable interface supersede the old @future annotation in Apex. now you can chain a job to another job an unlimited number of times.

Trigger
trigger SendAccount on Account(after insert)
{ 
 for(Account a : Trigger.new)
  {
 SendAccountUsingRESTAPI.callcreateAcc(a.Name, a.Id);
  }
}
SendAccountUsingRESTAPI Apex Class
public class SendAccountUsingRESTAPI {
  @future(callout=true)
   public static void callcreateAcc (String accName, String accId)
   {
  System.debug('Created Account Name:'+accName);
  System.debug('Created Account Id:'+accId);
   }
}

I tried to call future method 50 times below is code. Execute the code in developer console or on the Apex Execute on Workebench

List<Account> lstAccount=new List<Account>();
for(Integer i=0;i<51;i++){
 Account acc=new Account();
 acc.Name= 'Test Sample'+i;
 lstAccount.add(acc);
}
insert lstAccount;
I modifed the trigger and created a new class AccountQueuebleJob which implements Queueable. I ran the same code above eeverything when fine.
trigger SendAccount on Account(after insert)
{
  List<Account> lstAccount =new List<Account>();
  for(Integer i=0;i<Trigger.new.size();i++){
      if(i<50){
          SendAccountUsingRESTAPI.callcreateAcc(Trigger.new[i].Name, Trigger.new[i].Id);
      }else{
          lstAccount.add(Trigger.new[i]);
      }
  }
  
  if(!lstAccount.isEmpty()){
      ID jobID = System.enqueueJob(new AccountQueuebleJob(lstAccount));
  }
}
AccountQueuebleJob Class
public class AccountQueuebleJob implements Queueable, Database.AllowsCallouts {

    private List<Account> lstAccount;
    
    public AccountQueuebleJob(List<Account> lsacc){
        this.lstAccount = lsacc;
    }

    public void execute(QueueableContext context) {
        if(!lstAccount.isEmpty()){
            for(Account a:lstAccount){
                SendAccountUsingRESTAPI.callcreateAcc(a.Name, a.Id);
            }
        }
        
    }
}

Thursday, August 6, 2015

Passing Objects/sObjects to Future Annotated Methods

Future Method 

The future annotation is a great feature on the Salesforce Platform to allow you to execute custom logic asynchronously. I often use them to perform callouts within the context of a database trigger.  However, one of the restrictions of future annotations is that you can not pass sObjects or objects as arguments to the annotated method. I regularly use encapsulation to pass a collection of parameters, or even an sObject, but this won’t work in @future method:

But thankfully there is a way you can do using JSON serialize|deserialize methods.
public with sharing class InsertAccountFuture {
 public static void createAccountUsingFuturer () {
  List<String> lstAccount = new List<String>();
  Account a1 = new Account('Sample Acount1');
  Account a2 = new Account('Sample Acount2');
  Account a3 = new Account('Sample Acount3');
  lstAccount.add(JSON.serialize(a3));
  lstAccount.add(JSON.serialize(a2));
  lstAccount.add(JSON.serialize(a3));
  createAccounts(lstAccount);
 }
 @future
 static void createAccount(List<String> lstAccount) {
  List<Account> insAccounts=new List<Account>();
  for (String ser : lstAccount)
  {
   insAccounts.add((Account) JSON.deserialize(ser, Account.class));
  }
  if(!insAccounts.isEmpty()){
   insert insAccounts;
  }
 }
}

Monday, August 3, 2015

Multiselect picklist in visualforce page


The Image Look like this.

In this demo we are displaying all contacts in the available contacts. Querying on the Contact object in the MultiselectController constructor.

Once after select some contact names and if you click on save button. you will get the id and the name of the respective record.



Visualforce Page

    
        
            
            
           
     
{!message}
Apex Component

  
  
  
  
  

  
  

  
    
Add Remove Up Down
Apex Class
public with sharing class MultiselectController {
    // SelectOption lists for public consumption
    public SelectOption[] leftOptions { get; set; }
    public SelectOption[] rightOptions { get; set; }
 
 public SelectOption[] selectedContacts { get; set; }
    public SelectOption[] allContacts { get; set; }
 
 public String message { get; set; }
 
 public MultiselectController() {
        selectedContacts = new List<SelectOption>();
        
        List<Contact> contacts = [SELECT Name, Id FROM Contact];    
        allContacts = new List<SelectOption>();
        for ( Contact c : contacts ) {
            allContacts.add(new SelectOption(c.Id, c.Name));
        }
    }
    
    // Parse &-separated values and labels from value and 
    // put them in option
    private void setOptions(SelectOption[] options, String value) {
        options.clear();
        String[] parts = value.split('&');
        for (Integer i=0; i<parts.size()/2; i++) {
            options.add(new SelectOption(EncodingUtil.urlDecode(parts[i*2], 'UTF-8'), 
              EncodingUtil.urlDecode(parts[(i*2)+1], 'UTF-8')));
        }
    }
    
    // Backing for hidden text field containing the options from the
    // left list
    public String leftOptionsHidden { get; set {
           leftOptionsHidden = value;
           setOptions(leftOptions, value);
        }
    }
    
    // Backing for hidden text field containing the options from the
    // right list
    public String rightOptionsHidden { get; set {
           rightOptionsHidden = value;
           setOptions(rightOptions, value);
        }
    }
 
 public PageReference save() {
        message = 'Selected Contacts: ';
        Boolean first = true;
        for ( SelectOption so : selectedContacts ) {
            if (!first) {
                message += ', ';
            }
            message += so.getLabel() + ' (' + so.getValue() + ')';
            first = false;
        }
        
        return null;       
    }
}

Labels

visualforce page ( 13 ) apex integration ( 5 ) apex trigger ( 4 ) csv file from vf page ( 4 ) javascript ( 4 ) csv visualforce page ( 3 ) Too many ( 2 ) call out ( 2 ) integration ( 2 ) rest api ( 2 ) salesforce rest api ( 2 ) salesforce to salesforce integration ( 2 ) sfdc rest api ( 2 ) trigger ( 2 ) 15 digit to 18 digit ( 1 ) DML rows in Apex ( 1 ) Date Conversion ( 1 ) Date/Time conversion ( 1 ) Deploy ( 1 ) Objects to Future Annotated Methods ( 1 ) SFDC limits ( 1 ) Sobject to Future Annotated Methods ( 1 ) Test Class ( 1 ) TimeZone Conversion ( 1 ) Too many dml rows ( 1 ) Too many future calls ( 1 ) annotations ( 1 ) apex code ( 1 ) closed opportunities ( 1 ) commit ( 1 ) convert ( 1 ) create records ( 1 ) csv create records ( 1 ) custom setting ( 1 ) deployment ( 1 ) deployment changeset ( 1 ) disable apex class ( 1 ) disable apex trigger ( 1 ) disable in production ( 1 ) document ( 1 ) download ( 1 ) field name ( 1 ) formula fields ( 1 ) iframe ( 1 ) inactive ( 1 ) intellisense ( 1 ) jsforce ( 1 ) limits ( 1 ) matrix report in vf page ( 1 ) multi select ( 1 ) multi select salesforce ( 1 ) multiselect ( 1 ) paypal ( 1 ) picklist ( 1 ) record type ( 1 ) rollback ( 1 ) salesforce limits ( 1 ) salesforce list ( 1 ) salesforce map ( 1 ) salesforce rest ( 1 ) salesforce set ( 1 ) salesforce1 ( 1 ) sandbox deployment ( 1 ) sfdc collection ( 1 ) sfdc list ( 1 ) sfdc map ( 1 ) sfdc rest ( 1 ) sfdc set ( 1 ) uncommitted ( 1 ) updated field ( 1 ) user ( 1 ) validation rule opportunity ( 1 ) validation rules opportunities ( 1 ) vf page ( 1 )

Ad