Outlook Plugin with CRM Online & Office 365: Tracked Items Aren’t Tracked

Published by:

We recently ran across an issue when setting up CRM Online 2015 Update 1 with Office 365 Exchange Online. A user installed the Outlook plugin, and could click the Track and Set Regarding functions – even select which record it should be Regarding – but the record never made it to CRM. The original symptom wasthe “Convert To” function was grayed out no matter what we did.

The issue is that in some cases, the Email Server Profile defaults to a value that doesn’t work. Both Incoming Server Location and Outgoing Server Location need to be set to

https://outlook.office365.com/EWS/Exchange.asmx

But how? The field disappears almost immediately, and you can’t create a new form against this entity, nor can you create workflows.

So I forced it to reopen by using the F12 debugger and setting the field to be editable. For example, incomingserverlocation can be edited by opening the F12 debugger and removing the applicable clause. For example, here, you would remove the part that says disabled=”disabled” altogether. This opens up the field to be edited.

2015-08-05 14_02_04-Inspector - https___xxxxx.crm.dynamics.com_main.aspx_etc=9605&extraq

You need to do this for the three fields.

  • Autodiscover Server Location = No
  • Incoming Server Location = https://outlook.office365.com/EWS/Exchange.asmx
  • Outgoing Server Location = https://outlook.office365.com/EWS/Exchange.asmx

2015-08-05 13_54_43-Email Server Profile_ Test 2 - Microsoft Dynamics CRM

Then associate your user(s) to the profile. You then need to approve them and click the Test button.

But mine came back with this error:

Email cannot be sent because the email address of the mailbox John Smith requires an approval by an Office 365 administrator. The mailbox has been disabled for sending email and the owner of the email server profile Test 2 has been notified.

Make sure the person doing the approving and testing is both an Office 365 admin and a CRM admin.

ClickDimensions: What a bug, what a bug..

Published by:

We use ClickDimensions a lot for marketing automation. It’s a pretty good tool. But today, working with their support, I came across this doozie of a bug.

I had a form that was working fine unless an option set for country was on it.

Here’s what we found – when you create a web form and map it to an option set, it will replace the substring “options” in the field name with “select”. My option set was called “new_countryoptionset” and ClickDimensions failed because internally it thought the field was called “new_countryselectet”. I recreated the field and called it “new_countrypicklist” and it worked fine.

I don’t know whether to laugh or cry. But if you are having issues with option sets in ClickDimensions keep this in mind.

CRM for Phones: “A primary entity column cannot have a dot in its name”

Published by:

When using the new CRM For Phones and CRM for Tablets apps against Microsoft Dynamics CRM 2015 Online Update 1, I was getting the following error.

“Sorry, something went wrong while initializing the app. Please try again, or restart the app.”

I looked into some of the standard troubleshooting steps for this one and none of them applied.

So I enabled tracing. It was a big PITA to install all 150mb of iTunes and wait for sync, but it was worth it because I found the following error in the logs.

Error Message:Microsoft.Crm.CrmInvalidOperationException: Unable to process the following entities - contact. Please try disabling the entities and try again. ---> Microsoft.Crm.CrmException: A primary entity column cannot have a dot in its name

The key being A primary entity column cannot have a dot in its name.

Because “having a dot in its name” alludes to an aliased field, I checked that my custom code didn’t contain anything odd – it didn’t. I finally found the problem in a corrupt View definition.

2015-07-27 13_36_02-View_ Contacts_ No Orders in Last 6 Months - Microsoft Dynamics CRM - Internet E

2015-07-27 14_44_14-Message from webpage

After removing the bad columns, the mobile app fired up no problem.

ISV code aborted the operation.

Published by:

When writing a Microsoft Dynamics CRM plugin, you might come across the dreaded “ISV code aborted the operation” (isvaborted) error – code -2147220891 or 80040265.

In my experience this means the plugin is calling itself. For example, if your plugin fires on change of estimatedvalue on Opportunity, and your plugin updates estimatedvalue, you’re going to get an infinite loop.

Nip it in the bud with this simple line:

if (context.Depth > 1) {
                trace.Trace("Plugin has called itself. Exiting.");
                return;
            }

Configuring the Outlook Plugin: “A duplicate value cannot be inserted into a unique index.”

Published by:

Ever get this when configuring the Outlook client?

Error| Exception : A duplicate value cannot be inserted into a unique index.
[ Table name = MetadataSchema.EntityClientStatistics,Constraint name = PK__MetadataSchema.EntityClientStatistics__00000000000007A9 ] at System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommandText(IntPtr& pCursor, Boolean& isBaseTableCursor)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
at Microsoft.Crm.Application.SMWrappers.CrmSqlCeCommand.<ExecuteNonQuery>b__0()
at Microsoft.Crm.Application.SMWrappers.CrmSqlCeCommand.ExecuteCommandWithRetry[TResult](Func`1 body, Int32 retryCount)

Simple solution: Uninstall the plugin profiler.

Perform a Mileage Expense Calculation Using EasyPlugins

Published by:

We’ve recently deployed a few custom plugins based on the EasyPlugins tool for Microsoft Dynamics CRM 2013 and Microsoft Dynamics CRM 2015, available for free on CodePlex. In this blog we’d like to share some of the things we’ve learned, since the tool is great but not very well documented.

In this blog we’ll review how to calculate an expense mileage reimbursement amount. There are other ways to do this, but this way allows for business users to manage reimbursement rates and effective dates. I’m also making it it slightly complex because I want to demonstrate some of the things that aren’t covered in the official documentation.

In my example, I have:

  • An entity called ics_expense that contains:
    • A date (ics_date) that, by business requirement, the user must be able to leave blank. If they do, the date will default to today.
    • The per-mile rate that was applied (ics_permilerate), a Currency value.
    • A distance value (ics_distance) as a Decimal value.
    • An Amount value (ics_amount), also a Currency, that represents the amount to be paid to the employee.
    • An Option Set (picklist) (ics_type)
      • 100000000 : Itemized Expense
      • 100000001 : Mileage
  • An entity called ics_mileagereimbursementrate, which contains:
    • An effective start date (ics_effectivestartdate) as a Date
    • An effective end date (ics_effectiveenddate) as a Date
    • A reimbursement rate (ics_reimbursementrate) as Currency.

When the Expense is saved, this plugin should:

  • Identify if the user filled in the date (ics_date), if not default it to today.
  • Pull the mileage reimbursement rate that should be applied based on that date. (Design assumption is that there’s only one rate applicable for a given date.)
  • Calculate the amount to reimburse.
  • Fill in the total amount on the Expense but only if it’s of type Mileage.

Here’s the approach:

  1. Stage the data (left side of the EasyPlugins screen)
    1. Pull down the user-input date, the type, and the distance and store them in variables local to EasyPlugins.
    2. Determine if I need to use the user’s date or default it to today.
    3. Pull down the appropriate rate.
    4.  Calculate the total reimbursement.
  2. Update the Expense (right side of the EasyPlugins screen)
    1. Set the Per Mile and total expense fields on the Expense.
    2. Set the date on the Expense if the user left it blank.

Start by creating a new Plugin using EasyPlugins (easyplugins.codeplex.com) – install the package then access it under Settings -> EasyPlugins then selecting New -> Plugin. Give it a name and set it to run PostCreate, Post Update, and Synchronously.

First I need to stage up some data on the left side. First, I’m going to pull down the metadata from the entity. Add Attributes:

  • ics_date – becomes #p1
  • ics_distance – becomes #p2
  • ics_type – becomes #p3

For these, we want the State to be “after action” since we want the value the user actually input.

Now, we determine whether to use the user’s date or ours. To do this, we check if the user input one and if so use that, otherwise make a new one. To do this we use C# syntax in a Calculated field:

#p1 == null ?
DateTime.Today :
#p1

ep-1
Hit Test to make sure your expression is syntactically correct. This will place your new value in #p4.
You may find this syntax unfamiliar. In a more familiar format, it might look like:

if (p1 == null)
#p4 = new DateTime();
else
#p4 = #p1;

Now, find the Expense reimbursement rate to use. We need to find the rate that applied as of the date of the Expense, which is now held in #p4. To do this, we need to find the rate with an effective start date in the past, an effective end date in the future. Construct an Advanced Find like this – the dates you use don’t matter at this point. Make sure the reimbursement rate is in the returned values – add it using Add Columns.

Download the FetchXML. A few edits you need to make:

  1. Delete all attributes except ics_mileagereimbursementrate. That’s the number we’re really after.
  2. In the date fields, replace the value of the date with your calculated date variable, #p4.

It’ll look like this when ready:

<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="ics_mileagereimbursementrate">
    <attribute name="ics_reimbursementrate" />
    <order attribute="ics_reimbursementrate" descending="false" />
    <filter type="and">
      <condition attribute="ics_effectivestartdate" operator="on-or-after" value="#p4" />
      <condition attribute="ics_effectiveenddate" operator="on-or-before" value="#p4" />
    </filter>
  </entity>
</fetch>

Remember, our assumption going in was that there’s only one reimbursement rate that applies for a given date. So this should only return one record.

Now drop it in a Request parameter. In this case the Aggregate Function is “First” since we are expecting only one record to come back and we want the value from the first returned record.
ep-3
The results of this will be placed in an array called #p5.

The last thing to do is to use this rate to calculate the actual amount to reimburse. Enter a Calculated parameter and use this syntax:

Convert.ToDecimal(#p5[0]) * #p2

We need to be careful here.

  • #p2 is the mileage and is a Decimal value. So to make sure we’re calculating apples to apples, we need to convert the value returned from the query by using Convert.ToDecimal (C# syntax).
  • #p5 is the results of our query. Since we didn’t use an aggregate function, it comes back as an array. Rather than using the variable #p5 straight-up, we need to force it to read the first record, thus the [0] syntax.

ep-4
And that’s #p6, the actual amount to pay the employee.

Phew. A lot of work but our data is now staged and ready to go. To recap:

  • #p1 is the date the user set on the entity.
  • #p2 is the distance traveled as provided by the user.
  • #p3 is the option set that indicates the type of Expense (mileage or otherwise).
  • #p4 is the date the expense occurred – either provided by the user or defaulted to today.
  • #p5 is the array of expense rates that encompass your effective date.
  • #p6 is the total reimbursable amount (distance * rate).

First, we need to update the Expense with the mileage rate and the calculated value. On the right side, Add an Update record.

  • Entity is ics_expense.
  • Where ID = #id (Current ID) – we’re updating the current record, not anything related.

But how do we filter it so that this only happens if the type is Mileage on the Expense? We need to get it to read the Option Set (picklist) value. That’s stored in #p3. Option Sets are managed by EasyPlugins with a Code and a Label attribute. To get at them, you need to use:

#p3.Code

But since we are looking for a Condition here, you need to force the Code value to a String and then check if it’s the value you’re after.

#p3.Code.ToString().Equals("100000001")

Tricky, but that’s what worked for me. Then, just set the value of the amount and the rate.
ep-5
Then, set:

  • ics_amount = #p6
  • ics_permilerate = #p5[0]

What’d we miss? We didn’t set the Date. We need to use a separate Action step because we only want to override it if the user didn’t fill it out, regardless of the type of Expense. (The last Action only applied to Mileage records.)

Last step: make another Update action.

  • Entity = ics_expense
  • Filter = #p1 == null (you can check if a variable is null with a simple == operator like this)
  • Where ID = #id

And set the date field to #p4.
There you have it, folks.

E-Mailing Line Item Details

Published by:

It’s great how everything in CRM uses the XRM object model – it’s so easy to link entities.

But sometimes it can be annoying. Like, what if I want to e-mail a Quote to a customer, or to an internal customer service person who may not be a CRM user? I can’t just drop the line items into an workflow email since it’s a 1:N.

I created a custom workflow activity that does just this. It takes a sales entity’s line items (Opportunity Product, Quote Detail, Order Detail, or Invoice Detail) and assembles it into a string that you can drop into an email. It’s configurable on how it handles bundles and with currency symbols.

Check it out on Codeplex!

Codeplex Project

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.