Voodoo Chile...
Having implemented the workflow described by Jagan here to take an incoming e-mail and convert it automatically to a case, it became clear that some custom code would be need to solve the following problems:
After a quick scan through the SDK, I realised I could build a single custom workflow activity which would help me solve both issues. This activity would work as follows:
Following the SDK documentation for creating a custom workflow activity, my first job was to build the basic class, inherited from the workflow SequenceActivity base class, and override the execute method.
Imports System
Imports System.Workflow.ComponentModel
Imports System.Workflow.Activities
Imports System.Xml
Imports Microsoft.Crm.Sdk
Imports Microsoft.Crm.SdkTypeProxy
Imports Microsoft.Crm.Workflow
<CrmWorkflowActivity("Get Sender", "E-mail Utilities")> _
Public Class GetSender
Inherits SequenceActivity
Protected Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
Return ActivityExecutionStatus.Closed
End Function
End Class
Simply compiling this class and registering the workflow assembly with Microsoft CRM, enables this as a new activity in the CRM workflow editor.
Next, I needed to pass an email as an input parameters to the activity. Again, following the SDK documentation for creating workflow dependency properties, this was pretty straightforward.
Public Shared EmailProperty As DependencyProperty = DependencyProperty.Register("Email", GetType(Lookup), GetType(GetSender))
<CrmInput("E-mail"), CrmReferenceTarget("email")> _
Public Property Email() As Lookup
Get
Return CType(Me.GetValue(EmailProperty), Lookup)
End Get
Set(ByVal value As Lookup)
Me.SetValue(EmailProperty, value)
End Set
End Property
Compiling and registering the assembly exposes the input parameters to the CRM workflow editor.
In order to determine if the e-mail sender is already a recognised entity within CRM, I had to create five different output parameters, one for each of the possible entity types that could be a sender.
Public Shared ContactProperty As DependencyProperty = DependencyProperty.Register("Contact", GetType(Lookup), GetType(GetSender))
<CrmOutput("Contact"), CrmReferenceTarget("contact")> _
Public Property Contact() As Lookup
Return CType(Me.GetValue(ContactProperty), Lookup)
Me.SetValue(ContactProperty, value)
Public Shared AccountProperty As DependencyProperty = DependencyProperty.Register("Account", GetType(Lookup), GetType(GetSender))
<CrmOutput("Account"), CrmReferenceTarget("account")> _
Public Property Account() As Lookup
Return CType(Me.GetValue(AccountProperty), Lookup)
Me.SetValue(AccountProperty, value)
Public Shared LeadProperty As DependencyProperty = DependencyProperty.Register("Lead", GetType(Lookup), GetType(GetSender))
<CrmOutput("Lead"), CrmReferenceTarget("lead")> _
Public Property Lead() As Lookup
Return CType(Me.GetValue(LeadProperty), Lookup)
Me.SetValue(LeadProperty, value)
Public Shared UserProperty As DependencyProperty = DependencyProperty.Register("User", GetType(Lookup), GetType(GetSender))
<CrmOutput("User"), CrmReferenceTarget("systemuser")> _
Public Property User() As Lookup
Return CType(Me.GetValue(UserProperty), Lookup)
Me.SetValue(UserProperty, value)
Public Shared QueueProperty As DependencyProperty = DependencyProperty.Register("Queue", GetType(Lookup), GetType(GetSender))
<CrmOutput("Queue"), CrmReferenceTarget("queue")> _
Public Property Queue() As Lookup
Return CType(Me.GetValue(QueueProperty), Lookup)
Me.SetValue(QueueProperty, value)
Again, compiling and registering the assembly exposes these output parameter to the CRM workflow editor, and in this case they are visible in the create case form.
Finally, I had to write the code to call the CRM web service, retrieve the "from" attribute of the "e-mail" entity we are interested in and obtain the lookup type of the sender (if one exists)
' Set default NULL values for output properties
Me.Contact.IsNull = True
Me.Contact.IsNullSpecified = True
Me.Account.IsNull = True
Me.Account.IsNullSpecified = True
Me.Lead.IsNull = True
Me.Lead.IsNullSpecified = True
Me.User.IsNull = True
Me.User.IsNullSpecified = True
Me.Queue.IsNull = True
Me.Queue.IsNullSpecified = True
' Get the context service
Dim contextService As IContextService = CType(executionContext.GetService(GetType(IContextService)), IContextService)
' Get the context
Dim context As IWorkflowContext = contextService.Context
' Create an instance of CrmService
Dim service As ICrmService = context.CreateCrmService
' Make sure that the input lookup field has been specified
If Not (Email Is Nothing) Then
' Make sure that the input lookup field is of type "email"
If Email.type = "email" Then
' Initalise the TargetRetrieveEmail class
Dim target As New Microsoft.Crm.SdkTypeProxy.TargetRetrieveEmail
' Set the EntityId property equal to the E-mail Id
target.EntityId = Email.Value
' Only retrieve the "from" attribute of the "email" entity
Dim columnSet As New Microsoft.Crm.Sdk.Query.ColumnSet
columnSet.AddColumns(New String() {"from"})
' Initialise the RetrieveRequest class
Dim request As New Microsoft.Crm.SdkTypeProxy.RetrieveRequest
request.Target = target
request.ColumnSet = columnSet
' Call the CRM web service to retrieve the email entity
Dim response As RetrieveResponse = service.Execute(request)
Dim emailEntity As Microsoft.Crm.SdkTypeProxy.email = CType(response.BusinessEntity, Microsoft.Crm.SdkTypeProxy.email)
' email.from is a array of type "microsoft.crm.sdktypeproxy.activityparty"
Dim fromAttribute() As Microsoft.Crm.SdkTypeProxy.activityparty = emailEntity.from
' Determine the entity type of the first activityparty
' Return the correct entity type
If Not (fromAttribute Is Nothing) Then
If Not (fromAttribute(0) Is Nothing) Then
If Not (fromAttribute(0).partyid Is Nothing) Then
Select Case fromAttribute(0).partyid.type
Case "contact"
Me.Contact = fromAttribute(0).partyid
Case "account"
Me.Account = fromAttribute(0).partyid
Case "lead"
Me.Lead = fromAttribute(0).partyid
Case "systemuser"
Me.User = fromAttribute(0).partyid
Case "queue"
Me.Queue = fromAttribute(0).partyid
End Select
End If
' Set up return parameter
Now, I'm not going to delve into the code itself, but I stripped out any error handling, logging etc to leave just the key functionality, so hopefully it is pretty self explanatory.
The final piece of the puzzle is to build the workflow itself. In this example I have constructed a very simple example which works as follows:
This workflow can be seen in the series of screenshots below.
Here we are testing each of the output parameters of the custom workflow activity for Null data.
Here we are mapping the outputs of the custom workflow activity to the "Customer" field of a new case.
As you can see, just a little bit of code solves this issue. In addition, we can now add a couple of additional workflow steps to create an "Unknown Contact" record if the sender isn't recognised.
In order to make it easier for you to implement your own custom workflow activity, you can download the source code here. In addition, if you wish to use the functionality as-is, I've also included the test workflow which you can import as a customisation, as well as the compiled assembly file - all you have to do is register it with your own CRM application.
This posting is provided "AS IS" with no warranties, and confers no rights.
Laughing Boy
Great! Thanks, I was trying to create (!) something similar...
Now, I have a similar(?) case senario. I have an xml attachement with some various data like customer name or serial number etc; do you think is possible to upload these data to specific table (like case for expample)?
Thanks in advance,
Manos
Just tried this... doesn't work. Pity.
Hi Simon,
Is your solution available for CRM 2011?
I am having a situation which is very similar to your case.
thanks
Achim