Monday, 23 September 2019

Delaying emails to certain individuals based on their time zone

07:24 Posted by Benitez Here , , , , , , No comments
For the second time running the free Flow Online Conference was on September 10, 2019. I was lucky enough to be included among the awesome line up of speakers. My model-driven app was Star Wars themed too 😁


I presented a use case where emails needed to be delayed for certain individuals based on their time zone. To be more specific as an example, delay sending emails to only primary contacts based on their local time zone. This use case came about because previous clients who I have worked with had customers globally or nationally and wanted emails to be delayed to a group of customers.

But we can already delay emails in Outlook? 🤔

Outlook will send the emails based on the single time selected which is also in the context of the person who is sending those delayed emails. Same applies to any email marketing software out there, delaying emails will be based on the person who is sending those delayed emails and the date and time is based on their time zone - not the recipients' time zone.

Furthermore in Outlook the functionality to query contacts and have those resulting contacts in an email where they don't see each other's names is not there.

As demonstrated in previous WTF episodes, through the power of Flow the art of sending emails based on local time zones is 100% possible.

Watch my Flow Online Conference session in the below YouTube video. I present at 2:30.


The remainder of this blog post will outline what was presented.

User Story

The following is the user story from my presentation.

As a customer service technician,

I want to send delayed emails to primary contacts based on their time zone,

so that they can receive the email in their local time.

1.1 - The trigger

The trigger can be a recurrence or a Flow button. It is entirely up to how the Flow will be triggered.

1.2 - Retrieve Accounts

The email addresses of the individuals are stored in CDS in the contact entity. Since we are only sending the email to primary contacts of an organisation the CDS List Records action is used to retrieve the organisations from the account entity.


For the purpose of this Flow I did not enter a filter query. In the scenario where you need to email primary contacts of organisations that meet criteria such as primary contacts of VIP organisations, you can apply a filter query in the CDS List Records action against the account entity.

1.3 - Apply to Each

For every account returned the primary contact will need to be identified. The Apply to Each action is therefore used.

1.4 - Primary Contacts

The next step is to identify the primary contact of an organisation in CDS. The account entity has a single primary contact which is represented by a lookup field. The Apply to Each action is used alongside the CDS List Records action to retreive the contact entity with a filter query of

contactid eq @{items('1.3_Identify_Primary_Contact')?['_primarycontactid_value']}

This filter query will only return contacts based on the GUID in the primary contact field associated to the organisation. The dynamic content value is the Primary Contact field from the previous action that returns accounts.

1.5 - Apply to Each

For every identified primary contact from the previous action, a bunch of actions will be executed in order to send the delayed email to the primary contact. To ensure these actions are executed against each primary contact an Apply to Each action is used.

1.6 - Get location by address

As seen in a previous WTF episode there is a Bing Maps action "Get location by address" that allows the location latitude and longitude to be identified based on address information. The Address 1 fields in the contact record of the primary contact will be used. The assumption I made here is that the primary contact inherits the address of the account it is associated to.

1.7 - Identify local time zone of Contact

There are additional Bing Maps Time Zone APIs available which you can read from this Bing Maps blog post. These APIs are not available as Bing Maps action in Flow however the HTTP action can be used to call the APIs.

In order to find out the time in the primary contact's time zone, the "Given location coordinates, find the time zone of the place" Bing Maps API can be used through a HTTP action.

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

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

1.8 - Retrieve localTime property

To ensure the primary contact receives the email in their local time including the date such as September 21 at 4.30pm, we need to know what is their date and time in their local time zone.

To achieve this the localTime property in the response of the previous HTTP action can be used. Below is a screenshot of the response.


If you attempt to select the localTime as a dynamic content in a subsequent action you're out of luck as it won't be displayed. You will only see "Body" since the action used is HTTP as Flow only is aware of the Body output rather than the JSON properties.


There are two methods that can be applied to get around this.
  1. Use the Parse JSON action which will allow you to view and select the properties as dynamic content values.
  2. Use a Compose action which allows you to reference the property without using the Parse JSON action but simply an expression instead.
Both of these methods have been used and talked about in previous WTF episodes.

The Compose action with an expression is used in this Flow.

body('1.7_Local_time_of_Contact').resourceSets[0].resources[0].timeZone.convertedTime.localTime


Below is a screenshot to help you understand the expression used.

My way of explaining this expression is to work backwards:

From the HTTP action, get the localTime property from the convertedTime object, whose parent object is timeZone which is in the resources 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 😁

1.8.1 - Split the date string

The format of the localTime property will be in UTC format.


The date is only required, not the time to understand the date in the primary contacts' local time zone.

To retrieve only the date string we need to separate the date from the time in the string value of localTime. the split function can be used in an expression within a Compose action where T is used to separate the two.

split(outputs('1.8_Retrieve_LocalTime'), 'T')

1.8.2 - Retrieve date only string

The output of the previous action will return the following,


This results in an array where the date and time is separated. The first row is the date and the second row is the time.

Another compose action is used to retrieve the first array in order to reference only the date of 2019-09-21.

outputs('1.8.1_Split_the_date_string')?[0]

1.9 - Retrieve windowsTimeZoneID

The same technique is used from 1.8 where the windowsTimeZoneID is referenced in a Compose action.

body('1.7_Local_time_of_Contact').resourceSets[0].resources[0].timeZone.windowsTimeZoneId


The two actions 1.8 and 1.9 are within a parallel branch as they are not dependent on each other. Both actions can be performed simultaneously.

1.10 - Convert time zone

The next step is important as the Convert Time Zone action is used to ensure the email is delayed based on the local time of the primary contact.

Flow uses UTC which means the intended local time of the primary contact (for example 9am) needs to be converted to UTC. I've covered this in a previous WTF episode which can be referred to for more learning.


For example in order to send the email at 4.30pm in the local time zone of a primary contact who is based in Wellington, it has to be converted to UTC which is equal to 4.30am. Below is a screenshot from Savvy Time to illustrate the equivalent time in UTC of the Wellington time zone.

1.11 - Delay until

The Delay Until action is used where the date and time is the output from the previous 1.10 Convert time zone action. The email will be sent when the date and time is met which is equal to the local time of the primary contact in their time zone.

1.12 - Send an email

The final action used is the Outlook Send an email (V2) (Preview) action where the content of the message is entered and the recipient is the Primary Contact from the 1.4 Primary Contacts action.

Show time!

When you run the Flow it will go ahead and do its magic.


I'll use a primary contact in Wellington, New Zealand from the Flow run history as an example. If we look at the convert time zone output, we can see that the UTC time has correctly been reflected as 4.30am. 


As seen in the earlier screenshot, 4.30pm in Wellington is equal to 4.30am UTC. Therefore the email is delayed until 4.30pm in the Wellington time zone. The Primary Contact will receive the email at 4.30pm in their time zone.

Summary

Delayed emails based on a individuals time zone can be achieved through the power of Flow. To go one step further you can also perform queries as well in the business scenario where only selected individuals need to receive the email.

Although after reading one of the reviews of my presentation there is one step that is missing in my original Flow. Maybe I was low in energy that day to recognize the gap. I used the date from the localTime which is great. However what should also be in the expression is to add 1 day so that the email is sent the following day.

My Flow is available for download from the TDG Power Platform Bank.

I also want to take the time to say big thanks to Jon Levesque and Gabriel for their massive effort in organizing and putting together a free Flow Online Conference for everyone. It was really great and I can't wait to see the next one with a new line up of speakers.

Thursday, 25 July 2019

Developing your personal brand with Troy Hunt

18:07 Posted by Benitez Here , , No comments

Troy Hunt is a person who is well known by his creation of Have I Been Pwned and his presence on Twitter is also popular. He is currently a Microsoft MVP too. I recently had a chat with Troy about personal branding. For an explanation of what personal branding is, check out this blog post. Personal branding is common in the tech world. It naturally comes from the person's engagement through their online presence as well as in-person at conferences or events. If I use myself as an example, you can find my online presence on my blog site, YouTube, Twitter, and you'll also see me presenting at conferences and user groups.

Prior to my chat with Troy, I thought about questions that everyone can relate to - either as a person who is well known or thinking of increasing their presence. These are genuine questions and some of them I can relate to because I have friends in the community who have also experienced the '1%' that Troy refers to in our chat which you'll hear about. These questions also stem from my own curiosity of understanding what it is like when you are someone who has a high public profile because I don't have as much exposure. It was interesting to hear Troy's experience and I really enjoyed his honesty through out our chat.

Grab a coffee or tea and have a listen to our chat.


Note: this chat was done over Skype for Business and is raw footage. I was using WIFI from the Portugal Airbnb I was staying in at the time and the first couple of minutes are not the greatest video quality for some reason. Sometimes it is a bit laggy. It is fine to listen to and have it play in the background while you multitask 🙂

Questions asked

02:33 - What worked and what did not work for you along the way?

05:33 - Tall Poppy syndrome exists

10:56 - When you're well known, people may consider you or anyone else who has a high public profile as a celebrity. How do you remain intact in producing quality content to ensure that you are who you are, in terms of your knowledge and strengths and not just seen as a public celebrity

16:25 - When you're writing your content do you endeavour to keep to a schedule or do you write whatever comes into your mind or what's happening during that week

19:36 - With the content that you produce do you ever think about how to make it stand out or how to make it unique? As you know there's a lot of content that's getting written every week and there's more people out there who are starting blogs because technology in all fields is constantly evolving. Is it something you ever worry about it or you just shut it out and just mind your own content, and not worry about anyone else

22:06 - For content that was previously relevant at the time, let's say you wrote a blog post five years ago and now because of the evolving technology that's taking place, would you go back and revisit it, would you leave it as-is or would you write a new blog post to expand on it? To outline what has since changed or why it would not work or apply anymore to this day?

23:55 - Have you ever been called out by the 1% on something that you've written about in the past that's not relevant anymore? Has anyone in the last couple of years have been like - Hey Troy, by the way this thing you wrote back then is completely wrong 

25:06 - If you were the CEO or person in charge of LinkedIn, what would you do for a week in terms of changing it?

Key takeaways

  • Consistency with how you conduct yourself online and in-person. There are people who behave differently online compared to how they are in-person which is not always a good projection of who they are. The brand that you represent online is in parallel to how you are in real life.
  • Engaging in debates online such as Twitter can sometimes lead to no where and it's good to tune out early. When you stop engaging the debate usually dies down.
  • Tall poppy syndrome can happen and is personally hard to manage at times. A person's achievements is not necessarily welcomed by everyone.
  • People who respond in a negative manner online would not behave in the same way in-person.
  • For the 1% who respond with irrelevant comments that are not directly associated to your message or blog post, either ignore once reading the comment or have a sensible neutral response.
  • Learn to recognize the difference between the 1% compared to when you have said or done something that isn't well received on a broader basis that you would need to back track on and apologize for.
  • Blogging is based on your technical or functional knowledge. Your public profile of being seen as a "celebrity" shouldn't influence you to write in a certain way for people to pay attention and think you're awesome.
  • Writing (such as a blog post) does not always express sentiment well compared to a video. A video at times can be more convenient than writing a blog post especially if it is a busy week. Sticking to a set output is more likely to create unnecessary pressure. Videos take less effort which allows consistency in releasing content when time poor. Whereas a blog post can be more fitting when the information is better presented in words, such as a step-by-step guide on how to do something. The medium of choice can be dictated by the availability of your time and content.
  • No need to be too concerned if someone else has written similar content as it's all based on your motivation for the content such as the previous example of capturing a step-by-step guide in a blog post. A guide is also something that can be continually looked at, especially if it is a process that is complicated. Not only can a blog (or a vlog) be useful for yourself but it can also help others too who are experiencing the pain of understanding how to do that same one thing.
  • Writing a blog post about something new that pops up makes you take initiative in learning about it as well, even if you don't understand it to begin with. In Troy's case, if a notable security incident appears he'll learn it, research it and blog about it to have a finger on the pulse with what's happening right then and there.
  • When you have a large volume of blog posts it can be hard to maintain them if the content was relevant then and not so much anymore. If someone takes the time to call something out that it is incorrect, then the content will be updated. If the topic is high profile, a breaking change or fundamentally wrong then in these scenarios it is good to revisit and evaluate whether new content is suitable as a follow-on.
I also asked my last question of what he would do if he was the CEO or person in charge of LinkedIn for a week and what he would to change it. You'll have to listen to find out his response 😁 It is refreshing to hear someone talk about the aspects of a platform that is both useful and unhelpful at the same time.

My thoughts

The one item that I don't relate to is how videos for him are convenient. I am the complete opposite, I spend a lot more time producing my vlogs than blogging 😅

Those of you who have followed me for a while know that my YouTube channel is primarily focused on sharing concepts and functionality of Dynamics 365, Dynamics 365 for Portals, CDS and Flow. I demonstrate to you face-to-face and walk you through the process which is also supported by a blog post for those who would rather read than watch. Those are a lot of hours sunk in from my own personal time. However I can relate to the feeling of pressure because I do put pressure on myself whenever I plan, film, edit and write my content. It is hard when you have had a busy period juggling a project or any other work related matters, plus life outside of work. I know there are others in the community who go through this as well and I am not alone. Troy goes through it, I go through it, and you may go through it too.

It was cool to hear Troy speak of his experiences and appreciate that he took some time out of his day for us to chat. Currently we're both travelling and luckily we were both in time zones that worked!

Hope you enjoyed this vlog and blog post! Thanks for tuning in and remember to be kind to each other 😊

Follow Troy

Twitter - @troyhunt
Blog - https://www.troyhunt.com
YouTube - Troy Hunt

Monday, 3 June 2019

Automatically update the stage of a Business Process Flow with Flow 2.0

22:26 Posted by Benitez Here , , , , , No comments
From my last WTF episode:

I decided to use two Compose actions because I don't know how to store the Dynamics 365 API request in a Flow action. If you know how to do this, feel free to blog and tweet to me so that I can check it out.

I heard back from two people, hooray!

The first person I heard from was David Yack, he was wondering why I had chosen my uncanny method when really I could achieve the same outcome using the List Records action. When he explained it I was thinking to myself, hmmm... 🤔 oh... YEAH!!! It was so obvious only after hearing it from him haha. Like any other person who goes through the process of solution design - sometimes the answer is staring at you the whole time.

The second person I heard from was Yasir Safeer who I currently work with. He was going to suggest another method however I mentioned I already heard from David. However, he gave an awesome tip which made me go, really?! I tried it and to my surprise it worked.

This WTF episode talks about the two improvements I made to my Flow thanks to David and Yasir
#iAppreciateYou 🙏

Using a CDS List Records action

My Flow trigger is when the status reason of the Application has been updated. Next, I was using a CDS List Records action that would retrieve the Application Business Process Flow record so that I could identify the  associated Process Stage ID.

Rather than storing the Review stage ID and the Approved stage ID in two Compose actions after calling the API request in a separate browser using the Process stage ID, I can simply use the CDS List Records action against the Process Stage entity. I was able to use a filter query from David's suggestion that would only return the Process Stage record that had the stage name of "Review."


It's so funny to me because it was staring at me when I was reviewing the API request. It clearly tells you the entity to reference and the field as well. Thanks David for showing me way 😁 It was not obvious at the time but now it is.

I then applied a Parse JSON action where I used the first function. 

first(body('1.4.1_Get_Process_Stage_Review_record')?['value'])


I'm only after after a single record and the first function prevents the Apply to Each from appearing downstream in the Flow. I am using the properties later on in the Flow. I briefly covered the first function in another previous WTF episode so take a look when you can.

I applied the same methods for the Approve stage however what you'll see differently this time is that I am using parallel branching. Parallel branching is useful when you have actions that can be executed that are not dependent on the previous action or each other. Since I am retrieving the stage IDs through the List Records action both actions can run at the same time.

Changes to the last action

My very last action in the Flow is updating the Application Business Process Flow record. In the Active stage field of the action I am referencing the stage ID through the Parse JSON action. Previously I was achieving this through my two compose actions.


The cool tip that Yasir shared with me is that you don't need to update the Traversed Path value. I was puzzled because I followed what was in the docs.microsoft.com article but I trust Yasir's advice given that he's an awesome consultant so I gave it a go. I cleared my previous expression and left it blank.

Flow in action

When you submit an Application, the Flow succeeds as I expected.


When there are two approvals (one from the External Reviewer and the other form the Internal Reviewer) the Flow succeeds as I expected.


In both use cases the traversed path correctly updates. Amazing. Thanks Yasir, good tip man.

Summary

By using a CDS List Records action against the Process Stage entity, it will allow you to reference to the stage IDs of your Business Process Flow entity. You also don't need to use an expression to update the Traversed Path field either, it magically works 🧙‍♂️

My WTF Flows is available for download on the TDG Power Platform Bank.

Till next time, toodles.

Thursday, 9 May 2019

Automatically update the stage of a Business Process Flow with Flow

I have worked on Dynamics 365 for Portals projects and one of the things I get asked is how to reflect the lifecycle of an application back in the model-driven app. This is for the purpose of users who need to interact with the application to progress it for the ultimate goal of the application being approved. Naturally Business Process Flows come to mind because it's a visual aid to help users understand what is to be completed in each stage.

I wanted to see if it is possible to automatically update a Business Process Flow using Flow based on interactions by multiple types of end users. These would be end users of the portal and end users of the model-driven app. I put Flow to the test and it worked 😄 In this WTF episode I cover how to automatically update a Business Process Flow stage with the power of Flow.

Quick lesson on behind the scenes of Business Process Flows

For the folks in the community who have been around the Dynamics 365 circuit for a while, the method of retrieving and updating BPF fields within the record is deprecated.


For example Stage ID and Traversed Path will display as deprecated in the Application entity customization settings. Note: for some reason in https://web.powerapps.com the Stage ID was not displaying so my screenshot is from the classic solution editor.


The current supported method is to reference fields in the BPF entity that would have been created when your BPF was created. In my scenario I created a custom Application BPF and a custom BPF entity was also created. There are fields within this entity that will be used in my WTF Flow which are
  • Active Stage ID - this will contain the stage ID value of the Business Process Flow stage the application is currently in
  • Application - this is the lookup to the application record
  • Business Process Flow Instance ID - this is the ID of the record that will be used in a Flow action
  • Status
  • Status Reason
  • Traversed Path - this will contain the active stage IDs the Business Process Flow has gone through in its lifecycle

Use case

I am extending my Grant Application Management solution to replicate the real world of an organisation. I have previous vlogs and blogs that cover it.
  1. Grant application management part 1: Universal Resource Scheduling
  2. Grant application management part 2: the external reviewer and applicant experience
  3. Dynamics 365 Saturday Scotland 2019 - Grant Application Management
In a nutshell
  • a grant application is submitted through the portal
  • an external reviewer is assigned to the application where they are required to approve or reject it through the portal
  • an internal reviewer also approves or rejects it within the model-driven app
  • until there are two approvals, the application will not be updated to Approved
To keep it simple and for demo purposes I have three stages in my custom Application Business Process Flow.
  1. In Progress
  2. Review
  3. Approved

Here is the interactions of the end users that will trigger the Flow
  1. When an application is submitted by an Applicant through the portal, the Business Process Flow stage of the application record should automatically move from the In Progress stage to the Review stage. This is so that anyone within the organisation can see that the Application is now in the Review stage.
  2. When both the External Reviewer and Internal Reviewer has approved the Application, the Business Process Flow stage of the application record should automatically move from the Review to Approved stage.
This is a Business Process Flow that uses a single entity.

Let's Flow

1 - The trigger

The trigger will be when the status reason of the Application has been updated.

2 - Retrieving the Application Business Process Flow record

Since the supported method is to reference the fields in the Business Process Flow record, the List Records action is used to retrieve the Application BPF record associated to the Application.

The filter query is for the purpose of only returning the Application BPF record associated to the Application. If a filter query is not used then ALL Application BPF records will be returned.

3 - Parse JSON

To reference the properties returned in the JSON response from the List Record returned, the Parse JSON action will be used. In my expression I am use the first function which I covered briefly in my previous WTF episode.

4 - Compose action for the Review stage ID

To move the Business Process Flow from one stage to another, the stage ID needs to be captured in the traversed path field. I am using two compose actions to store the value of the stage IDs.

To identify the stage ID values of the Business Process Flow there is a Dynamics 365 API request which can be found in the docs.microsoft.com article.


In the API request you reference the Dynamics 365 organisation URL along with the ID of the business process flow record. The simplest way to grabbing the ID is to do it from the Business Process Flow record.


Once you execute it in a browser it will look something like this:

Copy and paste into Notepad++ and use the JSON viewer plugin so that the response can be easier to read for the Business Process Flow stage IDs.


The Review stage ID can now be referenced in the Compose action.


I decided to use two Compose actions because I don't know how to store the Dynamics 365 API request in a Flow action. If you know how to do this, feel free to blog and tweet to me so that I can check it out.

5 - Compose action for the Approved stage ID

This Compose action references the Approved stage ID.

6 - Initialize Variable action for the Switch action

If you have not used the Switch action before I recommend checking out the Flow blog and Pieter Vienstra's blog to learn about the Switch action. He has a couple of great blog posts on the Switch action.
The reason why I am using a Switch action is that there are multiple outcomes for the Application Business Process Flow based on the Application Status Reason. Using the Condition action would work too but it means that there will be a few branches and if you have a Business Process Flow with more than 3 stages, the Flow could look like a mess.

Using a Switch is just as effective and provides the desired output based on the Application Status Reason. A single field is defined in the Switch that will drive what should happen next in the Flow.

The Initialize Variable allows me to reference the Application Status Reason in my switch. I set the type to Integer as the Status Reason is an Option Set where each value in the Option Set is an integer.

7 - Switch it up

In the Switch action we reference the variable. Based on the value of the Status Reason a switch will be performed. The two cases can are defined as
  • If the Status Reason has been updated to Submitted the Business Process Flow record needs to be updated from In Progress to Review.
  • If the Status Reason has been updated to Approved the Business Process Flow record needs to be updated from Review to Approved.

7.1 - Moving to the Review stage

When the Status Reason equals Submitted, the CDS Update a record action will be used to automatically move the stage from In Progress to Review.

Entity

The entity referenced will be the Application BPF. Reminder that this is the entity that was automatically created when the custom business process flow was created.

Record Identifier

The Record Identifier will be the business process flow instance id from the Parse JSON action. This property uniquely identifies the business process flow record associated to the Application.

Traversed Path

The Traversed Path is a field as mentioned earlier that represents the lifecycle of the business process flow as it contains the stage IDs the Business Process Flow as gone through. The Traversed Path field value needs to be updated to include the stage ID of the current active stage it will be moved to. This is the Review stage.

To include the stage ID in the Traversed Path field the concat function is used in an expression. This allows the Review stage to be appended to the existing Traversed Path field value which can be derived from the Traversed Path property in the Parse JSON action.

The expression will be the following

concat(body('1.3_Parse_JSON')?['traversedpath'], ',',outputs('1.4_Review_Process_Stage_ID'))

Active Stage

This field value will be the earlier Compose action that references the Review stage ID.

Application

The final field to ensure you populate is the lookup that associates the Business Process Flow record to the Application. This is done through the Application ID from the trigger. 


7.2 - Moving to the Approved stage

When the Status Reason equals Approved, the CDS Update a record action will be used to automatically move the stage from Review to In Progress.

Same definitions except for a couple

The same field definitions apply however there's two more that is required in the CDS Update a record action since the Approved stage is the final stage in the Business Process Flow. This is so that the final state of the Business Process Flow is correctly reflected to the end user in the model-driven app. For further information refer to the docs.microsoft.com article.
  • Status Reason - this will be set to Finished
  • Status - this will be set to Inactive

Flow in action

Time to run the Flow. When the Application has been submitted through the Portal, the Business Process Flow of the application record will automatically move from In Progress to Review.


When the Application has two approvals - one from the External Reviewer and the other from the Internal Reviewer, the Business Process Flow of the application record will automatically move from Review to Approved where the state will show as finished.

Summary

Based on different end user interactions whether it is through Dynamics 365 for Portals or within the model-driven app, the business process flow of a record can be automatically updated to progress from one stage to the other using the power of Flow.

Don't forget to subscribe to my YouTube channel.

Toodles.