“Control Add In” in Business Central: why iframes are not a good idea?

BUSINESS CENTRAL CONTROL ADD IN IFRAMES

Most of Business Central Control AddIns sample I found on the internet mostly uses the <IFRAME> to embed an external webpage or website inside Business Central page.

This solution is pretty common because it allows to easily and quickly embed the page just by using the <iframe> tag.

With the iframe the business central webpage (https://businesscentral.dynamics.com/) just embeds your own webpage (https://www.yoursite.com)

But <iframes> have a major problem. They allow cross site javascript scripting between the parent and the iframe page but they does not allow the scripting on the other direction. This is prevented for security reason, as often happens with iframes.

On the past cross scripting was used to get private information, it was enough just to embed any bank website inside an iframe to access all of its information from the parent page. So, just embedding the login page in an iframe, allowed malicious users to access credentials entered in the login page.

This means that, by using iframes, the browser JavaScript’s engine does not allow cross site scripting.

In my previous article I used in fact a different approach to implement a control addin.

“Control Add In” in Business Central: a responsive web app fully integrated in web client and mobile app:

https://businesscentraldotblog.wordpress.com/2021/02/26/control-add-in-in-business-central-a-responsive-web-app-fully-integrated-in-web-client-and-mobile-app/

With the correct solution I used this piece of code to embed an external static resource on my BC page:

I first included: ‘https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-debug.js&#8217;,

Then I loaded the page with this AJAX function :

$.ajax({ url: url, xhrFields: { withCredentials: true } )).done(function(data) { $(“#controlAddIn).text(data); });

REFERENCES: SOLUTION NOT USING FRAMES

https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-control-addin-object

REFERENCES: SOLUTIONS USING FRAMES

LAST UPDATED

27th of February, 2021

“Control Add In” in Business Central: a responsive web app fully integrated in web client and mobile app.

BUSINESS CENTRAL CONTROL ADD IN

Control AddIns looks like a fantastic solution to integrate a Business Central page with an external responsive Web Application.

The responsive website automatically adapts to the layout thus is fully working both on web client and mobile app for tablets and mobile phones.

The integration is pretty simple, in my scenario I a page includes a control AddIn that embeds a single WEB page. The page exchanges data with a Web Page. The Web Page is responsive and based on a a basic Azure Function.

The control addin allows to easily implement a clean solution that integrates business central pages with external websites.

As shown in figure the business control page implements TRIGGERS, the control addin implements the INTERFACE of the common procedures and events. The WebSite shares those procedures and events with the business central page.

This allow a two ways binding between those two components: the webpage calls the procedures declared in the control add in and implemented in the trigger page.

THE CONTROL ADDIN

The Control AddIn is the most important part of the project and is basically a gateway between the Business Page and the WebSite.

Inside the control add is specified the “StartupScript” which is a special script that the web client calls once the page is loaded.

THE “STARTUP SCRIPT”

The startup script is a normal JavaScript, the magic happens with this method: Microsoft.Dynamics.NAV.InvokeExtensibilityMethod().

In this script is possibile to invoke extensibility (CallBack methods),

What is InvokeExtensibilityMethod… As from official microsoft documentation: “InvokeExtensibilityMethod Invokes an AL trigger on the Dynamics 365 Business Central service on the page that contains the control add-in”

This file acts as “interface” between those two components: the BC page and the WebApp.

Here i declared events and procedures that are “shared” between the control addin and the page:

THE “SCRIPTS” SECTION

The Scripts property can reference both external and local scripts. I added both my local script and the external knockout library.

The knockout library allows me to use later on this way to load the external page: $.ajax({ url: url, xhrFields: { withCredentials: true } )).done(function(data) { $(“#controlAddIn).text(data); });

The local script contains a CallJavaScript method. This method is declared in the previous “StartupScript” and in consequence is accessible from the Business Central Page as I will describe later. This method is called in my webpage to raise events and call a trigger in the bc page.

THE “PAGE”

The control add-in is placed in the page.

There are two triggers in the page.

Those two triggers have the same signature as in the control add-in.

THE AZURE FUNCTION

The Azure Function returns an HTML5 page.

It contains three content files , those file are read, combined and sent back as an http response.

The interesting part is in the javascript that is called on the OnClick Event of the button.

The javascript function invokes PostToBc() and consequently the trigger in bc is raised.

IFRAME PROBLEMS

As I described on my other article, the iframe solution is not really the best one because this piece of code:

window.parent.document.CallJavaScript(‘OnBcPageCallBack’, [json]);

Does not work and cross site scripting is blocked by default by modern browsers.

https://businesscentraldotblog.wordpress.com/2021/02/28/control-add-in-in-business-central-why-iframes-are-not-a-good-idea/

Thus I used another solution to embed an external static resource on my BC page:

I first included: ‘https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-debug.js&#8217;,

Then I loaded the page with this AJAX function

$.ajax({ url: url, xhrFields: { withCredentials: true } )).done(function(data) { $(“#controlAddIn).text(data); });

SOURCES

Source codes are available:

Business Central Extension: https://github.com/avalonit/ExBcMarameoProdManager

Azure Function WebSite https://github.com/AlbertoValentiVaprime/AzWebMarameoProdManager

REFERENCES

Roberto Stefanetti and Vjieko Control addins

https://github.com/vjekob/TicTacToeAL

LAST UPDATED

26th of February, 2021

My JSON To AL Converter

JSON TO AL CONVERTER

Since I am very lazy and I didn’t find any AL code generator I decided to develop it on my own.

My app gets a JSON file as input and, for each entity present, it automatically generates three files :

1) the AL table ;

2) the AL page ;

3) the AL API page ;

The AL code is SaaS/Cloud ready (I’m not sure if it works on OnPremise, but it should) . It is targeting the current Business Central version : 17.2. I am planning to migrate it as a Visual Studio extension soon.

It’s really simple to use, you have a demo video below.

EXECUTABLE

If you don’t want to compile the VS2019 solution you can get the application executable from here:

http://www.katanet.it/download/JsonToAlConverter.zip

It requires framework installed.

SOURCES

Source code is available.

https://github.com/avalonit/JsonToAlConverter/tree/master

REFERENCES

This project is a fork of existing C# class generator from JSON.

JSON C# Class Generator (xamasoft.com)

https://www.xamasoft.com/json-class-generator/

LAST UPDATED

8th of January, 2021

Implementing OData API authorisation in Business Central v17.x (Part 2 integration with Xamarin)

ODATA

Good news: the medieval webkey authentication for Business Central API has finally been deprecated.

In this first article I presented a little overview of the secure and modern OAuth protocol with a “delegated permission” sample.

In this article I describe an “Application permissions” implementation for an application that runs as a background service or daemon without a signed-in user. I am going to describe how I refactored an existing Xamarin mobile application that is currently directly getting and posting data to Business Central APIs.

IT’S TIME TO MOVE TO AZURE

Login in Azure Portal https://portal.azure.com/ , go to “All Services”, search “App Registration” and select “App registrations” list item.

Create a new registration.

Configure your application registration name and the redirect URI As Web with the URL “https://businesscentral.dynamics.com/&#8221;.

Or even better “https://businesscentral.dynamics.com/OAuthLanding.htm

Note down the “Application (client) ID” GUID, this is the “CLIENT ID” we are going to use in Insomia.

Select “API permission” blade,

Configure as in picture, this time we are going to use a different kind of permissions: “Application permission”.

Go the “Certificate and secrets” blade.

Add a new “Client secret”

The “Client secret” and “Secret ID” will be generated,

Please notice that the “Client secret” is shown in clear text ONLY after the first generation and it will NEVER EVER be shown again. If lost you will need to generate a new one. There is not way to recover an existing “Client secret”.

Copy the “Value”, we are going to use it in Insomia as “CLIENT SECRET”.

TEST WITH INSOMNIA

Here are all mandatory parameters to make an OATH request:

Token-Name: Give the appropriate Token Name
Grant Type: Authorization Code

Callback URL: https://businesscentral.dynamics.com/

Auth URL:
https://login.windows.net/<tenant_id>/oauth2/authorize?resource=https://api.businesscentral.dynamics.com
Access Token URL:
https://login.windows.net/<tenant_id>/oauth2/token?resource=https://api.businesscentral.dynamics.com

Client ID: Client ID as in “ID” column in azure
Client Secret: Client Secret Value as in “Value” column in azure.

By using Insomnia you easily test the API:

The well known Microsoft Azure AD web page appears asking for the username.

And the password.. and it’s done.

XAMARIN SOURCES

Will post in a couple of days a sample Xamarin App that gets a list of customers from BC17x API by using OData authorisation. Stay tuned.

SOURCES

Olisterr Tech blog

https://www.olisterr.tech/2020/12/setting-up-oauth-authentication-for.html

Arend-Jan Kauffmann

How to Authenticate Through Azure Active Directory (AAD) to Use Microsoft Dynamics 365 Business Central API: https://www.1clickfactory.com/blog/how-to-authenticate-through-azure-active-directory-to-use-business-central-api/

OAuth Explained with Xamarin.Forms: https://www.codementor.io/@ravitejalingineni/oauth-explained-with-xamarin-forms-q2zomkhda

Adding Auth to your Xamarin.Forms App – Part 2: https://www.heyraviteja.com/post/projects/xam-forms-oauth-part2/

LAST UPDATED

25th of December, 2020

My “step by step” guide for implementing OData API authorisation in Business Central v17.x (Part 1)

ODATA

Good news: the medieval webkey authentication for Business Central API has finally been deprecated.

In this first article I am going to present a little overview of the secure and modern OAuth protocol with a “delegated permission” sample.

In next article I will describe an “Application permissions” implementation for an application that runs as a background service or daemon without a signed-in user. I am going to describe how I refactored an existing Xamarin mobile application that is currently directly getting and posting data to Business Central APIs.

IT’S TIME TO MOVE TO AZURE

Login in Azure Portal https://portal.azure.com/ , go to “All Services”, search “App Registration” and select “App registrations” list item.

Create a new registration.

Configure your application registration name and the redirect URI As Web with the URL “https://businesscentral.dynamics.com/&#8221;.

Or even better “https://businesscentral.dynamics.com/OAuthLanding.htm

Note down the “Application (client) ID” GUID, this is the “CLIENT ID” we are going to use in Insomia.

Select “API permission” blade,

Configure as in picture.

Go the “Certificate and secrets” blade.

Add a new “Client secret”

The “Client secret” and “Secret ID” will be generated,

Please notice that the “Client secret” is shown in clear text ONLY after the first generation and it will NEVER EVER be shown again. If lost you will need to generate a new one. There is not way to recover an existing “Client secret”.

Copy the “Value”, we are going to use it in Insomia as “CLIENT SECRET”.

TEST WITH INSOMNIA

Here are all mandatory parameters to make an OATH request:

Token-Name: Give the appropriate Token Name
Grant Type: Authorization Code

Callback URL: https://businesscentral.dynamics.com/

Auth URL:
https://login.windows.net/<tenant_id>/oauth2/authorize?resource=https://api.businesscentral.dynamics.com
Access Token URL:
https://login.windows.net/<tenant_id>/oauth2/token?resource=https://api.businesscentral.dynamics.com

Client ID: Client ID as in “ID” column in azure
Client Secret: Client Secret Value as in “Value” column in azure.

By using Insomnia you easily test the API:

The well known Microsoft Azure AD web page appears asking for the username.

And the password.. and it’s done.

SOURCES

Olisterr Tech blog

https://www.olisterr.tech/2020/12/setting-up-oauth-authentication-for.html

Arend-Jan Kauffmann

LAST UPDATED

24th of December, 2020

Consume Azure Maps API from Business Central through an Azure Function

THE RECIPE

For every Business Central customer I have to store Lat and Lng coordinates.

Starting form the customer full address, the coordinates are obtained by using Geolocation and Reverse Geolocation APIs of Azure Maps.

The coordinates are finally stored in the Customer table.

The whole procedure is implemented with a time triggered Azure Function acting as a scheduled orchestrator between Business Central API and Azure Maps API.

The next step implementation will automate this process by using webhooks: with this solution Business Central notifies the Azure Function every time a customer entity is updated and the azure function automatically and immediately update the coordinates.

ACTIVATE AND CONFIGURE AZURE MAPS ON AZURE

Login in Azure Portal and create a new source.

After resource is created just grab the primary key from “Authentication” blade.

TEST AZURE MAPS WITH INSOMNIA

The rest call to get the address’s coordinates is pretty simple:

Eg: https://atlas.microsoft.com/search/address/json?subscription-key={key}&api-version=1.0&query=Via Martiri del Popolo, 22, 50055 Lastra a Signa (FI)

By using Insomnia you easily test the API:

The JSON contains the coordinates of given address.

AZURE FUNCTION

The Azure function ProcessCustomers downloads a list of customers by using an API. For each customer it queries Azure Map service to get the lat and lon coordinates from the given address.

First it gets the list of customers by using a custom API and deserialize it.

The Customers.cs contains the business central Customer domain class converted in C# class.

Azure Map API services are used to get the lat and lon coordinates.

The full address is composed by formatting the business central address fields to obtain an Azure Map compliant address.

The Azure Map returns a JSON as represented in AzureMapResults.cs class file.

This is a typical json as returned from Azure Maps services:

The coordinates are updated in Business Central by using an http PATCH request on my custom API.

The patch request has the standard format: The If-Match header represents the entity ETag as returned from Business Central and the Query URL must contain the filter by using the primary key field (No in my case).

If the PATCH request succeeded the API returns the new updated entity.

ETAG: PAY ATTENTION!!!

The Etag is updated every time an entity in modified in Business Central. This means the Etags are loaded when the azure function is called and if an user change some field in Bc this Etag is changed too.

In this case the Http PATCH request will be denied because we try to update a record that has changed on ERP and it will return an “Http 409 Conflict” error.

SOURCES

https://github.com/avalonit/AzAPP365AzureMaps

LAST UPDATED

9th of September, 2020

Azure Cognitive and Business Central : “Form Recognizer” API integration

Easily track employee expenses (Part 2 – Business Central AL code).

How to get the receipt grand total from an image by using Azure Cognitive API and save it Business Central.

HOW TO TEST OUR CUSTOM MODEL WITH INSOMNIA

Let’s have a look on how cognitive requests work by using Insomia.

First of all I make a POST call with a Json with the format:.

https://{ my address }.cognitiveservices.azure.com//formrecognizer/v2.0-preview/custom/models/ { my model id } /analyze

The posted json contains the source URL of the blob image in source.

The header contain the Cognitive Key in “Ocp-Apim-Subscription-Key”.

You should get back a “202 – Accepted” containing Operation-Location which is the URL to be queried to get the result.

The “Operation Location” to be queried to get back the result has the following format.

https://{service name}.cognitiveservices.azure.com/formrecognizer/v2.0-preview/custom/models/{model id}/analyzeresults/{result id}

By calling with a GET the URL we finally get the tags we trained with our custom model.

BUSINESS CENTRAL AL

The codeunit 70659914 “ALV Cognitive Service API” contains the logic to call the azure cognitive API.

GetCognitive() makes the first call to cognitive to start the blob storage image processing by using custom model.

The GetCognitiveResult() queries the Cognitive API waiting for the result to be evaluated.

Source code in AL available here:

https://github.com/avalonit/ExBcCognitiveAzure

CREDITS

AL support for REST Web Services by Kauffmann

AL support for REST Web Services

LAST UPDATED

22st of July, 2020

Azure Cognitive and Business Central : “Form Recognizer” API integration

Easily track employee expenses (Part 1 – training).

How to get the receipt grand total from an image by using Azure Cognitive API and save it Business Central.

SCENARIO

  • The user, by using a mobile device, takes the photo of the receipt and uploads to an azure blob storage;
  • The Azure “Form Recognizer” API service automatically recognise the receipt grand total;
  • Business Central automatically fills the employee expense grand total;

WHAT IS NEEDED

  • A blog storage to store images.
  • An Azure Form Recognizer account.
  • A docker image to train a custom model.
  • A custom Business Central AL codeunit

AZURE COGNITIVE SERVICES

Azure cognitive services offers a variety of APIs:

https://westus2.dev.cognitive.microsoft.com/docs/services/form-recognizer-api-v2/operations/AnalyzeReceiptAsync

Among all the available features, “Form Recognizer” allows to detect part of a receipt.

At the moment “Form Recognizer”, just out of beta stage, only applies to UK/USA receipts and isn’t yet able to correctly detect grand totals with EU receipts.

This means it’s necessary to train a custom model to get better results.

https://docs.microsoft.com/en-gb/azure/cognitive-services/form-recognizer/overview#train-with-labels

To create the service is as easy as three mouse clicks: Add Resource, select “Form Recognizer” from list, configure names and location/resource group.

Once the service has been created, under Keys and Endpoints you can find “Key 1” and “Endpoint”, these two strings will be used when configuring Label Tools.

AZURE BLOB STORAGE

I create a blob container on Azure Portal and I upload there all of my receipts by using “Storage Explorer”.

It’s important to enable “Configure cross-domain resource sharing (CORS)” access.

HOW TO CREATE AND TRAIN A CUSTOM MODEL

Since the service by default recognises only US/UK receipts I have to create my own custom model.

To create a custom model I followed official microsoft instructions https://docs.microsoft.com/it-it/azure/cognitive-services/form-recognizer/quickstarts/label-tool

I installed docker, and switched to Linux containers (unfortunately the label tools requires a linux container instead of a Windows container).

Just with two lines are enough to have the container up and running:

First I pull the label tool container:

docker pull mcr.microsoft.com/azure-cognitive-services/custom-form/labeltool 

Then I start the container:

docker run -it -p 3000:80 mcr.microsoft.com/azure-cognitive-services/custom-form/labeltool eula=accept

It’s now possibile to open the web console from local browser:

http://localhost:3000/

First of all I configured connection with the blob storage, then I configured the form recognizer endpoint and API key from previous chapter.

The web application imports all of the images from blob storage.

I create my own tag as a numeric.

Now the boring part, for each of the image I manually assign the tag to the corresponding text on receipt.

Now it’s time to start the training.

After a couple of minutes I get the result of training session:

The web console allows to test the custom model on receipts by uploading them.

COMING SOON

In next article I will cover how to call the Cognitive API from a Business Central codeunit.

https://businesscentraldotblog.wordpress.com/2020/07/22/azure-cognitive-and-business-central-integrate-form-recognizer-api-and-automatically-process-receipts-by-using-al-language/

LAST UPDATED

21st of July, 2020

Delta Tokens and Deep Inserts: a smart way to use API/ODATA … A real case with Shopify and Business Central (part 1)

SCENARIO

  • I have to implement a bi-directional sync between Business Central ERP and Shopify.
  • The sync has to be a scheduled one, not a real time one, and I have to optimise as much as I can to save API requests. The solution has to work on SaaS and I expect to have many requests to handle from both sides.

CREDITS 🙂

Just while I was getting depressed because I was finding no solutions, an online API course from Arend Jan Kauffmann showed as the icing on the cake and made me discover an unbelievable feature of API: the deltaLink and the deep inserts.

WHAT ARE ODATA DELTA LINKS AND WHY I USED THEM

The annoying thing about Business Central API is the current API usage limit.

When working in production environment the Http Error “429 Too Many Requests” was starting to be my nightmare. When dealing with synchronisation procedures this limit is a persistent problem.

Delta Links is a way to get deltas from a specific table. Basically when you get records you need to save a “delta link”, this delta link allows to retrieve only records that have been modified since previous requests.

First of all I created my custom API and I enable delta : ChangeTrackingAllowed = true;

This allows me to make a request with odata.track-changes http parameters.

All the magic is in the “@odata.deltaLink” URL.

Let’s supposed, after this initial request, I insert an item, update another one and delete the third. When calling again the provided odata.deltaLink I am going to get only the deltas as in picture:

Something really important is the delta links are “one time links”, it means that they allow one single requests and, after the request is served, they expire and you get a “410 Error Gone”. This means that sync mechanist must be implemented as “state of art” because if miss a request then the procedure has to reset from the initial request.

This is the way a deleted record is returned:

I just had to take to care about the way the insert action is managed in business central: it generates two events: an initial insert event and followed by an update event. This is by design.

GITHUB SOURCES

https://github.com/avalonit/ExBcCloudFiles

DELTA LINKS

https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-connect-apps-delta

https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/properties/devenv-changetrackingallowed-property

DEEP INSERTS IN BUSINESS CENTRAL

https://www.kauffmann.nl/2020/05/05/deep-insert-with-business-central-apis/

https://www.olisterr.tech/2019/05/creating-apis-in-business-central-1.html

COMING SOON

In next article I will cover how to manage deep inserts and save API requests for SaaS.

LAST UPDATED

1st of June, 2020

Azure File, Azure Blob and Alibaba Cloud integration in Business Central (with interfaces and telemetry)

A codeunit to upload files to different containers, implemented with interfaces and monitored with telemetry

SCENARIO

  • The requirement is to replace the reference of “File Management” codeunit with a new “Azure File Management” for the SaaS (Cloud) version of Business Central.
  • I want to implement a base “app” that manages file basic operation on different cloud platforms: upload, download, create folders, copy and move files and folders.
  • I want the app to be reusable by all other app depending on it.
  • I want to monitor the activity with telemetry and application insights.

THE WONDERFUL WORLD OF INTERFACES AND TELEMETRY

This project allows me to use in the real world two of the new powerful and wonderful features of Business Central: interfaces and telemetry. Interfaces are available with a minimum of runtime 5 (you have to kiss goodbye older versions of BC).

AZURE BLOBS VS AZURE FILES

One of the requirement was to work with Azure Files, not just with Azure Blobs. The web is full of samples of Azure Blobs integration but it lacks Azure Files implementation.

As you should already know, Azure Files and Azure Blobs are two different containers and you deal with them with a different set of APIs.

THE PROJECT

First of all I implemented the interface, in my GitHub sample I only give you the code of Download() 🙂

Then I implemented Download codeunits for Azure Files and Azure Blobs.

The “ALV AzBlob Service API” contains Azure Blob implementation, to deal with blobs is really easy and with a few lines of code the job is done. This is a dummy copy/paste from Demiliani sample integrated with telemetry monitoring.

The Azure File, implemented in “ALV AzFile Service API”, drove me mad because I didn’t find any ready-to-use solution on the internet and I had to implement all by myself.

The Azure Files management involves a tricky feature to calculate a Base64Hash SharedKey by using “Cryptographic Management” codeunit. The azure file API needs an authentication procedure which is not so easy to implement from scratch in AL language. By the way, here it is.

The “ALV Application Insights Mgt.” and “ALV Application Insights SDK” allow to deal with azure application insight telemetry. The Management codeunit implements the TraceInformation(), TraceError() and all the other function to write logs data.

I added just a function to manage telemetry where I manage the telemetry duration.

GITHUB SOURCES

https://github.com/avalonit/ExBcPepperiConnector

APPLICATION INSIGHT AND TELEMETRY FOR BUSINESS CENTRAL

https://github.com/mynavblog/ApplicationInsights

http://www.mynavblog.com/category/navdevtips/

AZURE FILE API REFERERENCE

https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-requests-to-azure-storage

https://docs.microsoft.com/it-it/rest/api/storageservices/file-service-rest-api

COMING SOON

In next article I will cover how to access telemetry and how to implement Alibaba Cloud Container Download() function.

LAST UPDATED

30th of May, 2020