MS NAV Web Services to/from PHP, receiving and sending data
Some time ago I came across a project where I had to connect MS’NAV and Drupal, two completely different systems one is proprietary (hello MS) and another is PHP & MySql (welcome, open source!)
There where few options how both systems could communicate automatically, but unfortunately REST was not available as an option on the MS’NAV version I had to connect to, so I was left with only option – SOAP.
Don’t get me wrong – I’ve developed multiple SOAP services in Java and PHP, used different libraries, so I am not new to the technology. I do have my own opinion about it and consider it slightly “redundant”, but… in this case I didn’t have much choice.
So… connecting MS’NAV and PHP via SOAP, with NTLM authentication on NAV side:
First of all I came across this really nice article http://blogs.msdn.com/b/freddyk/archive/2010/01/19/connecting-to-nav-web-services-from-php.aspx – we’ve set-up authentication in NAV, had few services exposed and everything seemed to work as expected (unfortunately only for receiving data from NAV).
Biggest problem, when I tried to insert data into NAV – was following error message:
Start tag expected, '<' not found
or this one:
The Element is expected by Min Occurs value: Once. Element received: .
It cost me at least a day and a half, not mentioning the time spend on the “other side of things”, but I have finally found my solution. Main problem is – MS have it’s own “vision” of how SOAP should look like, so don’t be surprised too much, with a “strange” parameter names 😉
You can download class I’ve modified/developed here.
Basic usage sample here (please note that you have to configure NAV to accept NTLM and so on, as described in the article from FreddyK above and also have soap module enabled in PHP):
require('nav_soap_client.php'); // or... die it's not gonna work without it anyway
class commerce_nav_crons{
function __construct() {
$this->service_url_main = 'http://192.168.1.111:7047/DynamicsNAV/WS'; // just a sample - i read it from db
// we unregister the current HTTP wrapper
stream_wrapper_unregister('http');
// we register the new HTTP wrapper
stream_wrapper_register('http', 'NTLMStream') or die("Failed to register protocol");
define('USERPWD', 'your_domain\your_username:your_password'); // put your own values - mine are actually taken from db
}
function __destruct() {
// restore the original http protocole
stream_wrapper_restore('http');
}
/**
*
* this is a main run function to send Order Returns to NAV, very basic, just to illustrate sample
*
* @return bool
*/
function sample_run(){
$ret = FALSE;
$service_url = $this->service_url_main.'/Codeunit/Drupal_CreateSalesReturnOrder';
$service_params = array(
'trace' => TRUE,
'cache_wsdl' => WSDL_CACHE_NONE,
);
$service = new NTLMSoapClient($service_url, $service_params);
$service->fuck_you_ms = TRUE; // because fuck you, that's why!
$params['drupalSRO']['ReturnOrderDetails']['OrderNo'] = 'W0000111';
$params['drupalSRO']['ReturnOrderDetails']['CustNo'] = 'C0000142';
$params['drupalSRO']['ReturnOrderDetails']['ContactNo'] = 'CC000023';
$params['drupalSRO']['ReturnOrderDetails']['OrderType'] = 'WEB';
// @start: ************* RUN LOLA RUN !!!! **************************
try{
$result = $service->CreateSRO($params);
if($result->return_value == 1){ // this is actually defined on NAV side
$ret = TRUE;
}
}catch(Exception $e){
// here should be some nice debugging if you want
echo "<hr><b>ERROR: SoapException:</b> [".$e."]<hr>";
echo "<pre>".htmlentities(print_r($service->__getLastRequest(),1))."</pre>";
}
// @end: ************* RUN LOLA RUN !!!! **************************
return $ret;
}
}
Right, what you have to do is to instantiate my class commerce_nav_crons and run function sample_run, something like that:
$cl = new commerce_nav_crons();
if($cl->sample_run()){
echo 'OK';
}else{
echo 'SHIT';
}
Please note of the parameter – it can be omitted as soon as you sure your soap service on NAV side is not going to change:
'cache_wsdl' => WSDL_CACHE_NONE,
And most important one here, that actually run extra processing BEFORE SOAP envelope and everything is being send to MS’NAV (it’s hacking in it’s core – but I found that this actually solves the problems and believe me – I spend LOTS OF TIME figuring out best solution:
$service->fuck_you_ms = TRUE; // because fuck you, that's why!
That’s all folks! THE END
In: Drupal, English, Fighting the system · Tagged with: microsoft, MS'NAV, SOAP
on May 7, 2013 at 07:09
Permalink
Do you know how to authenticate using basic auth instead? Got it working when testing soap, but not when using PHP.
on May 7, 2013 at 10:03
Permalink
Should be very simple – just provide URL like http://login:password@servername/service – Curl will take care of everything else.
on May 7, 2013 at 14:24
Permalink
I tried that, problem is, the supplier does not have NTLM activated on their NAV installation. I’d love it if you could contact me via mail and help with this, i’d gladly compensate for you time.
on June 27, 2013 at 10:12
Permalink
Sorry Morgan,
I was on my holidays during your last comment and didn’t really noticed it.
I don’t think there is an easy way of accessing anything that is NOT NTLM service – in this case it’s probably some other MS half-proprietary crap, network groups or “god only knows what”.
on July 2, 2013 at 14:29
Permalink
Hi arte
I am struggling to get updating working from PHP to NAV. Have you written anything for updating existing records rather than dumping in new records (orders).
I don’t have really any experience with SOAP, and I am really only looking for a pointer as I want to work out most of it for myself.
I have tried adpating your example but when ever I try the Update method I get nothing, not even an error message which is very frustrating and very hard to work with/learn from!
I know MS don’t like supporting non-MS stuff but surely they should have ~some~ documentation for PHP connecting to their web services?
on July 2, 2013 at 15:43
Permalink
Hi TB
I am not sure why your service is not returning anything, but I would suggest – first to make sure Service is working at all.
Try to receive some/any information from it and please, do have PHP debug switched on to a maximum level – you might be getting some error messages (for example Curl extension is not installed or god knows what else isn’t working).
The original article here – http://blogs.msdn.com/b/freddyk/archive/2010/01/19/connecting-to-nav-web-services-from-php.aspx is actually from one of the MS employees, so my guess it’s the only source of information 🙁
Also, if you are using the very latest version of MS Navision – they should have Rest services available, not just SOAP, and REST is SO MUCH easier to debug!
Hope that helps!
on July 2, 2013 at 17:06
Permalink
Hi arte
Thanks for coming back so quickly! Really appreciated.
Sorry I wasn’t very clear in my first post. I can read data via SOAP from pages I have exposed via web services with no problem.
The problem comes when I try to write back to the record I have just found. The Update method doesn’t work how, in my mind, it should 🙂 and there is no documentation on writing back. Your post is the only thing I can find and that just covers creatinga record in a custom codeunit
RE: REST, I was excited when I first read about OData in NAV 2013 but then depressed to find out it is read only at present 🙁 I guess they are going to enable the C, U and D of CRUD in SP1 ???
on July 3, 2013 at 10:35
Permalink
Hi TB,
Yes, that sucks what they did in NAV 2013 🙁
Update is very important part of any service…
My code is working successfully on one of the production servers we have, not sure where the problem might be on your side. I wonder what version of NAV do you have?
Either way you should get exception (
echo "
ERROR: SoapException: [".$e."]
"; ) or some results…
The actual data that is going back from the NAV soap can be seen if you uncomment line 158 in my attached class:
//echo "\n
response:".$response."\n";
If nothing comes back… well I am not sure – sounds like something is not working on NAV side and unfortunately this is a part where I didn’t have any control over, so can not help with any code/hints.
Possibly try to expose one of the default services and try updating them first, make sure it’s all fine.
Hope that helps!
Arte
on August 1, 2013 at 14:42
Permalink
Hi arte,
we need to know the create,delete and update methods? also, So could you give me examples for those methods.
thanks in advance……………
on August 7, 2013 at 07:11
Permalink
hi arte,
great work…………
on August 26, 2013 at 15:57
Permalink
Hi Arte,
First of all thanks!
I’ve got a problem, I tried using your class but it didn’t work 🙁
I started debugging and saw your are removing the namspace that is the culprit. First thing I did is echo the request to screen that was about to be processed by your method. I was expecting to see a SOAP request printed to my screen but it only printed the actual value I inserted into the service call. In your case it would only echo W0000111 in stead of a SOAP request.
I fixed it by replacing the request variable in the __doRequest method with an actual SOAP request and it worked. Just wanted to check whether you’ve come across this or you might know what’s up?
Thanks again!
on August 26, 2013 at 19:36
Permalink
Hi Remco,
I am not sure if you are using the latest version of NAV or the older than mine, but my class is running successfully on the production server without any changes to it.
Also if you expecting to see full SOAP request in the browser – you have to look into source code, not on the screen, as all tags are hidden on the screen (unless you do html encode before output).
So no… I’ve never came across that issue before.
on August 28, 2013 at 13:58
Permalink
Hi Arte,
It is working now, the namespace for the xmlimport had illegal xml chars in it, we changed that on the nav side and voila your solution is working like a charm!
Thanks again!!
Remco
on August 28, 2013 at 18:35
Permalink
Glad it worked!
Spend endless hours banging my head against the wall 😉
on October 30, 2013 at 08:43
Permalink
Hi Arte,
I’m new in PHP and web service, so i need to ask these questions:
I do understand that the ‘OrderNo’ is a parameter we set in codeunit NAV,right? But what i dont understand are these two:
‘drupalSRO’ and ‘ReturnOrderDetails’ from ur line here:
$params[‘drupalSRO’][‘ReturnOrderDetails’][‘OrderNo’] = ‘W0000111’;
Also the ‘CreateSRO’ on here:
$result = $service->CreateSRO($params);
is it a service name we publish on web service?
Could you please explain what are those variable from, and for?
Thank you very much in advance
Regards,
Andre
on October 30, 2013 at 11:17
Permalink
Hi Andre,
Correct: CreateSRO is a service name.
$params – are the parameters we “push” into that service. So it’s really up to you to create that service and add parameters you want.
on October 31, 2013 at 05:05
Permalink
Hi Arte,
Thank you very much for the quick response.
I understand that you “push” parameters into the service,
But i still dont get, how the receiver (the codeunit on NAV) would accept this syntax:
$params[‘drupalSRO’][‘ReturnOrderDetails’][‘OrderNo’] = ‘W0000111′;
What i understand is only this part: [‘OrderNo’] = ‘W0000111′;
It will push the value ‘W0000111’ into variable named ‘OrderNo’
Right?
Could you please show me the code under this >> Drupal_CreateSalesReturnOrder codeunit?
So i can clearly understand the whole process?
Regards,
Andre
on October 31, 2013 at 09:22
Permalink
Unfortunately I haven’t developed the NAV part and don’t have access to the code on that side.
All I know there was some mapping done to the existing DB fields and it worked.
After that you can start sending any parameters from PHP and SOAP service will give you error messages explaining what fields are missing and all you have to do is to provide them.
Hope that helps.
on November 4, 2013 at 10:42
Permalink
Hi Arte,
I try to create a simple process for the start.
But when I try to run it, it gave me a blank white page…no error or whatsoever.
I try to view the page source, it shows me: ‘1’
And that is all.
Could you help me about this problem?
Regards,
Andre
on November 6, 2013 at 04:26
Permalink
Hi Arte,
Nevermind my previous question.
Its such a newbie question.. 🙂
Btw, im getting this error:
ERROR: SoapException: [SoapFault exception: [Client] Function (“Insert_Customer_Pub”) is not a valid method for this service in C:\inetpub\wwwroot\update_customer.php:44 Stack trace: #0 C:\inetpub\wwwroot\update_customer.php(44): SoapClient->__call(‘Insert_Customer…’, Array) #1 C:\inetpub\wwwroot\update_customer.php(44): NTLMSoapClient->Insert_Customer_Pub(Array) #2 C:\inetpub\wwwroot\update_customer.php(64): commerce_nav_crons->sample_run() #3 {main}]
SHIT
Could you give me some hint about this?
on November 6, 2013 at 09:42
Permalink
Well this is exactly what is says: Insert_Customer_Pub method is missing in your NAV.
Sometimes you have to restart NAV in order to get new methods working, but I can not be much of the help on the NAV side. I only did PHP integration.
on December 4, 2013 at 08:26
Permalink
Greetings! Very useful advice within this post!
It’s the little changes that make the largest changes. Many thanks for sharing!
on January 20, 2016 at 12:19
Permalink
Hi Arte,
Thanks for this great post.
I’m getting the following error:
ERROR: SoapException: [SoapFault exception: [a:Microsoft.Dynamics.Nav.Service.WebMetadata.ServiceBrokerException] Namespace “” in message is invalid, expected “urn:microsoft-dynamics-schemas/page/oti_cust_list” in /var/www/SZL/call.php:50 Stack trace: #0 /var/www/SZL/call.php(50): SoapClient->__call(‘Read’, Array) #1 /var/www/SZL/call.php(50): NTLMSoapClient->Read(Array) #2 /var/www/SZL/call.php(78): schweppes_nav->run_oti_cust() #3 {main}]
10000
Can you please give me some hint about this?
on January 31, 2016 at 05:53
Permalink
Hi Arte,
Great post! Do you happen to have the NAV Code for the XMLport as well?
I’m trying to get this exact thing going but running into issues.
Thanks!
on February 24, 2016 at 18:07
Permalink
Hi, have an error and i hope that you can help me.
The error:
[WSDL] SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://192.168.1.248:8047/DynamicsNAV/WS/XXXX/Page/WS_List_User_Work_Center’ : Start tag expected, ‘SoapClient(‘http://192.168….’, Array)
#1 C:\Users\appproduzione\Documents\XXX\php_test2.php(65): commerce_nav_crons->sample_run()
#2 {main}
thrown in C:\Users\appproduzione\Documents\XXX\php_test2.php on line 3
I try http, https i try to add commented User-Agent but nothing. Not work!
Have you ideas?
Thanks,
Jacopo
on February 26, 2016 at 11:59
Permalink
Well try to open you url here: http://192.168.1.248:8047/DynamicsNAV/WS/XXXX/Page/WS_List_User_Work_Center and you should see XML
I’d say your XXXX have to be replaced by a number – ID of your company or something..
on February 26, 2016 at 12:09
Permalink
Sorry – no. I only did PHP part.
on February 26, 2016 at 12:11
Permalink
It’s hard to tell exactly what is wrong in it. Either a service name or one of the parameters.
on March 3, 2016 at 14:59
Permalink
I have a new error after some change:
PHP Fatal error: SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘http://192.168.1.248:8047/DynamicsNAV/WS/XXX/Page/WS_List_User_Work_Center’ : Document is empty
But if i open it on browser i see xml.
I try other url as SystemService and WS.asmx?WSDL
Can you give me a hint?
Thanks,
Jacopo
on May 27, 2016 at 05:55
Permalink
I get the following error AFTER I implemented your solution:
SoapFault exception: [a:Microsoft.Dynamics.Nav.Service.WebMetadata.ServiceBrokerException] Namespace “” in message is invalid, expected “urn:microsoft-dynamics-schemas/page/itemjournal”
Which is better than BEFORE when I go a different error that would crash my system. Any thoughts?
I am trying to create an Item Journal line and and having a rough time.
I can authenticate, no problem.
Retrieve my list of companies, no problem.
Create Sales Orders – including multiple Sales Lines, not problem.
But Item Journal creation is giving me the error above and I can’t figure out why.
on July 7, 2016 at 15:00
Permalink
I have the same problem as KeepUp: I get XML content in the browser, but PHP tells me the document is empty.
Any idea what could be the solution?
on October 18, 2016 at 12:13
Permalink
I suppose, you’ve solved it by now. But it feels like retrieval of the data is not working properly – login/password or url is incorrect. Also IP restrictions could be in place.