Question

I am trying to make an activity that migrates an address from a lead to a contact. We do not use the default Address1 and Address2 in our CRM deployment (not my decision) so although the Qualification process does copy the address entered in the lead to the contact, it does so using the Address1 fields. I am using the code below, and everything seems to be working (no errors registering, no errors running the workflow that uses this activity). There's just one problem... nothing happens. Although there are no errors, no address gets created. I am running as the CRM admin so this shouldn't be a permissions thing, however even if it was shouldn't that generate a security exception? Any ideas why this is not working?

public class MigrateLeadAddressToContactActivity : CodeActivity
{
    [Input("Contact input")]
    [ReferenceTarget("contact")]
    public InArgument<EntityReference> InContact { get; set; }

    protected override void Execute(CodeActivityContext executionContext)
    {
        // Get the tracing service                         
        var tracingService = executionContext.GetExtension<ITracingService>();

        if (InContact == null)
        {
            const string errorMessage = "Contact was not set for Address Migration Activity";
            tracingService.Trace(errorMessage);
            throw new InvalidOperationException(errorMessage);
        }

        // Get the context service.                         
        var context = executionContext.GetExtension<IWorkflowContext>();
        var serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();

        // Use the context service to create an instance of CrmService.             
        var service = serviceFactory.CreateOrganizationService(context.UserId);

        //Retrieve the contact id
        var contactId = this.InContact.Get(executionContext).Id;

        // Get The Lead if it exists
        var query = new QueryByAttribute
                    {
                        ColumnSet = new ColumnSet(
                            new[]
                            {
                                "address1_line1",
                                "address1_line2",
                                "address1_line3",
                                "address1_city",
                                "address1_stateorprovince",
                                "address1_postalcode",
                                "address1_country",
                            }
                        ),
                        EntityName = "lead"
                    };

        // The query will retrieve all leads whose associated contact has the desired ContactId
        query.AddAttributeValue("customerid", contactId); 

        // Execute the retrieval.
        var results = service.RetrieveMultiple(query);

        var theLead = results.Entities.FirstOrDefault();
        if (null == theLead)
        {
            tracingService.Trace("Activity exiting... Contact not sourced from Lead.");
            return;
        }

        var newAddress = new Entity("customeraddress");
        newAddress.Attributes["name"] = "business";
        newAddress.Attributes["objecttypecode"] = "contact";
        newAddress.Attributes["addresstypecode"] = 200000;
        newAddress.Attributes["parentid"] = new CrmEntityReference("contact", contactId);
        newAddress.Attributes["line1"] = theLead.Attributes["address1_line1"];
        newAddress.Attributes["line2"] = theLead.Attributes["address1_line2"];
        newAddress.Attributes["line3"] = theLead.Attributes["address1_line3"];
        newAddress.Attributes["city"] = theLead.Attributes["address1_city"];
        newAddress.Attributes["stateorprovince"] = theLead.Attributes["address1_stateorprovince"];
        newAddress.Attributes["postalcode"] = theLead.Attributes["address1_postalcode"];
        newAddress.Attributes["country"] = theLead.Attributes["address1_country"];

        service.Create(newAddress);
        tracingService.Trace("Address Migrated from Contact to Lead.");
    }
Was it helpful?

Solution

Assuming there are indeed no exceptions thrown, the only other exit for that piece of code is here

if (null == theLead)
{
    tracingService.Trace("Activity exiting... Contact not sourced from Lead.");
    return;
}

So it would be fair to assume that theLead is null at this point and the workflow ends gracefully.

To test this, throw an exception instead. Assuming the exception is throwm, you can investigate why it is null. Presumably, the value you are filtering on is not valid or not what you expect - or the field is empty.

OTHER TIPS

Everything looks right to me. I'd try throwing an exception if no lead is found, just to verify that your query is working correctly. If you don't get an exception, verify that you don't have any plugins on the create of the customer Address that could be causing it not to be created.

Try tracing out the returned GUID from Service.Create and see what that contains.

Something like:

Guid addressId = service.Create(newAddress);
tracingService.Trace(string.format("Address ID Result: {0}", addressId));

You seem to be trying to create an Address record directly, bypassing the two already created for the Contact record by default (it always creates these two records, whether or not you use them and whether or not you want the "shadow" fields from them to be visible in the Contact form). You say nothing happens - is that verified by looking in the SQL tables or at the "More Addresses" link in the Contact record?

One thing you seem to have omitted from the create is specifying the address number (and ideally doing this by first finding the largest number on all addresses already associated to the Contact and incrementing by 1). This would need to be at least 3 in all cases.

If you look at the Address entity Associated View definition you will see that it explicitly filters to include addresses where address number > 2 in order to provide the illusion that 1 and 2 are "part of" the Contact and the "more" are separate records, whereas in fact all addresses including 1 and 2 are rows in the address entity table in their own right.

So I suspect that if the value is left as null, the address simply won't show up in the GUI as it does not meet the filter criteria. It may or may not also fail to create as it relies on that field quite heavily internally so I would treat it as "system required" and make sure to include it in the column collection.

Alternative approach:

If you are never using the fields for Add_1 or Add_2 on the form, you could still use the Address record with address number 2 to store your data from the Lead, making it an update of this existing (empty) record rather than a create.

Modify the Address Associated view to change the filter so it will include Add_2 in the view, by making it filter for address number > 1 instead of 2.

This will appear to users like there is one address there already and they can add more with normal functionality for address_3, 4 etc (which they might consider to be 2, 3 etc). Address 1 exists but is empty and can be ignored.

Ah! I see now that you are doing the query backwards! (or rather you don't need a query at all)

The customer ID field is only used when the user specifies that this is a Lead for an existing customer, the intention being to create an Opportunity linked to that customer.

If you Qualify a Lead and create a Contact (and/or Account and/or Opportunity) the field which connects them together is on the newly created record and called "originatingleadid" in all three cases for all three entities. Notice this supports qualifying, unqualifying and requalifying a lead several times over and every record created by doing so knows where it came from (think "who's your daddy?" and you won't forget this!).

The Lead would only be able to remember the last of these if the relationship were the other way round.

So to fix the query part of the code, replace the query and just get the originatingleadid explicitly from the lookup field on the Contact you are dealing with.

My other answer still stands as a better way to handle this process overall using update rather than create, but this answer should get you on the way to using the correct lead record in the first place.

If this code is written for CRM 2011 (and it looks like it is), then you need to set the addresstypecode value not with an integer, but with an OptionSetValue object:

newAddress.Attributes["addresstypecode"] = 200000; 

becomes

newAddress.Attributes["addresstypecode"] = new OptionSetValue(200000);

I would actually expect this to throw an exception. I'm not sure why it isn't.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top