Sunday 11 December 2016

How to get case resolution information on case record when the case resolved in MS CRM

How to capture case resolution information on case record when the case resolved in MS CRM


Few days ago, a requirement came to me in which we have to store the resolution data filled in resolution dialog  while resolving the case.

When user click resolve button on a case, a dialog box appears as shown below in which user fills the resolution details:




This information is not stored over case entity but is stored in incident resolution entity which is a backend entity in CRM and is not available to directly interact through CRM screen.

Each time a case is resolved, a record in incient resolution entity is created having refrene to the case resolved.
(It is possible to have more than 1 reord for same case is incident resolution if case is resolved and reopened multiple time.)

At first i thought of writing some server side code on resolve of case, but the problem was that after case us resolved the case record get deactivated. That means we can't update the  record by any means, be it CRM screen or service udpate request.

So writing a server-side logic on resolution of case was out of scope.

After some digging, i found out that if we run a server-side logic on creation of record of incident resolution entity, the solution would work and it will update the case before it get freezed.

I write a plugin to get the details filled by user in resolution dialog box from the incident resolution record created.

I then updated this information on 3 custom fields on case form. The output of the solution on case form is as shown below image:


Below is the code snippet of the logic::

Plugin Step Details:
Message                :    Create
Primary Entity      :    IncidentResolution
Registered on        :    Post event

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class TrackTimeonCase : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            #region Setup
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationService service = ((IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory))).CreateOrganizationService(new Guid?(context.UserId));
            ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            #endregion
            tracing.Trace("0.0");
            Entity targetCase = new Entity("incident");
            if ((context.InputParameters.Contains("Target")) && (context.InputParameters["Target"] is Entity) & context.Depth < 3)
            {
                    Entity entity = (Entity)context.InputParameters["Target"];
                    if (entity.LogicalName != "incidentresolution")
                        return;
                    try
                    {
                        if (entity.Contains("incidentid"))
                        {
                                targetCase.Id = ((EntityReference)entity["incidentid"]).Id;
                                Entity ResolveCase = service.Retrieve("incident", targetCase.Id, new ColumnSet("new_billablemintotal"));
                                int timespent = entity.Contains("timespent") ? entity.GetAttributeValue<int>("timespent") : 0;
                                targetCase["new_resolutiondescription"] = entity.Contains("description") ? (entity["description"] != null ? entity["description"].ToString() : string.Empty) : string.Empty;
                                targetCase["new_resolutionsubject"] = entity.Contains("subject") ? entity["subject"].ToString() : string.Empty;
                                if (ResolveCase.Contains("new_billablemintotal"))
                                {
                                  timespent = timespent + ResolveCase.GetAttributeValue<int>("new_billablemintotal");
                                }

                                    targetCase["new_billablemintotal"] = timespent;
                                    targetCase["new_billablehrs"] = ((decimal)timespent / 60);
                                    service.Update(targetCase);
                           
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidPluginExecutionException(ex.Message);
                    }
                }

            }
        }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Hope this was helpful...!!!

Thursday 1 December 2016

How to send email notification to all members of a team in Dynamics CRM

How to send email notification to all members of a team in Dynamics CRM


Recently this requirement came where we have to notify all the team members of a team, which [team] was the owner of a queue.

I checked and found that there isn't any straight forward OOB feature provided by Microsoft to do the same.

So i decided to implement this using some code and action process.

Here is what was the scenario:-

There is a team named "Sales Team" and a queue named  "Sales Team Queue".

"Sales Tea Queue" is the default queue of  "Sales Team" and  "Sales Team" was the owner of the "Sales Team Queue".


Sales team queue as the default queue of sales team:

Sales team as the owner of sales team queue:


So what does it means?

It means that the emails which are sent on the email address of the Sales Team Queue, Sales Team  will be the owner of all those emails.


So how does it matter?

It does and it does a lot.

What it does is that it give us the capability of managing Just Sales Team members and by adding and removing the members in Sales Team you can change who got the notification email and who not.


So how does we design it?

Now we also want to give the administrator freedom of changing the from email address and the notification text  without changing any code.

What we did to achieve the above requirement is:

1. We write a plugin on create of email and check whether the owner is some team.

2. If it is a team then fetch all the Members of the team.

3. We create an action process in Dynamics crm as shown below:



Create an email(don't send) and return the reference of created email in output parameter:



Below is how we have created the email to send:

Here is how we returned the reference of created email to the output parameters:



Now the steps is : Register the plugin on create event of email in asynchronous mode.

Why in asynchronous mode?

Just to be on safe side. I have observed that if we write some synchronous logic on create even of email and in any case the logic/code breaks in any condition, it might affect the creation of email.i.e. may be the incoming email might stop.

So it's better to make this step asynchronous.

Below is the code snippet of the plugin:

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void Execute(IServiceProvider serviceProvider)
        {
            List<Entity> _toList = new List<Entity>();
        #region Setup
        IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationService service = ((IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory))).CreateOrganizationService(new Guid?(context.UserId));
            ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            #endregion
            tracing.Trace("0.0");
            if ((context.InputParameters.Contains("Target")) && (context.InputParameters["Target"] is Entity) & context.Depth < 3 )
            {
                #region codearea
                try
                {
                    NotificationtoUsers obj = new NotificationtoUsers(); //NotificationtoUsers  is my Class name
                    tracing.Trace("0.1");
                    Entity Activity = service.Retrieve(context.PrimaryEntityName, context.PrimaryEntityId, new ColumnSet(true));
                    tracing.Trace("1");
                    EntityReference Owner = Activity.GetAttributeValue<EntityReference>("ownerid");
                    if (Owner.LogicalName == "team")
                    {
                        EntityCollection toPartyColl = new EntityCollection();
                        EntityCollection TeamMembers = obj.GetAllTeamUsers(service, Owner.Id);
                        Console.WriteLine("TeamMembers Count" + TeamMembers.Entities.Count);
                        foreach (Entity User in TeamMembers.Entities)
                        {

                            Entity entityUser = new Entity("activityparty");
                            entityUser.Attributes["partyid"] = (object)new EntityReference("systemuser", (Guid)User.GetAttributeValue<Guid>("systemuserid"));
                            if (!Enumerable.Any<Entity>((IEnumerable<Entity>)_toList, (Func<Entity, bool>)(t => ((EntityReference)t.GetAttributeValue<EntityReference>("partyid")).Id == (Guid)User.GetAttributeValue<Guid>("systemuserid"))))
                            {
                                _toList.Add(entityUser);

                            }
                        }


                        StringBuilder std = new StringBuilder();
                        std.Append("https://statictrial6.crm.dynamics.com"); //Write your own crm url here
                        std.Append("/main.aspx?etn=");
                        std.Append(Activity.LogicalName);
                        std.Append("&pagetype=entityrecord&id=%7B");
                        std.Append(Activity.Id.ToString());
                        std.Append("%7D");
                        String ClickHere = "Click Here to Open the Email";
                        String url = std.ToString();
                        String EmailLink = " <a href= '" + url + "'>'" + ClickHere + "'</a>";

//Make a request to get the action
                        OrganizationRequest orgRequest = new OrganizationRequest("new_emailtousers");
                        //Passing the dynamic link of email crated as a parameter to action
orgRequest["link"] = EmailLink;
                        OrganizationResponse orgResponse = (OrganizationResponse)service.Execute(orgRequest);
                        if (orgResponse.Results != null)
                        {
                            //Get the email reference from action which is returned from action
                            EntityReference emailRef = (EntityReference)orgResponse.Results["email"];
                            if (emailRef.Id != Guid.Empty)
                            {
                                Entity email = service.Retrieve(emailRef.LogicalName, emailRef.Id, new ColumnSet("to", "from"));
                                if (email.Id != Guid.Empty)
                                {
                                    obj.SendEmailAction(email, service, _toList);
                                }
                            }
                        }


                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("Error occured" + ex.Message);
                }
                #endregion
            }
        }

        private void SendEmailAction(Entity email, IOrganizationService service, List<Entity> toList)
        {

            try
            {
                email["to"] = toList.ToArray();
                service.Update(email);
                SendEmailRequest sendEmailreq = new SendEmailRequest
                {
                    EmailId = email.Id,
                    TrackingToken = "",
                    IssueSend = true
                };
                SendEmailResponse sendEmailresp = (SendEmailResponse)service.Execute(sendEmailreq);
            }
            catch (Exception ex)
            {
                Console.WriteLine("" + ex.Message);
            }
       }
        private  EntityCollection GetAllTeamUsers(IOrganizationService _CrmService, Guid TeamID)
        {
            //Create query expression
            QueryExpression _Query = new QueryExpression();
            _Query.EntityName = "systemuser";
            _Query.ColumnSet = new ColumnSet(true);
            //_Query.ColumnSet.AddColumn("systemuserid");
            _Query.LinkEntities.Add(new LinkEntity
            {
                LinkFromEntityName = "systemuser",
                LinkToEntityName = "teammembership",
                LinkFromAttributeName = "systemuserid",
                LinkToAttributeName = "systemuserid",
                LinkCriteria =
                        new FilterExpression
                        {
                            Conditions ={
                            new ConditionExpression("teamid",ConditionOperator.Equal,TeamID)
                                        }
                        }
            });
            return _CrmService.RetrieveMultiple(_Query);
        }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Below is the email all Team Member received:



Hope this was helpful.

You can utilize the above implementation as per your requirement.

Happy Learning..!!!