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

Posted on April 2, 2013 at 17:51 by arte · Permalink
In: Drupal, English, Fighting the system · Tagged with: , ,

32 Responses

Subscribe to comments via RSS

  1. Written by Morgan
    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.

  2. Written by arte
    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.

  3. Written by Morgan
    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.

  4. Written by arte
    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”.

  5. Written by TB
    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?

  6. Written by arte
    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!

  7. Written by TB
    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 ???

  8. Written by arte
    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

  9. Written by Bojjaiah
    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……………

  10. Written by Bojjaiah
    on August 7, 2013 at 07:11
    Permalink

    hi arte,
    great work…………

  11. Written by Remco
    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!

  12. Written by arte
    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.

  13. Written by Remco
    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

  14. Written by arte
    on August 28, 2013 at 18:35
    Permalink

    Glad it worked!
    Spend endless hours banging my head against the wall 😉

  15. Written by Andre
    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

  16. Written by arte
    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.

  17. Written by Andre
    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

  18. Written by arte
    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.

  19. Written by Andre
    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

  20. Written by 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?

  21. Written by arte
    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.

  22. Written by Juliann
    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!

  23. Written by Papin d'Eve
    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?

  24. Written by Joe Draeger
    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!

  25. Written by KeepUp
    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

  26. Written by arte
    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..

  27. Written by arte
    on February 26, 2016 at 12:09
    Permalink

    Sorry – no. I only did PHP part.

  28. Written by arte
    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.

  29. Written by KeepUp
    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

  30. Written by Andy Borgmann
    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.

  31. Written by Jelle De Loecker
    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?

  32. Written by arte
    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.

Subscribe to comments via RSS

Leave a Reply