Saturday, March 3, 2018

MS CRM ActivityParty REST Queries

I recently had to create a rollup display showing all activities under a given entity. Pulling the sender or receiver out of an email activity requires a few steps.

Adding confusion the email entity has a text field ToRecipients which is an accumulated convenience text field. I would avoid using it as it is not complete if an email is sent to an email address that is not associated with a CRM contact, user etc. In this case the ToRecipients field will be null and the activity party AddressUsed field must be evaluated.

To fully reach the sender and receivers of email activities from an ActivityPointer query, the REST query needs to expand to include the activitypointer_activity_parties link table. You will need the ParticipationTypeMask to find out the role of each related activityparty, and both the AddressUsed and PartyId attributes.

Below is a sample query for all activities (ActivityPointer) related to an entity.
function retrieveActivities() { var caseId = AllCases[ProcessCaseIndex].Id; var parameters = "$select=ActivityId, RegardingObjectId, Subject, ActivityTypeCode, CreatedOn, ScheduledStart, Description, StateCode, OwnerId"; var expandParameters = ",activitypointer_activity_parties/ParticipationTypeMask,activitypointer_activity_parties/PartyId,activitypointer_activity_parties/AddressUsed "; var expand = "&$expand=activitypointer_activity_parties"; var filter = "&$filter=RegardingObjectId/Id eq (guid'" + caseId + "')"; var orderby = "&$orderby=CreatedOn"; var options = parameters + expandParameters + expand + orderby + filter; SDK.REST.retrieveMultipleRecords("ActivityPointer", options, retrieveActivitiesCallBack, function (error) { alert(error.message); }, activitiesRetrieveComplete); }

The ActivityPointer.ActivityTypeCode specifies the activity types:
  • email, appointment, phonecall, task, letter, fax, serviceappointment, campaignactivity, bulkoperation
  • caseresolution, opportunityclose, quoteclose, campaignresponse
The ParticipationTypeMask specifies the activity party role.
1 From, 2 To, 3 CC, 4 BCC, Owner 9, Regarding 8, and a number of others.

In the following code snipet we are just looking for the Sender or From activity party for email activities. The PartyId.Name is used if available otherwise it looks at the AddressUsed field, but this field is used in all the other email activity parties (From, To, CC, BCC).

function retrieveActivitiesCallBack(retrievedActivities) { for (var i = 0; i < retrievedActivities.length; i++) { var activity = retrievedActivities[i]; var type = activity.ActivityTypeCode; if (type == "email") { var activityParties = activity.activitypointer_activity_parties.results; for (var j = 0; j < activityParties.length; j++) { var activityMask = activityParties[j].ParticipationTypeMask.Value; // Sender Only if (activityMask == 1) { if (activityParties[j].PartyId.Name == null) { senderName = activityParties[j].AddressUsed } else { senderName = activityParties[j].PartyId.Name; } break; } } }
...

Friday, December 15, 2017

MS CRM Email Template with dynamic id in link

You are trying to create a custom email to a group of contacts and want to create a URL with a contactid in the query string to customize what they see when they click on that link.

It turns out that you need to understand two things to make this work.

In the Template Editor when you press Insert/Update there is no option for the contactid.

#1 Generating a contact id


To create a contactid merge field you need to type the following which is case sensitive.

{!contact:contactid;}

Now Save the Template and this text will turn into a working merge field that returns the contact id of the contact the email is being sent to.

{!Contact:Contact;}

#2 Be careful about links in the Email Template Editor


The following two lines will not be processed the same way when the emails are sent.

The following line will be merged properly and the contact's email browser will typically display a link.

https://www.myportal.com/OrderService.aspx?id={!Contact:Contact;}&type=0


This line may surprise you since the email will look fine but the link won't work. 
https://www.myportal.com/OrderService.aspx?id={!Contact:Contact;}&type=0

What you get is the following:




Notice that the text of the link is merged, but the href value is not.


What if you want a nicely formatted link?

Please click here to find out more!

At this point I "believe" your only course of action is to manually modify the XML in the email template body in SQL. I haven't tried that yet, but will post additional information if I do.



Wednesday, October 4, 2017

MS Dynamics CRM Web Resources Basic Starter Page

When creating a Web Resource Page in MS CRM there are some basic parts that I've tend to use in all of my pages. These are all contained in the script block above the body.

User Information:

     UserId = Xrm.Page.context.getUserId();
     UserName = Xrm.Page.context.getUserName();

Query String parameters to drive the page

     QueryStringParameters = Xrm.Page.context.getQueryStringParameters();
     
     QueryStringParm = "None";
   
     if (QueryStringParameters["feature"] != null) {

         QueryStringParm = QueryStringParameters["feature"].toString();             
     }

Entity Id  - when you are running in a Form iframe and the entity has been saved.

     EntityId = window.parent.Xrm.Page.data.entity.getId();
 
     Id = QueryStringParameters["id"].toString();

Trigger for Query

  document.onreadystatechange = function () {

         if (document.readyState == "complete") {

           // Typically call a query to populate the page

         }
     }

Function to Open a CRM Form in a new window

  function OpenCRMForm(entityName, entityId) {

         var windowOptions = {
             openInNewWindow: true
         };

         Xrm.Utility.openEntityForm(entityName, entityId, null, windowOptions);

     }

Saturday, July 25, 2015

On Premises MS CRM 2011 to CRM 2015 Upgrade

Other than my CRM Online customers, the rest of my customer base which are using on premises systems still run CRM 2011 and are looking to upgrade to CRM 2015. While my customer base is a small subset of the microcosm, I am betting they are not alone. This is a high level overview that I will modify and embellish over time.

This upgrade will need to be done in stages and will require an intermediate upgrade to CRM 2013. 

Step 1: Upgrade your SQL server system if necessary:

CRM 2015 discontinues support for SQL Server 2008 / 2008R2

However CRM 2015 continues to support Windows 2008 / 2008R2

Step 2: Any CRM 4.0 SDK solutions will no longer be supported, so make sure that all of your solutions are using the CRM 2011 SDK as a starting point.  This includes all of your plugins, custom workflows, web applications and scheduled applications that use the CRM API.

Step 3: it is recommended that CRM 2011 should be updated to Rollup 14 before starting.

Step 4: Run the Custom Code Validation Tool to test your JavaScript Form customizations. http://blogs.msdn.com/b/crm/archive/2012/06/21/microsoft-dynamics-crm-2011-custom-code-validation-tool-released.aspx

Step 5: Run the Legacy Feature Check Tool to see if you are still using any CRM 4.0 API calls.

Warning:  Inventory any SQL Views or direct SQL queries or updates that you may have created against the actual CRM database tables. It is recommended to always use the CRM Views and not access the CRM database directly, but in the event that you did anything unsupported, the Base and Extension tables will be merged at the end of this process.

Running the CRM 2013 Upgrade

There are 3 upgrade options ranging from upgrading In-place which is over the top of your existing system to migrating your data from your existing CRM system to a completely new CRM 2013 system. 

If you have VM’s you will want to try the In-place upgrade first as it updates similar to a rollup and if successful is the fastest.

CRM 2013 Modifications

Optional 1: Most of the XRM systems I’ve worked on have numerous plugins that fire when the vast majority of the CRM entities are modified. If you have a large number of plugins that fire like this and your system could be bogged down by frequent saves by your users, you may consider disabling the new Autosave feature in 2013 at least initially. 

http://blogs.msdn.com/b/crm/archive/2013/10/15/manage-auto-save.aspx

This feature can be disabled on a form by form basis later.

Optional 2: Microsoft recommends migrating to the new forms created for each entity in 2013.  The CRM 2011 forms will be named “Information”. The new 2013 forms will have the entity name like “Account”.  From the new entity form there is a Merge feature that will pull the customizations from the 2011 form and put those customizations at the bottom of the form.  There will be some work to clean these forms up.

Optional 3: You can merge your database in 2013 however this is a required step in the CRM 2015 Upgrade, so I prefer to let the latest upgrade code take care of this step. This is the merger of the Base and Extension tables in the CRM database.

Running the CRM 2015 Upgrade

There are 3 upgrade options ranging from upgrading In-place which is over the top of your existing system to migrating your data from your existing CRM system to a completely new CRM 2013 system.

Since this is the step when the database tables will be merged, I would suggest going with the MS recommendation to migrate from the Updated 2013 system to a fresh CRM 2015 system especially on a production system. If you have VM copies to play with, upgrading In-place is less of a risk.

CRM 2015 Modifications

MS says that CRM 2015 will use CRM 2011 SDK plugins but substitute calls to the 2011 SDK with calls from the CRM 2015 SDK.

However there are some important changes that need to be handled.

As of CRM 2015 Update 1 which is currently only used on CRM Online as I type this.  For plugin registration the SetState and Assign messages have been deprecated. Ownership and the state of an entity can now be changed using Update rather than separate AssignRequest and SetStateRequest messages.

Once your plugins are registered to fire on “Update” make sure to set their filtering attributes to be appropriate  “ownerid” and “statecode” for example and you may need to check your execution order if you have other plugins firing on the Update of an entity.

Friday, April 4, 2014

MS CRM 2013 Online upgrade to Office 365 authentication

Recently many CRM Online customers were transitioned from Live ID to Office 365 authentication.

The result is that the earlier Live ID users accounts no longer exist and any software still using Live ID authentication stopped working.

This requires updating how any portal code authenticates and using a new Office 365 user account to authenticate with.

In addition the CRM 2013 Email Router needs to be reconfigured and updated to the Rollup 2 version ( at this moment in time ).

O365 Authentication

If you start with the latest CRM 2013 SDK  example AuthenticateWithNoHelp solution, you can add the code snippet below to the file  AuthenticateWithNoHelp.cs to get O365 authentication to work.

image

First open and compile this solution. Than add the code snippet below to the AuthenticationWithNoHelp class.

The following gives you an IOrganizationService and an IOrganizationServiceContext

If you are incorporating this into an existing CRM 2011 solution, you will need to update your Microsoft.Xrm.Client and Microsoft.Xrm.Sdk references.

The global variables beginning with underscores are used internally in the AuthenticationWithNoHelp class. Substitute your organization name for {orgname}.

public IOrganizationService OrgService { get; set; }
public OrganizationServiceContext OrgContext { get; set; }

private String _discoveryServiceAddress = "https://dev.crm.dynamics.com/XRMServices/2011/Discovery.svc";
private String _userName = "newusername@{orgname}.onmicrosoft.com";
private String _password = "password";
private String _domain = "";



public Authenticate()
{
IServiceManagement<IDiscoveryService> serviceManagement = ServiceConfigurationFactory.CreateManagement<IDiscoveryService>(new Uri(_discoveryServiceAddress));
AuthenticationProviderType endpointType = serviceManagement.AuthenticationType;

IServiceManagement<IOrganizationService> orgServiceManagement = ServiceConfigurationFactory.
CreateManagement<IOrganizationService>( new Uri("https://{orgname}.api.crm.dynamics.com/XrmServices/2011/Organization.svc "));

AuthenticationCredentials credentials = GetCredentials(orgServiceManagement, endpointType);

using (OrganizationServiceProxy organizationProxy =
GetProxy<IOrganizationService, OrganizationServiceProxy>(orgServiceManagement, credentials))
{
OrgService = organizationProxy;

organizationProxy.EnableProxyTypes();

OrgContext = new OrganizationServiceContext(organizationProxy);
}
}


Email Router
If you are running an older version of the email router update to the appropriate 32 or 64 bit version found here.
http://www.microsoft.com/en-us/download/details.aspx?id=42272
Install and reboot.
 
To reconfigure your Email Router:
1. Change the Server URL  from dev.crm.dynamics.com to disco.crm.dynamics.com
2. Change the User Name and Password to new a new Office 365 account
image

Then under Configuration Profiles Set the Outgoing Access Credentials to match the Office 365 Credentials used above.

image

Now

  1. Publish your changes
  2. Load Data
  3. Test Access

That may not cover every Email Router configuration change, but it should cover most of them.

Saturday, August 4, 2012

MS CRM and Portals (fast caching method)

Let’s say that you have a portal application that is very data driven by highly related data, or that has some displays that require a lot of heavily related data to very flexibly dynamically build the controls on the pages.  Leveraging the quick prototyping of MS CRM to design your schema, and managing this controlling data within  MS CRM helps abstract away a lot of grunt work and very quickly get  your portal online,  but the time required to retrieve a long stream of related data by web services is affecting the performance of your website. Because your pages are very data driven depending on the user who logs in some of the website caching available out of the box isn’t helping much because of many web services calls required to customized the information.

What if you could cache all of this related data that you needed in an extremely quick and efficient way so that the portal could get the data that drives it in an nearly instant way?

In this example there is a MS CRM Online system and an interactive Dashboard displayed on a Portal. This is a simple example, but the real project contains a very large amount of data nested many levels deep.  All the queries by web services would take well over a minute or two to run, but a typical portal user is not that patient.

Caching Mechanism

Advantages of this mechanism:

1. Requires a single select to a local database to fetch extremely complex relational data that is already organized into a consumable format.

2. Utilizes a background task to do the heavy lifting to keep things up to date without the portal user having to wait for a many web services calls to MS CRM and also allows for processing of the raw data if need be.

3. Can be part of a plan to allow a portal to be self sufficient for periods of time when the CRM system is not available.

Overview

Part 1: Filling the Cache

  1. Query data from MS  CRM via web services in a background task. This could be a scheduled applications or service on a timer that keeps the cached updated.
  2. Put data into related serializable containers under one container with all of the information needed for a single account in the format needed to drive your application.
  3. Serialize this container
  4. Write it to the local SQL database as a single blob with an identifier.

Part 2: Reading the Cache:

  1. With a single select from the local database return the entire blob of information needed.
  2. Deserialize this blob
  3. Cast it to the Parent container class.
  4. Consume it in your Portal application.

Example Code 

This example is very simplistic. The class structure and queries to pull MS CRM data can be as complex as needed. The real gains to using this are when there is a lot of related data requiring a large number of queries.

Nested Serialized Classes

All classes must be serializable. The top level container class will contain all other containers or lists of containers

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Serialization;


/// <summary>
/// Top Level Serializable Container
/// All classes contained must also be serializable
/// </summary>
[Serializable]
public class DashboardBlob
{
public Guid AccountId;
public DateTime LastUpdated;
public List<ContactS> students = new List<ContactS>();
}

/// <summary>
/// Nested Serializable class
/// </summary>
[Serializable]
public class ContactS
{
public Guid ContactId;
public string FirstName;
public string LastName;
public string EMailAddress1;
}

Filling the Serialized Classes

This can be done in any way that queries the data needed and fills the appropriate serialized classes.

/// <summary>
/// Create Top level container and call methods to fill all nested containers
/// </summary>
/// <param name="accountId"></param>
public static DashboardBlob FillDashboardBlob(Guid accountId)
{
var blob = new DashboardBlob
{
AccountId = accountId,
LastUpdated = DateTime.Now,
students = GetSerializedContactsByAccount(accountId)
//pull all other related data and nested data.
};

return blob;
}

/// <summary>
/// Query that pulls data from a CRM context and stores it in a serializable class
/// </summary>
/// <param name="accountId"></param>
/// <returns></returns>
public static List<ContactS> GetSerializedContactsByAccount(Guid accountId)
{
var serializedContacts =
(from c in DemoContext.ContactSet
join students in DemoContext.new_account_studentSet on c.ContactId equals students.contactid
where students.new_accountid == accountId
orderby c.LastName, c.FirstName
select new ContactS
{
ContactId = c.ContactId.Value,
FirstName = c.FirstName,
LastName = c.LastName,
EMailAddress1 = c.EMailAddress1
}).ToList();

return serializedContacts;
}

The code below this point is nearly generic with the exception of the field names for your SQL tables.
Create a simple local SQL table for cache storage
image
Save/Update the Local SQL Cache
Now that you have the data stored in a serialized container, serialize that container and store in the local SQL server database as a blob.
/// <summary>
/// Update that writes the blob back to SQL
/// </summary>
/// <param name="blob"></param>
/// <param name="connectionString"></param>
/// <param name="isCreate"></param>
public static void SaveUpdateDashboardBlob(DashboardBlob blob, string connectionString, bool isCreate)
{
var bytes = SerializeAnObject(blob);
var connection = new SqlConnection(connectionString);
SqlCommand sqlCmd;
connection.Open();
if( isCreate)
sqlCmd = new SqlCommand("INSERT INTO dbo.MainCache(accountid,updated,dashboardxml) VALUES (@id,@updated,@xml)", connection);
else
sqlCmd = new SqlCommand("UPDATE dbo.MainCache Set updated=@updated, dashboardxml=@xml where accountid=@id", connection);
sqlCmd.Parameters.Add("@id", SqlDbType.UniqueIdentifier);
sqlCmd.Parameters["@id"].Value = blob.AccountId;
sqlCmd.Parameters.Add("@updated", SqlDbType.DateTime);
sqlCmd.Parameters["@updated"].Value = blob.LastUpdated;
sqlCmd.Parameters.Add("@xml", SqlDbType.Xml, Int32.MaxValue);
sqlCmd.Parameters["@xml"].Value = bytes;
sqlCmd.ExecuteNonQuery();
connection.Close();
}

public static string SerializeAnObject(object AnObject)
{
var Xml_Serializer = new XmlSerializer(AnObject.GetType());
var Writer = new StringWriter();
Xml_Serializer.Serialize(Writer, AnObject);
return Writer.ToString();
}


Read the Local SQL Cache
This is the payoff. A single call to the local database returns a completely organized sorted and ready for consumption data container.
/// <summary>
/// Read the blob from local SQL database, deserialize it and cast it back to the container class.
/// </summary>
/// <param name="accountId"></param>
/// <param name="connectionString"></param>
/// <returns></returns>
public static DashboardBlob GetDashboardBlob(Guid accountId, string connectionString)
{
var connection = new SqlConnection(connectionString);
connection.Open();
var adapter = new SqlDataAdapter(string.Format("select dashboardxml from dbo.MainCache where accountid='{0}' ", accountId), connection);
var ds = new DataSet();
adapter.Fill(ds);
DataTable table = ds.Tables[0];
connection.Close();

if (table.Rows.Count != 0)
{
var data = (DashboardBlob)DeSerializeAnObject(table.Rows[0][0].ToString(), typeof(DashboardBlob));
return data;
}
return null;
}

public static Object DeSerializeAnObject(string xmlOfAnObject, Type objectType)
{
var strReader = new StringReader(xmlOfAnObject);
var xmlSerializer = new XmlSerializer(objectType);
var xmlReader = new XmlTextReader(strReader);
try
{
Object anObject = xmlSerializer.Deserialize(xmlReader);
return anObject;
}
finally
{
xmlReader.Close();
strReader.Close();
}
}


Thursday, June 2, 2011

The new MS CRM 2011 64 bit VM and VirtualBox

I’ve gotten a number of emails asking what was needed to get the new Partner Source MS CRM 2011 64bit Virtual Machine to work in Virtual Box in Windows 7.

The compatibility issue appears to be the default hard disk settings. The following is a workaround that has worked for many people.  I’ve been told this doesn’t work on Windows 2008 Servers, but on a 2008 server you could also run Hyper-V.

Change your VHD to run from an IDE controller:

Open up your storage settings and add the vhd as an IDE drive.  Click the right + icon to create a new virtual hard  drive. Choose an existing drive.

image

Select the VHD file the you decompressed and open it.

image

Now  remove your SATA drive

image

Now Click OK.

image

Memory

This VM likes 2.5Gb or more RAM to operate in. As always more is better especially if you are going to demonstrate both CRM and SharePoint together.

Now Start it up!

Addendum

You should enable hardware virtualization when running 64-bit OS’s.  This  is a firmware setting on your motherboard. This is normally called “Intel Virtualization”,  “VT-x” or “Intel VT” and AMD has an equivalent setting. Laptops frequently have this disabled by default.

Tuesday, March 8, 2011

CRM 2011 OData, JSON and CRM Forms

1. Generating OData queries

OData commands can easily be tested out in IE. To help with this, you will want to turn off the feed reading view in IE.

image

The first thing that you will want is the name of the Sets that you will be calling on. If you use a path like the following it will provide a listing of the names of your sets. http://crmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc

You will see a list of them in the following format.

- <collection href="AccountSet">
<atom:title>AccountSet</atom:title>
</collection>

Since these names are case sensitive you will want to look at the names of your custom entities. Your stock items like AccountSet and ContactSet will be camel cased, but your custom entities will likely show up as new_myentitySet.

http://crmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc/AccountSet

As you can see the field names use the Schema Name in CRM rather than the lowercase name the is used on the CRM forms. This is something that can cause confusion.

<d:Address1_Name m:null="true" />
<d:Address1_Telephone2 m:null="true" />
<d:OverriddenCreatedOn m:type="Edm.DateTime" m:null="true" />
<d:Telephone3 m:null="true" />
<d:DoNotBulkPostalMail m:type="Edm.Boolean">false</d:DoNotBulkPostalMail>
If you specify the guid of that entity, the results returned will be for that one entity. All fields are returned whether they are null or not. This listing will show you the exact case of each attribute name that you may want to query.
You can save bandwidth by only selecting the fields that you need and only those fields will be returned. Below will only return the AccountNumber and AccountName
There are numerous references from MS explaining how to build OData Queries, however, Rhett Clinton’s recent addition to Codeplex is probably the easiest way to generate these queries. His tool can be found at the link below.
Screen shot of his Query Designer
CRM 2011 OData Query Designer
2. Using OData Queries with JSON
To use any of the following in a javascript library as webresource in CRM 2011 solution, you will first need to include a jquery library and a json library. The jquery1.4.1.min.js and json2.js files can be found in the most recent MS CRM SDK. sdk\samplecode\js\restendpoint\jqueryrestdataoperations\jqueryrestdataoperations\scripts. Add these a libraries and include them above the JavaScript library that you put your code in. Click here to see what that looks like in the Forms Property page.
To utilize these OData queries in a CRM Form using JSON, there is a pretty standard template to use. Just set the odataSelect below to your OData select url, and use the appropriate return method.
var odataSelect = "Your OData Query";

$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
datatype: "json",
url: odataSelect,
beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
success: function (data, textStatus, XmlHttpRequest)
{
// Use only one of these two methods

// Use for a selection that may return multiple entities
ProcessReturnedEntities(data.d.results);

// Use for a single selected entity
ProcessReturnedEntity(data.d);

},
error: function (XmlHttpRequest, textStatus, errorThrown) { alert('OData Select Failed: ' + odataSelect); }
});

Notice the two methods:
  • ProcessReturnedEntities(data.d.results)
  • ProcessReturnedEntity(data.d)

When selecting what could be any number of entities, there will be an array returned, and you want to look at the data.d.results.  When selecting a specific Guid there is no results array created, and you will need to just look at the data.d that is returned.

For example:
function ProcessReturnedEntities(ManyEntities)
{
for( i=0; i< ManyEntities.length; i++)
{
var oneEntity = ManyEntities[i];
var accountNumberAttribute = oneEntity.AccountNumber;

var accountNumberValue = eval(oneEntity.AccountNumber);
}
}

function ProcessReturnedEntity(OneEntity)
{
var oneEntity = OneEntity;
var accountNumber = oneEntity.AccountNumber;

var accountNumberValue = eval(oneEntity.AccountNumber);
}

Entity Attributes
As you can see the JavaScript entity objects returned have attributes named with matching camel casing in the OData query.
In that case of simple CRM attributes like:
  • string
  • memo
  • decimal
  • double
  • integer

You can get the value from them by simply using eval like shown in the example above.

For the following CRM attributes there is more involved.

  • optionset
  • money
  • datetime
  • lookup

For example:

var moneyValue = eval( oneEntity.new_MoneyAttribute.Value);
var optionSetValue = eval ( oneEntity.new_OptionSet.Value);
 
Setting CRM Form Fields with Queried Values
This gets a bit more complex when setting values to CRM form controls.
  1. The form field names are all lower case, so the retrieved names do not match.
  2. The form fields have validation and maintain more types than the returned OData values have.
  3. There is some conversion required between them.

You can find out the type of a form control as follows:

var attrType = Xrm.Page.getAttribute("accountnumber").getAttributeType();

With the type you can then use the appropriate means to set form controls.

string, memo fields:

Xrm.Page.getAttribute("accountnumber").setValue(eval(oneEntity.AccountNumber));

decimal, double fields:
Xrm.Page.getAttribute("new_float").setValue(parseFloat(eval(oneEntity.new_Float)));

integer fields
Xrm.Page.getAttribute("new_integer").setValue(parseInt(eval(oneEntity.new_Integer)));

money fields
Xrm.Page.getAttribute("new_moneyattribute").setValue(parseFloat(eval(oneEntity.new_MoneyAttribute.Value)));

optionset fields
Xrm.Page.getAttribute("new_optionset").setValue(eval(oneEntity.new_OptionSet.Value));

date fields
var fieldValue = eval(oneEntity.new_DateTime);                       
var dateValue = new Date(parseInt(fieldValue.replace("/Date(", "").replace(")/", ""), 10));
Xrm.Page.getAttribute("new_datetime").setValue(dateValue);

The addition of support for JSON, OData queries in MS CRM 2011 has
added some great power to what can be done easily in the JavaScript of a CRM form. This should reduce the number of solutions that require web applications running in iframes dramatically. This is a good thing since MS is phasing out the ISV folder for web applications running in the same app pool on a web server and there is currently no support for aspx files in CRM solutions.

Friday, March 4, 2011

CRM 2011 Set Default Transaction Currency in JavaScript

The following function uses JSON and OData to select the first Currency entity that is configured on your system and then sets the transactioncurrencyid lookup to be that currency.

This particular feature is just as relevant for CRM 2011 as it was for CRM 4.0, but it is easier to implement now.

The OData selection for this consists of the following:
/TransactionCurrencySet?$select=TransactionCurrencyId,CurrencyName

If you have multiple currencies, you can always add a filter to OData select to grab the specific currency that you need. This function uses the first currency returned.

function SetDefaultCurrency() { var context = Xrm.Page.context; var serverUrl = context.getServerUrl(); var ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc"; var odataSelect = ODataPath + "/TransactionCurrencySet?$select=TransactionCurrencyId,CurrencyName"; $.ajax({ type: "GET", contentType: "application/json; charset=utf-8", datatype: "json", url: odataSelect, beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); }, success: function (data, textStatus, XmlHttpRequest) { var myCurrency = data.d.results[0]; var idValue = eval('myCurrency.TransactionCurrencyId'); var textValue = eval('myCurrency.CurrencyName'); var thisEntityType = 'transactioncurrency'; Xrm.Page.getAttribute("transactioncurrencyid").setValue([{ id: idValue, name: textValue, entityType: thisEntityType }]); }, error: function (XmlHttpRequest, textStatus, errorThrown) { alert('OData Select Failed: ' + odataSelect); } }); }

In order to use this. You need to make sure that you include jquery1.4.1.min.js and json2.js as libraries first since this script relies on them. Create another library with the above function. If you just call the SetDefaultCurrency function in the OnLoad event of your form, the currency will be set as the form opens and will be be ready for you to set any currency values that rely on this.

image

The jquery1.4.1.min.js and json.js files can be found in the most recent MS CRM SDK. sdk\samplecode\js\restendpoint\jqueryrestdataoperations\jqueryrestdataoperations\scripts

Wednesday, March 2, 2011

CRM 2011 RTM VM Installation Differences

There are a few differences between the RTM installation and the Beta installation I have documented in earlier blog articles. Starting with the following: http://crmscape.blogspot.com/2010/09/creating-ms-crm-2011-vm-part-1-of-2.html

1. There are minor appearance changes in the CRM 2011 Installation screens, but everything else looks close enough that the Beta instructions still work.

2. The entire section of my instructions called SQL Reporting Services ( Configure Data Source ) is unnecessary, which is an improvement.  However I did add my Admin user account to the PrivUserGroup and PrivReportingGroup and then everything worked great.

blog_Admin properties

Otherwise the instructions still worked well for me. I’ll look over the updated installations instructions shortly to make sure there isn’t anything of importance that needs to be added.

I’m also using the latest Virtual Box 4.0.4 release for this which is working well. The only reason I used it instead of VMWare is that I had an existing VM just waiting for MS CRM to be installed with all the prerequisites in place.

I’ve been “living on a cloud” for the past few months in CRM Online and Windows/SQL Azure land, but now that the 2011 On Premises release is out, I’m back on terra firma working with that again too.

Wednesday, December 15, 2010

MS CRM 2011, the “Cloud”, Azure and the future

Something really important is happening right now, and if you are involved in MS CRM take notice or fall by the wayside!  This is a real game changer!

Almost all of my MS CRM installations were on premises installations until just this Fall.  Sure there were IFD configurations and web portals with Silverlight etc., but the customer was always in tight control of the servers, even if the servers were hosted.

I thought the next big thing would be the tighter SharePoint 2010 integration with MS CRM 2011 and to be fair I have seen interest in that and it will be a growing thing.

What I didn’t expect is that suddenly I would almost exclusively be working with hosted MS CRM systems, MS CRM Online and Windows Azure and SQL Azure. BTW I’ll be working with the new SQL Azure Reporting Services CTP this week as well.

What is pretty obvious is that MS has a lot of technologies coming together at about the time that MS CRM 2011 is hitting the scene. Frankly I thought it all looked interesting, but I didn’t expect the market to react so quickly or for it all to fit together so well.

What I also didn’t expect was how much I would enjoy this transfer of responsibility. I enjoy being in control of things, but I also love abstracting away busy work to concentrate on solving real problems.  That is the point of MS CRM. System administration work is not where the fun is, at least for me.

I was also pleasantly surprised at how easy using SQL Azure is. Just make sure you are using SQL 2008 R2 Management Studio and it is really nothing more than setting up an IP range you will be connecting through in the Azure portal, and changing your connection string. All of your tables need to have Clustered indexes as well and you have to use SQL scripts for all of your modifications, but otherwise it is just like the SQL you already know. Another thing to beware of is that the Web database is 1-5Gb and if you expect to grow beyond that you need to start with a Business Database which starts at 10Gb and can grow to 50Gb.

I’m currently working on a project that is using an Azure 10Gb database, the new to SQL 2008 geography fields and spatial indexes. I imported about 7Gb of data in about 3.5 hours. The database has been very responsive.  I did notice that when I ran a very intensive task like generating a Spatial index across 9.2 million records that it would occasionally idle my process, but it still finished in a respectable time.

Windows Azure, likewise, was very easy to set up.  Install the Azure SDK, create a Cloud Service Project, setup the Diagnostics and build a web application. If you make the Cloud Service your Start project it can be debugged locally as it will be deployed. There are a few configuration switches, but then again you are abstracting away all of the IIS administration.

Where this all gets interesting is that additional instances of your application can be added with a few key clicks and without purchasing equipment or understanding anything about network load balancing. Adjust the amount of CPU cores and memory your application needs as it grows.  Scale it back during the off season or grow though the ceiling.

I have customers who want to scale up quickly to compete with some pretty large competitors with a better product, but without the infrastructure or small army to maintain it. The Azure platform allows a small guy with a better mousetrap to take on the world.

As a lone gun for hire, without a staff of sysadmins, what this means to me is that I can create some really really big solutions very quickly and without the setup time normally involved, or the meetings with IT departments to budget for servers, and schedule for installation and configuration.

What this also is going to do is let a lot more killer apps actually see the light of day. I think we are going to see a new wave of enabled developers make a lot of really cool things happen.

The cost of admission to see if something will stick has really dropped and if your solution is really that good and your website is nailed with traffic, all you need is a few key clicks to give you more bandwidth, memory and processing power.

I was a bit skeptical at first, but as the saying goes, “People vote with their wallets”.  Based on what I am seeing the people are voting and much quicker than I expected!

Thursday, November 11, 2010

MS CRM Custom Workflow CreateCrmService permissions

If you are writing custom workflows and calling CRM services, you are probably familiar with the following code which creates a service with system administrator access to all data.  ( true = administrator access )

ICrmService service = context.CreateCrmService(true);

You may have noticed that any entities created by a custom workflow using this administrator access default to being created and owned by the account that your Microsoft CRM Asynchronous Processing Service runs as.

This has some trickle down depending on the account your async service is using.

For example if you are using the Local System Account which “typically” does not have a CRM user account dedicated to it, there are some things that you won’t have permission to do.

For example the Local System account doesn’t have permission to send an email and the following will fail even with a service having administrator access.

var sendRequest = new SendEmailRequest
{
EmailId = emailId,
IssueSend = true,
TrackingToken = String.Empty
};

sendEmailService.Execute(sendRequest);

This will give you the following SoapException:

<error>
  <code>0x80040216</code>
  <description>An unexpected error occurred.</description>
  <type>Platform</type>
</error>

You could create another service reference like below just to send email with which will work as long as the user running the workflow can normally send an email from a workflow.
ICrmService sendEmailService = context.CreateCrmService(false);

You could also use a CRM user CAL and create a CRM user for the account your async service runs as.

The important thing to realize is that any service request that requires user configuration in order to function beyond security role permissions will be dependent on whether there is a CRM user assigned to the account the acync service is running under and how that CRM user is configured. For example: whether the email access type is E-mail Router.

Wednesday, September 29, 2010

Developing for CRM 2011 and SharePoint 2010 Foundation

If you are a MS CRM person looking at SharePoint as something new here are a few things that may be of interest.

The SharePoint 2010 Foundation is free and will work with the MS CRM list component but it is limited to database sizes of 4Gb. This makes it much easier to leverage some of the power of SharePoint for your CRM customers. Your customers by default already have licensing for Windows Server and SQL Server in order to run MS CRM and SharePoint Foundation can share that same server.

In addition the new Business Connectivity Services (BCS) are also supported by SharePoint Foundation: http://msdn.microsoft.com/en-us/library/ee557646.aspx

BCS will allow you to expose data from other databases to show up in SharePoint, but not take space in the SharePoint database, this of course includes CRM data. There is obviously a performance penalty depending on the interface to access this external data, but it also allows maintaining legacy databases while having access to that data in SharePoint without passing the 4Gb database limit in the SharePoint database.

If you are looking at SharePoint integration using their web services, you can see from the following link which services are supported by both SharePoint Foundation and by SharePoint Server and just by SharePoint Server. http://msdn.microsoft.com/en-us/library/ee705814.aspx

As an obvious guideline it would make sense to write your base code to support SharePoint Foundation to let customers try out SharePoint functionality, and to have any features that require SharePoint Server features be switchable at a later time if your customer upgrades.

SharePoint Server has a number of useful features shown in this version comparison that you may or may not need: http://sharepoint.microsoft.com/en-us/buy/Pages/Editions-Comparison.aspx

As a practical note, SP 2010 is built on .NET 3.5 and CRM 2011 is built on .NET 4.0, so while most of you will not have a lot of .NET 4.0 legacy code at this point, is will be important to make sure your libraries with logic that will be shared between SP and CRM components are all kept at .NET 3.5.

Sunday, September 26, 2010

Connecting to MS CRM 2011 outside the VM

While the CRM 2011 VM created so far works well by itself, you will most likely want to access MS CRM web site, web services and endpoints from outside the VM for development purposes.

To do this you will need to set up VirtualBox to allow access from your host computer.

1. With your VM shutdown, modify the Network Settings and enable the second Adapter. As shown below:

  • Attached to: Host-Only Adapter
  • Name: VirtualBox Host-Only Ethernet  Adapter

image

Note: when a VM is created from scratch it appears to select the correct network adapter type. When I copied the VDI file to my laptop and setup a VM based on that VDI file, it picked a different adapter by default and the networking didn’t work until I made it match the original Network settings that I had when I first configured it.
image 

2. Start up your VM, open up a command prompt and run  ipconfig. Notice the new Local Area Connection 2 should be in your local network. The other is the default NAT adapter that allows your VM to see the Internet as your host computer.

image

3. On your host computer go to C:\Windows\System32\drivers\etc and edit your hosts file and add the new IP address to your hosts file.

image

4.You may want to add a new Credential to your Credential Manager to reduce your login time under Control Panel since you are not in the domain of the VM.

Use the Hosts alias created above, then add a domain\username, and password. This will give you a mostly filled out login when you try to reach the website for the first time.

image

5.  Test the connection out by bringing up MS CRM on your host. I’m using the alias in my hosts file pull up CRM.  http://w2008r2crmdev:5555/CRMDEMO

image

6. Now make sure you can access the endpoints through the browser

http://w2008r2crmdev:5555/CRMDEMO/xrmservices/2011/organizationdata.svc/$metadata 
http://w2008r2crmdev:5555/CRMDEMO/xrmservices/2011/organizationdata.svc/

image

7. If the endpoints show up in the browser than they should be available to add a reference to a Silverlight project as shown below.

VS Service Reference MetaData

Side Note: I have found that this VM is capable of running with 2.3Gb of RAM and on one core on my 2 year old Core Duo laptop. There is just enough memory to run VS 2010 on the host and connect to the guest VM’s endpoints as shown above in the 4Gb of RAM my laptop has. This is by no means an optimum configuration, but it is possible to demonstrate CRM 2011 beta with SharePoint integration and write code against both SDK’s.