0

I'm trying to update Customer data in MS NAV 2017 using OData and PHP. I can read and create Customers using the /CustomerCard uri. But I can't get the update working (PATCH nor MERGE nor PUT). I try several combinations but with no success. I also already read a lot of threads, forums, etc.

When I want to do a PATCH on /Customer(No=XXXXXX), I get the error message : Resource not found for the segment 'Customer'.

But when I want to do a PATCH on /CustomerCard(No=XXXX), I get the error message : Bad Request - Error in query syntax.

And when I want to do a PATCH on /CustomerCard without parameters, I get the error message : 'PATCH' requests for 'Collection' are not supported by Microsoft Dynamics NAV OData web services.

I'm sending the ETag in the header. I tried with PATCH, MERGE and PUT. Same result.

Am I missing some configuration ?

Thanks for your help.

Here is my code.

$customernb = "R1500000";

$url1 = 'https://MYDOMAIN/ODatav4/CustomerCard';
$url2 = '?$filter=No%20eq%20%27';
$url3 = '%27';
$url = $url1.$url2.$customernb.$url3;

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$username = "MYUSER";
$password = "MYPWD";
curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);

$result = curl_exec($ch);
curl_close($ch);

$j = json_decode($result);
print_r($j);

// THIS WORKS FINE !

/*
 * Update
 */

$newcustomer = new stdClass();
$newcustomer->Name = "OMEGA SHIRT UPDATED";
$newcustomer->Salesperson_Code = "JVP";
$newcustomer->Sales_Contact_Code = "TFS";
$etag = $j->value[0]->ETag;
$headeretag = $j->value[0]->{'@odata.etag'};
$newcustomer->ETag = $etag;
$newcustomer->{'@odata.etag'} = $headeretag;
$jsonDataEncoded = json_encode($newcustomer);

$url5 = 'https://MYDOMAIN/ODatav4/CustomerCard('.$j->value[0]->No.')';
//$url5 = 'https://MYDOMAIN/ODatav4/CustomerCard';
//$url5 = 'https://MYDOMAIN/ODatav4/CustomerCard?$filter=No%20eq%20%27'.$j->value[0]->No.'%27';
$ch = curl_init($url5);

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncoded);
//$encodedetag = "W/\"'".urlencode($etag)."'\"";
$encodedetag = $headeretag;
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json;If-Match: '.$encodedetag));
//$headers.Add("If-Match",'W/"' + "'" + [uri]::EscapeDataString($JToken.ETag.ToString()) + "'" + '"') #Etag Value
curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
//CURLAUTH_NTLM
$result = curl_exec($ch);
print_r(json_decode($result));
  • **[You should not switch off `CURLOPT_SSL_VERIFYHOST` or `CURLOPT_SSL_VERIFYPEER`](https://paragonie.com/blog/2017/10/certainty-automated-cacert-pem-management-for-php-software)**. It could be a security risk! [Here is how to get the certificate bundle if your server is missing one](https://stackoverflow.com/a/32095378/1839439) – Dharman Oct 11 '19 at 18:50

1 Answers1

2

I found the solution and will document it here for other developers. Here are the changes I made to my previous code:

  1. The URI I now use is : https://MYDOMAIN/ODatav4/CustomerCard('R1500000') where R1500000 is the customer number (unique id).
  2. I don't add the ETag nor the header ETag in the body of the JSON customer structure. But I still submit the header ETag I received from the GET operation as If-Match header value in the PATCH operation.
  3. The structure of my HTTP Header in the PATCH call was not correct. I specified the Accept, the Content-Type and the If-Match separately.

So, to summarize, I fixed the URI and I fixed the HTTP Header. The rest was fine. Here is my new code:

$customernb = "R1500000";

$url1 = 'https://MYDOMAIN/ODatav4/CustomerCard';
$url2 = '(\'';
$url3 = '\')';
$url = $url1.$url2.$customernb.$url3;

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$username = "MYUSER";
$password = "MYPWD";
curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);

$result = curl_exec($ch);
curl_close($ch);

$j = json_decode($result);
print_r($j);

/*
 * Update
 */

$newcustomer = new stdClass();
$newcustomer->Name = "OMEGA SHIRT UPDATED";
$newcustomer->Salesperson_Code = "JVP";
$newcustomer->Sales_Contact_Code = "TFS";
$etag = $j->value[0]->ETag;
$headeretag = $j->value[0]->{'@odata.etag'};
$jsonDataEncoded = json_encode($newcustomer);

$ch = curl_init($url);

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncoded);
$encodedetag = $headeretag;
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json','Content-Type: application/json','If-Match: '.$encodedetag));
curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
$result = curl_exec($ch);
print_r(json_decode($result));