product_gtin.py 5.34 KB
# -*- coding: utf-8 -*-
##############################################################################
#
#    Product GTIN module for Odoo
#    Copyright (C) 2004-2011 Tiny SPRL (<http://tiny.be>).
#    Copyright (C) 2010-2011 Camptocamp (<http://www.camptocamp.at>)
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
_logger = logging.getLogger(__name__)

from openerp.osv import orm, fields
import operator


CONSTRAINT_MESSAGE = 'Error: Invalid EAN/GTIN code'
HELP_MESSAGE = ("EAN8 EAN13 UPC JPC GTIN \n"
                "http://en.wikipedia.org/wiki/Global_Trade_Item_Number")


def is_pair(x):
    return not x % 2


def check_ean8(eancode):
    """Check if the given ean code answer ean8 requirements
    For more details: http://en.wikipedia.org/wiki/EAN-8

    :param eancode: string, ean-8 code
    :return: boolean
    """
    if not eancode or not eancode.isdigit():
        return False

    if not len(eancode) == 8:
        _logger.warn('Ean8 code has to have a length of 8 characters.')
        return False

    sum = 0
    ean_len = int(len(eancode))
    for i in range(ean_len-1):
        if is_pair(i):
            sum += 3 * int(eancode[i])
        else:
            sum += int(eancode[i])
    check = 10 - operator.mod(sum, 10)
    if check == 10:
        check = 0

    return check == int(eancode[-1])


def check_upc(upccode):
    """Check if the given code answers upc requirements
    For more details:
    http://en.wikipedia.org/wiki/Universal_Product_Code

    :param upccode: string, upc code
    :return: bool
    """
    if not upccode or not upccode.isdigit():
        return False

    if not len(upccode) == 12:
        _logger.warn('UPC code has to have a length of 12 characters.')
        return False

    sum_pair = 0
    ean_len = int(len(upccode))
    for i in range(ean_len-1):
        if is_pair(i):
            sum_pair += int(upccode[i])
    sum = sum_pair * 3
    for i in range(ean_len-1):
        if not is_pair(i):
            sum += int(upccode[i])
    check = ((sum/10 + 1) * 10) - sum

    return check == int(upccode[-1])


def check_ean13(eancode):
    """Check if the given ean code answer ean13 requirements
    For more details:
    http://en.wikipedia.org/wiki/International_Article_Number_%28EAN%29

    :param eancode: string, ean-13 code
    :return: boolean
    """
    if not eancode or not eancode.isdigit():
        return False

    if not len(eancode) == 13:
        _logger.warn('Ean13 code has to have a length of 13 characters.')
        return False

    sum = 0
    ean_len = int(len(eancode))
    for i in range(ean_len-1):
        pos = int(ean_len-2-i)
        if is_pair(i):
            sum += 3 * int(eancode[pos])
        else:
            sum += int(eancode[pos])
    check = 10 - operator.mod(sum, 10)
    if check == 10:
        check = 0

    return check == int(eancode[-1])


def check_ean11(eancode):
    pass


def check_gtin14(eancode):
    pass


DICT_CHECK_EAN = {8: check_ean8,
                  11: check_ean11,
                  12: check_upc,
                  13: check_ean13,
                  14: check_gtin14,
                  }


def check_ean(eancode):
    if not eancode:
        return True
    if not len(eancode) in DICT_CHECK_EAN:
        return False
    try:
        int(eancode)
    except:
        return False
    return DICT_CHECK_EAN[len(eancode)](eancode)


class product_product(orm.Model):
    _inherit = "product.product"

    def _check_ean_key(self, cr, uid, ids):
        for rec in self.browse(cr, uid, ids):
            if not check_ean(rec.ean13):
                return False
        return True

    _columns = {
        'ean13': fields.char(
            'EAN/GTIN', size=14,
            help="Code for %s" % HELP_MESSAGE),
    }

    _constraints = [(_check_ean_key, CONSTRAINT_MESSAGE, ['ean13'])]


class product_packaging(orm.Model):
    _inherit = "product.packaging"

    def _check_ean_key(self, cr, uid, ids):
        for rec in self.browse(cr, uid, ids):
            if not check_ean(rec.ean):
                return False
        return True

    _columns = {
        'ean': fields.char(
            'EAN', size=14,
            help='Barcode number for %s' % HELP_MESSAGE),
        }

    _constraints = [(_check_ean_key, CONSTRAINT_MESSAGE, ['ean'])]


class res_partner(orm.Model):
    _inherit = "res.partner"

    def _check_ean_key(self, cr, uid, ids):
        for rec in self.browse(cr, uid, ids):
            if not check_ean(rec.ean13):
                return False
        return True

    _columns = {
        'ean13': fields.char(
            'EAN', size=14,
            help="Code for %s" % HELP_MESSAGE),
        }

    _constraints = [(_check_ean_key, CONSTRAINT_MESSAGE, ['ean13'])]