Sonoma Partners Microsoft CRM and Salesforce Blog

Dynabacus: Now Available in AppSource

We are happy to announce that Dynabacus is now available in AppSource and on our website for on premise Dynamics CRM users! 

Dynabacus supports Dynamics CRM 2015+. As we described in a previous introductory post, Dynabacus is a record counting tool that is packaged as a Managed Solution in your CRM instance. Your team might be looking to see the impact of applying field changes, or preparing for a new integration or data migration. Or maybe you just need a record count that is more than the maximum count of 5,000 shown in grids and advanced find. Dynabacus will allow you to leverage existing views from CRM to allow you to filter and count the records you want for multiple entities or even multiple views on the same entity. Just select the entities and views and click Get Counts.

Dynabacus

We have also added the ability to export a set of counts to a csv file. This can be used to compare results before and after changes, as one example. This ability can also come in handy when running tests before making a change in production as you can import these csv files into a Dynamics instance and the set of entities and views that were used on a previous run will be populated and ready to run again.

Topics: Microsoft Dynamics 365

A Lesson for all Manufacturers: Ace Hardware Gets Better Operational Insight with Salesforce

Ace Hardware implemented SAP CRM back in 2011. The deployment was unsuccessful from the start due to the product’s inflexibility and inability to meet specific business needs. For example, SAP didn’t allow for customized workflows for District Managers and the Business Development team to document investor relationships. Investing in a custom Salesforce deployment provided Ace Hardware with a platform to overall provide more effectively for their employees as well as gain better insight into store operations and productivity.

Who is Ace Hardware?

Ace Hardware is a national hardware store chain with over 4,000 locations. Their headquarters is in Oak Brook, Illinois.

ACE infographic may 2017

Driving Initiatives

One objective of the CRM project focused on assisting Ace Hardware’s District Managers. For District Managers, one of their primary responsibilities is to implement new initiatives in their stores to drive increased revenue. These initiatives could be, for example, driving the sale of more paint or creating new product displays. With SAP, Ace Hardware was unable to see how these store operations were carried out and what their success – or lack thereof – looked like. Too often managers would agree to move forward and forget to follow-up with corporate management after the concept was “sold.” Ace Hardware needed a solution that would better track and monitor individual store activities to further enable their managers and support continued growth for their company.

Stronger Operations

With Salesforce, Ace Hardware could automate this campaign process. You can call that a huge win. With CRM, District Managers are prompted to follow-up and guide a store through the implementation of the initiative. This automation also provides better insight into the effectiveness of these campaigns. Every year, there are several corporate campaigns that they want implemented in all stores. Salesforce allows Ace Hardware to easily push a single opportunity to every user in the field.

Lead Management Automation

Salesforce completely automates Ace Hardware’s lead management process. From the moment an interested investor reaches out to open a new store, CRM prompts Ace Hardware’s Business Development team to complete the multiple transactions to go through this process. First, an initial inquiry reaches the team. Once Business Development qualifies the lead, Salesforce automatically generates the Contact record for the lead. Workflows initiate background checks, qualifies the funds available to invest in the store, and so on. Salesforce automation speeds up these transactions and encourages further investment opportunities for Ace Hardware.

Data and Even More Data

Custom-built store visit reports in Salesforce allow district managers to track various data points each time they visit a store, such as “are employees all wearing uniforms, “do they have the craftsman display set up correctly,” etc. This is a checklist that is also accessible through Salesforce1 for easy on-the-go reporting.

Dashboards are a Manager’s Best Friend

Salesforce provides custom dashboards and reports for easy tracking and stronger store management. These reports track things like top stores year-to-date, bottom 10 stores, wholesale sales metrics reports, and more. These critical numbers allow corporate management to make key departmental decisions with informed, easy-to-read reports.

Looking to take your company’s operations to the next level with a custom Dynamics 365 or Salesforce deployment? Let us know.

Topics: CRM for Manufacturing Salesforce

Dynamics 365 - Learning Path Authoring Basics

With the release of Dynamics 365, Microsoft has added new authoring capabilities to the previously released Learning Path feature.  Additionally, head over to the CRM Roadmap site, or the CRM What’s New site to see more of the features that have recently gone live.

Learning Path, initially released in the Dynamics CRM 2016 Spring release, was a tool to provide rich contextual based training (walkthroughs, videos, and articles) directly inside of the application.  This was intended to allow new users of CRM (or users of a trial org) to get used to the application quickly.

One thing that was missing from that release, was the ability for organizations to author their own custom content.  That gap has now been filled with the December 2016 release of Dynamics 365, and we’ll discuss the authoring process in more detail below.  Please note, that with the initial release of Dynamics 365, this functionality is in it’s initial release, and you may run across some small hiccups along the way.  I anticipate this functionality to only improve with future releases.

Note, that Learning Path (including Authoring) is currently only available for Dynamics 365 Online.

 

Enable Learning Path Authoring

First off, you need to enable Learning Path Authoring in your organization.  To do this, navigate to Settings –> Administration –> System Settings.  Under the General tab, there’s a Set custom Help URL section where you can Enable Learning Path and Enable Learning Path Authoring.

image

You can enable Learning Path or customizable Help, but you’re can’t enable both at the same time.  Therefore, Use custom Help for customizable entities and Append parameters to URL must be set to No.

If you navigate to the Learning Path Content Library (Training –> Learning Path –> Content Library), you may get a message about not being in the O365 Learning Patch Authors security group.  If so, you must open the O365 Admin Center, click on Groups on the left, and select the Learning Path Authors group.  Click Edit in the Members area, and add the user that you’d want to be a Learning Path Author.

image

In addition to being in the Learning Path Authors O365 group, users will need to have a permission enabled on their Dynamics 365 Security Role.  They’ll need to have the Learning Path Authoring permission at the bottom of the Customizations tab.

You can navigate to the Content Library via the Site Map as instructed above (under the Training area), or you can get to it via any sidebar by clicking the Content Library Button.  You can open the sidebar by clicking the ? in the top right corner to the right of the Settings gear icon.

image

Content Library

Once learning path is enabled, you can navigate to Training –> Learning Path –> Content Library to see all the Guided Tasks and Sidebars created for your organization.  There are two types of Learning Path Content:  Guided Tasks and Sidebars.

  • Guided Task:  These bubbles walk users through elements of the application in a step by step fashion. They can be kicked off from a sidebar, navigating to a page, or clicking a link on a page.
  • Sidebar:  These are help instructions that fly in from the right side of the application.  These are displayed when the user clicks the Sidebar button or if there’s an error displaying the intended Sidebar.

From the Content Library, you can create a new Guided Task or a new Sidebar (we’ll go into more detailed explanation of creating a Guided Task and a Sidebar in a subsequent blog), but we’ll give a high-level introduction to the content library, and managing content in this post.

image

Clicking on the content name opens the content to be edited.  You can also take specific actions on content from here via the Manage menu, specifically:

  • Check in:  New content appears with a red lock next the name which indicates it’s checked out and not visible to anyone else.  You must check in content before publishing.
  • Delete:  Deletes the selected content.
  • Export:  Exports the selected content to a ZIP file so that it can be imported in another environment.
  • Import:  Imports previously exported content into this environment.

Content can be localized into the language the users have selected in their Dynamics 365 personal options.  This can be done by clicking the Localize button, and then either Export or Import.

Publishing

Before you can publish new content, you must first check it in.  After you check in your content, you use publishing environments to publish the content for the end user to use.

Publishing Environments are a collection of Dynamics 365 Organizations that you want to group together for use when publishing Learning Path content.  The organizations that are visible are those that are all visible in your O365 account.

To view and configure your Publishing Environments, navigate to the Content Library, then Configuration, and finally on Publishing Configuration.  From here you can view, edit, create, and delete Publishing Environments.  By default, a single Publishing Environment is created with the Unique Name of your organization.

image

After setting up your Publishing Environments, when you’re in the Content Library, you can click on Publish to publish content which makes the modifications made to content visible to end users.  When publishing, you can select one or many Publishing Environments to publish to.

image

A good best practice for publishing would be to create your content in a Sandbox first, and then you can publish to Dev/Test/UAT to test the content, and when it looks good, finally publish it to Production.

Security Roles

Learning Path Content is associated with Security Roles.  If users are associated to multiple security roles, they’ll see the Learning Path Content associated with all security roles.  If different content is created for different roles on the same Dynamics 365 page or screen, users will see the content associated with the role with the higher precedence. 

Navigate to the Content Library, click on Configuration, and you’ll see the roles and their precedence order.  From here you can click on Sync Roles to synchronize the Dynamics 365 Roles with Learning Path.  You can also click on the arrows to move the roles up or down in precedence, as well as click the trash can icon to remove the role from Learning Path (note that this doesn’t delete the role from Dynamics 365, but simply from the Learning Path designer to define how content is displayed to users).

image 

Additional Resources

The following additional resources are available to learn more about Learning Path Authoring.

Final Thoughts

Learning Path has great potential, and the addition of the ability to create your own content makes this feature much more valuable.  I can see the need to create custom content for most of our customer deploys.

For more information on this topic, please see the other posts we’ve made about Learning Path:

Topics: Microsoft Dynamics 365 Microsoft Dynamics CRM Microsoft Dynamics CRM Online

Dynamics 365 Demo Video: Relationship Assistant

Today's blog post and video were created by Bryson Engelen, Sales Engineer at Sonoma Partners.

The Relationship Assistant really makes Dynamics 365 work for you by giving you the right information at the right time.  It presents the user with the most relevant and timely information the system has to offer, like if a Lead or Opportunity is getting stale, if someone emailed about a proposal, or which of your Contacts will attend an upcoming meeting.  And what makes Relationship Assistant even more valuable is it presents information from your email inbox and calendar, like reminders about flights or a heads-up about a customer asking for a product.  Some of the things the Relationship Assistant can help you with include reminders about upcoming activities; suggestions on when to reach out to a contact that’s been inactive for a while; identifying email messages that may be waiting for a reply from you; or alerting you when an important opportunity is nearing its close date. Best of all, the Relationship Assistant can be set up against Custom Activities, so if you have important reminders that don’t fit the out-of-the-box cards, you can add some of your own.

The Relationship Assistant works constantly in the background, analyzing what’s going on for you day-to-day, and presenting you an agenda of the most important tasks, meetings, emails, accounts, opportunities, and more that deserve your attention right now. These insights are displayed as action cards that you can drill into for more information, snooze, or dismiss. The action cards are visible on the mobile app, on dashboards, and even at the top of individual records. There are many different types of cards available, which are linked to in the description of the video below. Enjoy!

Topics: Microsoft Dynamics 365

How To Make Filtering of N:N Lookups Work for You

Today's blog post was written by Rob Montague, Senior Developer at Sonoma Partners.

Sometimes you need to filter a lookup that happens to be an N:N relationship. The issue that we as developers run into is that the control.addCustomFilter JavaScript function does not support N:N relationships. In this scenario, we had Manufacturer and Product entities in a N:N relationship, and we want to prevent the user from picking the wrong product line for a manufacturer. To do this, we created 6 different views on manufacturer entity, each view representing a different product line. The below supported code is an example of how we switched views to filter the Manufacturer lookup based on which Product line is selected.

function onLoad() {

Xrm.Page.getAttribute('sonoma_productLine').addOnChange(filterManufacturer);

           }

function filterManufacturer() {

            // Lookup Filter for Manufacturer field, filter by product Line with N:N relationship

            // needed to switch default views to make this work since N:N is not supporting filters

            var productLine = Xrm.Page.getAttribute('sonoma_productLine').getValue();

            if (productLine == null || productLine.length <= 0) {

                return;

            }

var audioFilterId ='{cb607e22-5403-e711-810e-e0071b6a9211}',

       caraudioFilterId = '{cee7cc88-5403-e711-810e-e0071b6a9211}',

       computerFilterId = '{9b74e0f3-5003-e711-810e-e0071b6a9211}',

       entertainmentFilterId = '{1f5f78d3-5303-e711-810e-e0071b6a9211}',

       videoFilterId = '{5ae0cb69-5403-e711-810e-e0071b6a9211}',

       videoGamesFilterId = '{d6c351ae-5303-e711-810e-e0071b6a9211}',

       defaultFilterId = '{205AC19F-F655-4580-B894-A1D33A6FC800}',

       manufacturerControl = Xrm.Page.getControl('sonoma_manufacturer');

            switch(productLine[0].name.toLowerCase()) {

                case 'audio': //View: Active Audio Manufacturers

                    manufacturerControl.setDefaultView(audioFilterId);

                    break;

                case 'video': //View: Active Video Manufacturers

                    manufacturerControl.setDefaultView(videoFilterId);

                    break;

                case 'car audio': //View: Active Car Audio Manufacturers

                    manufacturerControl.setDefaultView(caraudioFilterId);

                    break;

                case 'computer': //View: Active Computer Manufacturers

                    manufacturerControl.setDefaultView(computerFilterId);

                    break;

                case 'video games': //View: Active Video Game Manufacturers

                    manufacturerControl.setDefaultView(videoGamesFilterId);

                    break;

                case 'entertainment': //View: Active Entertainment Manufacturers

                    manufacturerControl.setDefaultView(entertainmentFilterId);

                    break;

                default: //View: Active Manufacturers

                    manufacturerControl.setDefaultView(defaultFilterId);

         break;

            }

        }

This simple solution ended up resolving the issue we ran into and since we used all supported code, this works on mobile as well. Let us know what solutions you have come up with for this problem by commenting below!

Topics: Microsoft Dynamics 365

Don't Cut Class: 5 Reasons to Attend End User Training

Today's blog post was written by Kayla Silverstein, Marketing Specialist at Sonoma Partners.

This June, we’re offering End User Training for both Salesforce and Dynamics 365 (formerly Microsoft Dynamics CRM). Why should you and/or members of your organization attend user training?

668_CRM_end_user_training_

Here are 5 reasons you should consider signing up:

1. Get the most out of your investment.

CRM is no easy spend. A CRM deployment costs not only money, but precious time and resources.  In training, we’ll show you how to integrate CRM into your daily processes in a way that not only makes your life easier, but also contributes on a larger scale to the success of your organization. Maybe you’ve already been tracking prospective client information with some sort of data management system. Now you can learn how to take the work you’ve already been doing and multiply it across your organization by making it visible, accessible, and easy to organize in CRM. On the other hand, if you’ve never engaged with any sort of customer relationship management software before, get the knowledge you need to hit the ground running. This isn’t just a lesson in button pushing; this is about teaching you how to do your job more efficiently and with a greater, long-lasting impact to your organization.

2. Increase collaboration across your organization.

Training your team on how to use CRM can build a community of collaboration at your organization. We’ve seen pushback from some sales organizations who are hesitant to share best practices or critical information, afraid they’ll divulge their “secret sauce” and lose out on future deals to other sellers. In training, you’ll walk away with the tools to communicate a message of internal collaboration that leads to internal success. We’ll discuss techniques for combatting commonly asked questions, such as, “I’ve been winning deals with my methods for years. Why should I change things up now?” Learn how to explain CRM and its role in the broader scheme of success for your organization. If done effectively, you can see your employees engaging with a tool they understand.

3. Ease the transition.

No one likes change. Adopting an entirely new system and trying to integrate new operations into your daily routine can seem daunting. By attending a training session, you will equip yourself with the foundational knowledge to thoroughly understand the system. Training also helps ease those early adoption jitters, allowing you and your team to walk away with confidence over your investment. From there, you can relay that confidence to further encourage other early adopters to get on board. More confidence means more users, which means an overall more effective system for your organization.

4. Hear real stories from real customers in the same boat.

Training sessions offer plenty of time for discussion between users. Gain a network of people outside your organization to discuss creative solutions to the challenges you might be facing internally.

Maybe you haven’t completely rolled out CRM to your organization and are looking to vet out a new partner. Engage with CRM professionals and their current clients before you make the decision to invest with a partner and/or move forward with a complete deployment for your organization. Training can be a great trial period to feel out a consulting partner and determine whether you think their CRM philosophy and methodology is what you’re looking for.

5. Learn the language.

Certain fundamental concepts around CRM can be extremely helpful to learn before you dive in. Understanding the basic terminology of CRM, how the system is structured, how you are expected to interact with the tool can be vital to starting on the right foot when it comes to not only using the new system but explaining it to others at your organization.

We see customers with grandiose goals for their CRM implementations who are disappointed when it takes longer than anticipated to get off the ground. Just when you were learning to ride a bike – training wheels are important! Taking the time to set yourself up for success with CRM can make the difference between a tool your team understands and is willing to use vs. a solution they struggle to see value in.

Convinced yet? You can read more about our upcoming training sessions here. We hope to see you there!

Topics: CRM Best Practices

Service Console in Lightning Experience

Today's blog post was written by Caitlin Pfeiffer, Principal Consultant at Sonoma Partners.

In Spring '17, Salesforce added the Service Console to the Lightning Experience. While the new Lightning Service Console is still in Beta, it's an exciting glimpse into what is to come, with a GA release anticipated for Summer '17.

The new Lightning Experience Console looks to keep the best of the existing Service Console available in Classic, including:

Workspace

The console navigation in the Lightning Experience is very similar to Classic. Clickable records open as separate tabs in the Console to increase productivity and allow Service Reps to multi-task, managing multiple interactions within the same session. Also, similar to Classic, records related to each other open up as sub-tabs, allowing you to see them within the context of the record that you originally are viewing.

Place Saver

Similar to Classic, the Service Console in the Lightning Experience will also save your place from when you were last logged into the system, so your customer service team can pick up right where they left off the next time they log into the Service Console.

Lightning Utility Bar

Classic Console Components will be replaced with the Lightning Utility Bar in the Lightning Experience. NOTE: Not all of the out-of-the-box Console Components are available in the Lightning Experience as of the Spring '17 release, but I imagine that these will continue to be added to the Lightning Utility Bar in future releases.

Pfeiffer console 1

Pfeiffer console 2

While the new Service Console in Lightning Experience is very familiar, all of the new features available in the Lightning Experience are now available in your Service Console, namely:

Lightning Page Layouts

You now can take advantage of the extremely flexible Lightning Page Layouts. This includes the out-of-the-box Lightning Components that Salesforce provides, but also any custom Lightning Component that you build or install from the AppExchange.

Pfeiffer console 3

Kanban List Views

Service Cloud users are a now going to be able to take advantage of the Kanban list views to easily manage and view their cases and work orders in a more visual summary. Users can select which field they want to use within the Kanban view to group their records. 

Pfeiffer console 4

Enhanced Notes

Another cool feature that I can see being helpful for the busy, multi-tasking service rep is the new enhanced Note functionality that allows reps to easily jot down a note without having to stop what they are doing. Notes will be available from the Lightning Utility Bar and will overlay whatever record they are working on. Users can keep these notes open while they work and expand/minimize them as they continue to work.

Pfeiffer console 5

Before rolling out the new Service Console in the Lightning Experience, keep in mind that:

  • This feature is currently only available in Beta, which means that there may still be a few known issues or missing features. Salesforce anticipates that the Lightning Service Console will be generally available in Summer '17, but may still be missing some of the features that are only available in Classic, such as Case Teams, Live Agent, and Service Contracts/Entitlements. 
  • Similar to the core Lightning Experience, Salesforce will continue to add more and more features to the Service Console in LEX with each release. If you are interested in using it, I would recommend that you keep an eye out for the release notes for each release to watch of key features critical for your service team.
  • Also similar to the core Lightning Experience, this is something that you can roll out gradually, to specific users and teams. Users are also able to switch back and forth between the Service Console in Lightning and Classic.

Have additional questions about the Salesforce Service Cloud, Sonoma can help, please contact us.

Topics: Salesforce

CRM: When Homemade Doesn't Cut It

Today's blog post was written by Kayla Silverstein, Marketing Specialist at Sonoma Partners.

There are a lot of scenarios in which homemade is best – be it chocolate chip cookies, cozy knitted socks, or an old-fashioned rocking chair. When it comes to building a CRM system to meet the complex needs of your business, a homegrown CRM system will not provide you with the business tool you crave.

SP_Homemade_CRM_blog

Customer Relationship Management tools are critical to building relationships and growing businesses. Market-leading platforms like Dynamics 365 (formerly Dynamics CRM Online) and Salesforce are market-leading for a reason – they are used by organizations of every shape and size to improve sales performance, proposal generation, marketing automation, and so much more.

Here are 3 things you’re missing out on when you choose to invest in a homegrown CRM system:

1. No updates for you.

When you invest in Salesforce or Dynamics 365, you have access to the tools, functionality, and integrations your team needs to productively operate. From forecasting to integrations, incorporating these features in your homegrown platform can come at a huge cost. The market-leading CRM platforms provide access to today’s vital features and tomorrow’s future innovations. Technology is moving faster than ever, and investing in a name brand platform ensures you can keep up.  

With a homegrown CRM system, the only updates your system is going to receive are the ones you create. By comparison, both Salesforce and Dynamics 365 release updates 2-3 times a year. With these updates, you’re getting access to new features, tried and tested for users just like you and your team. In our experience, we’ve seen about 200-500 new features per release. That’s anywhere from 400-1,500 features a year! How many features do you think, even in your slower seasons, your development team could put together? Probably not 1,500.

2. Forget about custom UX.

Investing in a thoughtful, engaging user experience design provides your team with a tool they  want to use. When you configure Dynamics 365 or Salesforce in a way that satisfies your team’s daily operations, you can present your employees with a trusted platform that uniquely meets their needs. Investing in custom UX is one way to drastically increase both user adoption and the quality of data in your CRM system.

With the steep costs of developing custom UX within a homegrown platform, you could be left with a system that your users will not adopt. With inconsistent use, the system grows stale and underutilized. When you work with Microsoft Dynamics CRM or Salesforce, you can build completely custom mobile applications, enhance simple controls, or deploy fully-branded portals. The sky is the limit!

3. Lack of support.

CRM implementations are naturally works-in-progress. Your different teams are utilizing the system for a variety of means, and odds are, you won’t be able to update every department at the same time. This is one of the reasons Salesforce and Microsoft dedicate time and money to an array of support networks for their users. As a customer, you can access crowdsourced information from blogs and partner channels, to self-help portals to get you the answers you need and fast. Support for your homegrown solution is at the mercy of whoever is around to assist. To set yourself up for success in the long term, invest money where you can get continued assistance and resources to support the road ahead.

If you have questions about how your organization can start investing in a Salesforce or Dynamics 365 solution, feel free to drop us a line.

Feel you’ve gone past the point of no return with your homegrown solution? Don’t panic. We offer a variety of resources to help get you back on course. Start with our eBook on The Trail to CRM Triage, helping you begin the 5-step trail to solve stalled or static CRM solutions.

Topics: CRM Best Practices

Power BI Online Integration with Dynamics CRM On-Premise

Today's blog post was written by Hayden Thomas, Associate Developer at Sonoma Partners.

Integrating Power BI online with Dynamics CRM On-Premise is not currently supported natively. Recently we had a need to integrate a Power BI Report with a Dynamics CRM On-Premise environment, so we needed to create a custom solution to enable embedding reports and dashboards from Power BI into CRM.

Power BI natively allows reports to be ‘Published to Web.’  Doing this would allow us to simply IFrame the report on a Dynamics form or dashboard, but this makes it accessible to anyone who may have the link which is not very secure. This is unsuitable as the reports we’re looking to embed may have sensitive data which we want to make absolutely sure no one has outside access to.

In our case, we are connecting to Power BI through Azure. Azure uses OAuth 2.0 and Active Directory services for authentication. We need to be able to store:

  1. A Client ID that represents a connection to Power BI through Azure.
  2. A Tenant ID for our Azure Active Directory.
  3. An access token that will allow us to request a report from Power BI.
  4. A refresh token that will allow us to programmatically keep our authentication alive, so that we don’t need to continuously keep putting in our username and password.
  5. The lifespan of the authentication and the date-time we obtained it, so we can check if our current authentication is still good.

The Client and Tenant ID are the same for all of the users in our org, so we simply created a configuration record to hold these values.

The other fields will either be created for every user and we need to ask the user to enter their authentication to populate these fields, or we need to create a service account that can be authenticated in the background without the user needing any information on how the reports are being displayed.

Our solution used the second option. Our reports are shared amongst a group in Power BI. In order to have everyone log in and have their own tokens, it would require them to all be added to that group (and in turn, require everyone to have a Power BI Pro license). This also allowed us to just add the authentication fields to the configuration record, along with the service account’s username and password.

Powerbi brendan 1

Our next step is to make sure we actually have an app registered to our Azure AD that we can authenticate this user against. If we log in to dev.powerbi.com/apps, we can register one directly to ensure that it’s set up correctly. In order to make sure we don’t need to handle anything with redirect URLs, since we expect to move this around to different orgs without much issue, we use Native app from the App Type drop down, and for our redirect URL we use this link. For our case, where we only want to be able to read dashboards/reports, we only give it the read all dashboards and read all reports access levels. Once done, we can click Register App to obtain the Client ID we will use for our configuration record.

We have our app created, but haven’t yet given permission from our service account to the app to be able to log the user in programmatically. In order to give permission, we wrote a LINQPad script that does nothing but connect to the Client ID of our app, and allow the user to log in to give access.

Powerbi brendan 2

Running the script will pop up a dialog to allow a user to log into the App created with the specified client ID.

Powerbi brendan 3

In order to connect and display the report, we look to the Power BI documentation on how to show a report in an IFrame. https://powerbi.microsoft.com/en-us/documentation/powerbi-developer-integrate-report-load-report-iframe/. We see that we need an embed URL and an access token. Since we need to send information to the IFrame after it’s already set, and because we want to be able to use different reports in different areas, we create an HTML web resource that’s got an Iframe in it, and set the frame contents accordingly using JavaScript.

Powerbi brendan 4

Excess code for styling and other libraries used in JavaScript removed for brevity.

The JavaScript in this page does a number of things. When the resource initially loads it parses the report ID and the group ID, in which the report is stored, from the query string. This lets us use the same web resource on the same page to load multiple reports. In the web resource properties on dynamics, we can set this report and group ID field accordingly in the custom parameters.

Powerbi brendan 5

It then triggers a custom action that takes in both of those parameters. The custom action triggers some plugin code that loads the configuration record, ensures the authentication is up to date, and then queries Power BI for the embed URL for that report.

For ensuring our authentication is up to date, we see if we have an access token or if our token has expired (based on the authentication lifespan and authentication obtained date time fields we have on our configuration). If we don’t have a refresh token, we need to use the password grant_type along with the service account username and password. (If we can refresh, we do something similar using grant_type refresh and the refresh token that we have stored in our configuration record. More details on Azure OAuth operations can be found here: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oauth-code). In this example, we deserialize into an AccessToken model class that’s described by the documentation above.

Powerbi brendan 6

With our access token, we can query Power BI for the reports which are shared with the Group ID we sent added as a parameter by doing an HTTP GET request against https://api.powerbi.com/v1.0/myorg/groups/GROUP_ID/reports with an Authorization: Bearer ACCESS_TOKEN header.

Powerbi brendan 7

This will give us a response that’s a JSON string which will be an array of all of the reports for the group. Each entry in the array will contain the report ID and the embed URL. There are additional fields, such as the display name for the report, but they’re unimportant for what we’re doing. We simply need to find the entry that has the report ID that we passed in, and return the embed URL and access token back to the web resource. Powerbi brendan 8

Once we have those fields in the client side, we can simply set the source of our Iframe to the embed URL we received, and post the access token to it.

Now we can see our Power BI report as an iFrame.  In this case we embedded as a Dashboard in Dynamics CRM.

Image5

Topics: Microsoft Dynamics 365 Microsoft Dynamics CRM

Async is Smooth, Smooth is Fast

Today's blog post was written by William "Dibbs" Dibbern, Principal Developer at Sonoma Partners.

Having worked with Dynamics 365 for Sales (CRM) for nearly seven years now, we've seen a lot of things which cannot be unseen when it comes to client-side code. Whenever we get called in to evaluate the current state of an implementation, a good gauge by which to judge the state of the union is a glance at the JavaScript. Since JavaScript as a language is so forgiving, you can end up with a lot of code that "just works" but suffers in the areas of durability, maintainability, and performance.

What's the number one offense we see? Synchronous service calls.

When code degrades the end user experience, it shoots straight to the top of our "fix it now" list.

What's so bad about synchronous service calls?

Synchronous service calls have been prooven to be detrimental to the user experience. The browser literally stops everything to wait for the result of that call. When the browser is locked up waiting for the result of a synchronous service call, the user can't click anywhere else, enter any other information, cancel the operation, nor see any updates. Accordingly, users can't be given a glimpse into any synchronous operation's progress. In certain browsers, like Google Chrome, these types of synchronous requests are even becoming deprecated so you won't be able to do them much longer.

How do we fix this on forms?

Synchronous requests from the form are fairly straight forward to transition to asynchronous calls. Either continue processing the result in a callback function, or even better, you can implement promises for a cleaner way of dealing with multiple service calls. This does require a bit of thinking, but after a few refactories it quickly becomes second nature. Hastily written code almost always has a side effect, a few minutes saved coding could cost users hours of productivity in the long run.

Below is a contrived example where we retrieve the full name of the primary contact for the current record's parent account. Phew that's a mouthful. Long story short: let's retrieve a value from a related record. This example is designed as such purely to demonstrate the difference when multiple service calls are required, so yes, you can actually retrieve the required information in one service call. Also note that I've abstracted away all of the underlying XMLHttpRequest code, expecting that you are using a library to wrap that as well (though hopefully not jQuery, but that's another subject for another post).

  /* Bad */
  function onParentAccountChanged(parentAccountId) {
  var parentAccount,
  primaryContact;
   
  if (!parentAccountId) {
  return;
  }
   
  parentAccount = WebAPI.get('/accounts?$filter=accountid eq ' + parentAccountId);
   
  if (!parentAccount || !parentAccount._primarycontactid_value) {
  return;
  }
   
  primaryContact = WebAPI.get('/contacts?$filter=contactid eq ' + parentAccount._primarycontactid_value);
   
  if (primaryContact && primaryContact.fullname) {
  Xrm.Utility.alertDialog(primaryContact.fullname);
  }
  }
   
  /* Good (using callbacks) */
  function onParentAccountChanged(parentAccountId) {
  if (!parentAccountId) {
  return;
  }
   
  WebAPI.get('/accounts?$filter=accountid eq ' + parentAccountId,
  function onSuccess(parentAccount) {
  WebAPI.get('/contacts?$filter=contactid eq ' + parentAccount._primarycontactid_value,
  function onSuccess(primaryContact) {
  if (primaryContact && primaryContact.fullname) {
  Xrm.Utility.alertDialog(primaryContact.fullname);
  }
  },
  function onError(e) { /* handle error */ });
  },
  function onError(e) { /* handle error */ });
  }
   
  /* Good (using promises) */
  function onParentAccountChanged(parentAccountId) {
  if (!parentAccountId) {
  return;
  }
   
  WebAPI.get('/accounts?$filter=accountid eq ' + parentAccountId)
  .then(function onSuccess(parentAccount) {
  return WebAPI.get('/contacts?$filter=contactid eq ' + parentAccount._primarycontactid_value);
  })
  .then(function onSuccess(primaryContact) {
  if (primaryContact && primaryContact.fullname) {
  Xrm.Utility.alertDialog(primaryContact.fullname);
  }
  })
  .catch(function onError(e) { /* handle error */ });
  }

 

Did you notice how with the last good example, using promises, that the flow is actually very similar to how you would do things synchronously? That's one of many reasons why we love promises.

Did you also notice how the code in the callbacks example, with all the indentation, starts to look like a pyramid? That's one of the many reasons why we don't like callback as much as promises. You could flatten that out by pulling out the callback functions and defining them alongside onParentAccountChanged but let's be honest here, that doesn't usually happen until it's too late.

What about the command bar (ribbon)?

Ok. Let's address the tricky bit: custom enable rules. You might think you need your code to immediately return the result of your service call so that Dynamics knows to show the button as enabled or disabled, but this is not the case. You can return a smart default (usually default to disabled), do your service calls to determine what the actual state of it should be, and then refresh the ribbon (by invoking Xrm.Page.ui.refreshRibbon()) such that it presents that newly determined state. A bit steppy, with a potential for a flash of disabled -> enabled or vice versa, but overall a better experience than having the form lockup.

Looking for an example? Have a look below. The following example checks if the parent account's primary contact field is set:

  /* Bad */
  function isPrimaryContactSet(accountId) {
  var account;
   
  if (!accountId) {
  throw new Error('`accountId` is a required parameter for `isPrimaryContactSet`');
  }
   
  account = WebAPI.get('/accounts?$filter=accountid eq ' + accountId);
   
  if (account._primarycontactid_value) {
  return true;
  }
   
  return false;
  }
   
  /* Good */
  var isPrimaryContactCheckComplete = false,
  isPrimaryContactSetResult;
   
  function isPrimaryContactSet(accountId) {
  if (!accountId) {
  throw new Error('`accountId` is a required parameter for `isPrimaryContactSet`');
  }
   
  if (isPrimaryContactCheckComplete) {
  return isPrimaryContactSetResult;
  }
   
  WebAPI.get('/accounts?$filter=accountid eq ' + accountId)
  .then(function onSuccess(parentAccount) {
  if (parentAccount._primarycontactid_value) {
  isPrimaryContactSetResult = true;
  }
  else {
  isPrimaryContactSetResult = false;
  }
   
  Xrm.Page.ui.refreshRibbon();
  })
  .catch(function onError(e) { /* handle error */ });
   
  return false;
  }

 

Let's take note of a few key differences found in the "good" example:

  1. The service call is only ever run once as the result is cached.
  2. We default the button to disabled as the first time it hasn't run so we end up returning false.
  3. The variables storing the state of the call are stored outside the function being invoked. I would expect those variables not to be global variables however, they should instead be local to a parent scope that is not the global scope. This will help avoid conflicts.

A Special Note on Progress Indicators

I'm guessing by now you may have had the thought that since we're not locking up the browser any more, the user is now free to double click buttons and duplicate actions. I believe there are certain time frames in which that is fine. As long as a user sees a relatively instanteous response of any positive form, they are not very likely to anger click again and again.

However, if you do have a longer running request being executed, you'll probably need to introduce some form of progress notification in order to keep the user informed. For example, when you have a ribbon button that processes a bunch of records, perhaps have the button pop open a dialog which would display a progress indicator, and let the actual operation run inside the dialog's code. This way the user sees something "productive" happening immediately.

What's the cut off that determines if you need a progress indicator? The Nielsen Norman Group provides a good rule of thumb that if the operation is going to take longer than 10 seconds, you should provide a progress indicator, and consider displaying detail of changes in progress if possible. If the operation averages between 2 and 10 seconds, the recommmendation is to provide an indeterminate progress indicator.

In Summary

You should (almost) never be using synchronous service calls. 99.99% of the time you can, with a little bit more elbow grease and human processing power, accomplish the same exact thing with the application of an asynchronous pattern.

Topics: Microsoft Dynamics 365