Learn AL: New string features

The AL language is bringing some nice features to Dynamics NAV developers. I want to cover a couple of the cool new string features.

  1. Function chaining
  2. Replace() function

The following example is something I’ve used in the past numerous times. It finds and replaces a substring within a string. Created in Visual Studio Code, but using good ol’ C/AL syntax, the code would look something like this:

NewString := DELSTR(OriginalString,STRPOS(OriginalString,FindString))
                + ReplaceWithString + COPYSTR(OriginalString,STRPOS(OriginalString,FindString) + STRLEN(FindString));

The above function is easy enough to create, but it was always something that I thought should have been part of the system functions. In AL, we now have the Replace() function, which can be used like this:

NewString := OriginalString.Replace(FindString,ReplaceWithString);
Oh by the way……invoking string functions directly from the variable!? Yes!
Using the “<variable>.<function>” syntax, we can now chain multiple functions together like this:
NewString := OriginalString.Replace(FindString,ReplaceWithString).ToLower();

Whoah! Pretty cool right!?

If you’re not always familiar with function chaining, I should point out that the functions in the chain get applied to the string from left to right, Depending on what you are doing to the string, the order you define the functions in the statement will matter.

This is some fantastic stuff and really brings Dynamics NAV development inline with current development practices.

Until next time, happy coding!

Advertisements

NAV Supporter’s Blog

Back in February, Microsoft resurrected one of their old NAV developer blogs. It’s been renamed to the NAV Supporter’s Blog and as Microsoft puts it:

“This will be by and for people who support the Dynamics NAV products in one way or the other.”

This blog is not necessarily about new front-end features in the product, but more about tips and tools that can be used to better support and diagnose the product.

Recent posts include:
  • Multi-part series on a new NAV diagnostics toolkit
  • NAV performance troubleshooting
  • Build overviews for NAV 2013 –> 2017

The blog can be found here. Check it out!

Happy coding!

Find Out if an Extension is Installed, part 2

“Hi, this is Library, is Extension 1 home? What about Extension 2, are they home too?”

In my previous post on this topic, I explained how you can use the NAV App Installed App system table to see if a specific extension is installed. I later found out though that the ability to access that table may not always be available in the Dynamics 365 for Financials platform, so back to square one. I want a stable long-lasting solution.

Alas…events!

First…some background on why I need this functionality. I’m developing 2 extensions that share a common library, but I want to develop these extensions so that they are independent, meaning that a customer is able to install only one of the extensions if they choose to, and not be forced to install both. I also need certain features in each extension to act differently depending on if the other extension is installed or not. I know….never simple. 🙂

For those that are not aware, when you submit an extension to AppSource, you are able to submit along with it a library extension. Multiple extensions can be dependent on the same library, which makes it easy to deliver foundation type functions that are shared amongst your extensions. The library extension will not be seen in AppSource, but it will be automatically installed when any of the dependent extensions are installed.

What this also means is that when I am developing in the context of one of my “functional” extensions, I am able to directly reference objects that are contained in my library because the library is guaranteed to exist when the functional extension is installed. What I cannot do though is the opposite, because although the library extension knows that at least one functional extension was installed, it does not know directly which one it was. Make sense!? Clear as mud I know. 🙂

In the example below, let’s assume that I have a “Library” extension, and two functional extensions, brilliantly named “Extension 1” and “Extension 2”.

First, I need to create a codeunit in my library extension that will act as the “extension handler” so to speak. In other words it will house the functions required to determine what extensions are installed. In my library codeunit, I’ll add the following functions:

CheckIsExtensionInstalled(ExtensionAppID : GUID) : Boolean
IsInstalled := FALSE;
IsExtensionInstalled(ExtensionAppID,IsInstalled);
EXIT(IsInstalled);

GetExtension1AppID() : GUID
EXIT('743ba26c-1f75-4a2b-9973-a0b77d2c77d3');

GetExtension2AppID() : GUID
EXIT('a618dfa7-3cec-463c-83f7-7d8f6f6d699b');

LOCAL [IntegrationEvent] IsExtensionInstalled(ExtensionAppID : GUID;VAR IsInstalled : Boolean)

The above functions allow me to call into the codeunit to determine if either of my functional extensions are installed. The GUIDs that I used are samples above, but you should use the same GUID that is used in the corresponding extension manifest file.

The piece that makes this all possible is the published event IsExtensionInstalled. What I can do now is from within each functional extension, I can subscribe to that event so that each extension can basically give “answer” the library when it asks if it’s installed.

To do that, I create 2 more codeunits, one in each functional extension. These codeunits will contain a subscriber to the event that we published in the library codeunit. This way, if the extension is installed, its subscriber will respond to the event and let the library know that it is installed. If the extension is not installed then there won’t be anyone home to answer that call.

Extension 1

LOCAL [EventSubscriber] OnCheckIsExtensionInstalled(ExtensionAppID : GUID;VAR IsInstalled : Boolean)
IF ExtensionAppID = ExtensionHandler.GetExtension1AppID THEN
  IsInstalled := TRUE;

Extension 2

LOCAL [EventSubscriber] OnCheckIsExtensionInstalled(ExtensionAppID : GUID;VAR IsInstalled : Boolean)
IF ExtensionAppID = ExtensionHandler.GetExtension2AppID THEN
  IsInstalled := TRUE;

So how do we use all of this? Easy, of course. The example below shows code that you could use from either of the functional extensions, so that your extensions can act differently depending on what other extensions are installed.

IF LibraryCodeunit.CheckIsExtensionInstalled(LibraryCodeunit.GetExtension1AppID) THEN
  MESSAGE('Extension 1 is installed.');

IF LibraryCodeunit.CheckIsExtensionInstalled(LibraryCodeunit.GetExtension2AppID) THEN
  MESSAGE('Extension 2 is installed.');

There, easy right? If you want to try it out yourself, you can grab the above example on GitHub here.

Now, while you can do this if you own all of the source code for each extension, you cannot use this solution to determine if “any old random extension” is installed, as you need to add the subscriber function to the functional extension code. But, if you are developing out multiple extensions and if you need to know which of them are installed, then this solution will work wonderfully!

Happy coding!

Timestamp fields in Dynamics NAV 2016

Have you ever needed to keep track of when records changed, or have you ever had to do an integration where you want to make sure you keep records in synch across multiple systems?

For master records, this was not a huge chore, as you typically were able to use the Last Date Modified field (if there is one of course), but of course you better hope that all updates to the record call the onModify() trigger. For transactional records though, this became more of a chore, as you would need to either keep track of the last entry you synchronized, or figure out some other way of keeping track what you’ve synched and what you haven’t.

I remember a particular integration that I had to do in which involved synchronizing the Item Ledger Entry table. Sure, I could keep track of the last entry no. so I could easily find all new records since the last synch session, however what became difficult was finding all of the older ledger entries that were updated as inventory transactions were applied to them. Remember that the Item Ledger Entry table is unique in that it gets updated even long after they get created.

Enter the Timestamp field. New to Dynamics NAV 2016, it will simply these types of integration and synchronization scenarios. As far as I can remember, all versions of Dynamics NAV running on SQL Server have had an internal system field name that holds the record timestamp. You could however, only access this field by querying the SQL table directly. Anything done via NAV would never even see the field.

Now with Dynamics NAV 2016, you have the ability for all records to determine which records have not only been inserted, but also modified. You still need to track your date/time reference (e.g. the last time the synch was run), and you are still left to determine what to do if records are deleted, but overall, this is a great new feature to the product!

Here’s how to use them…

  1. In any given table, create yourself a custom field, and assign it a Data Type of BigInteger. The name of the field can be anything you want.

  2. View the Properties of your new field, and set the SQL Timestamp property to Yes. It’s probably a good idea to make it a non-editable field as well, just for logic sake.

  3. Close, save, and compile your table.
  4. Voila, when you run the table, you now how your timestamp!

Umm, ok Mr. Blogger, I get all of this, but that field definitely does not contain either a date or a time!! What’s up with that??

Yes…this is true; the field does not actually hold date or time information. What it does hold though is a “version numbering” that is assigned to each row in a table, across all tables in the database, so every row in the database will have a unique timestamp value. This means that no matter what table you are dealing with, you can use a single reference timestamp and obtain all records that have been added or changed. AWESOME!
(as I said above though, finding deleted records is still a chore that requires coding)

One more note on Timestamp fields. You cannot add them to a table key, however you can still perform a SETCURRENTKEY using the timestamp, since NAV now allows us to sort on any field regardless of it being in a key or not.

So now you know, hopefully you can use this new feature to your advantage.

Happy coding….

MG