On Harley’s, chaos, and Business Central

I am always struck by the similarity between Harley Davidson and Navision. Both are fantastic products with devoted cult followings.

Both also have had trouble lately having to adapt to the new World. Navision struggled in a world that became increasingly cloud and mobile oriented. Harley Davidson struggles with increasing environmental regulations and an aging demographic. Both recently also released amazing new products. Microsoft Released Business Central. Harley Davidson released the LiveWire. Both also risk alienating their cult following with their latest releases.

I’m told that on twisting the throttle of a LiveWire it slips quietly into hyperspace miraculously reappearing a 100 miles down the road carrying a rider with a stupid grin on her face feeling that somehow she glimpsed the very fabric of reality. If it is anywhere near this description then I want one.

I will go one step further though and claim that the LiveWire is the only viable mode of transport for the future. we must all buy one or we won’t be able to move from a to b at all. Obviously all older Harleys must be traded in immediately. Cars, buses, airplanes, and rockets will all be obsolete. Sorry Elon.

Sound familiar much? I often get this same idea when people talk about Business Central. The truth though is that we have never been more spoiled for choice. Gas or electric, cloud or on premise. Customized or not customized, old or new. Just as a Harley Knucklehead is still a serviceable motorcycle NAV2009 is still a working product. Both just require a lot more maintenance, parts are hard to get, and there are less people who can work on them. But that does not make them unusable.

Let’s talk about accessories. Harley Davidson’s accessory catalogue has it’s own gravity field and it is orbited by moons made of discarded wish lists. Same thing with Business Central right? I dare not descend into the depths of Azure without leaving a trail of pebbles so I can find my way back. Making matters worse is the fact that by using modern development techniques you can interface Business Central with everything. Where do you start?

What we end up with is complete chaos. Where once we had one ERP that did everything we now have so many choices to make many people don’t know where to begin.

The only way out of this mess is to bring order into it ourselves. We need to do this by going back to our roots. Our customer and their business processes. Let’s start with the basics. We need to add value for our customers by efficient automation and reduction of technical debt.

Don’t just build and don’t just buy Azure or PowerApps or anything else because it is hot or you just had a slick presentation on TechDays. You need to think critically. Before you build or buy something you need to think about if there are enough developers for that technology, security, ease of use, and how hard it will be to keep it up to date. Remember that it is not just Business Central that is updated automatically, everything is. Every API in the world faces breaking changes and what you develop now will be broken at some point in the life cycle of your product.

The purpose of this story is not to scare you into doing nothing. The purpose of this story is to think critically and give your customers something that is of lasting value. Something that is safe, secure, and easy to use. Something that is kept up to date as a matter of course.

Don’t get me wrong, I think Business Central, Azure, Power Apps, and the LiveWire are cool and, more than that, that they can add value to your customer. But so can other things. We cannot rely on the old gods of Danish sensibility to bring order to our world. We need to do that ourselves.

Photo by Harley-Davidson on Unsplash

Building a React front end in Business Central

Why on earth would you want to do that?

That, honestly, still is one of my reactions when I work on this project. Fair warning to you, dear reader, this is very much still a work in progress. What I am going to share in this blog post will hopefully help someone to realize the power of React front ends and help that someone get started. Don’t expect a glib demo style bit of code though, I am just going to try to inflict my learning curve on you. Or at the very least help someone to get started with React.

So why React? Mostly because I want to be able to create better looking and more interactive user interfaces than the standard page lets me. Don’t get me wrong, I think the way we define pages in Business Central is genius and it has been for the past 40 years. There are some cases however where it just falls short. In the past few years there have been enough of those that I started to build Control Add-Ins with JavaScript. React is just a logical progression of that. I’ll let someone else explain what React is.

ReactJS is an open-source JavaScript library which is used for building user interfaces specifically for single page applications. It’s used for handling view layer for web and mobile apps. React also allows us to create reusable UI components. React was first created by Jordan Walke, a software engineer working for Facebook. React first deployed on Facebook’s newsfeed in 2011 and on Instagram.com in 2012.

React allows developers to create large web applications which can change data, without reloading the page. The main purpose of React is to be fast, scalable, and simple. It works only on user interfaces in application. This corresponds to view in the MVC template. It can be used with a combination of other JavaScript libraries or frameworks, such as Angular JS in MVC. Source.

In my opinion this says it all. “specifically for single page applications”, “web applications which can change data, without reloading the page”, and “fast, scalable, and simple” just screams Control Add-In to me. Microsoft itself uses React in its new UI Fabric framework. I expect us to see a lot more of that in the near future.

How to get started? Before making it as far as building an extension there are some steps you need to take first. If you are completely new to HTML, CSS, and/or JavaScript I highly recommend doing the codecademy courses first. To get started with React follow the add React to a website and the tutorial instructions.

In this blog post I won’t go into the building of a Control Add-In and its initial setup. If you are interested there is a ton of stuff out there on Google. Hat tip to Vjeko and Arend-Jan for posting stuff that helped me. And to a ton of more or less anonymous Stack Overflow posters. Where would we be right?

The nitty gritty

This is by no means a comprehensive How To. I will just describe the interaction between React and Business Central. If you want to know more just get in touch or check the GitHub project.

First thing I did was to create two VS Code projects, one for my extension, one for my React project. In my React project I added Node.js so I can use JSX. JSX is an easier way to create HTML code in JavaScript. With it you can simply write HTML in JavaScript. JavaScript code will be placed in parentheses.

JSX, and with it the second VS Code project, is completely optional. I just prefer using it. The only requirement is to have Node.js installed on your computer. To add JSX to your React project go to your project folder in the terminal, and paste these two commands (source):

Step 1: Run npm init -y
Step 2: Run npm install babel-cli@6 babel-preset-react-app@3

You start the compiler from the VS Code terminal. I find it useful to build straight to the Scripts folder of my extension:

npx babel --watch src --out-dir "~path to my extensions~\reactpages\React Pages\Objects\Scripts" --presets react-app/prod

The first thing i create in my extension is a Control Add-In on a page. Mine looks like this:

 controladdin ReactPageControlAddinRED
{
    Scripts =
        'https://unpkg.com/react@16/umd/react.development.js',
        'https://unpkg.com/react-dom@16/umd/react-dom.development.js',
        'Objects/Scripts/reactPageFunction.js';
    StartupScript = 'Objects/Scripts/reactPageStart.js';

    StyleSheets = 'Objects/Css/reactPage.css';

    HorizontalStretch = true;
    HorizontalShrink = true;
    MinimumWidth = 250;

    procedure InitControls(FieldList: JsonArray; Data: JsonArray; PageActions: JsonArray);
    procedure ValidationResult(FieldKey: Integer; Data: JsonArray; LastError: Text);
    event ControlReady();
    event ValidateField(FieldKey: Integer; Data: JsonArray);
    event HandleAction(Data: JsonArray; PageAction: JsonObject);
    event UpdateRecord(Data: JsonArray);
} 

From top to bottom there are some things I want to draw you attention to.

The first two scripts are needed to make React work. Please don’t use these for production. When deploying, replace “development.js” with “production.min.js”.

My reactPageFunction.js contains my React classes. The startup script just calls the reactPageFuntion.js. The reason for doing is is to be able to host the JavaScript elsewhere. It is not possible to host the startup script elsewhere so this is my solution for that problem.

My procedures are used to move data from my page to my JavaScript. events work the other way. On this particular page I send a FieldList with its data and an ActionList. React will display the fields and actions I define in the JSON arrays. The React code to do this looks like this:

window.InitControls = function(fieldList, data, actions) {
  ReactDOM.render(
    <FieldList
      fieldList={fieldList}
      data = {data}
      actions = {actions}
    />,
    document.querySelector('#controlAddIn'),
  )
}

From top to bottom this event creates a function that will render my React component. It renders the FieldList class that I have created earlier and appends it to the Control Add-In div that the Business Central page created for us.

The FieldList class has a state in which I load the three JSON arrays. this state is what we manipulate and what ultimately determines what React shows on the screen.

class FieldList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fields: Object.values(this.props.fieldList),
      data: Object.values(this.props.data),
      actions: Object.values(this.props.actions),
    }
  }

If I want to validate the content of a field in React I call the ValidateField event of my Control Add-In which validates the field and sends the validated data back to React.

Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("ValidateField", [e.target.dataset.id, newData])

In a bit of typical React magic I added a listener for the ValidationResult procedure inside the componentDidMount function. In this function I call the setState to update the state of my React class. That is what renders the changes on the page. The fun thing about React is that while the underlying Business Central page is validating my React component will remain responsive. I can even start typing in another field and everything will work perfectly!

componentDidMount(){
  window.ValidationResult = (fieldkey, data, lastError) => {
    let newData = Object.values(data);
    this.setState({data: newData});
    let fields = [...this.state.fields];
    if (lastError)
    {
      fields[fieldkey].errorMessage = lastError;
    } else {
      fields[fieldkey].validated = true;
      fields[fieldkey].errorMessage = null;
    }
    this.setState({ fields : fields });
  }
}

In the calling and handling of the validation I also set the validated boolean of the field to false and then true. This allows me to display a nice loading image on my HTML form while Business Central is validating the field. Same for the error message, when there is an error in validating the field React will display an error message based on the state of the React Class. Finally to display the fields on the React Component I call the fieldsForm which renders everything into a form.

I won’t go into the details on how to make a React form work. In this blog I wanted to focus on the integration of React with Business Central. If you want to know more about React forms I suggest reading this. Some quick highlights though.

 fields.map((val, idx) => { }

This loops through the fields array.

 { fields[idx].validate && fields[idx].validated ?  [some new div]  : null } 

The question mark in JavaScript is a ternary operator. This line is basically an if statement, you could write it like this. Null in this case means don’t display anything.

if (fields[i].validate && fields[idx].validated) {
  [some new div]
} else {
  null
}

Add a little CSS magic and the result is this:

Annotation 2019-10-22 214647

This is of course a very very brief description of what I made. You can find the full project on GitHub.

Some final thoughts. First, there are people who are working on extensions that will allow you to create really nice UI elements in Business Central without mucking about with JavaScript. If you are interested in this please send me a message and I will get you in touch with them.

Second, and blindingly obvious, I am not a React developer (yet). I am only learning and sharing what I learned. If you have anything to add please create a pull request on GitHub. I welcome collaboration.

Third, as I mentioned before, this is a work in progress. I will share more when I learn more.

Fourth, it is of course entirely possible to create a React frontend outside of Business Central using the APIs. Or even to call the APIs from inside a Control Add-In. That will be a new blogpost. Stay tuned!

Photo by Greg Rakozy on Unsplash

Using HTML inside ForNAV reports

Earlier this week I was conducting a Reports ForNAV training in Bremen. As I was showing my students ForNAV multilanguage labels with HTML formatting we started talking about the other features that ForNAV has regarding HTML. My students suggested that it would be useful if users could write their own HTML code, save it in a blob field and then display it in a report. That is perfectly possible! In this blog post I use a Dynamics 365 Business Central Cloud ForNAV report with a per tenant extension but this will work with an On Prem Extension or plain old C/Side just as well. So, how does HTML work in a ForNAV report?

A while ago I was working with another ForNAV customer who wanted a flag for every item on a catalogue report so customers could see the origin of that item. We ended up with a simple ForNAV text box with this source expression:

Annotation 2019-10-17 205301

That results in a report that looks like this:

Annotation 2019-10-17 205451

So that is fun right? No messing about with saving images anywhere, just instant prettyness in our report. But we wanted something a bit more intricate, something we can use across multiple reports. Yes I know ForNAV has a template function. Please indulge my love of coding.

First thing I did was to create an extension with three objects, a table with the blob fields, a page to edit the blobs, and a standard ForNAV sales invoice. You can find the extension on my github page. I won’t go into detail here as it is pretty standard stuff.

I then used an online HTML editor to create a simple report footer text. I wrapped that in the HTML doctype labels and saved it in my new HTML blob table. Please note the %1 text I use as a placeholder for my company name. Yes, I could have created a nice ReactJS WYSIWYG HTML editor on my page but I really wanted to get this blog post out. Maybe later.

<p style="text-align: center;"><span style="background-color: #00ff00;">We at %1 do so enjoy your custom</span>. <strong>Please</strong> come back and spend <span style="color: #ff0000;">more</span> cash.</p>

Next step is to get this blob field and use it as a source expression in ForNAV. To do that I first added the record in the Records property of the report. To do this open the properties of the ForNAV report and drill down into the Records property and add the tables you want to get data from.

Annotation 2019-10-17 210857

The next step is to get the record from Business Central. For that we write some JavaScript code in the ForNAV OnPreReport trigger.

HTMLBlobRED.Get(1);
HTMLBlobRED.CalcFields('HTML');

decodeBase64 = function(s) {
var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;
var A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for(i=0;i<64;i++){e[A.charAt(i)]=i;}
for(x=0;x<L;x++){
c=e[s.charAt(x)];b=(b<<6)+c;l+=6;
while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}
}
return r;
};

The decodeBase64 function is needed because in Business Central Cloud I get the content of a blob field as a base64 string.  In On Prem situations this is not needed.

Finally I created a new text field in a footer with this source expression:

decodeBase64(HTMLBlobRED.HTML).replace('%1', CompanyInformation.Name);

What this does is it decodes the base64 string in my blob field and in the resulting string it replaces the %1 placeholder with the CompanyInformation.Name field which is part of the dataset of this report.

The result is this:

Annotation 2019-10-17 213021

Some final thoughts. In this example I used a new ForNAV report that is part of my extension. That is not strictly necessary. The records property in a ForNAV report can access all installed extension tables without the need for dependencies. I just wanted a complete stand alone example set so I chose not to use the phenomenal ForNAV Report Pack.

Full disclosure, I do work for ForNAV as a freelance trainer/consultant. They have not encouraged nor paid me to write this blog. I write about products and technology that I love working with and I love working with and wholeheartedly endorse ForNAV.

Two more ways to break your extension

I realize I have been remiss in sharing with you my continued experiences with creating extensions for our Hybrid NAV 2018 database. This is mostly because I have been busy doing exactly that, building extensions. Mostly it goes very well. Sometimes it does not, usually because we mess something up.

Recently we ran in to two problems, both of which can be fixed in the same way.

As I said before, most of our problems arise from us creating extensions in a heavily modified NAV2018 database. This is a so called hybrid system where we have both C/Side modifications and extensions. It is not something we particularly like but it is something we, and many others, have to live with. This problem arose in us moving tables from C/Side to extensions.

In doing this we ran into a situation where an object was removed that an installed extension depended on. In another situation an object was added in C/Side that had the same type and ID that already existed in an installed extension. Normally this will just result in errors when you try to run these objects. However, if all your service tiers are down they won’t start up again so there is no possible way to remove either objects or extension. The only possible way to start the service tier is to uninstall the extension from SQL.

DISCLAIMER, do not attempt to remove records in SQL without first making a back-up! If you are not sure of what your are doing please ask someone who does!

Fortunately for us there is a table in SQL that holds the information on installed extensions. It is called ”NAV App Installed App” and you can access it from SQL Server Management Studio. It is also the only NAV App table that you can remove records from. Any changes to the other NAV App tables will result in you having to restore a backup. Be warned.

After removing the records for the offending extensions we managed to start our service tiers, remove or add the C/Side objects and reinstall said extensions. No data from said extensions was lost as they were just uninstalled.

I hope this helps you in creating your own extensions. Please share your own extension stories, I’d like to read them.

 

Interface extensions with workflows

A while ago I wrote about our Extension Design Principles. In it I discussed various ways to interface between extensions without having to take dependencies. One of these interface methods was the workflows Business Central is equipped with.

Because you can configure these workflows at run-time you can configure Workflow Events and responses from different extensions.

But how can you create new workflow events and responses? Quite easily in fact. I suggest you start by reading how you can configure existing Workflows by reading this excellent piece by ArcherPoint. That will give you a good idea about what you can do with workflows.

In the aforementioned Extension Design Principles I also spoke about how we want to move our email address validation to an API. As I said, we want a consistent check across all our ERP systems, web shops, and other platforms. But we don’t want to program a call to this API in all our extensions. And we don’t want our extensions to have to take a dependency on a base extension or something. As I said, we want to be able to have all our extensions as stand alone units in the App Source.

How to do it then? With a workflow of course. To the code!

Create the check email Azure Function

First thing I did was create an Azure Function for our email address check. As you can see in the code below it checks the formatting of the email address first and, if that is correct, checks to see if the email addresses hostname has an MX record. So a bit more reliable than the CheckValidEmailAddress function in CodeUnit 9520.
The DNS check is done with an online API. If you would consider taking something like this to production you should build or host something like it yourself. Or just use a DNS check library. Also, bear in mind that a domain does not need to have a MX record in order to be email enabled.

For the curious, you can read how to create an Azure Function in VS Code here.

After this I created a CodeUnit that calls this Azure Function. In it I work with Variants. As I said, this function needs to be callable from many different extensions and many different source records. This function is quite simple, just a simple call to the Azure Function. If it gets a response code 200 it shows a message saying the email address is correct. If the response code is 400 it throws an error.

Create the workflow response

Then of course the real issue of this blog post, how do I call this function from the workflow? For this you need to create a workflow response. Have a look in CodeUnit 1521. If you follow the code in there you see you need to do a few things to add a workflow response:

  • Create a function to add a response to the library;
  • Call that function from the event that is triggered when workflows are added to the library;
  • Call the response from the workflow using the event that is triggered when a workflow runs.

As you can see in the above image the steps are quite simple. A few highlights.

In the call to the WorkflowResponseHandling.AddResponseToLibrary you can see me use ‘GROUP 6’. These groups are used to add arguments to your workflow step. GROUP 6 adds a Field No argument. This is used to identify the email address field on the triggering record. Other groups are found on Page 1523:

  • ‘GROUP 0’: No Arguments
  • ‘GROUP 1’: Selects General Journal Template and Batch
  • ‘GROUP 2’: Selects the linked target page to open
  • ‘GROUP 3’: Create Notification
  • ‘GROUP 4’: Create Message
  • ‘GROUP 5’: Selects approval arguments
  • ‘GROUP 6’: Selects field
  • ‘GROUP 7’: Applies selected values to selected field
  • ‘GROUP 8’: Selects Response Type and User ID”

I have added a function to delete the added workflow responses from the library. This should be called on uninstallation of the extension. Unfortunately this is not yet possible with installer CodeUnits.

Once we have added the response to the library we can create an event subscriber that will run our code.

What happens here is really quite simple. The code gets the workflow response and its arguments. If the Function Name of the workflow response is one of ours we execute the code that belongs to it. The Variant and xVariant come from the workflow event and represent the record that triggered the workflow. As you see I use the Workflow Step Argument to get the Field No. that we want to use.

If the workflow response has executed, the code needs to switch the ResponseExecuted variable to true. This lets the event publisher know that the event has been dealt with.

Creating the workflow event

On the events side things work more or less the same way. First we create an event that can trigger the workflow. This particular event is added in an extension that adds a notification email address on the Job Queue Entry. Once we have created the event all we need to do is make sure that the OnValidate event on our new field triggers the workflow:

Configuring the workflow

Now we have created the workflow event and response we can configure them. The events that add the event and response to their libraries is triggered when you open the Workflows page. Nothing needs to be done for that.

Just create a new workflow. Set the event to the newly created event. Set the response to the new check email response. Just make sure that you select the email address field in the Workflow Responses.

As a bonus you can even use your response with the standard Dynamics events. As you can see in the image below the workflow trigger I use here is the change on the customer record. Unfortunately the standard language of my Business Central sandbox is Dutch so all the events are added in that language. Not a problem for most users but still something I feel Microsoft should address. As you can easily regenerate all the events and responses by clearing tables 1520 and 1521 and opening the workflows page it is not a big problem.

Conclusion

That is pretty much it. I’m sure we can all delve deeper into the workflows as I have not yet touched upon workflow templates of event predecessors yet. That is something for another day.

For now I want to urge you all to explore the workflow module and start adding events and responses to your extensions. That will make my job of integrating with them much easier.

As always, all the code is on GitHub. Happy coding!