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.
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
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
Hi Andreas,
ReplyDeleteI'm trying to use your example but I cannot find the reference for ActivityParty. Which assembly I need to add to my solution?
Hi Cleiton,
DeleteActivityParty 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
Cool stuff. Just out of curiosity, any reason why you used security roles and not teams?
ReplyDeleteHi.
DeleteThanks. 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
Does this line of code worked for you [ var activityParty = new List(); //used to add instances]? Please respond
ReplyDeleteSorry please ignore my previous comment, got it now.
ReplyDeleteList = System.Collections.Generic.List<>