September 2013 Entries
Dynamics NAV 2013 consuming a rest web service using DotNet DataTypes

In this example we will use Melissa Data to validate addresses (http://www.melissadata.com/) but you can modify the example to use any service.  For this example, the goal is to use the Melissa data rest based web service to verify a customer address in NAV and get the results back.  What is done with those results will be up to the developer. 

There is an assumption that you know enough about C/AL code and different object types to know where you are trying to put the code.

First thing to do is to setup the variables that will be used. 

Name DataType Subtype
xml DotNet System.Xml.XmlDocument.'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
xmlnodelist DotNet System.Xml.XmlNodeList.'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
i Integer  
xmlnode DotNet System.Xml.XmlNode.'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
results Text  
j Integer  

DotNet Datatypes are relatively new.  To pick the ones that you want to use, see the instructions below.  These instructions show what it looks like after all options have been chosen.  As you walk through it the first time, many of the fields will be blank until the previous option has been clicked.

Here is how you would setup the first item in our variable list above.  This is the variable we called xml and the subtype is shown as System.Xml.XmlDocument.'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.  When you break that up, what we are looking for is the System.XML 2.0.0.0 Assembly and the XmlDocument Type within that assembly.

image

  1. 1.  From the C/AL locals or globals screen, add a new item.  Give it a variable name and choose DotNet under DataType.  At this point, the Subtype will be blank.  Click the ellipse in the Subtype feild to assign the appropriate .net data type (the ellipse is shown under the 1 in the image above).
  2. 2.  The .NET Type List will appear.  Click the up arrow in the Assembly field.
  3. 3.  This will open the Assembly List window.  Chose the .NET tab.
  4. 4.  In our case, find System.Xml.  Here, we chose to use the 2.0.0.0 version. Click OK.  Note: You are welcome to experiment with the 4.0.0.0 version or any other .NET types that may do what you want.
  5. 5.  After choosing OK on the Assembly that you want, the list of Types for that Assembly will appear in the  .NET Type List.  Find the System.Xml.XmlDocument and click OK.

 

Follow the same steps for xmlnodelist.  In that case, it shows System.Xml.XmlNodeList.'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' so you will want the System.XML Assembly 2.0.0.0 then the XmlNodeList Type.  Use the same type for another variable called xmlnode.

First setup the XML Document and call Melisa data with the correct URL to get the results.


  xml := xml.XmlDocument();
  Customer.GET("Customer No.");
  xml.Load('https://addresscheck.melissadata.net/v2/REST/Service.svc/doAddressCheck?id='+MelisaDataCustomerId+'&opt=true&comp=&u=&a1='+Customer.Address+'&a2='+Customer."Address 2"+'&ste=&pmb=&city='+Customer.City+'&state='
    +Customer.County+'&zip='+Customer."Post Code"+'&p4=&ctry=&last=');

After that, the idea is to read the xml and parse it to get the values out.


xmlnodelist := xml.SelectNodes('//*');
IF( xmlnodelist.ToString()<>'') THEN
BEGIN
j:=xmlnodelist.Count; 

FOR i := 0 TO xmlnodelist.Count - 1 DO
BEGIN
  xmlnode := xmlnodelist.Item(i);
  CASE xmlnode.Name OF
    'Value': [Field or variable to store value]:= xmlnode.FirstChild.InnerText;
  // add more cases
  End
End

 

The basic code for this example looks like this:



 xml := xml.XmlDocument();
  Customer.GET("Customer No.");
  xml.Load('https://addresscheck.melissadata.net/v2/REST/Service.svc/doAddressCheck?id='+MelisaDataCustomerId+'&opt=true&comp=&u=&a1='+Customer.Address+'&a2='+Customer."Address 2"+'&ste=&pmb=&city='+Customer.City+'&state='
    +Customer.County+'&zip='+Customer."Post Code"+'&p4=&ctry=&last=');

xmlnodelist := xml.SelectNodes('//*');
IF( xmlnodelist.ToString()<>'') THEN
BEGIN
j:=xmlnodelist.Count; 

FOR i := 0 TO xmlnodelist.Count - 1 DO
BEGIN
 
  xmlnode := xmlnodelist.Item(i);
  CASE xmlnode.Name OF
    'Urbanization': "Urbanization Code":= xmlnode.FirstChild.InnerText;
    'Address1': "Address 1":= xmlnode.InnerText;
    'Address2': "Address 2":= xmlnode.InnerText;
    'Suite': IF xmlnode.ParentNode.Name = 'Address' THEN "Suite Name":= xmlnode.InnerText;
    'PrivateMailBox': "PMB Name":= xmlnode.InnerText;
    'City': 
      BEGIN
        City:= xmlnode.FirstChild.InnerText;
      END;
    'State':
      BEGIN
        State:=xmlnode.LastChild.InnerText;
      END;
    'Zip':
      IF xmlnode.ParentNode.Name = 'Address' THEN
           Zip:=xmlnode.InnerText;
    'Plus4': "Plus 4":=  xmlnode.InnerText;
    'Country':
      BEGIN
        Country:= xmlnode.FirstChild.InnerText;
        //MESSAGE('Set country name to: ' + xmlnode.LastChild.InnerText);
      END;
    'CarrierRoute': "Carrier Route":= xmlnode.InnerText;
    'DeliveryPointCode': "Delivery Point Code":= xmlnode.InnerText;
    'DeliveryPointCheckDigit': "Delivery Point Code Digit":= xmlnode.InnerText;
    'CongressionalDistrict': "Congressional District":= xmlnode.InnerText;
    'AddressKey':"Address Key":= xmlnode.InnerText;
    'Results':
      BEGIN
        IF xmlnode.ParentNode.Name = 'Record' THEN
          BEGIN
            results := '';
            IF STRPOS(xmlnode.InnerText, 'AE01') > 0 THEN results := results + 'Zip Code Error; ';
            IF STRPOS(xmlnode.InnerText, 'AE02') > 0 THEN results := results + 'Unknown Street; ';
            IF STRPOS(xmlnode.InnerText, 'AE03') > 0 THEN results := results + 'Component Error; ';
            IF STRPOS(xmlnode.InnerText, 'AE04') > 0 THEN results := results + 'Non-Deliverable Address; ';
            IF STRPOS(xmlnode.InnerText, 'AE05') > 0 THEN results := results + 'Address Matched to Multiple Records; ';
            IF STRPOS(xmlnode.InnerText, 'AE06') > 0 THEN results := results + 'Address Matched to Early Warning System; ';
            IF STRPOS(xmlnode.InnerText, 'AE07') > 0 THEN results := results + 'Empty Address Input; ';
            IF STRPOS(xmlnode.InnerText, 'AE08') > 0 THEN results := results + 'Suite Range Error; ';
            IF STRPOS(xmlnode.InnerText, 'AE09') > 0 THEN results := results + 'Suite Range Missing; ';
            IF STRPOS(xmlnode.InnerText, 'AE10') > 0 THEN results := results + 'Primary Range Error; ';
            IF STRPOS(xmlnode.InnerText, 'AE11') > 0 THEN results := results + 'Primary Range Missing; ';
            IF STRPOS(xmlnode.InnerText, 'AE12') > 0 THEN results := results + 'Box Number Error from PO Box or RR; ';
            IF STRPOS(xmlnode.InnerText, 'AE13') > 0 THEN results := results + 'Box Number Missing from PO Box or RR; ';
            IF STRPOS(xmlnode.InnerText, 'AE14') > 0 THEN results := results + 'Input Address Matched to CMRA but secondary number not present; '; 

            IF STRPOS(xmlnode.InnerText, 'AS01') > 0 THEN results := results + 'Address matched USPS; ';
            IF STRPOS(xmlnode.InnerText, 'AS02') > 0 THEN results := results + 'Address matched to non-postal database; ';
            IF STRPOS(xmlnode.InnerText, 'AS09') > 0 THEN results := results + 'Foreign Postal Code Detected; ';
            IF STRPOS(xmlnode.InnerText, 'AS11') > 0 THEN results := results + 'Address matched to CMRA; ';
            IF STRPOS(xmlnode.InnerText, 'AS12') > 0 THEN results := results + 'Address deliverable by non-USPS; ';
            IF STRPOS(xmlnode.InnerText, 'AS13') > 0 THEN results := results + 'Address Updated By LACS; ';
            IF STRPOS(xmlnode.InnerText, 'AS14') > 0 THEN results := results + 'Suite Appended by Suite Link; ';
            IF STRPOS(xmlnode.InnerText, 'AS15') > 0 THEN results := results + 'Suite Appended by Suite Finder; '; 

            IF STRPOS(xmlnode.InnerText, 'SE01') > 0 THEN results := results + 'Web Service Internal Error; ';
            IF STRPOS(xmlnode.InnerText, 'GE01') > 0 THEN results := results + 'Empty XML Request Structure; ';
            IF STRPOS(xmlnode.InnerText, 'GE02') > 0 THEN results := results + 'Empty XML Request Record Structure; ';
            IF STRPOS(xmlnode.InnerText, 'GE03') > 0 THEN results := results + 'Counted records send more than number of records allowed per request; ';
            IF STRPOS(xmlnode.InnerText, 'GE04') > 0 THEN results := results + 'MelissaData Customer Number is Invalid; ';
            IF STRPOS(xmlnode.InnerText, 'GE05') > 0 THEN results := results + 'MelissaData Customer Number is Invalid; ';
            IF STRPOS(xmlnode.InnerText, 'GE06') > 0 THEN results := results + 'MelissaData Customer ID is Disabed; ';
            IF STRPOS(xmlnode.InnerText, 'GE07') > 0 THEN results := results + 'XML Request invalid; '; 

            IF STRPOS(xmlnode.InnerText, 'AC01') > 0 THEN results := results + 'Unknown (AC01); ';
            IF STRPOS(xmlnode.InnerText, 'AC02') > 0 THEN results := results + 'Unknown (AC02); ';
            IF STRPOS(xmlnode.InnerText, 'AC03') > 0 THEN results := results + 'Unknown (AC03); ';
            IF STRPOS(xmlnode.InnerText, 'AC04') > 0 THEN results := results + 'Unknown (AC04); ';
            IF STRPOS(xmlnode.InnerText, 'AC05') > 0 THEN results := results + 'Unknown (AC05); ';
            IF STRPOS(xmlnode.InnerText, 'AC06') > 0 THEN results := results + 'Unknown (AC06); ';
            IF STRPOS(xmlnode.InnerText, 'AC07') > 0 THEN results := results + 'Unknown (AC07); ';
            IF STRPOS(xmlnode.InnerText, 'AC08') > 0 THEN results := results + 'Unknown (AC08); ';
            IF STRPOS(xmlnode.InnerText, 'AC09') > 0 THEN results := results + 'Unknown (AC09); ';
            IF STRPOS(xmlnode.InnerText, 'AC10') > 0 THEN results := results + 'Unknown (AC10); ';
            IF STRPOS(xmlnode.InnerText, 'AC11') > 0 THEN results := results + 'Unknown (AC11); '; 

            IF xmlnode.InnerText = 'AS01,AS12' THEN results := 'Input Address Matched to DPV; '; 

             "Result Codes" :=  xmlnode.InnerText;
             "Result Text" := FORMAT( results,250);
          END
      END
  END;
END;

 

The MelisaDataCustomerId is just a key that Melisa data assigns to allow use of the service.  The code gets more complicated because we have to parse multiple results but that is not the point of this post and may come in a later post.

Styling Dynamics NAV 2013 Page Values To Bring Attention To Them

 

When editing pages in NAV 2013, you can set styles to draw attention to values.  This is done by using the property value for style on a field and setting it to a pre-defined value.  Note:  The styling does not apply in the Web Client.

Style shown in the property list:

Style shown in the property list

 

Here is examples of how values will appear with the style applied on the page from NAV 2013 base installed on Windows 8.

Style Result Example
Standard no style image
StandardAccent Blue image
Strong Bold image
StrongAccent Bold Blue image
Attention Italic  Red image
AttentionAccent Italic Blue image
Favorable Green image
Unfavorable Bold Italic Red image
Ambiguous Yellow image
Subordinate Grey image
     
Reporting with Dynamics NAV 2013

 

This is a basic description of report writing for Dynamics NAV 2013.  It does not get into any advanced functions or the ability to use C/AL code. 

Use the NAV CSIDE environment to either choose a report or click design or new report

For reporting, everything starts at the Report Dataset Designer:

image

The important key areas to understand are the Label Designer, Request Options Page Designer, and the Visual Studio 2010 Report Designer. Knowing these base areas will allow you to explore current reports to learn how base functions were done and use your favorite NAV site or search engine to learn more.

From the dataset designer, click view and then request page to bring up the request page. The request page is used to show user options for filtering or making decisions before the report is run.

image

Also from the dataset designer, you can press view labels to open the Label designer. The advantage to using the label designer instead of putting plain text in your report on labels is multi language support or the ability to dynamically change labels in code behind.

image

Everything up to now has been the backend setup for the report.  The UI is controlled in Visual Studio 2010.  From the Dataset Designer, choose view and Layout.  If properly setup, visual studio will open.

clip_image008

Totals are controlled in the footers of groups.

clip_image010

Using understandable names to every control is essential to trouble shooting later and succession planning. Do not be afraid to use descriptive words and avoid abbreviating.

Right click on the upper right hand corner of the table that was just added and choose properties.  This will allow you to set the dataset to the table for displaying.

clip_image012

clip_image014

clip_image016

Expressions are available for most properties and can be used to set values. A couple of useful example are to use expressions to change colors to show extreme values or show and hide a row based on a value in it. 

clip_image018