There is simple way how to create barcode generator for Dynamics 365 Business Central using Azure app functions.
First what we need it is
- Azure account with subscription,
- Visual Studio and
- Visual Studio code
- In my case Docker with BC container
In order to create our azure function lets head to: portal.azure.com
Press on Functions App
Then Add Function App
In finish of this step we should have Function App in list:
Next step will be with Visual Studio create function witch will be generate barcode image.
So lets open Visual studio (Visual Studio 2019) and create new project :
In Visual Studio, select New > Project from the File menu.
In the New Project dialog, select Installed, expand Visual C# > Cloud, select Azure Functions, type a Name for your project, and click OK. The function app name must be valid as a C# namespace, so don't use underscores, hyphens, or any other nonalphanumeric characters.
Write name and press create
Select Http Trigger (Function will be activated by REST request)
Autentication level you can choose some option. In my case i will choose Function
In our Visual Studio project should instal this two NuGet Package:
Change file ant class name to "GetBarcode":
and fix code in class:
Change the code to generate barcode (GetBarcode.cs):
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Drawing.Imaging;
using ZXing;
using ZXing.Common;
using System.Drawing;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Collections.Generic;
namespace CreateBarcode
{
public static class GetBarcode
{
[FunctionName("GetBarcode")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
Dictionary<string, string> reqField = JsonConvert.DeserializeObject<Dictionary<string, string>>(requestBody);
var writer = new BarcodeWriter();
writer.Format = BarcodeFormat.CODE_128;
switch (reqField["Type"])
{
case "CODE_39":
writer.Format = BarcodeFormat.CODE_39;
break;
case "CODE_128":
writer.Format = BarcodeFormat.CODE_128;
break;
case "QR_CODE":
writer.Format = BarcodeFormat.QR_CODE;
break;
case "PDF_417":
writer.Format = BarcodeFormat.PDF_417;
break;
case "UPC_A":
writer.Format = BarcodeFormat.UPC_A;
break;
case "EAN_13":
writer.Format = BarcodeFormat.EAN_13;
break;
case "CODE_93":
writer.Format = BarcodeFormat.CODE_93;
break;
case "DATA_MATRIX":
writer.Format = BarcodeFormat.DATA_MATRIX;
break;
case "MAXICODE":
writer.Format = BarcodeFormat.MAXICODE;
break;
case "EAN_8":
writer.Format = BarcodeFormat.EAN_8;
break;
case "PHARMA_CODE":
writer.Format = BarcodeFormat.PHARMA_CODE;
break;
default:
var response = new HttpResponseMessage()
{
Content = new StringContent("Error: (1) Barcode Type can be only:CODE_39; CODE_128; QR_CODE; PDF_417; UPC_A; EAN_13; CODE_93; DATA_MATRIX; MAXICODE; EAN_8; PHARMA_CODE; "),
StatusCode = HttpStatusCode.BadRequest
};
return response;
}
writer.Options = new EncodingOptions
{
Height = System.Convert.ToInt32(reqField["Height"]),
Width = System.Convert.ToInt32(reqField["Width"])
};
var barcodeBitmap = writer.Write(reqField["Value"]);
var barcodeImg = (Image)barcodeBitmap;
using (var memStream = new MemoryStream())
{
barcodeImg.Save(memStream, ImageFormat.Png);
var response = new HttpResponseMessage()
{
Content = new ByteArrayContent(memStream.ToArray()),
StatusCode = HttpStatusCode.OK,
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
return response;
}
}
}
}
Before publish to azure you can test it locally, by passing rest request to adress showing in console window.
Now lets try our function project publish to Azure. Go Build>Publish CreateBarcode. Select Azure and then Azure function App (Windows)
Finish publishing. On next screen finish cofigure because mostly time it have warning message:
Do not forget mark NuGet packages on configure.
Now we can finish publishing by pressing Publish button
After publishing return to Azure portal ant take Function App adress:
So now we can create RDLC report on Business Central.
Rep50100.ItemBarcode.al
report 50100 "Item Barcode"
{
DefaultLayout = RDLC;
RDLCLayout = './R50100.rdl';
dataset
{
dataitem(Item; Item)
{
column(Barcode; TmpTempBlob.Blob) { }
column(No; Item."No.") { }
column(Description; Item.Description) { }
trigger OnAfterGetRecord()
begin
GetBarcode(TmpTempBlob, Item."No.");
end;
}
}
requestpage
{
layout
{
area(content)
{
group(GroupName)
{
}
}
}
actions
{
area(processing)
{
}
}
}
var
TmpTempBlob: Record TempBlob temporary;
procedure GetBarcode(var BarcodeImg: Record TempBlob; Value: Text)
var
Client: HttpClient;
RequestMessage: HttpRequestMessage;
RequestContent: HttpContent;
ResponseMessage: HttpResponseMessage;
InStr: InStream;
OutStr: OutStream;
begin
BarcodeImg.RESET;
BarcodeImg.DeleteAll();
RequestMessage.Content.WriteFrom(StrSubstNo('{"Type": "%1","Height": "%2","Width": "%3","Value": "%4" }', 'CODE_128', '50', '200', Value));
Client.DefaultRequestHeaders.Add('x-functions-key', 'VUNTbrqdEBMZhuArrjsZ29MKuY4T2YRqebXvvv2XXEgeuavyz7Hg7w==');
Client.Post('https://testforbarcode.azurewebsites.net/api/GetBarcode', RequestMessage.Content, ResponseMessage);
BarcodeImg.Init;
BarcodeImg.Blob.CreateInStream(InStr);
ResponseMessage.Content.ReadAs(InStr);
BarcodeImg.Blob.CreateOutStream(OutStr);
CopyStream(OutStr, InStr);
BarcodeImg.Insert();
BarcodeImg.CALCFIELDS(Blob);
end;
}
Add to rdlc image and setup this parameter:
Run report and we have a result :)
If you run BC from Docker container be sure that in docker DNS settings are set