Best Practices for Salesforce to Salesforce

Abstract

Salesforce to Salesforce is a Force.com feature that lets you configure two Force.com environments (orgs) so that they share data records in real time. This article describes several best practices to solve some of the complex salesforce to salesforce scenarios.

Introduction

In today's online world of business, it is a competitive advantage to have partners who can contribute to your business growth. While doing business with partners, it might be required to share business data with them. If both you and your partner use Salesforce.com, it is very easy to share data using Salesforce to Salesforce.

In most scenarios, data can be shared using the standard Salesforce.com user interface, however, there are some instances when it's better to automate sharing, including carrying over relationships between objects in the source org to the target org. This document provides tips & tricks with code samples, as well as best practices, to roll-out both simple and complex business processes with Salesforce to Salesforce.

What follows is a list of best practices - each is described by a challenge, and each has a solution provided in code. Feel free to skip to the best practices that best suite your needs.

In all of the best practices below, we use two orgs (environments):

the Force Corp org represents the source org (we'll typically be moving data from this org)

the ACME Corp org represents the target org (we'll typically be moving data to this org)

Best Practice: Reestablishing lookup relationships

Challenge:
There are two lookup fields on an object in the source org and you would like to share the object, along with the lookup objects, and reestablish the lookup relationship with the target org.

Solution: Generally, it is easier to set a lookup field as parentrecordid if there is only one lookup, but when you have two lookup fields, you cannot use parentrecordid.

The prerequisite for this solution is that both referenced records (via lookup) are already externally shared via Salesforce to Salesforce (S2S) with the target org.

Publish the second lookup object's ID from the source org using a formula field and subscribe it into a custom text field with size 100 in the target org object.

Once you have the lookup ID available in the target org, find out the local record ID corresponding to the lookup ID using PartnerNetworkRecordConnection, and populate the second lookup using the queried localrecordid corresponding to partnerrecordid.

Here's an example: The Pyramid construction account has two contacts: Pat and Joe. Pat reports to Joe (i.e. the Reports To field on Pat's record has a lookup to Joe). The Contact record is shared from Force Corp to ACME.

Force Corp Org (Source Org) Setup

Define a new custom formula field on Force Corp's contact object to make the Reports To ID visible to Salesforce to Salesforce. Call the new field ReportsToIdFormula, give it a type Formula, Formula: ReportsToId.

Publish this new custom formula field on the Contact object.

ACME Corp Org (Target Org) Setup:

Define a new custom field on Acme Corp's Contact object to receive the Reports To ID,
New field name: Remote Reports To ID, API Name: Remote_Reports_To_ID__c, Data Type: Text (100)

Go to the Connections tab, click Edit against Contact in the Subscribe/Unsubscribe section and map the Reports To field to the new custom field (ensure that the Reports To field on Contact object is published from Force Corp Org)

Write a trigger on the Contact object to find the corresponding local record and assign it to the Reports To lookup field.

Best Practice: Forcing Updates to return generated case numbers to the source org

Challenge: You need the case number generated in the target org back in the source org. The case number in the target org (ACME) will not flow back to the custom field in the Force Corp org until the Case record is updated in the ACME org after initial record creation, as Salesforce to Salesforce will not flow data unless it sees an update to a record.

Solution: Do a pseudo update on the newly-created case in the ACME org via a future method. Here's the setup!

ACME Corp Org (Target Org) Setup

From ACME org, publish the Case Number field on the Case object (Go to the Force Corp connection, click Publish/Unpublish, select Case object, and Save. Click Edit against Case in the Publish/Unpublish section, select Case Number, and Save.)

Force Corp Org (Source Org) Setup

In the Force Corp Org, create a new field on Case object in the Force Corp org, let's call it Acme_Case_Number of Data Type Text (30)

Go to the Connection tab, click on the ACME connection, click Subscribe/Unsubscribe, and map the Case to Case object and Save. Click on the Edit link against Case in the Subscribe/Unsubscribe section.

Map the Case Number to the ACME Case Number field and click Save.

Now you need to define a trigger on the Case object. It will fire after a new case record is created, and if the conditions are correct, call our helper method to do the "touch" - a write to active the Salesforce to Salesforce functionality:

Note how this simply does an update on the list of cases. The update will fire the Salesforce to Salesforce.

Best Practice: Reflect Case comment status changes in the target org

Challenge: Delete Case comments from the target org if Case comments in the source org are made private or deleted.

Solution: In an Apex batch, check the PartnerNetworkRecordConnection, locate the record connection status, and delete the local record if the shared record from the source org is deleted. Schedule the batch to run every hour. The Salesforce.com UI does not permit scheduling to less than a weekly or monthly frequency. To schedule the following batch programatically, you can create a Visualforce page and call Apex code to do the scheduling (the code sample is below the batch code.)

Best Practice: Propagating Account and Contact merges

Challenge: The Account Merge and Contact merge in the source org (Force Corp Org) are not propagated to the target org (ACME Corp).

Note: Any child records that are reparented as a result of the merge operation do not fire triggers. See the docs for more information on this.

Solution:

Account Merge: As a result of an Account merge, reparenting is done on child records of losing accounts. (In a merge, there is a survivor account and a victim account. The victim account, or losing account as we refer to it here, gets deleted from the org.) The child records for the account are contacts and cases. Assuming that both accounts' child records are already shared externally (via Salesforce to Salesforce), we will need to propagate the new relationship between the losing account's children records and winner accounts in the target org.
Contact Merge: As a result of contact merge, reparenting is done on child records of losing contact. The child records for contacts are cases. Assuming that both contacts' child records are already shared externally (via Salesforce to Salesforce), we will need to propagate the new relationship between the losing contacts' children records and winner contacts in the target org.

Force Corp Org (Source Org) Setup

Contact Object:

Create a new formula field of type text, label it as S2S AccountId, API name: S2S_AccountId__c.

Formula text: For AccountId, select a profile corresponding to the user having "manage connection" permissions.

Case Object:

Create a new formula field of type text, label it as S2S AccountId, API name: S2S_AccountId__c. Formula text: For AccountId, select a profile corresponding to the user having "manage connection" permissions.

Create another new formula field of type text, label it as S2S Contact Id, API name: S2S_ContactId__c. Formula text: For ContactId, select a profile corresponding to the user having "manage connection" permissions.

ACME Corp Org (Target Org) Setup

Contact Object:

Create a new field of type text, size 80, label it as S2S AccountId, API name: S2S_AccountId__c.

Case Object:

Create a new field of type text, size 80, label it as S2S AccountId, API name: S2S_AccountId__c.

Create another new field of type text, size 80, label it as S2S Contact Id, API name: S2S_ContactId__c.

Force Corp Org (Source Org) Setup:

First, here are the changes in the source org.

Account Merge:

The following Account trigger will update account references on all the contacts and cases under the losing account. To propagate the change of account ID on contact and case, do a pseudo update on Contacts and Cases in the source org.

trigger AccountMergeTrigger on Account (before delete) {
try {
if (Trigger.isBefore && Trigger.isDelete) {
// call @future method to do a pseudo update on the contacts so that the reparenting flows via S2S
// Cases under contacts would reparented automatically on source org side
// Case under contacts on target Org side would be updated as part of contact update trigger
List<Contact> contactListForUpdate = [SELECT Id from Contact Where Accountid in :Trigger.old];
Map<Id,Contact> contactMap = new Map<Id,Contact>();
contactMap.putAll(contactListForUpdate);
if (contactMap.keySet().size() > 0) {
ExternalSharingHelper.touchContactsForAccountMerge(contactMap.keySet());
}
// Case under contact would automatically be updated in Target org
// Only update cases which does not have contact but has an associated account which got merged with the new account.
List<Case> caseListForUpdate = [SELECT Id from Case Where Accountid in :Trigger.old and contactid = null];
Map<Id,Case> caseMap = new Map<Id,Case>();
caseMap.putAll(caseListForUpdate);
if (caseMap.keySet().size() > 0) {
ExternalSharingHelper.touchCases(caseMap.keySet());
}
}
} catch (System.Exception ex) {
// do nothing for now
System.debug(LoggingLevel.Error, 'Account Merge trigger failed -' + ex.getMessage());
}
}

Contact Merge:
This trigger will update the Contact reference to winning contacts on all the cases under losing contacts. To propagate change on the case to the target org, do a pseudo update on cases under the losing contacts in the source org.

ACME Corp Org (Target Org) Setup:

Now let's look at the changed in the target org.

Account Merge:
The Contact update will populate new Account references in the S2S_AccountId__c field on the Contact object.
Use the account ID corresponding to the account in the source org to fetch S2S-linked account IDs in the target org, and update the account ID to the contact's account, as well as the Account reference on Cases under updated Contact.

The Case update will populate new Contact references in the S2S_ContactId__c field on the Case object.
Use the contact ID corresponding to the contact in the source org to fetch S2S-linked contact IDs in the target org and update the contact ID to the case's contact.

Summary

This article shows how two different Salesforce.com customers, or customers with two Force.com orgs, can automate data sharing between their Force.com orgs, using Salesforce to Salesforce native data sharing technology and Apex programming based on the Force.com platform. The programmatic approach not only automates data exchange, but also enables exchanged records to carry the lookup and parent-child relationship among objects from source org to parent org, and vice versa.