There is build-in functionality in Salesforce for tracking history on specific fields, which can save you development efforts.
Per each object you can enable the tracking history feature, and select the fields you would like track. In each page layout you may add the history related list, that will show all changes for this record. All this feature is build-in and can be done with setup only.
I had once issue, with main object that have several telated childs objects. When user view the history for the object he doesn't want to dive into each child record, but rather to see all the changes (main object + childs) in one place.
Therefore I develop kind of history page which show all changes together.
At first this page was design for the specific custom object. Lately, with some modification it was amended to work completely generic. Means, it can work on any custom object.
Its show only changes for custom objects, as SF objects sometimes have different names for their history table.
The relevant components are visualforce page and controller, usage is by the URL:
/apex/HistoryPage?ids=<recordID>
Per each object you can enable the tracking history feature, and select the fields you would like track. In each page layout you may add the history related list, that will show all changes for this record. All this feature is build-in and can be done with setup only.
I had once issue, with main object that have several telated childs objects. When user view the history for the object he doesn't want to dive into each child record, but rather to see all the changes (main object + childs) in one place.
Therefore I develop kind of history page which show all changes together.
At first this page was design for the specific custom object. Lately, with some modification it was amended to work completely generic. Means, it can work on any custom object.
Its show only changes for custom objects, as SF objects sometimes have different names for their history table.
The relevant components are visualforce page and controller, usage is by the URL:
/apex/HistoryPage?ids=<recordID>
The URL should get as parameter: ids. The Id of the record you want to view its history.
First should select the objects (master/child) to view their changes, press 'Get Changes' and view the result.
I created for example custom object: "Parent Object", and 2 child custom object.
Controller Code:
First should select the objects (master/child) to view their changes, press 'Get Changes' and view the result.
I created for example custom object: "Parent Object", and 2 child custom object.
Controller Code:
public class vf_HistoryPage { public Id objID {get; set;} //main object id (should get as URL parameter) public String objName {get; set;} //main object name (should get as URL parameter) public List<HistoryChange> historyChangeLst {get; set;} //list with all changes public list<String> childObjLst {get; set;} //list of all child objects public map<String, Boolean> obj_showBol_map {get; set;} //for each object if need to show its changes public map<String, String> objLabel_API_map {get; set;} //map from Label to API private map<String, String> objAPI_objLabel=new map<String, String>(); //for each object its label name private map<String, String> objAPI_fieldName_map=new map<String, String>(); //for each object the field name of his parent private map<String, Set<String>> tableFieldAdd_Map=new Map<String, Set<String>>(); //used to prevent from same change return twice public vf_HistoryPage() { //record ID should be as parameters in the URL objID=ApexPages.currentPage().getParameters().get('ids'); objName=objID.getsobjecttype().getDescribe().getName(); //DescribeResult of the main object Schema.DescribeSObjectResult objectResult = Schema.getGlobalDescribe().get(objName).getDescribe(); //intialize maps objLabel_API_map=new map<String, String>(); childObjLst=new list<String>{objectResult.getLabel()}; obj_showBol_map=new map<String, Boolean>(); //adding the main object obj_showBol_map.put(objName, true); objAPI_objLabel.put(objName, objectResult.getLabel()); objLabel_API_map.put(objectResult.getLabel(), objName); objAPI_fieldName_map.put(objName, 'id'); for(Schema.ChildRelationship child : objectResult.getChildRelationships()) { Schema.DescribeSObjectResult objRes=child.getChildSObject().getDescribe(); if(objRes.isCustom()) { childObjLst.add(objRes.getLabel()); obj_showBol_map.put(objRes.getName(), true); objAPI_objLabel.put(objRes.getName(), objRes.getLabel()); objLabel_API_map.put(objRes.getLabel(), objRes.getName()); objAPI_fieldName_map.put(objRes.getName(), child.getField().getDescribe().getName()); } } historyChangeLst=new List<HistoryChange>(); } //Get all the changes public PageReference getChanges() { //clear list of changes historyChangeLst.clear(); for(String childObjName : obj_showBol_map.KeySet()) { if(obj_showBol_map.get(childObjName)) { //reset this list for the specific object tableFieldAdd_Map.put(childObjName, new Set<String>()); //collect all the childs IDs Set<ID> childObjIDSet=new Set<ID>(); for(sObject childObj : Database.query('SELECT id FROM ' + childObjName + ' WHERE ' + objAPI_fieldName_map.get(childObjName) + ' =\'' + objID +'\'')) { childObjIDSet.add(childObj.id); } //add all the object changes to list of all changes if(!childObjIDSet.isEmpty()) { addAllChanges(childObjIDSet, objAPI_objLabel.get(childObjName), childObjName, childObjName.endsWith('__c') ? childObjName.replace('__c', '__History') : childObjName + 'History'); } } } historyChangeLst.sort(); return ApexPages.currentPage(); } public void addAllChanges( Set<Id> idLst, String objectName, String tableName, String tableHistoryName) { String idConcatenateLst=''; String dynamicSQLHistory; for(String objID : idLst) { idConcatenateLst+= idConcatenateLst!='' ? ', \''+objID+'\'' : '\''+objID+'\''; } dynamicSQLHistory='SELECT id, oldvalue, parentid, newvalue, field, createdDate, CreatedBy.LastName, CreatedBy.FirstName'; dynamicSQLHistory+=' FROM ' + tableHistoryName + ' WHERE parentId IN ( ' + idConcatenateLst + ')'; dynamicSQLHistory+= ' order by createdDate'; try { List<sObject> objLst=Database.query(dynamicSQLHistory); sObject userObj; String firstName, lastName; for(sObject histObj : objLst) { userObj=histObj.getSObject('CreatedBy'); firstName=(String)(userObj.get('FirstName')); lastName=(String)(userObj.get('LastName')); addChange(tableName, (String)histObj.get('field'), histObj.get('oldvalue'), histObj.get('newValue'), (DateTime)histObj.get('createdDate'), (firstName==null ? '' : (firstName + ' '))+ (lastName==null ? '' : lastName), objectName, (String)histObj.get('parentid')); } } catch(System.QueryException sqe) { System.debug('##err: ' + sqe); //possible error: object not support history System.debug('##DYNAMIC SQL: ' + dynamicSQLHistory); } catch(Exception e) { ApexPages.addMessage(new ApexPages.Message(ApexPages.severity.ERROR, ' Error with ' + tableName + '. ' + e)); } } //add change private void addChange( String table_Str, String field_Str, Object oldValue_Obj, Object newValue_Obj, DateTime createDate, String user, String objectName, String objID) { String fieldLabel_Str, name_Str=''; String result_Str=''; Boolean creation=false; if(!tableFieldAdd_Map.get(table_Str).contains(field_Str+createDate+objID)) { tableFieldAdd_Map.get(table_Str).add(field_Str+createDate+objID); if(field_Str=='created') { result_Str='Record created'; creation=true; } else { if(oldValue_Obj != null && newValue_Obj != null) { result_Str+=' changed from ' + (oldValue_Obj==null ? '' : oldValue_Obj) + ' to ' + (newValue_Obj==null ? '' : newValue_Obj); } else if(oldValue_Obj == null && newValue_Obj != null) { result_Str+=' added ' + (newValue_Obj==null ? '' : newValue_Obj); } else if(oldValue_Obj != null && newValue_Obj == null) { result_Str+=' deleted ' + (oldValue_Obj==null ? '' : oldValue_Obj); } else { result_Str+=' Field has been changed'; //for long text field SF doesn't store the values } } if(result_Str!=null && result_Str.trim() !='') { historyChangeLst.add(new HistoryChange(result_Str, user, createDate, objectName, objID, table_Str, field_Str=='RecordType' ? 'RecordTypeID' : field_Str, creation)); } } } //Object HistoryChange - represent history record public class HistoryChange implements Comparable { public String changeStr {get; set;} public DateTime changeDate {get; set;} public String userStr {get; set;} public String objectName {get; set;} public String objectID {get; set;} public String objectAPI {get; set;} public String fieldAPI {get; set;} public Boolean creationChange {get; set;} public HistoryChange(String pChange, String pUser, DateTime pChangeDate, String pObjectName, String pObjectID, String pObjectAPI, String pFieldAPI, Boolean pCreationChange) { changeStr=pChange; changeDate=pChangeDate; userStr=pUser; objectName=pObjectName; objectID=pObjectID; objectAPI=pObjectAPI; fieldAPI=pFieldAPI; creationChange=pCreationChange; } public Integer compareTo(Object historyRec) { HistoryChange compareToHC = (HistoryChange)historyRec; if(changeDate!=compareToHC.changeDate) { return changeDate >= compareToHC.changeDate ? 0 : 1; } else { return creationChange ? 1 : 0; } } } }
Visualforce Page:
<apex:page controller="vf_HistoryPage"> <apex:pageMessages /> <apex:form id="frm"> <apex:pageBlock mode="edit" id="mainblock" title="Object Selection"> <apex:pageBlockButtons id="buttons" location="bottom"> <apex:commandButton value="Get Changes" action="{!getChanges}"/> </apex:pageBlockButtons> <apex:pageBlockSection id="filters" columns="1" collapsible="true"> <apex:repeat value="{!childObjLst}" var="obj"> <apex:pageBlockSectionItem > <apex:outputText value="{!obj}"/> <apex:inputCheckbox value="{!obj_showBol_map[objLabel_API_map[obj]]}"/> </apex:pageBlockSectionItem> </apex:repeat> </apex:pageBlockSection> </apex:pageBlock> <apex:pageBlock mode="edit" id="results"> <apex:pageBlockTable value="{!historyChangeLst}" var="change"> <apex:column headerValue="Date"> <apex:outputText value="{0,date,dd'/'MM'/'yyyy HH:mm:ss}"> <apex:param value="{!change.changeDate}" id="datefromid"/> </apex:outputText> </apex:column> <apex:column headerValue="User"> <apex:outputText value="{!change.userStr}"/> </apex:column> <apex:column headerValue="Object"> <apex:outputLink target="_blank" value="/{!change.objectID}" id="eds"> {!change.objectName}</apex:outputLink> </apex:column> <apex:column headerValue="Change"> <apex:outputPanel rendered="{!NOT change.creationChange}"> {!$ObjectType[change.objectAPI].fields[change.fieldAPI].Label} </apex:outputPanel> {!change.changeStr} </apex:column> </apex:pageBlockTable> </apex:pageBlock> </apex:form> </apex:page>
No comments:
Post a Comment