How to: Manage a mobile app project

When a custom mobile client for Dynamics Nav is needed, there are a couple of ways to implement that. A couple of questions need to be answered, major decisions have to be made, especially, if you do want use the app not only for yourself, but you want to provide the app in online stores, provide it your customers.

Using ready-to-use solutions:

  • Anveo: Full mobile client for Dynamics Nav especially for Sales People. Provides offline working.
  • Mobile Nav: Ready-to-use mobile client, customizable, extensible, can be used online and offline (!), Secure comunication, barcode scanning, direct printing from device, multilingual; available for Android, IOS, Windows; usable for Nav 2009 R2 – Nav 2016
  • Sana Mobile: similar features as ‘Mobile Nav’, but only available for IOS (Apple devices: IPhone and in the near future also available for IPads). The app is optimized for sales agents.
  • Dynamics Mobile: similar features as Mobile Nav

Starting from scratch:

This is maybe the more expensive and more time consuming variant, but you can develope a full custom solution. For the mobile app you need an Sdk according the OS (Android, IOS, Windows). At the end it could be that you have to develope the mobile client 3 times. You have also to decide, if you want the get an online only app or if it also usable offline. Alternatively you could develope a web solution, which runs in the device browser.

The mobile app is then connected to the Dynamics Nav system with Nav web services. Nav web services are nav objects like codeunits and pages, which are so called published as .net web services, they are based on the SOAP protocol. For details goto here. If you go that way and want to use nav web services, i recommend NOT to use standard nav objects, especially not to use pages, because that is unsecure and you publish much more functionality than you want or need. Nav web services based on SAP are available since Nav 2009. Since Nav 2013 ODATA is provided, a new web service technology, quite faster, but with that Nav version only for read operations. Since Nav 2015 ODATA is usable for read AND write operations.

Prepare you project:

Start with a market study:

  • What are the existing solutions?
  • Are they suitable for you, are they customizable?
  • Are they usable online and offline, if needed ?
  • Which platforms are supported (Android, IOS, Windows), which do you need/want ?

Features:

  • What features, functions do you really want/need in your app? The few the better !!
  • Only provide the really needed functions (cheaper, faster): i suggest start for the first version with item list, customer list, vendor list, sales orders, purchase orders; additional the possibility to view, maybe edit the entries.

Users:

  • Which users do you target: “normal” end users, sales persons, CEOs? Different types of users may need different functions, different interfaces.
  • Ask some of your customers what they would expect from your mobile app

Interface:

  • The Gui, the interface of the app is the main thing. Always start with the gui; it should be cool, easy to use, should make the WOW effect. You’ll need a graphic designer for the gui.
  • Create the custom codeunits with the needed functions AFTER the gui, not vice versa, because that won’t work, will end in a crashed project.

so have fun with your mobile app project.

cheers

 

Where-Used-Tools for Dynamics Nav

Dynamics Nav does not ship with a Where-Used-Tool, although it is often needed. A Where-Used Tool is a tool, with which you can check, which nav objects use/reference a certain nav object or a certain table field.

Idyn has developed a widly used Toolset, the Object Manager Advanced (OMA). It is very helpful, contains that kind of tool, also a version/source control, rename tool, a.s.o. but it has it’s prize. 😦

Other Where-Used/Search tools:

  • Statical Prism“: free community version available, cheapest full version you can get for $37.
  • GDT Where Used“: This small free tool lists all Nav objects and has a “Where used” function. It supports Nav 5, Nav 2009, Nav 2013. . Looks very nice, it’s worth to test it.

02/2016, News about “GDT Where used”: There is a new version, which now supports also Nav 2015 and Nav 2016.

Error: An attempt was made to change an old version of a “Sales Line” record

When posting e.g. a Warehouse Shipment or an other document concerning direct or indirect table “Sales Line”, then following error can occur:

An attempt was made to change an old version of a “Sales Line” record. The record should first be reread from the database. This is a programming error. Identification fields and values: …

Technical Detail:
This error occurs, when “Sales Line”.modify is called.

Reason:
The code was customized. Along the call chain the same “Sales Line” record is modified before the call, where the error occurs. That means there is a customized code (maybe in CU 80 or table “Sales Line”), where some fields are changed, then “Sales Line”.modify is called. After that “Sales Line”.modify is called a second time. That is not allowed.

Solution:
If you did not change the code by yourself, contact the colleague, who made the last changes or contact your NAV Partner. First challenge is to find the position, where “Sales Line”.modify is called first.
Hint: Debug the function, which causes the error and set a breakpoint into trigger OnModifyDatabase in CU 1. This trigger is called each time command modify is called.
When the position is found, change the code. If possible remove the first “Sales Line”.modify call.
Other solution: Reload the “Sales Line” record at the second position (where the error occurs) before calling modify with GET, then write the needed field changes, then call modiy.

cheers

Error: You do not have the following permissions on CodeUnit ApplicationManagement: Execute

If you get following error:

then your nav user account has to less rights to work with the RTC client. This kind of error is normally shown, when you start the RTC client.

Reasons for that error can be:

  • You work with a database which comes from outside of your windows domain
  • Your permission set was changed

To solve that issue best you open SSMS (Sql Server Management Studio) and view table “User”. Search for your windows/nav account in field “User Name”. To go further you need the value in field “User Security ID”. Remember that value.

Then edit table “Access Control” and look for the same value in field “User Security ID”.

Change the “Role ID” value to SUPER, click the ENTER key to assign the change.

After that restart the RTC client and change your user settings as needed.

Cheers

Disable page & ribbon customization

In the RTC client it’s possible to prohibit customizations done by users. This can be controlled by user profiles. In the sample i use profile “Project Manager”. To assign a profile to a user goto page “user personalisation”.

After that close the RTC client and re-open it. After that click on the Dynamics Button (blue button in the top/left corner) on the start page (Home/Role Center) and select menu item “Customize”. There we get a list of subitems to customize the page.

Then we change the profile. To change profile settings write “profiles” into the Search box and select page “Profiles”. Select profile “Project-Manager” and open the card page (Edit Button).

Here set checkbox “Disable Personalization” to true and close the page. After that close the RTC client and re-open the client. Now click again on the Dynamics Button/Customize on the Home page (Role Center). We get then a reduced list with no customze subitems.

Additional changes:

With opportunity to customize you can save filter settings, without you can’t.

In Page “Items “Factbox “Item Details – Invoicing” the settings button with 2 menu items is visible with default settings.

Without customize opportunity the settings button is invisible.

If you have the problem that a user cannot change page settings (Customizations), then check if the user has assigned a profile with above setting “Disable Personalization”.

Cheers

Convert SETSELECTIONFILTER to SETFILTER

If you want to print more than one document in a document list at one stroke – e.g. sales invoices – you would select 2 or more records and then start printing.

But, … in the request page only the last selected document no. or, also possible, no number is set.

Internally in most cases a setselectionfilter command is applied to get the selected records. But whats going on, when selecting the records? each record is marked. With setselectionfilter we can filter the selected record for the setfilter expression.
To print all selected records in one stroke you can do following. Add a new action to the action list of the list page and add following code to the new action trigger:

// local variablesSalesInvHeader | Record | Sales Invoice HeadernoFilter | Text// the code - OnAction()CurrPage.SETSELECTIONFILTER(SalesInvHeader); // fetch the marks// internally property Marked is set to true at the selected records// the loop will fetch only these recordsIF SalesInvHeader.FINDFIRST THEN BEGIN  REPEATIF noFilter  '' THEN  noFilter := noFilter + '|';noFilter := noFilter + SalesInvHeader."No."; // create filter expr.  UNTIL SalesInvHeader.NEXT = 0;  CLEAR(SalesInvHeader);  SalesInvHeader.SETFILTER("No.",noFilter); // create the filterEND;REPORT.RUNMODAL(206,TRUE,FALSE,SalesInvHeader);

As result we get:

Cheers

Error: Table 2000000009 has no name

If you get following error:

then the database is corrupted. In detail it means sql view “Session” is missing in the database! This can be a result of a crashed backup/restore process or a failed conversion process.

It can be, that View “Database File” is also missing. Although it is possible to restore the missing view e.g. from a (not changed) default cronus database, it won’t help, but you can give it a try. You can restore a new cronus database from the nav 2009 setup, subfolder SQLDemoDatabase\CommonAppData\Microsoft\Microsoft Dynamics NAV\60\Database.

In most cases it’s needed to restore the database from an earlier backup. If you do not have a backup strategy for your databases – but you should have – then export all changed nav objects as text files from the current, crashed database and start with a new database on base of a cronus database. then import/migrate the changed nav objects. after that re-compile all nav objects and restart the nav service.

Helpful Links:
http://saurav-nav.blogspot.co.at/2012/02/metadata-for-object-of-type-table-with.html

cheers

Active Directory queries from C/AL

I wanted to get a list of all NAV users with displayname and roles per user in a report. The data base is table “Windows Access Control”. One field should show the displayname of each user. The displayname can be read from hidden table “Windows Object”, field Name. To embed that value in the report i tried to import the field with different methods: by code and by a second, linked dataitem in the report. The result was the same in both cases: the read call (internally an AD query) of the displayname (field Name) is really very, very slow! Although AD queries are quite slow in general (the technique behind is slow), this simple report lasted over 20 minutes and longer. That is not slow, that is really a hoax. Maybe that’s a bug in Nav 2009, maybe the implementation is not very good. So i searched for an alternative solution.

In the end i developed a little automation dll to get the displayname of each user with a given domain account (field “Login ID”). With that automation the report rendering speed was quite acceptable.

This automation, i called it ActiveDirectoryLib, delivers 2 methods:
* GetDisplayName(string userName) : string
* GetUserName(string loginSID) : string

// the displayed report fieldsLogin SID | Username | Role ID | Role Name// global variablesUsername | Text | 100adLib | Automation | 'ActiveDirectoryLib'.AccountInfo// the codeWindows Access Control - OnPreDataItem()CREATE(adLib);Windows Access Control - OnAfterGetRecord()CALCFIELDS("Login ID","Role Name");Username := adLib.GetDisplayName("Login ID");IF Username = '' THEN  Username := "Login ID";Windows Access Control - OnPostDataItem()CLEAR(adLib);

You can download the file here.

cheers

Time driven actions with .Net Class Timer

Till Nav 2009 if time driven actions were needed, it was done using automation NTimer.dll (NavTimer). With Nav 2013 and newer Versions Microsoft recommends to avoid usage of automations. As a result many of the common used automations, shipped with Nav 2009 and earlier, disappeared. There are few descriptions, how to solve common issues formerly solved with automations. So, let’s have a look at the Timer issue.

The .Net Framework contains class System.Timers.Timer in assembly System.dll. To use and test it, create a new codeunit and add some global variables:

Variable Timer is of subtype System.Timers.Timer. We set properties RunOnClient to false, but WithEvents to true. Only one of these properties can be set to true at one time, both together is not allowed. But ok, we need the Triggers (in .Net called Events). With RunOnClient=false, the code runs on the server … and that’s ok. Setting WithEvents to true we get automatically all embedded .Net Events written in the C/AL code, in that case Timer::Elapsed(sender : Variant;e : DotNet “System.Timers.ElapsedEventArgs”) and Timer::Disposed(sender : Variant;e : DotNet “System.EventArgs”). We only use the first one.

In the sample we want create a file and write lines into the file, step by step, every 3 seconds one line. We use a Counter for a break condition.

OnRun()Counter := 0; // Counter is an Integer variableMESSAGE('Timer started');IF EXISTS('c:\temp\sample.txt') THEN  ERASE('c:\temp\sample.txt'); // delete the file, if it already existsTextFile.CREATE('c:\temp\sample.txt'); // TextFile is a FILE variableTextFile.TEXTMODE := TRUE;Timer := Timer.Timer(); // create a Timer instanceTimer.Interval := 3000; // i.e. 3 secs (unit is ms)Timer.Enabled := TRUE;Timer.Start(); // starts the timerTimer::Elapsed(sender : Variant;e : DotNet "System.Timers.ElapsedEventArgs")Counter := Counter + 1;TextFile.WRITE('line ' + FORMAT(Counter) + ', ' + FORMAT(TODAY) + ' ' + FORMAT(TIME));// stop timer after trigger Elapsed was called 10 timesIF Counter > 10 THEN BEGIN  Timer.Enabled := FALSE;  Timer.Stop(); // stops the timer  Timer.Close();  CLEAR(Timer);  TextFile.CLOSE;END;

Result:

cheers

Error: Could not load the selected type library

This is a common error, which can be caused by following reasons:

  • an automation is missing
  • the automation was missing, then installed on the system, but the nav object was not re-compiled after installation
  • a different version of the automation is expected by the nav object.

If this error occurs, check where the variable is defined (local or global variables list). After you found the variable, you will see, that the value in field Subtype is like “Unknown Automation Server.Application”. When exporting the Nav object as text file, you would find a subtype value like that: Class1@1000000010 : Automation “{D1233675-2BBA-49DD-AD90-1680A404EAD5} 1.0:{DB3E185E-C123-46C2-9C62-F4A4E81E0B8F}:’FunnyAutomation’.Class1”. If you don’t know exactly what you are missing, this strange looking subtype value can help you for research.

Next step is – and that is the importand one – remove the value in field subtype! Do not click on the Assist button first and choose the automation without removing the value. It won’t work! So, first remove the value, then click on the Assist button and search for the missing automation.

If you do not find the missing automation, then look for the setup file and install the automation. After that restart the CC Client and start this fix procedure from the beginning. If you don’t know, which automation is missing (maybe an old code, a code of a different developer, …), then start with a research, ask your colleagues, your PM, do a little web search or find an alternative solution.

If all is fine, the automation is installed and the Unknown… value was removed, find and choose the automation after clicking on the Assist button and select the needed embedded class of the automation. After that re-compile the Nav Object and test the whole thing.

cheers