I've seen some strange behaviour with the new ability to have trigger on OpportunityContactRole
and its rather special IsPrimary
field.
For a long time, I've observed that if you set IsPrimary=true
on an OCR, then Salesforce takes care of setting it false for any other records on the same Opportunity. But, this produces weird results in the trigger context...
It seems that if I use this method to implicitly unset IsPrimary
on an OCR, then later directly set IsPrimary=true
, then the trigger context shows IsPrimary=true
in both the old and new trigger lists.
Fortunately, I have code to explain all this more clearly.
There are two tests here, sharing the same setup code. In the first test, one OCR is primary and the other is not. In the second test, both OCRs are set to primary, so the second one implicitly wins. In both tests, we query back the OCRs after insert to check that the first has IsPrimary=false
. And, in both tests, we store the results of that query in a static variable. The trigger asserts that IsPrimary
is the same in Trigger.old
as it is the static variable. For the second test, this assertion fails.
@IsTest
public class OpportunityContactRoleChangedTest {
public static OpportunityContactRole afterInsert;
private static Opportunity testOpportunity = new Opportunity(Name = 'Test Opp', CloseDate = Date.today(), StageName = 'Open');
private static List<Contact> testContacts = new List<Contact> { new Contact(LastName = 'Test 1'), new Contact(LastName = 'Test 2') };
private static List<OpportunityContactRole> testOpportunityContactRoles;
static void setup() {
insert testOpportunity;
insert testContacts;
testOpportunityContactRoles = new List<OpportunityContactRole> {
new OpportunityContactRole(
OpportunityId = testOpportunity.Id,
ContactId = testContacts[0].Id,
IsPrimary = false
),
new OpportunityContactRole(
OpportunityId = testOpportunity.Id,
ContactId = testContacts[1].Id,
IsPrimary = true
)
};
}
@IsTest
static void succeeds() {
setup();
insert testOpportunityContactRoles;
afterInsert = [SELECT IsPrimary FROM OpportunityContactRole WHERE Id = :testOpportunityContactRoles[0].Id];
System.assertEquals(false, afterInsert.IsPrimary);
testOpportunityContactRoles[0].IsPrimary = true;
update testOpportunityContactRoles[0];
}
@IsTest
static void fails() {
setup();
testOpportunityContactRoles[0].IsPrimary = true;
insert testOpportunityContactRoles;
afterInsert = [SELECT IsPrimary FROM OpportunityContactRole WHERE Id = :testOpportunityContactRoles[0].Id];
System.assertEquals(false, afterInsert.IsPrimary);
testOpportunityContactRoles[0].IsPrimary = true;
update testOpportunityContactRoles[0];
}
}
Trigger code:
trigger OpportunityContactRoleChanged on OpportunityContactRole (after update) {
for(OpportunityContactRole ocr : Trigger.old) {
System.assertEquals(OpportunityContactRoleChangedTest.afterInsert.IsPrimary, ocr.IsPrimary, ocr);
}
}
So, I the question is: Is this a bug in SF? Or is it wrong to just go around setting IsPrimary
without unsetting it elsewhere? Or have I made some silly mistake here?