Sunday, 14 April 2019

Storing the date input value in a Dynamics 365 or CDS DateTime field

In Flow there are triggers and one of the CDS triggers available is the "When a record is selected." In this trigger you can configure an input that will allow the end user to enter information. The available inputs today are the following:


When you configure your trigger to use the date input, it might puzzle you at first when you look at the value in your model-driven app. For example in my Flow the end user is selecting a date and the value will be used as the Due Date of a Task that is created from the Flow.


When you review your Flow run history it may look like this:


The Flow will be successful however when you look into the run detail you'll notice that in the Task action the default time value associated is midnight.


When the end user views the Task in the model-driven app it will look like the following:


This may not make sense at all to the end user. The reason behind it is due to how Flow, Dynamics 365 and CDS use UTC for DateTime fields. In a model-driven app the end user is viewing it in their time zone. In my case I am in the Melbourne, Australia time zone and UTC midnight equals 11am in Melbourne.


So how do you display it accordingly? That's what this WTF episode covers.

Retrieving the time zone of the user who triggered the Flow

Since the trigger is "When a record is selected," it's not possible to use the "Get record" action to retrieve the user's time zone as you would've seen in my previous WTF episode. This action is more suited to when you want to retrieve information based on a user lookup field. However since the Flow will be triggered from an end user selecting a record, the method this time has to be different.

Enter two other MVP sources
  1. Tip #1205: Local time in Flow using Common Data Service
  2. CDS, Microsoft Flow and DateTime formats
Both outline methods that allow you to identify a user's time zone. For this WTF episode I am using the method described in the CRM Tip of the Day post. The Flow in the CRM Tip of the Day post is available for download as well.

For the remainder of this blog post I'll now break down the Flow.

1 - The trigger

The trigger is when a record is selected in CDS through the model-driven app and the date input is used as seen earlier.

2 - Retrieving the date input value

Use a compose action to grab the date input value of the trigger. This is so that it can be referenced in the last CDS action.

3 - Get the end user's Office 365 profile information

Use either the Office 365 Get My Profile or Get User Profile - either one will work, by referencing User ID from the trigger. 



The purpose of this action is to retrieve a value that will identify the Dynamics 365 or CDS user through their Office 365 ID in order to retrieve their time zone from their defined personal settings. This value is what will be used in the next action to associate it to the user's Azure Active Directory Object ID.

4 - List records to grab details of the user in Dynamics 365 or CDS

The entity to references is Users and the filter query is the following,


This will identify the user in Dynamics 365 or CDS. If you look at the output of this action and view it in Notepad++, it's this property that lives in the Users entity:

5 - Retrieve the user GUID

This step is not necessary but it keeps your Flow tidy. If you don't do this step, the Apply to Each action will appear. Use a compose action to retrieve the systemuserid value. The function you're going to use is first. In a list record action, multiple results are usually returned. When you know that you're only going to reference one record returned by the query, the first function is handy because it'll make the output appear as "one" rather than a list of records. This will prevent the Apply to Each from appearing. Your expression will look like this

first(body('1.4_List_executing_user')?['value']).systemuserid

6 - Retrieving the time zone information of the user

This get record action is the same action from my last WTF episode where you retrieve the user's personal settings to identify their time zone through the timezonecode property. Reference the output of the previous compose action that has the systemuserid.

7 - Retrieving the time zone name of the user's time zone

This list records action is the same action my last WTF episode where you reference the time zone definition entity to retrieve the time zone name. The time zone of the user is defined through the property timezonecode.

8 - Retrieve the time zone name

Similar to #5 where you use the first function in an expression to retrieve the time zone name from the standardname property. This will keep your Flow tidy.

first(body('1.7_Get_Time_Zone_Name_of_User')?['value']).standardname

9 - Set the Time of the DateTime value

When you select a date from within the trigger (#1) the default time will be midnight. To avoid confusing the end user when they are viewing the DateTime field that will reference the selected date, provide a set/fixed time. In my vlog I was using 9am which is 09:00:00.

Use a compose action and the function you are going to use in your expression is formatDateTime to reference the output of the Date Input followed by a string format of the date and time. In the string format is where you included your desired set/fixed time.

formatDateTime(outputs('1.2_Date_input_value'), 'yyyy-MM-ddT09:00:00')

10 - Convert time zone action

This is your best friend when it comes to storing date and time values in Dynamics 365 or CDS DateTime fields. To display the desired set/fixed time of 9am, you must convert 9am in the context of the end user's time zone into UTC.  Since Flow, Dynamics 365 and CDS use UTC, this is required whenever you want to display a DateTime value that makes sense to the end user based on their time zone.
  1. For the base time, the output of the compose action in #9 is used.
  2. For the the format time, use the usual ISO format. To do this select custom and enter the format.
  3. For the source time zone, use the output of the compose action in #8.
  4. For the destination time zone, use UTC.

You can also use a compose action and use the convertTimeZone function in an expression if you want to be ultra nerdy and not use the official convert time zone action. This works too #TriedAndTested

11 - Grabbing the converted time

In my action that is creating a new Task record, I am referencing the output of the convert time zone action as the Due Date. 

Ta da!

Now when you run the Flow and view the record in the model-driven app, the date and time now displays correctly according to the end user's defined time zone.

Summary

Whenever you use the Date Input in your "When a record is selected" action and need to display the value back to the end user, it has to be defined in the local time zone of the user and then converted into UTC. To reference the user's personal settings who triggered the Flow for their time zone information, you'll need to grab details of their Office 365 profile to identify their user record in Dynamics 365 and CDS.

Thursday, 14 March 2019

How to populate a date and time field based on a user's local time zone

22:17 Posted by Benitez Here , , , , , , No comments
There's been questions from the Dynamics community about not being able to successfully populate a date and time field in Dynamics 365 or CDS. A cryptic error is presented at times and often leads to frustration because when populating a date and time field in classic Dynamics 365 workflows it is a breeze. Hold on to that thought though.

In this WTF episode I show you how to populate a date and time field based on a user's local time zone. Yes, you read that right. This has been a problem for organisations that use Dynamics 365 or a Model-Driven app where end users are in multiple time zones. Now with the power of Flow you can do it without code and it's fast.

Use case

When a Case is created, create a new Task for the Owner with a Due Date by adding 2 days and set the date to 9am. Guilty - I forgot to explain this in my WTF vlog episode 😂 #oops

What you're familiar with today

There's two methods in classic Dynamics 365 workflows that are available when configuring the Due Date of an activity.

Using operators

You can add or subtract the following
  • Months
  • Days
  • Hours
  • Minutes
Nice but not good enough. Why?

You can't use a fixed time of 9am. It's either add or subtract the Hours and Minutes.

Using a default Date and Time

You can set a default date and time value.

Nice but still not good enough. Why?

You're restricted to the fixed date you've selected and cannot use the add or subtract function. However you can set a fixed time of 9am.


If you do choose to default the date and time, there's till an underlying problem that exists. It's to do with the time zone of a user.

The time set is based on the time zone of the user who created the classic Dynamics 365 workflow. For users in a different time zone of that particular user, they will see the time of the Due Date field in their local time.

As seen in my vlog - a user in the Melbourne, Australia time zone will see the Tasks with a Due Date of 9am.


A user in the Wellington, New Zealand time zone will have their Task created with a time of 11am for the Due Date. This is because Wellington is 2 hours ahead of Melbourne, Australia time zone.


This has always been a constraint when working with classic Dynamics 365 workflows.

Still think populating a date and time field in classic Dynamics 365 workflows is a breeze? 
No soup for you!

Show me the power of Flow

The solution I've come up with applies the convert time zone action which was covered previously in WTF episode 11 where I showed you how to delay sending a birthday email based on the time zone of recipient.
  • Watch WTF episode 11 here

1.2 Get the User of the Case

The trigger of the Flow will be when a Case is created. Through the Owner field, we can retrieve the user record using the CDS Get Record action.

1.3 Get the User's time zone

In Dynamics 365 and CDS a user's time zone is configured through Personalization Settings.



We want to retrieve this information about the user. To achieve this, use the CDS Get Record action and reference the User Settings entity. This entity is not visible from within Dynamics 365 or CDS and is traditionally accessed through the Dynamics 365 SDK.


When you run this Flow and observe the response returned in this action, you'll see a property (which is a field in the User Settings entity) called timezonecode.


This represents the time zone of the user. However we want to know the name of the time zone so that it can be used later in the Convert Time Zone action.

1.4 Get the time zone name of the time zone code

As per this docs.microsoft article, there are three entities that store time zone information in Dynamics 365 (and I also assume CDS). The entity that has the time zone code information is the Time Zone Definition entity. This entity is not visible from within Dynamics 365 or CDS and is traditionally accessed through the Dynamics 365 SDK.

Use the CDS Get Record action to retrieve the time zone definition entity.


When you run this Flow and observe the response returned for this action, you'll see an array of all the time zone definitions for Dynamics 365. For each of the time zones there will be a property called standardname that represents the time zone name.


How do you return only the time zone that is associated to the user's settings?

1.5 Use a query through the CDS List Records action

This is a tip I learnt from fellow MVP and colleague Natraj. The CDS List Records action allows you to query against an array. In previous episodes I showed how to query against records in Dynamics 365. This time round, I am going to use it against the returned response.

The filter query expression to use is to reference the timezonecode field in the Time Zone Definitions entity and the timezonecode field from the User Settings entity from the earlier action (1.3 Get User's Personal Settings). 


This will then only return the time zone definition details of the user's configured time zone.

1.6 Configuring the date and time for the value of the Due Date field

Use a Compose action to set the date and time. In my example, I am adding 2 days to the current date and setting the time to 9am. To achieve this I am using the addDays function
  • To reference the current date, I am using the utcnow function
  • To set the number of days, I am using the integer of 2
  • To set the time, I am entering a string value
The expression I have used is addDays(utcNow(), 2, 'yyyy-MM-ddT09:00:00')

1.7 Convert the local time into UTC

You would have seen or read me cover this in WTF episode 11. I'll outline what I am doing slightly different this time.
  • For the Base Time, I am referencing the output from the previous action where I've set the date and time.
  • For the Format String value it has to be in the UTC Date time format.
  • The source time zone will be the standardname property from 1.5 Get Time Zone Name of User action. When you select this, the Apply to Each will appear.
  • The destination time zone will be the UTC time zone. 

Quick recap on why UTC

  • Remember, Dynamics 365 underneath the hood stores date and time as UTC. Time zone entity definitions + user's time zone is what makes it appear as "local" in a Model-Driven app.
  • Stephen's blog post explains that Flow treats Date and Time in UTC.

1.8 Creating the Task

The last action is to create the Task and reference the output of the convert time zone action as the Due Date value.


The result

When you trigger the Flow as a user based in Wellington, New Zealand, the Task will have a time of 9am in their local time for the Due Date.


When you trigger the Flow as a user based in Melbourne, Australia, the Task will have a time of 9am in their local time for the Due Date.


If you compare the Tasks with each other the due date and time will be different.

As the Wellington based user, I will see the Melbourne user's Task have a due time of 11am.


As the Melbourne based user, I will see the Wellington user's Task have a due time of 7am.


To see it in action watch my WTF vlog.

Summary

Using the power of Flow you can now set the Due Date of an activity or any other date and time field based on a user's local time zone. You are not able to achieve this functionality today in a classic Dynamics 365 workflow.

This Flow is available for download in the TDG Power Platform Bank.

For another great time zone related post, check out Tip #1205.

Wednesday, 27 February 2019

Flow Condition Builder - How To's for Dynamics 365 and CDS

21:39 Posted by Benitez Here , , , , , , No comments
The Flow Condition Builder was released a few weeks ago and Stephen Siciliano announced it on the Flow Community site. I took it for a test drive to replicate what we are already familiar with in Dynamics 365 workflows. In this WTF episode I share the techniques in Flow because I want to help you with your learning to come up to speed with the new Flow Condition Builder.

Introducing the Flow Condition Builder

The following can be performed more easily now in Flow rather than using expressions as covered in my WTF Episode 2 blog post
  • AND
  • OR
  • group OR and AND statements

One thing I like that is not available today in Dynamics 365 workflows is the ability to move your criteria up and down, whether it's by a single field or a group.


The edit in advanced mode is no longer available however you can still use expressions within the Flow Condition Builder.

Referencing a lookup field

This is one of the most common conditional checks when configuring Dynamics 365 workflows. In the following screenshot I am checking that the Customer of a Case equals a particular Account record.

This was covered previously in my WTF Episode 2 blog post. To reference a lookup field in the Flow Condition Builder is straight forward. You select the lookup field, followed by the operator is equal to and paste in the GUID of the record.

Combining referencing a related entity field and contains data

Related entity field

If you want the criteria of your condition to use a field from a related entity and check that it contains data, the following is what we are familiar with today. In this screenshot I am checking that the selected Account in the Case has a Primary Contact. In other words the Primary Contact contains data.


If it does contain data, a Task will be created for the Owner of the Case to notify the Primary Contact that a Case has been created.

To do reference a related entity field in Flow you need to use the Get Record action which I covered in my WTF Episode 7 blog post. If you try search for the field in the Dynamic Content of the Flow Condition Builder you won't be successful because you have not provided the related entity for Flow to reference, it will only display fields from the entities currently referenced either in your trigger or preceding actions.


The Get Record action allows you to retrieve information from a N:1 related entity in the same manner that you're familiar with in Dynamics 365 workflows. In the following screenshot I am retrieving the Account record that is selected in the Customer field of the Case.


Now I can select the Primary Contact in the Dynamic Content of the Flow Condition Builder.

Contains Data

As mentioned earlier, the edit in advanced mode is no longer available in Flow which was what I covered in my WTF Episode 3 blog post. In the Flow Condition Builder you can now select operators instead of writing the expression which was the previous method. However you'll notice that there is no Contains Data or Does Not Contain Data which is what you're familiar with today in Dynamics 365 workflows.


To achieve Contains Data or Does Not Contain Data you need to use the function of null in an expression.


For Does Not Contain Data, the operator to select is is equal to


For Contains Data, the operation to select is not equal to


Ta da - now you have achieved referencing a related entity field and Contains Data.

Activity Count Including Process

If you're not familiar with Activity Count in Dynamics 365 workflows, I recommend reading fellow MVP Feridun's blog post. The one I am replicating is Activity Count Including Process because it doesn't make sense to me to only count activities that were not created by Dynamics 365 workflows.

In the example I demonstrated in my vlog, I'm checking the number of activities against a Case I've selected. 


Since activities is a 1:N related entity, there are multiple records to be checked. The CDS action to use is List Records which will allow you to query the activities against the case. 


I briefly cover List Records in my WTF Episode 9 blog post so check it out if you want to know more about the List Records action.

After you have configured your List Records action the next step is performing a count against the action. To do this use the Compose action and an expression. The function to use in the expression is Length and is described by another MVP Fautso in his blog post,

 This function returns the number of items in a string or an array

The List Records action will provide a collection of activities that are associated against the case. When using the Length function in an expression the output will be the number of records returned in the list.

In my example, the expression I used is length(body('1.2_List_activities')?['value'])


The next step is to use the Flow Condition Builder where you can specify your criteria. In my example I used the following

  • Check that the length output (number of activities) is greater than or equal to 1
To demonstrate the Yes and No paths, I used something simple where a Note was created for each path that indicated the number of activities against the case.

Summary

The Flow Condition Builder allows you to replicate most of the functionality you are familiar with today in Dynamics 365 workflows. There are actions and expressions that you'll need to use where relevant. I hope this helps you with your learning and understanding the Flow Condition Builder in the context of Dynamics 365 and CDS.

Post a tweet to let me know if this has helped you or if you've used any of the techniques mentioned in here.

If you think there can be improvements made to the Flow Condition Builder, please head over to the Flow Community Site to submit your idea.

Thanks and till next time, toodles!