I've had help creating some Apex code which is supposed to do the following

Via Web-to-Lead I'm populating a field in our Lead Object called Promo_Code__c. Promo_Code__c has also been setup in our Campaign Object as an External ID. What I want to be able to do, is upon creation/update of a Lead is to cross reference the Promo_Code__c with the relevant Campaign and link the Lead to that Campaign (using Promo_Code__c External ID).

The code below saves okay, and works correctly if I create a Lead Record, save it, then edit it and include the Promo_code__c. It doesn't work correctly however if I create a brand new Lead and input the Promo_Code__c before saving. If I do this, the error code is as follows:

There were custom validation error(s) encountered while saving the affected record(s). The first validation error encountered was "Apex trigger Update_Campaign caused an unexpected exception, contact your administrator: Update_Campaign: execution of BeforeUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: DUPLICATE_VALUE, This entity is already a member of this campaign: []: Trigger.Update_Campaign: line 32, column 1".

I'm guessing it's an issue with the after insert piece....

The Lead Trigger is as follows

trigger Update_Campaign on Lead (after insert, before update) {

  List<ID> LeadIds = new List<ID>();
  List<String> prStr = new List<String>();
  List<CampaignMember> newList = new List<CampaignMember>();

    for(Lead LD: Trigger.new){
    prStr.add(LD.Promo_Code__c); //Assuming Promo_Code__c is of Text type;

    Map<String,Id> cmMap = new Map<String,ID>();

    for(Campaign cm1 :[select id,Promo_Code__c from Campaign where Promo_Code__c IN: prStr])

    for(Lead LD: Trigger.new){
            CampaignMember cm = new CampaignMember(CampaignId=cmMap.get(LD.Promo_Code__c),LeadId=Ld.id);

        insert newList;


1 Answer 1


With version 16 of the API, you were not allowed to insert a preexisting campaign member for the same contact or lead. You can change the API version of your code to version 15 or check for the existence of the lead in the campaign and use an upsert in lieu of an insert. This is tricky because you have to find all of the existing campaign members, and it also must be bulk safe in a trigger context, prior to upserting. To do this, you need a combo key for a map (string of lead id + campaign id). Here's all the code, although it was written for having an actual campaign id and not a promo code as a foreign key match, so you'd have to make some minor adaptations:

public static void NewLeadAddToCampaign(list<Lead> listLead) {

    Map<Id,Lead> leadsToProcess = new Map<Id, Lead>();
    for (Lead l : listLead) {
        if (l.Campaign_Id__c != NULL) {
            leadsToProcess.put(l.id, l);

    if (leadsToProcess.size() > 0) {            
        List<CampaignMember> membershipRecords = new list<CampaignMember>();
        for(Lead thisLead : leadsToProcess.values()) {
            if (thisLead.Campaign_Id__c != NULL) {  
                CampaignMember cmLead = new CampaignMember (                


        if (membershipRecords.size()>0) {           

public static void upsertCM(list<CampaignMember> cml) {

    list<CampaignMember> existingCM = findExistingCM(cml);
    list<Database.UpsertResult> uResults = database.upsert(existingCM, false);


//find and update existing CampaignMembers
public static list<CampaignMember> findExistingCM (list<CampaignMember> cmList) {
    map<id,id> memberIds = new map<id,id>();
    map<string,CampaignMember> concatIds = new map<string,CampaignMember>();
    list<CampaignMember> cmResult = new list<CampaignMember>();

    //construct Contact/Lead Id to Campaign Id map for finding existing CampaignMembers
    for (CampaignMember cm : cmList) {
        if (cm.ContactId!=null && cm.CampaignId!=null) 
            memberIds.put(cm.ContactId, cm.CampaignId);
        else if (cm.LeadId!=null && cm.CampaignId!=null) 
            memberIds.put(cm.LeadId, cm.CampaignId);

    //find all existing CampaignMembers     
    for (CampaignMember cm:[SELECT Id, ContactId, LeadId, CampaignId, LastModifiedDate FROM CampaignMember WHERE (ContactId in :memberIds.keySet() OR LeadId in:memberIds.keySet()) AND CampaignId in :memberIds.values()]) {
        if (cm.ContactId!=null) concatIds.put((string)cm.ContactId + ((string)cm.CampaignId), cm);
        else concatIds.put((string)cm.LeadId + ((string)cm.CampaignId), cm);

    //loop through CampaignMembers to insert, add Id for upsert if they exist already
    for (CampaignMember cm:cmList) {
        id memberid;
        if (cm.ContactId!=null) memberid = cm.ContactId;
        if (cm.LeadId!=null) memberid = cm.LeadId;
        //if the CampaignMember exists, add the existing CampaignMember to the results, update the status
        if (memberid!=null && cm.CampaignId != NULL && concatIds.keyset().contains((string)memberId + cm.CampaignId)) {
            CampaignMember tempcm = concatIds.get((string)memberId + cm.CampaignId);
            tempcm.Status = cm.Status;
        //if the CampaignMember doesn't exist, add the original result
        } else cmResult.add(cm);
    return cmResult;

As for writing your tests for you, you should take a cut at that yourself and come back here if you get stuck.

  • Thanks @greenstork, appreciate your help. I'll give the test code a crack and see how i go :)
    – user8622
    Commented Jun 2, 2014 at 0:48

You must log in to answer this question.

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