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;
}