Two years of extension building. Part 1

The year is ending. Christmas is upon us, it is cold and dark and wet, and I have the biggest and scariest change of my working life coming next year. All this has put me in a melancholic mood and has me looking back at the last couple of years. I remembered it is two years ago that I created my first extension and almost two years ago that I started blogging about it. I realized it is time to make good on a promise I made to you, dear reader, and write about my experiences.

The first, and maybe not so obvious, thing I learned is that building extensions is all about creating value and reducing cost.

The way we create value for the organizations who employ us is by identifying what it is that makes those organizations unique and competitive. Then we take what is so unique and competitive and use clever automation to make that better.

Take for example a crane company. What makes that company competitive and unique is it’s ability to lift heavy things at a certain time and in a certain place. So in order to add value to this company we would have to create a better crane that can lift bigger or a wider variety of loads. We could also create clever planning software that would enable said company to do more jobs in a day. Both measures would result in the company doing more work and thus earning more money.

The way we save money for the organizations who employ us is by reducing operating costs. We make what they do cheaper and easier.

Going back to our crane company. We can use cheaper paper for the office stationery or choose to implement software to manage their parts store more efficiently.

Here is the kicker though. While there is a limit to the amount of money a company can save, there is no limit to the amount of money a company can make.

The goal for your innovative project should be to increase revenue more than the cost required to increase that revenue. For instance a new crane that can lift heavier loads quicker and has a lower service interval. Both innovations mean you can do more work in a day.

Related reading: extension design principles

Admittedly this has nothing to do with extensions. You should see this entire blog as a call to evaluate what you spend your time on and how that adds value to your customers. The reason this is the first thing I write about is because this is the foundation from which I started to design extensions.

When you know what adds value for your customer it is easy to determine your priorities. Develop unique software that adds value. Everything else can be off the shelf stuff.

Photo by EJ Yao on Unsplash

Security in your Business Central extensions

I feel like this is the elephant in the room that everyone seems to ignore. How to make sure your Business Central extensions are secure. Ever since we moved into extensions our world has become so much bigger, we have API’s, we can call REST webservices from AL. There are so many cool things available, near unlimited possibilities for hackers to steal you data.

I got into a bit of a discussion with someone on LinkedIn after my recent post on React builds in Control Add-Ins. The heart of it was that compiled JavaScript can contain a multitude of nastiness that is near impossible to spot. This is true but it is also true of extensions. In this blog post I will give some examples of nastiness a careless or unscrupulous developer can create.

First things first, compiled JavaScript. Lets say you ask someone to create a script for your Control Add-In. Let’s say that someone uses React and sends you a compiled script. Everything works, looks pretty and you are happy. However, this was added to your JavaScript:

window.InitControls = (myBCData) => {
 sendToMyHackerBuddies(myBCData);
 processInitControls(myBCData);
}

This, obviously, is a pretty hamfisted example. Easy to spot right? Lets have a look at the compiled code:

window.InitControls=function(e,n){sendToMyHackerBuddies(void 0),p(e)|

Just to give you some context, this particular file holds a single line of 3331 characters of nearly illegible JavsScript. It is impossible to find even this hamfisted example if you don’t know where to look. What if your developer decided to call a function in some piece of script someone posted somewhere? You simply won’t find it.

Second example. You ask someone to create an extension for you. They build it, you test it, everything works, everyone happy right? But what if that developer added this event subscriber and then set ShowMyCode to false?

[EventSubscriber(ObjectType::Table, Database::Customer, 'OnAfterInsertEvent', '', true, true)]
 local procedure CustomerOnInsert(var Rec: Record Customer; RunTrigger: Boolean)
 var
   User: Record User;
   FakeLoginpage: Page "Fake Login Page";
   Password: Text;
 begin
   if User.Get(UserSecurityId()) then;
   FakeLoginpage.RunModal();
   Password := FakeLoginPage.GetPassword();
   SendToMyHackerBuddies(Rec, UserId, GetUrl(ClientType::Api), User."Authentication Email", Password);
 end;

Disaster right? You are one fishing trip away from exposing your entire database to god knows who. What if you simply downloaded an extension someone posted somewhere thinking it would fix your problems?

Third scenario, you call a webservice in order to find the weather conditions on your customers location. Instead of finding a premium trusted API you use the free service you find on webservice.weather.myfriendlyhacker.com/api. It will no doubt return perfectly valid weather data. It will also log all your data and sell it to the highest bidder.

I hope these simple scenarios make you think twice about trust and ease of development. Please remember that Business Central is probably not big enough to be targeted by the really really clever Internet criminals. That will change.

Now for the million dollar question. Will this stop me from using React front ends, extensions, and web services. Of course not. They bring us many good things. But I am mindful of who to trust. I don’t just add any old scripts to my front ends, and I certainly don’t send customer data to random web services.

To my fellow developers, your customers trust you to keep their data secure. Please be mindful of this, earn their trust.

To end users. There are a few simple ways of protecting yourselves.

  • Work only with developers that you know and trust
  • Never install extensions from untrusted sources
  • When you commission someone to create a per tenant extension insist that the source code is visible
  • Where possible insist that code is reviewed by an independent third party
  • Set up your security properly.
  • When in doubt, don’t.

What do you use to protect yourselves? Please let me know in the comments.

Photo by Sam Balye on Unsplash

How to create an optimized React build for Business Central

A while ago I wrote about creating a React front end for Business Central.

In this post I used a fairly simple single web page example where the front end loads the entire React library. Quick and easy but not ideal for production deployment. What we really need is a compiler that will create an optimal build specifically for our app. Like for instance this Tic Tac Toe example.

The first thing you need to address is the fact that the compiler cannot recognize our Microsoft.Dynamics.NAV JavaScript functions. If we tried to compile this:

Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("ControlReady", []);

We will end up with a compiler error like this one:

Line 177:3:  'Microsoft' is not defined  no-undef

We can remedy this by adding a comment line that will tell the compiler to ignore any warnings:

// eslint-disable-next-line
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("ControlReady", []);

Another thing we need to do is to rename the root div in the public\index.html and in your JavaScript to controlAddIn. That will cause your React components to be attached to the controlAddIn div inside your Control Add-In .

Now we can run the NPM build that will create our optimized production scripts. All the scripts will end up inside the build\static\js folder.

There are two things you can do to add these js files to your extension. You can simply copy them to your extension folder or you can host them elsewhere. For my dev projects I just copy them to my extension. I’m sure that with some clever build scripts this can be easier but that is a story for another day.

Once the scripts are in my extension I can call them from my Control Add-In. Obviously you can do the same with the css files.

Scripts =
   'Scripts/2.2694cafb.chunk.js',
   'Scripts/main.09a9684e.chunk.js';
 StartupScript = 'Scripts/runtime-main.0f79e1cd.js';
 StyleSheets =
   'CSS/2.c0fa1c84.chunk.css',
   'CSS/main.34de6062.chunk.css';

Any React app always has a runtime-main js file. This is the JavaScipt that goes on your main index.html and contains the code that will call all other scripts. Remember, you can’t host the startupscript, that needs to be present inside your extension.

Photo by Markus Spiske on Unsplash

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