Sunday, January 18, 2009

CRM 4.0 Custom Workflows

Custom Workflows in MS CRM 4.0 are very simple to deploy and are pretty simple to create especially if you are familiar with Plugin-in development. 

Before you can create a workflow you need to have the Workflow Extensions installed. This is more of a concern if you are running VS2005. If you are running VS 2008 SP1, you should have everything you need already.

If you have an assembly that you are already deploying for plug-ins you can even add a workflow to that assembly so that you have fewer deployment assemblies.

The following custom workflow was created for a customer who wanted to have a matching customer sales relationship created for an account whenever an opportunity role was assigned to a contact.

namespace OneCRMPro { // In the workflow editor // "OneCRMPro" is going to show up at the bottom of the Add Step pick list // "Create CustomerRelationship" will show up as item off of OneCRMPro. [PersistOnClose] [CrmWorkflowActivity("Create CustomerRelationship", "OneCRMPro")] public partial class CreateCustomerRelationship : SequenceActivity { // These DependencyProperty entries will show up in the Set Properties editor public static DependencyProperty RoleProperty = DependencyProperty.Register("Role", typeof(Lookup), typeof(CreateCustomerRelationship)); [CrmInput("Sales Role")] [CrmReferenceTarget("relationshiprole")] public Lookup Role { get { return (Lookup)GetValue(RoleProperty); } set { SetValue(RoleProperty, value); } } public static DependencyProperty CustomerProperty = DependencyProperty.Register("Customer", typeof(Lookup), typeof(CreateCustomerRelationship)); [CrmInput("Customer Contact")] [CrmReferenceTarget("contact")] public Lookup Customer { get { return (Lookup)GetValue(CustomerProperty); } set { SetValue(CustomerProperty, value); } } // End Set Properties /// <summary> /// Execute is called when the custom workflow is invoked /// </summary> /// <param name="executionContext"></param> /// <returns></returns> protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { //Get a workflow context var contextService = executionContext.GetService(typeof(IContextService)) as IContextService; var workflowContext = contextService.Context; CreateRole(workflowContext); return ActivityExecutionStatus.Closed; } /// <summary> /// Create a matching sales customer relationship role /// </summary> /// <param name="context"></param> private void CreateRole(IWorkflowContext context) { try { // Get Guids of Customer and RelationshipRole Guid contactId = Customer.Value; Guid salesRoleId = Role.Value; // get a local service ICrmService service = context.CreateCrmService(true); // Grab the contact involved var cols = new ColumnSet(new [] {"parentcustomerid"}); var thisContact = (contact)service.Retrieve(EntityName.contact.ToString(), contactId, cols); // Create a cooresponding customer relationship var role = new customerrelationship { customerid = thisContact.parentcustomerid, partnerid = CrmTypes.CreateCustomer(EntityName.contact.ToString(), contactId), partnerroleid = CrmTypes.CreateLookup(EntityName.relationshiprole.ToString(), salesRoleId) }; service.Create(role); } catch (SoapException) { // Handle error } catch (Exception) { // Handle error } } } }

Before you can use your workflow it needs to be strongly signed and registered.  Its assembly is registered in the same way as a plugin, but it doesn't require registering it at the entity or event level. If you don't have the plugin Registration Tool you can find it here. Plugin Deployment Tool

image

When you create a new Workflow you can now select this custom Workflow when you "Add Step" and pick the following at the bottom of the list.

OneCRMPro   >   Create CustomerRelationship

Now when you "Set Properties", you will see below how the DependencyProperty values will show up below.

The CrmInput keyword specifies the Property Name and the CrmReferenceTarget keyword specifies the expected attribute type to be mapped.

image

That's about it. Publish your workflow and try it out!

No comments: