Friday, 25 April 2025

Evaluate Dynamic Formulas in Apex in Summer '25


Image generated by ChatGPT o3 in response to a prompt by Bob Buzzard
Proving yet again AI isn't good at putting text in images!

Introduction

The ability to evaluate dynamic formulas in Apex, which went GA in the Spring '25 Salesforce release, gets a minor but very useful improvement in the Summer '25 release - template mode. In this mode you can use merge syntax to refer to record fields, making formulas that concatenate strings much easier to specify.  Using the example from the release notes,  rather than writing the formula to combine an account name and website as a clunky concatenation :

    name & " (" & website & ")"

we can write:

    {!name} ({!website})

and tell the formula builder to evaluate it as a template. This felt like a good addition to my formula tester page, that I created to demonstrate the Spring '25 functionality, and it also uncovered some unexpected behaviour.

The Sample

My formula tester page is updated to allow the user to specify whether the formula should be evaluated as a template or not via a simple checkbox under the text area to input the formula:


I've also tried to make the page helpful and added an onchange handler to the text area that toggles the Formula is template checkbox based on whether the entered text contains the patter {! - note that the user can override this if they need that string literal for some other reason:



Note also that there's a timeout of 1 second in the onchange handler so it will only fire when the user has (hopefully) finished typing. 

There will be code


The revised Apex method to build and evaluate the formula is as follows:

@AuraEnabled
public static String CheckFormula(String formulaStr, String sobjectType, String returnTypeStr,
                                  Id recordId, Boolean formulaIsTemplate)
{
    FormulaEval.FormulaReturnType returnType = 
                   FormulaEval.FormulaReturnType.valueof(returnTypeStr);

    FormulaEval.FormulaInstance formulaInstance = Formula.builder()
                                    .withType(Type.forName(sobjectType))
                                    .withReturnType(returnType)
                                    .withFormula(formulaStr)
                                    .parseAsTemplate(formulaIsTemplate)
                                    .build();


    //Use the list of field names returned by the getReferenced method to generate dynamic soql
    Set<String> fieldNames = formulaInstance.getReferencedFields();
    Set<String> lcFieldNames=new Set<String>();
    for (String fieldName : fieldNames)
    {
        lcFieldNames.add(fieldName.toLowerCase());
    }
    if (lcFieldNames.isEmpty())
    {
        lcFieldNames.add('id');
    }

    String fieldNameList = String.join(lcFieldNames,',');
    String queryStr = 'select ' + fieldNameList + ' from ' + sobjectType + 
                      ' where id=:recordId LIMIT 1'; //select name, website from Account
    SObject s = Database.query(queryStr);
    Object formulaResult=formulaInstance.evaluate(s);
    system.debug(formulaResult);

    return formulaResult.toString();
}
The bit I expected to change was the additional formulaIsTemplate parameter and invoking parseAsTemplate on the builder. While testing however, I found that if I built a string literal without using any fields, my fieldNameList parameter was empty and I got an error running the SOQL query of select from Account where id='<blah>'.

My first attempt at a fix was to skip the query if the field list was empty, but the evaluate method errors when passed a null sObject parameter. No problem, I'll just add the Id field if the Set of field names is empty. 

Turns out there was a problem. I checked if the field named Id was in the Set, which it wasn't, then added it. Executing the query duly errored as I had a duplicate field in my query. It turns out that the getReferencedFields method isn't case agnostic and it considers Id as a different field to id and returns them both in the Set.

To confirm this I ran the following Apex:

FormulaEval.FormulaInstance formulaInstance = Formula.builder()
		.withType(Schema.Account.class)
		.withReturnType(FormulaEval.FormulaReturnType.STRING)
		.withFormula('{!name} {!Name} {!NaMe} {!NAME}')
		.parseAsTemplate(true)
		.build();
        
String fieldNameList = String.join(formulaInstance.getReferencedFields(),',');
System.debug('Field name list = ' + fieldNameList);
and got the output:

    09:55:32:043 USER_DEBUG [9]|DEBUG|Field name list = NAME,NaMe,Name,name

So I had to add some extra code to iterate the field names, lower case them and add them to a new Set to remove any duplicates. Then I could check if it was empty and if it was add the id field.

You can find the updated code in my Summer 25 samples repository - note that this needs a Salesforce instance on the Summer '25 which, at the time of writing (April 25th), is pre-release orgs. Sandboxes are available on May 9th and scratch orgs on May 11th, assuming Salesforce hit their dates.

More Information





Tuesday, 1 April 2025

Secret Agentforce Pen

The Artificial Intelligence revolution continues to upend the technology industry as Salesforce makes its first move into hardware devices with the Secret Agentforce Pen.

The Secret Agentforce Pen resembles a spy pen that everyone will know and love from their youth, but with a key difference - rather than simply recording a conversation, the embedded Agentforce connection acts on what it hears. 

No more running workshops to find out what your users are struggling with when using your Salesforce implementation, simply place the Secret Agentforce Pen in an unobtrusive place in the office and capture their views as they work. 

Any issues or ideas are seamlessly turned into Cases, and the best part is that as your users have no idea they are being recorded, you'll get the unvarnished truth!

Or wear your Secret Agentforce Pen with pride to take action on those casual conversations in the kitchen room. Delight your users when Agentforce implements their request by the time they've returned to their desk - they'll think you are some kind of wizard!

According to a source at Salesforce, speaking on condition of anonymity as they were not authorised to share information about the new product, "We've tried various prototypes over the last 18 months, but struggled to find that blend of cutting-edge Generative AI and fun products from the classified pages of kid's comics in the 70s. The Secret Agentforce Pen was the culmination of this search, providing that elusive mix of cheap retro-style and modern functionality."

Pricing for the Secret Agentforce Pen has not yet been announced, but the same source confirmed that in keeping with the rest of the Agentforce product set, it will be confusing enough that most customers will be scared to use it.