Daily Archives: April 17, 2015

Business Process Flows: Tweak A Global Process Based on User’s Role

Published by:

I recently had a requirement for Business Process Flows – a pretty standard lead to order process. The challenge was that the client had an optional first stage they called “pre-lead,” which would only apply to users on a certain inside sales team. In other words, when the Lead was created by Steve, it was supposed to get process A; but if started by Sally, it’d get process B.

Normally you could do this with two flows controlled by user roles and prioritization. But it was important that everyone have access to both flows since both types of resources could work on a given opportunity, so that was out.

The solution was to create two flows and assign it at the time the record is created.

On the User record, create a flag indicating which process they should default to.  I called it ics_defaultleadstoprelead. You’ll also need a system field on the Lead, which I called ics_processflowset, defaulted to no.

We’re going to use Xrm.Page.data.process.setActiveProcess() for this, but the gotcha is that you can’t use it on create. So our logic is:

  • If Form Type == Create
    • Hide Business Process Flow
  • Else
    • If the type has not been set yet (ics_processflowset == no)
      • Determine which process flow should be active on the Lead based on the flag on the User.
      • Set the type.
      • Set the flag (ics_processflowset) to not change it later.
    • Show the flow.

Here’s the code. It utilizes a utility library I have.

  • ics_util.getSingleton() fires a Fetch and returns one field from one returned record. This is very useful for pulling one attribute off of a given record where I have a GUID.
  • ics_util.setFieldValue() just sets a field value.

 

var ics_lead = function () {
	
	SetDefaultLeadState = function () {
	
		if (ics_util.getFormType() === ics_util.FORM_TYPE_CREATE)
		{
			Xrm.Page.ui.process.setVisible(false);
		}
		else
		{
			Xrm.Page.ui.process.setVisible(true);
			if (ics_util.getFieldValue("ics_processflowset") === false)
			{
				var fetch = 
				'<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">'+
				'  <entity name="systemuser">'+
				'    <attribute name="systemuserid" />'+
				'    <attribute name="ics_defaultleadstoprelead" />'+
				'    <order attribute="ics_defaultleadstoprelead" descending="false" />'+
				'    <filter type="and">'+
				'      <condition attribute="systemuserid" operator="eq-userid" />'+
				'    </filter>'+
				'  </entity>'+
				'</fetch>';
				
				var tupleValue = ics_util.getSingleton(fetch,"ics_defaultleadstoprelead",false,"There has been an error determining if this should be a pre-lead. (fetch)");

				// (User entity) Default Leads to Pre-Lead? (ics_defaultleadstoprelead) - "Default to Prelead" = 0; "Default to Lead" = 1
				
				// Automatically set lead or prelead process
				if (tupleValue) 
				{
					Xrm.Page.data.process.setActiveProcess(this.PROCESS_ID_NO_PRELEAD, this.onSetActiveProcess);
				}
				else
				{
					Xrm.Page.data.process.setActiveProcess(this.PROCESS_ID_PRELEAD, this.onSetActiveProcess);
				}
				ics_util.setFieldValue("ics_processflowset",true);
			}
		}
	
	};
	
	OnSetActiveProcess = function(returnStatus){
		
		switch (returnStatus) {

		case "success":
			//alert("success");
		break;

		case "invalid":
			alert("There has been an error determining if this should be a pre-lead. (setActive Process returned invalid)");
		break;
		}

	};
	return {
		setDefaultLeadState : SetDefaultLeadState,
		onSetActiveProcess : OnSetActiveProcess,
		PROCESS_ID_PRELEAD : "15dbaafe-xxxx-xxxx-xxxx-990bcd934931",
		PROCESS_ID_NO_PRELEAD : "5db8867d-xxxx-xxxx-xxxx-67afb06a53c9" 
	};
	

}();

Associate A Record To One of Two Entity Types (CRM 2011)

Published by:

Here’s another old blog I’m republishing. This functionality is now native in CRM 2013 and CRM 2015 as Business rules.

Sometimes, you want to one same entity and associate it to two other entity types. For example, you might have a License that could apply to either an Account or a Contact. You could create two entities – an “Account License” and a “Contact License,” or you could use this simple technique to use one entity that appears to be linked to a Contact or an Account.

Here’s how:

  1. Create your new entity.
  2. On the Entity form, add three fields:
    1. A “Two Options” field called “Owned By,” and set option 0 to be Account and 1 to be Contact. Set the default to Account (1).
    2. A Lookup to a Contact called something like “Owner (Contact)”
    3. A Lookup to an Account called something like “Owner (Account)”.
  3. Create a new Web Resource with the following code:
    function licenseOnLoad()
    {
            if (Xrm.Page.ui.getFormType() == 1)
            {
                            // On form create (formType = 1), the system will map either the Contact or Account that it was created from.
                            // We defaulted the Owned By to assume it's an Account.
                            // So if it's a Contact, change the field.
                              if (Xrm.Page.data.entity.attributes.get("new_ownercontact").getValue() != null)
                              {
                               Xrm.Page.data.entity.attributes.get(“new_ownedby”).setValue(0);
                              }
            }
            // Disable this field in all cases, not just on create.
        Xrm.Page.ui.controls.get("new_ownedby").setDisabled(false);
            // Call the below function which will display only Account or Contact to the user.
            accountContactToggle();
    }
    
    function accountContactToggle()
    {
        // Toggles ownership field (contact/account) based on the user input.
            var ownedBy = Xrm.Page.data.entity.attributes.get("new_ownedby").getValue();    // Account = 0; Contact = 1
            if (ownedBy == 1) // Account
            {
                            Xrm.Page.ui.controls.get("new_ownercontact").setVisible(false);
                            Xrm.Page.ui.controls.get("new_owneraccount").setVisible(true);
            }
            if (ownedBy == 0)  // Contact
            {
                            Xrm.Page.ui.controls.get("new_ownercontact").setVisible(true);
                            Xrm.Page.ui.controls.get("new_owneraccount").setVisible(false);
            }
    }
    
  4. In the Form Properties, in the Events tab, add the new Web Resource and set the onLoad() to call the new licenseOnLoad() function.

As this is jScript, if you are deploying on mobile platforms, be careful.

One drawback to this approach is that if the user creates a License but not from Account or Contact, such as from the FILE tile in the top left, there will be no mapping to indicate Account or Contact ownership, so the License will always be associated to Account. You can enforce this with process or provide a Dialog or Workflow to allow users to fix retroactively.

How to Flip Sender and Recipient in a Phone Call Record

Published by:

There is an annoying tendency in Microsoft Dynamics CRM 2011 for the system to set the Recipient of a Phone Call incorrectly. For example, when adding a Phone Call to a Case, it assumes the Recipient is the Account of the Case it is tied to and marks the call as Incoming.

preview flip sender (1)

It doesn’t make sense that an Incoming call would be directed to an Account. Rather, it should be a User.

The business rule I needed to implement was:  if the call is Incoming, the Recipient must be a User and conversely, if the call was Outgoing, the Sender must be a User.

Here is some scripting you can implement – based on Rajeev Pentyala’s blog based here: http://rajeevpentyala.wordpress.com/2012/04/02/get-and-set-partylist-fields-using-jscript/
It’s tricky to work this out since there are Activity Parties / Party Lists involved.

Note here that I have some utility functions that contain some Xrm.Page functions. This will make it easier to update encase there is another change in Microsoft’s syntax such as the 4.0 to 2011 transition.

Here are my definitions:

· getFieldValue returns Xrm.Page.data.entity.attributes.get(field).getValue()
· getAttribute returns Xrm.Page.getAttribute(field)
· getAttributeValue returns Xrm.Page.getAttribute(field).getValue()

First, the following function will return true if the field contains a record of the type you need. For example, if you want to see if Sender is a User, send it “from” and “8”. This just pulls the values from the array and if it finds one of the type you need, returns true.

function isPartyPresent(field, type)
{

      // Parameters :
      // Field is the field to look in
      // Type is the ObjectTypeCode of the thing you are looking for (eg 8 = User)
      var partyArray = getAttributeValue(field);
      if (partyArray == null)
      {  // If partyArray comes back null, there is nothing there, so there is no party present.
            return false;
      }
      for (var indxParties = 0; indxParties < partyArray.length; indxParties++) {
      if (partyArray[indxParties].type == 8) {
            return true;
      }
}
return false;
}

Next, implement a quick function that will tell you if there is an “inversion.”

function detectPartyInversion()

{
      var direction = getFieldValue("directioncode");
      // Direction -> Incoming = 0 (False) and Outgoing = 1 (true)
      if ((direction == false) && (isPartyPresent("to",8) == false ))
      {
            return true;
      }
      if ((direction == true) && (isPartyPresent("from",8) == false ))
      {
            return true;
      }
      return false;
}

This allows us to implement a pretty simple handler to detect the inversion and correct it.

function invertedPartyHandler()
{          
      if (detectPartyInversion())
      {
            var recipient = getAttribute("from");
            var sender = getAttribute("to");
            var recipientValues = getAttributeValue("from");
            var senderValues = getAttributeValue("to");
            sender.setValue(recipientValues);
            recipient.setValue(senderValues);
      }
}

Finally, we can run this check onLoad.

function phoneCallOnLoad()
{
      if (getFormType() == 1)
      {
            invertedPartyHandler();
      }
}

All that’s left to do is to add the phoneCallOnLoad() function to the onLoad event of your form.

How to Prevent Ribbon Actions Based on User Input (CRM 2011)

Published by:

To kick things off, here’s a classic blog from a few years back.

Alright ladies and gents, this is a fairly hefty blog post but is also very informative.

I was once asked to implement a requirement where orders could not be submitted if one or more Quote Products was a write-in in Microsoft Dynamics CRM 2011.

To do this, we needed to modify the button in the Ribbon that submits the order. Here’s a simple way to do that. It looks complicated but it’s really not that bad!

Here’s a step-by-step guide:

Using the Visual Ribbon Editor on Codeplex (http://crmvisualribbonedit.codeplex.com/) , open your Organization and the Quote entity.

Notice that the Create Order button is there, but since it’s a System button, it can’t be edited. So we’re going to have to replace it with our own. Make a new button and copy the Display Rules from the native button. In this case, the “Entity Salesorder Privilege = Create Basic” rule is an EntityPrivilegeRule. Under Enable Rules, copy the JavaScript path and function names into two new rules. Hold off on the Action section for now.

Your new button’s configuration will look like this so far:

1

2

3

Now comes the harder part. You need to make your own jScript function that will run a quick web service call to see if there are any Quote Products that are write-ins.

First, make an Advanced Find with the query you want. In this case, we are looking for Quote Products where the Quote is the specific one we are on. So when making the Advanced Find, pick any Quote in the system for now. Later we’ll have the jScript swap out the individual Quote GUID on the fly.

Here is our Advanced Find:
4

Download the FetchXML into a Text file. This is going to be added to jScript so we need to wrap each line in quotes to turn it into a string (meaning the single tick at the beginning and the single tick at the end, the plus concatenating it):

'<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">'+
' <entity name="quotedetail">'+
' <attribute name="productid" />'+
' <attribute name="productdescription" />'+
' <attribute name="priceperunit" />'+
' <attribute name="quantity" />'+
'<attribute name="extendedamount" />'+
' <attribute name="quotedetailid" />'+
' <order attribute="productid" descending="false" />'+
' <filter type="and">'+
' <condition attribute="isproductoverridden" operator="eq" value="1" />'+
' </filter>'+
' <link-entity name="quote" from="quoteid" to="quoteid" alias="ad">'+
' <filter type="and">'+
' <condition attribute="quoteid" operator="eq" uiname="Interested in our newer offerings (sample)" uitype="quote" value="{6EE28A24-602A-E111-8EBC-1CC1DEE87ACD}" />'+
' </filter>'+
' </link-entity>'+
' </entity>'+
'</fetch>';

Now we are going to make a jScript function out of this that will tell us the quantity of Quote Products that match. This requires the XRMServiceToolkit from Codeplex (http://xrmservicetoolkit.codeplex.com/). You don’t have to know how it works. Download the code from Codeplex and add it to your system as a Web Resource of type Script. Then add the Web Resource to your form (Form Properties, then Form Libraries). Back in your text editor, make a jScript function out of your query.

function getQuantityWriteInOrders()

{

var fetchQuery =
'<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">'+
' <entity name="quotedetail">'+
' <attribute name="productid" />'+
' <attribute name="productdescription" />'+
' <attribute name="priceperunit" />'+
' <attribute name="quantity" />'+
' <attribute name="extendedamount" />'+
' <attribute name="quotedetailid" />'+
' <order attribute="productid" descending="false" />'+
' <filter type="and">'+
' <condition attribute="isproductoverridden" operator="eq" value="1" />'+
' </filter>'+
' <link-entity name="quote" from="quoteid" to="quoteid" alias="ad">'+
' <filter type="and">'+
' <condition attribute="quoteid" operator="eq" uiname="Interested in our newer offerings (sample)" uitype="quote" value="' + Xrm.Page.data.entity.getId() + '" />'+
' </filter>'+
' </link-entity>'+
' </entity>'+
'</fetch>';
var returnArray = XrmServiceToolkit.Soap.Fetch(fetchQuery);
if (returnArray == null)
{ return null; }
return +returnArray.length;
}


A couple of things here. Note that in the “condition attribute” clause, I replaced what was the GUID from the Fetch download with the Xrm.Page.data.entity.getId() method. This will insert the GUID of the current Quote you are on at run-time. In short, it makes the query say, get me a list of Quote Products that are write-ins that are associated to the Quote I am looking at right now.

XRMServiceToolkit will return the results of the query as an array. Since we are only interested in the quantity, we can just return the length of the array. The + operator forces the return to an integer value.

Now we create the control function. This will simply run the query, get how many Quote Products match, and if the number is greater than zero, die.

function checkOrders()
{
if (getQuantityWriteInOrders() == 0 )
{
acceptQuoteOrCreateOrder();
}
else
{
alert ("You can not submit orders with write-in products.");
return null;
}
}

Where did acceptQuoteOrCreateOrder() come from? It’s the native function within CRM that pops the Quote Submit screen. We know this from the definition of the original Create Order button back in the Ribbon editor:

5

Basically, the checkOrders function runs the query and only lets you submit the order if the quantity of Write-In products is zero.

Take the two functions and add them to CRM as a Web Resource, then add it to the Form. Your form Properties will look like this:

6

No need to set triggers onLoad, onSave or onChange since we are going to fire this code when the user clicks the Ribbon button.

Now, we need to add the Action to our new custom button. Back in the Ribbon editor, add a JavaScript function under the Action tab.

5

checkOrders is the name of the function we wrote above. To get the Library URL, it’s at the bottom of the Web Resource configuration screen:

ribbon8

In this case, we pull off the beginning part and keep everything after WebResources; start with one slash not two (“/WebResources/new…”)
Lastly, use the toggles in the Sequence field to bump the button left or right to be next to the old one.

Once you save your Ribbon in the Editor, your new button will be published.

Once it works, you change the Label of the new one to “Create Order” and disable the original button by clicking Hide. Even though you’ve replaced the button, it looks like the original button to users.