Thursday, September 28, 2017

Future Method


Future Method:


1. Future method is the asynchronous method which runs in background and used to run processes in separate thread, when system resources become available.
2. We use @future annotation to define future method.
3. We can call future method for long-running processes, such as calling external we services or any operation you would like to run in its own thread.
4. We can also use future method to overcome Mixed DML Error.
5. Each future method is queued and executes when system resource available then execution of   future method does not have to wait for the completion of long running processes.
6.  Method with the future annotation must be static method with void return type.
7. Methods with the future annotation cannot take sObjects or objects as arguments.
      Reason: The reason why sObjects cannot be passed as arguments to future methods is because the sObjects might change between you call the future method and the time it executes.

  Syntax of future method: 



global class FutureMethodClass
{
    @future
    public static void processRecords(List<ID> recordIds)
    {  
         // Get those records based on the IDs
         List<Account> accts = [SELECT Name FROM Account WHERE Id IN :recordIds];
         // Process your  records here
    }
}

 Now if you want to call any external web service from future method then use syntax, see below-

global class FutureMethodClass
{
    @future(callout=true)
    public static void processRecords(List<ID> recordIds)
    {  
          // Perform a callout to an external service
    }
}

Prevent Mixed DML Error Using Future Method

Now, let’s see how to use future method and how to overcome MIXED DML Error by using future method, firstly understand about Mixed DML Error.

What is Mixed DML ERROR?

This error occurs when you are performing DMLs on setup objects and Non-setup objects simultaneously. So in other words we can say in a piece of code you cannot INSERT/UPDATE/DELETE commands on Setup objects and Non-Setup objects together.
Setup Objects are—User, RecordType, Profile etc.
Non-Setup Objects are—All other  general objects like native objects(Standard Objects) and custom objects falls in to category of Non-setup objects. Ex- Contact, Account, Lead etc.

Now remove this error by taking an example—

Scenario is, insert user with non-null role after account has been inserted

Step-1:
Create apex class and write code to insert account and then insert user like-

public class MixedDMLFuture
{
    public void insertUserWithAccount()
    {
        Account a=new Account(Name='Aman');
        insert a;
        Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
        UserRole r = [SELECT Id FROM UserRole WHERE Name='COO'];
        // Create new user with a non-null user role ID
        User u = new User(alias = 'mruiz', email='gargarun25322@gmail.com',
            emailencodingkey='UTF-8', lastname='garg',
            languagelocalekey='en_US',
            localesidkey='en_US', profileid = p.Id, userroleid = r.Id,
            timezonesidkey='America/Los_Angeles',
            username='gargarun25322@gmail.com');
        insert u;
        Util.insertUserWithRole(
            'gargarun25322@gmail.com', 'mruiz',
            'gargarun25322@gmail.com', 'garg');
    }
}
   Now run this class from developer console using anonymous window
MixedDMLFuture m=new MixedDMLFuture();
m.insertUserWithAccount();


Now to overcome this error, Inserting of user must be done in separate thread from DML operation and use future method to achieve this.

The future method, insertUserWithRole which is defined in FutureClass, perform the insertion of user with CEO role. insertUserWithAccount method in MixedDMLFuture class insert an account and calls the future method, insertUserWithRole.

This is the definition of the FutureClass class, which contains the future method for inserting a user with a non-null role.


public class FutureClass
{
    @future
    public static void insertUserWithRole(String uname, String alias, String email, String lname) {

        Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
        UserRole r = [SELECT Id FROM UserRole WHERE Name='CEO'];
        // Create new user with a non-null user role ID
        User u = new User(alias = alias, email=email,
            emailencodingkey='UTF-8', lastname=lname,
            languagelocalekey='en_US',
            localesidkey='en_US', profileid = p.Id, userroleid = r.Id,
            timezonesidkey='America/Los_Angeles',
            username=uname);
        insert u;
    }
}

   This is the class containing the main method that calls the future method defined previously.

public class MixedDMLFuture
{
    Public static void insertUserWithAccount()
    {
        Account a=new Account(Name='Aman');
        insert a;
       
        FutureClass.insertUserWithRole(
            'gargarun25322@gmail.com', 'mruiz',
            'gargarun25322@gmail.com', 'garg');
    }
}

      Now run the class then no error will come and you can insert both account and user successfully.

Limitations of Future Method-

  1. A future method cannot invoke another future method.
  2. No more than 50 methods call per Apex execution.
  3. The maximum number of future method invocation per a 24 hours period is 250,000 or the number of user licenses in your organization multiplied by 200, whichever is greater

Wednesday, September 27, 2017

What are setup and non-setup objects in Salesforce?

There are two types of objects that you can interact with on the platform. One is "setup" object and the other is a "non-setup" object. 
A "setup" object is one that must be edited from the setup or builder area of the platform. 

 Setup objects are 

* User

* RecordType

* Profile

 etc

and all other general objects i.e.

* Contact

* Account

* Lead

etc

are the None setup objects.

If you run a DML operation on setup object you can't run a DML operation on None setup object in the same context, it will show the "Mixed_DML_operation" error.


For example, you cannot insert an account and then insert a user or a group member in a single transaction.

Salesforce don’t allow you to run.

 Solving MIXED_DML_OPERATION exception in Apex

Sometimes there is a need to update a Setup Object and a non-setup Custom object in Apex on the same event. It can get really tricky if the operation is needed on 2 Setup Objects instead 1. The solution lies in using RunAs function in Apex and putting one of the operation in @future construct. As an example, the following code is not expected to work-
...
User testuser = [Select id, isActive from User where isActive=true Limit 1];
testuser.isActive = false;
update testuser;
CustomObject__c co = new CustomObject__c(name='Test object');
insert co;
...
and this should be re-written as -
...
@future
function updateUser() {
User testuser = [Select id, isActive from User where isActive=true Limit 1];
testuser.isActive = false;
update testuser;
}

CustomObject__c co = new CustomObject__c(name='Test object');
insert co; 

Prevent the contact creation while Lead conversion. Is this possible?


Yes,It's Possible.

 Description:

Create A checkbox Field in lead object (Created_By_Lead_Convert) and Select default checked .
Create A checkbox Field in Contact object (Created_By_Lead_Convert) and Select default unchecked.

Than go the Lead Object Fields And click on "  Map Lead Fields " button And map the Contact field with lead field.

Write A validation on contact object:

AND(
ISNEW(),
Created_By_Lead_Convert__c = TRUE,
CreatedBy.Profile.Name <> "System Administrator")


Thursday, September 7, 2017

Generic Pagination with Visualforce Component

Apex Class for Component : GenericPaginationComponentContrl


public class GenericPaginationComponentContrl {    
    //Stores the records which are supplied to the 'Records' attribute.
    public Sobject[] sObjLst {get;set;}    
    public ApexPages.StandardSetController con {
        get {
            //initializing con with the records.
            if(con == null)
                con = new ApexPages.StandardSetController(sObjLst);
            
            //Setting the pagination size
            con.setPageSize(5);
            
            return con;
        }
        set;
    }    
    //Method which returns subset of records from the sObjLst.
    public List<sobject> getSobjRecords() {        
        //Type Casing the records and returning to display on the page.
        return (List<sobject>)con.getRecords();
    }
}


Visualforce Component : GenericPaginationComponent

<apex:component controller="GenericPaginationComponentContrl">
    
    <!-- Attributes to accept the parameters -->
    <apex:attribute name="Records" Type="Sobject[]" assignTo="{!sObjLst}" required="true" description="Accepts list of records of any object and assign to a variable in controller class"/>
    <apex:attribute name="Fields" Type="String[]" required="true" description="Accepts list of field API names of a sobject in string format"/>
    <apex:attribute name="Title" Type="String" required="true" description="Accepts the title of the section"/>
    
    <!-- Table which displays records along with pagination -->
    <apex:form >
        <apex:pageBlock >
            <apex:pageBlockSection columns="1" title="{!Title}" id="pbSec">
                <apex:pageBlockTable value="{!SobjRecords}" var="sObj">
                    <!-- Dispalys the multiple columns based on the user input -->
                    <apex:repeat value="{!Fields}" var="fld">
                        <apex:column value="{!sObj[fld]}"/>
                    </apex:repeat>
                </apex:pageBlockTable>
                
                <!-- Dispalys pagination buttons -->
                <apex:panelGrid columns="5">
                    <apex:commandButton value="First" action="{!con.first}" disabled="{!!con.hasPrevious}" status="pagStatus" reRender="pbSec"/>
                    <apex:commandButton value="Previous" action="{!con.previous}" disabled="{!!con.hasPrevious}" status="pagStatus" reRender="pbSec"/>
                    <apex:commandButton value="Next" action="{!con.next}" disabled="{!!con.hasNext}" status="pagStatus" reRender="pbSec"/>
                    <apex:commandButton value="Last" action="{!con.last}" disabled="{!!con.hasNext}" status="pagStatus" reRender="pbSec"/>
                    <apex:actionStatus startText="Fetching..." id="pagStatus"/>
                </apex:panelGrid>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:component>


Visualforce Page: 

<apex:page controller="GenericPaginationUsageContrl" tabStyle="Account">
  
  <!-- Default name space is : C ; Referring the Visualforce Component -->
  <c:GenericPaginationComponent records="{!accLst}" fields="{!accFieldLst}" title="Accounts"/>
  <c:GenericPaginationComponent records="{!conLst}" fields="{!conFieldLst}" title="Contacts"/>
  <c:GenericPaginationComponent records="{!oppLst}" fields="{!oppFieldLst}" title="Opportunities"/>
  
</apex:page>

Apex Class For Page : GenericPaginationUsageContrl

public class GenericPaginationUsageContrl {
    
    //Declaring variables to store list of records.
    public List<Account> accLst {get;set;}
    public List<Contact> conLst {get;set;}
    public List<Opportunity> oppLst {get;set;}
    
    //Declaring variables to store list of Field API names in string format.
    public List<String> accFieldLst {get;set;}
    public List<String> conFieldLst {get;set;}
    public List<String> oppFieldLst {get;set;}
    
    //Default Constructor    
    public GenericPaginationUsageContrl() {
        
        //Querying the records from the database.
        accLst = [select Name, AccountNumber, Fax, Phone, Industry from Account limit 100];
        conLst = [select Name, AccountId, Email, Phone from Contact limit 100];
        oppLst = [select Name, AccountId, Amount from Opportunity limit 100];
        
        //Preparing the list of fields to display in the table.
        accFieldLst = new List<String>{'Name', 'AccountNumber', 'Fax', 'Phone', 'Industry'};
            conFieldLst = new List<String>{'Name', 'AccountId', 'Email', 'Phone'};
                oppFieldLst = new List<String>{'Name', 'AccountId', 'Amount'};
                    }
    
}



Screen Shot Of the output: