13

I have created a trigger that is supposed to avoid a (lead) record being inserted if it contains an email found in another (contact) record. I am able to get the trigger to update (lead) fields if a match is found, however I want it to delete the (lead) record/s instead of inserting them, i.e., I do not want the record/s to be inserted if a match is found. I get the following error:DML statement cannot operate on trigger.new or trigger.old: below is my trigger code, what I must do to get the trigger to fire successfully on a match?

trigger triggerULDC on Lead(before insert){

map<string,list<Contact>> BObjMap = new map<string,list<Contact>>();
for(Contact contact : [Select Id, Name, email From Contact]){
    if(BObjMap.get(contact.email) == null){
        BObjMap.put(contact.email, new list<Contact>());
    }
    BObjMap.get(contact.email).add(contact);
}

List<Lead> listLeadsToDelete = new List<Lead>();
for(Lead lead : trigger.new){
    if(BObjMap.containskey(lead.email)){
    listLeadsToDelete.add(lead);
    continue;
    }
    }
    delete listLeadsToDelete;

}

2
  • @DanielHoechst - Were you able to successfully create a test class using this methodology? In my testmethod, when I try to insert two records, the test the assertion, I am still getting 2 records returned. Any feedback.
    – user5044
    Commented Mar 14, 2014 at 23:16
  • 1
    welcome to salesforce.stackoverflow Neo Donlad. Answers are intended for answers to the original question. You'll be able to comment anywhere once you've got some reputation. You can earn reputation by posting good questions or answers. Commented Mar 15, 2014 at 12:26

2 Answers 2

7

What follows is a trivial trigger that will delete a lead that is considered "duplicate" based on email address. Ideally, you would want more criteria, such as choosing at minimum last name, email, phone, and company, but this is just to get the creative juices flowing.

Note that the magic happens by calling clone on the record; we can't DML on Trigger.new records, but simply by cloning them, we skirt around that limitation. On completion of a duplicate record in the UI, users will see the following error:

Record deleted
The record you attempted to access has been deleted. The user who deleted this record may be able to recover it from the Recycle Bin. Deleted data is stored in the Recycle Bin for 15 days. 

Unfortunately, we can't customize or intercept this message, so it's probably better to limit this logic to automatic processes, such as web to lead, etc. Following is the code that I produced to demonstrate the logic.

trigger deleteDuplicateLead on Lead (after insert) {
    Lead[] dupes = new Lead[0];
    Set<String> email = new Set<String>(), dupEmail = new Set<String>();
    for(Lead record: Trigger.new)
        email.add(record.email);
    email.remove(null);
    for(Lead record: [SELECT Id, Email FROM Lead WHERE Email IN :email])
        if(!Trigger.newMap.containsKey(record.id))
            dupEmail.add(record.Email);
    for(Lead record: Trigger.new)
        if(dupEmail.contains(record.Email))
            dupes.add(record.clone(true));
        else
            dupEmail.add(record.Email);
    delete dupes;
}
12

You won't be able to delete the leads because your trigger is firing before insert. The leads aren't in the database yet. What you should probably do here is add an error to the records that have duplicates.

for(Lead lead : trigger.new){
    if(BObjMap.containskey(lead.email)){
        lead.email.addError('Lead with that email address already exists');
    }
}

The error then will display to the user and also prevent it from being inserted.

If you really want to delete it without any feedback to the user, change the trigger to an after insert so the records are in the database. You will also have to clone the leads you want to delete so that you aren't doing dml on the Trigger.new.

for(Lead lead : trigger.new){
    if(BObjMap.containskey(lead.email)){
        listLeadsToDelete.add(lead.Clone(true));
    continue;
    }
if (!listLeadsToDelete.isEmpty()) {
    delete listLeadsToDelete;
}
13
  • what if the user wants some records to process and some to fail ?Assuming a bulk load of 200 is happening?My question is will add error stop all dml or can i do a partial processing with database.insert() and sepcifying boolean as false? Commented Oct 8, 2013 at 12:47
  • You can use the AllorNoneHeader salesforce.com/us/developer/docs/api/Content/… when doing the bulk load. Commented Oct 8, 2013 at 12:49
  • is this available in trigger context too? Commented Oct 8, 2013 at 12:51
  • 2
    You'd have to set it prior to doing the insert. If you are doing that via SOAP API, then just set the header there. If you are doing it in Apex, you can set DMLOptions when doing the insert. Commented Oct 8, 2013 at 12:53
  • Thank you for some lessons to me !Appreciate it from heart !Thanks again Commented Oct 8, 2013 at 12:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .