+ Start a Discussion
Uma PrabhakarUma Prabhakar 

trigger to prevent duplicate records on multiple fields in salesforce

Hello Developers

i am new to the apex development, i have been given a task for creating Triggers to prevent duplicate records on multiple fields , i know this is can be achived through Duplicate Rules, but still i want to try with the Apex trigger

Assignedto__c (Lookup(User)

Quater_Year__c(Picklist)

Month__c(Picklist)

i want to create a Duplication rule on these 3 fields, 

i refered the below link, but i could not get the correct code

https://www.mstsolutions.com/technical/preventing-duplicate-records-based-on-multiple-fields-in-salesforce/

 
Best Answer chosen by Uma Prabhakar
Christan G 4Christan G 4
Hi Viniya! Awe thanks for the compliment! Practice makes perfect. I was once a newbie as well and knew nothing about code. I just studied and kept practicing. I am still learning as we speak. Lol. Anyway, I have updated the logic of the code to mitigate the scenario I mentioned. Please view the updated code below. The main difference in the updated code is that rather than using the contains method, I actually iterate through all the existing records one by one to see if there is a match. Also, due to me iterating through each record, I didn't need those other list I created. As a result, I removed them. If you have any questions, please feel free to reach out to me.

Apex Class (Updated!):
public class ContactTriggerHandler {

    public static void dupContactError (List <Contact> newTrigCon) {
        
    //List of all Contact Records currently in the database    
    List <Contact> allCon = [SELECT ID, Name, Assignedto__c, Quater_Year__c, Month__c FROM Contact];
        
        //Iterate through new potential records
        for (Contact oneConTrig : newTrigCon) {
            
            //Needed to iterate through all existing records which is contained in the allCon List
            for (Integer i = 0; i < allCon.size(); i++)
            
            //If all conditions are met, there is a duplicate and thus, returns an error.
            if (oneContrig.Assignedto__c == allCon[i].Assignedto__c 
                && oneContrig.Quater_Year__c == allCon[i].Quater_Year__c 
                && oneConTrig.Month__c == allCon[i].Month__c) {

                    oneConTrig.addError('Duplicate Contact Error! This record already exists.');
 
           }
        }      
    }
}

All Answers

Christan G 4Christan G 4
Hi Viniya, I hope you are well. Just to be sure that I understand, you would like to create a trigger that would prevent the insertion of a new record if there already exist a record with the same value for those 3 fields?
Uma PrabhakarUma Prabhakar
@Christan

Yes You are Correct, i tried to write the trigger for only one field ie (Assigned to) user lookup field but still i am not able to write a proper trigger for all 3 fields
 
trigger AvoidDup on Contact (before insert, before update)
{
    
    Set<String> setName = new Set<String>();
    For(Contact acc : trigger.new)
    {
        setName.add(acc.Assignedto__c);
    }
    
    if(setName.size() > 0 )
    {
        List<Contact> lstContact = [select id,Assignedto__c from Contact where Assignedto__c in :setName ];
        
        Map<String ,Contact> mapContact = new Map<String,Contact>();
        For(Contact Con: lstContact)
        {
            mapContact.put(Con.Assignedto__c ,Con);
        }
        
        For(Contact Con : trigger.new)
        {
            if(mapContact.containsKey(Con.Assignedto__c))
            {
                Con.Assignedto__c.addError('Applicant Already Exists ');
            }
        }
        
    }
}

 
Christan G 4Christan G 4
Hi Vinya, I wrote a trigger that fulfills your requirement. Please view the code below. I've put comments also so that you could follow my thought process while making it. If you have any questions, please feel free to reach out to me. The code has also been bulkified and as best practice, I have separate the logic of the trigger into its own Apex class. The trigger just calls this method when processing a new contact or contact update.

Apex Class:
public class ContactTriggerHandler {

    public static void dupContactError (List <Contact> newTrigCon) {
        
    //List of all Contact Records currently in the database    
    List <Contact> allCon = [SELECT ID, Name, Assignedto__c, Quater_Year__c, Month__c FROM Contact];
        
    //List to hold all Assignedto ID 
    List <ID> allConAssignedto = new List <ID>();
        
    //List to hold all Quater Year values
    List <String> allConQuarterYear = new List <String>();
        
    //List to hold all Month values    
    List <String> allConMonth = new List <String>();
        
        //Store values to new Lists from allCon List
        for (Contact oneCon : allCon) {
            
            allConAssignedto.add(oneCon.Assignedto__c);
            allConQuarterYear.add(oneCon.Quater_Year__c);
            allConMonth.add(oneCon.Month__c);
            
        }
        
        
        //Iterate through new potential records
        for (Contact oneConTrig : newTrigCon) {
            
            
            //If all conditions are met, there is a duplicate and thus, returns an error.
            if (allConAssignedto.contains(oneContrig.Assignedto__c) 
                && allConQuarterYear.contains(oneContrig.Quater_Year__c) 
                && allConMonth.contains(oneConTrig.Month__c)) {

                    oneConTrig.addError('Duplicate Contact Error! This record already exists.');
 
           }
        }      
    }
}
Apex Trigger:
trigger ContactTrigger on Contact (before insert, before update) {

    ContactTriggerHandler.dupContactError(Trigger.new);
    
}
Christan G 4Christan G 4
After writing this code, I may have to make a potential enhancement based on a certain scenario I thought of just now. Lets say, for example, that there are 10 contacts with the same assigned user but different values for the 2 picklist fields. Imagine there being another set of contacts that have the same Quarter Year picklist value but assigned to different users and have different Month picklist values. Lastly, imagine there being another set of 10 users who have the same month value but different Quarter Year and assigned users. Based on the logic of the code that I writter above, it'll return an error stating that a duplicate exists even though there really isn't a duplicate. I will update the logic to accomodate this type of scenario.
Uma PrabhakarUma Prabhakar
@Christan

I have Never Thought of the Other Possibe Scnerios & Assumptions, And By the Way you have Written such a Beautifull Code that even Newbie like me is able to understand the code line by line, Let me Check in my org and try different assumptions and i will get back to you
Christan G 4Christan G 4
Hi Viniya! Awe thanks for the compliment! Practice makes perfect. I was once a newbie as well and knew nothing about code. I just studied and kept practicing. I am still learning as we speak. Lol. Anyway, I have updated the logic of the code to mitigate the scenario I mentioned. Please view the updated code below. The main difference in the updated code is that rather than using the contains method, I actually iterate through all the existing records one by one to see if there is a match. Also, due to me iterating through each record, I didn't need those other list I created. As a result, I removed them. If you have any questions, please feel free to reach out to me.

Apex Class (Updated!):
public class ContactTriggerHandler {

    public static void dupContactError (List <Contact> newTrigCon) {
        
    //List of all Contact Records currently in the database    
    List <Contact> allCon = [SELECT ID, Name, Assignedto__c, Quater_Year__c, Month__c FROM Contact];
        
        //Iterate through new potential records
        for (Contact oneConTrig : newTrigCon) {
            
            //Needed to iterate through all existing records which is contained in the allCon List
            for (Integer i = 0; i < allCon.size(); i++)
            
            //If all conditions are met, there is a duplicate and thus, returns an error.
            if (oneContrig.Assignedto__c == allCon[i].Assignedto__c 
                && oneContrig.Quater_Year__c == allCon[i].Quater_Year__c 
                && oneConTrig.Month__c == allCon[i].Month__c) {

                    oneConTrig.addError('Duplicate Contact Error! This record already exists.');
 
           }
        }      
    }
}
This was selected as the best answer
Uma PrabhakarUma Prabhakar
@Christan

The Code is Working perfectly fine, i was wondering if it is possible to find the duplicates for the Specific RecordType, ex i have two record types 
1)Contacts and 2)Associated Contacts

Now i want to find the Duplicate in Contact Recordtype itself, how do i do that, i tried to define it  by Putting the Contact RecordTypeID of contact that is 012p0000000Nn0ZAAS

Id RecordTypeIdContact = Schema.SObjectType.Contact.getRecordTypeInfosByName().get('Contact').getRecordTypeId();

is that possible??

i have modified the code, still its not doing anything, its finding duplication for both record types
 
public class ContactTriggerHandler {

    public static void dupContactError (List <Contact> newTrigCon) {
        Id RecordTypeIdContact = Schema.SObjectType.Contact.getRecordTypeInfosByName().get('Contact').getRecordTypeId();
        
    //List of all Contact Records currently in the database    
    List <Contact> allCon = [SELECT ID, Name, User__c, Quater_Year__c, Month__c FROM Contact Where RecordType.Name = 'RecordTypeIdContact'];
        
        //Iterate through new potential records
        for (Contact oneConTrig : newTrigCon) {
            
            //Needed to iterate through all existing records which is contained in the allCon List
            for (Integer i = 0; i < allCon.size(); i++)
            
            //If all conditions are met, there is a duplicate and thus, returns an error.
            if (oneContrig.User__c == allCon[i].User__c 
                && oneContrig.Quater_Year__c == allCon[i].Quater_Year__c 
                && oneConTrig.Month__c == allCon[i].Month__c) {

                    oneConTrig.addError('Duplicate Contact Error! This record already exists.');
 
           }
        }      
    }
}



 
Christan G 4Christan G 4
Hi Viniya, that should be possible. I have made additional changes to the code you provided. I haven't had a chance to test it so I apologize if you experience any errors. Working with schema code can be pretty tricky lol.
 
public class ContactTriggerHandler {

    public static void dupContactError (List <Contact> newTrigCon) {

//Correct code but I don't think you will need it for comparison         
Id RecordTypeIdContact = Schema.SObjectType.Contact.getRecordTypeInfosByName().get('Contact').getRecordTypeId();
        
    //List of all Contact Records currently in the database    
    List <Contact> allCon = [SELECT ID, Name, User__c, Quater_Year__c, Month__c FROM Contact Where RecordType.Name = 'RecordTypeIdContact'];
        
        //Iterate through new potential records
        for (Contact oneConTrig : newTrigCon) {

//Checks to see if Record type of this record equals "Contact"        
boolean newRecMatch = oneConTrig.getSObject().getDescribe().getRecordTypeInfosById().get(oneConTrig.RecordTypeID).equals('Contact');            


            //Needed to iterate through all existing records which is contained in the allCon List
            for (Integer i = 0; i < allCon.size(); i++)
            
            //Checks to see if the record type of the existing record equals "Contact"
            boolean existingRecMatch = oneConTrig.getSObject().getDescribe().getRecordTypeInfosById().get(allCon[i].RecordTypeID).equals('Contact'); 

            //If all conditions are met, there is a duplicate and thus, returns an error.
            if (oneContrig.User__c == allCon[i].User__c 
                && oneContrig.Quater_Year__c == allCon[i].Quater_Year__c 
                && oneConTrig.Month__c == allCon[i].Month__c && newRecMatch == existingRecMatch) {

                    oneConTrig.addError('Duplicate Contact Error! This record already exists.');
 
           }
        }      
    }
}
Uma PrabhakarUma Prabhakar
@Christan

i tried the above code and its working fine, How do i write a Test class for the above code

This is what i have come up so far, but it is not covering anything
 
@isTest
public class ContactTriggerHandlerTestClass {
    
    static testmethod void Duplication(){
        
        List<Contact> Contacts = new list <Contact>();
        Id RecordTypeIdLead = Schema.SObjectType.Contact.getRecordTypeInfosByName().get('Contact').getRecordTypeId();
        
        Account testAccount = new Account();
        testAccount.Name='DCCL' ;
        insert testAccount;
        
        contact con = new contact();
        con.FirstName='Julian';
        con.LastName='Rogers';
        con.Month__c = 'march';
        con.Quater_Year__c = '2020';
        con.AccountId = testAccount.id;
        Contacts.add(con);
    }
    
}

 
Christan G 4Christan G 4
Hi Viniya! Sorry for the late reply. I fell asleep. Below I posted all the steps you'll need to do to achieve 100% code coverage. Unfortunately, don't have time to write it right now due to having to get ready for work. In the meantime, feel free to try writing the test class for it based on my guidance. I plan to write the test class later tonight.

//Test Setup
    //Create 200 test Users
    //Create 200 contact records and for each contact to one of the test users through the AssignedTo attribute
    //Please note that the reason why we are creating so many contact records is to ensure that the trigger is bulkified
    //Insert 200 Contact Records
    //Use a system Assert method to ensure that there only exist 200 contact after the insert
    //Create another contact that is a duplicate of one of the test contacts you inserted. Don't insert the duplicate contact yet.
    //Start testing using: System.Test.startTest(); method
    //Use Database.insert(List <sObject>, boolean); to insert the second contact.
    //Make sure boolean above is set to true to simulate as if we are using DML 
    //We are using Database method to retrieve the duplicate error message through its Database results counterpart.
    //Pass the second contact into the method we are testing: ContactTriggerHandler.dupContactError(List <Contract> newTrigCon); 
    //End testing using - System.Test.stopTest();  method
    //Evaluate Test Results
    //Use System assert method to evaluate that there still only exist 200 contact records and not 201 records
    //If the code worked, it should have prevented the second contact from being inserted since it is a duplicate
    //Iterate through the Database Results and ensure that the error message received is equivalent to the error message we specified.
Uma PrabhakarUma Prabhakar
@Christan
Sorry to bother you again and again,but i tried designing a test class from above instruction but i am able to cover only 50%
 
@isTest
public class ContactTriggerHandlerTestClass {
    
    static testmethod void Duplication(){
        
        List<Contact> Contacts = new list <Contact>();
        Id RecordTypeIdContact = schema.SObjectType.Contact.getRecordTypeInfosByName().get('Contact').getRecordTypeId(); 
        
        //insert Account     
        Account testAccount = new Account(); 
        testAccount.Name='DCCL' ; 
        insert testAccount;
        
        // insert contact  
        contact con = new contact(); 
        con.FirstName='Julian'; 
        con.LastName='Rogers'; 
        con.Month__c = 'march'; 
        con.Quater_Year__c = '2020'; 
        con.AccountId = testAccount.id; 
        con.User__c= con.Id;
        con.Email = 'hello123@gmail.com';
        con.recordtypeid=RecordTypeIdContact;
        Contacts.add(con);
        insert con;
        
        
        // Trying to insert duplicate contact with same User__c, Month__c, Quater_Year__c  and recordtype as your are comparing these fields in your trigger.
        
        if(con.id!=NULL){
            contact con1=new contact();
            con1.firstname='Testing';
            con1.lastname='duplicate Contact';
            con1.Month__c = 'march';
            con1.Quater_Year__c = '2020';
            con1.AccountId = testAccount.id;
            con1.User__c= con.Id;
            con1.recordtypeid=RecordTypeIdContact;
            try{
                insert con1;
            }
            catch(Exception ex){
                Boolean expectedExceptionThrown =  ex.getMessage().contains('Duplicate Contact Error! This record already exists.') ? true : false;
            }
        }
        
    }
    
}

 
Uma PrabhakarUma Prabhakar
@Christan
i removed the user__c in the test class and i run the test and it worked fine, it covers 100% code, i am pasting the final code, plz check and let me know if this is the best pratice to write a test class

@isTest
public class ContactTriggerHandlerTestClass {
    
    static testmethod void Duplication(){
        
        List<Contact> Contacts = new list <Contact>();
        Id RecordTypeIdContact = schema.SObjectType.Contact.getRecordTypeInfosByName().get('Contact').getRecordTypeId(); 
        
        //insert Account     
        Account testAccount = new Account(); 
        testAccount.Name='DCCL' ; 
        insert testAccount;
        System.debug('Account  inserted....'+testAccount);
        
        // insert contact  
        contact con = new contact(); 
        con.FirstName='Julian'; 
        con.LastName='Rogers'; 
        con.Month__c = 'march'; 
        con.Quater_Year__c = '2020'; 
        con.AccountId = testAccount.id;
        con.Email = 'hello123@gmail.com';
        con.recordtypeid=RecordTypeIdContact;
        Contacts.add(con);
        insert con;
        System.debug(' First Contact  inserted...and Id is .....'+con.id);
        
        
        // Trying to insert duplicate contact with same User__c, Month__c, Quater_Year__c  and recordtype as your are comparing these fields in your trigger.
        
        if(con.id!=NULL){
            contact con1=new contact();
            con1.firstname='Testing';
            con1.lastname='duplicate Contact';
            con1.Month__c = 'march';
            con1.Quater_Year__c = '2020';
            con1.AccountId = testAccount.id;
            con1.recordtypeid=RecordTypeIdContact;
            System.debug(' Scecond Contact  inserted...and Id is .....'+con.id);
            try{
                insert con1;
            }
            catch(Exception ex){
                Boolean expectedExceptionThrown =  ex.getMessage().contains('Duplicate Contact Error! This record already exists.') ? true : false;
                System.debug('Error message is '+ex.getMessage());
            }
            
        }
        
    }
    
}
Christan G 4Christan G 4
Hi Viniya, don’t worry regarding asking questions. I am happy to help! The test code you written is good start but I wouldn’t say it is the most optimal way. Did you make sure that you got 100% code coverage for both the trigger and the class method you are testing?
Christan G 4Christan G 4
Hi Viniya, so sorry for the late post. Below is the more optimal test code for your situation. I made some minor adjustments to the instructions and included all my comments in the code so that it is easy to follow. You should not only receive 100% test code coverage for the trigger but also the method also. If you have any questions, please feel free to reach out to me.

Apex Test Class:
@isTest
public class ContactTriggerHandlerTest {

    //Test Setup
    //Create 1 test User
    //Create 1 contact records and assign to that test user. Insert the contact record
    //Create a list of 200 Contact Duplicate Records
    //Please note that the reason why we are creating so many duplicate records is to ensure that the trigger is bulkified
    //Start testing using: System.Test.startTest(); method
    //Use Database.insert(List <sObject>, boolean); to insert the second contact.
    //Make sure boolean above is set to false to prevent exception thrown when testing 
    //We are using Database method to retrieve the duplicate error message through its Database.SaveResults counterpart.
    //Pass the 200 duplicate contacts into the method we are testing: ContactTriggerHandler.dupContactError(List <Contract> newTrigCon); 
    //End testing using - System.Test.stopTest();  method
    //Evaluate Test Results
    //Use System assert method to evaluate that there still only exist 1 contact records and not 201 records
    //If the code worked, it should have prevented the 200 contacts from being created since they were duplicates
    //Iterate through the Database.SaveResults received and ensure that the error message received is equivalent to the error message we specified
    
    
    //TEST CODE STARTS HERE!
    
    @isTest static void dupContactErrorTest() {
        
     	   //Create test User
           User testUsr = new User(LastName = 'LastName Test',
                                  Alias = 'UsrR',
                                  Email = 'ClasstestReg@curious-fox.com',
                                  Username = 'ClasstestReg@test.com',
                                  CommunityNickname = 'ClassTestReg',
                                  ProfileId = [SELECT Id from Profile WHERE Name = 'Standard User' LIMIT 1].id,
                                  FirstName = 'Test Reg',
                                  TimeZoneSidKey = 'America/Los_Angeles', 
                                  LocaleSidKey = 'en_US',
                                  EmailEncodingKey = 'UTF-8', 
                                  LanguageLocaleKey = 'en_US');     
        
        
        insert testUsr;
        
        //Create ID variable to hold record type
        ID recType = Schema.SObjectType.Contact.getRecordTypeInfosByName().get('Contact').getRecordTypeID();
        
        
        //Create a contact and assign test user to it
        Contact testCon = new Contact(LastName = 'LastCon Test',
                                      Quater_Year__c = '',
                                      Month__c = 'Month 1',
                                      Assignedto__c = testUsr.id//,
                                      RecordTypeID = recType;
                                      );
        
        insert testCon;
        
        //Create list to hold duplicate Contacts
        List <Contact> dupCon = new List <Contact>();
        
        //Add 200 duplicate Contacts to the list. We are adding 200 to ensure code is bulkified.
        for (Integer i = 0; i < 200; i++) {
            
        dupCon.add(new Contact(LastName = 'LastCon Test',
                               Quater_Year__c = '',
                               Month__c = 'Month 1',
                               Assignedto__c = testUsr.id//,
                               RecordTypeID = recType;
                               ));    
              
        }
        
        //Tell System we are ready to test
        System.Test.startTest();
        
        //Insert 200 Duplicate Records
        List <Database.SaveResult> res = Database.insert(dupCon, false); //This tests the trigger
        ContactTriggerHandler.dupContactError(dupCon); //This tests the code in the methods
        
       
        //Tell system to stop Testing
        System.Test.stopTest();
        
        
        //Evaluate test results
        
        //Iterate through the errors received from trying to insert 200 duplicate records
        for (Database.SaveResult oneRes : res) {
            
            //If the insert was not successful,
            if(!oneRes.isSuccess()) {
                
                //Then iterate through the errors
                for (Database.Error oneEr : oneRes.getErrors()) {
                    
                    //This system check ensures that the error message received is the same as what the code specified
                    System.assertEquals('Duplicate Contact Error! This record already exists.', oneEr.getMessage());
                    
                }   
            }
        }
        
      //Create a list of all Contacts in the database.
      Contact[] allCon = [SELECT ID FROM Contact];
      
      //Due to the prevention of the duplicate rule, there should only exist one record contact still. Lets check
      System.assertEquals(true, allCon.size() == 1);  
        
    }
}