Thursday, 14 March 2019

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

22:17 Posted by Elaiza Benitez , , , , , , 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 Elaiza Benitez , , , , , , 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!

Friday, 8 February 2019

Delaying a birthday email to the recipients’ local time

07:18 Posted by Elaiza Benitez , , , , , , No comments
It was bugging me in WTF episode 9 that my Flow would send the email immediately when the daily recurrence time had been met. As a follow on from WTF episode 9, I showed in WTF episode 10 how you can identify a Contact's time zone from their address information using a Flow Bing Maps action plus a Bing Maps time zone API.

In this WTF episode I go through how you can delay sending a birthday email based on the recipient's local time zone. It's pretty cool because you can't do this today with a Dynamics 365 workflow and I don't even think it is possible with the Microsoft Dynamics 365 for Marketing app either. Flow on the other hand can handle this requirement without development effort.

Quick recap

WTF episode 9 was about sending a birthday email to CDS Contacts when their day of birth and month of birth equals today's date. I mentioned that the Flow would not be suitable for Contacts in different time zones to my local time (Melbourne, Australia) because Contacts who are in a time zone behind my local time zone would get their email straight away.

For example Contacts based in Seattle would get their birthday email a day early because of how Melbourne is 19 hours ahead. Not ideal to be greeted a day early.

WTF episode 10 paved the way on how to identify a Contact's local time zone where the address information stored in CDS was referenced in the Bing Maps Get Location by Address action followed by identifying the time zone through a Bing Maps time zone API using the latitude and longitude returned from the Bing Maps action. I also showed you how to retrieve the windowsTimeZoneId property form the JSON response.
  • Watch WTF Episode 9 here.
  • Watch WTF Epsideo 10 here.

Combing the two WTF Flows

Actions in both of the Flows can be used in a single Flow to delay sending a birthday email with some minor adjustments and a couple of extra actions. But first, learnt something new from Natraj who is the guy behind Chrome Level Up for Dynamics 365/CRM, a MVP and also currently a colleague 😁

Formatting date and time

In the daily recurrence schedule action you specify the time zone and the start time. 


In the Flow docs.microsoft site it's slightly misinformed which is what I learnt from Natraj. If you enter 'Z' in the start time field of the action it will ignore the time zone entered and use UTC time zone. If you leave out the 'Z' it will respect the time zone entered. In my WTF episode 9 I was entering 'Z' and was entering a UTC date and time. A n00b mistake on my part because of how I was using 'Z' which was causing the action to use UTC, I thought you had to have the date and time in UTC because of what was outlined in the Flow doc. It's OK, everyone makes mistakes and you learn from it.

The documentation Natraj pointed me to was this one which correctly states it.


At the time of writing this blog post I also noticed someone else has commented the same thing in the Flow docs.microsoft page too - scroll down to the bottom. 

Adjustment to a few steps 

1.  In the Compose action that converts the current UTC date and time to my local time zone of Melbourne, I am now interested in 'yyyy.' Previously in WTF episode 9 I was only returning 'MM-dd.' 


2. In the Compose action that splits date values into an array, I can now reference 'yyyy.' The year value is set within another Compose action.


3. One thing I forgot mention in my vlog was that I am also using an Apply to Each action which will perform the actions based on the list of records returned. In this Apply to Each action is where I am checking the local time zone of the Contact through the HTTP action.

In the HTTP action that calls the Bing Maps Time Zone API Given location coordinates, find the time zone of the place, I am referencing the CDS Address 1 Latitude and Address 1 Longitude fields. Since this is a Flow that will be triggered on a daily basis, I have another Flow that triggers on create of a Contact where the Bing Maps Get Location by Address action is used to identify the Latitude and Longitude from their address information (show cased in WTF episode 9).

What I did to extend the Flow

Convert Time Zone action

I am using the Convert Time Zone action to set the desired time of when the birthday email should be sent. For example, I want the birthday email to send on the day of their birthday at 8am their local time. Remember, the date and time needs to be in the format of UTC date time format.

In order for the Base Time value to be set, this is where we have to reference outputs from actions in the Flow. The first one is to reference the compose action that contains the 'yyyy', followed by 'MM' and 'dd.' This makes up the date value. For the time value, simply enter the desired values for HH:MM such as 08:00 which represents 8am in the local time zone.

The reason why I adjusted the Flow by adding the Compose action that retrieves the 'yyyy' in the array is so that the Flow will continue to work whenever a new year starts 👍 No manual intervention required.
For the Format String value it has to be in the UTC Date time format.

For Source Time Zone value you will need to reference the local time zone of the contact which is achieved using the Compose action that has retrieved the windowsTimeZoneId value from the Bing Maps time zone API. 

For the Destination Time Zone you need to use UTC. Why? A couple of reasons
  • Flow uses UTC date and time for the Delay Until action which is what will be used to delay sending the email until it equals 8am in the recipient's local time zone
  • Using UTC as the time zone will account for Daylight Savings Time of the local time if it is in affect. If you chose a non-UTC time zone such as Melbourne, it will be behind by 1 hour which is what I found when playing with this.
Your Convert Time Zone action should look like this:


Delay Until action
As mentioned earlier this action will delay sending the birthday email to the local time of the recipient based on the set date and time value in the converted Base Time from the Convert Time Zone action. Then simply add the Send Email action as seen in WTF episode 9.

Yay

The Flow will trigger as per the Daily Recurrence action. Flow will return the list of CDS Contacts whose day of birth and month of birth equals today's date and will send in their local time using the converted base time from the Convert Time. Pretty cool!


This screenshot shows what the UTC time is that represents Wellington Daylight Savings Time.

In Flow you can see that it had correctly converted the desired set time of 10.22pm (in Wellington (which was 8.22pm Melbourne time when I was vlogging). For a more realistic time if this was a live Flow, this would be set to 8am.

What you need to know

Apply to Each action UI might trip you up

I tripped. I fell. I swore. Haha yeah nah, I was fine but I was scratching my head because I was confused 🤔

I was confused from the UI of the Apply to Each action. When I was testing my WTF Flow I had a contact in Seattle, USA and a contact in Wellington, New Zealand. I set the Base Time in the next few minutes for the Wellington time zone so that I could check it does work. When the Flow ran the Apply to Each action appears grayed out.


I interpreted this as it's not working for either contact. I was watching the clock like a hawk and every time the delayed time was met for the Wellington time zone, I was expecting the record in the Apply to Each action to show green ticks. This puzzled me and I was also frustrated with it because I was thinking "Oh man I am so close in getting this working but it's showing as grayed out." 

Enter John Liu my Flow guardian angel. He explained to me that it would have processed the record in spite of the lack of visual cue. Until all records within the Apply to Each action have completed being processed you won't see the green ticks or red crosses.

When I experiment with Flow I normally create each action one by one and run the Flow to check each action will succeed so that I know what I need to fix if there's a problem before configuring the next action. My Send Email action was the last step, I didn't know if it was actually sending since I was not able to know for sure if the Delay Until action was working as Seattle is 19 hours behind my time. The Apply To Each was appearing grayed out so it confused me.

As soon as I added the Send Email action and ran the Flow again, John was right - the email does send because the record meets all the action criteria even though the Apply to Each is greyed out.

It will show the green ticks or red crosses when ALL records in the Apply to Each have completed being processed. Eventually when 10.21pm in Seattle was met 19 hours later, the Apply to Each action displays the comforting green ticks as the email sent and no other record needed to be processed.


Makes sense but at the same time the UI confused me. It would be good if the actions within the Apply to Each action has visualisation so that you know what has succeeded or failed instead of waiting till all actions of all the records in the liast have been completed. Please note this is when a Delay Until action is used within an Apply to Each action and I also suspect will also occur when a large volume of records are being processed.

Flow concurrency

Another thing you can do with Apply to Each is enable what you call Concurrency. It means that records within the Apply to Each action can be processed simultaneously rather than one at time. I enabled concurrency in my Apply to Each action. I'm going to direct you to John Liu's blog post for further information on the Apply to Each Concurrency setting. 

Summary

You can send a birthday email to a recipient and ensure they get it at a set time within their local time zone. The recipient won't get it at weird hours of the day or a day early. Using a combination of Flow connectors it's possible without code and even accounts for Daylight Savings Time of the recipients local time zone.

Till next time, toodles.

Thursday, 10 January 2019

How to identify a Contacts' time zone in Flow using CDS and Bing Maps

23:11 Posted by Elaiza Benitez , , , , , No comments
Have you ever needed to find the time zone of a Contact? This WTF episode is for you then my friend. Time zone can be easily identified through  the power of a Bing Maps API using a couple of connectors and an expression in Flow. No code!

Bing Maps Time Zone APIs

Before I share the steps with you, I want to talk briefly about the Bing Maps Time Zone APIs available as I'm using one of them in my Flow.

Last year the product team made Bing Maps Time Zone APIs available which you can read from this Bing Maps blog post.

The Bing Maps Time Zone API I am using is "Given location coordinates, find the time zone of the place."


I decided to use this one instead of "Given a place name, find the time zone of the place" because personally I find that using latitude and longitude coordinates would be more accurate rather than using a place, such as a city.

To use the Bing Maps Time Zone API (and Bing Maps Flow connector which I'll get to soon), you need a Bing Maps key. 

I signed up for a Bing Maps Basic Key to use a Bing Maps Flow connector and the Bings Maps Time Zone API. If you are going to be using this in your organisation or if you are a consultant and will include this in your solution design, you need to review the Bing Maps licensing options beforehand to understand if this is a valid solution design.

Steps

1. Trigger

For the trigger of my Flow, I'm using the When record is created CDS trigger. I want the Flow to trigger when a Contact is created.

2. Bing Maps action

The next step in my Flow is to use the Bing Maps action of "Get location by address." This will retrieve latitude and longitude coordinates from address data. We will use the address data from the Contact. The assumption here is that an address is entered against the Contact in the model-driven app in the Contact entity.


When the Bing Maps action successfully returns an output, it looks like the following where you will see the latitude and longitude coordinates.

3. HTTP action

To use the "Given location coordinates, find the time zone of the place" Bing Maps API we need to use the HTTP action and reference the URL provided in the Bing Maps blog post.

https://dev.virtualearth.net/REST/v1/TimeZone/latitude, longitude?key=<bingmaps-key>

Simply reference the latitude and longitude outputs from the previous step, followed by inserting your Bing Maps key.

Result

The Bing Maps API will then successfully retrieve the Time Zone of the Contact. Pretty cool right?

One step further - saving the time zone value

If you need to sore the time zone value of the Contact, you can do so by creating a custom single line of text (string) field in your Contact entity in CDS. The property that will be retrieved from the JSON response is the windowsTimeZoneId from the timeZone object. This is what will be stored in the custom field in CDS.

There's two options that will allow you to retrieve the windowsTimeZoneId property successfully.

Option 1 - without using an expression

You can use the Parse JSON action where you'll be required to copy and paste the JSON response  from the HTTP action to generate the schema.


The next step is to use a Compose action. When you select the Compose action and review the Dynamic Content values presented, you won't see the timeZone object as it is in the array of resources as seen in the JSON response.


If you select resources, you'll then see an apply to each magically appear. John Liu has a blog post that outlines what you need to be aware of when using the Parse JSON action and he briefly talks about this behaviour. When you add another Compose action, the windowsTimeZoneId property will now be visible.


It's not a bad option but it may be confusing.

Option 2 - with an expression

John Liu explained to me that if you don't want to use the Parse JSON action and simplify your Flow, you can use a Compose action to retrieve the windowsTimeZoneId property.


The expression to use is
body('1.2_HTTP').resourceSets[0].resources[0].timeZone.windowsTimeZoneId

My way of explaining this expression is to work backwards:
From the HTTP action, get the windowsTimeZoneId property from the timeZone object which is in the resource array whose parent object is resourceSets.

If you have a better way of explaining this to a non-technical soul, you can tweet to me 😁

The final step

For either Option, make sure you have an action that will update the custom field in the Contact entity.

Summary

By using a Bing Maps Flow action, a Bing Maps Time Zone API and a Compose Flow action you can retrieve the time zone of a Contact in CDS through the address information without code. So cool! Flow is the best.

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

#WTF #FlowFever #FlowAllDayEveryday

Help me get to 500 YouTube subscribers

As mentioned in my vlog, subscribe to my YouTube channel if you haven't already done so as Will from TDG will supporting my milestone achievement by doing a sheperd's pie vlog. This was mentioned in the Power Apps podcast episode where I was a guest and chatted to Shawn, Chris and Will. It would be great to see in Will in the kitchen 😄

Jokes aside, it would be great if I reach 500+ subscribers. I started in 2014 and I'm going to keep going till I can. Thank you!