mirror of
https://github.com/bringout/oca-ocb-technical.git
synced 2026-04-18 07:52:02 +02:00
19.0 vanilla
This commit is contained in:
parent
5faf7397c5
commit
2696f14ed7
721 changed files with 220375 additions and 91221 deletions
|
|
@ -0,0 +1,215 @@
|
|||
import re
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools.barcode import check_barcode_encoding, get_barcode_check_digit
|
||||
|
||||
|
||||
UPC_EAN_CONVERSIONS = [
|
||||
('none', 'Never'),
|
||||
('ean2upc', 'EAN-13 to UPC-A'),
|
||||
('upc2ean', 'UPC-A to EAN-13'),
|
||||
('always', 'Always'),
|
||||
]
|
||||
|
||||
|
||||
class BarcodeNomenclature(models.Model):
|
||||
_name = 'barcode.nomenclature'
|
||||
_description = 'Barcode Nomenclature'
|
||||
|
||||
name = fields.Char(string='Barcode Nomenclature', required=True, help='An internal identification of the barcode nomenclature')
|
||||
rule_ids = fields.One2many('barcode.rule', 'barcode_nomenclature_id', string='Rules', help='The list of barcode rules')
|
||||
upc_ean_conv = fields.Selection(
|
||||
UPC_EAN_CONVERSIONS, string='UPC/EAN Conversion', required=True, default='always',
|
||||
help="UPC Codes can be converted to EAN by prefixing them with a zero. This setting determines if a UPC/EAN barcode should be automatically converted in one way or another when trying to match a rule with the other encoding.")
|
||||
|
||||
@api.model
|
||||
def sanitize_ean(self, ean):
|
||||
""" Returns a valid zero padded EAN-13 from an EAN prefix.
|
||||
|
||||
:type ean: str
|
||||
"""
|
||||
ean = ean[0:13].zfill(13)
|
||||
return ean[0:-1] + str(get_barcode_check_digit(ean))
|
||||
|
||||
@api.model
|
||||
def sanitize_upc(self, upc):
|
||||
""" Returns a valid zero padded UPC-A from a UPC-A prefix.
|
||||
|
||||
:type upc: str
|
||||
"""
|
||||
return self.sanitize_ean('0' + upc)[1:]
|
||||
|
||||
def match_pattern(self, barcode, pattern):
|
||||
"""Checks barcode matches the pattern and retrieves the optional numeric value in barcode.
|
||||
|
||||
:param barcode:
|
||||
:type barcode: str
|
||||
:param pattern:
|
||||
:type pattern: str
|
||||
:return: an object containing:
|
||||
- value: the numerical value encoded in the barcode (0 if no value encoded)
|
||||
- base_code: the barcode in which numerical content is replaced by 0's
|
||||
- match: boolean
|
||||
:rtype: dict
|
||||
"""
|
||||
match = {
|
||||
'value': 0,
|
||||
'base_code': barcode,
|
||||
'match': False,
|
||||
}
|
||||
|
||||
barcode = barcode.replace('\\', '\\\\').replace('{', '\\{').replace('}', '\\}').replace('.', '\\.')
|
||||
numerical_content = re.search("[{][N]*[D]*[}]", pattern) # look for numerical content in pattern
|
||||
|
||||
if numerical_content: # the pattern encodes a numerical content
|
||||
num_start = numerical_content.start() # start index of numerical content
|
||||
num_end = numerical_content.end() # end index of numerical content
|
||||
value_string = barcode[num_start:num_end - 2] # numerical content in barcode
|
||||
|
||||
whole_part_match = re.search("[{][N]*[D}]", numerical_content.group()) # looks for whole part of numerical content
|
||||
decimal_part_match = re.search("[{N][D]*[}]", numerical_content.group()) # looks for decimal part
|
||||
whole_part = value_string[:whole_part_match.end() - 2] # retrieve whole part of numerical content in barcode
|
||||
decimal_part = "0." + value_string[decimal_part_match.start():decimal_part_match.end() - 1] # retrieve decimal part
|
||||
if whole_part == '':
|
||||
whole_part = '0'
|
||||
if whole_part.isdigit():
|
||||
match['value'] = int(whole_part) + float(decimal_part)
|
||||
|
||||
match['base_code'] = barcode[:num_start] + (num_end - num_start - 2) * "0" + barcode[num_end - 2:] # replace numerical content by 0's in barcode
|
||||
match['base_code'] = match['base_code'].replace("\\\\", "\\").replace("\\{", "{").replace("\\}", "}").replace("\\.", ".")
|
||||
pattern = pattern[:num_start] + (num_end - num_start - 2) * "0" + pattern[num_end:] # replace numerical content by 0's in pattern to match
|
||||
match['match'] = re.match(pattern, match['base_code'][:len(pattern)])
|
||||
|
||||
return match
|
||||
|
||||
def parse_barcode(self, barcode):
|
||||
if re.match(r'^urn:', barcode):
|
||||
return self.parse_uri(barcode)
|
||||
return self.parse_nomenclature_barcode(barcode)
|
||||
|
||||
def parse_nomenclature_barcode(self, barcode):
|
||||
""" Attempts to interpret and parse a barcode.
|
||||
|
||||
:param barcode:
|
||||
:type barcode: str
|
||||
:return: A object containing various information about the barcode, like as:
|
||||
|
||||
- code: the barcode
|
||||
- type: the barcode's type
|
||||
- value: if the id encodes a numerical value, it will be put there
|
||||
- base_code: the barcode code with all the encoding parts set to
|
||||
zero; the one put on the product in the backend
|
||||
|
||||
:rtype: dict
|
||||
"""
|
||||
parsed_result = {
|
||||
'encoding': '',
|
||||
'type': 'error',
|
||||
'code': barcode,
|
||||
'base_code': barcode,
|
||||
'value': 0,
|
||||
}
|
||||
|
||||
for rule in self.rule_ids:
|
||||
cur_barcode = barcode
|
||||
if rule.encoding == 'ean13' and check_barcode_encoding(barcode, 'upca') and self.upc_ean_conv in ['upc2ean', 'always']:
|
||||
cur_barcode = '0' + cur_barcode
|
||||
elif rule.encoding == 'upca' and check_barcode_encoding(barcode, 'ean13') and barcode[0] == '0' and self.upc_ean_conv in ['ean2upc', 'always']:
|
||||
cur_barcode = cur_barcode[1:]
|
||||
|
||||
if not check_barcode_encoding(barcode, rule.encoding):
|
||||
continue
|
||||
|
||||
match = self.match_pattern(cur_barcode, rule.pattern)
|
||||
if match['match']:
|
||||
if rule.type == 'alias':
|
||||
barcode = rule.alias
|
||||
parsed_result['code'] = barcode
|
||||
else:
|
||||
parsed_result['encoding'] = rule.encoding
|
||||
parsed_result['type'] = rule.type
|
||||
parsed_result['value'] = match['value']
|
||||
parsed_result['code'] = cur_barcode
|
||||
if rule.encoding == "ean13":
|
||||
parsed_result['base_code'] = self.sanitize_ean(match['base_code'])
|
||||
elif rule.encoding == "upca":
|
||||
parsed_result['base_code'] = self.sanitize_upc(match['base_code'])
|
||||
else:
|
||||
parsed_result['base_code'] = match['base_code']
|
||||
return parsed_result
|
||||
|
||||
return parsed_result
|
||||
|
||||
# RFID/URI stuff.
|
||||
@api.model
|
||||
def parse_uri(self, barcode):
|
||||
""" Convert supported URI format (lgtin, sgtin, sgtin-96, sgtin-198,
|
||||
sscc and ssacc-96) into a GS1 barcode.
|
||||
:param barcode str: the URI as a string.
|
||||
:rtype: str
|
||||
"""
|
||||
if not re.match(r'^urn:', barcode):
|
||||
return barcode
|
||||
identifier, data = (bc_part.strip() for bc_part in re.split(':', barcode)[-2:])
|
||||
data = re.split(r'\.', data)
|
||||
match identifier:
|
||||
case 'lgtin' | 'sgtin':
|
||||
barcode = self._convert_uri_gtin_data_into_tracking_number(barcode, data)
|
||||
case 'sgtin-96' | 'sgtin-198':
|
||||
# Same as SGTIN but we have to remove the filter.
|
||||
barcode = self._convert_uri_gtin_data_into_tracking_number(barcode, data[1:])
|
||||
case 'sscc':
|
||||
barcode = self._convert_uri_sscc_data_into_package(barcode, data)
|
||||
case 'sscc-96':
|
||||
# Same as SSCC but we have to remove the filter.
|
||||
barcode = self._convert_uri_sscc_data_into_package(barcode, data[1:])
|
||||
return barcode
|
||||
|
||||
@api.model
|
||||
def _convert_uri_gtin_data_into_tracking_number(self, base_code, data):
|
||||
gs1_company_prefix, item_ref_and_indicator, tracking_number = data
|
||||
indicator = item_ref_and_indicator[0]
|
||||
item_ref = item_ref_and_indicator[1:]
|
||||
product_barcode = indicator + gs1_company_prefix + item_ref
|
||||
product_barcode += str(get_barcode_check_digit(product_barcode + '0'))
|
||||
return [
|
||||
{
|
||||
'base_code': base_code,
|
||||
'code': product_barcode,
|
||||
'encoding': '',
|
||||
'type': 'product',
|
||||
'value': product_barcode,
|
||||
},
|
||||
{
|
||||
'base_code': base_code,
|
||||
'code': tracking_number,
|
||||
'encoding': '',
|
||||
'type': 'lot',
|
||||
'value': tracking_number,
|
||||
},
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _convert_uri_sscc_data_into_package(self, base_code, data):
|
||||
gs1_company_prefix, serial_reference = data
|
||||
extension = serial_reference[0]
|
||||
serial_ref = serial_reference[1:]
|
||||
sscc = extension + gs1_company_prefix + serial_ref
|
||||
sscc += str(get_barcode_check_digit(sscc + '0'))
|
||||
return [{
|
||||
'base_code': base_code,
|
||||
'code': sscc,
|
||||
'encoding': '',
|
||||
'type': 'package',
|
||||
'value': sscc,
|
||||
}]
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_default(self):
|
||||
default_record = self.env.ref("barcodes.default_barcode_nomenclature", raise_if_not_found=False)
|
||||
if default_record and default_record in self:
|
||||
raise UserError(_(
|
||||
"You cannot delete '%(name)s' because it's the default barcode nomenclature.",
|
||||
name=default_record.display_name
|
||||
))
|
||||
Loading…
Add table
Add a link
Reference in a new issue