CRM → Salesforce real-time integration
Introduction
We frequently come across requirements where customers use Dynamics CRM and Salesforce CRM and want to synchronize data between both the systems. We generally have requirements where data should flow back and forth from Dynamics CRM and Salesforce CRM. In this blog, I have focused on one way real time integration of Case entity from Dynamics CRM → Salesforce CRM.
Requirements
We have total 4 environments as per below:
- Dynamics CRM Development
- Dynamics CRM Production
- Salesforce CRM Sandbox
- Salesforce CRM Production
- The requirement is to perform development on development environment and then deploy the changes on production environment. There should be no code changes done on production environment and hence the solution should be configurable.
- Only below basic fields of Case entities should be mapped i.e. transferred from CRM Online à
- Case Title (Text field)
- Customer (Lookup of Account field)
- Contact (Lookup of Contact field)
- Description (Multiple line of Text field)
- Origin (Option Set)
- Salesforce Id (Text field – custom field to track GUID of case record created in Salesforce)
Basics of Salesforce API
- Salesforce has list of APIs (https://developer.salesforce.com/page/Salesforce_APIs) from which we have used Partner WSDL (SOAP API).
- To login to Salesforce using APIs, we need to have below 3 details:
- User Id
- Password
- Security Token
- Unlike Dynamics CRM, Salesforce API uses 2 separate URLs based on environment type i.e. Sandbox/Production.
- Sandbox Environment uses
- Production Environment uses
Implementation Approach
- To ensure no code changes on production, we have used configuration entity which will keep below configuration details:
- Salesforce User Id
- Salesforce Password
- Salesforce Security Token
- Salesforce environment Type
- We have used Partner WSDL to communicate between Salesforce environment and CRM Online environment. Hence based on Salesforce Environment Type, we are using login URLs. To achieve this, we have done a trick (explained below in Implementation Steps section).
- On create/update of Cases in CRM Online, we have written a plugin that sends data to Salesforce environment.
Implementation Steps
Note: I have used a separate class file that keeps all constant values. Hence whenever you find any code in all capitals, kindly replace constant values as per your requirements.
-
- Generate Partner WSDL for Salesforce environment (https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_quickstart_steps_generate_wsdl.htm)
- Create Configuration entity in CRM with below fields:
- Key (text)
- Value (text)
- Create a plugin class and perform below operations in the plugin.
-
- Add web reference using downloaded Partner WSDL (https://msdn.microsoft.com/en-us/library/bb628649(v=vs.100).aspx).
- After adding web reference, you will see reference in Solution explorer as per below.
-
- Expand Reference.map and open Reference.cs class. Locate SforceService() method and create a copy of the same with one string parameter as per below and save the file.
-
- In plugin class, fetch configuration details from Configuration entity. It should contain 4 records.
- Based on the environment type, pass appropriate URL to the service to authenticate with Salesforce environment.
private void ConnectToSalesforce() { LoginResult currentLoginResult = null; switch (this.dctConfigurations[CodeConfiguration.ConfigurationKeys.SalesforceEnvironmentType.ToString()]) { case CodeConfiguration.SALESFORCE_SANDBOX_ENVIRONMENT_TYPE: this.sfdcBinding = new SforceService(CodeConfiguration.SALESFORCE_SANDBOX_WEBSERVICE_URL); break; case CodeConfiguration.SALESFORCE_PRODUCTION_ENVIRONMENT_TYPE: this.sfdcBinding = new SforceService(CodeConfiguration.SALESFORCE_PRODUCTION_WEBSERVICE_URL); break; default: throw new InvalidPluginExecutionException(CodeConfiguration.ERROR_MESSAGE_CONFIGURATION_NOT_FOUND); } currentLoginResult = this.sfdcBinding.login(this.dctConfigurations[CodeConfiguration.ConfigurationKeys.SalesforceUsername.ToString()], this.dctConfigurations[CodeConfiguration.ConfigurationKeys.SalesforcePassword.ToString()] + this.dctConfigurations[CodeConfiguration.ConfigurationKeys.SalesforceSecurityToken.ToString()]); this.sfdcBinding.Url = currentLoginResult.serverUrl; this.sfdcBinding.SessionHeaderValue = new SessionHeader(); this.sfdcBinding.SessionHeaderValue.sessionId = currentLoginResult.sessionId; }
-
- Once authenticated, use the session id to perform CRUD operations. Below is a sample code.
- Create an object of case record to be created in Salesforce
sObject caseRecord = new sObject(); caseRecord.type = Case_Salesforce.LOGICAL_NAME;
-
-
- Create objects of attributes of the case record
-
XmlElement[] caseFields = new XmlElement[10]; XmlDocument doc = new XmlDocument(); caseFields[counter] = doc.CreateElement(Case_Salesforce.ATTR_CASESUBJECT); caseFields[counter++].InnerText ="Any value"; caseFields[counter] = doc.CreateElement(Case_Salesforce.ATTR_CONTACT); caseFields[counter++].InnerText ="Salesforce Guid of Contact record"; caseFields[counter] = doc.CreateElement(Case_Salesforce.ATTR_ORIGIN); caseFields[counter++].InnerText ="text value of an option from picklist";
-
-
- Add attributes to an object of case record
-
caseRecord.Any = caseFields; sObject[] caseList = new sObject[1]; caseList[0] = caseRecord;
-
-
- Create record in Salesforce
-
SaveResult[] results = null; results = this.sfdcBinding.create(caseList); for (int j = 0; j < results.Length; j++) { if (results[j].success) { Entity oCase = new Entity(Case_CRM.LOGICAL_NAME, this.currentEntity.Id); oCase[Case_CRM.ATTR_SALESFORCEID] = results[j].id; this.service.Update(oCase); LogIntegrationDetails("Record Id: " + this.currentEntity.Id.ToString(), null, (int)IntegrationLog.Status.Success); } else { for (int k = 0; k < results[j].errors.Count(); k++) { Error err = results[j].errors[k]; } } }
Key Takeaways
-
-
- From Dynamics CRM → Salesforce, real time integration is possible using CRM plugins and Salesforce APIs.
- Salesforce uses different authentication URLs for different type of environments. We have handled this scenario by modifying Reference.cs class file.
- Configuration entity is used to make the code configurable.
- We are storing back GUID of case record created in Salesforce which will be used in case of update plugin.
-