AutoFormatType and Role Tailored Layout

If you have worked on a Role Tailored Layout and wanted to show a report without decimals you have most likely seen that the AutoFormatType option that we have in the Dynamics NAV 2009 R2 Classic Client does not work in the Role Tailored Layout

If you study the dataset that the Role Tailored Client delivers to the report the numbering format is not related to the AutoFormatType.  Why Microsoft chose to ignore this in this release I will never know.

In Iceland we do not use decimals in currency amounts.  However, when printing currency amount they are supposed to show decimals.  So I created my own AutoFormat solution.

The first step is to create a codeunit that uses the same logic as the AutoFormatType and delivers the Role Tailored Layout number format.

[code]OBJECT Codeunit 50012 RTC Decimal Format
{
OBJECT-PROPERTIES
{
Date=29.11.12;
Time=09:47:37;
Version List=Dynamics.is;
}
PROPERTIES
{
OnRun=BEGIN
END;

}
CODE
{
VAR
GLSetup@1100408003 : Record 98;
Currency@1100408004 : Record 4;
SetupRead@1100408000 : Boolean;

PROCEDURE AutoFormatLayout@12(AutoFormatType@1000 : Integer;AutoFormatExpr@1001 : Text[80]) : Text[80];
VAR
AmountDecimalPlaces@1100408000 : Text[30];
BEGIN
IF AutoFormatType = 0 THEN
EXIT(”);

IF NOT GetSetup THEN
EXIT(”);

CASE AutoFormatType OF
1: // Amount
IF AutoFormatExpr = ” THEN
EXIT(CreateNumberFormat(GLSetup."Amount Decimal Places"))
ELSE BEGIN
IF GetCurrency(COPYSTR(AutoFormatExpr,1,10)) AND
(Currency."Amount Decimal Places" <> ”)
THEN
EXIT(CreateNumberFormat(Currency."Amount Decimal Places"))
ELSE
EXIT(CreateNumberFormat(GLSetup."Amount Decimal Places"));
END;

2: // Unit Amount
IF AutoFormatExpr = ” THEN
EXIT(CreateNumberFormat(GLSetup."Unit-Amount Decimal Places"))
ELSE BEGIN
IF GetCurrency(COPYSTR(AutoFormatExpr,1,10)) AND
(Currency."Unit-Amount Decimal Places" <> ”)
THEN
EXIT(CreateNumberFormat(Currency."Unit-Amount Decimal Places"))
ELSE
EXIT(CreateNumberFormat(GLSetup."Unit-Amount Decimal Places"));
END;
10: EXIT(”);
END;
END;

LOCAL PROCEDURE CreateNumberFormat@1100408000(AmountDecimalPlaces@1100408000 : Text[30]) RTCFormat : Text[30];
VAR
Pos@1100408001 : Integer;
DecimalPlaces@1100408002 : Integer;
BEGIN
Pos := STRPOS(AmountDecimalPlaces,’:’);
IF Pos > 0 THEN
AmountDecimalPlaces := COPYSTR(AmountDecimalPlaces,Pos + 1);

IF NOT EVALUATE(DecimalPlaces,AmountDecimalPlaces) THEN
DecimalPlaces := 0;

RTCFormat := ‘#,##0’;
IF DecimalPlaces > 0 THEN
RTCFormat := RTCFormat + ‘.’ + PADSTR(”,DecimalPlaces,’0′);
END;

LOCAL PROCEDURE GetSetup@10015() : Boolean;
BEGIN
IF SetupRead THEN EXIT(TRUE);

SetupRead := GLSetup.GET;

EXIT(SetupRead);
END;

LOCAL PROCEDURE GetCurrency@1(CurrencyCode@1000 : Code[10]) : Boolean;
BEGIN
IF CurrencyCode = Currency.Code THEN
EXIT(TRUE);
IF CurrencyCode = ” THEN BEGIN
CLEAR(Currency);
Currency.InitRoundingPrecision;
EXIT(TRUE);
END;
EXIT(Currency.GET(CurrencyCode));
END;

BEGIN
END.
}
}

[/code]

Then I implement this on the Classic Section, example in Report 105.  First I add my codeunit to Globals.

Then I insert two hidden fields in the Sections.

The first field for the LCY format

and the second field for the current currency format

This adds the fields CurrencyAutoFormat and LCYAutoFormat to my dataset.

The final step is to replace the default Format property in the Role Tailored Layout.  I look at the field CustBalanceDue_1_ and see that this field can in some cases show currency amount and sometimes it shows LCY amounts.  This is determined by the PrintAmountsInLCY value.  I then set the Format property for this field to “=iif(Fields!PrintAmountsInLCY.Value,Fields!LCYAutoFormat.Value,Fields!CurrencyAutoFormat.Value)”

Next I look at the sum for this field.  This sum is always shown in the local currency.  I then set the Format property for this field to “=First(Fields!LCYAutoFormat.Value)”.

This way I have implemented the AutoFormatType functionality to the Role Tailored Layout.

RTCAutoformat Codeunit

Using a .dll proxy for web services

I have now completed my first all-dotnet codeunit.  The codeunit uses a dll file that I created from the web service WDSL.  This makes the programming a lot easier.

This solution has a BLOB fields that stores both incoming and outgoing xml.  When I use a proxy dll I don’t build a xml document and I never handle xml documents.  Again dotnet has a solution.  I use xml serializer from the system.xml object.

[code]LOCAL PROCEDURE SerializeToXMLStream@1100408008(VAR Object@1100408002 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Object" RUNONCLIENT;VAR NavOutstr@1100408004 : OutStream);
VAR
xmlSerializer@1100408000 : DotNet "’System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Xml.Serialization.XmlSerializer" RUNONCLIENT;
StreamWriter@1100408003 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.StreamWriter" RUNONCLIENT;
File@1100408001 : DotNet "’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.File" RUNONCLIENT;
NavInstr@1100408005 : InStream;
BEGIN
IF ISNULL(Object) THEN EXIT;
StreamWriter := StreamWriter.StreamWriter(TempFileName);
xmlSerializer := xmlSerializer.XmlSerializer(Object.GetType);
xmlSerializer.Serialize(StreamWriter,Object);
StreamWriter.Close;
UPLOADINTOSTREAM(”,MagicPath,”,TempFileName,NavInstr);
COPYSTREAM(NavOutstr,NavInstr);
File.Delete(TempFileName);
END;[/code]

I can send the proxy object to this function and will get the xml into the BLOB field.

[code]CurrencyRates := StatementService.GetCurrencyRates(TypeRates,CREATEDATETIME(RatesDate,235959T));

WITH BankAccAction DO BEGIN
"Modified by User ID" := USERID;
"Modification Date and Time" := CURRENTDATETIME;
"Incoming Message".CREATEOUTSTREAM(OutStr);
SerializeToXMLStream(CurrencyRates,OutStr);
MODIFY;
COMMIT;
END;[/code]

This example is executed on the client since I am using certificates and web services security for the communication.

The Kill Idle batch

In an earlier post I showed how to kill idle sessions from C/AL code.  I have been asked to provide a batch that works with Job Queue.

To set this up please add a field to table 91.  Field name is “Kill Idle Duration” and the data type is “Duration”.  In my case this field has the number 50000.  If that number is unavailable just use another number and import the text object and compile.

Attached is the report that can be used in the job queue.  Just make sure that the user running the job queue has enough permissions as shown in my earlier post.

If this is of any value to you please check my copyright page and donate to the web.

Report 89209

Need NAS to communicate with a web service ?

My recent post on creating a class in visual studio to simplify the NAV C/AL code for web service is a easy solution for NAV 2013.  It will work both for the user and for the Job Queue.  In NAV 2009 it will also work for the user running the Role Tailored Client but it will not work in the Job Queue.

There is a solution.  Use the VB.NET NAV Application Server.  This application server is able to execute any codeunit in NAV with dotnet support. 

The license required to start this application server is also cheaper than the classic NAS license.

Record Links in NAV 2009 R2

I made a function that collects all the record links from the documents related to a customer ledger entry or a vendor ledger entry.  I then display the list or record links and offer the user the change to open the links or email them.

This all worked just fine in my Classic Client but in the Role Tailored Client the page gives me a metadata error.  I asked Microsoft and got an answer from Lars Lohndorf-Larsen.

I have been in contact with development and they say that this is unfortunately a restriction in NAV2009. It is not possible to use “Record Link” as Source Table on a page.
“Record Link” is a special table which gets (re)built dynamically. A page expects a normal table and will implicitly compile the underlying table. But this particular table does not have usual metadata that the page expects, so it fails.
In NAV2013 it works because this table was redesigned.

This means that I will have to use the Record Link table as a global variable instead of using it as a source table.  That is what I will do.