[SalesForce] Update Error: Cannot specify both an external ID reference Contact and a salesforce id

I have some code that was being called from a trigger on a case that, in certain situations, would create add an account and contact to the case. I originally tried to do it synchronously and while it would run without errors, it wouldn't add the contact or account. When I modified the code to run asynchronously in a future call (with no change to the logic except specifying the update of the case!), it worked in the UI, adding the account and contact, but failed unit tests with the error:

System.DmlException: Update failed. First exception on row 0 with id 50021000000wSokAAE; first error: INVALID_FIELD, Cannot specify both an external ID reference Contact and a salesforce id, ContactId: []

List<sObject> toInsert = new List<sObject>();
List<Case> toUpdate = new List<case>();
for (case c: cases){
Account a = new Account();
a.name = c.Account_Name_If_not_in_Salesforce__c;
a.Master_ID__c = util.getRandomString(20);
toInsert.add(a);

Contact con = new Contact();
con.firstname = c.Contact_First_Name__c;
con.lastname = c.Contact_Last_Name__c;
con.Account = new Account(Master_ID__c = a.Master_ID__c);
con.Master_ID__c = string.valueof(a.Master_ID__c);
toinsert.add(con);

c.Account = new Account(Master_ID__c = a.Master_ID__c);
c.contact = new Contact(Master_ID__c = con.Master_ID__c);
toUpdate.add(c);
}
database.insert(toInsert, false);
database.update(toupdate);

Has anyone else found an issue to a similar issue or is this just on of the quirks of the platform?

Best Answer

You need to set the AccountId and ContactId values, not Account and Contact. In addition, you need to wait until after you insert them to set the relationship.

To do so, you're probably going to need to maintain a map of Case id to Master_Id__c. In addition, using a combined list for insert is going to work poorly for you if you have more than 5 records. More on that later.

List<Account> accounts = new List<Account>();
List<Contact> contacts = new List<Contact>();
Map<String, Case> masterIdToCase = new Map<String, Case>();
for (Case record : cases)
{
    String masterId = util.getRandomString(20);
    masterIdToCase.put(masterId, record);

    // other logic
    a.Master_Id__c = masterId;
    accounts.add(a);

    // other logic
    c.Master_Id__c = masterId;
    contacts.add(a);
}
insert accounts;
insert contacts;

for (Account a : accounts)
    masterIdToCase.get(a.Master_Id__c).AccountId = a.Id;
for (Contact c : accounts)
    masterIdToCase.get(c.Master_Id__c).ContactId = c.Id;
update masterIdToCase.values();

In regards to why you want to maintain separate lists, take a look at Things You Should Know about Data in Apex.

Creating Records for Multiple Object Types As with the SOAP API, you can create records in Apex for multiple object types, including custom objects, in one DML call with API version 20.0 and later. For example, you can create a contact and an account in one call. You can create records for up to 10 object types in one call.

...

Records for multiple object types are broken into multiple chunks by Salesforce. A chunk is a subset of the input array, and each chunk contains records of one object type. Data is committed on a chunk-by-chunk basis. Any Apex triggers that are related to the records in a chunk are invoked once per chunk. Consider an sObject input array that contains the

You can only create 10 separate insert chunks, and every time you alternate creates a new chunk. Consider the following list, which counts for 12 chunks instead of 2:

insert new List<SObject> {
    new Account(Name='A1'), new Contact(Name='C1'),
    new Account(Name='A2'), new Contact(Name='C2'),
    new Account(Name='A3'), new Contact(Name='C3'),
    new Account(Name='A4'), new Contact(Name='C4'),
    new Account(Name='A5'), new Contact(Name='C5'),
    new Account(Name='A6'), new Contact(Name='C6')
};
Related Topic