 Barcode initialization

Rather frequently these days (since the coming of WHS) I find myself in a situation where I require all items in a company to have at least some provisional barcode.

Also bear in mind that as long as you do not use your barcodes outside your own warehouse(s), there is no requirement to have an official gtin-range assigned.

To keep things simple (I do like my things on the simple side), here is a little job I wrote to generate a random EAN13-style barcode for each item in the current company.

static void FrenCoInitBarcodes(Args _args)
{
InventItemBarcode       iibc;
InventTable             it;
UnitOfMeasure           uom;
UnitOfMeasureConversion uomc;

ItemBarCode GenerateEAN13()
{
System.Random       random = new System.Random();
int                 ean[];
int                 i, a, b, x;
ItemBarCode         bc;

InventItemBarcode   inventItemBarcode;

do
{
bc = "";

//generate array with 12 random numbers
for (i = 1; i < 13; i++)
{
ean[i] = random.Next(10);
}

//calculate check digit
for (i = 12; i > 0; i-=2)
{
//There is a bug in AX. According to the EAN specs, the digits are numbered from right to left.
//This means that ean[12, 10, 8, 6, 4, 2] are the sum of the odd numbers (1, 3, 5, 7, 9, 11)
//Subsequently 11, 9, 7, 5, 3, 1) are the sum of the even numbers (2, 4, 6, 8, 10, 12).
//However...AX wants them left to right.
a += ean[i];                //sum of even numbers
b += ean[i-1];              //sum of odd numbers
bc += int2str(ean[i]);      //add next odd number to barcode
bc += int2str(ean[i-1]);    //add next even number to barcode
}
//( 10 - [ (3 * Odd + Even) modulo 10 ] ) modulo 10
bc += int2str((10 - ((3 * b + a) mod 10)) mod 10);  //add checkdigit to barcode

sleep(5);   //delay to compensate for writing lag
x++;
}
while (InventItemBarcode::findBarcode(bc, false, false) && x < 100);    //check if barcode is available. Give up after 100 attempts

return bc;
}

delete_from iibc;

while select uomc
join it
where uomc.Product == it.Product
&& it.ItemType  == ItemType::Item
{
uom = UnitOfMeasure::find(uomc.FromUnitOfMeasure);
select iibc
where iibc.itemId == it.ItemId
&& iibc.UnitID == uom.Symbol;
if (!iibc)
{
iibc.clear();
iibc.itemId = it.ItemId;
iibc.barcodeSetupId = 'EAN13';
iibc.description = it.productName(CompanyInfo::languageId());
iibc.inventDimId = InventDim::findOrCreateBlank().inventDimId;
iibc.itemBarCode = GenerateEAN13();
iibc.useForInput = true;
iibc.useForPrinting = true;
iibc.RetailShowForItem = true;
iibc.UnitID = uom.Symbol;
iibc.qty = uomc.Factor;
try
{
iibc.insert();
}
catch
{
exceptionTextFallThrough();
}
}

uom = UnitOfMeasure::find(uomc.ToUnitOfMeasure);
select iibc
where iibc.itemId == it.ItemId
&& iibc.UnitID == uom.Symbol;
if (!iibc)
{
iibc.clear();
iibc.itemId = it.ItemId;
iibc.barcodeSetupId = 'EAN13';
iibc.description = it.productName(CompanyInfo::languageId());
iibc.inventDimId = InventDim::findOrCreateBlank().inventDimId;
iibc.itemBarCode = GenerateEAN13();
iibc.useForInput = true;
iibc.useForPrinting = true;
iibc.RetailShowForItem = true;
iibc.UnitID = uom.Symbol;
iibc.qty = uomc.Factor;
try
{
iibc.insert();
}
catch
{
exceptionTextFallThrough();
}
}

}
info('Done!');
}

I ran into some trouble getting the above to work, and I suspect this is due to a bug in AX.
The checkdigit of the barcode is calculated by first adding the (positional) odd numbers and the (positional) even numbers, then multiplying the sum of the odd numbers by 3. According to the EAN13 spec, the digits are numbered from right to left.

 Digit 8 3 6 9 0 3 3 3 1 1 5 3 Pos. 12 11 10 9 8 7 6 5 4 3 2 1

So in the above example, the sum of the odd numbers would be aggregating positions 1, 3, 5, 7, 9 and 11: 3 + 1 + 3 + 3 + 9 + 3 =22.
Calculating the check digit this way does however not agree with AX. After some trial and error, I found that AX counts from left to right instead, aggregating positions 12, 10, 8, 6, 4 and 2 to get the sum of the odd positions.

It seems to me that this would cause loads of problems when using an official gtin-range. Personally I have not heard of such problems, so let's not exclude the possibility that I somehow misinterpreted the specs. As always, I welcome any and all feedback on this article.

This site uses Akismet to reduce spam. Learn how your comment data is processed.