0

I have a custom field on my AccountContactRelation object, called SurrogateId__c, that has to be unique across all AccountContactRelation. I have the following code that is called upon before insert of an AccountContactRelation:

public static void attachAccountAndContact(List<AccountContactRelation> accountContactRelations) {
    List<AccountContactRelation> existingRelationships = (new AccountContactRelationsSelector()).selectRelatedContactsByAccountId(AccountHelper.getAccountIdSet(accountContactRelations));
    Map<Id, Map<Id, AccountContactRelation>> existingAccountContactToRelation = accountIdContactIdToRelation(existingRelationships); // maps account -> contact -> accountcontactrelation

    List<AccountContactRelation> relationsToUpdate = new List<AccountContactRelation>{};
    for(AccountContactRelation accountContactRelation: accountContactRelations){
        Id accountId = accountContactRelation.AccountId;
        Id contactId = accountContactRelation.ContactId;
        if (existingAccountContactToRelation.keySet().contains(accountId) && existingAccountContactToRelation.get(accountId).keySet().contains(contactId)) {
            accountContactRelation.addError('Contact ' + contactId + ' is already connected to account ' + accountId);
            AccountContactRelation existingRelation = existingAccountContactToRelation.get(account.Id).get(contact.Id);
            existingRelation.SurrogateId__c =  accountContactRelation.SurrogateId__c;
            relationsToUpdate.add(existingRelation);
        }
    }
    if (relationsToUpdate.size() > 0) {
        Database.update(relationsToUpdate);
    }
}

Basically I try to update the existing AccountContactRelation with a - potentially - new surrogate id. I add an error to the record being inserted to skip it as the relationship is already there.

I'm running the following test:

private static void accountRelationAvoidInsertIfAlreadyExists() {
    Account account = new Account(Name = 'Example company');
    insert(account);
    Contact contact = new Contact(FirstName  = 'John', LastName = 'Smith', AccountId = account.Id); // note that at this point a trigger on the user sets the SurrogateId as JohnSmith
    upsert(contact);
    AccountContactRelation relation1 = new AccountContactRelation(SurrogateId__c = 'AnotherSurrogateKey');
    Database.Insert(relation1, false);
    List<AccountContactRelation> checkRelations = [SELECT Id, SurrogateId__c, Account.Name, Contact.FirstName, Login_email__c from AccountContactRelation WHERE AccountId =: account.Id];

    System.assertEquals(1, relations1.size());
    System.assertEquals('AnotherSurrogateKey', relations1.get(0).SurrogateId__c);

}

First test passes - that suggests that as intended, there's no additional relationship added between contact and account. However, the second test fails. It looks like the update at the end of the code was rollbacked - as SurrogateId__c at the end of the test is JohnSmith, as in the beginning of the test, and not AnotherSurrogateKey as I would expect.

Note that debugging the code, I can be sure that the update is executed (I tried to select the updated AccountContactRelations right after the Database.update, and indeed it works. As soon as I arrive at the end of the test, however, any change was rolled back).

Any idea about what is going on? Why is the update rolled back? In my understanding the addError should allow me to skip the insertion of that record and it should not trigger any rollback.

Is there any other way to do what I'm trying to do - so on before insert skip the creation of a record but update the existing one?

0

So, short answer is that addError will rollback the entire transaction unless the DML is invoked using

  1. Database.insert(recList,false); // isAllorNothing = false
  2. REST composite API request sobjects that does the equivalent of #1 (AllOrNone property)
  3. SOAP API equivalent

Rather than doing addError, why query for the presence of the records you are going to insert, and, if any found skip the record(s)?

  • I'm actually calling Database.insert(relation1, false) - see the test code. And this is a before insert trigger, I think addError is the only way to skip the record. And as per documentation, it should roll back any operation on the object itself, not on every single object in the transaction. – Pamela Gotti Nov 21 '18 at 8:36
  • @PamelaGotti - true, it will not do the insert for the testmethod execution -- but what about the 'live' use case? will every invoker of this attachAccountAndContact method be called with isAllorNothing = false? – cropredy Nov 21 '18 at 22:39
  • yes, we don't actually care to add a new relationship and for this particular trigger the already existing exception is the only error we can have, so it's safe so set isAllOrNothing = false – Pamela Gotti Nov 22 '18 at 13:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.