Plugin on AddListMembers message. When does it fire?

In one of our recent implementations involving marketing list entity, we were analysing the AddListMembers message for it.

We registered a plugin on AddListMembers message and below were our findings.

  • Dynamics Marketing List – The plugin didn’t trigger.
  • Static Marketing List – The plugin didn’t trigger for “Add using Lookup“. It only triggered for “Add using Advanced Find” when “Add only the selected members to the marketing list” option was selected. It didn’t trigger for “Add all the members returned by the search to the marketing list

This slideshow requires JavaScript.

So basically, we need to be very careful while implementing a plugin on AddListMembers message.

Hope it helps..

Auto-Closure of Task considering Work Hours and Holidays in Dynamics 365

Auto-Closure of Task considering Work Hours and Holidays in Dynamics 365.

Recently we had a requirement to auto close the task after 2 days of due date passed. Here we had to consider work hours and holidays as well.

The good thing was that with SLA considering both work hours and holidays, we could implement this without writing a single piece of code.

Below are the steps to implement it –

Enable SLA for Task

Navigate to Settings à Service Management à Click on Service Level Agreement and create a new SLA against Task.

Create a new SLA record with following details.

Here we need to specify Business Hours in our SLA. For this, we will be creating a Customer Service Schedule record which observes the holiday.

So let us first define a new Holiday Schedule.

Navigate to Settings à Service Management and click on Holiday Schedule.

This holiday schedule will be used while defining Customer Service Schedule.

Create a holiday schedule record and add holiday records to it.

Here we have added a sample holiday of 2 days.

Now let us define the Business Hours.

Navigate to Settings à Service Management à Customer Service Schedule

Create a new schedule

Set Workdays as Mon – Friday and work hours as 8 – 5 P.M. and select our Holiday Schedule record created earlier.

Let us update our SLA record with these new customer service schedule.

Let us try creating a new SLA Item record. Here we will find that there is no KPI’s defined i.e. SLA KPI drop down doesn’t have any value.

Let us define an SLA KPI here.

For defining KPI we need to create a new relationship between Task and SLA KPI Instance (n-1 i.e. Many to 1).

Open the Task form for customization and add this new lookup SLA KPI Instance on the form.

Next, add a Quick View Form of SLA KPI entity in it.

Click on Edit to update the Resolve By SLA quick view form to add a new field Failure Time in it.

Add the Quick View Form to the Task form.

Back in our SLA item,

  • Select the new KPI created.
  • Specify Applicable as when status reason not equal to either completed or cancelled.
  • Failure after 18 hours i.e. 2 Days (as we specified 9 hours working in Customer Service Schedule)
  • Warning as 9 hours.

G

  • Activate and Set as Default.

Creating a task with Due Date will start the Timer for Resolve In.

In the task record created we have set the Due Date to 22nd Nov 8 A.M. and we can see the resolve by date to be 27th.

In our SLA we had set the Failure After as 18 hours, and the holiday was set on 23rd and 24th. And also 25th and 26th being Sat-Sun the Failure Time comes as 27th Monday, which is exactly what we want.

Another interesting way of implementing the same without using SLA.

https://community.dynamics.com/crm/b/hayersdynamicscrm/archive/2014/06/20/dynamics-crm-how-to-add-working-days-by-calculating-business-closure-and-weekends

Hope it helps..

“the requested formId is not a valid form to display for this user” exception while opening a record inside Dynamics 365.

Recently while opening a marketing list record inside Dynamics 365, we got the below issue.

The issue was because we had multiple marketing list forms and the one the user had accessed last had all the security roles removed from it.

Enabling the security role that user had, for that particular form fixed the issue.

Hope it helps..

Implementing “Does not contain data” for N – N relationships through RetrieveMultiple plugin in Dynamics 365

Recently we had a requirement to create a marketing list with members (contact) which are not part of any other marketing list through Advanced Find.

In version 9.0 we have “Does not contain data” filter added in Advanced Find. However, it works only in case of 1 – N relationship.

https://nishantrana.me/2017/10/19/does-not-contain-data-filter-in-advanced-find-in-dynamics-365-july-2017-update/

Here in our case, it is an N-N relationship between Contact and Marketing List.

The solution we implemented was following –

We asked the user to select the following condition in their Advanced Find query.

Which will have the following fetch XML à

And in our Retrieve Multiple Plugin, we added a new filter condition and replaced link entity condition as shown below for implementing the outer join.

Below is the sample code for the plugin that achieves this. The plugin is Registered on Pre Retrieve Multiple of Contact.

The code –


public class PreRetrieveMultipleML : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));

// Obtain the organization service reference.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Query") && context.InputParameters["Query"] is QueryExpression && context.MessageName == "RetrieveMultiple")
{
var query = (QueryExpression)context.InputParameters["Query"];

tracingService.Trace("Inside Plugin");

if (query != null && query.EntityName == "contact")
{
foreach (var linkedEntity in query.LinkEntities.Where(
a =>
a.LinkFromAttributeName.Equals("contactid") &&
a.LinkToAttributeName.Equals("entityid") && a.LinkToEntityName.Equals("listmember") &&
a.LinkEntities.Count == 1 && a.LinkEntities[0].LinkCriteria != null))
{
if (linkedEntity.LinkEntities[0].LinkCriteria.Conditions.Count == 1
&& linkedEntity.LinkEntities[0].LinkCriteria.Conditions[0].AttributeName.Equals("listid")
&& linkedEntity.LinkEntities[0].LinkCriteria.Conditions[0].Operator == ConditionOperator.Null)
{
// remove the existing link entity
query.LinkEntities.Remove(linkedEntity);

// create a new link entity
LinkEntity doesNotContainLinkEntity = new LinkEntity()
{
EntityAlias = "ml",
LinkFromEntityName = "contact",
LinkToEntityName = "listmember",
LinkFromAttributeName = "contactid",
LinkToAttributeName = "entityid",
JoinOperator = JoinOperator.LeftOuter
};

// add the newly created link entity
query.LinkEntities.Add(doesNotContainLinkEntity);

// create a new condition expression
var doesNotContainCondition = new ConditionExpression();
doesNotContainCondition.AttributeName = "listid";
doesNotContainCondition.Operator = ConditionOperator.Null;
doesNotContainCondition.EntityName = "ml";

//create a new filter condition
var doesNotContainFilterExpression = new FilterExpression();
doesNotContainFilterExpression.AddCondition(doesNotContainCondition);

// add this new filter in query
query.Criteria.AddFilter(doesNotContainFilterExpression);

break;
}
}
}
}
}
}

We can update the plugin’s code to be used for any other N-N relationship similarly.

Hope it helps..

“Register a New WebHook” through Plugin Registration Tool in Dynamics 365

With July 2017 Update, now we have the option to register a new Webhook through Plugin Registration tool.

Download the latest Plugin Registration Tool from NuGet using the PowerShell script à

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/download-tools-nuget

Through registering a Webhook, we can send data (execution context) about any operation performed on Dynamics 365 to the external services or application. The execution context information is passed in JSON format here.

More details here

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/use-webhooks

Let us take a simple scenario to see it in action.

First will create an Azure Webhook which passes the Execution Context to the Azure Service Bus Queue. We will also create a sample Queue Listener application that parses and reads the information from the queue.

  • Create a new Function of type Webhook + API (CSharp).

  • Select Integrate in the newly created function and click on New Output button and add Azure Service Bus as an output.

  • Specify Message type as Service Bus Queue and other required values like Service Bus Connection, Queue Name and the parameter name and click on Save.
  • Update the code for our function to pass the execution context information to the queue
  • Now let us register our Webhook inside Dynamics 365. For this copy the function URL using Get function URL button.


  • Click on Copy.

  • Inside Plugin Registration tool, click on Register à Register New Web Hook.

  • Specify any Name for the Webhook. In the Endpoint URL, paste the URL copied without any query string part. For Authentication select WebhookKey and paste the “code” query string value to register the Webhook.

  • Register a new step of Lead Create to it.

  • After registering the step, to test our Webhook Function, let us create a new lead record inside Dynamics 365.

  • Back in our function, click on Monitor to check the log. There we can see the execution context information passed to our function.

  • Inside the Queue specified as Output of the function, we can see the new messages added.

  • Below is the sample code to read the message from the queue.

private static void QueueClientExample()
{
// create a new Shared Access Policy for the queue
// set the connection string of the Shared Access Policy created
var connectionString = "Endpoint=sb://[namespace].servicebus.windows.net/;SharedAccessKeyName=MyPolicy;SharedAccessKey=[KeyValue];EntityPath=mycrmqueue";

// create the Queue Client object
var client = QueueClient.CreateFromConnectionString(connectionString);

// get the message from the Queue Client
BrokeredMessage brokeredMessage = client.Receive();

var stream = brokeredMessage.GetBody<Stream>();
StreamReader streamReader = new StreamReader(stream);
string jsonData = streamReader.ReadToEnd();
jsonData = jsonData.Replace(@"\", string.Empty).Trim(new char[] { '\"' });
JObject context = JObject.Parse(jsonData);

Console.WriteLine("Primary Entity Name = " + context["PrimaryEntityName"] + " Message Name = " + context["MessageName"]);

}

  • The output

More on Azure Functions à

https://nishantrana.me/category/azure-functions/

Similar example using Azure Service Bus Integration à

https://nishantrana.me/2017/03/22/configure-dynamics-365-and-azure-service-bus-integration-through-queue-and-queueclient/

Hope it helps..