Continuous Integration, Deployment & Test Automation for Dynamics 365 CE in Azure DevOps/VSTS – Part 1

One of the most comprehensive articles on Azure DevOps integration & Dynamics 365. Blog series consisting of 3 articles which describe the process end-end

Microsoft Dynamics CRM/Dynamics 365 CE Blog

In this blog series, we will explore building out DevOps processes and
practices for Dynamics 365 Customer Engagement (CE) by utilizing Wael Hamez
MSCRM Build tools. In this first blog, we will cover the version control for
Solutions.

What is
DevOps?

DevOps is a new term emerging from the collision of two major related
trends. The first was also called “agile infrastructure” or “agile
operations”; it sprang from applying Agile and Lean approaches to operations
work.  The second is a much-expanded understanding of the value of
collaboration between development and operations staff throughout all stages of
the development lifecycle when creating and operating a service, and how
important operations has become in our increasingly service-oriented world (cf.
Operations: The New Secret Sauce).

Problem
Statement

I have started working on CI/CD when I was assigned as a Platform
Engineer for a Dynamics 365 CE implementation project. At that time, I had a…

View original post 1,268 more words

Enhancement in Lead Qualification and Email experience in Release wave 2 in Dynamics 365

I have worked with lots of clients on Dynamics 365 for sales and we can’t ignore the fact that lead qualification is one of the major functionality in any tool. However even in a functionality as primitive as lead qualification, there were some issues which didn’t provide a great customer experience. Trust me on this, Release Wave 2 has eased these pain points a lot. And I really wonder why it took such a long time to do so.

This morning I was in my customer location and I just enabled these features in one of their Sandbox environment. Only consultants like us would know how much these simple things are appreciated.

So let’s understand what is new in here.

1. Contact and Account information automatically pulled up when Existing Contact and Account field is selected.

As we all know, we have fields on the lead like “Existing Contact” & “Existing Account” and then we have separate fields on the form to fill out contact information. Previously even if you have selected the Existing contact and account fields, you would see the details fields not getting populated. But now once you select them, the contact and account details are pulled from them.

Please do note however that the details are not changed if you change those look up fields. To reflect the latest value from lookup fields, clear out the details fields and then change the value in contact or account lookup and you would see the new value reflecting.

image

2. Ability to provide a choice for account/ contact/ opportunity creation while qualifying a lead.

With the new release you can provide an option to the user to choose whether to create a contact or account or opportunity while qualifying a lead. The setting is available in Administration –> System settings –> Sales tab.

image

As you can see, I have chosen “not to” automatically create account, contact and opportunity record. Now when I try to qualify a lead, I get a prompt where I can choose to create the entity or not.

image

3. Contextual email experience while creating an email from timeline.

This is something which customers were really annoyed at. For example, say you are working on an opportunity and if you create an email from timeline, it will take you to new email creation page and you loose the whole context of the opportunity.

Well, it’s no longer the case. With enhanced email experience, you can now create emails from your timeline without losing context of the record you are working on.

But first let’s see how you can enable this. It can be tricky if you are not aware.

Step 1:

Open you Sales Hub app.

image

Step 2:

Once the app is opened, go to the App settings area of the app.

image

Step 3:

Click on Enhanced email and enable it.

image

And now when you try to create an email from timeline, you see a nice pop-out

image

So you can go ahead and edit the email content by taking in required values from the record you are working on and also from related records.

In the above example, you can click on account “Tailspin Toys” and navigate to the account record. The email pop-up would hold it’s place.

Wonderful isn’t it? And do I need to say more that it was loved by our customer.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

View emails as conversation in Activity Timeline in Dynamics 365

Release wave 2 have brought with it some nice little features and one of them is the ability to view email conversation threads in activity timeline. So let’s see what this feature is all about.

Suppose a CRM user is having conversation for a customer and emails are going back and forth. There are a series of emails being sent. In between some other emails or activities are being created as well. If we come to the timeline, below is the screenshot of how it looks like. While we have all the conversations in there, the emails are not in sequence

image

From the above screenshot, as you can see there is an unrelated conversation in the middle of a conversation thread (encircled in green). And in real life cases where conversations are frequent, it’s very easy to loose track of this.

Then how to fix it? Well, with this new release, the user have an option to view email conversations as conversation thread.

Go to Personalization Settings –> Email Tab and check the box – “Show emails as conversation in timeline” as highlighted in the screenshot below.

image

And once you do that, come back to the timeline and you could see your emails being tracked as conversation. The related conversations are groped together.

image

Believe me a small setting but certainly puts a smile on customer face.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Report this ad

Report this ad

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Open webresources Modal or Inline using Xrm.Naviagtion.naviagteTo in Dynamics 365 Unified interface

Release wave 2 have released some wonderful features and one of them is the capability to open a webresource as modal or inline. I bet this is going to ease the life of lot of consultants who were traditionally using Xrm.Navigation.openWebResource or Xrm.Utility.openWebResource (deprecated) to open webresource and hear about the perennial client complaint of why it is not modal.

Introducing to you, the newest star on the rise – Xrm.Navigation.navigateTo

So let’s see how we can open the webresource as modal. Detailed documentation can be found here – https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/xrm-navigation/navigateto

Sample code below.

var qs = "param1=1&param2=2"; 
var pageInput = { 
    pageType: "webresource", 
    webresourceName: "trng_/pages/newapitest.html", 
    data: encodeURIComponent(qs) 
}; 
var navigationOptions = { 
    target: 2, // 2 is for opening the page as a dialog. 
    width: 400, // default is px. can be specified in % as well. 
    height: 300, // default is px. can be specified in % as well. 
    position: 1 // Specify 1 to open the dialog in center; 2 to open the dialog on the side. Default is 1 (center). 
}; 
Xrm.Navigation.navigateTo(pageInput, navigationOptions).then( 
    function success() { 
        debugger; 
    }, 
    function error(e) { 
        // Handle errors 
    } 
);

 

And there you go. I have opened up the same webresource on click of a ribbon button. Honestly have waited long for this one. Certainly a smile on customer’s face.

image

Well you can even resize it to occupy full screen using the resize option as highlighted inside Orange rectangle. Also you can pass custom parameters as data like I did in my code.

So far so good but as a consultant don’t get carried away by this new feature as you may need to update your customer upfront on some important aspects.

  • Users need to close the dialog using the “X” button available on the dialog. There is no API available as of the time of writing this blog where we can close the dialog using a custom button. The user have the use the default close dialog option only.
  • No data is passed to the callback method. So don’t expect some data from the dialog being passed to the successcallback method

Even better if you want to open your webresource on the side pane, just like Quick create form opens, all you need to do is change “position” value of navigationOptions parameter to 2. And below is how it looks.

image

And finally don’t forget to include a reference to clientglobalcontext.js.aspx  if you want to get hold of the Xrm namespace methods inside it, just like the old ways.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

How to call CDS/ Dynamics 365 action from Microsoft flow (Power Automate)

Calling actions from Microsoft flow is now just a cakewalk. All you need to is to use the Common Data service (Current environment) connector introduced in flow. This is not to be confused with the Common data service connector which is there for quite sometime now. If you are not accustomed to this connector, I suggest to refer my previous article on this.

P.S – In this article I am going to show you on how to call a custom action. However you can call any action provided by the system as well.

In this article I am going to show you on how to invoke an action using flows. And for this sample I have taken up an action which has a simple as well was complex input and output parameters. Below screenshot for reference.

image

As you can see there are two input parameters of type integer and entityreference and two output parameters of type string and entitycollection respectively. The entity reference input I have set it type ‘Contact’

I went ahead and created a flow. For this demo when an account record is created, I will be invoking my action which is unbound (not tied to entity).

Since my action is unbound, I use the “Perform an unbound action” action step in the connector. If you have  an action tied to entity, you should use “Perform a bound action” step.

image

When you try to set the values of Input arguments, the editor will show you the required input types. Since I have kept the entity reference input argument as type ‘Contact’, it shows me the contact input fields.

image

You can set the input values accordingly and your action would work just fine.

An observation here.

Using EntityCollection as the output parameter may not work. This is because when you set output parameter of type EntityCollection, the action would need an output of type Array. If you use EntityCollection as output parameter, you would get an error similar to the one below.

The API operation ‘PerformUnboundAction’ requires the property ‘body’ to be of type ‘Array’ but is of type ‘Object’.

The solution to the above would be to serialize the entitycollection to a string and returning it as string json as the output parameter. And then use parse json in the flow to get the results.

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

{Quick tip} Common Data Service (Current environment) connector not showing up while creating Microsoft flows (Power Automate)

Flows are now Power Automate and why not? After all it is an enterprise process automation flow framework personally I believe the name change is long due. And the name change has brought with a plethora of new features but Robotic process automation (RPA) is the big brother taking all due credit.

However there are some hidden gems being released with the new version and one of them is the Common data service (current environment) connector. And with that some wonderful stuffs like Invoking an action, Relating and un-relating record and quite of few of them deserves mention.

Announcement of the capability to invoke bound and unbound actions with the CDS connector have been announced way back with March release 2019. And I had my customer who I informed prior about that and as soon as Release Wave 2 came, customer tried out to invoke custom action using CDS connector. But unfortunately they could not find it. I tried to find it from my end as well but no luck with me.

And finally the MVP community came to my rescue to save my face. Special thanks to Alex Shlega for helping me out that there is a new connector Common data service (current environment). One more time I realized that MVP’s are MVP’s for a reason. You can check out his awesome  blog right here.

I will run a series of blogs describing the capabilities introduced by the new connector. So let’s begin with setting up the connector.

So first let’s set up the new connector.

Setting up the CDS (current environment) Connector

While this sounded quite simple for me. After what’s the big deal in selecting a connector from the list of connectors? Well sometimes the simplest of things you think may take out couple of hours from you.

If you go to My flows section and try to create a new flow, it simply won’t appear. Below screenshot is when I try to create the flow from my “My flows” section.

image

It turns out that the connector is visible only when you try to create a flow from within a CDS solution. So I went ahead and created a solution and when I tried to create a new flow this time the CDS (current environment) trigger is right up there.

So remember to use this connector you would need to design your flows from within your solution. Small tip but can save you some time in case you are trying to explore something new.

image

Hope this helps!

In my next article I will show you on how to invoke custom actions using flows.

Debajit Dutta

(Dynamics MVP & MCT)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

Step by Step – Calling Flow from PowerApps (Dynamics 365 CE/CRM)

Good one from @Nishant Rana

Nishant Rana's Weblog

Let us take a simple example of creating lead to understand how we can call Flow from PowerApps.

Log in to PowerApps Studio and create the form as below.

  • TextInput for entering the Last Name, First Name and Email ID.
  • Button to call Flow.
  • The label named GUID which will display the GUID of the created lead record.

Now select the button, go to Action menu and click on Flows to create a new flow.

Click on create a new flow.

Select Instant – from blank option

Select PowerApps as the trigger.

Add a new Step – Create a new Record (Dynamics 365)

To create a new record step, specify the Dynamics 365 Organization Name, select Leads as the entity name.

For the Last Name field, click Ask in PowerApps that will generate the parameter to which we will pass the value from our PowerApps.

The generated parameter.

Repeat the…

View original post 241 more words

{Quick tip} : Determine if OpportunityClose dialog is opened for “Close as Won” or “Close as Lost” in Dynamics 365 Unified interface.

As many of us know, for unified interface now we can customize the “opportunityclose” entity. A feature awaited for a very long time and finally it’s here.

If you are not aware, you can now customize the dialog box that pops up when you click “Close as Won” and “Close as Lost” button on the opportunity. The entity is “opportunityclose” entity. For more details, please refer the below links

https://docs.microsoft.com/en-us/dynamics365/sales-enterprise/enable-opportunity-close-customization

https://docs.microsoft.com/en-us/dynamics365/sales-enterprise/customize-opportunity-close-experience

So you can customize the opportunityclose form and add your custom fields as well. But the same form fires both when you click “Close as won” or “Close as lost” button. My customer had a requirement where based on the button clicked, appropriate fields need to show up and certain business logic need to be performed when the “Opportunity Close” form is loading.

Just when I was about to pose before my customer as “know it all person”, Dynamics 365 again intervened and put me in proper place. So I was back to drawing boards. The first thing that struck me was – “How is this quick create form getting launched?”. In the ribbon workbench I evaluated “Close as won” button and found the below action.

image

Now we are going to modify the Opportunity system library script file. Ahhhhhhhhh..Just joking.

The important thing to note here is the “Boolean parameter” which is passed to the function. So the key takeaway from here is the system function is taking this boolean parameter as input and then passing this parameter to the Quick create form of opportunity close.

Now things are sorted out. A quick guess – this parameter must be passed as a Querystring parameter to the Quick create form. And hell yeah! I was right.

function formLoad (e) {
        debugger;
        var isCloseAsLostClicked = false;

        var fc = e.getFormContext();
        var gc = Xrm.Utility.getGlobalContext();

        var queryString = gc.getQueryStringParameters();
        isCloseAsLostClicked = queryString["param_won"];
// value will be true for won and false for lost button click.    

}

I have highlighted the lines of code here which will help you determine whether “Close as Won” or “Close as Lost” was clicked.

Once you have that information, it’s up to you to utilize this information. Cool isn’t it?

Before I end, this will work for Unified interface only as the feature of customizing opportunityclose entity is supported only for Unified interface.

P.S – Haven’t found this documented in MS docs. So obviously can’t vouch whether this parameter will always be there. As of now validated for multiple environments and scenarios and it worked just fine. Would be happy for alternatives.

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

How to use Promise to evaluate your ribbon enable rules with asynchronous Xrm.WebApi methods in Dynamics 365 Unified interface.

We all have been using Xrm.WebApi methods isn’t it? After all why not? They are wonderful. You no longer need to write lengthy XmlHttpRequests and parse the raw JSON results back. Xrm.WebApi methods does the hard part of converting the raw JSON outputs into strongly typed objects.

Not sure if anyone is still there but if you are new to Xrm.WebApi methods, below is the perfect link to get you started.

https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/xrm-webapi

Just when you are about to think that Xrm.WebApi methods have successfully ticked all the boxes, comes the ribbon rules. Xrm.WebApi rules are asynchronous.

Suppose you have a requirement where you need to show a ribbon button only if user is in role of Salesperson. For that we write one function “userHasRole” with the code below

 function userHasRole() {

       var isUserAdmin = false;

        Xrm.WebApi.retrieveMultipleRecords("role", "?$select=roleid&$filter=name eq 'salesperson'").then( 
            function success(result) { 
                 var userSettings = Xrm.Utility.getGlobalContext().userSettings; 
                var securityRoles = userSettings.securityRoles;

                var isUserAdmin = false; 
                 for (var i = 0; i < result.entities.length; i++) { 
                     var roleId = result.entities[i].roleid;

                    if (securityRoles.indexOf(roleId) !== -1) { 
                         isUserAdmin = true; 
                        break; 
                    } 
                } 
            }, 
            function (error) { 
                console.log(error.message); 
            } 
         );

return isUserAdmin; 
}

The problem with this code is very evident. Before Xrm.WebApi return the results, the function would return false. Off course you can modify this function using window level objects and then using Xrm.Page.ui.refreshRibbon() to refresh the ribbon after Xrm.WebApi returns results. And perhaps we all have done that so far.

While the above method would certainly work, you may think like – “Is this the only way?” Certainly not. If you are using Unified interface (only option left for online users after Oct 2020), you have a better way to achieve the same. Unified interface rules support returning a Promise rather than boolean for asynchronous rule evaluation. Let’s see how we can re-write the same rule using Promise. And surprisingly, many of us working in Dynamics 365 day in and day out are not aware of this.

function userHasRole() { 
    return new Promise(function (resolve, reject) {

        Xrm.WebApi.retrieveMultipleRecords("role", "?$select=roleid&$filter=name eq 'salesperson'").then( 
            function success(result) { 
                var userSettings = Xrm.Utility.getGlobalContext().userSettings; 
                var securityRoles = userSettings.securityRoles;

                var isUserAdmin = false; 
                for (var i = 0; i < result.entities.length; i++) { 
                    var roleId = result.entities[i].roleid;

                    if (securityRoles.indexOf(roleId) !== -1) { 
                        isUserAdmin = true; 
                         break; 
                    } 
                 }

                // return true or false 
                resolve(isUserAdmin); 
            }, 
            function (error) { 
                reject(error.message); 
                console.log(error.message); 
            } 
        ); 
    }); 
}

Not much change in the code. All you need to do is return a Promise the resolve method of which should return true or false depending on business logic. And one very important to remember is that not only with Xrm.WebApi methods but also if you are using XMLHttp async, it would work just fine.

Put your code within the Promise and everything works like charm.

Any functionality before we implement, we should be aware of the limitations of the same. So we have a couple of them here.

  • This only works for Unified Interface. If you are using WebClient this won’t work
  • If the rule does not evaluate within 10 seconds, the rule would resolve with false value

Before I end, you may be asking where should can I find this in Microsoft documentation. Well if you are a big fan of Microsoft docs like me, here you go – https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/customize-dev/define-ribbon-enable-rules#custom-rule

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)

Dynamics 365 integration with SmartSheet using Flows–What’s there and what’s not.

A dry spell of a month and here I am back to my favourite way of connecting with community which is writing one more blog post.

So here I got a new requirement from my customer. Basically they are using Smartsheet and Dynamics 365 and the simple requirement is whenever any changes are made in the smartsheet (row added or updated), some updates need to happen back in Dynamics 365.

Although I haven’t used Smartsheet connector in Flow, I was just aware of it and my initial hunch was like this would a walk in the park for flows. But like so many other times, this time also it didn’t quite turnout like the way we expected.

The first scenario was “When a row is added to a smartsheet”, I should be able to sync those changes to Dynamics 365 instance. So I started creating and flow and searched for Smartsheet connector. And the trigger is just there.

“When a new row is created”

image

I select that trigger and connect to the smartsheet.

image

As you can see from the above screenshot, I provided the smartsheet I want connect to and also mentioned the columns that I want to sync to. “Requestor” and “Business Group” are columns in the smartsheet as seen from the below screenshot.

image

The next step is to identify the values of the fields entered by the user. Now this one can be tricky if you are new here. For this demo I will use the Initialize variable action and take the value of the Requestor and Business Group in those variables. But before we do that, we need to understand how does it return the data.

So I go ahead and add a new row to the Smartsheet. As expected within a minute the flow fired. Let’s inspect the body of the flow here.

image

As seen from the above screenshot, it returns cells collection. The value highlighted here is for the" “Business Group” field.

To take this value and assign it a variable you can use the below expression.

triggerBody()[‘cells’][0].displayValue

image

In this way you can use variables for other values and then use CDS connector to update CRM.

Now comes the update part. Surprisingly there is no trigger for when a row is updated in Smartsheet.

image

We have something – ‘When a sheet is update’ but unfortunately that does not return the updated rows. Infact it returns the whole sheet data. Kind of stuck and hence had to resort to custom development of consuming SmartSheet API’s.

Would be delighted if I have some inputs here!

Hope this helps next time you get a requirement on this.

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics 365 (http://www.xrmforyou.com/record-cloner.html)

Multiselect picklist for Dynamics 365 (http://www.xrmforyou.com/multi-select-picklist.html)