declarative-lookup-rollup-summaries
declarative-lookup-rollup-summaries copied to clipboard
Merged child records make RollupService potentially throw
Describe the bug
If any child record is mergable (e.g. Contacts or Cases) and was merged, the child update will go down the 'parent was merged' path. We get a mixture of unrelated DLRS rules and somewhere things get messy.
To Reproduce
In our case we have 2 dlrs rules:
- Realtime COUNT open Case records on Account (RelationshipField__c: AccountId)
- Realtime COUNT open Task records on Account (RelationshipField__c: WhatId)
Create 2 Case records for the Account and merge them.
Try to update the merged Case: It will not work:
CaseTrigger: execution of AfterUpdate caused by:
System.SObjectException: Invalid field WhatId for Case
Class.RollupService.handleRollups: line 1065, column 1
Class.RollupService.triggerHandler: line 334, column 1
Trigger.CaseTrigger: line 7, column 1
What happens
Even though this is a child update, RollupService
line 915: if (sObjectDescribe.isMergeable())
will evaluate to true. Because the merged Case has the MasterRecordId
populated, the RollupService goes down the path for merged parent records, even though this is a child update. After the loop line 932 to 941, all possible child types for Case will be used for further processing, including Task
.
Because we have a rule for Task
, it will be return by describeRollups()
in line 944. Later in line 1065 we try to get the value of WhatId
from the Case record, which makes it throw...
Expected Behaviour
The update should not go down the 'parent was merged' path and only process the correct rules.
Thoughts on a solution
The child records can be mergable too and also can be the parent of another rule at the same time (e.g. in theory we could also count Tasks on Case or something similar).
So currently i think the only save way around it, would be to have separate triggers for parent merge checks and for child updates, which pass a additional parameter like Boolean possibleMerge
to RollupService.triggerHandler()
and only go down this path if this is true
:shrug:
This is not very optimal, but i do not have another idea yet. And also a lack of time to look deeper into it :S
Another way could be to change the block from 916 - 925, to not only check if MasterRecordId
is set and instead check if it changed. Not sure about the consequences. Could look like this:
if (sObjectDescribe.isMergeable()) {
for (Id recordId : existingRecords.keySet()) {
SObject existingRecord = existingRecords.get(recordId);
SObject newRecord = newRecords.containsKey(recordId) ? newRecords.get(recordId) : null;
try {
Id oldMasterRecordId = (Id) existingRecord.get('MasterRecordId');
Id newMasterRecordId = newRecord != null ? (Id) newRecord.get('MasterRecordId') : null;
if (newMasterRecordId != oldMasterRecordId) {
masterRecordIdsFromMerge.add(oldMasterRecordId);
}
} catch (Exception e) {
}
}
Workaround in the meantime
WARNING: this can lead to unwanted bypasses and really depends on what cross record automations you use..... (The Bypass will be active for the whole transaction) If possible in your usecase, bypass the conflicting rule in your trigger like this:
trigger dlrs_CaseTrigger on Case
(before delete, before insert, before update, after delete, after insert, after undelete, after update)
{
dlrs.RollupService.bypass('OpenTaskCountOnAcc');
dlrs.RollupService.triggerHandler(Case.SObjectType);
}
Related Error Reports
#679 #926 (possibly) https://trailhead.salesforce.com/trailblazer-community/feed/0D54S000009cvFNSAY https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000FSUzCSAX
I ran into this issue with merged Cases. Error:
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY: dlrs_CaseTrigger: execution of AfterUpdate caused by: System.SObjectException: Invalid field Name for Case (dlrs) Trigger.dlrs_CaseTrigger: line 7, column 1.
I've encountered this as well and although I don't totally follow the technical details outlined above, I think I understand the crux and it's the same problem.
In my example I'm counting child cases and rolling up to the parent, and I think I agree that the child updates are triggering the rollup to run again and the code is getting confused.
I do think there is a fundamental issue that should be addressed (invalid field name is not actually an invalid field name), but the suggestion above to bypass + link to the trailhead community issue gave me an idea that's working. What I noticed is that the error is actually a flow error that's failing. I added entry criteria for status != 'merged' to the flows that run on Case with the hypothesis that it wasn't the merge operation itself triggering the error, but subsequent flows firing that called DLRS again.
It worked!
This might not be a viable solution for everyone, but could be a better way of working around the problem for certain circumstances. I'm not sure if doing an Account or Contact merge has a clean way of determining within Flow they were the target of a merge operation, but maybe there's a way.
Hope this helps someone else
I believe this is tied to #1038, if this is still happening in your environment please comment with your installed version on that issue and provide any details to reproduce.
Thanks!