关于UPC E条码的校验
关于UPC E条码的知识可以参照BlueSky老师的空间,非常感谢BlueSky老师的耐心讲解。
下面这篇文章是BlueSky对条码的讲解,其中第四部分是关于UPC E的。
http://user.qzone.qq.com/26425753/blog/1252850602
从BlueSky老师的讲述来看,UPC E条码应该是8位的,只不过UPC E的第一位一定是0,并且校验位用A,B子集的排列来表示(这跟EAN13的第一位的形成方式是一样的),所以条码的条空只表示6位数字,但是加上系统位0和A,B子集排列代表的校验位,条码本身代表的数字含义一定是8位的。所以我不太明白AX里在做条码设置的时候UPC E为什么规定成6位的?用扫描枪扫描出来的是8位的数字。。。
基本设置->设置->条码设置
这样给商品添加UPC-E条码的时候,就只能输入6位了,否则会报错说位数不对,问题是即便把0和校验位去掉了,输入6位数字,还是会报错说校验位不对。
BlueSky老师的一篇文章介绍了校验位的生成算法:
http://user.qzone.qq.com/26425753/blog/1251297004
在AX中UPC和EAN类型的编码的校验位算法在类BarcodeEAN_UPC的validateCheckDigit
{
boolean ret = true;
str tmp;
;
if (strlen(barCodeString) != this.strlen())
return true; // Actually an error, but has already been reported.
barcodeStr = this.encodeString(substr(barCodeString,1,this.strlen()));
tmp = Barcode::insertModulo10CheckDigit(barCodeString, this.strlen());
if (tmp != barCodeString)
ret = checkFailed(strfmt("@SYS76957", substr(barCodeString, this.strlen(), 1), substr(tmp, this.strlen(), 1)));
return ret;
}
AX的代码就是实现了BlueSky老师介绍的校验位生成算法,类BarCode的静态方法insertModulo10CheckDigit:
{
Integer checkDigit = 0;
Integer counter;
Integer temp;
Integer digit(Integer position)
{
return any2int(substr(s_in,stringLength + 1 - position,1));
}
;
counter = 2;
while (counter <= stringLength)
{
checkDigit += digit(counter);
counter += 2;
}
checkDigit = 3 * checkDigit;
counter = 3;
while (counter <= stringLength)
{
checkDigit += digit(counter);
counter += 2;
}
temp = checkDigit mod 10;
if (temp)
checkDigit = 10 - temp;
else
checkDigit = 0;
return substr(s_in,1,stringLength - 1) + num2str(checkDigit,1,0,0,0);
}
EAN8,EAN13以及UPC-A用这个算法生成校验位都没有问题,UPC-E直接用这个算法就有问题了,如果按照AX的要求输入去掉首位和校验位的六位数字,生成的校验位显然不符合要求。
UPC-E要生成校验位必须首先还原成UPC-A,然后再用上面的算法才能生成正确的校验位,还原的算法很简单,按照前面提到的BlueSky老师的文章里介绍UPC-A到UPC-E的压缩算法反过程回去就可以了,X++代码如下:
{
BarCodeString barCodeString;
;
switch(str2int(substr(_barCodeString,strlen(_barCodeString)-1,1)))
{
case 0:
case 1:
case 2:
{
barCodeString = substr(_barCodeString,1,3)
+substr(_barCodeString,strlen(_barCodeString)-1,1)
+'0000'
+substr(_barCodeString,4,3)
+substr(_barCodeString,strlen(_barCodeString),1);
break;
}
case 3:
{
barCodeString = substr(_barCodeString,1,4)
+'00000'
+substr(_barCodeString,5,2)
+substr(_barCodeString,strlen(_barCodeString),1);
break;
}
case 4:
{
barCodeString = substr(_barCodeString,1,5)
+'00000'
+substr(_barCodeString,6,1)
+substr(_barCodeString,strlen(_barCodeString),1);
break;
}
case 5:
case 6:
case 7:
case 8:
case 9:
{
barCodeString = substr(_barCodeString,1,6)
+'0000'
+substr(_barCodeString,7,2);
break;
}
default:
throw error("");
}
return barCodeString;
}
然后在类BarcodeUPCEvalidateCheckDigit
{
boolean ret;
str tmp;
str barCodeStringLocal;
;
barcodeStr = this.encodeString(substr(barCodeString,2,this.strlen()));
barCodeStringLocal = this.upce2UPCA(barCodeString);
tmp = Barcode::insertModulo10CheckDigit(barCodeStringLocal, 12);
if (substr(tmp,strlen(tmp),1) != substr(barCodeString,strlen(barCodeString),1))
ret = checkFailed(strfmt("@SYS76957", substr(barCodeString, this.strlen(), 1), substr(tmp, strlen(tmp), 1)));
return ret;
}
其实在类BarcodeEAN_UPC的encodeStr的前半部部分就是用来计算校验位的,算法大同小异,首先还原成UPC-A,然后再计算得到校验位,根据校验位得到各个数字对应的打印的字符。这个方法是按照UPC-E为六位计算得到打印字符的,所以也需要改一下,把传入的字符去头舍尾变成六位。
return '';
_stringIn = subStr(_stringIn,2,6);
修改一下表BarcodeSetup的fieldModifiedBarcodeType方法,让其当类型为UPCE时,最大和最小长度设置为8。
{
Barcode barcode;
;
switch(this.BarcodeType)
{
case BarcodeType::UPCA :
this.MinimumLength = 12;
this.MaximumLength = 12;
break;
case BarcodeType::UPCE :
this.MinimumLength = 8;
this.MaximumLength = 8;
break;
case BarcodeType::EAN13 :
this.MinimumLength = 13;
this.MaximumLength = 13;
break;
case BarcodeType::EAN8 :
this.MinimumLength = 8;
this.MaximumLength = 8;
break;
}
barcode = this.barcode();
if (this.FontName && !barcode.validateFontName(this.FontName))
this.FontName = '';
if (!this.FontName)
this.FontName = barcode.defaultFont();
}
UPCE条码应该还是有很多地方用的,AX在全世界也有很多用户,如果这是个bug的话,应该早就发现了,所以我觉得可能是我某个地方理解错了,但是又实在找不到相关文档,也只能这么从代码上这么改了,还望知道其中原委的高人指点。