The other day I was in need of cloning Activities from a Lead to an existing Contact. As it seems, this is not standard behavior of CRM when qualifying to an Existing Contact only and NOT to an Opportunity. Of course I immediately thought of the cloning extension method that Wouter posted on our blog a while ago. I’m not going into details about it, instead you can find it here.

Easy, right?

Even though I thought it was going to be a piece of cake, nothing could be further from the truth. When the cloning method was implemented and the plugin and its steps were in place, I immediately gave it a try and got following error: ‘Cannot insert duplicate key’. While we erase the record ID in our cloning method, this was a bit strange, of course.

After digging a little deeper and searching the almighty web, I found that the actual cause of the error is in the PartyList fields that all activities have. More specifically the fields “to”, “from”, “cc”, “bcc”, “requiredattendees”, “optionalattendees”, “organizer” and maybe others. When we clone our activities, we are also cloning the references to the parties in these fields. These references have a GUID assigned to them. When trying to insert them again for our cloned activity, we find that the GUID for the references already exists.

How do we fix this?

Well, to pull this off, we will have to take the GUID’s of the PartyList references of the activity we want to clone and regenerate them to be used for the cloned activity. This way we will be able to insert the same references again for our cloned activity. Now with the newly generated GUID’s.

Right before we start cloning our activity with the extension method, we just call following method with the activity as a parameter.

 private void RegenerateActivityPartiesIdsForActivity(Entity activity)
 {
     string[] searchFields = new string[] { "to", "from", "cc", "bcc", "requiredattendees", "optionalattendees", "organizer" };
 
     foreach (var field in searchFields)
     {
         if (activity.Attributes.Contains(field))
         {
             foreach (Entity entity in ((EntityCollection)(activity.Attributes[field])).Entities)
             {
                 entity.Attributes.Remove("activityid");
                 entity.Attributes.Remove("activitypartyid");
                 entity.Id = Guid.NewGuid();
             }
         }
     }
 }

Here is how I’m using it:

 EntityReference targetRef = (EntityReference)context.InputParameters["LeadId"];
 Entity target = service.Retrieve("lead", targetRef.Id, new ColumnSet("parentcontactid"));
 EntityReference contactRef = target.GetAttributeValue<EntityReference>("parentcontactid");

 if (contactRef != null)
 { 
     List<Entity> activities = new List<Entity>();
     List<Entity> clones = new List<Entity>();
     List<string> activityTypes = new List<string>()
     {
         { "appointment" },
         { "email" },
         { "phonecall" },
         { "task" }
     }; 
 
     activityTypes.ForEach(type => activities.AddRange(GetLeadActivities(service, targetRef.Id, type).Entities)); 
 
     activities.ForEach(e =>
     {
         RegenerateActivityPartiesIdsForActivity(e);
         e["regardingobjectid"] = contactRef;
 
         clones.Add(e.Clone());
     });
}

 

This way we are finally able to clone our activities altogether with the references in the Activity Parties fields.