0

I originally had the following trigger and REST-callout class that functioned properly:

trigger SendOpp_Trigger on Opportunity (before insert, before update) {
    for(Opportunity o : Trigger.new){
        SendOpportunity.createOpportunity(o.name, o.Account_External_Id__c, o.CloseDate, o.StageName);
    }
}

public class SendOpportunity {

    String clientId = '*****';
    String clientsecret ='*****';
    String username='fake@fake.com';
    String password = '*****';
    String accesstoken_url = 'https://login.salesforce.com/services/oauth2/token';
    String authurl = 'https://login.salesforce.com/services/oauth2/authorize';

    public class deserializeResponse {

        public String id;
        public String access_token;

    }

    public String ReturnAccessToken(SendOpportunity Opp){

        String reqbody = 'grant_type=password&client_id='+clientId+'&client_secret='
        +clientSecret+'&username='+username+'&password='+password;

        Http h= new Http();
        HttpRequest req= new HttpRequest();
        req.setBody(reqbody);
        req.setMethod('POST'); 
        req.setEndpoint('https://na50.salesforce.com/services/oauth2/token');

        HttpResponse res=h.send(req);

        system.debug(res.getBody()); 

        deserializeResponse resp1= (deserializeResponse)JSON.deserialize(res.getBody(),deserializeResponse.class);

        return resp1.access_token;
    }

    @future(callout=true)
    public static void createOpportunity(String oppName, String acctId, Date d, String stage){

        SendOpportunity opp1 = new SendOpportunity();

        String accessToken = opp1.ReturnAccessToken(opp1);

        if(accessToken!=null){

            String endPoint='https://na50.salesforce.com/services/data/v42.0/sobjects/Opportunity';             

            String jsonstr='{"Name":"'+ oppName +'","AccountId":"'+ acctId +'","CloseDate":"'+ String.valueOf(d) +'","StageName":"'+ stage +'"}';

            Http h2 = new Http();
            HttpRequest req2 = new HttpRequest();
            req2.setHeader('Authorization','Bearer ' + accessToken);
            req2.setHeader('Content-Type','application/json');
            req2.setHeader('accept','application/json');
            req2.setBody(jsonstr);
            req2.setMethod('POST');
            req2.setEndpoint(endPoint);

            HttpResponse res2 = h2.send(req2);

            system.debug(res2.getBody()); 

            deserializeResponse deresp=(deserializeResponse)System.JSON.deserialize(res2.getBody(),deserializeResponse.class);
        }
    }
}

However, the only thing that code can do is create the same opportunities in a target salesforce org.

Now I want the callout to upsert opportunties in the target org, so I made the following additions according to online suggestions:

enter image description here

enter image description here

But I still get this message:

{"errorCode":"METHOD_NOT_ALLOWED","message":"HTTP Method 'PUT' not allowed. Allowed are HEAD,GET,PATCH,DELETE"}

Any ideas?

Thanks

  • 2
    Did you try PATCH? – Tushar Sharma Mar 1 '18 at 3:09
  • Yes, and that gives me: “invalid cross reference Id” – Gabriel Rivera Mar 1 '18 at 3:25
  • Did you check that may be an actual issue? – Tushar Sharma Mar 1 '18 at 3:32
  • 1
    "invalid cross-reference id" is probably because you're sending an accountid that doesn't exist in the target org. – Mark Pond Mar 1 '18 at 4:48
6

I'm not sure where you're getting the idea that PUT is the correct verb ("method") here; it should be a PATCH; you'll need to use the _HttpMethod query parameter to set the correct method. Also, to assign the correct account, you need the account's External ID (or it's actual other-org ID), and specified in the payload.

 // Define upsert field and value
 String endPoint='https://na50.salesforce.com/services/data/v42.0/sobjects/Opportunity/External_Id__c/'+oppId+'?_HttpMethod=PATCH';
String jsonstr='{"Name":"'+ oppName +'","Account":{ "External_Id__c":"'+ acctId +'"},"CloseDate":"'+ String.valueOf(d) +'","StageName":"'+ stage +'"}';
...
req2.setMethod('POST');

Do not include the External ID value in the payload, or the request will also fail.

See Insert or Update (Upsert) a Record Using an External Id for more info.

Also, using this method is incredibly error-prone to silly mistakes, you should have the system generate the JSON for you:

String jsonstr = JSON.serialize(new Map<String, Object> {
  'name' => oppName,
  'account' => new Map<String, Object> {
    'External_Id__c' => accId
  },
  'CloseDate' => d,
  'StageName' => stage
});

This takes a lot of the guesswork out of the JSON when something goes wrong.

  • 2
    HTTP Callout doesn't support PATCH except through a workaround that endpoint system might not accept: success.salesforce.com/ideaView?id=0873A000000PST0QAO – cropredy Mar 1 '18 at 5:35
  • @cropredy Oh, huh. I thought they finally fixed that. sigh Well, the _HttpMethod is supposed to work, at any rate. I'll edit. Besides, I have another workaround I can post. – sfdcfox Mar 1 '18 at 7:41
  • @sfdcfox I changed jsonstr/generated the JSON the way you suggested, but got this back: {"message": "Foreign key external ID: 0016a00000jlvn8qaf not found for field External_Id__c in entity Account", "errorCode": "INVALID_FIELD", "fields": [] }. And do please post the workaround you mentioned! – Gabriel Rivera Mar 1 '18 at 14:05
  • @sfdcfox Regarding the error message I told you about in my comment above, disregard. I fixed that problem by populating External_Id__c, in the target org, with the necessary Id. – Gabriel Rivera Mar 1 '18 at 14:34
  • 1
    @GabrielRivera Great, glad I could help! – sfdcfox Mar 1 '18 at 15:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.