Deep Diving in Salesforce Triggers IV – Coderinme

In the last post of Deep Diving in Salesforce Triggers,we have discussed about how to bulkify our Salesforce Triggers .

In this post we will discuss about making our triggers Logic-less.  Logic-less trigger’s  are those trigger’s whose Business logic’s contains in a handler class.  Also we will learn  how to stop a trigger being recursive aka recursive trigger.

Salesforce Triggers

So what is a helper class in a Salesforce Triggers ?

Since we write our logic in Salesforce Triggers  itself, sometimes it’s difficult to analyze and modify our trigger. Also code’s readability will simultaneously decreases.

Also we cannot write methods in trigger’s itself which leads to duplication of code. So we uses a separate apex class to write our business logic  inside that apex class method. Apparently this Apex class is know as Helper Class . we invoke this class method according to trigger’s context variable.

Example : if trigger isInsert then invoke method A of class ABC and if isUpdate then invoke method B of class ABC.

Using helper class will increase code readability and a modeler approach to our trigger which is easy to debug and modify.

It’s a best practice to to name helper class name as : triggername + helper

Suppose we have a business senerio which stated as follow:

There are three custom objects Centre __c, Manger__c and Salesperson__c having lookup relationship.

Salesperson is child of Manger and Manger is child of  Centre.

So can say that a centre can have one or many manager’s and a manager can have one or many salesperson’s.

Centre object have a checkbox which is to be true when all manger’s of same centre have same number of salesperson’s .Other wise it should be false.

So our trigger should check the number of salesperson’s of each manager of specific centre whenever a new salesperson gets added to a manager of specific centre.

Note: We assume you have a basic understanding of collection’s in Salesforce Triggers as we are heavily using collection’s to bulkify and optimize our trigger.

Here’s the code,

Trigger

trigger SalespersonTrigger on Salesperson__c ( after insert ) {

    if( trigger.isAfter && trigger.isInsert ){
       
        SalespersonTriggerHelper.checkTotalSalesPersonPerCenter( trigger.new );
    }
}

Trigger Helper Class

public class SalespersonTriggerHelper {
    
    public static void checkTotalSalesPersonPerCenter( list< Salesperson__c > triggerNew ){
        
        map< Center__c, set< String > > mapCenterToMang = new map< Center__c, set< String > >();
        map< String, Integer > mapMangtoSalesCount = new map< String, Integer >();
        
        for( Salesperson__c objSalesTemp : triggerNew ){
            
            if( objSalesTemp.Manager__c <> null )
                mapMangtoSalesCount.put( objSalesTemp.Manager__c, 0 );
        }
        
        for( Manager__c objMangerTemp : [ SELECT Id,center__c FROM manager__c 
                                         WHERE Id IN:  mapMangtoSalesCount.keySet() ] ){
                                             
                                             if( objMangerTemp.Center__c <> null ){
                                                 
                                                 // a new instance need to be created as for the key
                                                 Center__c objCenterTemp = new Center__c( Id = objMangerTemp.Center__c );
                                                 
                                                 set< String > tempMangList = new set< String >();
                                                 if( mapCenterToMang.containsKey( objCenterTemp ))
                                                     tempMangList = mapCenterToMang.get( objCenterTemp );
                                                 tempMangList.add( objMangerTemp.Id );
                                                 mapCenterToMang.put( objCenterTemp, tempMangList );
                                             }
                                             
                                         }
        system.debug('mapCenterToMang'+mapCenterToMang);
        system.debug('mapMangtoSalesCount'+mapMangtoSalesCount);
        
        for(Center__c objCen : [SELECT Id,
                                (SELECT Id from managers__r) FROM Center__C 
                                WHERE Id IN: mapCenterToMang.keyset() ]){
                                    
                                    if( objCen.managers__r.size() > 0 ){
                                        
                                        for( Manager__c objMangerTemp : objCen.managers__r ){
                                            
                                            // a new instance need to be created as for the key center object as only id 
                                            // filed but here it has new filed managers__r which change its description
                                            // and will not match
                                            Center__c objCenterTemp = new Center__c( Id = objCen.Id );
                                            set< String > tempMangList = new set< String >();
                                            
                                            if( mapCenterToMang.containsKey( objCenterTemp ))
                                                tempMangList = mapCenterToMang.get( objCenterTemp );
                                            tempMangList.add( objMangerTemp.Id );
                                            mapCenterToMang.put( objCenterTemp, tempMangList );
                                            mapMangtoSalesCount.put(objMangerTemp.Id,0);
                                        }
                                    }
                                }
        
        system.debug('mapCenterToMang'+mapCenterToMang);
        system.debug('mapMangtoSalesCount'+mapMangtoSalesCount);
        
        for( Manager__c objMan : [SELECT Id,center__c,
                                  (SELECT id FROM Salespersons__r) FROM Manager__c
                                  WHERE Id IN: mapMangtoSalesCount.keyset() ]){
                                      
                                      if( objMan.Salespersons__r.size() > 0 ){
                                          
                                          mapMangtoSalesCount.put( objMan.Id, objMan.Salespersons__r.size() );
                                      }
                                      
                                  }
        system.debug('mapCenterToMang'+mapCenterToMang);
        system.debug('mapMangtoSalesCount'+mapMangtoSalesCount);
        
        for( Center__c objCen : mapCenterToMang.keySet() ){
            
            integer intialSize = 0,finalSize = 0;
            
            for( String managerId : mapCenterToMang.get(objCen) ){
                
                system.debug('managerId'+managerId);
                
                if( intialSize == 0 && finalSize == 0){
                    
                    intialSize =  mapMangtoSalesCount.get(managerId);
                    finalSize = mapMangtoSalesCount.get(managerId);
                    
                }
                else if( mapMangtoSalesCount.get(managerId) > finalSize ){
                    
                    finalSize = mapMangtoSalesCount.get(managerId);
                }
                else if( mapMangtoSalesCount.get(managerId) < intialSize ){
                    intialSize = mapMangtoSalesCount.get(managerId);
                }
            }
            if( intialSize == finalSize ){
                objCen.Equivalent_Salesperson_s__c = true;
            }else{
                objCen.Equivalent_Salesperson_s__c = false;
            }
           
        }
        //now mapCenterToMang Center obj will lose all its key(Manager id) as object description was changed
        // as added a new field(Equivalent_Salesperson_s__c)
        system.debug('mapCenterToMang'+mapCenterToMang);
        
        update new list< center__c > ( mapCenterToMang.keySet() );
    }
}

So how does it work….

Whenever a new Salesperson record is being created method name checkTotalSalesPersonPerCenter from helper class SalespersonTriggerHelper.

First, we fetch all the manager’s id from triggerNew( it contains all the new Salesperson record’s) and mapped it to mapMangtoSalesCount.

Then these manager id’s are used to fetch there center record’s as we don’t need to fetch all center records in the system. As in multi tenant environment fetching un-necessary records is a NO-NO.

So we need to filter our SOQL query to fetch only selective center record’s  and mapped it to mapCenterToMang .

Note:  mapCenterToMang contains only those centre’s and  manager’s id which are being fetch or referenced with new salesperson record. Still we need to fetch all the manager record’s which are linked to respective centre’s.

So now we fetch all the manager record’s which are being linked with selected centre’s.

And now we have all the manager id’s then now its time to calculate all there salesperson’s which are under them.

So at last we have all the centre’s and there manger’s and respective salesperson’s.

Now it’s time to see if each centre has equivalent salesperson’s or not.

So we iterate each centre from mapCenterToMang and  we used two variable intialSize and finalSize for calculating each manager salesperson’s.

First we  initialise these variables with first manger saleperson count. We then iterate on each manager which are linked with current center.

If  we found any manager having less or more salesperson then we store there count on intialSize and finalSize respectively.

At last

If intialSize is not equal to finalSize then it means that that particular centre does not have equivalent salesperson’s.

So we marked Equivalent Salesperson check box false.  If it does not change then it has equivalent salesperson’s making  Equivalent Salesperson check box true.

And finally we update the centre record’s according to above logic.

Some working screenshots..

 

Hurray!! Now we have completed a complex business logic with Salesforce Triggers.

Now there might me a senerio where you got this error inSalesforce Triggers execution.

 

This generally happens when  a record is  updated recursively through its trigger or some else trigger .

As an example account trigger on update , updates a contact and contact on update will update it’s account will create a recursive trigger.

Even a workflow or a process builder can make a update trigger recursive.

As Account trigger will update a value of a field which invoke a workflow which changes back its value to old one which will again invoke update trigger itself.

So we use a boolean variable which allows trigger to run once per transaction.

Example

trigger AccountTrigger on Account(after update) {
    if(RecursiveCheck.isRunOnce())
    {
    //this logic will only execute only once        
    }
}
public Class RecursiveCheck{
    private static boolean runOnce = true;
    public static boolean isRunOnce(){
    if(runOnce){
     runOnce=false;
     return true;
    }else{
        return runOnce;
 }
    }
}

 

So till now we have discussed how to solve complex business logic, how to used helper class and how to use stop a trigger being recursive.

In the next post we will discuss about trigger frameworks.
Stay tuned!!

All rights reserved. No part of this Post may be copied, distributed, or transmitted in any form or by any means, without the prior written permission of the website admin, except in the case of brief quotations embodied in critical reviews and certain other noncommercial uses permitted by copyright law. For permission requests, write to the owner, addressed “Attention: Permissions Coordinator,” to the admin @coderinme

Leave a reply:

Your email address will not be published.