Consuming Navision 2009 Web services

joeydoodjoeydood Member Posts: 4
All,

I've customized and coded against nav 2009 web services for almost 3 years now. Also created a business project with asp.net, vb and c# with over 30,000 lines of .net code, exclusively consuming nav 2009 web services. Thought you guys might appreciate a quick whitepaper on consuming nav 2009 web services with regard to producing automated invoices.

Below is the content of the whitepaper. If you would like it in ms word format, please let me know.

WHITE PAPER
Consuming Navision 2009 Web services
4 a.m. Productions, Joseph Palazzo
September 2013


Overview

This document will discuss the consumption of web services which are exposed in the Navision 2009 application. Although this topic is rather well known in the Nav developer community, a working example in a live environment will be reviewed for those developers who wish to make the transition from the current c/al and c/side coding environment to the .net development environment or for developers who require further information on the topic. At the time of publishing, this feature was implemented in a live, global environment amongst hundreds of other Navision web services-based features.

The working example will focus on producing an electronic version of Posted Sales Invoice information exposed via web services and automated for unattended delivery to the customer via email. This is accomplished with the use of the exposure of specific web services which can be consumed through any standard IDE, particularly Visual Studio.

Only 2 web services in Navision 2009 are required.

The possibilities of the final product from the consumption of these web services are only limited by the imagination of the developer.


Why Use Web Services

The use of Navision web services allows developers to enjoy the freedom to develop the customized functionality required to meet or exceed the growing needs of business and technology. The use of Navision web services can extend the Navision ERP system beyond its current limits without the worry about Navision business rules. The liberty to conduct the necessary experimentation with powerful .net technologies presents a new and open world to the development community which simply is not possible when one is restricted to archaic c/al and c/side coding techniques.

Make no mistake that opening Navision to .net development can produce very interesting software and features. It will sometimes be necessary to develop new web services in the c/al and c/side environments for consumption by .net developers because not all Navision web services are created equal. This is extremely rare and can be found in the most exotic of .net/Nav projects.

In other words, the possibilities are now as endless as the imagination of the developer.

This means that a developer does not need to know a single line of c/al or c/side code while easily meeting the ever changing needs of businesses and technology. Imagine being able to incorporate LINQ technology into a Navision web project.

Additionally, Navision web services are rather future-proof and therefore any code developed upon this technology will inherit this feature in a similar fashion. An upgrade to Navision means that the .net code written against the exposed web services is not lost nor does it require change unless the Navision web service changes. What this means for developers is that your Nav customizations live outside of Nav.

It will likely be the case that a developer will require some customization of web services, such as the addition of a new field to a table. Because web services are stored in the Navision database as SQL Server tables, the changes to the schema can theoretically be exported from the source and executed against the new Navision database, therefore synchronizing the schema information.


Web Services Flexibility

There is no doubt that any serious developer will eventually be required to engage in the manipulation of the core Navision database to extend its value. For instance, suppose there is a need for a new date/time field on the Customer Table: ‘emailSent’. Once this field is properly added to the Customer Table, the Navision web services fields will reflect the new value for use. When this field is added to the Navision web services, the .net development environment only requires a refresh of the web service in question. The .net WSDL is updated automatically and the field is ready for standard CRUD use in .net.

Visual Studio’s Object Browser and intellisense will instantly reflect the new field.


Web Services in Reverse

It is entirely possible for a developer to create very powerful .net-based business intelligence functionality based on the consumption of Nav web services. If this functionality is required back in the c/al or c/side environment, one could easily create the dll required for the c/al and c/side environment’s use. Imagine creating a .net class to acquire and manipulate some Nav information whereby the coding methodologies are much easier in .net than c/al or c/side. This .net class could be compiled into a stand-alone dll and used inside the Nav c/al or c/side development environment where it would have previously not been practical or possible.


Supporting Architecture

It is beyond the scope of this document to detail the architecture required to support this environment. Suffice it to say that such an environment is rather straightforward conceptually however; it can contain pitfalls which are typically contingent on the host network, supporting hardware, operating systems and security. It is recommended that developers contact their Navision vendors for further information where this is concerned.

From a high level point of view, the pieces which are needed include Navision 2009 running on its own server. This will expose the web services. If automation is required, then an application utilizing a custom Windows Service will be required on the same server. The Windows Service can fire any calls required to talk to the developer’s application which can reside inside an IIS server or even in a desktop or mobile application. The IIS calls are easily answered through WCF technology using either REST or SOAP.


The Development Environment

For any developer, the most valuable tool in one’s arsenal besides human imagination is the Integrated Development Environment; in this case, Visual Studio or any IDE which can properly transmit network credentials to a Navision application server can be used. This allows the developer to leverage the power of the entire .net framework.

As in the case of using Visual Studio to consume Nav web services, a simple web reference can be made in the project by pointing to a properly configured Navision application server for discovery. Once the reference is made, Visual Studio will create the WSDL automatically and the new class will be instantly available to the developer.


Electronic Posted Sales Invoices

Navision 2009 presents to the developer a series of web services which can appear to be confusing at first simply due to the vast quantity available. However, only the following web services are required to be exposed for consumption in the developer’s IDE:

• Posted_Sales_Invoice
• E_Mail_List_Entries_Service

Additionally, an apx page which is the visual representation of the acquired data will be used to create the html output for the transmission of the Posted Sales Invoice information to the Customer’s invoice email stored in the Customer Card. This page is rendered to a StringWriter in .net using the HttpContext.Current.Server.Execute method. The StringWriter’s content is then passed to the body of a standard email object, addressed to the Customer’s Sales Invoice email. The details of creating, populating and rendering an aspx page with invoice information are a topic for another discussion – and there are several ways to achieve this.

The following function demonstrates a general example of how to retrieve data from the Posted Sales Invoice web services exposed by Navision. Once the data is retrieved, it can be enumerated with simple coding techniques, creating an email for each customer which is sent to the appropriate email address retrieved from the email List Entries Service. The email List Entries are retrieved in a similar fashion as below.

Public Function getPostedSalesInvoicesByDate(ByVal fromDate As String, ByVal toDate As String) As List(Of Posted_Sales_Invoice)

Dim retval() As Posted_Sales_Invoice

Try

'====================================================
'create service
'====================================================
Dim service As New Posted_Sales_Invoice_Service
service.Url = System.Web.HttpUtility.HtmlEncode([your webservice url] & "Page/Posted_Sales_Invoice")
service.UseDefaultCredentials = False
service.Credentials = [your network credentials]
'====================================================

Dim filters As New List(Of Posted_Sales_Invoice_Filter)
Dim filter As Posted_Sales_Invoice_Filter

filter = New Posted_Sales_Invoice_Filter
filter.Field = Posted_Sales_Invoice_Fields.Document_Date
filter.Criteria = Date.Parse(fromDate) & ".." & Date.Parse(toDate)
filters.Add(filter)

retval = service.ReadMultiple(filters.ToArray, "", 0)

'====================================================
'convert array to list of
'====================================================
If Not IsNothing(retval) And retval.Length > 0 Then

Dim newRetval As New List(Of Posted_Sales_Invoice)

newRetval = retval.ToList()

Return newRetval
End If

Catch ex As Exception

Return Nothing

End Try

End Function

As you can see, the technique for retrieving Posted Sales Invoices within a given date range is quite simple and straight forward. Any developer with moderate coding experience can retrieve data, enumerate through it, create individualized emails and provide the ability to call this functionality from a Virtual Machine running a copy of the Navision 2009 application with web services exposed.

For the developer who wishes to go the extra mile, the enumeration of the Posted Sales Invoices presents an opportunity to create custom aggregators which can be used for internal-use summary email notifications. One other very good use of the enumeration process is to inform responsible parties of missing or incomplete information for correction: e.g. customer invoice email is missing. A code sample of this is below:

Dim sbCustEmails As New StringBuilder

If custEmails.Length = 0 Then

sbCustEmails.AppendLine(cust & " has no emails on file for INVOICING.<br />")
sbSummary.AppendLine("No invoice-email found for: " & cust & "<br />")

'===========================================
'build emails for missing info notifications
'first item in array is email subject
'===========================================
sbMissingInfo = New List(Of String)
sbMissingInfo.Add("No emails for INVOICING: " & cust)
sbMissingInfo.Add("Could not send invoice: " & psi.No & "<br />")

'add to missing email list
sbMissingInfos.Add(sbMissingInfo)
'===========================================



Developers may wish to begin building their own internal framework of functionality and it is recommended that a secondary layer of secured, credentialed web services be architected to expose various selected features to the inside and outside world. It is imperative that Navision’s internal core web services are always protected and never ever exposed to any entity besides developers. The reasoning is to firstly protect Navision from threats. Secondly, this layered architecture allows developers to engage in a mid to low level of customized functionality and also control points of entry should there be a need for the enforcement of security measures.

Challenges

The Navision web service OEM definitions are minimal yet robust enough for most developers to make rather advanced modifications. There is a significant lack of documentation and examples available to the development community. Because of this, development efforts can be hindered by unknown issues yet to be discovered by the community. It is recommended that development efforts are insulated with enough time to allow the proper discovery necessary to achieve the intended goals.

About Joseph Palazzo and 4 a.m. Productions

Joseph Palazzo is a self-taught .net developer with over 30 years coding and technology experience in the fields of pro audio manufacturing, grocery distribution and clinical/ER medical environments. His most recent Navision accomplishments involve pushing the limits of Navision web services in the pro audio industry for world-wide use by a popular but privately held entity. He has been developing .net web applications using Navision web services since 2010 and also has patents pending for technologies extending Navision web services.

Joseph presently works as a freelance enterprise architect in Baton Rouge La. and is the owner of 4 a.m. Productions. He can be reached via email: joeypalazzo@yahoo.com.

More Information on Web Services

What can I do with NAV Web Services?
NAV Web Services

Comments

  • ogefranogefran Member Posts: 13
    Hello Joseph,

    Thanks for your insightful and detailed post. I have a challenge that is slightly different from the issue you addressed here and I do hope you could suggest the best approach to my issue. I want to create a web service that NAV can use to update record in Microsoft CRM. What I mean is this, I have a field say “Invoice Status” on Navision and CRM and this field is updated from Navision based on the status of an Invoice. Anytime this field changes in Navision, I want to update the field in CRM through a web service. How can I achieve this?

    Thanks in advance as I await your suggestions.

    Francis
  • yukonyukon Member Posts: 361
    Hi ogefran,

    I think you have to choose 3 options for your case.
    1) Call CRM WS in navision (this one may be complex but easily to maintain)
    2) Use the Navision - CRM connector (you need to customize for your own)
    3) Write your own dll and call it from navision (i used this method but difficult to maintain)
    - in your dll need to use Nav webservice and Crm Web Service (entity should be use dynamic entity)
    - you need to install your dll file on each client machine if your are not using the job queue.

    Hope this help.

    Regards,
    Yukon
    Make Simple & Easy
  • ogefranogefran Member Posts: 13
    Hello Yukon,

    Thanks for your response. I am new in CRM but have good knowledge of NAV and looking at the options listed, option one seems fine to me because of the ease of maintainance. Please could you assist me with the step to follow to achieve this or any link/documentation that can be of help?

    Regards.
  • yukonyukon Member Posts: 361
    Hi ogefran,

    Srry for too late reply. I not sure which crm version you are using. But i hope you can refer below sample for your requirement.
    Local Variable :
    Name	DataType	Subtype	Length
    _xmlXMLHTTPRequest	Automation	'Microsoft XML, version 2.0'.XMLHTTPRequest	
    _xmlDOMDocument	Automation	'Microsoft XML, version 2.0'.DOMDocument	
    _xmlIXMLDOMNode	Automation	'Microsoft XML, version 2.0'.IXMLDOMNode	
    URL	Text		1024
    Host	Text		30
    Port	Integer		
    OrganizationName	Text		30
    User	Text		30
    Password	Text		30
    AccountName	Text		50
    AccountAddress_1	Text		50
    AccountAddress_2	Text		50
    
    Name	ConstValue
    Text50000	http://%1:%2/mscrmservices/2007/CrmService.asmx
    
    
    fnGetCRMAccount()
    //CRM 4.0
    Host     := 'Your CRM Dev. Server';
    Port     := 5555;
    OrganizationName := 'Your CRM Organization';
    User     := 'domain\user';
    Password := 'password';
    URL      := STRSUBSTNO(Text50000,Host,Port);
    
    IF ISCLEAR(_xmlXMLHTTPRequest) THEN
      CREATE(_xmlXMLHTTPRequest);
    IF ISCLEAR(_xmlDOMDocument) THEN
      CREATE(_xmlDOMDocument);
    
    IF User='' THEN
      _xmlXMLHTTPRequest.open('POST',URL,FALSE)
    ELSE
      _xmlXMLHTTPRequest.open('POST',URL,FALSE,User,Password);
    
    _xmlXMLHTTPRequest.setRequestHeader('SOAPAction','http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple');
    _xmlXMLHTTPRequest.setRequestHeader('Content-Type','text/xml; charset=utf-8');
    //_xmlXMLHTTPRequest.setRequestHeader('Content-Length',yourxmllength);
    _xmlXMLHTTPRequest.send(
    '<?xml version="1.0" encoding="utf-8"?>' +
      '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ' +
        'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
        'xmlns:xsd="http://www.w3.org/2001/XMLSchema">' +
        '<soap:Header>' +
          '<CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices">' +
            '<AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">0</AuthenticationType>' +
            '<CrmTicket xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes"></CrmTicket>' +
            '<OrganizationName xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">'+ OrganizationName + '</OrganizationName>' +
            '<CallerId xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes">00000000-0000-0000-0000-000000000000</CallerId>' +
          '</CrmAuthenticationToken>' +
        '</soap:Header>' +
        '<soap:Body>' +
          '<RetrieveMultiple xmlns="http://schemas.microsoft.com/crm/2007/WebServices">' +
            '<query xmlns:q1="http://schemas.microsoft.com/crm/2006/Query" xsi:type="q1:QueryExpression">' +
              '<q1:EntityName>account</q1:EntityName>' +                                                   //Case sensitive
              '<q1:ColumnSet xsi:type="q1:ColumnSet">' +                                                   //Retrive Fields
                '<q1:Attributes>' +
                  '<q1:Attribute>name</q1:Attribute>' +
                  '<q1:Attribute>address1_line1</q1:Attribute>' +
                  '<q1:Attribute>address1_line2</q1:Attribute>' +
                '</q1:Attributes>' +
              '</q1:ColumnSet>' +
              '<q1:Distinct>false</q1:Distinct>' + 
              '<q1:Criteria>' +
                '<q1:FilterOperator>And</q1:FilterOperator>' +
                '<q1:Conditions>' +
                  '<q1:Condition>' +                                                                       //Filter on Field
                    '<q1:AttributeName>accountid</q1:AttributeName>' +
                    '<q1:Operator>Equal</q1:Operator>' +
                    '<q1:Values>' +
                      '<q1:Value xsi:type="xsd:string">{E0D44A6E-6E22-DE11-97FF-001A646482F4}</q1:Value>' +//Filter Value
                    '</q1:Values>' +
                  '</q1:Condition>' +
                '</q1:Conditions>'+
              '</q1:Criteria>' +
            '</query>' +
          '</RetrieveMultiple>' +
        '</soap:Body>' +
      '</soap:Envelope>'
    );
    
    IF _xmlXMLHTTPRequest.status <> 200 THEN
      MESSAGE('Http Error ' + ' ' + FORMAT(_xmlXMLHTTPRequest.status) + ': ' + _xmlXMLHTTPRequest.statusText)
    ELSE BEGIN
      _xmlDOMDocument.async := FALSE;
      _xmlDOMDocument.load(_xmlXMLHTTPRequest.responseBody);
    
      _xmlIXMLDOMNode := _xmlDOMDocument.selectSingleNode('//BusinessEntity/q1:name');
      IF NOT ISCLEAR(_xmlIXMLDOMNode) THEN
        IF _xmlIXMLDOMNode.text <>'' THEN
          AccountName := _xmlIXMLDOMNode.text;
      
      _xmlIXMLDOMNode := _xmlDOMDocument.selectSingleNode('//BusinessEntity/q1:address1_line1');
      IF NOT ISCLEAR(_xmlIXMLDOMNode) THEN
        IF _xmlIXMLDOMNode.text <>'' THEN
          AccountAddress_1 := _xmlIXMLDOMNode.text;
    
      _xmlIXMLDOMNode := _xmlDOMDocument.selectSingleNode('//BusinessEntity/q1:address1_line2');
      IF NOT ISCLEAR(_xmlIXMLDOMNode) THEN
        IF _xmlIXMLDOMNode.text <>'' THEN
          AccountAddress_2 := _xmlIXMLDOMNode.text;
    
      _xmlDOMDocument.save('C:\response.xml');
      
      MESSAGE('Account Name : ' + AccountName + '\' + 'Address : ' + AccountAddress_1 + ' ' + AccountAddress_2);
    END;
    CLEAR(_xmlIXMLDOMNode);
    CLEAR(_xmlDOMDocument);
    CLEAR(_xmlXMLHTTPRequest);
    

    Script Ref Link:
    http://msdn.microsoft.com/en-us/library/aa496081.aspx
    http://msdn.microsoft.com/en-us/library/cc150828.aspx

    And don't forget to search at GG. :wink:

    Hope this help to you.

    Best Regards,
    Yukon
    Make Simple & Easy
Sign In or Register to comment.