mirror of
https://github.com/bringout/oca-ocb-technical.git
synced 2026-04-19 05:52:01 +02:00
Initial commit: Technical packages
This commit is contained in:
commit
3473fa71a0
873 changed files with 297766 additions and 0 deletions
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import barcode_events_mixin
|
||||
from . import barcode_nomenclature
|
||||
from . import barcode_rule
|
||||
from . import ir_http
|
||||
from . import res_company
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
class BarcodeEventsMixin(models.AbstractModel):
|
||||
""" Mixin class for objects reacting when a barcode is scanned in their form views
|
||||
which contains `<field name="_barcode_scanned" widget="barcode_handler"/>`.
|
||||
Models using this mixin must implement the method on_barcode_scanned. It works
|
||||
like an onchange and receives the scanned barcode in parameter.
|
||||
"""
|
||||
|
||||
_name = 'barcodes.barcode_events_mixin'
|
||||
_description = 'Barcode Event Mixin'
|
||||
|
||||
_barcode_scanned = fields.Char("Barcode Scanned", help="Value of the last barcode scanned.", store=False)
|
||||
|
||||
@api.onchange('_barcode_scanned')
|
||||
def _on_barcode_scanned(self):
|
||||
barcode = self._barcode_scanned
|
||||
if barcode:
|
||||
self._barcode_scanned = ""
|
||||
return self.on_barcode_scanned(barcode)
|
||||
|
||||
def on_barcode_scanned(self, barcode):
|
||||
raise NotImplementedError(_("In order to use barcodes.barcode_events_mixin, method on_barcode_scanned must be implemented"))
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
import re
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo.tools 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', size=32, 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):
|
||||
""" 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
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import re
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class BarcodeRule(models.Model):
|
||||
_name = 'barcode.rule'
|
||||
_description = 'Barcode Rule'
|
||||
_order = 'sequence asc, id'
|
||||
|
||||
name = fields.Char(string='Rule Name', size=32, required=True, help='An internal identification for this barcode nomenclature rule')
|
||||
barcode_nomenclature_id = fields.Many2one('barcode.nomenclature', string='Barcode Nomenclature')
|
||||
sequence = fields.Integer(string='Sequence', help='Used to order rules such that rules with a smaller sequence match first')
|
||||
encoding = fields.Selection(
|
||||
string='Encoding', required=True, default='any', selection=[
|
||||
('any', 'Any'),
|
||||
('ean13', 'EAN-13'),
|
||||
('ean8', 'EAN-8'),
|
||||
('upca', 'UPC-A'),
|
||||
], help='This rule will apply only if the barcode is encoded with the specified encoding')
|
||||
type = fields.Selection(
|
||||
string='Type', required=True, selection=[
|
||||
('alias', 'Alias'),
|
||||
('product', 'Unit Product'),
|
||||
], default='product')
|
||||
pattern = fields.Char(string='Barcode Pattern', help="The barcode matching pattern", required=True, default='.*')
|
||||
alias = fields.Char(string='Alias', default='0', help='The matched pattern will alias to this barcode', required=True)
|
||||
|
||||
@api.constrains('pattern')
|
||||
def _check_pattern(self):
|
||||
for rule in self:
|
||||
p = rule.pattern.replace('\\\\', 'X').replace('\\{', 'X').replace('\\}', 'X')
|
||||
findall = re.findall("[{]|[}]", p) # p does not contain escaped { or }
|
||||
if len(findall) == 2:
|
||||
if not re.search("[{][N]*[D]*[}]", p):
|
||||
raise ValidationError(_("There is a syntax error in the barcode pattern %(pattern)s: braces can only contain N's followed by D's.", pattern=rule.pattern))
|
||||
elif re.search("[{][}]", p):
|
||||
raise ValidationError(_("There is a syntax error in the barcode pattern %(pattern)s: empty braces.", pattern=rule.pattern))
|
||||
elif len(findall) != 0:
|
||||
raise ValidationError(_("There is a syntax error in the barcode pattern %(pattern)s: a rule can only contain one pair of braces.", pattern=rule.pattern))
|
||||
elif p == '*':
|
||||
raise ValidationError(_(" '*' is not a valid Regex Barcode Pattern. Did you mean '.*' ?"))
|
||||
try:
|
||||
re.compile(re.sub('{N+D*}', '', p))
|
||||
except re.error:
|
||||
raise ValidationError(_("The barcode pattern %(pattern)s does not lead to a valid regular expression.", pattern=rule.pattern))
|
||||
15
odoo-bringout-oca-ocb-barcodes/barcodes/models/ir_http.py
Normal file
15
odoo-bringout-oca-ocb-barcodes/barcodes/models/ir_http.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class IrHttp(models.AbstractModel):
|
||||
_inherit = 'ir.http'
|
||||
|
||||
def session_info(self):
|
||||
res = super(IrHttp, self).session_info()
|
||||
if self.env.user._is_internal():
|
||||
res['max_time_between_keys_in_ms'] = int(
|
||||
self.env['ir.config_parameter'].sudo().get_param('barcode.max_time_between_keys_in_ms', default='100'))
|
||||
return res
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
def _get_default_nomenclature(self):
|
||||
return self.env.ref('barcodes.default_barcode_nomenclature', raise_if_not_found=False)
|
||||
|
||||
nomenclature_id = fields.Many2one(
|
||||
'barcode.nomenclature',
|
||||
string="Nomenclature",
|
||||
default=_get_default_nomenclature,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue