Archive

Posts Tagged ‘CRM 2011’

Copy a security role programmatically – CRM 2011

In one of the requirement, we have to create a new security role by copying an existing role in a Plug-in.

The whole requirement break down to 3 steps

  1. Create a new security role
  2. Get all the existing role privileges by using “RetrieveRolePrivilegesRoleRequest
    1. RetrieveRolePrivilegesRoleResponse” contain, “RolePrivileges” property, with collection of privileges & access level
    2. Role Privilages

      Role Privileges

  3. Add the retrieved role privileges to the newly created role by using “AddPrivilegesRoleRequest

Below is the code for step 2 & 3

Guid existingRoleId = new Guid(“C85F0FFF-4C80-E211-A877-1CC1DE79B4CA”);

Guid newRoleId = new Guid(“B6690FFF-4C80-E211-A877-1CC1DE79B4CA”);

// Step 2

RetrieveRolePrivilegesRoleRequest getPrivilagesRequest = new RetrieveRolePrivilegesRoleRequest();

getPrivilagesRequest.RoleId = existingRoleId;

RetrieveRolePrivilegesRoleResponse privilagesResponse = (RetrieveRolePrivilegesRoleResponse)service.Execute(getPrivilagesRequest);

if (privilagesResponse != null && privilagesResponse.RolePrivileges != null){

// Step 3

AddPrivilegesRoleRequest addPrivilagesRequest = new AddPrivilegesRoleRequest();

addPrivilagesRequest.Privileges = privilagesResponse.RolePrivileges;

addPrivilagesRequest.RoleId = newRoleId;

AddPrivilegesRoleResponse addPrivilagesResponse = (AddPrivilegesRoleResponse)service.Execute(addPrivilagesRequest);

}

🙂

Unable to install CRM 2011 Report Extension – Reboot required error

I was getting below “Reboot Required” message when I tried to install “CRM 2011 Report Extension” (i.e., CRM2011-Bids-ENU-i386)

Installation cannot proceed

Installation cannot proceed

  • I restarted my machine and I retried to install, still I was getting same error.
  • As mentioned in the error message, I checked the log file under “C:\Users\{curr_user}\appdata\Roaming\Microsoft\MSCRM\Logs\BIDSExtensionsSetup.log” path, there it was written as

Reboot required — Key Found: HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce

Fix –

  • Seems a known issue
  • We need to delete the “RunOnce” key from “Registry” (Path – HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce)
  • Refer KB Article from Microsoft.

After deletion of “RunOnce” key from Registry, I was able to install CRM 2011 Report Extension.

🙂

Disabling controls in a section using JScript

We can’t disable a Section using “setDisabled(false)”, as Section is a container of controls.

Below is the logic to disable all the controls in a section

  • Loop through all the page controls
  • If the control’s parent is the specified section, disable the control

Below are the functions to disable controls in a Section. We need to pass “Section” object as parameter

function disableSectionFields(section) {

if (section) {

Xrm.Page.ui.controls.forEach(function (control, index) {

if (control && doesControlHaveAttribute(control)) {

if (control.getParent() === section) {

control.setDisabled(true);

}

}

});

}

}

// Validate if the control is not  IFrame/webresource/subgrid as we can’t disable them

function doesControlHaveAttribute(control) {

var controlType = control.getControlType();

return controlType != “iframe” && controlType != “webresource” && controlType != “subgrid”;

}

// Get the Section object by Tab name & Section name

function getSection(tabName, sectionName) {

var tab = Xrm.Page.ui.tabs.get(tabName);

if (tab) {

return tab.sections.get(sectionName);

}

return null;

}

How do I call

  • Assume my tab name is “tab_name” & section name is “section_name”
  • Get the section object and pass to the “disableSectionFields” function as below

var mySection = getSection(“tab_name”, “section_name”);

if (mySection) {

disableSectionFields(mySection);

}

🙂

Set desired form as default using script – CRM 2011

I have an “Account” entity with multiple forms (i.e.,4 forms). My requirement is to set particular form as default form based on some logic.

Below is the script to set desired form as default.

function setMyFormAsDefault() {

// Set the form name you want to set as default

var myDefFormName = “ABC”;

// Get all available forms

if (Xrm.Page.ui.formSelector.items.get()) {

var forms = Xrm.Page.ui.formSelector.items.get();

var formId, formName;

for (var indxForms = 0; indxForms < forms.length; indxForms++) {

formId = forms[indxForms].getId();

formName = forms[indxForms].getLabel();

// Check form name and if it matches set current form as Default

if (formName == myDefFormName) {

forms[indxForms].navigate();

break;

}

}

}

}

  • Xrm.Page.ui.formSelector.items.get()” give us all available forms of the current entity
  • getId() & getLabel() gives the form GUID and Form name
  • “navigate()” loads the selected form

🙂

Reading related records using OData and Jscript – CRM 2011

I have 1:N relationship between “Contact” entity and custom entity “Bikes” (i.e., 1 contact can have N no of Bikes).

If I want to read all “Bikes” along with my “Contact” information using OData service, I can use the “$expand” keyword of OData protocol.

Below is the generic function to prepare the OData URL and make a Asynchronous service call

function retrieveMultiple(odataSetName, select, filter, expand, successCallback) {

var serverUrl = Xrm.Page.context.getServerUrl();

var ODATA_ENDPOINT = “/XRMServices/2011/OrganizationData.svc”;

var odataUri = serverUrl + ODATA_ENDPOINT + “/” + odataSetName + “?”;

if (select) {

odataUri += “$select=” + select;

}

if (expand) {

odataUri += “&” + “$expand=” + expand;

}

if (filter) {

odataUri += “&” + “$filter=” + filter;

}

$.ajax({

type: “GET”,

contentType: “application/json; charset=utf-8”,

datatype: “json”,

url: odataUri,

beforeSend: function (XMLHttpRequest) {

XMLHttpRequest.setRequestHeader(“Accept”, “application/json”);

},

success: function (data, textStatus, XmlHttpRequest) {

if (successCallback) {

if (data && data.d && data.d.results) {

successCallback(data.d.results, textStatus, XmlHttpRequest);

}

else if (data && data.d) {

successCallback(data.d, textStatus, XmlHttpRequest);

}

else {

successCallback(data, textStatus, XmlHttpRequest);

}

}

},

error: function (XmlHttpRequest, textStatus, errorThrown) {

if (XmlHttpRequest && XmlHttpRequest.responseText) {

alert(“Error while retrieval of ” + odataSetName + ” ; Error – ” + XmlHttpRequest.responseText);

}

}

});

}

How do I use above function

  • Consider my “Contact” & “Bike” scenario and set required the columns and “expand” option and call the above “retrieveMultiple” function
  • Below is the function to define properties

function retrieveBikesByContactId() {

// Pass ‘Contact’ set name

var oDataSetName = “ContactSet”;

// Set Contact GUID

var contactId = {Contact GUID};

// Prepare filter

var filter = “ContactId eq guid'” + contactId + “‘”;

// Set expand (i.e., Relationship name of ‘Contact’ and ‘Bike’)

var expand = “honda_contact_honda_bikeinformation”;

// Column names of ‘Contact’ and ‘Bikes’

// Since Bike is related entity specify Bike columns as (Relationship name\column name) (i.e., honda_contact_honda_bikeinformation/honda_name)

var columns = “FullName,honda_contact_honda_bikeinformation/honda_name,honda_contact_honda_bikeinformation/honda_Year”;

retrieveMultiple(oDataSetName, columns, filter, expand, readDataOnSuccess);

}

  • After the success of data retrieval, parse the result using “readDataOnSuccess” method

function readDataOnSuccess(data, textStatus, XmlHttpRequest) {

if (data && data.length > 0) {

for (var indx = 0; indx < data.length; indx++) {

if (data[indx].honda_contact_honda_bikeinformation) {

              // Read the child entity records using “expand.results” (i.e., honda_contact_honda_bikeinformation.results)

var childResults = data[indx].honda_contact_honda_bikeinformation.results;

for (var indxChild = 0; indxChild < childResults.length; indxChild++) {

alert(childResults[indxChild].honda_name);

}

}

}

}

}

Use the “OData Query Designer” tool to get the “expand” name and column names

  • To get related entity record fields, click “One To Many” tab and choose the relationship
Get Related Entity Fields

Get Related Entity Fields

URL with Related records columns

URL with Related records columns

Related records result set

Related records result set

Lead qualify process design changes in UR 12 – CRM 2011

April 30, 2013 2 comments

In UR 12, the lead conversion process has been redesigned. Refer below for lead qualify process before and after UR 12

Lead conversion before UR 12

  • Before UR 12, we have “Qualify” button on Lead ribbon
  • It opens a “Convert Lead” web page, which gives us options to create Contact/Account/Opportunity.
Convert Lead

Convert Lead – Before UR 12

After UR 12

With new design, the “Convert Lead” dialog box no longer appears during lead qualification.

  • We have 2 ribbon buttons (Qualify and Disqualify).
Convert Lead - UR 12

Convert Lead – UR 12

  • The qualify button will automatically create the related Opportunity record based on the customer information entered into the Lead form
  • Associated Contact and Account records will also be automatically created based on the First Name, Last Name, and Company Name field data, respectively
  • If you click disqualify you are able to select the status reason, otherwise if you click qualify the lead will just automatically be qualified.

KB Article which talks about the same

Read and Parse XML web resource using Jscript – CRM 2011

April 25, 2013 3 comments

I have a XML file with below content added as a web resource (new_fruitsxml) in my CRM application

<?xml version=”1.0″ encoding=”utf-8″ ?>

<Fruits>

<Fruit Code=”App”>

<Name>Apple</Name>

<Price>20</Price>

</Fruit>

<Fruit Code=”Orng”>

<Name>Orange</Name>

<Price>10</Price>

</Fruit>

</Fruits>

Below is the Jscript code to read and parse the XML.

Note – The code uses JQuery to parse the XML, so you have to refer “jquery1.4.1.min.js” file to make the code works.

function ReadXML() {

try {

var xmlPath = “../WebResources/new_fruitsxml”;

$.ajax({

type: “GET”,

url: xmlPath,

dataType: “xml”,

success: parseXML

});

} catch (e) {

alert(“Error while reading XML; Description – ” + e.description);

}

}

function parseXML(xml) {

$(xml).find(“Fruit”).each(function () {

// Read attribute

alert(“Fruit Code – ” + $(this).attr(“Code”));

// Read Nodes

alert(“Fruit Name – ” + $(this).find(“Name”).text());

alert(“Fruit Price – ” + $(this).find(“Price”).text());

});

}

  • In “ReadXML()” function we read the webresource and if the read succeeds, we mention the “parseXML()” function in “success: parseXML”
  • So, on success, control comes to “parseXML()” function where we parse the XML

🙂

Associate/Disassociate plugin messages in CRM

April 17, 2013 11 comments

In CRM, the Associate or Disassociate event happens

  • If you have a N:N relationship between two entities and when you try to associate or disassociate records either from Associated view or Sub grid.
Entity Associate View

Entity Associate View

In Plugins, the Associate & Disassociate messages behave little different than other messages.

  • When you register a plugin on Associate message, you have to leave “Primary and Secondary” entities as ‘none’.
Associate Plugin Step

Associate Plugin Step

  • Since we don’t provide entity names, the registered Plug-in step triggers on all “Associate” operations, so we have to check few conditions to let the “Association” trigger happen only between intended entities.

You can use the below code template for Associate or Disassociate plugins

            EntityReference targetEntity = null;

            string relationshipName = string.Empty;

            EntityReferenceCollection relatedEntities = null;

            EntityReference relatedEntity = null;

            if (context.MessageName == “Associate”) {

                // Get the “Relationship” Key from context

                if (context.InputParameters.Contains(“Relationship”)) {

                    relationshipName = context.InputParameters[“Relationship”].ToString();

                }                                   

                // Check the “Relationship Name” with your intended one

                if (relationshipName != “{YOUR RELATION NAME}”) {

                    return;

                } 

                // Get Entity 1 reference from “Target” Key from context

                if (context.InputParameters.Contains(“Target”) && context.InputParameters[“Target”] is EntityReference)  {

                    targetEntity = (EntityReference)context.InputParameters[“Target”];

                }                      

                // Get Entity 2 reference from ” RelatedEntities” Key from context

                if (context.InputParameters.Contains(“RelatedEntities”) && context.InputParameters[“RelatedEntities”] is EntityReferenceCollection) {

                    relatedEntities = context.InputParameters[“RelatedEntities”] as EntityReferenceCollection;

                    relatedEntity = relatedEntities[0];

                }

            }

🙂

Multiple relationships between 2 entities

What if I have multiple relationships between my entities, how CRM handles multiple “Relationship Behaviors” (i.e., for example Parental & Referential)

Let’s consider this scenario,

  • I have 2 custom entities “Customer” & “Bike” and my requirement is a customer should possess multiple bikes
  • I created a 1:N relationship with “Type Of Behavior = Parental” between “Customer” & “Bike”
Parental Behavior

Parental Behavior

  • Since the relationship behavior is “Parental”
    • If I Delete a “Customer” record, all associated “Bikes” get deleted (i.e., Because of ‘Cascade All’ Delete)
    • If I Assign a “Customer” to user X, all associated “Bikes” owner change to user X (i.e., ‘Cascade All’ Assign)

Now, just to my curiosity

  • I created another 1:N relationship with “Type Of Behavior = Referential Restrict Delete” between “Customer” & “Bike”
Referential Restrict Behavior

Referential Restrict Behavior

  • Since relationship behavior is “Referential Restrict Delete”
    • I cannot delete a “Customer” record, If its associated with “Bikes” records (i.e., Because of ‘Restrict’ Delete)
    • If I Assign a “Customer” to user X, all associated “Bikes” owner does not change (i.e., ‘Cascade None’ Assign)

Now if you see the “1:N relationships” of “Customer“, it will have 2 relationships as below

Multiple Relationships

Multiple Relationships

Let’s try Delete & Assign scenarios and see how CRM application handles Parental & Referential behaviors

Delete Scenario

  • I have a customer “Test” with 2 bikes
  • Now, lets try to delete customer “Test” and see which of two behaviors (i.e., “Parental” or “Referential Restrict Delete”) applies
  • Here I got “Restrict Delete” validation message, so “Referential Restrict Delete” behavior take precedence
Could not delete

Could not delete

Assign Scenario

  • When I change the ownership of “Test”
  • All my “Bikes” ownership changed to new owner of customer “Test”, so “Parental” behavior take precedence

Conclusion

  • In these scenarios, CRM always unions, so in our scenario when I have “Parental” & “Referential Restrict Delete”
    • In case of Delete “Cascade All” union “Restrict”, so it restricted the delete
    • In case of Assign “Cascade All” union “Cascade None” = Cascade All, so all associated records ownership also change

🙂