Barcodes are usually processed on-device and this is the recommended method if possible, however sometimes companies have created barcodes that do not confirm to standards (Such as the GS1 standard).

This functionality is designed to handle those non-standard cases.

This functionality should not be used in conjunction with the changes to ‘Void round-trips’ as on-server GS1 processing necessitates a round-trip every time.

 

Creating a Barcode Processor

Barcode processors are company-wide modifications that apply across all handheld transactions and enquiries. There are two elements:

  • An implementation of a processor conforming to interface IBarcodeProcessor CHHFTMN.
  • An enum called Barcode Processor CHHFTMN that ties the barcode processor implementation to a setting in Clever Device Framework Setup.

 

Implementing a Barcode Processor Codeunit

codeunit 50100 "Sample Barcode Processor" implements "IBarcodeProcessor CHHFTMN"
{
    var
        SessionMgt: Codeunit "Session Mgt. CHHFTMN";

    procedure ProcessBase64DriverData(Base64DriverData: Text)
    begin
    end;

    procedure ProcessBase64TextData(TextData: Text)
    var
        Base64Convert: Codeunit "Base64 Convert";
    begin
        if StrLen(TextData) = 0 then
            exit;
        if SessionMgt.GetBarcodeAIMId() = ']d1' then
            ProcessRev00GS1Barcode(Base64Convert.FromBase64(TextData))
        else
            ProcessGS1Barcode(Base64Convert.FromBase64(TextData));
    end;

    ...

An implementation of IBarcodeProcessor CHHFTMN must contain procedures ProcessBase64DriverData() and ProcessBase64TextData().

In the example above we are checking the AIM Id, which in this case is for a non-standard barcode ‘]d1’ (ProcessRev00GS1Barcode) or an assumed standard GS1 barcode (ProcessGS1Barcode).

The barcode data is always received as Base64 encoded from the device.

 

Extending Enum “Barcode Processor CHHFTMN”

The Enum is used in Clever Device Framework Setup to indicate the barcode processor to be used, we must extend the enum and connect it to our IBarcodeProcessor codeunit:

enumextension 50100 "Sample Bcode Prc Enum" extends "Barcode Processor CHHFTMN"
{
    value(50100; SampleBP)
    {
        Caption = 'Sample Barcode Processor';
        Implementation = "IBarcodeProcessor CHHFTMN" = "Sample Barcode Processor";
    }
}

 

Choosing a Barcode Processor

Once the custom on-server processor is complete and published, then it is simply a case of switching over to it in Clever Device Framework Setup in Business Central:

For a full example, see the appendix at the end of this document or visit the GitHub repository of samples: CleverDynamics/CleverWMSDevicesSamples (github.com)

 

Support in older versions of Business Central

For versions that do not support interfaces, developers can subscribe to the OnEvaluateBarcode() event.

OnEvaluateBarcode(Base64DriverData: Text; TextData: Text)

  

Developer Functions – Session Management Codeunit

procedure GetBarcodeAIMId(): Text

Gets the AIM Id supplied by the device, for example ]d1, ]d2 etc.

procedure GetBarcodeSymbology(): Text

Gets the Symbology supplied by the device, for example Code128 or DataMatrix.

procedure GetBarcodeDriverSymbology(): Text

Gets the raw driver Symbology as supplied by the device. It is recommended that GetBarcodeSymbology() be used instead.

procedure GetBarcodeDataFormat(): Text

Gets the Data Format as supplied by the device – for example GS1 or Text.

procedure GetFirstValidationDataItem(): Code[20]

Gets the first validation data item, potentially useful when processing non GS1 barcodes, for instance a 3of9 code where you need to parse and populate the data into the data item that the device is prompting for.

procedure GetDataItemByAppIdent(AppIdent: enum "GS1 Identifier CHHFTMN"): Text

Gets the data item by matching the GS1 Application Identifier – for example, when the application identifier has been extracted from the barcode, we can match it to a specific data item and populate the value:

AppIdent := GetNextGS1AI(BarcodeData); //Get the AppIdent

AppIdentValue := GetVariableAIValue(BarcodeData); //Get the value

DataItem := SessionMgt.GetDataItemByAppIdent(AppIdent); //Find the DataItem using AppIdent

SessionMgt.SetValidationDataItem(DataItem, AppIdentValue); //Set the value of the DataItem

procedure SetValidationDataItem(DataItemName: Code[50]; DataItemValue: Text)

Sets the data item value as a validation item. This is different to setting a data item value normally (EG SetContent(), SetResponseData()) as this is not usually set to something to be validated, which we need to do in this case.

 

On-Server Barcode Processing Complete Sample

This is a complete example of an on-server barcode processor that caters to barcodes with an incorrect AIM (rev00) and no FNC1/GS codes between variable length identifiers.

enumextension 50100 "Sample Bcode Prc Enum" extends "Barcode Processor CHHFTMN"
{
    value(50100; SampleBP)
    {
        Caption = 'Sample Barcode Processor';
        Implementation = "IBarcodeProcessor CHHFTMN" = "Sample Barcode Processor";
    }
}


codeunit 50100 "Sample Barcode Processor" implements "IBarcodeProcessor CHHFTMN"
{
    var
        SessionMgt: Codeunit "Session Mgt. CHHFTMN";

    procedure ProcessBase64DriverData(Base64DriverData: Text)
    begin
    end;

    procedure ProcessBase64TextData(TextData: Text)
    var
        Base64Convert: Codeunit "Base64 Convert";
    begin
        if StrLen(TextData) = 0 then
            exit;
        if SessionMgt.GetBarcodeAIMId() = ']d1' then
            ProcessRev00GS1Barcode(Base64Convert.FromBase64(TextData))
        else
            ProcessGS1Barcode(Base64Convert.FromBase64(TextData));
    end;

    local procedure ProcessRev00GS1Barcode(BarcodeData: Text)
    var
        LoopCount: Integer;
        AppIdent: Enum "GS1 Identifier CHHFTMN";
        AppIdentValue: Text;
    begin
        repeat
            AppIdent := GetNextGS1AI(BarcodeData);
            case AppIdent of
                AppIdent::SSC:
                    AppIdentValue := CopyStr(BarcodeData, 1, 14);
                AppIdent::BatchNumber:
                    AppIdentValue := CopyStr(BarcodeData, 1, 14);
                AppIdent::ProdDate:
                    AppIdentValue := CopyStr(BarcodeData, 1, 6);
                AppIdent::ExpirationDate:
                    AppIdentValue := CopyStr(BarcodeData, 1, 6);
                AppIdent::SerialNumber:
                    AppIdentValue := CopyStr(BarcodeData, 1, 3);
            end;
            SessionMgt.SetValidationDataItem(SessionMgt.GetDataItemByAppIdent(AppIdent),
AppIdentValue);
            BarcodeData := CopyStr(BarcodeData, StrLen(AppIdentValue) + 1);
            LoopCount += 1;
        until (BarcodeData = '') or (LoopCount >= 5);
    end;


    local procedure ProcessGS1Barcode(BarcodeData: Text)
    var
        LoopCount: Integer;
        AppIdent: Enum "GS1 Identifier CHHFTMN";
        AppIdentValue: Text;
    begin
        repeat
            AppIdent := GetNextGS1AI(BarcodeData);
            case AppIdent of
                AppIdent::SSC:
                    begin
                        AppIdentValue := CopyStr(BarcodeData, 1, 14);
                        BarcodeData := CopyStr(BarcodeData, 15);
                    end;
                AppIdent::ProdDate, AppIdent::ExpirationDate:
                    begin
                        AppIdentValue := CopyStr(BarcodeData, 1, 6);
                        BarcodeData := CopyStr(BarcodeData, 7);
                    end;
                AppIdent::BatchNumber, AppIdent::AddlProduct, AppIdent::SerialNumber:
                    AppIdentValue := GetVariableAIValue(BarcodeData);
            end;
            SessionMgt.SetValidationDataItem(SessionMgt.GetDataItemByAppIdent(AppIdent),
AppIdentValue);
            LoopCount += 1;
        until (BarcodeData = '') or (LoopCount >= 6);
    end;

    local procedure GetNextGS1AI(var BarcodeData: Text)
AppIdent: Enum "GS1 Identifier CHHFTMN"
    begin
        case CopyStr(BarcodeData, 1, 2) of
            '01', '10', '11', '17', '21':
                begin
                    AppIdent := GetAIEnum(CopyStr(BarcodeData, 1, 2));
                    BarcodeData := CopyStr(BarcodeData, 3);
                end;
            else
                case CopyStr(BarcodeData, 1, 3) of
                    '240':
                        begin
                            AppIdent := GetAIEnum(CopyStr(BarcodeData, 1, 3));
                            BarcodeData := CopyStr(BarcodeData, 4);
                        end;
                end;
        end;
    end;

    local procedure GetAIEnum(AppIdentText: Text): Enum "GS1 Identifier CHHFTMN"
    begin
        case AppIdentText of
            '01':
                exit("GS1 Identifier CHHFTMN"::SSC);
            '10':
                exit("GS1 Identifier CHHFTMN"::BatchNumber);
            '11':
                exit("GS1 Identifier CHHFTMN"::ProdDate);
            '17':
                exit("GS1 Identifier CHHFTMN"::ExpirationDate);
            '21':
                exit("GS1 Identifier CHHFTMN"::SerialNumber);
            '240':
                exit("GS1 Identifier CHHFTMN"::AddlProduct);
        end
    end;

    local procedure GetVariableAIValue(var BarcodeData: Text) AppIdentValue: Text
    begin
        if not BarcodeData.Contains(GSChar()) then begin
            AppIdentValue := BarcodeData;
            BarcodeData := '';
        end else begin
            AppIdentValue := Copystr(BarcodeData, 1, BarcodeData.IndexOf(GSChar()) - 1);
            BarcodeData := CopyStr(BarcodeData, StrLen(AppIdentValue) + 2);
        end;
    end;

    local procedure GSChar(): Char
    begin
        exit(29);
    end;

}