Trigger: On Insert Contact check if linked Account is already associated to other Contact. Throw Error.

Scenario:While inserting Contact check if linked Account is already associated to other Contact. Throw Error is yes.

ContactTrigger
trigger ContactTrigger on Contact (before insert) {
    if(trigger.isBefore && trigger.isInsert) { 
    	ContactTriggerHandler.checkIfAccountAlreadyLinked(trigger.new);
    }
}
ContactTriggerHandler
public class ContactTriggerHandler { 
    // Scenario: While inserting Contact check if linked Account is already associated to other Contact. Throw Error is yes.
    public static void checkIfAccountAlreadyLinked(List<Contact> lstContact) { 
        Set<String> setAccIds = new Set<String>();
        Map<String, Id> oldContactsMap = new  Map<String, Id>();
        for(Contact con: lstContact) { 
            if(con.AccountId != null) { 
                // Note : Here we can't check con.AccountId != '' - because An ID isn't ever N/A. It will always be one of null
                 setAccIds.add(con.AccountId);
            }
        }
        for(Contact con: [Select Id, AccountId From Contact Where AccountId in: setAccIds]) { 
            oldContactsMap.put(con.AccountId, con.Id);
        }
        for(Contact con: lstContact) { 
            if(con.AccountId != null && oldContactsMap.containsKey(con.AccountId)) { 
                con.AddError('Contact with same Account already exist.');
            }
        }
    }

}

How the code works

  1. The trigger runs before insert on Contact and passes the new Contacts to the handler.
  2. The handler collects the Account Ids of the new Contacts. It skips Contacts with no Account, because an Id field is either filled or null, never an empty string.
  3. One query finds existing Contacts on those Accounts and stores their Account Ids in a Map.
  4. Any new Contact whose Account is already in the Map gets an error through addError, which stops it from being saved.

A gap in this version

The check only looks at Contacts that are already saved. If you insert two Contacts for the same new Account in one operation, for example from Data Loader, both pass the check. The improved handler below also remembers the Accounts it has already seen in the same batch.

Improved handler

public class ContactTriggerHandler {
    public static void checkIfAccountAlreadyLinked(List<Contact> lstContact) {
        Set<Id> accIds = new Set<Id>();
        for (Contact con : lstContact) {
            if (con.AccountId != null) {
                accIds.add(con.AccountId);
            }
        }
        Set<Id> accountsWithContact = new Set<Id>();
        for (Contact con : [SELECT AccountId FROM Contact WHERE AccountId IN :accIds]) {
            accountsWithContact.add(con.AccountId);
        }
        for (Contact con : lstContact) {
            if (con.AccountId == null) {
                continue;
            }
            if (accountsWithContact.contains(con.AccountId)) {
                con.addError('Contact with same Account already exist.');
            } else {
                accountsWithContact.add(con.AccountId);
            }
        }
    }
}

How to test

  1. Create a Contact on an Account that has no Contacts. It should save.
  2. Create a second Contact on the same Account. You should see the error message.
  3. Insert two Contacts for the same new Account in one Data Loader file. The second one should fail.

FAQ

Why before insert?
addError works before the record is saved, so the insert is cancelled with a clear message.

Does this stop updates?
No. The trigger runs only on insert. Add before update logic if Contacts can also be moved to another Account.

This Post Has 2 Comments

  1. shivam

    Trigger: On Insert Contact check if linked Account is already associated to other Contact. Throw Error.
    In this trigger try to use map instead of String as a key.

  2. shivam

    Trigger: On Insert Contact check if linked Account is already associated to other Contact. Throw Error.
    In this trigger try to use map instead of String as a key.

Leave a Reply