Tuesday, November 20, 2018

Future Method and Mixed DML Errors in Salesforce.com

Future Method and Mixed DML Errors in Salesforce.com

When we perform DML on setup and non-setup sObject simultaneously then mixed dml errors occurs. 

To avoid mixed DML errors
1) Create a method that performs a DML operation on one type of sObject.
2) Create a second method that uses the future annotation to manipulate a second sObject type.

Apex Class

public class MixedDMLFuture {
    public static void useFutureMethod() {
        // First DML operation
        Account a = new Account(Name='Acme');
        insert a;
       
        // This next operation (insert a user with a role)
        // can't be mixed with the previous insert unless
        // it is within a future method.
        // Call future method to insert a user with a role.
        
UtilClass.insertUserWithProfileAndRole('username@abc.com', 'myAlias',
            'emailAddress@abc.com', 'Bill','Business Head', 'CEO');       
    }
}


Apex Future Method

public class UtilClass {
    @future
    public static void insertUserWithProfileAndRole(
        String userNameIs, String aliasIs, String emailAdd, String lastNameIs, String profileName, String roleName) {
        
//Get the profile Id 
        Profile profile = [SELECT Id FROM Profile WHERE Name=:profile];
//Get the role Id
        UserRole role = [SELECT Id FROM UserRole WHERE Name=:role];
        
// Create new user with a non-null user role ID
        User usr = new User(alias = aliasIs, 
            email=emailAdd,
            emailencodingkey='UTF-8', 
            lastname=lastNameIs,
            languagelocalekey='en_US',
            localesidkey='en_US', 
            profileid = profile.Id, 
            userroleid = role.Id,
            timezonesidkey='America/Los_Angeles',
            username=userNameIs);
        insert usr;
    }
}


Apex Test Class
============

Mixed DML Operations in Test Methods :-
The System.runAs() block runs in the current user’s context. It creates a test user with a role and a test account, which is a mixed DML operation.
Using Test.startTest and Test.stopTest to bypass the mixed DML error in a Test Method.

@isTest
private class insertUserWithProfileAndRoleTest{

    static testMethod void createTestUser(){

        Profile pro = [Select Id From Profile Where Name = 'System Administrator'];

        //Create setup sObject records (User)
        User usr = new User();
        usr.FirstName = 'First';
        usr.LastName = 'Last';
        usr.Email = 'first@test.com';
        usr.Username = 'astest@test.com';
        usr.Alias = 'first';
        usr.ProfileId = pro.Id;
        usr.TimeZoneSidKey = 'America/Denver';
        usr.LocaleSidKey = 'en_US';
        usr.EmailEncodingKey = 'UTF-8';
        usr.LanguageLocaleKey = 'en_US';
        insert usr;

        system.debug('usr is ' + usr);

        System.runas(usr){

            //Create non-setup records & run tests here
        }
    }
}












Asynchronous Apex: Queueable Apex in Salesforce.com

Queueable Apex

The best features of Queueable Apex is job chaining. If you ever need to run jobs sequentially, Queueable Apex is only option.

To chain a job to another executing job, submit the second_job by using execute() method of your queueable class. You can add only one job from an executing job, which means that only one child job can exist for each parent job

For example, if you have a second class called Second_Job that implements the Queueable interface, you can add this class to the queue in the execute() method as follows:

Queueable Syntax:

public class SomeClass implements Queueable { 
    public void execute(QueueableContext context) {
        //awesome code here
    }
}

Chaining Jobs:

public class FirstQueClass implements Queueable { 

    public void execute(QueueableContext context) { 

        // Awesome processing logic here   
        // Chain this job to next job by submitting the next job
        System.enqueueJob(new Second_Job());

    }
}


Note: Once again, testing has a slightly different pattern. You can’t chain queueable jobs in an Apex test, doing so results in an error. 
To avoid errors, you can check if Apex is running in test context by calling Test.isRunningTest() before chaining jobs.

Scenario 1:

The following apex code takes a collection of Account records, sets the parentId for each record, and then updates the records in the database.


public class UpdateParentAccountQue implements Queueable {

    private List<Account> accounts;
    private ID parent;

    public UpdateParentAccount(List<Account> records, ID id) {

        this.accounts = records;
        this.parent = id;
    }

    public void execute(QueueableContext context) {

        for (Account account : accounts) {
        account.parentId = parent;
        
        //perform other processing or callout
        
        }
        update accounts;
    }  
}


Add this class as a job on the queue

//find all accounts in ‘NY’
List<Account> accounts = [select id from account where billingstate = ‘NY’];

//find a specific parent account for all records
Id parentId = [select id from account where name = 'ACME Corp'][0].Id;

//instantiate a new instance of the Queueable class
UpdateParentAccount updateJob = new UpdateParentAccount(accounts, parentId);

//enqueue the job for processing
ID jobID = System.enqueueJob(updateJob);

Monitor progress either through the Apex Jobs page or programmatically by querying AsyncApexJob.

SELECT Id, Status, NumberOfErrors FROM AsyncApexJob WHERE Id = :jobID

Test Class:

@isTest
public class UpdateParentAccountTest {

    @testSetup 
    static void setup() {
        List<Account> accounts = new List<Account>();
        // add a parent account
        accounts.add(new Account(name='Parent'));
        // add 100 child accounts
        for (Integer i = 0; i < 100; i++) {
            accounts.add(new Account(
                name='Test Account'+i
                ));
        }
        insert accounts;
    }
    
    static testmethod void testQueueable() {
     //Query for test data to pass to queueable class
     Id parentId = [Select Id From Account Where Name = 'Parent'][0].Id;
     List<Account> accounts = [Select Id, Name From Account Where Name Like 'Test Account%'];
     //Create our Queueable instance
     UpdateParentAccount updater = new UpdateParentAccount(accounts, parentId);
     //Test.startTest() and Test.stopTest() block to force async processes to run
    Test.startTest();        
        System.enqueueJob(updater);
    Test.stopTest();        
     //Validate the job ran. Check if record have correct parentId now
     System.assertEquals(100, [select count() from account where parentId = :parentId]);

    }

}











Asynchronous Apex: Scheduled Apex in Salesforce.com

Scheduled Apex 

Scheduled Apex lets you run Apex classes at a specified time; for example, This is ideal for daily or weekly maintenance tasks using Batch Apex. 

To invoke Apex classes at specific times, first implement the Schedulable interface for the class. Then, schedule an instance of the class to run at a specific time using the System.schedule method.

Note: Governor limits in case of asynchronous are higher as compared to synchronous. 

Schedule Apex Syntax:

global class SomeClass implements Schedulable {
    global void execute(SchedulableContext cntx) {
        // awesome code here
    }
}

Schedulable Apex

global class RemindOpptyOwners implements Schedulable {

    global void execute(SchedulableContext cntx) {
        List<Opportunity> opptys = [Select Id, Name, OwnerId, CloseDate  From Opportunity  Where IsClosed = False AND CloseDate < TODAY];
        
// Call util class and method here to create a task for each opportunity in the list
        TaskUtils.remindOwners(opptys);
    }
    
}

System.Schedule Method

It takes three arguments: 
1) Job name

2) CRON expression used to represent the time and date the job is scheduled to run

3) Instance variable of the class object

//Create a instance variable of the class object
RemindOpptyOwners reminder = new RemindOpptyOwners();

//Seconds Minutes Hours Day_of_month Month Day_of_week optional_year
String sch = '20 30 8 10 2 ?';

String jobID = System.schedule('Remind Opp Owners', sch, reminder);

Sec Min Hours Days Month WeekDay  Years 

0 0,30      * * * ? *

0 5 20 ? Jan,May Mon-Fri 2017

* --> All Values
? --> No Values


Monitoring Scheduled Jobs

It uses a job_Id variable which is returned from the System.schedule method.

CronTrigger ct = [Select TimesTriggered, NextFireTime From CronTrigger Where Id= :jobID];

Monitoring Queued Jobs

To query information about your submitted job, perform a SOQL query on AsyncApexJob by filtering on the job ID that the System.enqueueJob method returns.

AsyncApexJob jobInfo = [SELECT Id, Status, JobItemsProcessed,
 NumberOfErrors FROM AsyncApexJob WHERE Id = :jobID];

Schedule Apex Consideration:
1) You can only have 100 scheduled Apex jobs at one time, and the maximum number of scheduled Apex executions per a 24-hour period. 

2) Synchronous Web service callouts are not supported from scheduled Apex. But we can make an asynchronous callout by placing the callout in a method annotated with @future(callout=true) and call this method from scheduled Apex. 

(However, if your scheduled Apex executes a batch job, callouts are supported from the batch class.)












Asynchronous Apex: Batch Apex in Salesforce.com

Batch Apex

Batch Apex is used to run large jobs (thousands or millions of records!) that would exceed normal processing limits.
1) Every transaction starts with a new set of governor limits, which makes it easier to ensure that code stays within the governor execution limits.
2) It allow partial DML, which means if one batch fails to process successfully, all other successful batch transactions aren’t rolled back.

Batch Apex Syntax:

global class BatchClass implements Database.Batchable<sObject> {

    global Database.QueryLocator start(Database.BatchableContext bc) {
        // collect the batches of records or objects to be passed to execute
    }

    global void execute(Database.BatchableContext bc, List<P> records){
        // process each batch of records
    }    

    global void finish(Database.BatchableContext bc){
        // execute any post-processing operations
    }    

}

To Invoke a Batch Class and get Batch_id:

//1. Create an instance of batch class
MyBatchClass myBatchObject = new MyBatchClass(); 

//2. Pass instance of batch class into Database.excuteBatch method to get batch_id
Id batchId = Database.executeBatch(myBatchObject);

Id batchId = Database.executeBatch(myBatchObject, 100);

//Use batch_id to To fetch info about processing batch
AsyncApexJob job = [Select Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors From AsyncApexJob Where Id = :batchId ];

Using Database.Stateful in Batch Apex
It is used to retain state of execution across transactions.

global class UpdateContactAddresses implements Database.Batchable<sObject>, Database.Stateful {
    
    // instance member to retain state across transactions
    global Integer recordsProcessed = 0;

    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(
            'SELECT ID, BillingStreet, BillingCity, BillingState, ' +
            'BillingPostalCode, (SELECT ID, MailingStreet, MailingCity, ' +
            'MailingState, MailingPostalCode FROM Contacts) FROM Account ' + 
            'Where BillingCountry = \'USA\''
        );
    }

    global void execute(Database.BatchableContext bc, List<Account> scope){
        // process each batch of records
        List<Contact> contacts = new List<Contact>();
        for (Account account : scope) {
            for (Contact contact : account.contacts) {
                contact.MailingStreet = account.BillingStreet;
                contact.MailingCity = account.BillingCity;
                contact.MailingState = account.BillingState;
                contact.MailingPostalCode = account.BillingPostalCode;
                // add contact to list to be updated
                contacts.add(contact);
                // increment the instance member counter
                recordsProcessed = recordsProcessed + 1;
            }
        }
        update contacts;
    }    

    global void finish(Database.BatchableContext bc){
        System.debug('Records processed===> '+recordsProcessed.);
        AsyncApexJob job = [SELECT Id, Status, NumberOfErrors, 
            JobItemsProcessed,
            TotalJobItems, CreatedBy.Email
            FROM AsyncApexJob
            WHERE Id = :bc.getJobId()];
        // call some utility to send email
        EmailUtils.sendMessage(a, recordsProcessed);
    }    

}

Testing Batch Apex

@isTest
private class UpdateContactAddressesTest {

    @testSetup 
    static void setup() {
        List<Account> accounts = new List<Account>();
        List<Contact> contacts = new List<Contact>();
        // insert 10 accounts
        for (Integer i=0;i<10;i++) {
            accounts.add(new Account(name='Account '+i, 
                billingcity='New York', billingcountry='USA'));
        }
        insert accounts;

        // find the account just inserted. add contact for each
        for (Account account : [select id from account]) {
            contacts.add(new Contact(firstname='first', 
                lastname='last', accountId=account.id));
        }
        insert contacts;
    }

    static testmethod void test() {        
        Test.startTest();
        UpdateContactAddresses uca = new UpdateContactAddresses();
        Id batchId = Database.executeBatch(uca);
        Test.stopTest();

        // after the testing stops, assert records were updated properly
        System.assertEquals(10, [select count() from contact where MailingCity = 'New York']);
    }    
}

Abort Running Batch

1) Abort one Apex Batch Job by Job_Id.
Setup -> Monitoring -> Apex Jobs to get Batch Job Id.
Finally run below statement from anonymous block.

//pass JobId to System.abortJob() method
System.abortJob(JobID);

2) Abort bulk Apex Job by Job_Id.

// Loop through jobs located by name that we need to abort
List<CronTrigger> jobsToAbort = [Select Id, CronJobDetail.Name, CronJobDetail.JobType From CronTrigger Where CronJobDetail.Name like 'WorkOrder%'];

for(CronTrigger ct : jobsToAbort){
// abort the job, try/catch because the job might not exist
    try {
        system.abortJob(ct.id);
    } catch (exception e) {

System.debug('Exception Message: '+e.getMessage());

}
}


Batch Apex Consideration

1) Up to 5 queued or active batch jobs are only allowed.

2) A maximum of 50 million records can be returned in the Database.QueryLocator object. If more than 50 million records are returned, the batch job is immediately terminated and marked as Failed

3) If no size is specified as optional scope parameter of Database.executeBatch, Salesforce breaks the records returned by the start method into batches of 200, and then passes each batch to the execute method. 
Note: Apex governor limits are reset for each execution of execute.

4) The start, execute, and finish methods can implement up to 10 callouts each

5) The maximum number of batch executions is 250,000 per 24 hours

6) Only one batch Apex job's start method can run at a time in an organization. Batch jobs that haven’t started yet remain in the queue until they're started. 











Asynchronous Apex: Future Method in Salesforce.com

Future Method

1) Making callouts to external web services. 
i.e. When we are making callouts to external web services from a trigger or after performing a DML operation, we must use a future or queueable method.

2) Process we want to run in its separate thread in the back-end.
i.e. When we are working on some sort of resource consuming calculation or processing of records.

3) Isolating DML operations on different types of sObject.
i.e. When we are performing DML on setup and non-setup sObject types, it isolates DML to prevent the "Mixed DML Errors".


Future Method Syntax:
global class FutureClass {
     @future
     public static void futureMethod(List<Id> recordIds) {
     List<Account> accounts = [Select Id, Name from Account Where Id IN :recordIds];
     // process account records to do awesome stuff
     }
}


Future Method Considerations

1) Future methods must be static methods and return a void.

2) Parameters must be primitive data types, arrays of primitive data types, or collections of primitive data types.
Note:- Future methods can’t take standard or custom objects as arguments.

3) Common pattern is to pass a List<record IDs> that you want to process asynchronously.

4) Future methods can’t be used in Visualforce controllers in getMethod(), setMethod(), nor in the constructor.

5) You can’t call a future method from a future method.

6) You can not invoke a trigger that calls a future method while running a future method.

7) The getContent() and getContentAsPDF() methods can’t be used in future method.

8) You’re limited to 50 future calls per Apex invocation.

9) To test future methods, enclose your test code between the test.startTest() and test.stopTest() test methods.


Saturday, November 17, 2018

REGEX function, Validation Rule in Salesforce.com

REGEX or Regular Expression in Salesforce

REGEX(Input_Field_Name, 'Input_Format_Expression')

i.e. BLKPR7234B or blkpr7234b: 

NOT REGEX( PAN_Number__c , '[A-Z,a-z]{5}[0-9]{4}[A-Z,a-z]{1}' )

This function is Boolean, which is used to compare text format between the input text and the specified expression. If both the text format and expression are equal, then it returns true, else it returns false.

Prepare the regular expression by using wild-card characters as below.

[ ] --> Indicates a collection of characters

[A-Z] --> Indicates a collection of characters falls
between A-Z.

[a-z] --> Indicates a collection of characters falls
between a-z.

[A-Z,a-z] --> Indicates a collection of characters falls
between A-Z or a-z or combination of both.

[0-9] --> Indicates a Number, where each digit should be
falls between 0-9.

{Number} --> Indicates the Number of characters should be 
used in the string.

[JKL] --> Indicates a collection of characters, where
each characters should be either J/K/L.

 -  --> Represents a Single Character.

PAN Number Format: BLKPR 7234 J

BLKPR --> 5
7234   --> 4
J         --> 1

BLKPR7234J:
Format: [A-Z]{5}[0-9]{4}[A-Z]{1}


bLkPr7234j: 
Format: [A-Z,a-z]{5}[0-9]{4}[A-Z,a-z]{1}

//Create a validation rule, to validate the PAN Number format.
Validation Condition: 

i.e. BLKPR7234B or blkpr7234b: 

NOT REGEX( PAN_Number__c , '[A-Z,a-z]{5}[0-9]{4}[A-Z,a-z]{1}' )













Saturday, November 10, 2018

Mixed DML Error in Apex in Salesforce.com

Mixed DML Error 
Concept of Mixed DML error is to prevent transaction from incorrect user level access in the org.
Mixed DML error occurs when you try to insert or update the setup and non setup object records simultaneously.
To overcome from this restriction you need to insert or update setup and non setup object records separately, where one method must be implementing @future keyword.

Note:- This restriction exists because some sObjects affect the user's access to records in the org.

e.g. you can’t update an account and a user role in a single transaction but deleting has no restriction.

These fields of user object can not be updated
* Username
* ProfileId
* UserRoleId
* IsActive
* ForecastEnabled
* IsPortalEnabled

ApexCode1A

public class MixedDMLClass {
    public static void createAccountAndUser () {
        // First DML operation
        Account account = new Account(Name='Acme');
        insert account;
       
        // Second DML operation (insert a user with a role) can't be mixed with              // the previous insert unless it is within a future method.
        // Call future method to insert a user with a role.
        CreateUserUtil.insertUserWithRole(
           'allen','allenAs', 'allen@gmail.com', 'paul'
           );       

    }
}

ApexCode1B

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

        Profile std_profile = [Select Id From Profile Where Name='Standard User'];
        UserRole coo_role = [Select Id From UserRole Where Name='COO'];

        // Create new user with a non-null user role ID
        User user = new User(
lastname=lname,
username=uname,
profileid = std_profile .Id,
userroleid = coo_role.Id,
alias = alias,
email=email,
emailencodingkey='UTF-8',
languagelocalekey='en_US',
localesidkey='en_US',
timezonesidkey='America/Los_Angeles'
);

        insert user;
    }
}










Thursday, November 8, 2018

Trigger to count total number of contacts of account in Salesforce.com

Rollup Summary Trigger on Account in Salesforce.com

trigger numberContactsEachAccount on Contact (after insert,after delete,after undelete,after update) {

Set<Id> accountIds = new Set<Id>();
 
//for insert and undelete operation
if(Trigger.isAfter && (Trigger.isInsert || Trigger.isUndelete)){
        for(Contact con : Trigger.New){
        accountIds.add(con.AccountId);
        }
List<Account> accList = [Select Id, Number_of_Contacts__c, (Select Id from Contacts) From Account Where Id In :accountIds];

for(Account acc : accList){
        acc.Number_of_Contacts__c = acc.contacts.size();
        }
update accList;
}

//for delete operation
if(Trigger.isAfter && Trigger.isDelete){
        for(Contact con : Trigger.old){
            accountIds.add(con.AccountId);
            }
     
List<Account> accList = [Select Id, Number_of_Contacts__c, (Select Id From Contacts) From Account where Id In :accountIds];

for(Account acc : accList){
acc.Number_of_Contacts__c = acc.contacts.size();
}
update accList;
}

//for update trigger
if(Trigger.isAfter && Trigger.isUpdate){
for(Contact con : Trigger.newMap.values()){
            //comparing accountId of both new and old contacts
            if(con.AccountId != Trigger.oldMap.get(con.id).AccountId ) {
                accountIds.add(con.AccountId);
                accountIds.add(Trigger.oldMap.get(con.id).AccountId);
            }
}
List<Account> accList = [Select Id, Number_of_Contacts__c, (Select Id From Contacts) From Account Where Id In :accountIds];

for(Account acc : accList){
acc.Number_of_Contacts__c = acc.contacts.size();
}
update accList;
    }
}






Click on image to view...






Governor Limits in Salesforce.com

Governor Limits

Governor Limits come into picture because of multitenant architecture of Salesforce.com, which enforce resources availability for each users.

Total number of SOQL queries >> 100 >> (Asynchronously: 200)


Total number of records retrieved by SOQL queries >> 50,000

Total number of SOSL queries issued >> 20

Total number of records retrieved by a single SOSL query >> 2,000

Total number of DML statements issued >> 150

Total number of records processed as a result of >> 10,000

DML statements or
Database.emptyRecycleBin or 
Approval.process 

Total heap size >> 6 MB >> (Asynchronously: 12 MB)              

Maximum CPU time on the servers >> 10 sec >> (Asynchronously: 60 sec)

Maximum Callouts >> 100

Total number of sendEmail methods allowed >> 10

Total number of the future call >> 50

Number of Queueable job added to the queue >> 50

Total number of records retrieved by Database.getQueryLocator >>       10,000

Maximum execution time for each Apex transaction >> 10 min

Total stack depth for any Apex invocation that >> 16

recursively fires triggers due to insert, update or delete 
            
Total number of callouts  in a transaction >> 10
(HTTP requests or Web services calls)

Maximum timeout for all callouts in a transaction >> 120 sec
(HTTP requests or Web services calls)

Number of mobile apex push call >> 10










Bulkified and Non-Bulkified Apex Code concepts in Salesforce.com

Non-Bulkified code:
It can not process large set of data and eventually can hit governor limit because it works on one record.

For example: It can process only one (first) account record from the Trigger.new collection, but if more than one account has initiated this trigger, then those additional records will not be processed.

trigger nonBulkifiedTrigger on Account (before insert, before update) {

   //It can get only first record from the collection


   Account acct = Trigger.new[0];
   List<Contact> contacts = [Select id, firstname, lastname, email 
                                                        From Contact 
                                                        Where accountId = :acct.Id];
   
}

Bulkified Code:
It can process large set of data and escape governor limit because it works on list of records.

For example: It can process all account record from the Trigger.new collection.

trigger bulkifiedTrigger on Account (before insert, before update) {

    //Loop through all records in the Trigger.new collection
    for(Account a: Trigger.new){

    //Concatenate the Name and billingState into the Description field
    a.Description = a.Name + ':' + a.BillingState
   }
}










Handle exception by Try, Catch and Finally statement in Apex in Salesforce.com

Try-Catch-Finally Statements Syntax:

try {

      //Perform some operation that might cause an exception.

} catch (exception e){ 

      //Handle generic exception here.

catch (dmlexception de){ 

      //Handle dml exception here.

finally { 

      //this block of code always execute and allows you to clean up your code.

}

Apex Code

try {
  // Perform some operation that might cause an exception.
    Merchandise__c m = new Merchandise__c();
    insert m;

    // This doesn't execute since insert causes an exception
    System.debug('Statement after insert.');

} catch (Exception e) {
    
   // Handle generic exception here.
   }catch(DmlException e) {
    System.debug('The following exception has occurred: ' + e.getMessage());
    
 } finally {
    // Perform some clean up.
}










DML Statement vs Database Class Method in Salesforce.com

DML Statement:
It does not support partial execution, which means if an error encountered an apex records execution at that moment whole execution will rolled back and throw error messages. Use try and catch block to handle exceptions and  throw error messages as well. 

Database Class Methods:

It supports partial execution, which means if an error encountered an apex records execution at that moment the records causing errors will be expelled from execution, and successfully executed record will be processed. 

Note: Database.Insert(Boolean);


Partial ==> Database.Insert (false)

No Partial ==> Database.Insert (true)

ApexCode1A


// Create the list of sObjects to insert
List<Account> acctList = new List<Account>();
acctList.add(new Account(Name='Acme1'));
acctList.add(new Account(Name='Acme2'));

// DML statement

insert acctList;

ApexCode1B


// Create the list of sObjects to insert
List<Account> acctList = new List<Account>();
acctList.add(new Account(Name='Acme1'));
acctList.add(new Account(Name='Acme2'));

// DML statement (Partial)

Database.SaveResult[] srList = Database.insert(acctList, false);

// Iterate through each returned result

for (Database.SaveResult sr : srList) {
    if (sr.isSuccess()) {
        // Operation was successful, so get the ID of the record that was processed
        System.debug('Successfully inserted account. Account ID: ' + sr.getId());
    } else {
        // Operation failed, so get all errors               
        for(Database.Error err : sr.getErrors()) {
            System.debug('The following error has occurred.');                   
            System.debug(err.getStatusCode() + ': ' + err.getMessage());
            System.debug('Account fields that affected this error: ' + err.getFields());
        }
    }
}

SaveResult Methods (Instance Methods)

* getErrors()
* getId()
* isSuccess()










Best Practices for writing Apex Triggers in Salesforce.com

Best Practices for writing Apex Triggers

1) Follow only one trigger for each object.

2) Logicless Trigger, which means create a helper method to keep all logic and finally call that helper class and method in the trigger.

3) Bulkify helper class or trigger to avoid governor limits.

4) Avoid using SOQL and DML within for loop.

5) Use collections like list, set and map to store on large set of data.

6) Use streamline queries and efficient for loop to process large set of data.

7) Use addError() method to show error message upon validation.

8) Use try and catch block to handle exception and throw error messages.










Best Practices for writing Apex Test Class in Salesforce.com

Best Practices for writing Apex Test Class

1) Use @isTest keyword to declare apex test class and methods.

2) Use @testSetup to set up test data required for testing.

3) Use @TestVisible in apex controller method defined as private, so it can be accessed in test class.

4) Use System.runAs(user) to test apex on profile level.

5) Avoid using SeeAllData = true because it allows test methods to access database records for testing, resulting it can fail in production where database not having corresponding records.

Note: Only use SeeAllData = true, if there are sObjects that doesn't allow DML operation. e.g. PriceBook creation.

6) Use Test.startTest and Test.stopTest to reset the governor limit, and also to forces the asynchronous methods to execute synchronously, so that the results can be asserted.

7) Use System.assertEquals(expected, actual, msg) and System.assertNotEquals(expected, actual, msg) to assert expected and actual values from test methods.

8) Avoid Hard Code Ids of any sObject in any test method.

9) Use Null Pointer Exception test as part of negative testing for each method, specially the methods which takes parameters.


Test class important keywords are 
  • @isTest
  • @testSetup
  • @TestVisible
  • System.runAs(user)
  • SeeAllData = true
  • Test.startTest and Test.stopTest
  • System.assert(condition, msg)
  • System.assertEquals(expected, actual, msg)
  • System.assertNotEquals(expected, actual, msg)









Validation in flow input text field in sfdc: Maximum 255 characters limit

The input text field validate in sfdc flow behaves opposite of the generic sfdc validation rule.  Here the validation formula is evaluating ...