Wednesday, December 1, 2010

SharePoint - CRM Integration: Create a SharePoint Document Library on CRM Opportunity Creation

In CRM 4.0 there is no way to maintain a set of related documents for Opportunities created within the CRM. Therefore it is a good idea to integrate CRM and SharePoint here, so that for each Opportunity creation in CRM a document library will get created in a specified SharePoint site automatically. We can achieve this task by registering a plugin to execute in Opportunity Create step.

Plugin implementation

Add a Web Reference to the Lists service of the SP site (or sub-site) located in "/_vti_bin/Lists.asmx"

Document library creation method could be like this.


///<summary>
/// Create a SP document library for a specified Opportunity.
///</summary>
private string CreateDocLibrary(string opportunityName, string spSiteUrl)
{
ListsService.Lists listService = new ListsService.Lists();
listService.Url = String.Concat(spSiteUrl, "/_vti_bin/Lists.asmx");
listService.Credentials = CredentialCache.DefaultCredentials;

XmlNode xnodeList = listService.AddList("Opportunity-" + opportunityName,
opportunityName, 101);

// Get document library URL name.
string rootFolder = xnodeList.Attributes.GetNamedItem("RootFolder").Value;
return rootFolder.Remove(0, rootFolder.LastIndexOf("/"));
}

Call document library creation method with proper parameters and maintain information about new document libraries in another custom SharePoint list.


public void Execute(IPluginExecutionContext context)
{
string opportunityName;
string stateCode;

if (context.MessageName.Equals("Create"))
{
opportunityName = ((DynamicEntity)context.PostEntityImages["Target"]).
Properties["name"].ToString();
stateCode = ((DynamicEntity)context.PostEntityImages["Target"]).
Properties["statecode"].ToString();
string docLibraryUrl = CreateDocLibrary(opportunityName,
"http://prasadmoss2010/sites/CRMTest");
AddConfigurationData(opportunityName, "http://prasadmoss2010/sites/CRMTest");
}
}

///<summary>
/// Update configuartion data custom list.
///</summary>
private void AddConfigurationData(string listName, string spSiteUrl)
{
ListsService.Lists listService = new ListsService.Lists();
listService.Url = String.Concat(spSiteUrl, "/_vti_bin/Lists.asmx");
listService.Credentials = CredentialCache.DefaultCredentials;

// Get Name attribute values (GUIDs) for list and view.
XmlNode node = listService.GetListAndView("CustomListName", "");
string listID = node.ChildNodes[0].Attributes["Name"].Value;
string viewID = node.ChildNodes[1].Attributes["Name"].Value;

// Add configuration entry - Construct a Batch element and its attributes.
XmlDocument doc = new XmlDocument();
XmlElement batch = doc.CreateElement("Batch");

batch.SetAttribute("OnError", "Continue");
batch.SetAttribute("ListVersion", "1");
batch.SetAttribute("ViewName", viewID);

// Specify update method for the batch post using CAML,
batch.InnerXml = "<Method ID='1' Cmd='New'>" +
"<Field Name='Title'>" + listName + "</Field>" +
"<Field Name='ProposalStatus'>Open</Field></Method>";

// Update SP list item using the list GUID.
try
{
XmlNode xnodeList = listService.UpdateListItems(listID, batch);
}
catch (Exception) {}
}

In addition, Opportunity status maintained in the custom SharePoint list can be updated when the opportunity is marked as a closed opportunity. For that purpose same plugin can be extended to execute on Win or Lose messages

(Ex: "context.MessageName.Equals("Lose")")

by registering plugin for the "Win" and "Lose" steps. In order to capture revert updates (Reopen opportunity), plugin should be registered for the "SetStateDynamicEntity" step as well.

2 comments:

Anonymous said...

Prasad,

In your example here you create a string which takes 2 parameters:

private string CreateDocLibrary(string opportunityName, string spSiteUrl)



yet later on you try to USE that string passing in THREE parameters: the two in the constructor, plus one called statecode.

Is there an overload somewhere that didn't get included in the example?

Prasad Sampath Wickramasinghe said...

I updated code and removed stateCode parameter when calling CreateDocLibrary() method. I was using that parameter for a different purpose, which is not relevant to this topic.

Sorry about the confusion and thank you for pointing that out.