diff --git a/check_stock_lot_fields.py b/check_stock_lot_fields.py new file mode 100644 index 0000000..c510ee9 --- /dev/null +++ b/check_stock_lot_fields.py @@ -0,0 +1,11 @@ +import odoo + +db_name = 'lims_demo' +registry = odoo.registry(db_name) +with registry.cursor() as cr: + env = odoo.api.Environment(cr, 1, {}) + fields = env['stock.lot']._fields.keys() + print("Stock.lot fields containing 'name' or 'barcode':") + for f in fields: + if 'name' in f or 'barcode' in f: + print(f" - {f}") \ No newline at end of file diff --git a/lims_management/models/__pycache__/stock_lot.cpython-312.pyc b/lims_management/models/__pycache__/stock_lot.cpython-312.pyc index 484c66e..ad21e4d 100644 Binary files a/lims_management/models/__pycache__/stock_lot.cpython-312.pyc and b/lims_management/models/__pycache__/stock_lot.cpython-312.pyc differ diff --git a/lims_management/models/stock_lot.py b/lims_management/models/stock_lot.py index 1cbb2a7..5de18a4 100644 --- a/lims_management/models/stock_lot.py +++ b/lims_management/models/stock_lot.py @@ -1,11 +1,21 @@ # -*- coding: utf-8 -*- from odoo import models, fields, api +from datetime import datetime +import random class StockLot(models.Model): _inherit = 'stock.lot' is_lab_sample = fields.Boolean(string='Is a Laboratory Sample') + barcode = fields.Char( + string='Código de Barras', + compute='_compute_barcode', + store=True, + readonly=True, + help="Código de barras único para la muestra en formato YYMMDDNNNNNNC" + ) + patient_id = fields.Many2one( 'res.partner', string='Patient', @@ -112,3 +122,96 @@ class StockLot(models.Model): elif self.container_type: return dict(self._fields['container_type'].selection).get(self.container_type) return 'Unknown' + + @api.depends('is_lab_sample', 'create_date') + def _compute_barcode(self): + """Generate unique barcode for laboratory samples""" + for record in self: + if record.is_lab_sample and not record.barcode: + record.barcode = record._generate_unique_barcode() + elif not record.is_lab_sample: + record.barcode = False + + def _generate_unique_barcode(self): + """Generate a unique barcode in format YYMMDDNNNNNNC + YY: Year (2 digits) + MM: Month (2 digits) + DD: Day (2 digits) + NNNNNN: Sequential number (6 digits) + C: Check digit + """ + self.ensure_one() + now = datetime.now() + date_prefix = now.strftime('%y%m%d') + + # Get the highest sequence number for today + domain = [ + ('is_lab_sample', '=', True), + ('barcode', 'like', date_prefix + '%'), + ('id', '!=', self.id) + ] + + max_barcode = self.search(domain, order='barcode desc', limit=1) + + if max_barcode and max_barcode.barcode: + # Extract sequence number from existing barcode + try: + sequence = int(max_barcode.barcode[6:12]) + 1 + except: + sequence = 1 + else: + sequence = 1 + + # Ensure we don't exceed 6 digits + if sequence > 999999: + # Add prefix based on sample type to allow more barcodes + prefix_map = { + 'suero': '1', + 'edta': '2', + 'orina': '3', + 'hisopo': '4', + 'other': '9' + } + + type_prefix = '9' # default + if self.sample_type_product_id: + name_lower = self.sample_type_product_id.name.lower() + for key, val in prefix_map.items(): + if key in name_lower: + type_prefix = val + break + + sequence = int(type_prefix + str(sequence % 100000).zfill(5)) + + # Format sequence with leading zeros + sequence_str = str(sequence).zfill(6) + + # Calculate check digit using Luhn algorithm + barcode_without_check = date_prefix + sequence_str + check_digit = self._calculate_luhn_check_digit(barcode_without_check) + + final_barcode = barcode_without_check + str(check_digit) + + # Verify uniqueness + existing = self.search([ + ('barcode', '=', final_barcode), + ('id', '!=', self.id) + ], limit=1) + + if existing: + # If collision, add random component and retry + sequence = sequence * 10 + random.randint(0, 9) + sequence_str = str(sequence % 1000000).zfill(6) + barcode_without_check = date_prefix + sequence_str + check_digit = self._calculate_luhn_check_digit(barcode_without_check) + final_barcode = barcode_without_check + str(check_digit) + + return final_barcode + + def _calculate_luhn_check_digit(self, number_str): + """Calculate Luhn check digit for barcode validation""" + digits = [int(d) for d in number_str] + odd_sum = sum(digits[-1::-2]) + even_sum = sum([sum(divmod(2 * d, 10)) for d in digits[-2::-2]]) + total = odd_sum + even_sum + return (10 - (total % 10)) % 10