Extend CU 6224: Read xml node values and attributes

Xml documents can be created/processed via XmlPorts. An other opportunity is the usage of Codeunit 6224. It’s used for complex xml handling. This CU only delivers some create and find functions, but no read/get functions. Following functions extend CU 6224 with read functions.

First a sample for the usage of the new functions. This sample shows, how to load a xml document given as string and read some of the node attributes.

// local variables// XmlMgmtCodeunitXML DOM Management// xmlDocAutomation'Microsoft XML, v3.0'.DOMDocument60// rootNodeAutomation'Microsoft XML, v3.0'.IXMLDOMNode// childNodeAutomation'Microsoft XML, v3.0'.IXMLDOMNode// addressText30// prioInteger// createdAtDateCREATE(xmlDoc);xmlDoc.loadXML('<mail priority="1" createdAt="26/5/2014"><from address="from@test.com"/>' +'<to name="to@test.com"/><state value="1"/></mail>');rootNode := xmlDoc.selectSingleNode('mail');prio := XmlMgmt.GetAttributeValueAsInt(rootNode,'priority');createdAt := XmlMgmt.GetAttributeValueAsDate(rootNode,'createdAt');childNode := rootNode.firstChild;address := XmlMgmt.GetAttributeValueAsText(childNode,'address');MESSAGE('mail: ' + FORMAT(prio) + ',' + FORMAT(createdAt) + ',' + address);CLEAR(rootNode);CLEAR(childNode);CLEAR(xmlDoc);

The new functions:

// Read an attribute value from a given node, e.g. from <data att1="1"/> gets value 1GetAttributeValueAsText(xmlNodePar : Automation "'Microsoft XML, v3.0'.IXMLDOMNode";attributeNamePar : Text[100]) : Text// attributeNodeLoc | Automation | 'Microsoft XML, v3.0'.IXMLDOMNodeSetNormalCase;IF ISCLEAR(xmlNodePar) THEN  EXIT('Param. xmlNodePar must not be null');IF attributeNamePar = '' THEN  EXIT('Param. attributeNamePar must not be empty');attributeNodeLoc := xmlNodePar.attributes.getNamedItem(attributeNamePar);IF ISCLEAR(attributeNodeLoc) THEN  EXIT('Attribute ' + attributeNamePar + ' not found');IF STRLEN(attributeNodeLoc.text) > 0 THEN  EXIT(attributeNodeLoc.text);EXIT('');GetAttributeValueAsInt(xmlNodePar : Automation "'Microsoft XML, v3.0'.IXMLDOMNode";attributeNamePar : Text[100]) : Integer// attrValLoc | Text// returnValueLoc | IntegerattrValLoc := GetAttributeValueAsText(xmlNodePar,attributeNamePar);IF attrValLoc <> '' THEN  IF NOT EVALUATE(returnValueLoc, attrValLoc) THENEXIT(-1);EXIT(returnValueLoc);GetAttributeValueAsDec(xmlNodePar : Automation "'Microsoft XML, v3.0'.IXMLDOMNode";attributeNamePar : Text[100]) : Decimal// attrValLoc | Text// returnValueLoc | DecimalattrValLoc := GetAttributeValueAsText(xmlNodePar,attributeNamePar);IF attrValLoc <> '' THEN  IF NOT EVALUATE(returnValueLoc, attrValLoc) THENEXIT(-1);EXIT(returnValueLoc);GetAttributeValueAsDate(xmlNodePar : Automation "'Microsoft XML, v3.0'.IXMLDOMNode";attributeNamePar : Text[100]) : Date// attrValLoc | Text// returnValueLoc | DateattrValLoc := GetAttributeValueAsText(xmlNodePar,attributeNamePar);returnValueLoc := 0D;IF attrValLoc <> '' THEN  IF NOT EVALUATE(returnValueLoc, attrValLoc) THEN;EXIT(returnValueLoc);GetAttributeValueAsBool(xmlNodePar : Automation "'Microsoft XML, v3.0'.IXMLDOMNode";attributeNamePar : Text[100]) : Boolean// attrValLoc | Text// returnValueLoc | BooleanattrValLoc := GetAttributeValueAsText(xmlNodePar,attributeNamePar);returnValueLoc := FALSE;IF attrValLoc <> '' THEN BEGIN  IF NOT EVALUATE(returnValueLoc, attrValLoc) THENERROR('No bool value given');  EXIT(FALSE);END;EXIT(returnValueLoc);// Following is needed to read a node value, e.g. from <data>1</data> gets value 1GetNodeValueAsText(xmlNodePar : Automation "'Microsoft XML, v3.0'.IXMLDOMNode") : Text[1024]IF ISCLEAR(xmlNodePar) THEN  EXIT('Param. xmlNode must not be null');IF STRLEN(xmlNodePar.text) > 0 THEN  EXIT(xmlNodePar.text);EXIT('');// Return a node value as Integer valueGetNodeValueAsInt(xmlNodePar : Automation "'Microsoft XML, v3.0'.IXMLDOMNode") : Integer// attrValLoc | Text// returnValueLoc | IntegerxmlNodeValueLoc := GetNodeValueAsText(xmlNodePar);returnValueLoc := 0;IF xmlNodeValueLoc <> '' THEN  IF NOT EVALUATE(returnValueLoc, xmlNodeValueLoc) THENERROR('No int value given');EXIT(returnValueLoc);

Instead of Xml vs. 3 you can also use Xml vs. 6, e.g. xmlNode : ‘Microsoft XML, v6.0’.IXMLDOMNode.

Convert date string containing a month text value to a date value

Assuming you want to enter date values in the RTC client like 15-MAR-14. that won’t work. date values are only accepted with syntax like 15.03, 15.03.14, etc. But it can be that users prefer the above syntax. so then you can use a text field in your page and convert internally the value into a date value. Following function converts that kind of date string to a date value.

// local variables// dateString | Text// dateValue  | DatedateString := '15-MAR-14';dateValue := ConvertDateString(dateString);MESSAGE(FORMAT(dateValue,0,'<day,2>.<month,2>.<year4>'));ConvertDateString(dateString : Text[9]) : Date// local variables// dateString | Text// dayValue   | Integer// months | Text// monthValue | Integer// yearValue  | Integer// dateValue  | Date// mPos   | Integer// monthText  | Textif strpos(dateString,'-') > 0 then begin  dateString := CONVERTSTR(dateString,'-',',');  EVALUATE(dayValue,SELECTSTR(1,dateString));  months := 'jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec';  monthText := lowercase(SELECTSTR(2,dateString));  if strlen(monthText) <> 3 thenerror('Invalid month expression: ' + monthText);  mPos := STRPOS(months,monthText);  if mPos >0 then beginmonthValue := (mPos + 3) / 4;EVALUATE(yearValue,SELECTSTR(3,dateString));IF yearValue > 14 THEN  yearValue := yearValue + 1900ELSE  yearValue := yearValue + 2000;dateValue := DMY2DATE(dayValue,monthValue,yearValue);exit(dateValue);  end elseerror('No valid month given: ' + monthText);end;exit(0D);

to use the standard functionality of date validation and conversion it’s needed to change function MakeDateText in codeunit 1:

MakeDateText(VAR DateText : Text[250]) : Integer// additional local variables// mPos | Integer// monthText | Text | 10// monthValue | Integer// Text Constant: Text022 | jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,decPosition := 1;Length := STRLEN(DateText);ReadCharacter(' ',DateText,Position,Length); // begin changesIF STRPOS(DateText,'-') > 0 THEN BEGIN  DateText := CONVERTSTR(DateText,'-',',');  monthText := lowercase(SELECTSTR(2,DateText));  if strlen(monthText) <> 3 thenerror('Invalid month expression: ' + monthText);  mPos := STRPOS(Text022,monthText);  IF (mPos > 0) THEN BEGINmonthValue := (mPos + 3) / 4;// optional: add a leading 0, if needed; simple use format(monthValue) should also workmonthText := PADSTR('',2 - STRLEN(FORMAT(monthValue)),'0') + FORMAT(monthValue);DateText := SELECTSTR(1,DateText) + '-' + monthText + '-' + SELECTSTR(3,DateText);  END ELSEerror('No valid month given: ' + monthText);END;// end changes IF NOT FindText(PartOfText,DateText,Position,Length) THEN...

cheers

You cannot create an Automation object ‘name of autom.’ on Microsoft Dynamics NAV Server. You must create it on a client computer.

This is an error message, when trying to create an instance of an automation on a nav server vs. >= 2013. It’s only possible to create the automation instance as client instance. Creating automation instances on nav server is allowed till nav 2009.

solution:
use create(<name_of_autom.>,false,true) instead of create(<name_of_autom.>).

remarks:
you need a installed version of the automation on each client computer.

links:
create fct.: http://msdn.microsoft.com/en-us/library/dd355255(v=nav.70).aspx
Usage of automations in nav 2013: http://msdn.microsoft.com/en-us/library/ee909565(v=nav.70).aspx
Usage of automations in nav 2009: http://msdn.microsoft.com/en-us/library/ee909565(v=nav.60).aspx

Numbers to Words

This code converts given numbers to words.

For usage run (sample):

InitTextVariables;numText := NumberToWords(1256,'');  // numText | Text | 100Message(numText);
NumberToWords(number : Integer;appendScale : Text) : TextnumString := '';IF number < 100 THEN  IF number < 20 THENnumString := OnesText[number]  ELSE BEGINnumString := TensText[number DIV 10];IF (number MOD 10) > 0 THEN  numString := numString + ' ' + OnesText[number MOD 10];  ENDELSE BEGIN  pow := 0;  powStr := '';  IF number < 1000 THEN BEGIN // number is between 100 and 1000pow := 100;powStr := ThousText[1];  END ELSE BEGIN // find the scale of the numberlog := number DIV 1000;pow := POWER(1000, log);powStr := ThousText[log + 1];  END;  numString := NumberToWords(number DIV pow, powStr) + ' ' + NumberToWords(number MOD pow,'');END;EXIT(numString + ' ' + appendScale);// local variables// numStringText// powInteger// powStrText// logIntegerInitTextVariables()OnesText[1] := 'one';OnesText[2] := 'two';OnesText[3] := 'three';OnesText[4] := 'four';OnesText[5] := 'five';OnesText[6] := 'six';OnesText[7] := 'seven';OnesText[8] := 'eight';OnesText[9] := 'nine';OnesText[10] := 'ten';OnesText[11] := 'eleven';OnesText[12] := 'twelve';OnesText[13] := 'thirteen';OnesText[14] := 'fourteen';OnesText[15] := 'fifteen';OnesText[16] := 'sixteen';OnesText[17] := 'seventeen';OnesText[18] := 'eighteen';OnesText[19] := 'nineteen';TensText[1] := '';TensText[2] := 'twenty';TensText[3] := 'thirty';TensText[4] := 'forty';TensText[5] := 'fivty';TensText[6] := 'sixty';TensText[7] := 'seventy';TensText[8] := 'eighty';TensText[9] := 'ninty';ThousText[1] := 'hundred';ThousText[2] := 'thousand';ThousText[3] := 'million';ThousText[4] := 'billion';ThousText[5] := 'trillion';// global variables// OnesTextText30 | Dim:20// TensTextText30 | Dim:10// ThousTextText30 | Dim:5

Date Functions

Date functions are quite often used and needed. Here are some samples:

  1. Calculate the 1st of current month:
    EndDate := CALCDATE('-CM');
  2. Calculate last day of current month:
    EndDate := CALCDATE('CM');
  3. Calculate the end date of a defined period (e.g.3 months) starting with the 1st of the current month; get the last day of the end month:
    EndDate := CALCDATE('-CM+3M-1D');EndDate := CALCDATE('CM+2M');
  4. Get start and end date of a period with given day values and variable month value:
    startDay, endDay, month, prevMonth, currYear | integer valuesmonth := 10  // sample valueif month > 1 then prevMonth := month - 1 else prevMonth := 12;currYear := DATE2DMY(TODAY,3);  // get current yearstartDate := DMY2DATE(startDay, month - 1, currYear);endDate := DMY2DATE(endDay, month, currYear);

Be aware that the calc formulars are language specific. For german language e.g. use LM instead of CM, 1T instead of 1D.