JOIN CRUDSPACE (WAITLIST)

Manipulate Salesforce metadata and permisions with speed!

Custom field history tracker in Salesforce

Hello Trailblazer! There is a limit on how many fields you can track using the standard Salesforce Field History Tracking which is why we often need to write our own logic for custom field history tracking!

In this post, I will share with you how can reuse the simple and reusable custom field history tracking for any Salesforce Object.


Use Case

Need to create a custom field history tracking system so that we can track changes for more than 20 fields in Salesforce. Need to build a declarative tool so that admins can easily add/remove fields and objects in the field tracking system.


The Approach

  • We will create a generic object to store the field history data like Field Name, Old Value, New Value, Record Id, and a User who changed the data. Here is the complete schema for Field history.

    Custom Field History Object Schema

  • Also, we need two additional Long Text fields so that we can track Long text fields as well.
  • We need to create two custom metadata to store the Object and field information so that we can track data based on that.
  • Field Tracker Object - Define the object name to track fields from.

    Field Tracker Object

  • Field Track Field - Define fields to be tracked.

    Field Track Field

  • Lastly, we need to write a generic class (FieldTrackerService) with a function that will take the name of the Object, and records data. Based on the provided information it will create the history records.
  • Also, each configuration can be turned on/off from the Object as well as the Field level so that you can turn off field tracking by just checking/unchecking the Is Active flag.
  • Lastly, we need to call the generic class function from the Object triggers.

Implementation

Download the source code from this GitHub repo: custom-field-tracker-salesforce.

Or

Install the unmanaged package from here: Custom Field Tracker Unmanaged Package.


Example Trigger Code

Please make sure that you add Object and field values in the custom metadata before testing this! For testing purposes, I have it for the Account object and BillingCountry field.

AccountTrigger.trigger

trigger AccountTrigger on Account(after insert, after update) {
	AccountTriggerHandler.saveFieldHistories(Trigger.new, Trigger.oldMap);
}

AccountTriggerHandler.cls

public with sharing class AccountTriggerHandler {
	public static void saveFieldHistories(
		List<Account> newAccounts,
		Map<Id, Account> oldMap
	) {
		FieldTrackerService fts = FieldTrackerService.getInstance('Account');
		fts.saveFieldHistories(newAccounts, oldMap);
	}
} 

AccountTriggerTest.cls

@isTest
class AccountTriggerTest {
	@TestSetup
	static void makeData() {
		List<Account> accounts = new List<Account>();
		for (Integer i = 0; i < 200; i++) {
			Account acc = new Account(
				Name = 'Account FTS ' + i,
				BillingCountry = 'United States'
			);
			accounts.add(acc);
		}

		insert accounts;
	}

	@IsTest
	static void testAfterInsertHistory() {
		Set<Id> accountIds = new Map<Id, Account>(
				[SELECT Id FROM Account WHERE Name LIKE 'Account FTS %']
			)
			.keySet();

		Test.startTest();
		System.assertEquals(
			[
					SELECT Id
					FROM Field_History__c
					WHERE
						Tracked_Field_API__c = 'BillingCountry'
						AND Tracked_Record_Id__c IN :accountIds
						AND Old_Value__c = NULL
				]
				?.size(),
			200,
			'Field history records not found'
		);
		Test.stopTest();
	}

	@IsTest
	static void testAfterUpdateHistory() {
		Account[] accounts = [
			SELECT Id
			FROM Account
			WHERE Name LIKE 'Account FTS %'
		];

		Set<Id> accountIds = new Map<Id, Account>(accounts).keySet();

		for (Account acc : accounts) {
			acc.BillingCountry = 'India';
		}

		update accounts;

		Test.startTest();
		System.assertEquals(
			[
					SELECT Id
					FROM Field_History__c
					WHERE
						Tracked_Field_API__c = 'BillingCountry'
						AND Tracked_Record_Id__c IN :accountIds
						AND Old_Value__c = 'United States'
				]
				?.size(),
			200,
			'Field history records not found'
		);
		Test.stopTest();
	}
}


Further Customizations

Make sure that you make the Field History Object read-only to all users so that they can't edit the data from history.

You can add additional features as you need.

I have planned to add a Lightning web component to show the related history records on standard record pages.

Please let me know if you wish to add new features. You can also contribute to this code by submitting a pull request to this repository - custom-field-tracker-salesforce.

I hope that was helpful! Thanks for reading!


No comments :
Post a Comment

Hi there, comments on this site are moderated, you might need to wait until your comment is published. Spam and promotions will be deleted. Sorry for the inconvenience but we have moderated the comments for the safety of this website users. If you have any concern, or if you are not able to comment for some reason, email us at rahul@forcetrails.com