S2S (Salesforce-to-Salesforce) Adventures

To paraphrase talk radio – “long-time thinking about, first-time actually doing it”.

An application came up to implement S2S between my org and another org at the same client. Something seemingly simple – just forward Accounts and Opportunities to the target org. The problem statement called for automatic forwarding / automatic acceptance, no user-initiated forwarding.

Several gotchas were uncovered and I’ll keep a running list here. Most resolved through Google searches and Developer Console investigation.

No upserting of PartnerNetworkRecordConnection (PNRC)

You might run across this blog post with a helpful code example. Unfortunately, the sample uses the upsert DML operation. This is great if the PNRC is being inserted but not if you are trying to update one. Stick to using DML insert and avoid trying to modify an existing PNRC. See SFDC doc.

Don’t try and share an Opportunity before sharing its Account

If you use APEX to share an Opportunity before the parent Account is shared, the DML insert of PartnerNetworkRecordConnection will fail with an exception ‘Invalid status for partner network operation’.

You can share an Opportunity without specifying a value for ParentRecordId. In this situation, the Opportunity will forward and be accepted as an orphan (no parent Account). Yes, the schema allows for this but most orgs probably have validation rules to prevent this from happening. This can be a workaround (with trigger on the target org) for the multiple Lookups on Account problem (see below).

Deleting a PNRC doesn’t actually delete it

If you go into Developer Console and query on PartnerNetworkRecordConnection, then delete selected records through the DC user interface, the records don’t actually delete, they have their status changed to Inactive.

INVALID_PARTNER_NETWORK_STATUS invalid status for partner network operation
Causes:

  • Even if you have an active connection to the remote org, you need to publish the sObjects you insert in your code/testmethods and, the remote org has to subscribe to those sObjects.
  • If you are sharing a child record (like an Opportunity) and the parent record has not yet been accepted, this error ensues. In testmethods, unless using SeeAllData=true and a queryable parent record successfully shared, there is no way to get around this error except to ignore it.

It is impossible to auto-accept (without coding) an Opportunity if it has two or more lookup fields to Account

This is a weakness of the PNRC schema wherein you can’t specify the relationshipname used for the ParentRecordId field. Since the target SFDC org has no way of knowing which relationship to instantiate, it tosses the decision into the target user’s lap. The pending record is locatable only through the Opportunity tab, “Opportunities from Connections” section:

OpposFromConnections

If you’ve overridden your Opportunities tab page (like I had in my dev org), then you’ll need to add a button or link to get you to https://yourinstance.salesforce.com/04L?fcf=000000000000000. Yep – the fcf parameter is all zeroes.

By the way, the SFDC Help on this is here.

The only way I could think of to resolve this problem was to do the following:

  1. Publish the Opportunity.accountId field
  2. On the Subscribing (target) org, map that field to a String field called Partner_Oppo_AccountId__c
  3. Don’t include the ParentRecordId field when, on the publishing org, you create a PNRC for the Opportunity
  4. On the subscribing (target) org, create a before insert trigger that examines the value of Partner_Oppo_AccountId__c, and looks for the corresponding PNRC record with the same PartnerRecordId and also matches the PNRC ConnectionId to the triggered object’s ConnectionReceivedId. In the row you find, take the LocalRecordId and assign it to the triggered Opportunity’s accountId field. Then, when the trigger ends, the Sobject has the proper relationship. Now, I didn’t control the target org so I haven’t had a chance to test this.

On the target org, the running user is called Connection User and it has some quirks

  • This user can’t be queried in the User sObject
  • If you need to look at a debug log, you’ll need to monitor Connection User but it can’t be selected in the lookup for Add Users to Debug Log page. To resolve this, see this tip
  • If you need to know in Apex if some user is the Connection User, use the Apex below. See also
   public Boolean isRunningUserConnectionUser() {
      return UserInfo.getUserType() == 'PartnerNetwork' && 
           UserInfo.getName() == 'Connection User'; // localize appropriately

Don’t share a record back to the org that shared it to you

You’ll need to test to see if the object to be shared has the same ConnectionReceivedId as the connection you intend to share (forward) the record to. A good example of this is covered in this very helpful SFDC best practices guide.

You can’t add the External Sharing relatedlist to a VF page

You might think you could with:

<apex:relatedList list="PartnerNetworkRecordConnection"/>

But you can’t. This special related list, like field histories, either has to be rebuilt with a custom component or you need to let the user escape to a standard page layout.

You can only establish S2S connections between sandbox-sandbox, PROD-PROD, Dev Edition-Dev Edition or PROD<>Dev Edition
This makes deploying Apex code tricky from your sandbox to Production if you have testmethods that cover your S2S logic – as they will require an active PartnerNetworkConnection in order to try and insert any PartnerNetworkRecordConnection . You’ll either have to:

  • Establish a PROD-PROD S2S connection with some friendly PROD org
  • Establish a PROD-DevEdition S2S connection with a captive Dev Edition org

Both options aren’t great – the remote PROD org might not be ready when you are and it is not best practice to have a PROD<->Dev Edition connection, especially as it can share records!

See Also
S2S Stackexchange question on test isolation

One thought on “S2S (Salesforce-to-Salesforce) Adventures

Leave a Reply

Your email address will not be published. Required fields are marked *