Monday 26 December 2016

It's always shallow copy :)

Hi Awesome Folks,

All of us are aware of the clone method of the sobject class.

I got a requirement to clone the object of the class. I thought the clone will be deep copy but to my surprise its always shallow copy.

public class Demo
{
 
    List<Account> lstAcc;

    public Demo(){
        lstAcc = new List<Account>();
        lstAcc.add(new Account(Name='Ajay'));
    }
 
    public void doclone(){
 
      system.debug('>>>>>>>>this'+this.lstAcc[0]);
   
      Demo objClone = this.clone();
      system.debug('>>>>>>>>clone'+objClone);
 
      objClone.lstAcc [0].Name = 'Vijay';
      system.debug('>>>>>>>>this.clone'+objClone.lstAcc[0]);
      system.debug('>>>>>>>>this'+this.lstAcc[0]);
    }
}

Output :
>>>>>>>>thisAccount:{Name=Ajay}

>>>>>>>>cloneDemo:[lstAcc=(Account:{Name=Ajay})]

>>>>>>>>this.cloneAccount:{Name=Vijay}

>>>>>>>>thisAccount:{Name=Vijay}


From output you can see change in the clone object modifying the main object also.

Keep Coding !!!!

Tuesday 11 October 2016

Building Dynamic Triggers and Fieldset !!

Hi Folks,

Isn't it awesome if you are able to create trigger and fieldset dynamically :)

1) Create Trigger Dynamically :
map<string,string> mapinfo= new map<string,string>();
mapinfo.put('Name','AccountTrigger');
mapinfo.put('TableEnumOrId','Account');
mapinfo.put('Body','trigger AccountTrigger on Account(after insert){}');
String serialized = JSON.serialize(mapinfo);
         
Httprequest req = new HttpRequest();
req.setEndpoint('https://ajaytrailhead-dev-ed.my.salesforce.com/services/data/v27.0/sobjects/ApexTrigger'); // Enter the org.
req.setMethod('POST');
req.setHeader('Content-Type','application/json');
req.setHeader('Authorization','Bearer '+UserInfo.getSessionID());
req.setBody(serialized);

Http httpReq = new Http();
HttpResponse res = httpReq.send(req);
System.debug(res.getBody());

2) Create FieldSet Dynamically :

To make it possible you need to add the following class to your org.

https://github.com/financialforcedev/apex-mdapi/blob/master/apex-mdapi/src/classes/MetadataService.cls

Use below code to create fieldset dynamically :

public with sharing class DynamicFieldSetCreator
{
public static MetadataService.MetadataPort createService()
{
 MetadataService.MetadataPort service = new MetadataService.MetadataPort();
 service.SessionHeader = new MetadataService.SessionHeader_element();
 service.SessionHeader.sessionId = UserInfo.getSessionId();
 return service;
}
   
public static void createFieldSet()
{
        MetadataService.MetadataPort service = createService();

MetadataService.FieldSet fieldSet = new MetadataService.FieldSet();
        fieldSet.fullName = 'Account.MyFieldSet'; // You can use custom object also.                                                                                                             //Mycustomobject__c.MyFieldSet
        fieldSet.label = 'My FieldSet';
        fieldSet.description = 'Demo fieldset';

        MetadataService.FieldSetItem myAvailableField = new MetadataService.FieldSetItem();
        myAvailableField.field = 'Name'; // Add standard/custom fields to be included in fieldset
        myAvailableField.isFieldManaged = true;
        myAvailableField.isRequired = true;

        fieldSet.availableFields = new List<MetadataService.FieldSetItem>();
        fieldSet.availableFields.add(myAvailableField);

        List<MetadataService.SaveResult> results =
            service.createMetadata(new MetadataService.Metadata[] { fieldSet });
        handleSaveResults(results[0]);
}

public static void handleSaveResults(MetadataService.SaveResult saveResult)
    {
        if(saveResult==null || saveResult.success)
            return;

        // Handle Errors
        if(saveResult.errors!=null)
        {
            List<String> Erros = new List<String>();
            Erros.add(
                (saveResult.errors.size()==1 ? 'Error ' : 'Errors ') +
                    'occured processing component ' + saveResult.fullName + '.');
            for(MetadataService.Error error : saveResult.errors)
                Erros.add(
                    error.message + ' (' + error.statusCode + ').' +
                    ( error.fields!=null && error.fields.size()>0 ?
                        ' Fields ' + String.join(error.fields, ',') + '.' : '' ) );
            if(Erros.size()>0)
            {
                system.debug('>>>>>>>>>>>>>Here');
               
             }
        }
        if(!saveResult.success)
        {
            system.debug('>>>>>>>>>>>>>HereAlso');
        }
    }
}

Keep Coding !!!!



Tuesday 9 August 2016

Stuck with Salesforce Limited Feed Tracking.....Here you go with UNLIMITED Feed Tracking.......

Hi Awesome Salesforce Developers,

Recently I got a requirement where a client wants feed tracking for every change that Users are doing.Salesforce Limits Feed Tracking to 20 fields per object.
I have used FieldSet and After Update trigger on object (for which we want to do feed tracking) to overcome the 20 field limit.

What is feed tracking ? 
The below salesforce documentation link will explain in detail : 
https://help.salesforce.com/apex/HTViewHelpDoc?id=collab_feed_tracking_overview.htm&language=en

Example : Account Feed tracking 

IMP : Enable Feed Tracking by : 
setup -> Chatter -> FeedTracking -> Select Account -> Click on Enable Feed Tracking

Step 1) Create field set on Account called AccountChatter. Add the fields you want to track in feed tracking.

Step 2) Create the following after update trigger on Account : 

trigger AccountChatter on Account (after update) 
{
 List<Schema.FieldSetMember> lstTrackedFields = SObjectType.Account.FieldSets.AccountChatter.getFields();

 if (lstTrackedFields.isEmpty()) return; 
    
 List<FeedItem> lstFieldChanges = new List<FeedItem>();
    
 if(!trigger.isUpdate) return;
    
for (Account objNewAccount : trigger.new) 
{

 final Account oldAccount = trigger.oldmap.get(objNewAccount.Id);
    // Iterate over all fields in Fieldset 
 for (Schema.FieldSetMember objField : lstTrackedFields) 
 {
String fieldName  = objField.getFieldPath();
String fieldLabel = objField.getLabel();

if (objNewAccount.get(fieldName) == oldAccount.get(fieldName))
continue;

String oldValue = String.valueOf(oldAccount.get(fieldName));
String newValue = String.valueOf(objNewAccount.get(fieldName));

if (oldValue != null && oldValue.length()>255) 
oldValue = oldValue.substring(0,255);

if (newValue != null && newValue.length()>255) 
newValue = newValue.substring(0,255); 

FeedItem post = new FeedItem();
post.ParentId = objNewAccount.Id; // RecordId
post.Body = UserInfo.getName()+' changed '+fieldLabel+' from '+oldValue +' to '+newValue ;

lstFieldChanges.add(post);
  }
}

if (!lstFieldChanges.isEmpty()) insert lstFieldChanges;

}

Output : 
Add fields in FieldSet :















Create Record in Account with Name : Ajay test chatter 

Now update the record with Name : Ajay test verified





















The above solution implemented by taking idea from below post :
(To overcome the limit of History Tracking)
http://salesforce.stackexchange.com/questions/39956/what-is-the-best-workaround-for-the-20-field-history-tracking-cap

Happy Coding !!!

Cheers !!



Tuesday 26 July 2016

Integrating Significant Ecommerce and Helpful Platforms with Salesforce

Hi Awesome Salesforce Developers,

Recently I did custom integrations with shopify,bill.com,LiveChat,Helpscout so sharing with you authentication request/response formats.

Integration with Shopify.com 
(Using private app in Shopify)
Shopify is a complete e-commerce solution that allows you to set up an online store to sell your goods. It lets you organize your products, customize your storefront, accept credit card payments, track and respond to orders — all with a few clicks of the mouse.

Firstly you need to create private app in Shopify.com which will generate the API key , Password,Shared secret

You need to use those keys for authentication purpose and while sending REST requests from salesforce : 

Shopify API Documentation : https://docs.shopify.com/api

Example : Create customer in Shopify.com from salesforce 
API : https://help.shopify.com/api/reference/customer#create

POST Request from salesforce  :

JSONGenerator gen = JSON.createGenerator(true);
gen.writeStartObject();
 gen.writeFieldName('customer');
 gen.writeStartObject();
   gen.writeStringField('email', Contact.Email);        
   gen.writeStringField('first_name', Contact.FirstName);
   gen.writeStringField('last_name', Contact.LastName);             
 gen.writeEndObject();
gen.writeEndObject();

string strRequest = gen.getAsString();

Http objHttp = new Http();

HttpRequest request = new HttpRequest(); 
request.setMethod('POST'); 
Blob headerValue = Blob.valueOf('your apikey: your password');
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
request.setHeader('Authorization', authorizationHeader);
request.setHeader('Content-Type', 'application/json');
request.setBody(strRequest);
request.setEndpoint('https://apikey:password@nameofyourshop.myshopify.com/admin/customers.json'); 

HttpResponse res = objHttp.send(request);  

Map<String, Object> mapResponse =                                                                                   (Map<String,Object>)JSON.deserializeUntyped(res.getBody());

Map<String, Object> mapCustomerData =
                       (Map<String, Object>)mapResponse.get('customer');

if(mapCustomerData .containskey('id'))
system.debug('>>>Shopify='+string.valueof(mapCustomerData.get('id'));)



Integration with Bill.com
Bill.com is a US-based cash flow management software system, provided as a software as a service that integrates with accounting and banking systems. It is intended to serve as a command and control dashboard for cash flow, by businesses, accounting firms and banks.

Documentation : http://developer.bill.com/api-documentation/api/bill

Every API request needs a valid session id, which is obtained by issuing a Login request. Login API takes in an organization id, developer (or application) key, and user credentials (or Token), and returns a valid session id, as well as an end-point URL, which should be used for any subsequent API requests. This session id will remain valid for up to 35 minutes without activity. If the session becomes inactive, you will have to issue a new Login request and obtain a new session ID. You can explicitly end the session by issuing a Logout request.

You need Bill.com -> username,password,OrganizationId,Devkey which can obtain from bill.com account / support.

Login Request :
String strLoginRequest = 'userName='+'Bill_com_Username'+'&password='+'Bill_com_Password'
                   +'&orgId='+'Bill_com_OrgId'+'&devKey='+'Bill_com_Devkey'+';

HttpRequest objHttpReq = new HttpRequest();
objHttpReq.setHeader('Content-Type','application/x-www-form-urlencoded');
objHttpReq.setEndpoint('EndPOINTURL'+'/Login.json');
objHttpReq.setMethod('POST');
objHttpReq.setbody(strLoginRequest);

Http http = new Http();
HTTPResponse objHttpResp = http.send(objHttpReq);     

if (objHttpResp.getStatusCode()==200)
{
  Map<String,object> mapLoginResp = (Map<String,object>)JSON.deserializeUntyped(objHttpResp.getBody());
   
 if(mapLoginResp.containsKey('response_status') &&   Integer.valueOf(mapLoginResp.get('response_status'))==0)
 {
  if (mapLoginResp.containsKey('response_data') && 
                    mapLoginResp.get('response_data') instanceof map<string,object>)
   {   
   Map<String,Object> mapLoginRespData =  (Map<String,Object>)mapLoginResp.get('response_data');
   
   if(mapLoginRespData.containskey('apiEndPoint') )
   { 
    system.debug('>apiEndPoint='+string.valueof(mapLoginRespData.get('apiEndPoint')));
     system.debug('>sessionId='+string.valueof(mapLoginRespData.get('sessionId')));
     system.debug('>orgId='+string.valueof(mapLoginRespData.get('orgId')));
     system.debug('>usersId='+string.valueof(mapLoginRespData.get('usersId')));
   }
   }  
 }
}


The session Id,API end point to need to use for making next requests:

For example :Create Bill request : 

HttpRequest objHttpReq = new HttpRequest();
objHttpReq.setEndpoint('ENDPOINTURL');
objHttpReq.setMethod('POST');
objHttpReq.setbody('devKey='+'DEVKEY'+'&sessionId='+'SESSION ID RECEIVED from LOGIN'+'&orgId='+'Bill_com_OrgId'+'&data='+'RequestBODY');
objHttpReq.setHeader('Content-Type','application/x-www-form-urlencoded');       
Http http = new Http();
HTTPResponse objHttpResp = http.send(objHttpReq); 



Integration with LiveChat :
LiveChat is an online customer service software with live support, help desk software and web analytics capabilities. It was first launched in 2002 and since then it is developed and offered in SaaS (software as a service) business model by LiveChat Software.

Documentation : https://developers.livechatinc.com/rest-api/

Request to get all chats :
Livechat give the response in pages so intially we need to make request with page=1 and get a total number of pages. Do the requests till you reach the last page.

    integer intPageCountFromBatch = 1;

    Http objHttp = new Http();
    HttpRequest request = new HttpRequest(); 
    request.setMethod('GET'); 
    Blob headerValue = Blob.valueOf('Livechatusername/email:apikey');
    String authorizationHeader = 'Basic ' +    EncodingUtil.base64Encode(headerValue);
    request.setHeader('Authorization', authorizationHeader);
    request.setHeader('Content-Type', 'application/json');
    request.setHeader('X-API-Version','2');
    request.setEndpoint('https://api.livechatinc.com/chats?page='+intPageCountFromBatch);
    request.setTimeout(120000); 
    HttpResponse res = objHttp.send(request);  

if (res.getStatus() == 'OK')
{
  Map<String, Object> mapResponse =
          (Map<String, Object>)JSON.deserializeUntyped(res.getBody());

  if (mapResponse.containsKey('pages'))
  intTotalPageCount = String.valueOf(mapResponse.get('pages')) != null ? Integer.valueOf(String.valueOf(mapResponse.get('pages'))) : 1 ;
      
List<Object> lstChatData =(List<Object>)mapResponse.get('chats');
   
}

IMP : You need to make callout till you reach the last page number.



Integration with HelpScout :
Help Scout is a simple help desk designed for small businesses. 

Documentation : http://developer.helpscout.net/help-desk-api/

integer intPageCountFromBatch = 1;

Http objHttp = new Http();
HttpRequest request = new HttpRequest(); 
request.setMethod('GET'); 
Blob headerValue = Blob.valueOf('APIKey:Password');
String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
request.setHeader('Authorization', authorizationHeader);
request.setHeader('Content-Type', 'application/json');
request.setEndpoint('https://api.helpscout.net/v1/mailboxes/YourMailboxId/conversations.json?page='+intPageCountFromBatch); 

HttpResponse res = objHttp.send(request);
request.setTimeout(120000); 

if (res.getStatus() == 'OK')
{
Map<String, Object> mapResponse = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());

if (mapResponse.containsKey('pages'))
  intTotalPageCount = String.valueOf(mapResponse.get('pages')) != null ? Integer.valueOf(String.valueOf(mapResponse.get('pages'))) : 1 ;

 List<Object> lstChatData = (List<Object>)mapResponse.get('items');
}



IMP : You need to make callout till you reach the last page number.


For both LiveChat & HelpScount you need to implement the batch chain logic.

Feel free to contact for detailed code : ajay.ghuge09@gmail.com

Cheers,
Ajay

Tuesday 10 May 2016

Picklist Handling in Salesforce

Hi Awesome Salesforce Developers,

I hope you have enjoyed the last three blogs.

The current blog is related with handling picklist values in salesforce.

We can use picklist in many ways on VisualForce page i.e either use the field defined as picklist data type or build your own picklist using <apex:selectlist>

For this purpose, I have created the custom fields called Product Single Select & Product with datatype as picklist and picklist (Multiselect) on Case object.

On visual force page also, I have created picklists using <apex:selectlist> with attribute multi-select.

The visualforce page will look like below :
















When you select value/values from each picklist and click on show values button the output panel will display how salesforce will give values from each picklist in the apex controller.






















* Please check the result of standard and custom multi-select picklist selection.
In standard multi-select picklist values are separated using (;) and in custom multi-select values are surrounded with [] & (,).

For getting values you need to split the string using (;) or (,).

Sharing below controller and visual force page of above demo :

Apex Controller :
public with sharing class CasePickDemoController
{
    public Case objCase {get;set;}
    public string strProduct {get;set;}
    public string strProducts {get;set;}
    public boolean blnshowresult{get;set;}
    
    public CasePickDemoController(ApexPages.StandardController controller) 
    {
      objCase = new Case();
      blnshowresult = false;
    }
    
    public List<SelectOption> getlstProductSingleSelect()
    {
        List<SelectOption> lstProduct = new List<SelectOption>();
        lstProduct.add(new SelectOption('--None--','--None--'));
        lstProduct.add(new SelectOption('GC1040','GC1040'));
        lstProduct.add(new SelectOption('GC3020','GC3020'));
        return lstProduct;
    } 
     
    public List<SelectOption> getlstProductMultiSelect()
    {
        List<SelectOption> lstProduct = new List<SelectOption>();
        lstProduct.add(new SelectOption('--None--','--None--'));
        lstProduct.add(new SelectOption('GC1040','GC1040'));
        lstProduct.add(new SelectOption('GC3020','GC3020'));
        return lstProduct;
    } 
     
    public void getPickListValues()
    {
      blnshowresult = true;
      system.debug('>'+objCase.Product_Single_Select__c);
      system.debug('>>>>>>>>objCase.Product__c '+objCase.Product__c);
      system.debug('>>>>>>>>strProduct '+strProduct);     
      system.debug('>>>>>>>>strProducts'+strProducts);
    }
}



VisualForce Page :
<apex:page standardController="Case"  extensions="CasePickDemoController">
<apex:form >
<apex:pageblock>
<apex:pageblocksection>
  <apex:pageBlockSectionItem >
 <apex:outputLabel value="Product Single Select Picklist"/> 
 <apex:inputfield value= "{!objCase.Product_Single_Select__c}"/>         
  </apex:pageBlockSectionItem>      
<br/>
   <apex:pageBlockSectionItem >
 <apex:outputLabel value="Product Multi Select Picklist"/>
 <apex:inputField value="{!objCase.Product__c}" />
   </apex:pageBlockSectionItem>          
<br/>
   <apex:pageBlockSectionItem >
 <apex:outputLabel value="Product Custom Single Select Picklist "/>
 <apex:selectList size="5" value="{!strProduct}" multiselect="false">
   <apex:selectOptions value="{!lstProductSingleSelect}" />
 </apex:selectList>
   </apex:pageBlockSectionItem>  
<br/>
   <apex:pageBlockSectionItem >
 <apex:outputLabel value="Product Custom Multi Select Picklist "/>
 <apex:selectList size="5" value="{!strProducts}" multiselect="true">
   <apex:selectOptions value="{!lstProductMultiSelect}" />
      </apex:selectList>
 </apex:pageBlockSectionItem>
   </apex:pageblocksection>
</apex:pageblock>
      
<apex:commandButton value="Show Values" action="{!getPickListValues}"/> <br/>

<apex:outputpanel rendered="{!blnshowresult}">
 <apex:outputLabel value="Values selected from standard pickist field (Single)"/>
 <apex:outputLabel value = "{!objCase.Product_Single_Select__c}"/>
 <br/> <br/>
 <apex:outputLabel 
      value="Values selected from standard pickist field (MultiSelect)"/>
 <apex:outputLabel value = "{!objCase.Product__c}"/> 
 <br/> <br/>
 <apex:outputLabel 
     value="Values selected from custom pickist (Multiselect= false)"/>
 <apex:outputLabel value = "{!strProduct}"/>
  <br/> <br/>
 <apex:outputLabel 
    value="Values selected from custom pickist (Multiselect= true)"/>
 <apex:outputLabel value = "{!strProducts}"/>   
</apex:outputpanel>
</apex:form>
</apex:page>

Happy Coding !!!!


Wednesday 16 March 2016

Extension to salesforce standard formula for calculating the date difference in Minute and Seconds

Hello Awesome Salesforce Developers ,

Few days ago, I got a requirement to calculate the elapsed time between Case open date and Case closed date.

There is standard formula of salesforce which will calculate the Date Difference (Field with datatype formula and return type text)









The above formula will calculate the difference in days, hours and minutes.

My requirement is to extend this formula to calculate the difference in days, hours, minutes and seconds.

Let's calculate it !!! 

First will understand some factors about datetime.
1 Day = 24 Hours
24 Hours = 1440 Minutes
1440 Minutes = 86400 Seconds

Create a field with datatype formula and return type as Text. Use the below formula and you are set !!!!

FLOOR() -  Used to round up to nearest integer.
ROUND() - Returns the nearest number to a number you specify. 


Cheers !!!!

Monday 7 March 2016

Redirecting to selected list view

Hello Awesome Salesforce Developers ,

In one of my project I got following requirement :
1. Add custom button in list view. 
2. User will select the list view and select the records from the list view and on the     custom button click (added in step (1) ) user will be redirected to another page       where he perform certain operation.
3. After operation is over then on click of another custom button on Visualforce          page say "Return to List View" user should redirect to list view he selected              in the step (2)


The challenging part in this task was to get selected list view as we need to redirect user to list view selected in previous step.

If you check the URL of any list view it is constructed as follows : 

https://ajaytrailhead-dev-ed.my.salesforce.com/001?fcf=00B28000003fFfg
----------- Domain Name -------------------------/Object Prefix ? fcf= Id of the list view 

"fcf" is the parameter used to identify the list view Id. 

So you just need to construct the redirection URL in the above format.

Next question is from where we will get value for "fcf" parameter ?
Use Apexpages.currentpage.getParameters().get('fcf'); and you are set !

We got all the parameter now we just need to form the redirect URL explained in the following class.



















The associated Visual force page :











The custom button code 










Add above custom button to Search Layout --> Account List View.

Cheers !!! 



Thursday 3 March 2016

Parsing the CSV file in salesforce

Hi Salesforce folks ,

Many times you get a request to parse the contents of csv file and create records in salesforce objects.

In this blog I am going to demonstrate you how the parse the csv file using Apex.

Before starting the Apex code I would like to give you information about csv file

What is a csv file ?

CSV file having records as comma separated values.It stores the tabular data in plain text. Each line in the file is a record.

How the data is structured in the csv file ?
There are various formats of saving data to the csv file for example
"Name", "Address" => Header Row
"Ajay","Pune" => Data
"Ajay","Talegaon,Pune" => You may get data like this : Nested inside the "" with comma(,)

You can just google it for the various formats.

In the below snippet I am parsing the csv file and creating records in the Account salesforce object.

Assumption : You got csv file data in a string format. If you are getting data in blob format then you can convert it to string using string.valueof function.

Pass the string value to the below function and take a cup of tea !!!! :)






You can enhance the above code by adding exception handling of your choice.

Limitation : 
The above code will work for 10000 records.(Standard Salesforce DML Limit).

You can overcome this limit by writing batch class. Send records to batch class for further processing.

Many more blogs are coming soon !!!

Stay connected !!!

Cheers !!