Solutions
Markets
References
Services
Company
Extending the NAV / Outlook integration in NAV 2017

Extending the NAV / Outlook integration in NAV 2017

4. March 2018

Extending the NAV / Outlook integration in NAV 2017

While we are actively working on our plan how to move forward into the VS code development paradigm and actually have our first app ready and approved in AppSource, the undeniable fact is that our customers are still on-prem and using NAV 2017. One of the very nice “new” feature in NAV however is the integration into Outlook using Outlook-Addins which works fine in NAV 2017 and actually is quite easy to extend (not using extensions v2 and not even v11 but plain old C/AL modifications).

The TL;DR

The mechanism is quite open and flexible but in my first step I took the easiest route: Just reuse and extend what is already there. I extended the XML file that describes for what kind of data you are looking for in an incoming email, used that data to look for a tax object in our solution (part of our vertical as custom object) and then present it to the user. That works with only a couple of simple C/AL modifications. The following short film shows you how it looks like and while this might look similar to demos you’ve seen in the past, the big difference is that it shows our custom page and allows the end user to work with our custom entity (not even an extended standard one, this is really custom code from scratch)

In the end, the user can interact with the tax object directly from within Outlook which is amazing. I really liked the integration from the very beginning but as our vertical has quite different use cases and users, the standard scenarios didn’t really fit for us. Having the ability to plug into how this works allows us to match with how our endusers work and also let them in into the Outlook integration fun!

The details

On Directions EMEA 2017 I had the chance to attend a session by Jared Hall which also covered the Outlook integration2. As he obviously only showed the standard use cases like items and invoices I asked him after the session if it also would be possible to extend this mechanism and he was kind enough to let me have a peak at some early documentation on this topic. As he also mentioned that this might become a blog post by Microsoft at some time, I’m only going to cover the things I did and where I touched standard code. And for the sake of simplicity here, I didn’t create proper events in the 2017 standard code (which I think are already there in 2018) but just added my code.

The first place where NAV get’s called from the Outlook integration is Codeunit 1630 Office Management. As I decided to just piggypack on the standard document integration, I changed nothing there. That means that Codeunit 1637 Office Document Handler will be called, where function RedirectToDocument extracts the regex matches from Outlook3 and tries to find matching documents in your database in function CollectDocumentMatches. In the FOREACH loop there every document number that was identifed in Outlook is searched in all relevant tables like sales and purchase orders or invoices. I’ve extended this mechanism to also call my method which searches for tax objects. That works very easily as follows:

LOCAL CollectDocumentMatches(VAR TempOfficeDocumentSelection : TEMPORARY Record "Office Document Selection";VAR DocNo : Code[20];TempOfficeAddinContext : TEMPORARY Record "Office Add-in Context")
...
    FOREACH DocNo IN DocNos.Split(Separator.ToCharArray) DO
      WITH TempOfficeDocumentSelection DO BEGIN
        ...
        SetTaxesDocumentMatchRecord(DocNo,"Document Type"::"Tax Object",TempOfficeDocumentSelection);
        
      END;
...

LOCAL SetTaxesDocumentMatchRecord(DocNo : Code[20];DocType : Integer;VAR TempOfficeDocumentSelection : TEMPORARY Record "Office Document Selection")
Steuerobjekt.SETRANGE(Kassenzeichen, DocNo);
IF Steuerobjekt.FINDSET THEN
  REPEAT
    CreateTaxesDocumentMatchRecord(TempOfficeDocumentSelection,TempOfficeDocumentSelection.Series::Taxes,DocType,
      Steuerobjekt."Objektnr.",TRUE,Steuerobjekt.Beginndatum, Steuerobjekt.Abgabenart, Steuerobjekt."Objektnr.",Steuerobjekt."Gemeindenr.", Steuerobjekt."Adressnr.");
  UNTIL Steuerobjekt.NEXT = 0;

LOCAL CreateTaxesDocumentMatchRecord(VAR TempOfficeDocumentSelection : TEMPORARY Record "Office Document Selection";Series : Option;DocType : Option;DocNo : Code[20];Posted : Boolean;DocDate : Date;Abgabeart : Code[20];Objektnr : Code[20];Gemeindenumm
TempOfficeDocumentSelection.INIT;
...
TempOfficeDocumentSelection.VALIDATE(Steuerabgabenart, Abgabeart);
TempOfficeDocumentSelection.VALIDATE(Steueradresse, Adressnummer);
TempOfficeDocumentSelection.VALIDATE(Steuergemeindenr, Gemeindenummer);
TempOfficeDocumentSelection.VALIDATE(Steuerobjektnr, Objektnr);
IF NOT TempOfficeDocumentSelection.INSERT THEN;

As you can see I’ve made the trivial addition to add my own document type “Tax Object” to table Office Document Selection. I’ve also added a number of additional fields I need to identify the tax object in our solution4.

After collecting all matches the standard code then will either offer you to select the right document if you have multiple matches or just open it if you have one. I also extended that part by adding some logic to open the right page for our tax objects, in this case dog tax5

OpenIndividualDocument(TempOfficeAddinContext : TEMPORARY Record "Office Add-in Context";TempOfficeDocumentSelection : TEMPORARY Record "Office Document Selection")
CASE TempOfficeDocumentSelection.Series OF
  ...
  TempOfficeDocumentSelection.Series::Taxes:
    OpenIndividualTaxesDocument(TempOfficeAddinContext,TempOfficeDocumentSelection);
END;

LOCAL OpenIndividualTaxesDocument(TempOfficeAddinContext : TEMPORARY Record "Office Add-in Context";TempOfficeDocumentSelection : TEMPORARY Record "Office Document Selection")
WITH TempOfficeDocumentSelection DO BEGIN
  IF Steuerobjekt.GET(Steueradresse,Steuergemeindenr,Steuerabgabenart,Steuerobjektnr,'1') THEN
    PAGE.RUN(PAGE::"Steuerobjekt Hundesteuer" , Steuerobjekt);
END;

And that is all I needed to do on the NAV side! On the Office side I had to extend the XML file that describes the addin. You can just download that from page 1610 Office Addin Management and adjust it. In my case I again went for the way of least effort and only updated the version string to make sure that my version gets used and the regex strings by adding

|([0-9]+.200.[0-9])</code> to the No.Series regex and <code>Bescheid</code>[note]"Steuerbescheid" is the German word for "tax report"[/note] to the DocumentTypes regex

[code]
      <Rule xsi:type="ItemHasRegularExpressionMatch" RegExName="No.Series" RegExValue="(GVKR-|GGSD-|GEKR-|GGSK-|EKR-|BEST-|GSD-|VKR-|AUF-|ANF-|GSK-)([0-9]+)|([0-9]+.200.[0-9])" PropertyName="BodyAsPlaintext" IgnoreCase="true" />
      <Rule xsi:type="ItemHasRegularExpressionMatch" RegExName="DocumentTypes" RegExValue="(invoice|order|quote|credit memo|Einkaufsrechnung|Einkaufsbestellung|Bestellung|Verkaufsgutschrift|Verkaufsrechnung|Verkaufsauftrag|Verkaufsangebot|Einkaufsanfrage|Einkaufsgutschrift|Bescheid):? ?#?([\w_/\#\*\+\\\|-]*[0-9]+)" PropertyName="BodyAsPlaintext" IgnoreCase="true" />

Is there room for improvement in the implementation? Absolutely. But I created this as a PoC to make sure extending the standard functionality really works in a way that allows us to bring the Outlook integration to our customers in a meaningful way. And as I wanted to share this with you because I presonally am really excited about how well it works, I decided to blog about it before cleaning it up6.

  1. As I really wasn’t a fan of how that worked
  2. Really one of the best sessions at Directions that year as Jared definitely knows what he is talking about and also is able to transport that knowledge. I highly recommend to join one of his sessions, if you get the chance
  3. more on that later
  4. admittedly not the cleanest piece of code… ;)
  5. I can’t imagine there are too many countries with dog taxes…
  6. Sincere apologies to Luc van Vugt and Mark Brummel who held multiple workshops including clean code at Axians Infoma. Don’t mistake this PoC code for anything they would approve of :)