Showing posts with label Salesforce Apex. Show all posts
Showing posts with label Salesforce Apex. Show all posts

Friday, March 7, 2014

Continued... Salesforce URL Hacking to Prepopulate Fields on a Standard Page Layout using Tooling API

Here are the pros and cons using Tooling API in force.com development purpose. In my earlier post here to get the CustomField Ids and auto populate some of the CustomObject field values on Standard layout I used Tooling API to get it done. But......

Cons:

1. This process won't work other Salesforce users apart from Admins or equivalent privileged users only. This is no where documented, but this API meant for external languages to build tools for force.com, in this way some what very clear that we should be careful while using these type of APIs.

2. Only work with CustomFields on CustomObjects

Pros:

1. Not required to store the static CustomField Ids in a CustomSettings or CustomObject.

2. Not required to hack the URL with static CustomField Ids.


So I did a work around as inserting the Tooling API information in a CustomObject so that I can use this CustomObject to fetch the fieldIds and auto populate the values for the same for Salesforce users who are not admins.


Happy coding.....





Tuesday, March 4, 2014

Salesforce URL Hacking to Prepopulate Fields on a Standard Page Layout using Tooling API

To build Salesforce URL’s to standard pages for creating records, that can be applied to Custom Buttons in order to pre-populate field values when a user clicks a button native using Tooling API.

Tooling API support for Custom Object and Custom Fields

This can query (REST or SOAP) the CustomObject and CustomField objects (which are not accessible via Apex SOQL).As we know every object in Salesforce has an ID and thia API has the means to obtain the custom field Id’s.

We have  Apex wrapper for the Tooling API which can referred here

I tried to use the REST and getting a CustomObject's CustomFields Ids by using the below approach..

This code is just my work according to my requirement and can be extended up to your requirements..

NOTE: This supports only for CustomObjects and CustomFields.

To test this send the object name to the below method.

FieldIdUtil.findFieldIds('CustomObjectName') //Don't Append '"__c" for example
FieldIdUtil.findFieldIds('Implementation') //Don't Append '"__c"
You should give the object name but not the API name

This method will return map of custom fields with the respective field IDs from which we can grab the fields what ever we want to pre-populate

1. Consider the objectname is 'Implementation' and the fields in this are  'Field1', 'Field2';



1:  public class FieldIdUtil   
2:  {  
3:       public Static Map<String,String> fieldIdsMap = new Map<String,String>();  
4:       public Static String objectId; 
         
         public static List CustomApexTypes = new List();
 
 public class CustomApexType
        {
           public String Id;
           public String DeveloperName;
        }

5:       public static Map<String,String> findFieldIds(String ObjectName)   
6:    {  
7:         String objectIdQuery = 'Select Id From CustomObject Where DeveloperName = \''+ObjectName+'\'';  
8:      sendCallout(objectIdQuery, true);  
9:      if(objectId != null)  
10:      {  
11:        String fieldIdsQuery = 'Select Id, DeveloperName From CustomField Where TableEnumOrId = \''+objectId+'\'';  
12:        sendCallout(fieldIdsQuery, false);  
13:      }  
14:         return fieldIdsMap;  
15:       }  
16:       private static void sendCallout(String query, boolean flag)  
17:       {  
18:            String strIdValue,devName;  
19:            HttpRequest req = new HttpRequest();  
20:      req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());  
21:      req.setHeader('Content-Type', 'application/json');  
22:      String environmentURL =   
23:          URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v28.0/tooling/query/?q=' + EncodingUtil.urlEncode(query, 'UTF-8');  
24:      req.setEndpoint(environmentURL);  
25:      req.setMethod('GET');  
26:      Http h = new Http();  
27:      HttpResponse res = h.send(req);  
28:      JSONParser parser = JSON.createParser(res.getBody());  
29:      while (parser.nextToken() != null)
  {
      if ((parser.getText() == 'records'))
      {
          parser.nextToken();
  
          CustomApexTypes = (List<CustomApexType>)parser.readValueAs(List<CustomApexType>.class);
      }
  }  
64:   }  
65:  }  

The below code will generate the URL and the parameters from the above methods and do the redirect to a standard page by auto populate the specified values


1:  public PageReference doredirect()  
2:    {  
3:         Map<String,String> fieldIdsMap = new Map<String,String>();  
4:          Set<String> fieldAPINames = new Set<String>  
5:        {'Field1','Field2'};  
8:         fieldIdsMap = FieldIdUtil.findFieldIds('Implementation');//Passing the custom object name  
9:         PageReference pr = new PageReference(URL.getSalesforceBaseUrl().toExternalForm() + '/' + Implementation__c.SObjectType.getDescribe().getKeyPrefix() + '/e');  
10:         Map<String, String> params = pr.getParameters();  
           //we will be able to get the Master or Lookup id like this '00N11000000GysA' but inorder to refer in page we should add "CF"
            to the ID"
11:         //params.put('CF00N11000000GysA', Opp.Name);  // if you have parent object like opportunity we need to do this bit
12:      //params.put('CF00N11000000GysA' + '_lkid', Opp.Id);  // if you have parent object like opportunity we need to do this bit

13:         for(String fieldAPIName : fieldAPINames)  
14:         {  
15:           params.put(fieldIdsMap.get(fieldAPIName), (String) Opp.get(fieldAPIName));  
16:            }  
17:         params.put('saveURL',opp.id);  
18:      params.put('retURL', opp.id);  
19:         pr.setRedirect(true);   
20:         system.debug('*************' + pr);  
21:         return pr;  
22:    }  

This is just my initial thoughts and worked solution for me ..There may be some changes and best practices you may found to change in this code....

here is my experience in making this solution before taking to Production.  

Friday, July 5, 2013

Setting Id Fields on sObjects for Updates


Starting with Apex code saved using Salesforce.com API version 27.0, the Id field is now writeable

         list<Account> lstA = [select id,name from account limit 1];
Account a1 = new Account();
        a1.id = lstA[0].id;
a1.name = 'update through ID';
update a1;

but attempting to insert these sObject instances results in an error.

Monday, May 13, 2013

You have uncommitted work pending. Please commit or rollback before calling out issue workaround in salesforce




Sometimes we need to create a record and then update it with information provided by a Web Service. However,a Web Service Callout may not occur after a DML statement within the same transaction.To achieve the required action, the transaction must be separated into two parts so that the DML transaction is completed before the Web Service Callout occurs and one workaround like below.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<apex:page controller="WsCalloutTest" tabstyle="Account">
    <apex:form >
        <apex:actionFunction action="{!firstcall}" name="firstcall" Rerender="statuses" status="Status1" oncomplete="WebServiceCall();"/>
        <apex:actionFunction action="{!WebServiceCall}" name="WebServiceCall" status="Status2" reRender="statuses, msg"/>
        <apex:outputPanel id="statuses">
            <apex:actionStatus id="Status1" startText="...Inserting Record Into DB..." />
            <apex:actionStatus id="Status2" startText="...Calling Web Service..." />
        </apex:outputPanel>
        <apex:outputPanel id="msg">
            <apex:pageMessages />
        </apex:outputPanel>
        <div><input name="DoAction" class="btn" type="button" value="Do Action" onclick="firstcall();return false;"/></div>
    </apex:form>
</apex:page>



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class WsCalloutTest{
    
    Contact myContact;
   
    public PageReference firstcall() {
        myContact = new Contact(name = 'Test Contact');
        insert myContact;
        return null;
    }
    
    public PageReference WebServiceCall() {
        
        // Execute a call to a Web Service
        HttpRequest req = new HttpRequest();
        req.setEndpoint('http://yourserviceurl.com?id=' + myContact.Id);
        req.setMethod('GET');
        HttpResponse response = new Http().send(req);
        myContact.Name = 'Test Contact 2';
        update myContact;
  //if the update is successfull
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM, 'WebService Called on New Contact: ' + myContact.Name));
        return null;
    }
}

Thursday, May 2, 2013

Number to Word conversion using Salesforce Apex


1:  public with sharing class NumberToWord {  
2:      static String[] to_19 = new string[]{ 'zero', 'one',  'two', 'three', 'four',  'five',  'six',  
3:      'seven', 'eight', 'nine', 'ten',  'eleven', 'twelve', 'thirteen',  
4:      'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen' };  
5:    static String[] tens = new string[]{ 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'};  
6:    static String[] denom = new string[]{ '',  
7:      'thousand',   'million',     'billion',    'trillion',    'quadrillion',  
8:      'quintillion', 's!xtillion',   'septillion',  'octillion',   'nonillion',  
9:      'decillion',  'undecillion',   'duodecillion', 'tredecillion',  'quattuordecillion',  
10:      's!xdecillion', 'septendecillion', 'octodecillion', 'novemdecillion', 'vigintillion' };  
11:    // convert a value < 100 to English.    
12:   public static String convert_nn(integer val) {  
13:      if (val < 20)  
14:        return to_19[val];  
15:      if(val == 100)  
16:          return 'One Hundred';  
17:      for (integer v = 0; v < tens.size(); v++) {  
18:        String dcap = tens[v];  
19:        integer dval = 20 + 10 * v;  
20:        if (dval + 10 > val) {  
21:          if (Math.Mod(val,10) != 0)  
22:            return dcap + ' ' + to_19[Math.Mod(val,10)];  
23:          return dcap;  
24:        }      
25:      }  
26:      return 'Should never get here, less than 100 failure';  
27:    }  
28:    // convert a value < 1000 to english, special cased because it is the level that kicks   
29:    // off the < 100 special case. The rest are more general. This also allows you to  
30:    // get strings in the form of "forty-five hundred" if called directly.  
31:    public static String convert_nnn(integer val) {  
32:      String word = '';  
33:      integer rem = val / 100;  
34:      integer mod = Math.mod(val,100);  
35:      if (rem > 0) {  
36:        word = to_19[rem] + ' hundred';  
37:        if (mod > 0) {  
38:          word += ' ';  
39:        }  
40:      }  
41:      if (mod > 0) {  
42:        word += convert_nn(mod);  
43:      }  
44:      return word;  
45:    }  
46:    public static String english_number(long val) {  
47:      if (val < 100) {  
48:        return convert_nn(val.intValue());  
49:      }  
50:      if (val < 1000) {  
51:        return convert_nnn(val.intValue());  
52:      }  
53:      for (integer v = 0; v < denom.size(); v++) {  
54:        integer didx = v - 1;  
55:        integer dval = (integer)Math.pow(1000, v);  
56:        if (dval > val) {  
57:          integer mod = (integer)Math.pow(1000, didx);  
58:          integer l = (integer) val / mod;  
59:          integer r = (integer) val - (l * mod);  
60:          String ret = convert_nnn(l) + ' ' + denom[didx];  
61:          if (r > 0) {  
62:            ret += ', ' + english_number(r);  
63:          }  
64:          return ret;  
65:        }  
66:      }  
67:      return 'Should never get here, bottomed out in english_number';  
68:    }  
69:  }  
This will work for integer range values