In Salesforce Winter ’20 release, three new classes (Formula, FormulaRecalcResult, and FormulaRecalcFieldError) were introduced.
- With the help of these classes, we can recalculate our formula fields without any DML operations on the given SObject, which may be a new or queried list.
- If we don’t have any required fields for calculating the formula in the passed list, then the SOQL query will be minimized to retrieve the additional data required.
- Instead of using a batch class, we can use Formula class to update the formula fields even for the old records.
1.Formula Class
This class has a method recalculateFormulas(), which is used to recalculate the formula fields in the given SObject list. Namespace for this class is System. We can recalculate the formula field in a bulk of records (List) for a SObject. Then, the new values of the formula field stored in SObject override the previous values of that field in Salesforce.
Method:
- Syntax for this method is: public static List<System.FormulaRecalcResult> recalculateFormulas(List<SObject> sobjects)
- Argument is List of SObject.
- Return List of FormulaRecalcResult Class
2.FormulaRecalcResult Class
This class is used to return the result of Formula.recalculateFormulas(SObject) operation. Namespace for this class is System. It indicates the Status and result for recalculation formula fields for the given SObject. This class methods used to return the SObject name, Error, and Success status.
Methods:
getErrors(): This method returns the error codes and descriptions when the error occurred during formula field recalculation.
- Syntax: public List<System.FormulaRecalcFieldError> getErrors()
- Return List of FormulaRecalcFieldError Class
getSObject(): This method returns the SObject with formulas recalculated.
- Syntax: public SObject getSObject()
- Return type is SObject
isSuccess() : This method returns a Boolean value. if the value is true, then the formula recalculation process completed successfully; else, it is return false.
- Syntax: public Boolean isSuccess()
- Return Type is Boolean
3. FormulaRecalcFieldError Class
This class is used to indicate the error field name and description for the error. Namespace for this class is System.
Methods:
getFieldError(): This method returns an error message which describes the errors occurred during formula recalculation.
- Syntax: public String getFieldError()
- Return Type is String
getFieldName(): This method returns the field name of the error field.
- Syntax: public String getFieldName()
- Return Type is String
Use Case:
The following use case uses the Formula classes and methods to recalculate the formula fields in Account object.
Choose Account object from list of SObject. The formula/rollup fields in the chosen object are shown. If we click RecalculateFormulaField button, recalculate the formula fields in the chosen object and show the success and error message for formula field recalculation.
Solution:
VF Page:
<!–/*
* Company : MST
* Test class : FormulaRecalculator_Test
* Descrpition: This VF page used to perform SObject Formula field recalculation
*/–>
<apex:page sidebar=”true” controller=”FormulaRecalculator_AC“>
<apex:form >
<apex:pageMessages id=”pageMsg” escape=”false”></apex:pageMessages>
<apex:actionFunction name=”CallFormulaFieldMethod” action=”{!fetchFormulaFields}” reRender=”fields” />
<apex:pageBlock title=”Sobject Formula Recalculator“>
<apex:outputPanel id=”objectlist” >
Select Object:
<apex:selectList value=”{!selectedObject}” size=”1″ onchange=”CallFormulaFieldMethod()” >
<apex:selectOptions value=”{!listOfObjects}”></apex:selectOptions>
</apex:selectList>
</apex:outputPanel>
<br/>
<br/>
<apex:outputPanel id=”fields” >
Formula/Rollup Fields in the selected Object :
<apex:selectList value=”{!selectedField}” size=”1″ id=”FieldList” rendered=”{!selectedObject != null}”>
<apex:selectOptions value=”{!listOfFormulaFields}”></apex:selectOptions>
</apex:selectList>
</apex:outputPanel>
<br/>
<br/>
<apex:commandButton value=”RecalculateFormulaField” action=”{!reCalculateFormulaField}”></apex:commandButton>
</apex:pageBlock>
</apex:form>
</apex:page>
Controller:
/*
* Company : MST
* Test class : FormulaRecalculator_Test
* Descrpition: This controller used to perform SObject Formula field recalculation
*/
public class FormulaRecalculator_AC {
public List<SelectOption> listOfObjects{get;set;}
public List<SelectOption> listOfFormulaFields{get;set;}
public String selectedObject{get;set;}
public Map<String, Schema.SobjectType> gd = schema.getGlobalDescribe();
public Map<String, schema.SObjectField> MapOfFields{get;set;}
public FormulaRecalculator_AC(){
listOfObjects = new List<SelectOption>();
for(Schema.SObjectType obj : gd.values()){
Schema.DescribeSObjectResult dr = obj.getDescribe();
listOfObjects.add(new SelectOption(String.valueOf(dr.getName()), String.valueOf(dr.getLabel())));
}
listOfObjects.sort();
}
public void fetchFormulaFields(){
listOfFormulaFields = new List<SelectOption>();
MapOfFields = gd.get(selectedObject).getDescribe().fields.getMap();
for(Schema.SObjectField field : MapOfFields.values()){
Schema.DescribeFieldResult fr = field.getDescribe();
if(fr.isCalculated()){
listOfFormulaFields.add( new SelectOption(String.valueof(fr.getName()), String.valueof(fr.getLabel())));
} } }
public void reCalculateFormulaField(){
System.debug(‘Enter into the method’);
String SobjectApiName = selectedObject;
List<Account> accList = new List<Account>();
Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe(); Map<String, Schema.SObjectField> fieldMap = schemaMap.get(SobjectApiName).getDescribe().fields.getMap();
List<sObject> sobjectList = new List<sObject>();
String commaSepratedFields = ”;
for(String fieldName : fieldMap.keyset()){
if(commaSepratedFields == null || commaSepratedFields == ”){
commaSepratedFields = fieldName;
}else{
commaSepratedFields = commaSepratedFields + ‘, ‘ + fieldName;
}
}
String query = ‘select ‘ + commaSepratedFields + ‘ from ‘ + SobjectApiName + ‘ Limit 20’;
sobjectList = Database.query(query);
List<FormulaRecalcResult> results = Formula.recalculateFormulas(sobjectList);
System.debug(‘Result!!!!:’+results[0].isSuccess());
if(results[0].isSuccess()){
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM,’Formula field Recalculated successfully!’)); }
else{
FormulaRecalcResult result = results[0];
FormulaRecalcFieldError fieldError = result.getErrors()[0];
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL,’Formula field failed to Recalculated ‘+fieldError.getFieldError()));
System.debug(‘Result Error Field Name:’+fieldError.getFieldName());
System.debug(‘Result Error:’+fieldError.getFieldError());
System.debug(‘Result Error Sobject:’+result.getSObject());
System.debug(‘Result Error isSuccess:’+result.isSuccess());
} }}
Test Class:
/*
* Company : MST
* Test class for FormulaRecalculator_AC
*/
@isTest
public class FormulaRecalculator_Test {
//This method used to verfiy the Formula recalculator Success logic.
static testMethod void verfiyFormulaFieldRecalcuSuccess(){
List<Account> accList = new List<Account>();
for(Integer i=0;i<200;i++){
Account acc = new Account();
acc.Name = ‘Test Account’;
acc.Count_of_Quarter__c = 4;
accList.add(acc);
}
if(!accList.isEmpty()) insert accList;
FormulaRecalculator_AC controller = new FormulaRecalculator_AC();
controller.selectedObject = ‘Account’;
Test.startTest();
controller.reCalculateFormulaField();
controller.fetchFormulaFields();
Test.stopTest();
Account updatedAcc = [SELECT ID,Account_Revenue_Status__c FROM Account WHERE Name = ‘Test Account’ LIMIT 1];
System.assertEquals(‘Awarded‘,updatedAcc.Account_Revenue_Status__c, ‘Formula field was updated’);
}
//This method used to verfiy the Formula recalculator error logic.
static testMethod void verfiyFormulaFieldRecalcuFailed(){
List<Account> accList = new List<Account>();
for(Integer i=0;i<200;i++){
Account acc = new Account();
acc.Name = ‘Test Account 1’;
acc.Count_of_Quarter__c = null;
accList.add(acc);
}
try{
if(!accList.isEmpty()) insert accList;
FormulaRecalculator_AC controller = new FormulaRecalculator_AC();
controller.selectedObject = ‘Account’;
Test.startTest();
controller.reCalculateFormulaField();
controller.fetchFormulaFields();
Test.stopTest();
}
Catch(Exception e){
Account updatedAcc = [SELECT ID,Account_Revenue_Status__c FROM Account WHERE Name = ‘Test Account 1’ LIMIT 1];
System.assertEquals(‘Formula field failed to Recalculated Division undefined‘,e.getMessage(), ‘Formula field not populated’);
} }}
Output screenshots:
Recalculate formula field Success scenario 1:
Recalculate formula field Failure scenario 2:
Average Revenue formula field in Account to calculate sum of First Quarter Revenue, Second Quarter Revenue, Third Quarter Revenue, and Fourth Quarter Revenue and divide by Count_of_Quarter__c number field Mulitiply with 100.
(First_Quarter_Revenue__c + Fourth_Quarter_Revenue__c + Second_Quater__c + Third_Quarter_Revenue__c/Count_of_Quarter__c)*100
If anyone record Count_of_Quarter__c field is null,, it throws division undefine error due to Null/0.
Conclusion:
Formula classes are used to recalculate the formula fields in the given SObject by using recalculateFormulas() method. FormulaRecalcResult class is used to return the result of recalculation like the error code, SObject name and whether it is success or failure of recalculation. FormulaRecalcFieldError class is used to return the error type and error field. We can update the formula field without any DML operation, and it will help to minimize the number of fields to be queried for this operation. Instead of using a Batch class, we can use this class to update the formula fields even for the old records.
References: