Search code examples
phpodatanav

Update Customer data in MS NAV using OData from PHP not working


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));

Solution

  • 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));