At time of writing, it's pretty much an all-or-nothing affair.
Formula.recalculateFormulas()
re-calculates all formulas
- Sobject's
recalculateFormulas()
method also recalculates all formulas, except for cross-object formulas, but each use counts towards one additional SOQL query and it only operates on a single record
In this situation, you (most likely) can't avoid using a query (unless you can provide the data required to calculate all of your formulas). In fact, if you want to only get updated values for some of your formulas, re-querying your target records is likely the easiest/most efficient way to do it (though if you want updated formula values, you'll have to perform a DML insert/update/upsert as appropriate first).
+edit:
I had an idea where Formula.recalculateFormulas()
may be able to do its work if you provided the parent object inside of the records that you wanted to recalculate formulas for.
Spoiler: It doesn't work like in the way I would expect...
unless you happen to have a valid object Id in your org for the related object
i.e.
// Provided that you have a formula on Opportunity that references the Amount field
// and some cross-object formulas that reference account data...
// I had a feeling this might work (but it doesn't, it generates some internal
// error code)
Account myDummyAccount = new Account(
Id = '001000000000001',
ParentId = '001000000000002',
Name = 'DummyAcct'
);
Opportunity opp = new Opportunity(
// This is not a typo, you can set the nested SObject of a related field
// like this without issue (most of the time)
Account = myDummyAccount
Amount = 10
);
List<FormulaRecalcResult> results = Formula.recalculateFormulas(new List<Opportunity>{opp});
However, it can still consume a query and generates a FormulaRecalcFieldError
(or multiple errors, if you have multiple formulas referencing the same object).
This important information I took from playing around with this is as follows:
- Even though it generates some errors, you are still able to re-calculate and use formula fields that are entirely on-object (as opposed to cross-object, e.g.
Account.Name
in a formula field on Opportunity
)
- If you don't have (or try to assign) a record Id to an in-memory object instance,
Formula.recalculateFormulas()
does not consume a query
- If you have a valid Id in your org for the target related object, you can use that (i.e. if
001000000000001
is a valid Account Id, you could specify that in the AccountId
field on an Opportunity
). You won't encounter errors, you won't use a query, and you'll get the correct values from your related object
Final example code:
Account someAcc = new Account(
Id = '0013m000028yeyK', // had to get this from a query, but it doesn't need
// to be a query in this transaction
ParentId = '001000000000002AAA',
Name = 'testAcct'
);
Opportunity opp2 = new Opportunity(
//Id = '006000000000001', // If you include an Id (or the record has an Id
// from a DML insert), then recalculating formulas
// will consume a query.
// Otherwise, (as in this case) no query is consumed
//Account = someAcc,
AccountId = someAcc.Id, // You'd need to set the relationship field, not the
// embedded object
Amount = 100
);
List<FormulaRecalcResult> results = Formula.recalculateFormulas(new List<Opportunity>{opp2});
system.debug(opp2.Justa_Value__c); // On-object formula, pulls Opp.Amount
system.debug(opp2.Account_Name__c); // Cross-object formula, pulls account name
// from related account
// You won't see any errors here, provided you used a valid id
for(FormulaRecalcResult res :results){
system.debug(res);
}