Monday 30 August 2021

How to configure SLAs

 

Hey, congrats on updating to Microsoft Dynamics CRM Online 8.1. Of the many new and exciting features, you now have the capability to use service level agreements (SLAs) for entities apart from Case. The additional entities with the SLA capabilities are:

  • All activity entities (email, appointment, etc.) except recurring appointment
  • Account
  • Contact
  • Order
  • Invoice
  • Quote
  • Opportunity
  • Lead
  • And yes, custom entities and custom activities as well ðŸ™‚

 

I am assuming you are already adept with configuring SLAs for Cases.

Fair assumption? Good!

Unlike cases, however, you will need to do some extra steps for configuring SLAs on the above entities. This is because you will not get the out of the box SLA KPIs for the above entities. You will have to create your own SLA KPIs, timers, and quick view forms.

Here are the steps that you need to follow for configuring SLAs on the above entities. Though you can get this information from the official documentation (Enable entities for service level agreements (SLAs), Define service level agreements (SLAs), and Add a timer to forms to track time against enhanced SLAs), there are a few places you might want to watch out for while configuring SLAs (that’s why this blog ðŸ™‚ ). Let’s take Account as an example and learn how to configure SLAs for it.

First, you would need to enable Accounts for SLAs. (It is not enabled out of box)

  1. Navigate to Settings > Customizations > Customize the System > Entities > Account
  2. In the Communication & Collaboration section, you will see a checkbox ‘Enable for SLA’. Check, Save and Publish.
  3. Note that this is already checked for Cases out of box.

A few words of caution! SLA needs a committed relationship. Once you have selected that checkbox and saved, (by the power vested in you by Dynamics CRM and the position of a configurator) SLA cannot be disabled for the entity.

 

Enable entity for SLA

 

The second step is to create SLA KPIs for this entity.

  1. In the same Customization window, expand the SLA KPI Instance entity.
  2. Click 1: N Relationships.
  3. Click the New 1-to-Many Relationship button. The new relationship form opens.
     
     
    New 1 to Many Relationship  
     
  4. Select Account <entity> in Related Entity dropdown.Here is a catch! As soon as you select Account in the Related Entity dropdown, the Name field gets auto populated to “new_ slakpiinstance_account”.You can use it as it is, but you will face issues if you want to export the SLAs created in this org to an org that also has SLAs enabled for Account. This is because while importing the SLAs, the system will attempt to create this relationship in the target org. Since there will be a relationship already existing in the target org, the import will fail. So it is strongly recommended to add a different name or change the name to a GUID (with underscores).
     
     
    Relationship definition_Name  
     
  5. Fill the Display Name to your taste.
     
     
    Relationship definition_Display name  
     
  6. Save and close.

For Cases, you’ve got 2 KPIs out of the box (First Response in and Resolve by). You will need to create similar additional KPIs if your business demands.

One of the best things about SLAs is the use of timers to indicate the time left for the SLA to be noncompliant. You can use timers for these entities too. You have it out of the box for Case but you will have to configure it for the new entities you enable for SLA.

 

Let’s see how to configure the timer.

  1. Go to SLA KPI Instance > Forms.
  2. Create a new Quick View Form.
  3. On the Insert tab, click Timer to add a timer on this form with the values of Data Source section illustrated below.
     
    Timer control 

 

A couple of things to remember.

  1. The quick view form is automatically named to “New Form”. It is strongly advised to rename it to something identifiable such as (Account SLA QV). Else, you will get confused which one to use if multiple entities are configured with SLA. [Names not so important? Go on reading]
  2. For each KPI, you will have to create a quick view form with timer. So if you have multiple entities to configure, this can get boring. Here is a quick tip for making your life easier. You will have to apply minimal effort if the SLA name and label is same across all your entities, else you will just have to edit these. Oh, I didn’t tell you the trick. The trick is to create one quick view form manually and then use Save As option to create copies. This way the Data Source section will not have to reconfigured (at least). Also the name of the form will be distinct and easily identifiable as you will be giving appropriate names while you use “Save As”.

 

SLA KPI Instance Form

 

The final configuration step

Now you need to put the controls on the entity form so that you can see the timer at the runtime. A few other relevant fields will be placed.

You could apply SLAs to Cases by either default SLA, entitlement or manually using SLA lookup. In all above entities, you cannot use the out of box entitlements. You will have to have a default SLA or use SLA lookup.

We will place the SLA lookup, SLA KPI Instance quick view form and SLA KPI Instance sub grid on the Account form.

  1. Click Accounts > Forms > Account on the same customization window.
     
     
    Navigate to Account form  
     
  2. Drag SLA field from the Field Explorer to place the SLA lookup field.
     
     
    Account form ediitor  
     
  3. Insert corresponding SLA KPI Instance Quick View form using the created SLA KPI Instance relationship above and correct quick view form containing the timer. If we do not rename the quick view forms while creating, we will have multiple forms in the Quick View Form lookup with the same name ‘New Form’ making it difficult to recognize the correct form. [You can thank me now ðŸ™‚ ]
     
     
    Quick view control properties  
     
  4. You may also want to insert a sub grid to view all active SLA KPI Instances when SLA is applied to this record.
     
     
    Set SLA KPI instance properties  
     
  5. Save and close.
  6. Publish all customizations (Or go and publish individual customizations).

 

Phew! Configuration complete… ðŸ™‚
 
Now let’s create SLAs.

  1. Go to Settings > Service Management > Service Level Agreements
  2. Type a name for the SLA and select the entity for which you’re creating the SLA from entity dropdown. Note that only those entities that are enabled for SLAs will be listed here.
     
     
    Create SLA  
     
  3. Click OK.
  4. Choose correct date time field in the Applicable from field.
  5. Click Save.
  6. Add SLA Items. Save the form.
  7. Click Activate.
  8. If you want to make this SLA default, you can click Set as Default on the command bar. You can have one default SLA per SLA-enabled entity.
  9. Next you can configure the SLA Pause states for each SLA-enabled entity.
    1. Click Service Configuration Settings (Settings > Service Management > Service Configuration Settings)
    2. Select your entity and the corresponding status reasons (belonging to active state) which will pause SLA time calculations.

 

Voila! Enjoy SLAs!

 
Account form

Sunday 22 August 2021

Associate/Disassociate plugin messages in CRM

 

Associate/Disassociate plugin messages in CRM


In CRM, the Associate or Disassociate event happens

  • If you have a N:N relationship between two entities and when you try to associate or disassociate records either from Associated view or Sub grid.
Entity Associate View

Entity Associate View

In Plugins, the Associate & Disassociate messages behave little different than other messages.

  • When you register a plugin on Associate message, you have to leave “Primary and Secondary” entities as ‘none’.
Associate Plugin Step

Associate Plugin Step

  • Since we don’t provide entity names, the registered Plug-in step triggers on all “Associate” operations, so we have to check few conditions to let the “Association” trigger happen only between intended entities.

You can use the below code template for Associate or Disassociate plugins

            EntityReference targetEntity = null;

            string relationshipName = string.Empty;

            EntityReferenceCollection relatedEntities = null;

            EntityReference relatedEntity = null;

            if (context.MessageName == “Associate”) {

                // Get the “Relationship” Key from context

                if (context.InputParameters.Contains(“Relationship”)) {

                    relationshipName = context.InputParameters[“Relationship”].ToString();

                }                                   

                // Check the “Relationship Name” with your intended one

                if (relationshipName != “{YOUR RELATION NAME}”) {

                    return;

                } 

                // Get Entity 1 reference from “Target” Key from context

                if (context.InputParameters.Contains(“Target”) && context.InputParameters[“Target”] is EntityReference)  {

                    targetEntity = (EntityReference)context.InputParameters[“Target”];

                }                      

                // Get Entity 2 reference from ” RelatedEntities” Key from context

                if (context.InputParameters.Contains(“RelatedEntities”) && context.InputParameters[“RelatedEntities”] is EntityReferenceCollection) {

                    relatedEntities = context.InputParameters[“RelatedEntities”] as EntityReferenceCollection;

                    relatedEntity = relatedEntities[0];

                }

            }




***********************************************************************************


In MS CRM Plugin Associate/Disassociate messages are used only for N-N relationship adding/removal of a associate record.

Note: With 1-N relationship you will have to use Update message with filter by relationship lookup.
For handling of deleting of records from sub-grid you will have to handle Delete message.
For example if you have grid of child records on parent record then if the grid is containing 1:n relationship child records then no need to write plugin on associate and disassociate messages for any logic. For that Update message will work.

In MS CRM Plugins, the Associate & Disassociate messages behave little bit different than other messages.

When you register a plugin on Associate message, you have to leave "Primary"and "Secondary" entities as 'none'.

Since we don’t provide entity names, the registered Plug-in step triggers on all "Associate"  & "Disassociate" operations, so we have to check few conditions to let the “Association”  & "Disassociate" trigger happen only between intended entities. You can use the below code template for Associate or Disassociate plugins.

using System;

using System.Collections;

using System.Text;

using Microsoft.Xrm.Sdk;

using System.Web;

using System.Xml;

using System.Diagnostics;

using Microsoft.Xrm.Sdk.Query;

using Microsoft.Xrm.Sdk.Client;

using Microsoft.Crm.Sdk.Messages;

namespace Termination

{

    public class Delegates:IPlugin

    {

        public void Execute(IServiceProvider serviceProvider)

        {

            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

 

            if (context.Depth > 1)

                return;

            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            ITracingService trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

 

            EntityReference targetEntity = null;

            string relationshipName = string.Empty;

            EntityReferenceCollection relatedEntities = null;

            EntityReference relatedEntity = null;

            Entity eventBooking = null;

            Guid eventEntID = Guid.Empty;

            EntityReferenceCollection eqEntites = null;

            Relationship relationshipEventContact = null;

           

            try

            {

              

                #region Associate & Disassociate

                if (context.MessageName.ToLower() == "associate" || context.MessageName.ToLower() == "disassociate")

                {

                    // Get the “Relationship” Key from context

                    if (context.InputParameters.Contains("Relationship"))

                    {

                       // Get the Relationship name for which this plugin fired

                        relationshipName = ((Relationship)context.InputParameters["Relationship"]).SchemaName;                      

                    }

 

                    // Check the "Relationship Name" with your intended one

                    if (relationshipName != "new_new_eventbooking_contact")

                    {                     

                    return;                 

                    }

 

                     // Get Entity EventBooking reference from Target Key from context

                    if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is EntityReference)  {

                        targetEntity = (EntityReference)context.InputParameters["Target"];

                        eventBooking = service.Retrieve("new_eventbooking", targetEntity.Id, new ColumnSet("new_eventname"));

                        if (eventBooking.Attributes.Contains("new_eventname"))

                        {                         

                            var eventEnt = (EntityReference)eventBooking["new_eventname"];

                             eventEntID = eventEnt.Id;

                        }

                       }

     

                     // Get Entity Contact reference from RelatedEntities Key from context

                    if (context.InputParameters.Contains("RelatedEntities") && context.InputParameters["RelatedEntities"] is EntityReferenceCollection)

                    {

                        relatedEntities = context.InputParameters["RelatedEntities"] as EntityReferenceCollection;

                        relatedEntity = relatedEntities[0];

                        eqEntites = new EntityReferenceCollection();

                        eqEntites.Add(new EntityReference("contact", relatedEntities[0].Id));                     

                    }

                

                    if (eqEntites != null && eventEntID != null)

                    {

                        relationshipEventContact = new Relationship("new_new_events_contacts");

                        if (context.MessageName.ToLower() == "associate")

                        {

                            service.Associate("new_event", eventEntID, relationshipEventContact, eqEntites);

                        }

                        if (context.MessageName.ToLower() == "disassociate")

                        {

                            service.Disassociate("new_event", eventEntID, relationshipEventContact, eqEntites);

                        }

                        trace.Trace("Event-Delegate Associate & Dissassociate Plugin copleted Successfully");

                    }

                    else

                    {

                        trace.Trace("Event-Delegate Associate & Dissassociate Plugin not completed Successfully");

                    }

                }

                #endregion

            }

            catch (Exception ex)

            {

                trace.Trace(string.Format("Event-Delegate Associate & Dissassociate Plugin error: {0}", new[] { ex.ToString() }));

            }

        }

    }

} 

Custom Workflow Activities: How to access Pre and Post entity images

 

Custom Workflow Activities: How to access Pre and Post entity images



Can we use per and post entity in custom workflow ? 

I only found this blog. they use this pre and post business entity key. not tested. 

Dynamics CRM plugins allow in some events to register images which may be pre event or post event images whether they represent a copy of the context entity, before or after the core operation is performed, respectively.

https://msdn.microsoft.com/en-gb/library/gg309673.aspx#bkmk_preandpost

Images are only available for certain events:

http://crmbook.powerobjects.com/extending-crm/plug-in-development-and-workflow-extensions/plug-ins/plug-in-images-pre-vs-post/

Images are registered through the Plugin Registration tool. When registering an image is specified:

  • If is a pre image or post image;
  • Which context entity attributes it contains;
  • The name to get it inside the plugin.

The following image illustrates the registration of a pre image and a post image, for a plugin with a step on the post-operation of the account update, where each image contains only the account Name attribute.

register_images_plugin

The next image shows how to get the registered images inside the plugin.

 

plugin_images

With custom workflows activities there is not the possibility to register images, although the CRM “does it for us”, adding images to the custom workflow activity execution context depending on its stage.

The bellow table outlines all the events and stages where workflows can be executed and which images are available for each stage.

EventStage

(when the custom workflow activity is executed)

Image available (key )
When record is createdPre-event (*)
Post-event (Sync and Async)PostBusinessEntity
When record:

  • status changes;
  • is assigned;
  • fields change;
Pre-eventPreBusinessEntity
Post-event (Sync and Async)PreBusinessEntity

PostBusinessEntity

When record is deletedPre-eventPreBusinessEntity
Post-event (Sync) (*)
Post-event (Async)PreBusinessEntity

(*) (Stage not available to register real-time workflows)

Regarding to images in custom workflow activities:

  • Pre image is accessed through the PreBusinessEntity key;
  • Post image is accessed through the PostBusinessEntity key;
  • Both images contain all the attributes of the context entity.

The next image shows how to get both pre and post images, considering that the custom workflow activity is part of a workflow, running on the update event, asynchronously or synchronously after the entity is updated.

workflow_pre_post_images

Git Basic working

  Develop = dev   r