How to Handle Custom Views in CRM

By | September 13, 2011

Included in the CRM customization project I’m working on is the automated creation of custom opportunity views based on some specific criteria. These views have to be made available to all users. It took me sometime to put this together, its implementation is not quite straight forward and requires some additional schema files the reason of my decision in showing how this can be done.

The schema file necessary to the implementation of this functionality contains the query to be included in the view, the fields to be returned from the query and the layout of the view.

The schema used in this example is as follows:

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

<layout>

<contact>

<fetchxml>

<fetch version=”1.0″ output-format=”xml-platform” mapping=”logical” distinct=”false”>
<entity name=”opportunity”>
<attribute name=”name” />
<attribute name=”customerid” />
<attribute name=”estimatedvalue” />
<attribute name=”statuscode” />
<attribute name=”opportunityid” />
<order attribute=”name” descending=”false” />
<filter type=”and”>
<condition attribute=”statecode” operator=”eq” value=”0″ />
<condition attribute=”customerid” operator=”eq” uitype=”contact” value=”{%CONTACTID%}” />
</filter>
<link-entity name=”contact” from=”contactid” to=”customerid” visible=”false” link-type=”outer” alias=”a_2e3c63cd00754701974cf441fc79f23b”>
<attribute name=”address1_country” />
<attribute name=”address1_city” />
</link-entity>
</entity>
</fetch>
</fetchxml>
<columnsetxml>
<columnset>
<column>name</column>
<column>customerid</column>
<column>estimatedvalue</column>
<column>statuscode</column>
<column>opportunityid</column>
<column>address1_country</column>
<column>address1_city</column>
</columnset>
</columnsetxml>
<layoutxml>
<grid name=”resultset” object=”3″ jump=”name” select=”1″ icon=”1″ preview=”1″>
<row name=”result” id=”opportunityid”>
<cell name=”name” width=”300″ />
<cell name=”customerid” width=”100″ />
<cell name=”estimatedvalue” width=”100″ />
<cell name=”statuscode” width=”100″ />
<cell name=”opportunityid” width=”100″ />
<cell name=”a_2e3c63cd00754701974cf441fc79f23b.address1_country” width=”100″ />
<cell name=”a_2e3c63cd00754701974cf441fc79f23b.address1_city” width=”100″ />
</row>
</grid>
</layoutxml>
</contact>
</layout>

This schema is parsed by the method LoadOpportunitySchema. This method parses the schema and archives it in a custom entity to be returned to the calling method. This is what LoadOpportunitySchema looks like:

/// <summary>
/// Loads a view schema to create a view from
/// </summary>
/// <param name=”entity”></param>
/// <returns>The schema</returns>
private static CrmEntitySchema LoadOpportunitySchema(string entity)
{
string opportunitySchemaFile = ConfigurationManager.AppSettings[“OpportunitySchemaFile”];
CrmEntitySchema entitySchema = null;
DebugHelpers.TraceHelper(“In.”, MethodInfo.GetCurrentMethod(), RegistryManager.GetRegistryConfiguration<int>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.TraceOn) == 0 ? false : true, RegistryManager.GetRegistryConfiguration<string>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.BusinessLayerTraceFile), 0);

if (File.Exists(opportunitySchemaFile))
{
XmlDocument xmlOpportunitySchemaSource = new XmlDocument();
xmlOpportunitySchemaSource.Load(opportunitySchemaFile);

try
{
entitySchema = new CrmEntitySchema();

XmlNodeList fetchXmlNodeList = xmlOpportunitySchemaSource.DocumentElement.SelectNodes(string.Concat(“/layout/”, entity, “/fetchxml”));

XmlNodeList columnXmlNodeList = xmlOpportunitySchemaSource.DocumentElement.SelectNodes(string.Concat(“/layout/”, entity, “/columnsetxml”));

XmlNodeList layoutXmlNodeList = xmlOpportunitySchemaSource.DocumentElement.SelectNodes(string.Concat(“/layout/”, entity, “/layoutxml”));

entitySchema.XmlFetchSchema = fetchXmlNodeList[0].InnerXml;
entitySchema.XmlColumnSetSchema = columnXmlNodeList[0].InnerXml;
entitySchema.XmlLayoutSchema = layoutXmlNodeList[0].InnerXml;
}
catch (Exception exception)
{
throw new BLException(13, exception);
}
}

DebugHelpers.TraceHelper(“Out.”, MethodInfo.GetCurrentMethod(), RegistryManager.GetRegistryConfiguration<int>(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.TraceOn) == 0 ? false : true, RegistryManager.GetRegistryConfiguration<string>(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.BusinessLayerTraceFile), 0);

return entitySchema;

}

}

Once the xml is defined and is parsed the creation of the view is fairly simple. In our case the query in the view will return all the opportunities associated with a specific contact. If you look at the schema above you’ll see the custom tag {%CONTACTID%}. The method CreateOpportunityView will replace this tag with the id of the contact right before it calls the CRM web service to create the custom view. This is what the CreateOpportunityView looks like:

 

/// <summary>
/// Creates Division Custom view.
/// </summary>
/// <param name=”division_id”></param>
/// <param name=”name”></param>
/// <param name=”description”></param>
/// <returns>The created view’s id</returns>
public static Guid CreateOpportunityView(Guid division_id, string name, string description)
{
IOrganizationService orgService = null;
CrmEntitySchema entitySchema = null;
CreateResponse response = null;

DebugHelpers.TraceHelper(“In.”, MethodInfo.GetCurrentMethod(), RegistryManager.GetRegistryConfiguration<int>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.TraceOn) == 0 ? false : true, RegistryManager.GetRegistryConfiguration<string>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.BusinessLayerTraceFile), 0);

try
{
orgService = FacadeFactory.GetOrganizationService();
entitySchema = LoadOpportunitySchema(ViewManager.CONTACT_SCHEMA);

UserQuery query = new UserQuery()
{
Name = name,
Description = description,
ColumnSetXml = entitySchema.XmlColumnSetSchema,
QueryType = 0, // this is different from 4.0
ReturnedTypeCode = Opportunity.EntityLogicalName,
FetchXml = entitySchema.XmlFetchSchema.Replace(“{%CONTACTID%}”, division_id.ToString()),
LayoutXml = entitySchema.XmlLayoutSchema
};

CreateRequest request = new CreateRequest()
{
Target = query
};

response = (CreateResponse)orgService.Execute(request);
}
catch (Exception exception)
{
throw new BLException(9, exception);
}

DebugHelpers.TraceHelper(“Out.”, MethodInfo.GetCurrentMethod(), RegistryManager.GetRegistryConfiguration<int>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.TraceOn) == 0 ? false : true, RegistryManager.GetRegistryConfiguration<string>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.BusinessLayerTraceFile), 0);

return response.id;

}

Another important method in this example is the ShareView. This method allows you to share the just created view with the other users.

/// <summary>
/// Share a custom view with another user
/// </summary>
/// <param name=”queryid”></param>
/// <param name=”userId”></param>
/// <param name=”permissions”></param>
public static void ShareView(Guid queryid, Guid userId, AccessRights permissions)
{
IOrganizationService orgService = null;
GrantAccessRequest grantAccessRequest = null;

DebugHelpers.TraceHelper(“In.”, MethodInfo.GetCurrentMethod(), RegistryManager.GetRegistryConfiguration<int>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.TraceOn) == 0 ? false : true, RegistryManager.GetRegistryConfiguration<string>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.BusinessLayerTraceFile), 0);

try
{
orgService = FacadeFactory.GetOrganizationService();

grantAccessRequest = new GrantAccessRequest();
grantAccessRequest.PrincipalAccess = new PrincipalAccess();
grantAccessRequest.PrincipalAccess.Principal = new EntityReference(SystemUser.EntityLogicalName, userId);
grantAccessRequest.PrincipalAccess.AccessMask = AccessRights.ReadAccess;
grantAccessRequest.Target = new EntityReference(UserQuery.EntityLogicalName, queryid);
orgService.Execute(grantAccessRequest);
}
catch (Exception exception)
{
throw new BLException(12, exception);
}

DebugHelpers.TraceHelper(“Out.”, MethodInfo.GetCurrentMethod(), RegistryManager.GetRegistryConfiguration<int>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.TraceOn) == 0 ? false : true, RegistryManager.GetRegistryConfiguration<string>
(RegistryManager.CONFIGURATION_MEMBER_PATH, CustomConfigurationSettings.BusinessLayerTraceFile), 0);
}

You can download the QualTech.Crm.Prototypes solution containing this code from here. You find this code in the project QualTech.Crm.BL class ViewManager.cs. The schema used is in the file called XmlOpportunitySchema.xml in this same project.

Leave a Reply