Wednesday 22 April 2020

Executing Workflows using JavaScript / C# in Dynamics 365

Workflows are always robust choice in Dynamics 365 to execute fail safe processes. In OOB functionality you can schedule them on some event or can run on-demand. Good news is that you can trigger them using C# or JavaScript as well, be it some plugins, custom workflow activities, client side scripting or Azure/Windows job.

Calling Workflow using C#

ExecuteWorkflowRequest is here to rescue to trigger workflows using C# or any other .Net language.
var executeWorkflowRequest = new ExecuteWorkflowRequest()
{
  WorkflowId = Guid.Parse("24dc5603-a117-4221-a7bb-5b6ed17a1810"), // Guid of workflow
  EntityId = Guid.Parse("B0A19CDD-88DF-E311-B8E5-6C3BE5A8B200") // Guid of record
};            
var executeWorkflowResponse = (ExecuteWorkflowResponse)orgService.Execute(executeWorkflowRequest);
To test the above snippet, you can feel free to use Dynamics 365 Console Caller

Calling Workflow using JavaScript Ajax

ExecuteWorkflow Action let us trigger a Workflow using WebApi, which is a Bound Acton so we have to pass guid as first parameter in URI. Our request should be in blow format

HTTP Request Format

POST[Organization URI]/api/data/v9.0/workflows(<Workflow Guid>)/Microsoft.Dynamics.CRM.ExecuteWorkflow HTTP/ 1.1
Accept: application/json
Content-Type: application/json;charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0

{
  "EntityId": "<Entity Record Guid>"
}

Making Request using XmlHttpRequest

var clientUrl = Xrm.Page.context.getClientUrl();
var workflowId = "24dc5603-a117-4221-a7bb-5b6ed17a1810";
var entityId = "B0A19CDD-88DF-E311-B8E5-6C3BE5A8B200";

var requestUri = clientUrl + "/api/data/v9.0/workflows(" + workflowId + ")/Microsoft.Dynamics.CRM.ExecuteWorkflow";

var xhr = new XMLHttpRequest();
xhr.open("POST", requestUri, true);
xhr.setRequestHeader("Accept", "application/json");
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.setRequestHeader("OData-MaxVersion", "4.0");
xhr.setRequestHeader("OData-Version", "4.0");
xhr.onreadystatechange = function () {
    if (this.readyState == 4) {
        xhr.onreadystatechange = null;
        if (this.status == 200) {
            var result = JSON.parse(this.response);
        } else {
            var error = JSON.parse(this.response).error;
        }
    }
};
xhr.send("{\"EntityId\":\"" + entityId + "\"}");

Making Request in modern way using Fetch

var clientUrl = Xrm.Page.context.getClientUrl();
var workflowId = "24dc5603-a117-4221-a7bb-5b6ed17a1810";
var entityId = "B0A19CDD-88DF-E311-B8E5-6C3BE5A8B200";

fetch(
    clientUrl + "/api/data/v9.0/workflows(" + workflowId + ")/Microsoft.Dynamics.CRM.ExecuteWorkflow",
    {
        body: "{\"EntityId\":\"" + entityId + "\"}",
        credentials: "same-origin",
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json; charset=utf-8",
            "OData-MaxVersion": "4.0",
            "OData-Version": "4.0"
        },
        method: "POST"
    })
    .then(response => console.log("Success:", response))
    .catch(error => console.error("Error:", error));

Alright, but where can I get my Workflow GUID?

Execute below fetchXml using FetchXml Tester Online after replacing your Workflow name and grab GUID from response JSON
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="workflow">
    <filter type="and">
      <condition attribute="name" operator="eq" value="Enter Workflow Name Here!" />
    </filter>
  </entity>
</fetch>
Hope it helps šŸ˜Š

Saturday 11 April 2020

Microsoft Dynamics Ecosystem- Dynamics 365

Microsoft Dynamics Ecosystem- Dynamics 365



Microsoft Dynamics 365/ Finance and operations and other solutions provided by Microsoft.The List of various ERP Solutions in Microsoft is given below.



2: Microsoft Dynamics CRM or Dynamics 365.Most of the organisation use Dynamics 365 Sales.


3: Modular Solutions : Other solution for various type of industries, Along with the office product and outlook integrations.


4: Microsoft brings in the AI solution Combined with the CDS , Providing insights on the customers data.




5: Mixed Reality Solutions: Generally for collaboration and focus on specific product feature as given below.



6: Dynamics Services.



Overall Platform overerview.



Tuesday 7 April 2020

Connecting Dynamics 365 Web api using external HTML page

Connecting Dynamics 365 Web api using external HTML page

Here we are discussing on how we can connect Dynamics 365 Web Api using external application. Below are the steps we need to follow in order to achieve our goal.
Note: if the HTML page is not giving any result or not able to load browser cache (CRM user token) then your need to clear Brower cache and try to load HTML again.



 1.       Login in to https://portal.azure.com
2.       Provide your CRM username and password to log in into azure. Once you login create an application as below screen shot
Azure Active DirectoryĆ App RegistrationĆ Click on New application registration

3.       Provide Name, select Application Type as “Web App / API” and provide Sing –on URL as CRM URL as below.



4.       Open the newly created application and click on Settings.

5.       Click on Keys and provide “Description” as key name and “Expires” as Never expires. Once you click on Save it generates a key. make sure you copy the key otherwise it will be hidden for the next time.



6.       Open the same application and click on settings. Click on Reply URL’s and provide the complete URL from where you are calling (your client application URL).




7.       Copy the Appicaiton ID/Client ID, your application URL paste in html code.


    After every set up done copy below HTML code and try to run on your browser.

<!DOCTYPE html>
<html>
<head>
 <title>Simple SPA</title>
 <meta charset="utf-8" />
 <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.min.js"></script>

 <!--<script src="adal.js"></script>-->
 <script>
  "use strict";
  //Set these variables to match your environment
  var organizationURI = "https://agsfstrainings.crm8.dynamics.com"; //The URL to connect to CRM Online
  var tenant = "agsfstrainings.onmicrosoft.com"; //The name of the Azure AD organization you use
  var clientId = "c58b9636-71ea-4588-be48-39538d6488a1"; //The ClientId you got when you registered the application
  var pageUrl = "http://localhost:60347//HtmlPage1.html"; //The URL of this page in your development environment when debugging.
  var user, authContext, message, errorMessage, loginButton, logoutButton, getAccountsButton, accountsTable, accountsTableBody;
  var accountsQuery = "/api/data/v8.0/accounts?$select=name,address1_city&$top=10";
  var apiVersion = "/api/data/v8.0/";
  //Configuration data for AuthenticationContext
  var endpoints = {
   orgUri: organizationURI
  };
  window.config = {
   tenant: tenant,
   clientId: clientId,
   postLogoutRedirectUri: pageUrl,
   endpoints: endpoints,
   cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
  };
  document.onreadystatechange = function () {

   if (document.readyState == "complete") {
    //Set DOM elements referenced by scripts
    message = document.getElementById("message");
    errorMessage = document.getElementById("errorMessage");
    loginButton = document.getElementById("login");
    logoutButton = document.getElementById("logout");
    //getAccountsButton = document.getElementById("getAccounts");
    accountsTable = document.getElementById("accountsTable");
    accountsTableBody = document.getElementById("accountsTableBody");
    //Event handlers on DOM elements
    loginButton.addEventListener("click", login);
    logoutButton.addEventListener("click", logout);
    //getAccountsButton.addEventListener("click", getAccounts);
    //call authentication function
    authenticate();
    if (user) {

     loginButton.style.display = "none";
     logoutButton.style.display = "block";
     //getAccountsButton.style.display = "block";
     var helloMessage = document.createElement("p");
     helloMessage.textContent = "Hello " + user.profile.name;
     message.appendChild(helloMessage)
    }
    else {
     loginButton.style.display = "block";
     logoutButton.style.display = "none";
     //getAccountsButton.style.display = "none";
    }
   }
  }
  // Function that manages authentication
  function authenticate() {
   //OAuth context
   authContext = new AuthenticationContext(config);
   // Check For & Handle Redirect From AAD After Login
   var isCallback = authContext.isCallback(window.location.hash);
   if (isCallback) {
    authContext.handleWindowCallback();
   }
   var loginError = authContext.getLoginError();
   if (isCallback && !loginError) {
    window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
   }
   else {
    errorMessage.textContent = loginError;
   }
   user = authContext.getCachedUser();
  }
  //function that logs in the user
  function login() {
   authContext.login();
  }
  //function that logs out the user
  function logout() {
   authContext.logOut();
   accountsTable.style.display = "none";
   accountsTableBody.innerHTML = "";
  }
  //function that initiates retrieval of accounts
  function getAccounts() {

   //getAccountsButton.disabled = true;
   var retrievingAccountsMessage = document.createElement("p");
   retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v8.0/accounts";
   message.appendChild(retrievingAccountsMessage)
   // Function to perform operation is passed as a parameter to the aquireToken method
   authContext.acquireToken(organizationURI, retrieveAccounts)
  }
  //function that initiates retrieval of accounts
  function getAccounts() {

   //getAccountsButton.disabled = true;
   var retrievingAccountsMessage = document.createElement("p");
   retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v8.0/accounts";
   message.appendChild(retrievingAccountsMessage)
   // Function to perform operation is passed as a parameter to the aquireToken method
   authContext.acquireToken(organizationURI, retrieveAccounts)
  }
  function createContacts() {

   var retrievingAccountsMessage = document.createElement("p");
   retrievingAccountsMessage.textContent = "Creating contact record....!";
   //getAccountsButton.disabled = true;

   message.appendChild(retrievingAccountsMessage)
   // Function to perform operation is passed as a parameter to the aquireToken method
   authContext.acquireToken(organizationURI, createContactRecords)
  }
  function createContactRecords(error, token) {

   // Handle ADAL Errors.
   if (error || !token) {
    errorMessage.textContent = 'ADAL error occurred: ' + error;
    return;
   }
   var contactObj = {};
   contactObj["firstname"] = "Bangar";
   contactObj["lastname"] = "Raju";
   contactObj["parentcustomerid_account@odata.bind"] = "/accounts(475B158C-541C-E511-80D3-3863BB347BA8)";
   var req = new XMLHttpRequest()
   //"/api/data/v8.2/" + entityPlurarName, false
   req.open("POST", encodeURI(organizationURI + apiVersion + "contacts"), true);
   //Set Bearer token
   req.setRequestHeader("Authorization", "Bearer " + token);
   req.setRequestHeader("Accept", "application/json");
   req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
   req.setRequestHeader("OData-MaxVersion", "4.0");
   req.setRequestHeader("OData-Version", "4.0");
   req.send(JSON.stringify(contactObj));
   req.onreadystatechange = function () {
    if (this.readyState == 4 /* complete */) {

     req.onreadystatechange = null;
     if (req.status === 204) {

      var uri = req.getResponseHeader("OData-EntityId");
      var regExp = /\(([^)]+)\)/;
      var matches = regExp.exec(uri);
      var contactId = matches[1];
      var retrievingAccountsMessage = document.createElement("p");
      retrievingAccountsMessage.textContent = "Contact record created successfully with ID:" + contactId;
      message.appendChild(retrievingAccountsMessage)
     }
     else {
      var error = JSON.parse(this.response).error;
      console.log(error.message);
      errorMessage.textContent = error.message;
     }
    }
   };
  }
  //Function that actually retrieves the accounts
  function retrieveAccounts(error, token) {

   // Handle ADAL Errors.
   if (error || !token) {
    errorMessage.textContent = 'ADAL error occurred: ' + error;
    return;
   }
   var req = new XMLHttpRequest()
   req.open("GET", encodeURI(organizationURI + accountsQuery), true);
   //Set Bearer token
   req.setRequestHeader("Authorization", "Bearer " + token);
   req.setRequestHeader("Accept", "application/json");
   req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
   req.setRequestHeader("OData-MaxVersion", "4.0");
   req.setRequestHeader("OData-Version", "4.0");
   req.onreadystatechange = function () {
    if (this.readyState == 4 /* complete */) {
     req.onreadystatechange = null;
     if (this.status == 200) {
      var accounts = JSON.parse(this.response).value;
      renderAccounts(accounts);
     }
     else {
      var error = JSON.parse(this.response).error;
      console.log(error.message);
      errorMessage.textContent = error.message;
     }
    }
   };
   req.send();
  }
  //Function that writes account data to the accountsTable
  function renderAccounts(accounts) {
   accounts.forEach(function (account) {
    var name = account.name;
    var city = account.address1_city;
    var nameCell = document.createElement("td");
    nameCell.textContent = name;
    var cityCell = document.createElement("td");
    cityCell.textContent = city;
    var row = document.createElement("tr");
    row.appendChild(nameCell);
    row.appendChild(cityCell);
    accountsTableBody.appendChild(row);
   });
   accountsTable.style.display = "block";
  }

  function doOperationOnCRM() {
   var e = document.getElementById("crmOperation")
   var typeOfOperation = e.options[e.selectedIndex].value
   var retrievingAccountsMessage = document.createElement("p");
   retrievingAccountsMessage.textContent = '';
   switch (typeOfOperation) {
    case "1": getAccounts();
     break;
    case "2": //To Do
     break;
    case "3": createContacts(); break;
    case "4": break;//To Do
    default:
   }
  }
 </script>
 <style>
  body {
   font-family: 'Segoe UI';
  }

  table {
   border-collapse: collapse;
  }

  td, th {
   border: 1px solid black;
  }

  #errorMessage {
   color: red;
  }

  #message {
   color: green;
  }
 </style>
</head>
<body>
 <button id="login">Login</button>
 <button id="logout" style="display:none;">Logout</button>
 <br />
 <select id="crmOperation" onchange="doOperationOnCRM()">
  <option value="0">---Select---</option>
  <option value="1">Retrieve Accounts</option>
  <option value="2">Execute Fetch to get Accounts</option>
  <option value="3">Create Contacts</option>
  <option value="4">Create Task</option>
 </select>
 <button id="getAccounts" style="display:none;">Get Accounts</button>
 <table id="accountsTable" style="display:none;"> <thead><tr><th>Name</th><th>City</th></tr></thead> <tbody id="accountsTableBody"></tbody> </table>
 <div id="errorMessage"></div>
 <div id="message"></div>
</body>
</html>


If you are already logged in to your browser with CRM credentials then automatically it reads user token from your local browser and "Logout" button will appear other wise "Login" button will appear.

Once you click on "Retrieve Accounts" then it will display as below.




Git Basic working

  Develop = dev   r