Sunday, June 15, 2014

Custom History tracking in salesforce

Some time we might need to track the history of the long text field or the rich text area field and Salesforce doesn't store the values for that, or might some time you need more than what Salesforce do in history tracking

Here is the custom solution how you can achieve the custom history tracking




Create a custom setting which will hold the sobject name and field to be tracked . Changes will be stored as a note to the parent record you may create a custom object if you want

Invocation on Account Object 

/** 
 * This is the trigger to capture the changes made in long text area 
**/
trigger AccountHistoryTrigger on Account (after update) {
   //checking the conditions. Target org may have existing trigger 
    if(TRIGGER.isAfter && TRIGGER.isUpdate){
        HistoryTrackingHandler handler = new HistoryTrackingHandler();
        handler.checkAndCreateNote(Trigger.newMap,Trigger.oldMap,'Account'); 
    }
}

Handler class

/** 
 * This class is the handler for the triggers on the sobjects those want to track values 
**/
public class HistoryTrackingHandler{
    /**
     * This method will take the new map and old map along with the Sobject name 
     * and comapre the values present in the custom setting for the Sobject 
     * If any values found then it will insert a note with al the changes 
     **/
    public void checkAndCreateNote(Map<Id,Sobject> newMapValues, Map<Id,Sobject> oldMapValues,String sobj){
        /**
         * Checking if the custom setting value exists 
         * input newMap,oldMap, Api name of the Sobject
        **/ 
       
        if(LongTextAreaFieldstoTrack__c.getInstance(sobj)<> NULL && LongTextAreaFieldstoTrack__c.getInstance(sobj).Field__c <> NULL && LongTextAreaFieldstoTrack__c.getInstance(sobj).Field__c.trim() <> '' && LongTextAreaFieldstoTrack__c.getInstance(sobj).Active__c == TRUE){
            // getting the values from the custom setting
            String selectedFields = LongTextAreaFieldstoTrack__c.getInstance(sobj).Field__c;
            List<Note> noteList = new List<Note>(); // creating a list to insert notes 
            Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe(); 
            Schema.SobjectType oType = gd.get(sobj); // getting the object type in runtime 
            // getting all the fields at runtime
            Map <String, Schema.SObjectField> fieldMap = Schema.getGlobalDescribe().get(sobj).getDescribe().fields.getMap();
            //Iterate through the loop and check for the field if the field present in the custom setting 
            //and the values are modified then create a note record for per transction for each record 
            for(Sobject currentSobj : newMapValues.values()){
                sObject newObjectInstance =  oType.newSObject(); // will hold new instance
                sObject oldObjectInstance =  oType.newSObject(); // will hold old instance
                newObjectInstance = currentSobj; // getting the new instance 
                oldObjectInstance= oldMapValues.get(currentSobj.Id); // getting the old values               
                for (String fieldName : fieldMap.keyset()) { 
                    if(selectedFields.containsIgnoreCase(fieldName)){// checking if present in the custom setting or not
                        if(newObjectInstance.get(fieldName) != oldObjectInstance.get(fieldName) && oldObjectInstance.get(fieldName) <> NULL){
                            String nodeTitle = ''+ fieldName + ' has been changed by ' + userInfo.getName() + ' at ' + System.now();
                            //value has been changed, so append in the body
                            String nodeBody = 'Old value was \n'+ oldObjectInstance.get(fieldName) ;  
                            Note newNote = new Note(IsPrivate=false,ParentId=newObjectInstance.Id,Body=nodeBody,Title=nodeTitle);
                        noteList.add(newNote);
                        }
                    }
                }
            }
            // if added in list inserting the recod
            if(noteList.size() > 0){
                try{
                  insert noteList;
                }
                catch(Exception e){ System.debug('Error while inserting Note : ' + e.getMessage()); }
            }
        }
    }
}