Barcode Generator

<< Click to Display Table of Contents >>

Navigation:  Zetadocs SDK Guide > Creating a Per Tenant Extension >

Barcode Generator

 

Zetadocs AutoLink allows you to archive documents from the Zetadocs Document Queue against records in Business Central. The logic is usually reliant on use of barcodes. e.g. As soon as a supplier invoice has been entered onto Business Central, a barcoded label is produced by a small desktop printer for sticking onto the invoice. It can then be scanned and automatically linked to the relevant transaction in Business Central. This topic describes how to produce the barcode label.

 

How to Generate Barcodes in Business Central

There are three main steps to this process:

 

  1. Create Per Tenant Extension to Generate Barcodes.
  2. Create a Custom Report to Print the Barcode
  3. Add a Button to a Page in Business Central to run the Report

 

Note: That these instructions are not dependent on Zetadocs being installed.

 

  1. Create Per Tenant Extension to Generate Barcodes

Create Barcode Table as below, this table stores the Barcode values and the image produced from a Barcode Web API, and is used by the report later:

 

table 50690 Barcode

{

 

    fields

    {

        field(1; PrimaryKey; Guid)

        {

            Caption = 'Primary Key';

            DataClassification = SystemMetadata;

        }

        field(2ValueText[250])

        {

            Caption = 'Value';

            DataClassification = CustomerContent;

        }

        field(3TypeOption)

        {

            Caption = 'Type';

            DataClassification = SystemMetadata;

            //If you want to use the Barcodes4me API then uncomment the code below.

            // Barcodes4me WebAPI

            //OptionMembers = " ",c39,c128a,c128b,c128c,i2of5,qr;

            //OptionCaption = 'Select a Type,Code 39,Code 128a,Code 128b,Code 128c,2 of 5 Interleaved,QR Code';

 

            //If you want to use the Barcodes4me API then comment out the code below.

            // Tec-IT WebAPI

            OptionMembers = " ",Code11,QRCode,Code128,Code25IL,Code39,Code39FullASCII,Code93,EANUCC128,MSI,TelepenAlpha,EAN8,EAN13,UPCA,UPCE;

            OptionCaption = 'Select a Type,Code 11,QR Code,Code 128,2 of 5 Interleaved,Code 39,Code 39 Full ASCII,Code 93,EAN 128,MSI,Telepen Alpha,EAN 8,EAN 13,UPCA,UPCE';

        }

        field(4; Width; Option)

        {

            Caption = 'Width';

            DataClassification = SystemMetadata;

            //If you want to use the Barcodes4me API then uncomment the code below.

            // Barcodes4me WebAPI

            //InitValue = 250;

 

            //If you want to use the Barcodes4me API then comment out the code below.

            // Tec-IT WebAPI

            OptionCaption = 'Narrow, Normal, Wide';

            OptionMembers = "1","2","3";

 

        }

        field(5; Height; Integer)

        {

            Caption = 'Height';

            DataClassification = SystemMetadata;

            InitValue = 100;

        }

        field(6; IncludeText; Boolean)

        {

            Caption = 'Include Text';

            DataClassification = SystemMetadata;

        }

        field(7; Border; Boolean)

        {

            Caption = 'Border';

            DataClassification = SystemMetadata;

        }

        field(8; ReverseColors; Boolean)

        {

            Caption = 'Reverse Colors';

            DataClassification = SystemMetadata;

        }

        field(9; ECCLevel; Option)

        {

            Caption = 'ECC Level';

            DataClassification = SystemMetadata;

            //If you want to use the Barcodes4me API then uncomment the code below.

            // Barcodes4me WebAPI

            //OptionMembers = "0","1","2","3";

 

            //If you want to use the Barcodes4me API then comment out the code below.

            // Tec-IT WebApi

            OptionMembers = "L","M","Q","H";

            OptionCaption = 'Low (L),Medium-Low (M),Medium-High (Q),High (H)';

        }

        field(10; Size; Option)

        {

 

            DataClassification = SystemMetadata;

            //If you want to use the Barcodes4me API then uncomment the code below.

            // Barcodes4me WebAPI

            // Caption = 'Size';

            //OptionMembers = "1","2","3","4","5","6","7","8","9","10";

            //OptionCaption = '21x21,42x42,63x63,84x84,105x105,126x126,147x147,168x168,189x189,210x210';

 

            //If you want to use the Barcodes4me API then comment out the code below.

            // Tec-IT WebAPI

            Caption = 'DPI';

            OptionMembers = "72","96","128";

            OptionCaption = '72,96,128';

        }

        field(11; PictureType; Option)

        {

            Caption = 'Picture Type';

            DataClassification = SystemMetadata;

            OptionMembers = png,gif,jpg;

            OptionCaption = 'png,gif,jpg';

        }

        field(12; Picture; Media)

        {

            Caption = 'Picture';

            DataClassification = SystemMetadata;

        }

 

    }

 

    keys

    {

        key(PK; PrimaryKey)

        {

            Clustered = false;

        }

    }

 

    trigger OnInsert();

    begin

        PrimaryKey := CreateGuid;

        GenerateBarcode();

    end;

 

    trigger OnModify();

    begin

        GenerateBarcode();

    end;

 

    procedure GenerateBarcode()

    var

        GenerateBarcodeCode: Codeunit GenerateBarcode;

    begin

        GenerateBarcodeCode.GenerateBarcode(Rec);

    end;

 

}

 

Create a second table to store the Web Service parameters:

 

table 50691 RESTWebServiceArguments

{

 

    fields

    {

        field(1; PrimaryKey; Integer) DataClassification = SystemMetadata; }

        field(2; RestMethod; Option)

        {

            DataClassification = SystemMetadata;

            OptionMembers = get,post,delete,patch,put;

        }

        field(3; URL; Text[250]DataClassification = SystemMetadata; }

        field(4; Accept; Text[30]DataClassification = SystemMetadata; }

        field(5; ETag; Text[250]DataClassification = SystemMetadata; }

        field(6; UserName; text[50]DataClassification = SystemMetadata; }

        field(7; Password; text[50]DataClassification = SystemMetadata; }

        field(100BlobBlob) DataClassification = SystemMetadata; }

    }

 

    keys

    {

        key(PK; PrimaryKey)

        {

            Clustered = true;

        }

    }

 

    var

        RequestContent: HttpContent;

        RequestContentSet: Boolean;

        ResponseHeaders: HttpHeaders;

 

    procedure SetRequestContent(var valueHttpContent)

    begin

        RequestContent := value;

        RequestContentSet := true;

    end;

 

    procedure HasRequestContent()Boolean

    begin

        exit(RequestContentSet);

    end;

 

    procedure GetRequestContent(var valueHttpContent)

    begin

        value := RequestContent;

    end;

 

    procedure SetResponseContent(var valueHttpContent)

    var

        InStr: InStream;

        OutStr: OutStream;

    begin

        Blob.CreateInStream(InStr);

        value.ReadAs(InStr);

 

        Blob.CreateOutStream(OutStr);

        CopyStream(OutStr, InStr);

 

    end;

 

    procedure HasResponseContent()Boolean

    begin

        exit(Blob.HasValue);

    end;

 

    procedure GetResponseContent(var valueHttpContent)

    var

        InStr: InStream;

    begin

        Blob.CreateInStream(InStr);

        value.Clear();

        value.WriteFrom(InStr);

    end;

 

    procedure GetResponseContentAsText() ReturnValue: text

    var

        InStr: InStream;

        Line: text;

    begin

        if not HasResponseContent then

            exit;

 

        Blob.CreateInStream(InStr);

        InStr.ReadText(ReturnValue);

 

        while not InStr.EOS do begin

            InStr.ReadText(Line);

            ReturnValue += Line;

        end;

    end;

 

    procedure SetResponseHeaders(var valueHttpHeaders)

    begin

        ResponseHeaders := value;

    end;

 

    procedure GetResponseHeaders(var valueHttpHeaders)

    begin

        value := ResponseHeaders;

    end;

 

}

 

 

Next create Codeunits used to call the Web Service, firstly a method to handle the JSON:

 

codeunit 50691 JSONMethods

{

    procedure GetJsonValueAsText(var JSonObject:JsonObject; Property:Text) Value:text

    var

        JsonValue:JsonValue;

    begin

        if not GetJsonValue(JSonObject,Property,JsonValue) then

            exit;

        Value := JsonValue.AsText;

    end;

 

    procedure GetJsonValue(var JSonObject:JsonObject; Property:Textvar JsonValue:JsonValue) :Boolean

    var

        JsonToken:JsonToken;

    begin

        if not JSonObject.Get(Property,JsonToken) then

            exit;

        JsonValue := JsonToken.AsValue();

        exit(true);

    end;

 

}

 

The next codeunit to create is for calling REST web services:

 

codeunit 50692 RESTWebServiceCode

{

    procedure CallRESTWebService(var Parameters: Record RESTWebServiceArguments)Boolean

    var

       Base64Convert: Codeunit "Base64 Convert";

       TempBlob: Codeunit "Temp Blob";

       Client: HttpClient;

       Headers: HttpHeaders;

       RequestMessage: HttpRequestMessage;

       ResponseMessage: HttpResponseMessage;

       Content: HttpContent;

       AuthText: Text;

       convertOutputText: Text;

       blobInStream: InStream;

       blobOutStream: OutStream;

    begin

        RequestMessage.Method := Format(Parameters.RestMethod);

        RequestMessage.SetRequestUri(Parameters.URL);

 

        RequestMessage.GetHeaders(Headers);

 

        if Parameters.Accept <> '' then

            Headers.Add('Accept', Parameters.Accept);

 

        if Parameters.UserName <> '' then begin

            AuthText := StrSubstNo('%1:%2', Parameters.UserName, Parameters.Password);

            TempBlob.CreateOutStream(blobOutStream, TextEncoding::Windows);

           blobOutStream.WriteText(AuthText);

           TempBlob.CreateInStream(blobInstream, TextEncoding::Windows);

           convertOutputText := Base64Convert.ToBase64(blobInstream);

           Headers.Add('Authorization', StrSubstNo('Basic %1', convertOutputText));

        end;

 

        if Parameters.ETag <> '' then

            Headers.Add('If-Match', Parameters.ETag);

 

        if Parameters.HasRequestContent then begin

            Parameters.GetRequestContent(Content);

            RequestMessage.Content := Content;

        end;

 

        Client.Send(RequestMessage, ResponseMessage);

 

        Headers := ResponseMessage.Headers;

        Parameters.SetResponseHeaders(Headers);

 

        Content := ResponseMessage.Content;

        Parameters.SetResponseContent(Content);

 

        EXIT(ResponseMessage.IsSuccessStatusCode);

    end;

}

 

 

The next codeunit to create is for calling the specific Web API that will generate the barcode image, the code contains calls to two different web API’s, uncomment the Barcodes4me code if you would prefer to use that API instead of the Tec-IT.

 

Note: That you will need to un/comment code in the Barcode Table as well.

 

codeunit 50690 GenerateBarcode

{

    procedure GenerateBarcode(var Barcode: Record Barcode)Boolean

    begin

        exit(DoGenerateBarcode(Barcode));

    end;

 

    local procedure DoGenerateBarcode(var Barcode: Record Barcode)Boolean

    var

        Arguments: Record RESTWebServiceArguments temporary;

    begin

        InitArguments(Arguments, Barcode);

        if not CallWebService(Argumentsthen begin

            exit(false);

        end;

 

        SaveResult(Arguments, Barcode);

        exit(true);

    end;

 

    local procedure InitArguments(var Arguments: Record RESTWebServiceArguments temporary; Barcode: Record Barcode)

    var

        TypeHelper: Codeunit "Type Helper";

        BaseURL: Text;

    begin

        //Depending on the WebAPI you want ot call you should uncomment the code below, currently the Tec-It webapi 

        //is being used, if you want to use the Barcodes4me Web API cal lthen uncomment the code below and comment

        //out the code between the Tec-It comments.

 

        // Barcodes4me WebAPI - Start

        // BaseURL := 'http://barcodes4.me';

        // if Barcode.Type = Barcode.Type::qr then

        //     Arguments.URL := StrSubstNo('%1/barcode/qr/qrcode.%3?value=%2?size=%4?ecclevel=%5',

        //                                 BaseURL,

        //                                 TypeHelper.UrlEncode(Barcode.Value),

        //                                 GetOptionStringValue(Barcode.PictureType, Barcode.FieldNo(PictureType)),

        //                                 GetOptionStringValue(Barcode.Size, Barcode.FieldNo(Size)),

        //                                 GetOptionStringValue(Barcode.ECCLevel, Barcode.FieldNo(ECCLevel)))

        // else

        //     Arguments.URL := StrSubstNo('%1/barcode/%2/%3.%4?IsTextDrawn=%5&IsBorderDrawn=%6&IsReverseColor=%7',

        //                                 BaseURL,

        //                                 GetOptionStringValue(Barcode.Type, Barcode.FieldNo(Type)),

        //                                 TypeHelper.UrlEncode(Barcode.Value),

        //                                 GetOptionStringValue(Barcode.PictureType, Barcode.FieldNo(PictureType)),

        //                                 Format(Barcode.IncludeText, 0, 2),

        //                                 Format(Barcode.Border, 0, 2),

        //                                 Format(Barcode.ReverseColors, 0, 2));

        //Barcodes4me WebAPI-End

 

        // Tec-It WebAPI - Start

        BaseURL := 'https://barcode.tec-it.com/barcode.ashx?data=';

 

        if (Barcode.Type = Barcode.Type::QRCodethen

            Arguments.URL := StrSubstNo('%1%2&code=%3&unit=Px&dpi=%4&imagetype=%5&modulewidth=%6&eclevel=&7',

                                                    BaseURL,

                                                    TypeHelper.UrlEncode(Barcode.Value),

                                                    GetOptionStringValue(Barcode.Type, Barcode.FieldNo(Type)),

                                                    GetOptionStringValue(Barcode.Size, Barcode.FieldNo(Size)),

                                                    GetOptionStringValue(Barcode.PictureType, Barcode.FieldNo(PictureType)),

                                                    GetOptionStringValue(Barcode.Width, Barcode.FieldNo(Width)),

                                                    GetOptionStringValue(Barcode.ECCLevel, Barcode.FieldNo(ECCLevel)))

        else

            Arguments.URL := StrSubstNo('%1%2&code=%3&unit=Px&dpi=%4&imagetype=%5&modulewidth=%6',

                                        BaseURL,

                                        TypeHelper.UrlEncode(Barcode.Value),

                                        GetOptionStringValue(Barcode.Type, Barcode.FieldNo(Type)),

                                        GetOptionStringValue(Barcode.Size, Barcode.FieldNo(Size)),

                                        GetOptionStringValue(Barcode.PictureType, Barcode.FieldNo(PictureType)),

                                        GetOptionStringValue(Barcode.Width, Barcode.FieldNo(Width)));

        // Tec-It WebAPI - End

 

        Arguments.RestMethod := Arguments.RestMethod::get;

    end;

 

    local procedure CallWebService(var Arguments: Record RESTWebServiceArguments temporarySuccess: Boolean

    var

        RESTWebService: codeunit RESTWebServiceCode;

    begin

        Success := RESTWebService.CallRESTWebService(Arguments);

    end;

 

    local procedure SaveResult(var Arguments: Record RESTWebServiceArguments temporaryvar Barcode: Record Barcode)

    var

        ResponseContent: HttpContent;

        InStr: InStream;

        TempBlob: Codeunit "Temp Blob";

    begin

        Arguments.GetResponseContent(ResponseContent);

        TempBlob.CreateInStream(InStr);

        ResponseContent.ReadAs(InStr);

        Clear(Barcode.Picture);

        Barcode.Picture.ImportStream(InStr, Barcode.Value);

        Barcode.Modify();

    end;

 

    local procedure GetOptionStringValue(ValueInteger; fieldno: Integer)Text

    var

        FieldRec: Record Field;

    begin

        FieldRec.Get(Database::Barcode, fieldno);

        exit(SelectStr(Value + 1, FieldRec.OptionString));

    end;

}

 

 

  1. Creating a Custom Report

We need to create a Report to print the generated barcode, this involves two files, a new Report, and a new Custom Report Layout. More information about creating reports can be found on the Microsoft site here.

 

The code below is for the report that prints out the barcode, if you want more fields than just the barcode then you can add further columns under the dataset/dataitem section:

 

report 50693 ItemListBarcode

{

    UsageCategory = Administration;

    ApplicationArea = All;

    DefaultLayout = RDLC;

    RDLCLayout = 'ItemListBarcode.rdl';

 

    dataset

    {

 

        dataitem(DataItemName; Barcode)

 

        {

            column(Picture; Picture)

            {

 

            }

 

        }

 

    }

 

    requestpage

    {

        layout

        {

            area(Content)

            {

 

            }

        }

 

        actions

        {

            area(processing)

            {

                action(ActionName)

                {

                    ApplicationArea = All;

 

                }

            }

        }

    }

 

}

 

 

Next, create the Custom Report Layout that the report uses, create the following file:

 

Note: More information about custom report layouts from Microsoft can be found here.

 

This file should be named to match the RDLCLayout value in the previous codeunit ‘ItemListBarcode.rdl’

 

<?xml version="1.0" encoding="utf-8"?>

<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">

  <AutoRefresh>0</AutoRefresh>

  <DataSources>

    <DataSource Name="DataSource">

      <ConnectionProperties>

        <DataProvider>SQL</DataProvider>

        <ConnectString />

      </ConnectionProperties>

      <rd:SecurityType>None</rd:SecurityType>

      <rd:DataSourceID>af7a58e2-0a8d-4063-95f6-056a69099326</rd:DataSourceID>

    </DataSource>

  </DataSources>

  <ReportSections>

    <ReportSection>

      <Body>

        <ReportItems>

          <Tablix Name="Tablix1">

            <TablixBody>

              <TablixColumns>

                <TablixColumn>

                  <Width>3.02778in</Width>

                </TablixColumn>

              </TablixColumns>

              <TablixRows>

                <TablixRow>

                  <Height>0.77084in</Height>

                  <TablixCells>

                    <TablixCell>

                      <CellContents>

                        <Image Name="Image1">

                          <Source>Database</Source>

                          <Value>=Fields!Picture.Value</Value>

                          <MIMEType>image/png</MIMEType>

                          <Sizing>FitProportional</Sizing>

                          <Style>

                            <Border>

                              <Style>None</Style>

                            </Border>

                          </Style>

                        </Image>

                      </CellContents>

                    </TablixCell>

                  </TablixCells>

                </TablixRow>

              </TablixRows>

            </TablixBody>

            <TablixColumnHierarchy>

              <TablixMembers>

                <TablixMember />

              </TablixMembers>

            </TablixColumnHierarchy>

            <TablixRowHierarchy>

              <TablixMembers>

                <TablixMember>

                  <Group Name="Details" />

                </TablixMember>

              </TablixMembers>

            </TablixRowHierarchy>

            <DataSetName>DataSet_Result</DataSetName>

            <Top>0.125in</Top>

            <Left>0.20833in</Left>

            <Height>0.77084in</Height>

            <Width>3.02778in</Width>

            <Style>

              <Border>

                <Style>None</Style>

              </Border>

            </Style>

          </Tablix>

        </ReportItems>

        <Height>0.98959in</Height>

        <Style />

      </Body>

      <Width>7.03125in</Width>

      <Page>

        <Style />

      </Page>

    </ReportSection>

  </ReportSections>

  <Code>Public Function BlankZero(ByVal Value As Decimal)

    if Value = 0 then

        Return ""

    end if

    Return Value

End Function

 

Public Function BlankPos(ByVal Value As Decimal)

    if Value &gt; 0 then

        Return ""

    end if

    Return Value

End Function

 

Public Function BlankZeroAndPos(ByVal Value As Decimal)

    if Value &gt;= 0 then

        Return ""

    end if

    Return Value

End Function

 

Public Function BlankNeg(ByVal Value As Decimal)

    if Value &lt; 0 then

        Return ""

    end if

    Return Value

End Function

 

Public Function BlankNegAndZero(ByVal Value As Decimal)

    if Value &lt;= 0 then

        Return ""

    end if

    Return Value

End Function

</Code>

  <Language>=User!Language</Language>

  <ConsumeContainerWhitespace>true</ConsumeContainerWhitespace>

  <rd:ReportUnitType>Inch</rd:ReportUnitType>

  <rd:ReportID>0eeb6585-38ae-40f1-885b-8d50088d51b4</rd:ReportID>

  <DataSets>

    <DataSet Name="DataSet_Result">

      <Fields>

        <Field Name="Picture">

          <DataField>Picture</DataField>

        </Field>

      </Fields>

      <Query>

        <DataSourceName>DataSource</DataSourceName>

        <CommandText />

      </Query>

    </DataSet>

  </DataSets>

</Report>

 

Finally create the permission sets needed to access the two new tables create, this should be in your root folder:

 

<PermissionSets>

  <PermissionSet RoleID="BarcodeRoleId" RoleName="BarcodeRoleId">

    <Permission>

      <ObjectType>TableData</ObjectType>

      <ObjectID>50690</ObjectID>     

      <ReadPermission>Yes</ReadPermission>

      <InsertPermission>Yes</InsertPermission>

      <ModifyPermission>Yes</ModifyPermission>

      <DeletePermission>Yes</DeletePermission>

      <ExecutePermission>No</ExecutePermission>         

      <SecurityFilter />

    </Permission>

    <Permission>

      <ObjectType>TableData</ObjectType>

      <ObjectID>50691</ObjectID>     

      <ReadPermission>Yes</ReadPermission>

      <InsertPermission>Yes</InsertPermission>

      <ModifyPermission>Yes</ModifyPermission>

      <DeletePermission>Yes</DeletePermission>

      <ExecutePermission>No</ExecutePermission>         

      <SecurityFilter />

    </Permission>

  </PermissionSet>

  </PermissionSets>

 

  1. Adding a Generate Barcode Button to Business Central

The final step is to create the Page Extension to call the Barcode Generator, the example below is to add a button to the Zetadocs group under the Actions menu, it is possible to make this modification to any page.

 

The important code happens in the OnAction trigger, here we are setting the parameters for the Barcode creation, if you want to generate a different Barcode type, say QR Code you can select a different value, so for example:

 

Barcode.Type := Barcode.Type::QRCode;

 

Note: Be careful when setting the Barcode.Value that this matches the format setup in the Autolinking Per Tenant Extension, otherwise the record will not be matched.

 

pageextension 50690 "Custom Purchase Order" extends "Purchase Order"

{

    actions

    {

        addfirst("Zetadocs")

        {

            action("Generate Barcode")

            {

                Caption = 'Generate Barcode';

                Image = BarCode;

                ApplicationArea = All;

                ToolTip = 'Generate a Barcode';

                Visible = true;

 

                trigger OnAction()

                var

                    Barcode: Record Barcode;

                    GenerateBarcode: Codeunit GenerateBarcode;

                begin

                    //Create new Barcode record to store values.

                    Barcode.Init();

                    Barcode.PrimaryKey := CreateGuid();

                    //Setting the DPI to 96

                    Barcode.Size := Barcode.Size::"96";

                    //Setting to Code 3 of 9 format

                    //Change this value if you want a different barcode type.

                    Barcode.Type := Barcode.Type::Code39;

                    //Setting the value to encode to the Purchase Invoice Number

                    Barcode.Value := StrSubstNo('ZD-PO%1', Rec."No.");

                    //Output format of image is png, can be jpg or gif.

                    Barcode.PictureType := Barcode.PictureType::png;

                    //True/false flag if you want text under the barcode

                    Barcode.IncludeText := true;

                    Barcode.Border := false;

                    Barcode.ReverseColors := false;

                    Barcode.Insert();

                    Commit();

 

                    //Generate Barcode Image

                    if (GenerateBarcode.GenerateBarcode(Barcode)) then begin

                        Commit();

 

                        //Run report to print Barcode

                        Barcode.SetFilter(PrimaryKey, Barcode.PrimaryKey);

                        Report.RunModal(50693, false, true, Barcode);

 

                        //Delete Record as no longer needed

                        Barcode.Delete();

                        Commit();

                    end else begin

                        //Optional: Error message for Barcode4me WebAPI calls

                        Message(StrSubstNo('%1 is an invalid barcode value for barcode type %2.', Barcode.Value, Barcode.Type));

                    end;

 

                end;

            }

        }

    }

}