Tuesday, June 8, 2021

Dynamics Basics # 1: Microsoft Dynamics 365 Trial Instance

Hello Everyone!

I am going to start new series for Dynamics 365 basics, which will help others for getting started with Dynamics 365 learnings.

Today, we are going start with creating Microsoft Dynamics 365/Power Platform Trial Instance.

Step 1: Browse the link, https://trials.dynamics.com
Step 2: Click on Sing up here.


Step 3: In Sign In window, click on No, continue signing up.


Step 4: Once you navigate to set up account page, provide you work or personal email address to setup the account for trial and click Next.


Step 5: Click on Set up account.


Step 6: Then, you need to provide your personal information and click on Next.


Step 7: Next you need to provide your phone number for OTP verification from Microsoft. 
You can choose either Text me or Call me.
Click on Send Verification Code.
Provide the Verification Code received on your mobile and click on Verify.

Note: If you choose Text me, you will receive SMS on your mobile with OTP, and if you choose Call me, then you will get a call from Microsoft and the OTP will provided over IVR.


Step 8: Next you need to provide unique name for your business in yourbuisness field for you trial instance, and if the name you provided is unique it will show success message as available, click on Next.

Step 9: In this step, you need to provide Username & Password, using which you are going to access your trial instance, click on Sign up.



Step 10: Once sign up is done, you will get message as You 're all set, click on Get Started.

Note: Please make a note of username, using which you are going to login to your trial instance.


Step 11: Once your setup is completed, you will be navigated to Power Platform admin center with your default environment gets created. Now, you need to create new instance/environment. 

Provide below details, click on Next.

Name: Provide the name for your CRM instance.

Type: Trial (subscription-based)

Region: Here you can provide the region from where you're logged in (say India)

Purpose: Provide the description for your instance. (optional)


Step 12: In the next screen, you need provide some basic details as given below, click Save.

Language: English (default language for your instance)

URL: Provide a unique keyword for browsing trial instance.

Currency: Select your country currency.


Step 13: After you click on Save as mentioned in above step 12, it will take some time to prepare your instance. Once the instance is ready the state will be changed to Ready.


Step 14: Select your newly created instance and click on Open environment, it will navigate you to Dynamics 365 CE app selection phase.


Hope you have followed all the steps and got your instance ready with you. Please let me know if you find any difficulty while creating instance. I have also created a YouTube video on creating instance, you can refer it below;

Happy Learning!!

Wednesday, April 1, 2020

Broadcast Announcements of Dynamics 365 in Unified Service Desk

Hello Friends! In my previous two blog posts (Working on Announcements in Dynamics 365 & Announcements using PowerApps) I walked you through about working on announcements using web-resource or by creating PowerApps control in Dynamics 365.

Today we'll see broadcasting announcements in Unified Service Desk. Before getting started I assume you have basic knowledge of what is USD, what are hosted controls, action calls, events, toolbar, etc. in USD.

Let's get started..
Step 1: Create a hosted control to show announcements in Unified Service Desk.
  • Component Type : Standard Web Application
  • Hosting Type : Chrome Process
  • Application is Global : Yes
  • Display Group : RightPanel

Step 2: Create Actions to navigate to Announcements.

As I have created two controls for announcements one is using html web-resource and another using PowerApps, here I'll create two actions either to navigate to web-resource or to PowerApps.

Actions which I am going to create will be associated with hosted control for announcements which I've created in Step 1. Navigate action for web-resource contains the url of html web-resource which I've created in this post.


 Navigate action for PowerApps control contains the url of canvas app which I've created in this post.


Step 3: Trigger the navigate action for PowerApps as a default control when USD desktop loads.

To display the announcements when USD loads we need to add the action to the DesktopReady event of CRM Global Manager.

We are ready to go and test our changes in USD, but I have created 2 different actions for 2 different controls (web-resource and PowerApps) so I'll add navigation buttons on About Toolbar of USD to navigate to any of the control.

Steps:
  • Create Announcements Toolbar button
    • Add Announcement Toolbar Button on About Toolbar.
    • Add actions for navigation and expanding Right Panel on USD.

  • Create two toolbar buttons
    • Create PowerApps Control Toolbar button and add actions for navigation & expanding Right Panel to it.
    • Create Web Control Toolbar button and add actions for navigation & expanding Right Panel to it.
    • Associate this toolbar buttons to Announcements Toolbar button.


Finally, it's time to test our changes in USD. Let's see how to renders on Unified Service Desk.


Hope you understand the steps, feel free to connect me if you need any help.

Happy Learning..!


Thursday, March 26, 2020

Announcement entity in Dynamics 365 on PowerApps

Hello Friends!! In my previous Blog post I walked you through about working on Announcement entity in Dynamics CRM.
In this post we'll learn about creating PowerApps application to showcase Organizational Announcement on user's mobile, tablet, etc. or even host the application in unified service desk so that all users get to know the latest news or information of an organization.

Let's get started..

Creating PowerApps for Announcement


  • Navigate to PowerApps online IDE.
  • Go to Data > Connections > Create New Connection (Dynamics 365)

  • Now, go to Create > Start from data (other data sources).
  • Next, select Dynamics 365 connection which we have created above, then select the dataset (your organization) and finally select the table (entity) i.e. Announcements and click on Connect.


  • As we need Announcements App only for displaying the news or information of an organization and restrict users to edit the details, we'll delete the edit screen as it will be no longer required. 


  • Once we delete the edit screen, the control (+) available on main screen start showing errors which we then need to resolve. Remove the below highlighted button control and command associated with it.

  • After removing the control (+) from the browse screen the header label of the screen shows error message which we need to resolve from all the respective commands. Similarly we need to remove other two controls (Refresh & Sorting icons) and resolve the commands accordingly.


  • Similarly we need to remove controls (Delete & Edit icons) from detail screen and remove the reference of icons from all the respective commands.

 

After resolving all the errors (use App Checker icon available at right upper corner of the IDE to check all the errors and warnings if any) and completing UI changes it's time to publish and test the app.
  • To publish the app, Save the app and then click on Publish.

  • To test the app on your mobile or tablet, please download Power Apps application from 'Play Store' or 'App Store' and connect to your Dynamics 365 online environment and then you can see the published app.



Here we learn how to create simple PowerApps application and use it on our mobile device.

In next blog we'll learn to host this PowerApps application in Unified Service Desk so that the organization can broadcast their important information quickly at contact centers.

Happy Learning..!

Working on Announcement Entity in Dynamics 365

Sometimes customer occasionally ask for the capability of Dynamics CRM to broadcast important news or information of an organization to a wide set of users at one go so that all users get to know latest announcements without fail.

Using Announcements in Microsoft Dynamics 365 you can circulate important information quickly to a wide set of users at one go. Announcements can also server as message boards, where you can post topics of your interest that you wish to share or get answers to.

Let's get started on how to create Announcements in Dynamics CRM.

  • Make sure that you have the System Administrator or System Customizer security role or equivalent permissions.
  • Go to Settings > System.
  • Choose Administration > Announcements.
  • On the command bar choose New.
  • Fill in the information, as required.
    • Title - Type a title for the announcement that clearly and unambiguously states the purpose and nature of the announcement.
    • Body - Type the text for the announcement that you want to broadcast.
    • More Information URL - Type the address of the website that provides detailed information about the announcement. (Optional)
    • Expiration Date - Type the date on which you want to stop the broadcast and the announcement should expire.
  • Save or Save and Close to begin the broadcast.


Now, we need to broadcast the newly created announcement to other users in our organization by using HTML web-resource and dashboard or by creating PowerApps hosted in Unified Service Desk.

HTML web-resource in Dashboard
  • Go to Settings > Customizations > Customize the System.
  • Under Components, choose Web Resources > New.
  • Create new HTML web-resource.

  • In a text editor, type following code snippets.


<html>
  <head>
 <meta charset="utf-8">
  <title>Announcements</title>
  <script src="ClientGlobalContext.js.aspx"></script>
  <script type="text/javascript">
       function LoadPage(){
     window.location.href = "/home/homepage/home_news.aspx?pagemode=iframe";
    }
  </script>
 <meta>
  </head>
  <body style="margin: 0px; overflow-wrap: break-word;" onload="LoadPage()">
  </body>
</html>

  • Save and Publish the changes.
  • Add this new web-resource to any existing or new Dashboard.

As per my own experience, Yammer is the far better alternative in Dynamics CRM in place of Announcements.

In this blog post you will find how to create Announcements viewer using PowerApps.

Note: Announcements (Settings > Administration > Announcements) are deprecated and will be removed in a future major release (More Info: link)

Wednesday, March 25, 2020

Hierarchy Visualization in Dynamics 365


Dynamics CRM features a new way to visualize relationships that exists between connected records.
Hierarchy Visualizations provide a clear display of these structures by helping CRM users to navigate and understand each level.

CRM has long enabled users to define parent and child account relationships but these lacked any inbuilt views that provide a graphical overlay. From CRM 2015, Dynamics enables tree view structure to be easily configured between parent and child records.

In one of my previous Dynamics CRM implementation, customer were looking for hierarchy visualizations of Parent Contact of an Account on Account form.

Before going to start actual implementation, we need to first understand the limitations of Hierarchy Visualizations.

  • Hierarchy functions and visualizations are currently only supported within a single entity (self referential) on a 1:N basis.
  • Each hierarchy member must be of the same type, example for a Contact entity, a CRM hierarchy can involve multiple Contacts but it is not possible to bring an Account, or any other related CRM entity into hierarchy structure.
  • The CRM hierarchy view will only fit a maximum of 4 tiles horizontally into the view, and these are shown in alphabetical order by default.
  • If a hierarchy level has more than 4 records then the view will show the number of additional records either side of the present window enabling users to scroll left or right to browse other tiles.

Let's get started with implementation part;

  • Go to Customizations > Customize the System > Contact Entity (expand) > Fields > New.
  • Create a new self-referential lookup field named 'Parent Contact' or ' Manager' and place it on the Contact main form.

  • Go to Customizations Customize the System > Contact Entity (expand) > Forms New
  • Create a new Quick View Form to show as Contact Tile.
  • Now, go to Hierarchy Settings of Contact entity and create new settings as below.
    • Name - Type the name for your hierarchy settings in my case 'new_hierarchy_contact'.
    • Default Quick View Form - Select quick view form which you have created as above.
    • Hierarchical Relationship - We previously created a self-referential field called 'Parent Contact' or 'Manager' as created above that will look-up other records specific to contact entity so this has been selected for the hierarchy relationship setting.
  • To ensure the relationship is enabled for hierarchies click the link 'Mark a relationship as enabled for hierarchies', select the self-referential field and click on 'Mark hierarchical' and Done.
  • Publish the customization.

Now, its time to test the changes:
On the Contact form we've added a new self-referential field named 'Manager' that will determine the hierarchy structure between parent and child records.


For the above contact record, Thomas Andersen (sample) have manager set as Jim Glynn (sample).
Only one parent can be set so in this case no additional manager can be defined for this contact. However, as this is a 1:N relationship multiple child contacts can have same parent contact or manager in the system as below;


An existing hierarchy visualization can be opened from any record by clicking 'View Hierarchy' ribbon button or from view.


Now, we are able to see the contact hierarchy available in the CRM. But, as I said earlier the actual requirement was to show this contact hierarchy at Account level or say on Account form.

On Account form, we will be having Primary Contact and associate contacts as below;


To give the contact hierarchy visualization on Account form, we will add new web-resource and pass primary contact id to it and show the hierarchy in section on the form.
  • Go to Customizations Customize the System.
  • Under Components, select Web Resources > New.
  • Create new HTML web-resource and add below given code snippets;

<html>
  <head>
 <meta charset="utf-8">
 <style>
   html, body{
    height:100%;
    font-family: Arial, Helvetica, sans-serif;
     } 
    .button {
    background-color: #161515;
    border: none;
    color: #fff;
    font-weight:600;
    padding: 1% 1%;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 12px;
    margin: 4px 2px;
    cursor: pointer;
    border: none;
    border-radius: 10px;
   }
      .button:hover {background-color: #5C5959}
   .button:focus {box-shadow:none;}
   * {box-sizing: border-box;}

   .header {
     overflow: hidden;
  background-color: #f1f1f1;
  padding: 1% 1%;
    }
       .header a {
        float: left;
  color: black;
  text-align: center;
  padding: 12px;
  text-decoration: none;
  font-size: 18px; 
  line-height: 25px;
  border-radius: 4px;
    }
    .header-right {
  float: none;
    }
 </style>
 <script type="text/javascript">
   function loadiFrame() {
  var parentContact = window.parent.Xrm.Page.data.entity.attributes.get('primarycontactid').getValue();
  if(parentContact != null){
    var parentContactId = parentContact[0].id.replace("{", "").replace("}", "");
    var srcVal = "https://<OrganizationName>.crm11.dynamics.com/_root/hierarchy.aspx?oId=%7b"+parentContactId+"%7d&etc=2&sitemappath=SFA%7cCustomers%7cnav_conts&pagemode=iframe";
    document.getElementById("contactFrame").src = srcVal;
  }
   }
 </script>
 <meta></head>
 <body onfocusout="parent.setEmailRange();" style="overflow-wrap: break-word;">
   <div>
  <div class="header">
    <a href="#default" class="logo">Parent Contact Hierarchy</a>
   <div class="header-right">
     <button class="button" onclick="loadiFrame()">Generate Hierarchy</button>
   </div>
  </div>
  <iframe id="contactFrame" src="" name="Parent Contact hierarchy" scrolling="auto" width="100%" height="100%" frameborder="0"></iframe>
   </div>
 </body>
</html>

In above code we can see that we have placed one button control 'Generate Hierarchy' and on click of that we are loading iFrame which calls below URL by passing Primary Contact id which we have fetched in JavaScript function.


https://<OrganizationName>.crm11.dynamics.com/_root/hierarchy.aspx?oId=%7b"+parentContactId+"%7d&etc=2&sitemappath=SFA%7cCustomers%7cnav_conts&pagemode=iframe"
  • Go to Customizations Customize the System.
  • Under Components, select Entity Account > Forms > Account Form.
  • Insert new tab on the form and add web-resource which we have created above.
  • Publish the customization.
Now, finally its time to check contact hierarchy visualization on account form.


Hope it helps you to understand the hierarchy visualization and to play around it.

Happy learning!!

Friday, November 29, 2019

Google Maps Integration with Microsoft Dynamics CRM


As we all know Microsoft Dynamics CRM by default is integrated with Bing Maps. We can add Bing maps on any entity we want. We can enable or disable the feature in the Settings > Administration > System Settings area.


This post shows you how to integrate Google maps API with Dynamics CRM. Here we will integrate Google maps in Account entity form using HTML web resource and we create a Dashboard where we will show all the accounts of CRM on Google Map.
Before you start, you need Google Map API key to proceed further.
Getting an API key is not complicated and just requires a couple of minutes of your time. In the process, you will need to create a billing account or use credentials of already existing one, which will be used for payment, in case you exceed your limits. This link will guide you on how to get Google Maps API key in 3 easy steps. 
I assume you have basic Dynamics CRM and JavaScript knowledge to perform below guided steps.
Step 1: In order to integrate Google Maps, first we create a HTML web resource.

Code snippets:
<html>
 <head>
  <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initialize" defer="" async=""></script>
  <script>
   function initialize() {
    var map_canvas = document.getElementById('map_canvas');
    var map_options = {
      center: new google.maps.LatLng(54.737686, 2.126855),
      zoom: 4,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }
 
    var map = new google.maps.Map(map_canvas, map_options)
    var geocoder = new google.maps.Geocoder();
    var address = window.parent.Xrm.Page.data.entity.attributes.get('address1_composite').getValue();
    var googleLocation = window.parent.Xrm.Page.data.entity.attributes.get('new_googlelocation').getValue();
    var addressChanged = window.parent.Xrm.Page.data.entity.attributes.get('address1_composite').getIsDirty() ;
    var name = window.parent.Xrm.Page.data.entity.attributes.get('name').getValue();
    var contentString = '<div><strong>' + name + '</strong><br>' + address + '<br></div>';
    geocoder.geocode( { 'address': address}, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        if(googleLocation == null){
          var latitude = results[0].geometry.location.lat();
          var longitude = results[0].geometry.location.lng();
          var loc = latitude + ',' + longitude;
          window.parent.Xrm.Page.getAttribute("new_googlelocation").setValue(loc);
          window.parent.Xrm.Page.data.entity.save();
        }
        map.setCenter(results[0].geometry.location);
        map.setZoom(14);
        var marker = new google.maps.Marker({
          map: map,
          position: results[0].geometry.location,
          title: address
        });
        
  var infowindow = new google.maps.InfoWindow({
          content:contentString
        });
        google.maps.event.addListener(marker, 'click', function() {
          infowindow.open(map,marker);
        });
      } else {
        //alert("Geocode was not successful for the following reason: " + status);
      }
    });
   }
   google.maps.event.addDomListener(window, 'load', initialize);
  </script>
  <meta charset="utf-8"><meta><meta><meta>
 </head>
 <body style="overflow-wrap: break-word;" dir="LTR" onfocusout="parent.setEmailRange();" lang="en-US">
   <div id="map_canvas" style="width: 100%; height: 100%;"></div>
 </body>
</html>

Understanding the code:

In the code below, the script loads the API from the specified URL.the callback parameter executes the initialize function after the API loads. The async attribute allow the browser to continue rendering the rest of your page while the API loads. The key parameter contains your API key.

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initialize" defer="" async=""></script>

The code contains in the initialize function that initializes and adds the map when the web page loads. The code below constructs a new Google maps object, and adds properties to the map including the center and zoom level. As I am working for one of the client from United Kingdom, so I choose latitude and longitude location of UK.

var map_options = {
      center: new google.maps.LatLng(54.737686, 2.126855),
      zoom: 4,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    var map = new google.maps.Map(map_canvas, map_options)

Next, we will retrieve composite address of an account record and then using Geocoding we will find out geographic coordinates. Geocoding is the process of converting addresses into geographic coordinates, which can be used to place markers on a map, or position the map. 

Using Geocoding we will get the latitude and longitude for address of an Account record, we will save this details in another custom field called "new_googlelocation"  which is of single line of text. This field value will be useful for us to put marker on Dashboard's map.

var googleLocation = window.parent.Xrm.Page.data.entity.attributes.get('new_googlelocation').getValue();
var addressChanged = window.parent.Xrm.Page.data.entity.attributes.get('address1_composite').getIsDirty();
if(googleLocation == null){
  var latitude = results[0].geometry.location.lat();
  var longitude = results[0].geometry.location.lng();
  var loc = latitude + ',' + longitude;
  window.parent.Xrm.Page.getAttribute("new_googlelocation").setValue(loc);
  window.parent.Xrm.Page.data.entity.save();
}


And using an InfoWindow method we will show location on google map. An InfoWindow displays content in a popup window above the map, at a given location.

geocoder.geocode( { 'address': address}, function(results, status) {
  if (status == google.maps.GeocoderStatus.OK) {
    if(googleLocation == null){
      var latitude = results[0].geometry.location.lat();
      var longitude = results[0].geometry.location.lng();
      var loc = latitude + ',' + longitude;
      window.parent.Xrm.Page.getAttribute("new_googlelocation").setValue(loc);
      window.parent.Xrm.Page.data.entity.save();
    }
    map.setCenter(results[0].geometry.location);
    map.setZoom(14);
    var marker = new google.maps.Marker({
      map: map,
      position: results[0].geometry.location,
      title: address
    });
        
 var infowindow = new google.maps.InfoWindow({
      content:contentString
    });
    google.maps.event.addListener(marker, 'click', function() {
      infowindow.open(map,marker);
    });
  } else {
    //alert("Geocode was not successful for the following reason: " + status);
  }
});

Step 2: Next, we need to add google map on Account form.



Step 3: We have integrated google maps with our Dynamics environment, next we need to show all the accounts on Dashboard using Google Map. For this, we will create another html web-resource for dashboard.


Code snippets:

<html>
  <head>
 <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY;callback=initMap" defer="" async=""></script> <script type="text/javascript">
   function initMap() {
     var map;
  var bounds = new google.maps.LatLngBounds();
  var mapOptions = {
    center: new google.maps.LatLng(54.5260, 15.2551),
    zoom: 4,
         mapTypeId: google.maps.MapTypeId.ROADMAP
  }; 
        
  // Display a google map on the web page
  map = new google.maps.Map(document.getElementById("mapCanvas"), mapOptions);
  map.setTilt(50);
  
  //Show multiple location markers on the google map  
  var infoPopupWindow = new google.maps.InfoWindow(), marker, i;
   
  var req = new XMLHttpRequest();
    
  req.open("GET", parent.Xrm.Page.context.getClientUrl() + "/api/data/v9.1/accounts?$select=name,address1_composite,new_googlelocation&$filter=_ownerid_value eq 106E5F09-48CC-4BF0-BF21-FD7C074991F8", false);
  req.setRequestHeader("OData-MaxVersion", "4.0");
  req.setRequestHeader("OData-Version", "4.0");
  req.setRequestHeader("Accept", "application/json");
  req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
  req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
  req.setRequestHeader("Prefer", "odata.maxpagesize=10");
  req.onreadystatechange = function () {
    if (this.readyState === 4) {
      req.onreadystatechange = null;
   if (this.status === 200) {
     var results = JSON.parse(this.response);
     for (var i = 0; i < results.value.length; i++) {
       var address = "";
    var name = "";
    var contentString ="";
    var location = "";
    var latitude = "";
    var longitude = "";
    address = results.value[i]["address1_composite"];
    name = results.value[i]["name"];
    location = results.value[i]["new_googlelocation"];
    latitude = location.substring(0, location.indexOf(",")).trim();
    longitude = location.substring(location.indexOf(",")+1, location.len).trim();
    if(address != null && name != null){
             contentString = '<div><strong>' + name + '</strong><br>' + address + '<br></div>';
    }
       
    var position = new google.maps.LatLng(latitude, longitude);
    bounds.extend(position);
    marker = new google.maps.Marker({
      position: position,
      map: map,
      title: name
    });
        
    //Attach click event to the marker.
    (function (marker, contentString) {
      google.maps.event.addListener(marker, "click", function (e) {
        //Wrap the content inside an HTML DIV in order to set height and width of InfoWindow.
        infoPopupWindow.setContent(contentString);
     infoPopupWindow.open(map, marker);
      });
    })(marker, contentString);
        
    //Fitting Automatically center the screen
    map.fitBounds(bounds);      
        }
     // Customize google map zoom level once fitBounds run
     var boundsListener = google.maps.event.addListener((map), '', function(event) {
       this.setZoom(4);
       google.maps.event.removeListener(boundsListener);
     });
   }       
   else {
     alert(this.statusText);
   }
    }
  };
  req.send(); 
   }
   google.maps.event.addDomListener(window, 'load', initMap); 
     
 </script>
 <meta><meta>
  </head>
  <body style="overflow-wrap: break-word;" onfocusout="parent.setEmailRange();">
 <div id="mapCanvas" style="width: 100%; height: 100%;"></div> 
  </body>
</html>


Understanding the code:

We will retrieve all the active account records from organization and using for loop we will mark the geographic location on the map and attach click event to each marker InfoWindow, so that on click of each marker on map it will show Account Name and Address of an account in InfoWindow.

Step 4: Final step, we will create dashboard and add HTML web-resource created for Dashboard.



Alright, we have completed all the customization, its time to see changes on Dashboard.


Hope it helps someone, Cheers !! Happy Learning..