Salesforce is powerful, but like every platform, it has limitations. One such limitation is related to Field History Tracking—you can only track up to 20 fields per object, and even then, certain field types (like long text areas) aren’t supported.
I recently came across this limitation during a project where tracking every update to Task fields was critical for compliance and reporting. So, I designed a custom field tracking solution using Apex Triggers and a custom object. In this post, I’ll walk you through how I built it and how you can implement it too.
trigger TriggerOnTaskHistoryObject on Task_History_Tracking__c (before delete) {
switch on Trigger.operationType {
when BEFORE_DELETE {
TriggerOnTaskHistoryHandler.preventToDeleteRecords(trigger.old);
}
}
}
public class TriggerOnTaskHistoryHandler {
public static void preventToDeleteRecords(List<Task_History_Tracking__c> oldTaskHistory) {
for(Task_History_Tracking__c THT : oldTaskHistory){
THT.addError(System.Label.Task_history_error_message);
}
}
}
//----------------------------------------------------------------------------------------
trigger TriggerOnTaskObject on Task(before insert,before update,after update) {
switch on Trigger.operationType {
when AFTER_UPDATE {
TriggerOnTaskObjectHandler.createTaskHistoryTrackingRecords(Trigger.oldMap, Trigger.new);
}
}
}
public static void createTaskHistoryTrackingRecords(Map<Id, Task> oldMap, List<Task> newTask) {
List<Task_History_Tracking__c> THTList = new List<Task_History_Tracking__c>();
Set<String> ownerIds = new Set<String>();
Map<String,String> fieldMap = new Map<String,String> {
'Subject' => 'Subject',
'OwnerId' => 'Assigned To ID',
'CC_Due_Date__c' => 'CC Due Date',
'Status' => 'Status',
'Description' => 'Comments',
'LastModifiedById' => 'Last Modified By'
};
for(Task objTas : newTask) {
if(objTas.OwnerId != oldMap.get(objTas.Id).OwnerId) {
ownerIds.add(objTas.OwnerId);
ownerIds.add(oldMap.get(objTas.Id).OwnerId);
}
if(objTas.LastModifiedById != oldMap.get(objTas.Id).LastModifiedById) {
ownerIds.add(objTas.LastModifiedById);
ownerIds.add(oldMap.get(objTas.Id).LastModifiedById);
}
}
Set<Id> userIds = new Set<Id>();
Set<Id> queueIds = new Set<Id>();
for(String theId : ownerIds) {
if(theId.startsWith('005')) {
userIds.add(theId);
}
else if(theId.startsWith('00G')) {
queueIds.add(theId);
}
}
Map<Id, User> userMap = new Map<Id, User>([SELECT Name FROM User WHERE Id IN :ownerIds]);
Map<Id, Group> groupMap = new Map<Id, Group>([SELECT Name FROM Group WHERE Id IN :queueIds]);
for(Task objTas : newTask) {
if(
(objTas.Subject != oldMap.get(objTas.Id).Subject) ||
(objTas.OwnerId != oldMap.get(objTas.Id).OwnerId) ||
(objTas.CC_Due_Date__c != oldMap.get(objTas.Id).CC_Due_Date__c) ||
(objTas.Status != oldMap.get(objTas.Id).Status) ||
(objTas.Description != oldMap.get(objTas.Id).Description) ||
(objTas.LastModifiedById != oldMap.get(objTas.Id).LastModifiedById)
) {
for(String fieldNames : fieldMap.keySet()) {
if(objTas.get(fieldNames) != NULL && String.valueOf(objTas.get(fieldNames)) != String.valueOf(oldMap.get(objTas.Id).get(fieldNames))) {
Task_History_Tracking__c THT = new Task_History_Tracking__c();
THT.Field_Changed__c = fieldMap.get(fieldNames);
THT.Modified_By__c = objTas.LastModifiedById;
THT.Task_Id__c = objTas.Id;
THT.Task_Name__c = objTas.subject;
THT.Task_Created_DateTime__c = objTas.CreatedDate;
if(fieldNames != 'Description') {
if(fieldNames != 'OwnerId' && fieldNames != 'LastModifiedById') {
THT.Old_Value__c = String.valueOf(oldMap.get(objTas.Id).get(fieldNames));
THT.New_Value__c = String.valueOf(objTas.get(fieldNames));
}
else {
if(userMap.containsKey(String.valueOf(oldMap.get(objTas.Id).get(fieldNames)))) {
THT.Old_Value__c = userMap.get(String.valueOf(oldMap.get(objTas.Id).get(fieldNames))).Name;
}
if(userMap.containsKey(String.valueOf(objTas.get(fieldNames)))) {
THT.New_Value__c = userMap.get(String.valueOf(objTas.get(fieldNames))).Name;
}
if(groupMap.containsKey(String.valueOf(oldMap.get(objTas.Id).get(fieldNames)))) {
THT.Old_Value__c = groupMap.get(String.valueOf(oldMap.get(objTas.Id).get(fieldNames))).Name;
}
if(groupMap.containsKey(String.valueOf(objTas.get(fieldNames)))) {
THT.New_Value__c = groupMap.get(String.valueOf(objTas.get(fieldNames))).Name;
}
}
}
else {
THT.New_Text__c = objTas.Description;
THT.Old_Text__c = oldMap.get(objTas.Id).Description;
}
if(objTas.WhatId != null && objTas.WhatId.getSObjectType() == Case.SObjectType){
THT.Case__c = objTas.WhatId;
}
THTList.add(THT);
}
}
}
}
insert THTList;
}
If you're hitting Salesforce’s 20-field tracking limit or need to track rich/complex data, try this solution!
Comments
Post a Comment