Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

Exclusive offer: get 50% off this eBook here
Implementing Microsoft Dynamics NAV 2009

Implementing Microsoft Dynamics NAV 2009 — Save 50%

Explore the new features of Microsoft Dynamics NAV 2009, and implement the solution your business needs with this book and eBook

$35.99    $18.00
by David Roys Vjekoslav Babić | December 2008 | .NET Microsoft

The three-tiered architecture in Dynamics NAV 2009 enables wonderful things to happen. Yes, you can get better scalability by separating the business logic from the presentation layer, but there are a couple of other things you get as a result of the Dynamics NAV Service Tier: Web services enablement and Multiple Presentation Layers. The RoleTailored client is an example of a presentation layer. The user interfaces are lovely, without a doubt, but it's the Web services that have got people buzzing. Web services enablement opens up a whole new world for NAV implementations and in this article, we're going to look at some of the things we can now do.This is in continuation of the Previous Article on the same topic.

In this article by David Roys and Vjekoslav Babic you'll learn what you can do with Web services.

Sidebar gadget

A sidebar gadget is a simple single-tasked tool that sits in the sidebar on Windows Vista. If you don't have Windows Vista, you're out of luck and won't be able to run this sample. You can explore the free gadgets available for download at http://gallery.live.com/. Typical gadgets include:

  • RSS Feed Readers
  • News Readers
  • Weather Reports
  • Clocks
  • Performance Monitoring Tools
  • Mini Notepads
  • Photo Slideshows

Hopefully you get the idea.

We're going to create a sidebar gadget that will use the Web service capabilities of Dynamics NAV 2009 to display a cue (a stack of documents similar to those shown in the RoleTailored client), based upon the document approvals features that have been available since NAV 5.0. We want to display a document stack that represents the number of documents requiring approval from the current user and will allow the user to select the type of document as a configuration setting. In our example, clicking the document stack will show a list of documents and clicking an individual document will launch the RoleTailored client. There's no reason why you can't take this example and extend it to include the ability to display the actual documents and carry out the approval, all from the comfort of your Windows Vista desktop.

Design time

When we start to de sign NAV solutions, we use our knowledge of the standard application to create a solution that fits nicely within the NAV paradigm. We try to emulate the way the standard application solves common business problems and use the components that are used by the product team in a consistent manner.

Designing applications for .NET, or in this case for a sidebar gadget, follows the same conventions. First of all we need to understand a little bit about what makes a sidebar gadget so that we can know the constraints of our design.

What are little gadgets made of?

There is an excellent tutorial on MSDN Magazine's web site by Donavon West that tells you how to build a sidebar gadget for displaying MSDN Magazine articles in a news-ticker format with the ability to click an article to see more details and click another link to read the full article on the Web. We're going to use that article and the gadget provided for download as the basis for exploring what a gadget is, which will in turn help us to design our own gadget.

You can read Donavon West's MSDN Magazine article at:
http://msdn.microsoft.com/en-nz/magazine/cc163370(en-us).aspx
You can download the Gadget from:
http://gallery.live.com/liveItemDetail.aspx?li=b21af41e-b846-46d9-a873-ac12a3c65ab3

Essentially a gadget is little more than a mini web page (HTML file with some supporting resources such as images and JavaScript) and an XML definition file called gadget.xml. When we're writing a sidebar gadget for Windows Vista, the HTML page is rendered in Microsoft Internet Explorer 7, so there is no need to worry about cross-browser support. Which is nice.

If you download the gadget and save it somewhere instead of installing it, you will see an icon for the gadget like this:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

Before we can use this gadget there is a little problem that needs to be fixed—unfortunately it is pointing to an RSS Feed URL that is not valid and therefore the gadget doesn't work correctly.

Donavon explains that a sidebar gadget is simply a collection of files that are stored in a ZIP or CAB file with a .gadget extension, so we can rename the file with a .zip extension and we should be able to open it as a folder. If you open the compressed folder, or extract it, you will see the following files:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

There is a file called local.js that we will need to edit in order to fix the problem. Gadgets support multi-language capabilities and if your language matches the folder names shown, you are going to need to open that folder and edit the local.js thatit contains.

The languages supported by this gadget are as follows:

 

Folder Name

Language

de

German (Standard)

es

Spanish (Spain)

fr

French (Standard)

it

Italian (Standard)

ja

Japanese

kr

I don't think this is a valid language code, so we'll just ignore this.

pt

Portuguese (Portugal)

ru

Russian

zh-CN

Chinese (PRC)

zh-TW

Chinese (Taiwan)

You can get a full list of language codes at http://msdn.microsoft.com/en-us/library/ms533052(VS.85).aspx

According to the tutorial, whenever the sidebar tries to load a file, it searches for the file in folders in the following order:

  • Full locale (en-us, es-us, ja-jp)
  • Language portion of the locale (en, es, ja)
  • Gadget root folder

So what does this mean? If your locale has a language component that is one of the folders listed in the table, you are going to need to edit the local.js within that folder in order for the gadget to work correctly.

When you edit the local.js file (any text editor will do), you will see the following:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

If you copy the feedUrl string and paste it into a web browser address bar, you will see a runtime error telling you this is not a valid address. A little bit of digging soon reveals an address that we can use for the gadget:
http://msdn.microsoft.com/en-nz/magazine/rss/default(en-us).aspx?issue=1

You need to replace the old URL with the new one so that your line in the fi le looks like the following:

LOCAL.feedUrl = 'http://msdn.microsoft.com/en-nz/magazine/rss/
default(en-us).aspx?issue=1';

Now we can rename the file back to a .gadget extension and install it. This is to help us examine the main components of a sidebar gadget so we can consider how we will design our own gadget.

The gadget

The most obvious part of a gadget is the gadget itself. This is the gadget's main HTML page that is provided in the base src property of our gadget.xml file. For us, we want this to show a single document cue that represents the number of approval entries that are awaiting action for the current user. We want the main docked gadget to look something like this:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

The image is meant to look like a cue from an Activities Part in a Role Center. The pencil sketch is there to give you an idea. Since the 22 approval actions could be for multiple different document types, we will have some text underneath the stack of documents that tells us how many of each of the different document types there are. It could either fade in and out, or scroll horizontal like a marquee.

That takes care of the docked state of the gadget. When we undock it, we can get a larger area to play with, so it would be nice if the undocked state showed one stack of documents for each of the approval document types with the name of the document type shown underneath. This may be a little time-consuming, so maybe we'll add that to version 2. For now the undocked image will be the same as the docked image.

There are a couple of other pages that need to be considered: Flyouts and Options dialog.

Flyouts

When you click on a part of gadget, you can activate a flyout, which is basically a web page that gets displayed at the side of the gadget. The flyout file is specified by setting System.Gadget.Flyout.file to the name of the flyout HTML file. In the case of our MSDN Ticker gadget, the flyout looks like the following:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

For our flyout, we are going to show a list of approval entries with the ability to click a hyperlink to open the approval entries screen. An obvious next extension to this gadget is to provide the ability to approve, reject, or delegate the approval entry directly from the gadget without needing to open the RoleTailored client. For now, we'll concentrate on making this work with our NAV Web service. After taking a quick look at the fields available on the approval entry screen, our flyout will look something like the following:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

It's a simple table showing the documents with a document type and number, the ID of the sender and the amount that the document is for.

Options

There is one more part of the gadget to consider for our design and that is the options dialog page. Let's take a look at that for the MSDN Magazine Ticker sample gadget.

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

When I hover my mouse over the gadget, a mini tool bar appears allowing me to close the gadget, show the options, and drag the gadget to a different position. Click the spanner to show the options page.

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

As you can see it's just another little web page with some options on it. You need to instruct the gadget to enable the options icon by setting System.Gadget.settingsUI to the name of the options HTML file, generally in the gadget initialization area of our script. Donavon's article explains how this is done, and provides sample code for how to set up a callback function for when the options dialog closes (so your gadget can read the new user preferences).

For our gadget we are going to need a place where we can enter the URL for our Web service. For more advanced options, we could possibly provide the ability to specify how often the gadget will call the Web service.

Our options page will look something like this:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

These pencil sketches are just there to convey the intended layout of the pages; it's a lot quicker to scribble something on a piece of paper (for us) than to start playing around with graphics programs and although they're a little rough, if you squint at them, you can sort of work out what's intended in the final solution. Remember it's important to get an understanding of the design at this stage but it doesn't need to look great; form follows function.

The tricky bits

Now that we' ve done the high-level design for our sidebar gadget, and we know that a sidebar gadget is just a series of HTML pages, we can start to look at the technical design. There are a couple of tricky bits to take care of: how are we going to call our Web service from within what is essentially a web page, and how are we going to take our list of documents requiring approval and convert them into the table and graphics we want to display.

The great thing about sidebar gadgets and the way they are constructed is that you can simply rename the file and take a look at how they are doing what they do (and, of course, you can borrow ideas and code). If you search on the Web, you'll find quite a few examples of sidebar gadgets that call Web services, so there're plenty of examples to look at. Let's pick an example from Microsoft that uses the Exchange 2007 Web service to display email, calendar, and task information. You can download it from:
http://www.microsoft.com/downloads/details.aspx?FamilyID=F9A0D33CC894-4EA1-AD20-4E418C715175&displaylang=en

A quick search for Exchange Web services Gadget will help you locate it. Actually finding this gadget was a stroke of luck because it does pretty much everything we are after:

  • It has a setup page with a time interval on it.
  • It calls a Web service to find how many items there are in a folder and shows a summary.
  • It displays a flyout with a more detailed view of the folder contents.

Finding good examples on the Internet and learning from them is a key skill for doing this kind of development.

Just a little bit of SOAP

Calling a NAV Web service from within a Visual Studio.NET project is dead easy as we've already seen. We just add our Web service as a web reference and Visual Studio does all the hard work for us. It creates a proxy class that allows us to call the member functions and access the properties of the service as though it was a piece of code that we had written ourselves and not just some black box at the end of a URL. But how do we do this when we don't have Visual Studio?

Essentially a Web service is just some text sent over the Internet that generates a response (which is also text). It just so happens that the text being sent and received is formatted as XML which is handy because there is lots of support for reading XML text. Web services typically use a protocol called Simple Object Access Protocol (or SOAP) to allow any system that can post a request to a URL (and read a response message) to call to a function exposed by the Web service. In order to do this for our sidebar gadget, we need two things: we need to know how to send and receive our request, and we need to know what the SOAP request should look like.

Figuring out the HTTP call and response handling isn't too hard and you can do this by looking at the Exchange Web service gadget source code or, once again, searching the Internet.

Looking at how Microsoft did it in their Exchange Web service gadget shows us we can use the native Microsoft.XMLHTTP object provided by Internet Explorer (remember that a sidebar gadget runs in Internet Explorer only, so we don't need to worry about cross-browser support) to make an HTTP post to our Web service and read the response.

Finding the XML for the SOAP request that is needed to invoke a NAV Web service is going to be a little trickier. If we do a Web search for 'how to view a soap request in Visual Studio?', it doesn't take much to find a link to a freeware product called Fiddler that will allow me to inspect messages to and from my web server. Here is the URL:
http://www.fiddler2.com/fiddler2/

If we use this tool on the simple example we started the chapter with, we can see the SOAP request is:

And the response is:

We can guess we could have worked out this request and response format by reading the WSDL (pronounced 'wiz-dal'), that we get when we type the URL to the Codeunit in our web browser; however, we can think that using Visual Studio to test calling our Web service is by far the easiest way, and using the Fiddler tool to be able to inspect and copy the SOAP Envelope XML has got to be better than thinking.

Now before we get too carried away trying to create a series of Web service calls to allow us to pull data from a page type Web service, we're going to create a simple proof-of-concept web page that will make a JavaScript call to this NAV Web service with our ConvertStrToUpperCase function.

An HTML page that calls a NAV Codeunit

This next script is 72 lines of text. The point of the exercise is to show how easy it is to do things in the .NET world even when you don't know what you're doing. Here is the code in full; we'll go through it in detail later:

<HTML>
<HEAD>
<TITLE>Hello NAV 2009 With JavaScript</TITLE>
</HEAD>
<BODY>
<b>Input: </b>hello nav2009!<br/>
<div id="resultContainer"><b>Output: </b></div>

<FORM Name="Form1" ACTION="">
<INPUT TYPE=BUTTON VALUE="Call NAV" NAME="BtnHello" OnClick="Hello
NAV2009()">
</FORM>

<SCRIPT LANGUAGE="JavaScript">
<!--
function HelloNAV2009 ()
{

var data = "";

data += '<?xml version="1.0" encoding="utf-8"?>';
data += '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/
soap/envelope/" ';
data += ' xmlns:xsi="http://www.w3.org/2001/
XMLSchema-instance" ';
data += ' xmlns:xsd="http://www.w3.org/2001/
XMLSchema">';
data += ' <soap:Body>';
data += ' <ConvertStrToUpperCase ';
data += ' xmlns="urn:microsoft-dynamics-schemas/codeunit/NAV_
Codeunit">';
data += ' <p_Str>hello nav2009!</p_Str>';
data += ' </ConvertStrToUpperCase>';
data += ' </soap:Body>';
data += '</soap:Envelope>';

var xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");

var url = 'http://ds-srv-01:7047/DynamicsNAV/ws/CRONUS_
International_Ltd/Codeunit/NAV_Codeunit';

xmlHttpRequest.open("POST", url, false);

xmlHttpRequest.SetRequestHeader("Content-Type", "text/xml");
xmlHttpRequest.SetRequestHeader("SOAPAction", "urn:microsoftdynamics-
schemas/codeunit/NAV_Codeunit:ConvertStrToUpperCase");

xmlHttpRequest.onreadystatechange = readResponse;
xmlHttpRequest.send(data);

function readResponse()
{
if (xmlHttpRequest.readyState == 4)
{
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");

xmlDoc.loadXML(xmlHttpRequest.responseText);

resultText = xmlDoc.getElementsByTagName("return_value")[0].
childNodes[0].nodeValue;

xmlDoc = null;

resultContainerElement = document.getElementById("resultContaine
r");

if (resultContainerElement != null)
{
resultContainerElement.innerHTML = "<b>Output: </b>" +
resultText;
}

xmlHttpRequest = null;
}
}
}
//-->
</SCRIPT>
</BODY>
</HTML>

You can download the HelloNAV2009.html file from www.teachmenav.com (or http://www.packtpub.com/support). You may need to edit the file on the line where the url variable is assigned to point to the Web service URL available on your computer. When you open the file in your browser, you will need to allow the blocked content in order for the example to run.

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

When you click the Call NAV button, the screen updates to show the following:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

Wooohooo! It works!

OK, let's take a look at what's going on in the code. First of all we assign our variable called data to the XML for the SOAP request body (this is found by using the Fiddler application earlier). That block of code is not included for analysis, so let's move on.

This next block of code creates an instance of the Microsoft.XMLHTTP object that we are going to use to make the HTTP post and read the response.

var xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");

var url = 'http://ds-srv-01:7047/DynamicsNAV/ws/CRONUS_International_
Ltd/Codeunit/NAV_Codeunit';

xmlHttpRequest.open("POST", url, false);

xmlHttpRequest.SetRequestHeader("Content-Type", "text/xml");

xmlHttpRequest.SetRequestHeader("SOAPAction", "urn:microsoft-dynamicsschemas/
Codeunit/NAV_Codeunit:ConvertStrToUpperCase");

The highlighted text in the code caused a good deal of grief. Without the SOAPAction request header, the response always contained the WSDL definition of the Web service (the XML document that is shown when you type the Web service URL into the address bar on your browser). Once again this was the missing bit if we look at the results of the Fiddler application trace of .NET application we wrote at the start of this chapter.

The following code will hookup the readResponse function to the xmlHttpRequest so that the response can be read when the call is finished. I borrowed this code from the Exchange Web service gadget (although I had to wade up to my armpits in functions in order to find the code that actually did the business).

xmlHttpRequest.onreadystatechange = readResponse;
xmlHttpRequest.send(data);

function readResponse()
{

if (xmlHttpRequest.readyState == 4)
{

var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");

xmlDoc.loadXML(xmlHttpRequest.responseText);

This next bit of code assigns the resultText variable to the contents of the SOAP response and it took a while to figure out. This example has been taken from the W3 schools site by searching for Microsoft.XMLDOM (http://www.w3schools.com/Xml/xml_dom.asp).

resultText = xmlDoc.getElementsByTagName("return_value")
[0].childNodes[0].nodeValue;

The code is reading the text result from the SOAP envelope. A real example will have to do a lot more with this XML document but for now, this does the job.

Finally, we dispose of some objects and then inject the result text in to the body of our HTML page using the innerHTML property for our resultContainer div class.

xmlDoc = null;

resultContainerElement = document.getElementById("resultContainer");

if (resultContainerElement != null)
{

resultContainerElement.innerHTML = "<b>Output: </b>" + resultText;

}

xmlHttpRequest = null;

I now know that we've broken the back of the problem. We have successfully called a NAV Web service from a web page (which is essentially all a sidebar gadget is). The next tricky bit is to see how to use a page Web service to get the records back that match our documents requiring approval. We'll use the same approach of first writing the code in .NET as a console application and then after we have this working the easy way, we'll convert the code into JavaScript. After that, it's just a case of tidying everything up and making it look pretty.

Hey, Good Lookin'

If there's one thing you need for a sidebar gadget, it's nice graphics. Vista is a beautiful operating system and, to be honest, if a gadget doesn't look good, we don't want it on our desktop. The idea is to use a single image and position the images on top of each other and create the image by taking a document and flipping it and applying perspective. Here are the document stacks.

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

The images were created using Photoshop (and a lot of professional skill), and the original image that the stack is built from actually has the Microsoft Dynamics NAV logo at the top (how's that for attention to detail?) Here's the image of the document:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

HTML is used to render these documents as a stack with the number floating over the top, and the HTML to produce the previous image can be found on the www.teachmenav.com site under the Simple Document Stack sample for this article.

We would generate the HTML dynamically based upon the number of documents requiring approval. The HTML used to generate the previous image is manipulated to give 12 document stacks that will be used by the application. The largest stack is 10 images high but this would be used to represent 31 or more documents. The question mark on the final empty stack shown in the following image will be used when the gadget gets no response from the Web service or has not been properly configured.

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

Now we have nearly everything we need to be able to put together the sidebar gadget. There's just one piece of the puzzle missing; we need to be able to call a Web service to tell us how many documents we have for approval and also return the details of those documents, the rest is just applying more of what we know and writing a lot of code.

We've covered calling a page Web service in an earlier example, so I won't go into details here but we do need to know what we are calling. As you know Web services from NAV can be based on either Codeunits or Pages, so which should we use?

The temptation may be to use a Page Web service as this will allow us to bring back the Approval Entry records for the current user, but we need to do far more than read the records. Our first interaction will be to get a count of the records for approval so we can display the gadget; we don't want the gadget to have a lot of work to do in order to draw its initial state, so ideally we want a quick call that will return just the number of documents and maybe the document name.

If you remember from the beginning, our gadgets are meant to be simple, single-tasked applications, so we want a single document approval gadget to work for any one document type. This way our users can have multiple gadgets on their desktop if they want to be notified on multiple document types. We can achieve this by using a Codeunit type Web service and have one of the parameters an identifier of the type of document we are interested in. The following is an overview of the functions we will need.

Implementing Microsoft Dynamics NAV 2009 Explore the new features of Microsoft Dynamics NAV 2009, and implement the solution your business needs with this book and eBook
Published: January 2009
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:

Operation

Description

GetDocumentTypes

On our configuration (options) page we want the user to be able to select a single document type from a list of document types supported by the gadget. I expect we will return this as an XML document.

GetApprovalStatus

This will be the main operation that is called when the gadget is refreshed. We actually want to return three pieces of information, so our return value is going to be an XML document.

 

It will return the Document Name Singular and Document Name Plural (that is, Purchase Order when the number is 1 and Purchase Orders when it is 0 or greater than one). It will also return the Count, which is the number of documents that require approval from this user.

 

Finally, it will return the Overdue Count, which is the number of documents that require approval from this user that are overdue.

GetApprovalEntries

This will return an XML document that has the record set of approval entries for a given document type.

Approve

This will set the approval status on the selected entry. It will require some kind of record key as a parameter to identify which record is to be updated. This could be as simple as the Entry No.

Reject

This will set the approval status to rejected. It will take a record key and a reject reason.

Delegate

This will delegate the approval entry. Again a record key will be needed.

ShowDocument

This will not be implemented in the initial version as we will implement this by using the RoleTailored client to display the entry. The intention is that this operation can return an XML document that can be formatted by the browser. For the initial version, the ShowDocument feature in the user interface will simply activate a hyperlink that will show the document. The hyperlink could be implemented as a link to open the RoleTailored client, a link to a reporting services report, a link to an Employee Portal page, or a link to a bespoke ASP.NET web page.

The best thing you can do now, is download the final sidebar gadget from our book's web site (www.teachmenav.com), play with it (if you have Vista and Dynamics NAV 2009 installed), and then open up the source code contained within the gadget and take a look at how it's done. You'll also find some screenshots of the final gadget in all its glory.

Calling a Web service from NAV

Up to this point, we have looked at extending NAV by linking external applications to it through the newly introduced Web services enablement. Now we're going to look at the flip-side; we're going to see if NAV can call a Web service.

As the number of Web services available increase, it is more and more likely that we will want to consume them from within our ERP application. Some obvious examples are being able to pull-down the latest currency exchange rates, sending a business document to a trading partner, updating item prices by directly reading them from a vendor, or validate a postal address. There are numerous examples and believe it or not, it's not all that hard to do.

It's quite difficult to find a free Web service (a real practicle example) that we could use that would provide some real business value. But there is a site called http://seekda.com/ that provides a searchable directory of Web services. Here is one of the most frequently used services called GlobalWeather Web service.

Always take the weather with you

The service URL is http://www.webservicex.com/globalweather.asmx?WSDL, and we're going to stick with our tried and trusted technique of getting it to work in Visual Studio (because it's easy), and then making it work from NAV. We're going to use Dynamics NAV 5.0 SP1 for this example—just to illustrate that you don't need the Web services capabilities of NAV 2009 to be able to call a Web service.

Here's the WSDL for the service:

 

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

 

My plan is to use the GlobalWeather service to give some kind of weather report for a customer based upon the city and country on their address. This would work quite nice as a task part against the customer list place in NAV 2009—maybe you could extend the application to work in that way.

So our first part (no guiding you through this step-by-step as you should be able to figure this out from the previous examples) is to make a simple console application that will take a City Name and Country Name as input and output something about the weather (not really sure what it returns at this point). Here's the output from our program:

Enter City Name: Christchurch
Enter Country Name: New Zealand

Calling Web service for Christchurch, New Zealand
<?xml version="1.0" encoding="utf-16"?>
<CurrentWeather>
  <Location>Christchurch, New Zealand (NZCH) 43-29S 172-33E 30M</Location>
  <Time>Aug 11, 2008 - 04:00 AM EDT / 2008.08.11 0800 UTC</Time>
  <Wind> from the SSW (200 degrees) at 3 MPH (3 KT):0</Wind>
  <SkyConditions> mostly cloudy</SkyConditions>
  <Temperature> 39 F (4 C)</Temperature>
  <DewPoint> 37 F (3 C)</DewPoint>
  <RelativeHumidity> 93%</RelativeHumidity>
  <Pressure> 29.83 in. Hg (1010 hPa)</Pressure>
  <Status>Success</Status>
</CurrentWeather>

And this was generated by the following code:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{

using com.webservicex.www;

class Program
{

static void Main(string[] args)
{

GlobalWeather gw = new GlobalWeather();

string city;
string country;

Console.Write("Enter City Name : ");
city = Console.ReadLine();

Console.Write("Enter Country Name : ");
country = Console.ReadLine();

Console.WriteLine("");
Console.WriteLine("Calling Web service for {0}, {1}",
city, country);

Console.WriteLine("{0}", gw.GetWeather(city, country));
Console.ReadLine();
}
}
}

And using Fiddler, here is the SOAP envelope for our successful call:

<?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:Body>
<GetWeather xmlns="http://www.webserviceX.NET">
<CityName>Christchurch</CityName>
<CountryName>New Zealand</CountryName>
</GetWeather>
</soap:Body>
</soap:Envelope>

And here is the response:

<?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:Body>
<GetWeatherResponse xmlns="http://www.webserviceX.NET">
<GetWeatherResult>
&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;CurrentWeather&gt;
&lt;Location&gt;Christchurch, New Zealand (NZCH) 43-29S
172-33E 30M&lt;/Location&gt;
&lt;Time&gt;Aug 11, 2008 - 04:00 AM EDT / 2008.08.11
0800 UTC&lt;/Time&gt;
&lt;Wind&gt; from the SSW (200 degrees) at 3 MPH (3
KT):0&lt;/Wind&gt;
&lt;SkyConditions&gt; mostly cloudy
&lt;/SkyConditions&gt;
&lt;Temperature&gt; 39 F (4 C)&lt;/Temperature&gt;
&lt;DewPoint&gt; 37 F (3 C)&lt;/DewPoint&gt;
&lt;RelativeHumidity&gt; 93%&lt;/RelativeHumidity&gt;
&lt;Pressure&gt; 29.83 in. Hg (1010
hPa)&lt;/Pressure&gt;
&lt;Status&gt;Success&lt;/Status&gt;
&lt;/CurrentWeather&gt;
</GetWeatherResult>
</GetWeatherResponse>
</soap:Body>
</soap:Envelope>

Now that we have proven the technique, let's look at how to call this from within NAV.

Calling out around the world

The principle is quite simple; we are going to use an existing Automation control that is available without additional programming or installation to make an HTML post (similar to our sidebar gadget example where we called the Web service from within JavaScript). In practice, we should probably be using XMLPorts to format the SOAP envelope for the call and parse the response. As usual, we're going to keep things as simple as possible and just create the SOAP envelope as a string. For the response, we're going to copy the stream to a file so we can take a look at what we get back.

SOAP or COM?
There are two ways of calling the Web service from within NAV. One is to make an HTTP Post with a SOAP envelope that we have built up, and the other is to write an Interop enabled COM component and use it as an Automation variable in NAV. The SOAP method we have outlined here is certainly quick to get going, and doesn't require any extra components to be installed on the client (which is one reason I used it). The NAV 2009 architecture means that the COM component only needs to be installed on the server since it will be accessed there. This could make the COM component a better option going forward.
There are also limitations to what you can do using this HTTP Post technique, and as soon as we need to handle binary objects or UTF characters in our Web service call or response, this approach is unworkable. For details on how to get around these problems and how you can make a simple COM component to handle the Web services, take a look at Freddy Kristiansen's blog: http://blogs.msdn.com/freddyk/default.aspx

First of all let's create a simple test Codeunit to try to recreate the results of the previous console application.

We'll need to add an Automation control with type:

'Microsoft WinHTTP Services, version 5.1'.WinHttpRequest

Don't use the subtype with an 'I' infront, called IWinHttpRequest, otherwise you'll run into problems when you try to use the Automation control.

WinHTTP 5.1 is now an operating-system component of the following systems:

  • Windows Server 2003
  • Windows XP Service Pack 1 (SP1)
  • Windows 2000 Service Pack 3 (SP3) (except Datacenter Server)

And I'm guessing it's also available in Vista Business as that's what I have. You can find out more at http://msdn.microsoft.com/en-us/library/aa384273.aspx.

Let's call our local variable for this Automation control l_WinHTTPServices if that helps you to understand the following Codeunit code.

// Instantiate my 'Microsoft WinHTTP Services, version 5.1'.
WinHttpRequest control
CREATE(l_WinHTTPServices);

// Change this file name if you don't have the file path D:Dynamics
NAVWeb Service Demo
l_FileName := 'D:Dynamics NAVWeb Service DemoWeatherResult.XML';

// Create my HTTP Post request.
l_WinHTTPServices.Open('POST',
'http://www.webservicex.com/globalweather.asmx');

// Set the SOAPAction so the Web service will be called.
l_WinHTTPServices.SetRequestHeader('SOAPAction',
'"http://www.webserviceX.NET/GetWeather"');

// Without this it will not work
l_WinHTTPServices.SetRequestHeader('Content-Type', 'text/xml');

// Build the SOAP envelope XML String
l_Data += '<?xml version="1.0" encoding="utf-8"?>';
l_Data += '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/
envelope/"';
l_Data += ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"';
l_Data += ' xmlns:xsd="http://www.w3.org/2001/XMLSchema
">';
l_Data += ' <soap:Body>';
l_Data += ' <GetWeather xmlns="http://www.webserviceX.NET">';
l_Data += ' <CityName>Christchurch</CityName>';
l_Data += ' <CountryName>New Zealand</CountryName>';
l_Data += ' </GetWeather>';
l_Data += ' </soap:Body>';
l_Data += '</soap:Envelope>';

// Make the call
l_WinHTTPServices.Send(l_Data);

// Wait for a response and throw an error if none received
IF NOT l_WinHTTPServices.WaitForResponse(60) THEN
ERROR('Request timed out.');

// Create a file to dump my result to. In a real-world scenario
thiswould be a
// temp file or maybe a BLOB field. Or better still I would load the
stream into
// an XMLDOM object
l_File.CREATE(l_FileName);

l_File.CREATEOUTSTREAM(l_OutStream);
l_InStream := l_WinHTTPServices.ResponseStream;

REPEAT
l_InStream.READTEXT(l_Data);
l_OutStream.WRITETEXT(l_Data);
UNTIL l_InStream.EOS;

l_File.CLOSE;

HYPERLINK(l_FileName);

The following is the result of running the Codeunit:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

 

 

As you can see, it's still cold. we're not going to go through decoding this result string and building the final example, but you should be able to take this and extend it to meet the requirements set out at the beginning. If you get stuck, you can look on www.teachmenav.com for a working solution download. When you do start to use NAV calls to Web services, make sure you encapsulate the methods in their own Codeunits so you can simply call the function from a Codeunit rather than needing to put all that SOAP in your main code. It may be a good idea to create a single Codeunit for each Web service you want to call, or possibly keep all of your Web service calls in one Codeunit.

Service oriented or service enabled?

Most of us will have heard of Service Oriented Architecture or SOA, but does the new functionality in NAV 2009 make it an SOA? Well, in a word, no. The thing is that a service oriented architecture requires more than just Web services. In fact according to Krafzig, Banke, and Slama in their book Enterprise SOA, 'an SOA is based on four key abstractions: application frontend, service, service repository, and service bus.' We certainly have an application frontend (at least one), but what about the others?

When is a service not a service?

Is a NAV Web service a service in an SOA sense? Not really. An SOA service typically encapsulates a business concept such as 'open customer account' or 'raise purchase order'. While it is true to say that with NAV Web services we can insert purchase order headers and lines and also create customer records, this is not the same as the business processes an SOA would expose. For example, opening a customer account may start with a request which may require the collection of certain information such as name and address. The account will need some grouping for reporting and details of how related financial transaction should post to the General Ledger. The account will need a credit limit, and should be approved after a credit check has been performed. It is important to understand that the definition of a service such as this is far more than the formal service interface definition that is provided by WSDL, and can involve long running transactions and humans to make decisions.

Service repository

A service repository allows us to discover the services available in our SOA. You could argue that the list of services we get when we use http://localhost:7047/DynamicsNAV/ws/CRONUS_International_Ltd/Services URL is a service repository, but just as the formal interface definition from WSDL does not provide a service definition, this list of services does not really serve as a service repository. As well as the service definition for each of the available services, the service repository could include details of the service owner, access rights, a history of versions, performance, and scalability details. Krafzig et al, go on to say that although you can achieve many benefits of an SOA without a service repository, a repository is indispensable in the long term.

Service bus

In an SOA, the service bus glues the other components together. It allows the frontend (any frontend) to discover the services from the repository and call them. In addition, it can provide auditing, logging, and other technical functions such as message transformation and transaction handling.

Don't worry, be happy

There does not seem to be a universally accepted definition of SOA, and there are many interpretations of precisely what SOA means—so you may need to do some additional reading if you really need to understand it.

It seems that SOA has become one of those buzzwords that makes it on to everyone's wish list; even though they don't necessarily understand what it is, they still want it.

There are a good number of people that are saying that NAV 2009 has an SOA and this is not true. Often the people that are saying that NAV 2009 is SOA do not understand what SOA is but know that it will help them sell more product to people who want an SOA.

Just because NAV 2009 is not SOA, don't feel let down. The Web services enablement is fantastic, and if you are really involved in an SOA project it is going to make your life much easier—but you are still going to have to do a lot of work. Exposing a business process as a service requires a lot more thought than exposing a function as a Web service.

Any questions?

We've covered a lot on extending the application through Web services but there still remain a few unanswered questions:

  • What happens if I use a document type page as a Web service, how does the system expose the header and the lines?
  • Can I expose a Role Center page as a Web service?
  • Can my Codeunit Web services take complex data types (such as table records) as parameters?
  • What happens if I try to return a complex data type from a Codeunit?
  • I've heard that I can use an XMLPort as a parameter to a Codeunit to allow me to pass complex data types. How does this work?
  • Can I extend my page Web service to add other functions?
  • What happens to applications that consume my Web service if I add or remove fields from a page?

Let's answer each of these questions in turn.

Document Pages as Web services

I don't want to sound cynical, but I was surprised to find that this just works. Those guys at Microsoft have thought of everything! I added Page 50 (Purchase Order) to my Web services and the purchase order page was available for my .NET application as a web reference.

There were a number of classes that I could instantiate:

  • Purchase_Order
  • Purchase_Order_Filter
  • Purchase_Order_Line
  • And of course Purchase_Order_Service

The great thing is that we get the purchase order line as a strongly typed object, which means the page lets us work with the header and the lines on the page; but how do we access the lines of the purchase order?

One of the 'fields' on the purchase order is called PurchLines and this is actually an array of objects of type Purchase_Order_Line. This allows you to write code such as the following (don't try compiling this; it's not the full program, just enough to illustrate the use of PurchLines):

Purchase_Order myPO = new Purchase_Order();

myPO = PO_s.Find(PO_fList.ToArray(),null,1)[0];

Console.WriteLine("nnPurchase Order No. {0} : {1}", myPO.No, myPO.
Buy_from_Vendor_Name);
Console.WriteLine("nnHas {0} Purchase Lines...n", myPO.PurchLines.
GetLength(0));
foreach (Purchase_Order_Line POL in myPO.PurchLines) {
Console.WriteLine("{0}, {1}, {2}", POL.Type, POL.No, POL.
Description);
}

This will produce the following output:

Extending the Application using Microsoft Dynamics NAV 2009 (Part 2)

 

You can download the full source code for this simple Console application from our web site.

Role Center as a Web service

"Now I really, really, didn't think this one would work, and I was quite shocked when I was allowed to add Page 9001 (Account Manager Role Center) as a Web service and publish it. The new page even appeared in my list of available services, but alas I wasn't able to do anything with the suggested service URL for the page. It doesn't work, which isn't surprising because a Role Center page is whole bunch of containers that contain other pages and custom parts, some of which are quite complex. Since I can't think of a reason to do this (other than to write your own RoleTailored client) it's no big deal, but what about some of the pages that are on the Role Center, such as the Activities Part page, can they be used? Well sort of. You can create the Web service and even call some of the standard methods, but there are no fields on the Activities Part for you to read, so there's very little point. If you really want to write your own RoleTailored client (seriously why would you do that?), you should be looking at creating a Codeunit to give you the information you need to build your cues."

Records as parameters to Codeunits

Nope you can't do this. You can get as far as exposing the Codeunit but when you try to add it as a web reference, you get an unhandled exception for the type of record. The best thing to do is to write a function that takes a unique reference to the record that you want to work with. If you want to update records, you should use a Page type Web service. If you merely want to include a reference to the object, you should use the fields that you know to be in the primary key as parameters to the function. If you are going to use Codeunits to change records, you need to make sure you check that no one else has modified or even deleted the record since you read it from the Web service. When using Page type Web services, the system handles the concurrency for you, but with Codeunits you have full control (and therefore full responsibility).

Codeunit functions that return complex data types

This would mean creating a function that returned a complex data type, such as a customer record.

Err, you can't do this in C/AL, so there's no chance of doing this in a Web service. Let's just pretend this question is never asked and move on to the next one…

XMLports as parameters to Codeunits

By using an XMLport as a parameter to a Codeunit function, we can create services that can receive complex data types, such as records or documents as parameters. A good example would be if we wanted to create a purchase order in a single transaction. "I don't remember where I got this idea from, possibly it was suggested in one of the early Microsoft Whitepapers but initial tests on the released product indicate that this is possible."

If you don't want to create an XMLport, one way to get around this may be to use a Codeunit function parameter of type BigText that can be used to pass an XML document (up to 2GB in size) to the function by reference. The BigText can then be decoded using the XMLDOM automation control in NAV.

More functions on a Page

Page Web services are great and for creating, reading, updating, and deleting records (known as CRUD), and they're really simple to use. There are times when you need more from your page, such as releasing a document for approval, or posting a journal. If you take a look at the Microsoft Dynamics NAV 2009 Developer Help in the topic Walkthrough: Creating a Web Service using Extension Codeunits, you can see an example of how this is done.

Can I break it?

Of course you can, and really this isn't hard to do, so be careful when tinkering with Codeunits and pages that have been exposed as Web services. To test this rename the 'subform' control on our Purchase Order Page to be PurchLines2 from PurchLines and guess what? Our sample application that read the purchase order and printed the details of the lines stops working with an unhandled exception. Our recommendation is that whenever you expose a Codeunit or page from NAV as a Web service, you add a comment to the documentation trigger with the name of the service you have created. This will tell developers to be careful with their changes; renaming controls and removing fields can break things that are using the Web service and should be avoided.

You need to take care when executing code that initiates some kind of user interaction (such as FORM.RUNMODAL) during a Web service call. The call will fail with a strange error message: Callback functions are not allowed. You can avoid this error, by making sure that the user interaction code is within an IF statement that uses GUIALLOWED (see the section Handling UI Interaction When Working with Web Services in the online help for more details). Some areas of the application (such as the Sales Order Page) are relatively well shielded from this error through the use of GUIALLOWED, but you should still be aware of this issue when using NAV Web services, test the Web service calls thoroughly, and use the Code Coverage tool to search for the C/AL commands that are likely to cause problems.

It's also a good idea to keep a central repository of information about your services and for developers to document how they are using the services. This is similar to the idea of the Service Repository in the SOA model—it could be a series of SharePoint sites for the various services that allow the consumers of the Web service to detail what they are using the services for. This is going to help you when you are looking at upgrading and need to consider which systems need to be tested.

Summary

We've only scratched the surface of the possibilities that Web service enablement presents. It's expected there will be a plethora of samples on web sites and blog posts when NAV 2009 starts to take off and the NAV gurus decide to show off their skills. If you are non-technical, hopefully you found enough in this article to keep you interested, and you now have a better understanding of what NAV 2009 means in terms of extensibility. If you are a technical consultant and programmer, this article will provide a foundation for exploring the world of NAV 2009 and Web services, but there is plenty more for you to experiment with on your own.

Here're some more ideas you can try on your own:

  • Build a Codeunit that takes an XMLport as a parameter to allow a 'Create Purchase Order' service to be exposed as a Web service—try the same exercise using a BigText parameter.
  • Create an InfoPath form for creating new customers in NAV—for bonus points, you can deploy this to the Forms Server component of MOSS.
  • Download the Dynamics AX samples for Dynamics Snap from http://www.microsoft.com/dynamics/product/snap.mspx and make them work with NAV.
  • Create an ASP.NET or Silverlight application that will allow you to create, edit, and update customers in NAV

Enjoy!

Implementing Microsoft Dynamics NAV 2009 Explore the new features of Microsoft Dynamics NAV 2009, and implement the solution your business needs with this book and eBook
Published: January 2009
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:

About the Author :


David Roys

David Roys is a Microsoft Most Valuable Professional (MVP) for the Microsoft Dynamics NAV product. He has worked in the computer industry since 1992 and currently works in New Zealand for Intergen Ltd., a leading Microsoft Gold Partner and Dynamics Presidents Club member.

Since getting his honors degree in Computing Science from Staffordshire University, England, he has worked with a variety of custom-written and packaged financial solutions in a variety of roles. His first programming job provided experience of financial systems using CICS, COBOL, and JCL on an IBM 3090 series mainframe. From being a very junior developer in a large organization, he went on to be a oneman IT department at a small food manufacturing company in a role that allowed him to learn and develop solutions for a Danish ERP package called 'Concorde XAL'. David moved into the world of consulting and ERP reselling in 1996 where he enjoyed working with some truly brilliant people at Columbus IT Partner and was able to work on international projects in South Africa, Hungary, Poland, and Ireland. With many years of experience in XAL and Axapta he moved to New Zealand in 2002 to work as a consultant for Ernst & Young in their IT Consulting practice delivering financial solutions in Navision Attain. He now works in his dream job as a Dynamics NAV consultant and developer for Intergen Ltd., a bunch of fun-loving, incredibly smart people. David firmly believes that ERP systems are boring and is committed to bringing some entertainment to this dull and listless world.

Vjekoslav Babić

 

Vjekoslav Babić is a Microsoft Dynamics NAV expert, consultant, and architect with ten years experience in the IT industry and six years experience delivering project success on large-scale, high-risk, and international implementations of Microsoft Dynamics solutions. He has project experience in various industries, including telecommunications, insurance, pharmaceuticals, industrial gasses, chemicals, food and beverage, manufacturing, printing, distribution, and retail. He is a Project Management Institute certifi ed Project Management Professional, an accomplished Microsoft Certifi ed Trainer with a track record of successful trainings and presentations, a Microsoft Certifi ed Business Management Solutions Professional with several Microsoft Dynamics NAV and Microsoft Dynamics CRM specializations, and holds a number of Microsoft technical certifi cations. Vjekoslav has published more than forty articles on business solutions, software development, database design, and internet technologies; he is the author of the NAV Insights column and an Editorial Advisory Board member with MSDynamicsWorld.com. An active blogger, he frequently writes about Microsoft Dynamics implementation methodologies, Sure Step, and Project Management topics on his blog NavigateIntoSuccess.com.

Based in Zagreb, Croatia, he is employed as a consultant at Microsoft.

Contact Vjekoslav Babić through his blog http://NavigateIntoSuccess.com

Books From Packt

Programming Microsoft® Dynamics™ NAV
Programming Microsoft® Dynamics™ NAV

Quality Assurance for Dynamics AX-Based ERP Solutions
Quality Assurance for Dynamics AX-Based ERP Solutions

WCF Multi-tier Services Development with LINQ
WCF Multi-tier Services Development with LINQ

ASP.NET 3.5 Social Networking
ASP.NET 3.5 Social Networking

Software Testing with Visual Studio Team System 2008
Software Testing with Visual Studio Team System 2008

ASP.NET 3.5 Application Architecture and Design
ASP.NET 3.5 Application Architecture and Design

Active Directory Disaster Recovery
Active Directory Disaster Recovery

Entity Framework Tutorial
Entity Framework Tutorial

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
t
4
v
b
b
N
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software