Tuesday, January 5, 2021

Set Optionset value transition validation

There are scenarios we want the option set field value has to transition in an order, for instance, if Low, Medium and High are the available labels of an option set field, and we want to restrict transitions so that from low, can transition only to medium, and from high only to medium. We would need some client side scripting for best UI experience to achieve this. 
We do have this OOB for Status reason, but not for option set fields.

We will need to have functions onLoad, onChange (OnRatingLevelChange) of the option set field and onSave , as named in the script.
On Load, we save the value in a variable which will be used for accessing the Pre - Change value.

Below is an example where in the valid transitions are stored in an object - ratingMapping for        Low<->Medium->High transition and accordingly the changes to the option set field are validated. 

// if HS namespace is not defiende create it.
if (typeof (myNamespace) == "undefined") { myNamespace = {}; }
myNamespace.optionValidation =
{   
    previousRatingLevel: null,
    OnLoad: function (executionContext) {
        var formContext = executionContext.getFormContext();
        this.SetOnLoadRatingLevel(formContext);
    },
    OnSave: function (executionContext) {
        var formContext = executionContext.getFormContext();
        if (this.ValidateRatingLevel(formContext)) {
            this.SetOnLoadRatingLevel(formContext);
        }
    },
    SetOnLoadRatingLevel: function (formContext) {
        previousRatingLevel = formContext.getAttribute("abc_ratinglevel").getValue();
    },
    OnRatingLevelChange: function (executionContext) {
        var formContext = executionContext.getFormContext();
        this.ValidateRatingLevel(formContext);
    },
    ValidateRatingLevel: function (formContext) {
        if (!!previousRatingLevel) {
            var validated = false;
            var lowStatus = 717710000;
            var mediumStatus = 717710001;
            var highStatus = 717710002;
            var ratingMapping = { 717710002: [highStatus, mediumStatus], 717710000: [lowStatus, mediumStatus], 717710001: [mediumStatus, lowStatus, highStatus] };
            var ratingLevelAttribute = formContext.getAttribute("abc_ratinglevel");
            var ratingLevelControl = formContext.getControl("abc_ratinglevel");
            var currentRatingLevel = ratingLevelAttribute.getValue();
            var ratingLevelErrorMessage = 'Please make Valid transition Low-> Medium, Medium -> Low / High, High -> Medium';
            var invalidRatingLevelTransitionMessageId = 'invalidRatingLevelTransitionMessageId';
            var timeout = 5000;
            if (!!ratingMapping[previousRatingLevel] && ratingMapping[previousRatingLevel].indexOf(currentRatingLevel) > -1) {
                validated = true;
                ratingLevelControl.clearNotification(invalidRatingLevelTransitionMessageId);
            }
            else {
                ratingLevelControl.setNotification(ratingLevelErrorMessage, invalidRatingLevelTransitionMessageId);
                setTimeout(function () {
                    ratingLevelControl.clearNotification(invalidRatingLevelTransitionMessageId);
                }, timeout);
                ratingLevelAttribute.setValue(previousRatingLevel);
                validated = false;
            }
            return validated;
        }
    }
}

Monday, January 4, 2021

Create Email in Power automate using Email address string / for unresolved recipients

WE come across scenarios where the Email recipients are not the CRM Users/ Contacts/ Queues which can get resolved when chosen in the Activity Party field as To. In such cases, we can use the addressused property of the activity Party to intake the email address string directly. 

In the below example, I am going to use a CRM user having a valid address as a sender and direct Email address for the 'To' field.

Steps:

1)Set the, "Allow messages with unresolved email recipients to be sent" option to Yes in System Settings. 





2)Set the sender variable with the record type and Guid(Optional, This can also be input directly in the step 4, or the Email address can also be set directly instead of record type-guid)

Store in a variable using compose step - this I did environment specific by storing in a custom config entity. and below is what I have for the dev environment.



systemusers/336e28c4-bdc4-ea11-a812-000d3a79607c

3)In the Create Email Action,  at the right top corner of Activity Party section, click on "Switch to detail inputs for an array item".


4)Input the string as below: 

[

  {

    "participationtypemask": 1,

    "partyid@odata.bind": @{variables('EmailSender')}

  },

  {

    "participationtypemask": 2,

    "addressused": "testemail@fabrikam.com"

  }

]


In the above expression,
   "participationtypemask": 1 corresponds to Sender, 

   "participationtypemask": 2 corresponds to recipient.

@{variables('EmailSender')}--> should resolve to the Email sender variable. Sender can also use "addressused" in place of "partyid@odata.bind" in case want to use Email address string instead of the user with guid.

Below is a snapshot

And We are ready! Save and Test the flow!!





Tuesday, September 8, 2020

LinPadQuery to connect to an org with username, password

 <Query Kind="Program">

  <NuGetReference>Microsoft.CrmSdk.CoreAssemblies</NuGetReference>
  <NuGetReference>Microsoft.CrmSdk.Deployment</NuGetReference>
  <NuGetReference>Microsoft.CrmSdk.Workflow</NuGetReference>
  <NuGetReference>Microsoft.CrmSdk.XrmTooling.CoreAssembly</NuGetReference>
  <NuGetReference>Microsoft.CrmSdk.XrmTooling.PackageDeployment</NuGetReference>
  <Namespace>Microsoft.Xrm.Sdk</Namespace>
  <Namespace>Microsoft.Xrm.Sdk.Messages</Namespace>
  <Namespace>Microsoft.Xrm.Sdk.Query</Namespace>
</Query>
 
string orgUrl = "https://.crm.dynamics.com/";
string authType = "Office365"; // AD, IFD, OAuth, Office365
string user = ".com";
string password = "#########";
string domain = ""; //not required
Microsoft.Xrm.Sdk.IOrganizationService _orgService;
Microsoft.Xrm.Sdk.ITracingService _tracer;
 
void Main()
{
    _orgService = CreateOrgService();
    var account = _orgService.RetrieveMultiple(new Microsoft.Xrm.Sdk.Query.FetchExpression($@"<fetch>
          <entity name='account'>
            <attribute name='name' />
            <attribute name='accountid' />
          </entity>
        </fetch>"));
 
    account[0].Id.Dump();
}
 
// Define other methods and classes here
Microsoft.Xrm.Sdk.IOrganizationService CreateOrgService()
{
    var connectionString = $"Url={orgUrl};AuthType={authType};RequireNewInstance=true";
    if (!string.IsNullOrEmpty(user))
    {
        connectionString += $";UserName={user}";
    }
    if (!string.IsNullOrEmpty(password))
    {
        connectionString += $";Password={password}";
    }
    if (!string.IsNullOrEmpty(domain))
    {
        connectionString += $";Domain={domain}";
    }
    connectionString.Dump();
    return new Microsoft.Xrm.Tooling.Connector.CrmServiceClient(connectionString);
}

Mock XmlHttpRequest /odata request's response in Jasmine Unit test, without using any other module

 WE first need to replace the XmlHttpRequest With a Custom one that we can use for responding to the calls:

Declare a variable in describe, to make it available in all the it nodes

let mockXHR 

In beforeEach add the below code. This replaces the XrmHttpRequestObject definition when triggered, keeping the original definition backup.

mockXHR = {
open: ()=>{console.log('open called from mock')},
send: ()=>{},
setRequestHeader:()=>{},
onreadystatechange:()=>{},
readyState:4,
status:200,
response:JSON.stringify(
              { cci_accountstatus:803230000 }
            ) 
          }; 
window["mockXHR"]=mockXHR;
constoldXMLHttpRequest = window.XMLHttpRequest;
window.XMLHttpRequest =function(){returnmockXHR;};
In it, Call the onReadyStateChange after calling the intended function to be tested.
exampleFunctionCallWhichMakesXHR();
 mockXHR.onreadystatechange();

In afterEach add below, so that the original definition for the XHR is restored after all the tests are executed.
afterEach(() => {     window.XMLHttpRequest = oldXMLHttpRequest;   });

 

Mock Xrm.WebApi.retrieveRecord(...).then in Jasmine Unit test without using any other node module

In the describe/beforeEach/it add below :

For Successful call:

 Xrm: {

  WebApi: {

        retrieveRecord: () => {
            //Add the result Object Properties to this object below
            var retrieveRecordResult = {
            };
            return Promise.resolve(retrieveRecordResult);
        }
        retrieveMultipleRecords: () => {
            //Add the result array Properties to this object below
            var retrieveMultipleRecordsResults = {
                entities: [{ fieldName: fieldValue, anotherFiedName: anotherFieldValue }]
            };
            return Promise.resolve(retrieveMultipleRecordsResults);
        }
    }
}
  window['Xrm']=Xrm;

For Error in the call:

Xrm: {
    WebApi: {
        retrieveRecord: () => {
            //Add the result Object Properties to this object below
            var retrieveRecordError = {
                message: 'Error in retrieve Multiple'
            };
            return Promise.reject(retrieveRecordError);
        }
        retrieveMultipleRecords: () => {
            //Add the result array Properties to this object below
            var retrieveMultipleRecordsError = {
                message: 'Error in retrieve Multiple'
            };
            return Promise.reject(retrieveMultipleRecordsError);
        }
    }
}
  window['Xrm']=Xrm;

Sunday, August 2, 2020

Check User Roles - Power Automate

We'd have come across requirements to check the roles of a User in Power automate, but not have found any message to get a User Roles directly from the flow. So the way to do it is to retrieve the System User: Role N:N intermediate entity records and check the count.

There's a different challenge going this way! the intermediate entity name to be used for list Records action in the flow is not same as the name that we see in the CRM solution, we need to get that from the API service. You can get the same by searching for the relation in the page: https://[OrganisationName].api.crm [Version] .dynamics.com/api/data/v9.0/         (Source : https://ajitpatra.com/2019/08/26/microsoft-flow-retrieve-nn-records-in-d365/)
For a System user roles the entity name is systemuserrolescollection

Below is the list records action fetch xml to retrieve No. of roles out of roleName1/roleName2/roleName3 that a User with guid "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" this guid can be passed directly or  can also be replaced with a variable as in the snapshot.
fetchxml for list records:

<fetch>
  <entity name="systemuserroles" >
    <attribute name="systemuserroleid" />
    <filter>
      <condition attribute="systemuserid" operator="eq" value="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" />
    </filter>
    <link-entity name="role" from="roleid" to="roleid" link-type="inner" alias="Roles" >
      <filter type="or" >
        <condition attribute="name" operator="eq" value="roleName1" />
        <condition attribute="name" operator="eq" value="roleName2" />
        <condition attribute="name" operator="eq" value="roleName3 />
      </filter>
    </link-entity>
  </entity>
</fetch> 

The Length of the list - in this case  length(outputs('Retrieve_Creating_User_Roles_intent_roles_count')?['body']?['value']) returns non zero if User has at least one role out of above 3  mentioned. Replace Retrieve_Creating_User_Roles_intent_roles_count with the name of list records step.
And you are ready!