Tuesday, November 29, 2022

Migrate User Query (Personal Views) using Kingswaysoft

 It took us to the UAT stage to be sure the View Migration using XRM tool box - Personal View Migration Tool might now be viable when there are 1000s of saved Views, and the Saved Views referring to deleted fields which needs extreme manual monitoring.

Thanks to friends Harshal and Nilesh, WE figured out a way to do from Kingsway soft

The userquery table doesn't appear in the source Connection's Tables list for Dynamics, we need to use the OLEDB connection, but the Destination can be Dynamics Connection itself.

The Migration will be 3 step

1. Migrate(Insert/Upsert) the Userqueries without the owner mapping.

2. Share the UserQueries with the Users using the Principal Object Access table migration(Insert/Upsert),
with Source Data filter - Entity name = userquery

3. Migrate the Owner of the User query so that they get assigned to the Original Owners (Upsert)

At Step 1 we do not map the Owner because, once the Owner is changed, the Share will not work, even if you have System Administrator role .

Also If we try to Update the changes to the View like the criteria or the columns, will not be able to update unless Package running User is the Owner.

The Object Type Code in the Layout xml will need to be updated to the userquery's entity in the Target.
(Else the View will not render, for the Custom Entities - where the Object Type code at the Source Organization and the Target Organization are different.)

Monday, November 28, 2022

CRM Managed solution import fail - Plugin does not contain required types

This happens as the Plugin Assembly is seen as a different one by CRM.
To resolve this:
1.Increase the version of the Solution and Publish at the source, then import to the destination


Thanks to Friend Seshagiri - For arriving at this Simple yet tricky solution

Import File is too Large to upload (On Premise)

 One of the scenarios this happens is:
The CRM App Server - where the CRM Application folder is present has low space.
(C>Program Files>Microsoft Dynamics)/

Clear the folder to have at least 1 GB free to resolve the issue.

On a side note, other issues that you may face due to Space issue in the App/DB server are- 
CRM stops working

Other Tools that try to connect to CRM will throw - Metadata reference cannot be resolved error.

Saturday, October 22, 2022

Power Pages or Power Portal Captcha not rendering when embedded in another website / basic or multistep form redirection not working

 When a Portal page is embedded within another website as an Iframe, the cookies used by the portal Page might be treated as a third party site cookies by the browser.

It is recommended that the domain name be a sibling or a child of the domain name of the site where you're embedding the portal in an iframe Example: your root website is www.contoso.com, the portal domain name should be portal.contoso.com

Reference: 
https://learn.microsoft.com/en-us/power-apps/maker/portals/embed-portal-website

CRM 2011 not rendering on the browser, inspite of opening in the compatibility View

Even after compatibility view, the CRM 2011 doesn't open, on load- and opening the debugger says "mainscript is undefined"

One of the scenarios this would happen if the Main Folder of the CRM is secured and has no access provided to the User opening CRM.
To resolve this, navigate to the drive where CRM was installed , navigate to the main folder and select Security, provide Read access to the Users,

User creation On Premise "Attempting to create a user with a domain logon that does not exist"


This can happen in some scenarios of a valid Username as below:
1. Trust is not established between the CRM Server domain and the User Domain
2. The User is not present is not active in Active Directory
3. Duplicate entry of User is present with same user Name in the Active Directory (Can happen in scenarios where Post deactivation, same Username is created again)

To resolve the issue, 
  •  Correct the User Name in the ACtive Directory by deleting the entry corresponding to the inactive username.
If it is not feasible to make the correction in the Active Directory, 
  • Temporarily turn the AutoGroupManagementOff to 1 in the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM in CRM FrontEnd
    (This is by default 0 - And during any User creation in CRM they are automatically added to the Reporting Group and User Group)
  • Create User in CRM
  • Add the User manually to the ReportingGroup and the UserGroup of the corresponding CRM AD Group.

    For details refer: 
For details refer: https://community.dynamics.com/crm/f/microsoft-dynamics-crm-forum/167494/issue-in-creating-a-user-in-crm-2011/411072


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;   });