关于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

代码
protected boolean validateCheckDigit(BarCodeString barCodeString)
{
    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:

代码
public client server static str insertModulo10CheckDigit(str s_in, Integer stringLength)
{
    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++代码如下:

代码
private BarCodeString upce2UPCA(BarCodeString _barCodeString)
{
    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

代码
protected boolean validateCheckDigit(BarCodeString barCodeString)
{
    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为六位计算得到打印字符的,所以也需要改一下,把传入的字符去头舍尾变成六位。

if (len != this.strlen())
        
return '';

_stringIn 
= subStr(_stringIn,2,6);

修改一下表BarcodeSetup的fieldModifiedBarcodeType方法,让其当类型为UPCE时,最大和最小长度设置为8。

代码
public void fieldModifiedBarcodeType()
{
    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的话,应该早就发现了,所以我觉得可能是我某个地方理解错了,但是又实在找不到相关文档,也只能这么从代码上这么改了,还望知道其中原委的高人指点。

原文地址:https://www.cnblogs.com/Farseer1215/p/1733560.html