CRM 2011 Custom Workflow Activity - Send Email to Managers

In CRM 2011 (not online), you can create custom workflow activity for things that are not achievable using the OOTB workflow.

In my scenario, I want to send Overdue Case Reminder to group of users (in this case - managers) when a case has reached the Follow Up By date.
If you look at the SystemUser, it has a manager field by default. However, I need to be able to send the email to multiple managers.

My idea is that we create a separate Security Role (e.g. Managers) and put all managers in that role. Then we create a custom workflow activity to get all the users in that role and construct an email to notify them when the case is overdue.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Workflow;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Crm.Sdk.Messages;
using Org.Entities.Crm;
 
namespace My.Crm.WorkflowActivity
{
    /// <summary>
    /// This custom workflow activity is intended to:
    ///     Get Users from specific Security Role (e.g. Managers) and send the Case Overdue Reminder email to those users.
    /// </summary>
    
    public class EmailCaseOverdueToSupervisors : CodeActivity
    {
        private Guid _emailId;
 
        protected override void Execute(CodeActivityContext executionContext)
        {
            try
            {
                //Create the context and tracing service
                IExecutionContext context = executionContext.GetExtension<iexecutioncontext>();
                IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<iorganizationservicefactory>();
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
                ITracingService tracer = executionContext.GetExtension<itracingservice>();
          
                OrgServiceContext serviceContext = new OrgServiceContext(service);
 
                Entity entity = service.Retrieve(context.PrimaryEntityName, context.PrimaryEntityId, new ColumnSet(true));
                Incident caseEntity = entity.ToEntity<incident>();
                 
                //get users from the security role input
                tracer.Trace("Get users from the security role");
                Guid secRoleId = SecurityRole.Get<entityreference>(executionContext).Id;
                DataCollection<entity> users = GetUsersFromSecurityRole(secRoleId, service, tracer);
 
                //construct emails for managers
 
                if (users != null)
                {
                    var activityParties = new ActivityParty[users.Count];
                    var activityParty = new List<activityparty>(); //used to add instances
                    for (int i = 0; i < users.Count; i++)
                    {
                        SystemUser user = users[i].ToEntity<SystemUser>();
                        tracer.Trace("User FullName: " + user.FullName);
 
                        //create an activity party and add it to the array if it's not the owner
                        if (caseEntity.OwnerId.Id != user.Id)
                        {
                            activityParty.Add(new ActivityParty
                                                {
                                                    PartyId = new EntityReference(user.LogicalName, user.Id)
                                                });
                        }
                    }
 
                    activityParty.CopyTo(activityParties);
                    SendOverdueCaseReminder(activityParties, caseEntity, serviceContext, service, executionContext);
                }
 
                tracer.Trace("Workflow finished");
 
            }
            catch (Exception ex)
            {
               Helpers.Throw(String.Format("An error occurred in the {0} plug-in.",
                       this.GetType().ToString()),
                     ex);
            }
 
            return;
        }
 
        private void SendOverdueCaseReminder(ActivityParty[] activityParties, Incident caseEntity, OrgServiceContext serviceContext, IOrganizationService service, CodeActivityContext context)
        {
            //get Admin account - LastName : Admin
            var adminUser = serviceContext.SystemUserSet.Where(u => u.LastName == "Administrator").SingleOrDefault();
            ActivityParty fromParty = new ActivityParty
            {
                PartyId = new EntityReference(adminUser.LogicalName, adminUser.Id)
            };
       
            //construct Email
            Email email = new Email
            {
                To = activityParties,
                From = new ActivityParty[] { fromParty },
                Subject = "Case Overdue Reminder for Supervisors",
                Description = "Case details here",
                DirectionCode = true
            };
 
            _emailId = service.Create(email);
 
            // Use the SendEmail message to send an e-mail message.
            SendEmailRequest sendEmailreq = new SendEmailRequest
            {
                EmailId = _emailId,
                TrackingToken = "",
                IssueSend = true
            };
 
            SendEmailResponse sendEmailresp = (SendEmailResponse)service.Execute(sendEmailreq);
 
        }
 
        private DataCollection<entity> GetUsersFromSecurityRole(Guid secRoleId, IOrganizationService service, ITracingService tracer)
        {
                QueryExpression query = new QueryExpression();
                query.EntityName = "systemuser";
                query.ColumnSet = new ColumnSet("systemuserid", "fullname", "internalemailaddress");
                query.Criteria = new FilterExpression();
                query.Criteria.AddCondition(new ConditionExpression("isdisabled", ConditionOperator.Equal, false));
 
                Relationship relationship = new Relationship();
                relationship.SchemaName = "systemuserroles_association";
                RelationshipQueryCollection relatedEntity = new RelationshipQueryCollection();
                relatedEntity.Add(relationship, query);
 
                RetrieveRequest request = new RetrieveRequest();
                request.RelatedEntitiesQuery = relatedEntity;
                request.ColumnSet = new ColumnSet("roleid");
                request.Target = new EntityReference {
                    Id = secRoleId,
                    LogicalName = "role"
                };
 
                RetrieveResponse response = (RetrieveResponse)service.Execute(request);
 
                if (((DataCollection<Relationship,EntityCollection>)
                    (((RelatedEntityCollection)(response.Entity.RelatedEntities)))).Contains(new Relationship("systemuserroles_association"))
                    && ((DataCollection<Relationship,EntityCollection>)
                    (((RelatedEntityCollection)(response.Entity.RelatedEntities))))[new Relationship("systemuserroles_association")].Entities.Count > 0)
                {
                    return response.Entity.RelatedEntities[new Relationship("systemuserroles_association")].Entities;
                }
                else
                {
                    return null;
                }
        }
 
        #region Input Parameter
 
        [RequiredArgument]
        [Input("Case Link")]
        public InArgument<string> CaseLink { get; set; }
 
        [RequiredArgument]
        [Input("EntityReference input")]
        [ReferenceTarget("role")]
        public InArgument<entityreference> SecurityRole { get; set; }
 
        #endregion
 
        #region Output Parameters
        #endregion
    }
}


Note: for the CaseLink input, I'm using the CRM 2011 Workflow Utilities to generate the Case Link and put it into the Email Description field.

Hope this helps,
Andreas

Comments

  1. Hi Andreas,

    I'm trying to use your example but I cannot find the reference for ActivityParty. Which assembly I need to add to my solution?

    ReplyDelete
    Replies
    1. Hi Cleiton,

      ActivityParty is just another strongly typed entity like Account, PhoneCall etc.
      If you generate your entities class using the crmsvcutil tool, that class will have ActivityParty entity in it :)

      Regards,
      Andreas

      Delete
  2. Cool stuff. Just out of curiosity, any reason why you used security roles and not teams?

    ReplyDelete
    Replies
    1. Hi.

      Thanks. Good question. You can surely go with the Team. I can't really remember why role was chosen instead of team, but I can see nothing wrong either way. In fact I reckon if you can use Team it would be cleaner.

      Regards,
      Andreas

      Delete
  3. Does this line of code worked for you [ var activityParty = new List(); //used to add instances]? Please respond

    ReplyDelete
  4. Sorry please ignore my previous comment, got it now.

    List = System.Collections.Generic.List<>

    ReplyDelete

Post a Comment

Popular posts from this blog

SharePoint 2013 anonymous access add attachments to list item

CRM Plugin - Parent and Child Pipeline