6

I have a scenario where Contacts are being fed(upsert operation) from a third party application. Along with the Contact information, contact has an External Id for campaign. If a contact is updated, I need to first check if the contact is already a Campaign member of that campaign. If not add a new campaign member otherwise update the campaign member. It sounds to me like an upsert operation. Following is the logic I am using.

Here I am building a map of campaigns and Campaign external ID, where UTMCampaignID is a set of all campaign external id's fed from third party.

           //building a set of campaigns belonging to contact's UTMCmapaign Id
            List<Campaign> ExpenseCampaignList = [select Id, UtmCampaign_ID__c 
                                                    from Campaign
                                                    where UtmCampaign_ID__c in :UTMCampaignId];

            //building a map of campiagns and UTMCampaign ID
            map<string, Campaign> ExpenseCampaignMap = new map <string, Campaign>();
            for(Campaign c : ExpenseCampaignList)
            {   
                ExpenseCampaignMap.put(c.UtmCampaign_ID__c, c);
            }

In order to upsert campaign members, I am creating a new instance of campaign members and assigning an external id to them, which is nothing but concatenation of Campaign ID and Contact ID.

           list<CampaignMember> newCampaignMembersExpCamp = new list<CampaignMember>();
           Campaign UTMCampaign;
            for(Contact con: trigger.new)
                {

                    UTMCampaign = ExpenseCampaignMap.get(con.utmcampaign__c);
                    string ExternalCampaingID = UTMCampaign.Id;
                    ExternalCampaingID += con.Id;
                    CampaignMember cm= New CampaignMember(CampaignId=UTMCampaign.Id, ContactId= con.Id , Status='Opt-In',External_ID__c = ExternalCampaingID);
                    newCampaignMembersExpCamp.add(cm);
                }
            upsert newCampaignMembersExpCamp External_ID__c;

The above code works fine for a new campaign member, however if campaign member already exist, the code errors out. Following is the error message.

Review all error messages below to correct your data.
Apex trigger contactTrigger caused an unexpected exception, contact your administrator:
contactTrigger: execution of AfterUpdate caused by: System.DmlException: Upsert failed.  
First exception on row 0; first error: INVALID_FIELD_FOR_INSERT_UPDATE, 
Unable to create/update fields: ContactId. Please check the security settings of this  
field and verify that it is read/write for your profile or permission set.: 
[ContactId]: Trigger.contactTrigger: line 67, column 1
3

An upsert allows the DML operation to choose between an "insert" and an "update" operation. If a record already exists, it updates it. If the record is new, or doesn't already exist, it inserts it.

In your situation, if the record already exists and there are no changes to it, then you don't need to update it. You only need to insert new records. I don't see a check in your trigger above to see if the contact is already a member of the campaign. It appears to me that once you've assembled the external Id's, you need to query to see if any of them exist and remove them from your list.

First exception on row 0; first error: INVALID_FIELD_FOR_INSERT_UPDATE, Unable to create/update fields: ContactId. Please check the security settings of this
field and verify that it is read/write for your profile or permission set.: [ContactId]: Trigger.contactTrigger: line 67, column 1

The above tells you the problem begins someplace within line 67 of your code and relates to the ContactId.

Since I can't tell if you've posted all of your code or what the line numbers are, based on what you've posted, I'm guessing the problem lies with this line:

CampaignMember cm= New CampaignMember(CampaignId=UTMCampaign.Id, ContactId= con.Id, Status='Opt-In',External_ID__c = ExternalCampaingID); 

It appears to me the error is telling you that the contact Id already exists in your database for that External_ID__c.

2

Not 100% why (I think because CampaignMember can point to Contact and Lead) you can't include ContactId in any update call for the CampaignMember object, even if you aren't actually updating the field.

So I think for this scenario, assuming you do need to update the existing members, you would need to identify the contacts already on the campaign, and do that as a separate update call...you are already pulling the Campaigns, so maybe you can also pull all campaign members for the contacts in the trigger for the unique list of campaigns, then make a set of CampaignId + ContactId, and use a contains to check if they exist. If they do, add them to an update call, it not create them.

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.