Introducing AdvaniaGIT – SCM for Dynamics NAV

Almost two years ago we in Advania decided to start using GIT as Source Control Management (SCM).  We brought Kamil up to Iceland and we kicked off.  In Sorens session on NAVTechDays last year we demoed SourceTree as the GIT client for NAV SCM.

Everything we in Advania are doing with SCM is available on GitHub.  It is our hope that we can get as many users and companies to use and contribute to this solution.

Over the next coming days and weeks I will be writing here about this tool.  I will also be using the GitHub Wiki for some of the information.

Installing AdvaniaGIT will create a folder structure on your local drive. You can select any of the local drive installed. We suggest that the AdvaniaGIT\Workspace folder should be excluded from Windows Defender and that also goes for any GIT folder used.

Refer to the README.md file inside every subfolder for more details about each subfolder usage.

Inside the Data subfolder we store the module settings in JSON files.

  • BranchSettings.json is automatically managed by the module and used to link GIT branches to local NAV environments.
  • BuildSettings.json contains incremented values that will be used when building new environments.
  • GITSettings.json contains machine settings for the module.
  • NAVVersions.json contains information about locally installed NAV.
  • RemoteSettings.json contains settings for the Remote Management module. Not used by GIT in any way.
  • TenantSettings.json contains settings for each tenant running on a remote server that is managed using the Remote Management module. Not used by GIT in any way.

In the GIT repository folder we require a setup.json file. When the scripts are executed settings from the GIT branch (setup.json) and settings from the machine (GITSettings.json) are merged to a single settings object. If same settings exist in both files the one in the GIT branch will be used.

Installing the module will add custom actions to SourceTree and a command file (StartPowerShell.cmd) to your Windows directory. SourceTree will execute this command file with parameters telling the module what to do. The command file will execute Scripts\Start-CustomAction.ps1 with the same parameters. All custom actions within the Scripts\CustomActions subfolder can be executed.

For teams we suggest using a FTP server for backups and CRONUS text files.

My next blog post will be on the installation and update of AdvaniaGIT.  Stay tuned…

Working with optional NAV table fields

Now that we have entered the Extension era we must take into account that some extensions may or may not be installed at the time of code execution.

You might even have two Extensions that you would like to share data.

Let’s give an example.

In Iceland we add a new field to the Customer table (18).  That field is named “Registration No.” and is being used for a 10 digit number that is unique for the individual or the company we add as a customer to your system.

My Example Extension can support Icelandic Registration No. if it exists.

Using Codeunit 701, “Data Type Management”, Record Reference and Field Reference we can form the following code.

LOCAL PROCEDURE GetCustomerRegistrationNo@10(Customer@1000 : Record 18) RegistrationNo : Text;
VAR
  DataTypeMgt@1001 : Codeunit 701;
  RecRef@1002 : RecordRef;
  FldRef@1003 : FieldRef;
BEGIN
  IF NOT DataTypeMgt.GetRecordRef(Customer,RecRef) THEN EXIT('');
  IF NOT DataTypeMgt.FindFieldByName(RecRef,FldRef,'Registration No.') THEN EXIT('');
  RegistrationNo := FldRef.VALUE;
END;

Let’s walk through this code…

GetRecordRef will populate the record reference (RecRef) for the given table and return TRUE if successful.
FindFieldByName will populate the field reference (FltRef) for the given record reference and field name and return TRUE if successful.

Call this function with a code like this.

Customer.GET('MYCUSTOMER');
RegistrationNo := GetCustomerRegistrationNo(Customer);

We could create a more generic function.

LOCAL PROCEDURE GetFieldValueAsText@103(RecVariant@1000 : Variant;FieldName@1004 : Text) FieldValue : Text;
VAR
  DataTypeMgt@1001 : Codeunit 701;
  RecRef@1002 : RecordRef;
  FldRef@1003 : FieldRef;
BEGIN
  IF NOT DataTypeMgt.GetRecordRef(RecVariant,RecRef) THEN EXIT('');
  IF NOT DataTypeMgt.FindFieldByName(RecRef,FldRef,'Registration No.') THEN EXIT('');
  FieldValue := FldRef.VALUE;
END;

This function can be used in more generic ways, like

Customer.GET('MYCUSTOMER');
RegistrationNo := GetFieldValueAsText(Customer,'Registration No');

Vendor.GET('MYVENDOR');
RegistrationNo := GetFieldValueAsText(Vendor,'Registration No');

See where I am going with this?

So the other way around…

LOCAL PROCEDURE SetCustomerRegistrationNo@21(VAR Customer@1000 : Record 18;RegistrationNo@1004 : Text) : Boolean;
VAR
  DataTypeMgt@1001 : Codeunit 701;
  RecRef@1002 : RecordRef;
  FldRef@1003 : FieldRef;
BEGIN
  IF NOT DataTypeMgt.GetRecordRef(Customer,RecRef) THEN EXIT(FALSE);
  IF NOT DataTypeMgt.FindFieldByName(RecRef,FldRef,'Registration No.') THEN EXIT(FALSE);
  FldRef.VALUE := RegistrationNo;
  RecRef.SETTABLE(Customer);
  EXIT(TRUE);
END;

And using this with

Customer.GET('MYCUSTOMER');
SetCustomerRegistrationNo(Customer,'1234567890');

More generic versions can be something like this.

PROCEDURE PopulateOptionalField@25(VAR RecordVariant@1000 : Variant;FieldName@1001 : Text;FieldValue@1002 : Variant) : Boolean;
VAR
  RecRef@1004 : RecordRef;
  FldRef@1003 : FieldRef;
BEGIN
  IF NOT GetRecordRef(RecordVariant,RecRef) THEN EXIT;
  IF NOT FindFieldByName(RecRef,FldRef,FieldName) THEN EXIT;
  FldRef.VALUE := FieldValue;
  RecRef.SETTABLE(RecordVariant);
  EXIT(TRUE);
END;

PROCEDURE ValidateOptionalField@26(VAR RecordVariant@1000 : Variant;FieldName@1001 : Text;FieldValue@1002 : Variant) : Boolean;
VAR
  RecRef@1004 : RecordRef;
  FldRef@1003 : FieldRef;
BEGIN
  IF NOT GetRecordRef(RecordVariant,RecRef) THEN EXIT;
  IF NOT FindFieldByName(RecRef,FldRef,FieldName) THEN EXIT;
  FldRef.VALIDATE(FieldValue);
  RecRef.SETTABLE(RecordVariant);
  EXIT(TRUE);
END;

To use these functions we first need to copy our record to a variant variable and then back to the record after the function completes.

RecordVariant := Customer;
PopulateOptionalField(RecordVariant,'Registration No.','1102713369');
Customer := RecordVariant;

Or

RecordVariant := Customer;
ValidateOptionalField(RecordVariant,'Registration No.','1102713369');
Customer := RecordVariant;

I have requested Microsoft to add more generic functions to Codeunit 701, “Data Type Management”.  I trust that they will deliver as usual.

That NAV Codeunit is one of my favorite ones delivered by Microsoft.

Building Assisted Setup for Dynamics 365 for Financials

I had my Assisted Setup wizard up and running on NAV 2017.  Everything looked fine but when the extension was being validated nothing worked.

So, there is a difference between NAV 2017 and Dynamics 365 for Financials.

Remember the session on Design Patterns in NAV 2017 on NAV TechDays 2016?  Microsoft showed what they where planning in regards to assisted setup and manual setup.  This has been implemented in Dynamics 365 for Financials but has not been released for NAV 2017.

One of the feedback Microsoft got from us MVPs was about the Assisted Setup not using the discovery pattern (you will know what I am talking about after watching the session above).  The Assisted Setup table (1803) in NAV 2017 is the one used to register all assisted setup pages.  The problem was that a record for an extension in this table was not removed during uninstall.

Now we have a new table, Aggregated Assisted Setup (1808) that is a temporary table using the discovery pattern.  We also have a new discovery pattern for the Manual Setup with another new table, Business Setup (1875).  You can download these new tables from here (NewD365SetupTables) or wait for them to be released in one of the upcoming NAV 2017 Cu releases.

Here you can also download the guidelines for the new setup pattern (AssistedSetupGuidelines).  My code looks like this.

The Icon file that I created is 240x240px with foreground (RGB 55 55 55) and background (RGB 250 250 235).

More to come, stay tuned…

My first Dynamics 365 Extension – Approved for publishing

Yes!  I have passed all validation steps and Microsoft will publish my app soon.

These are my marketing validation results.

Marketing Validation_Objects4NAV – GL Source Names, 3.2.2017

Remember to look for this image in AppSource and try out my Extension.

As I promised, all the source code is now available on GitHub.

https://github.com/gunnargestsson/nav2017/tree/GLSourceNames

This concludes my blog series on “My first Dynamics 365 Extension”.  Stay tuned for more information on how to design and publish your extension.  I will have more to share in the coming weeks and months.

 

My first Dynamics 365 Extension – Mistakes I made

There are a few things to look out for – things not that obvious.

Look at the extension settings in my Source Control.

the “appName” must match the name in Azure Publishing.

The “appPublisher” must match the publisher short name.

The “appVersion” must be in this format and identical to the version of you app in Azure Publishing.

Also make sure to only select Canada and US even if your app can support more.  Also make sure to only select English as the language.  There can only be two industries and sub-categories.

There must be a web site for the extension.  The “appHelp” Url needs to land in a place where it is easy for the user to find help.

We would recommend to make the videos more prominent on the top of the side. The help link is intended to provide online customer help.”

Now, when you follow the help link you will see a video that will show the benefits of installing this Extension.

Also make sure to have a proper page for privacy, terms and conditions and the publisher website.

All videos must be Dynamics 365 only, both in speech and image.  Never mention Dynamics NAV nor NAV.  Using the Dynamics 365 shell or the upcoming Dynamics 365 for Financials testing environment to make your screenshots and videos.

The documentation must include a user story for the testing team to follow.  The testing team must be able to follow a guided path to test the Extension with new releases of Dynamics 365.  The user story should also show the gains by installing the extension.

Then there is the Lead Management.

There is a document describing how to do this.  You can download it from here, but this is a static document and will not be updated by Microsoft.  AppSource Publishing Guide for Dynamics CRM Solutions.

I am using Azure Table.  Sample final connection string:

{“connectionString”:”DefaultEndpointsProtocol=https;AccountName=spzademoaccount;AccountKey=Ld4mIh4DVrsFEaSw21HKbqn05bl5hLVKcw0fJ4DIsa6RuvwlSMZJzQZM312IHersOIMof4DEouEmc0jw==”}

After correcting all my issues I have restarted the request approval to push to production process…

 

Updates to my Object Renumbering Tool

Back in the end of 2014 I published a renumbering tool for NAV objects.  Using DotNet I was able to increase the renumbering speed for text object files dramatically.

Since then I have been asked if I could upgrade it to work with IDs and Field Numbers.

Now I have.

What’s more, it is also on GitHub.

The Process functions are the following;

  • Read Object Lines – Creates renumbering lines base on the objects in the selected object file.
  • Suggest IDs – Suggest new object numbers in the range from 50.000 based on the available objects in the current license.
  • Read from Excel – Reads object renumbering lines from Excel Sheet created with the Write to Excel process.
  • Write to Excel – Writes current renumbering lines to a new Excel Sheet to me managed within Excel and reread into the renumbering lines.
  • Renumber Using Lines – Prompts for a file to read and for a new file to save with renumbered objects based on the rules in the renumbering lines.
  • Renumber Using Controls – Prompts for a file to read and for a new file to save with renumbered objects based on the rules in the control IDs setup.

I have done some fixes to the renumbering function and have added support for the EventSubscriber.

Go to GitHub to download Page and Table 50000, try this out and submit improvements.

When I am processing an object file I have it open in my text editor.  When I see something to renumber I update the control ranges and execute the renumbering process, reading and writing to the same object file.  My editor will reload the file and I can see the results immediately.

 

NAV Http Web Request

In my post about Json and Rest web services I showed how to use the standard Codeunit no. 1297 for web service communication.

Today I was asked to do this in NAV 2015.  I must admit, I forgot that this Codeunit was not available in NAV 2015.

So I made one.

This one has identical functionality to the one delivered with NAV 2016.  To catch and handle the errors I use the NAV Web Request Add-in that I created and published here on my blog.

Now I can easily move that Json code down to NAV 2015.

Download here –> COD1297-NAV2015

My first Dynamics 365 Extension – step by step – eight step

Now we have gotten to Step 4 in the guided path Microsoft offered us on PartnerSource.

Step 4: Provide your offer information

After you have built your app, you will need to define all the attributes that will determine how your app will be listed in Microsoft AppSource. For example, your company information, your offer & plans, marketing information, support contact, and Microsoft AppSource categories.

To define the attributes, visit the Microsoft Azure Publishing Portal.

Refer to the Marketing Validation Guidance and checklist (coming soon) to get insight into the marketing requirements and recommendations.

 

 

Lets create a new Madeira offering.

Here I clicked on “Create Dev Center account and join the Azure program”.  This helped me link the development account I created in Step 2 to my Azure publishing.

Next I clicked on “Tell us about your company” and typed away.

Next step to describe my Extension.

Here we need a lot of information.  You can see the asterisk fields that are required.

In the Plan I just created a default plan.  There will be support for more later on.

Marketing, also a generic identifier.

Then go through the language and provide all the marketing information.

Put my name, email and phone in the support page.  In Categories I checked Business Application.  Then finally I requested approval to push to production.

Now I expect to be contacted by Microsoft and helped with getting my Extension online in Dynamics 365 for Financials.

My first Dynamics 365 Extension – step by step – seventh step

Help and notification.

To make one thing clear.  The help we are used to build for the help server is not yet available for Extensions. Therefore we must make sure that all the help we anticipate the user needs will be available from the product.

Microsoft have added tool tips to most of the fields in the application.  To make sure you follow the requirements create tool tips and make sure to have the property for Application Area  populated.

In the previous post about the installation process you can see that a link to the Extension help must be provided.  I did short videos and posted them to YouTube.

With NAV 2017 and Dynamics 365 for Financials Microsoft released a new notification framework.  It is important to use this framework in your Extension.  For example, when a user open the General Ledger Entries after the G/L Source Names Extension installation a notification will appear.

And when the user reacts to the notification a setup video will start inside the web client.

The notification disappears and will not be displayed again for this user.

If the administrator has done the Assisted Setup and the user has the required permissions we will show a different notification.

and that notification will play the usage video.

Now, let’s look at how to do this.

There is a new data type for Notification.  You define variable of type Notification, set the properties and send it on it’s way.

I start by catching the event when the General Ledger Entries page is opened.  Then, depending on the permissions the user has to G/L Source Names table I select between two notifications.

You can have up to three actions added to each notification.  An action must point to a public function in a Codeunit.  That function must have a single parameter of type Notification.

The notification ID is a Guid.  Refer to my last post on how to get a new Guid and keep that Guid for the notification.  The history of the notification – which  user has acted on it, is saved by this ID.  Change it and the history will be lost.  Today, all the notifications must have Local Scope.  The Global Scope is not yet supported by the clients.

In the action for both these notifications I start a YouTube video for the user.  If the user is running web client, including phone client and tablet client, the video will be started inside the client.  For other client types I will start the video in the default browser.

That concludes my development.  Next part is to submit my Extension to AppSource.  Stay tuned…

 

 

 

My first Dynamics 365 Extension – step by step – sixth step

Assisted Setup and Permissions.

But first lets look at the Extension Management Codeunit.

I want to store the appId in my Extension.  This appId is used in several tables in my tenant.  This appId must always be the same for this solution even if the solution is updated.  In my last post you can see that I am also setting my appId parameter in the Git repository.  There are few easy ways to get your appId.  NAV has the function CREATEGUID.  Powershell has the function New-Guid.  But perhaps the easiest way is to use this online Guid generator.

In OnNavAppUpgradePerDatabase – executed once when the Extension is installed, I want to assign my Extension Setup permission set to the user installing.  This is required to be able to run the Assisted Setup after installation.

In OnNavAppUpgradePerCompany – executed once in every company when the Extension is installed, I want to restore data for some of the Extension tables and delete rest of the archive data.  Even if I am just using tables as a temporary tables I still need to define what to do with the non-existing-archived data.  In here I also want to remove the persistent Assisted Setup record in every company.

So, why would I like to remove the Assisted Setup record?  Not because I like to make the user execute the setup every time the Extension is updated.  The Extension has its own Setup table.  In that Setup table I store the setup information belonging to this Extension and the Setup data is restored from archive during the installation as you can see above.

In the current release the Assisted Setup record is a database record.  I know that Microsoft intends to change this and use the same discovery pattern that is used in Service Connections.  When that happens the Assisted Setup record will be temporary only.  So, by designing my Extension in this way I make sure that I will still support Assisted Setup after this change.

In NAV 2016 we had the Mini Role Center, page 9022.  Today this Role Center has been updated to fit Dynamics 365 for Financials.

From here the user can access the Extension installation we covered in the last post, and we can also access the Assisted Setup & Tasks.

I add my Assisted Setup by subscribing to the OnOpenPage Event in the above page.

Looking at the Assisted Setup record we can see that is has a media field for an Icon.  NAV setup data includes icon for the standard Assisted Setup items – so must we.

I decided to ship my icons with the code.  Each icon has a dedicated Codeunit.

To fit an icon into a Codeunit we need to convert the binary data to base64 data.  This we can do with the following code.

This allows me to create a Codeunit like this.

And then code like this to import the icon into my help resources.

Now, back to the Assisted Setup.  I start the setup for my Extension.

This sums up what needs to be done.  The basic setup, the one done with Set Defaults, is to assign permissions to all users based on their current permissions to the tables we build our extension around.

The G/L Source Name lookup table is read from a FlowField in G/L Entries.  We can therefore expect that everyone that has read access to G/L Entries must also have read access to the lookup table. The data in the lookup table is updated when ever any of the four master tables is updated.  Hence, everyone that has access to modify any of the master tables must have access to modify the lookup table.

The Set Defaults function assigns the Extension permission sets to users and user groups based on the current permissions and that should be enough in most cases.  If the user likes the advanced way that possibility is available.

When the user presses Finish the wizard data and the assigned permission sets are applied to the tenant database.

When I created the Setup Wizard page I started with a copy of the Email Setup Wizard, page 1805.

The model I have created to assign permission sets to users and user groups with a Wizard can easily be modified to fit any Extension.  Remember that all this code will soon be available on my GitHub account.