diff --git a/lims_management/__manifest__.py b/lims_management/__manifest__.py
index 16f9d1f..b19986e 100644
--- a/lims_management/__manifest__.py
+++ b/lims_management/__manifest__.py
@@ -15,7 +15,7 @@
'author': "Gemini",
'website': "https://gitea.grupoconsiti.com/luis_portillo/clinical_laboratory",
'category': 'Industries',
- 'version': '18.0.1.0.0',
+ 'version': '18.0.1.0.1',
'depends': ['base', 'product', 'sale', 'base_setup'],
'assets': {
'web.assets_backend': [
diff --git a/lims_management/migrations/18.0.1.0.1/post-migrate.py b/lims_management/migrations/18.0.1.0.1/post-migrate.py
new file mode 100644
index 0000000..fed9202
--- /dev/null
+++ b/lims_management/migrations/18.0.1.0.1/post-migrate.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from odoo import api, SUPERUSER_ID
+
+def migrate(cr, version):
+ """Migrate existing selection values from char field to selection field."""
+ env = api.Environment(cr, SUPERUSER_ID, {})
+
+ # Find all results with selection parameters that have values
+ results = env['lims.result'].search([
+ ('parameter_value_type', '=', 'selection'),
+ ('value_selection', '!=', False),
+ ('value_selection', '!=', '')
+ ])
+
+ if results:
+ cr.execute("""
+ UPDATE lims_result
+ SET value_selection_field = value_selection
+ WHERE id IN %s
+ AND parameter_value_type = 'selection'
+ AND value_selection IS NOT NULL
+ AND value_selection != ''
+ """, (tuple(results.ids),))
+
+ # Log migration
+ print(f"Migrated {len(results)} selection values from value_selection to value_selection_field")
\ No newline at end of file
diff --git a/lims_management/models/lims_result.py b/lims_management/models/lims_result.py
index 46878bd..9e2da51 100644
--- a/lims_management/models/lims_result.py
+++ b/lims_management/models/lims_result.py
@@ -97,6 +97,12 @@ class LimsResult(models.Model):
string='Valor de Selección'
)
+ # Add Selection field with dynamic options
+ value_selection_field = fields.Selection(
+ selection='_get_selection_options',
+ string='Valor de Selección'
+ )
+
# Campo para mostrar las opciones disponibles
selection_options_display = fields.Char(
string='Opciones disponibles',
@@ -167,7 +173,7 @@ class LimsResult(models.Model):
else:
record.display_name = record.parameter_name or _('Nuevo')
- @api.depends('value_numeric', 'value_text', 'value_selection', 'value_boolean', 'parameter_value_type')
+ @api.depends('value_numeric', 'value_text', 'value_selection', 'value_selection_field', 'value_boolean', 'parameter_value_type')
def _compute_value_display(self):
"""Calcula el valor a mostrar según el tipo de dato."""
for record in self:
@@ -179,7 +185,8 @@ class LimsResult(models.Model):
elif record.parameter_value_type == 'text':
record.value_display = record.value_text or ''
elif record.parameter_value_type == 'selection':
- record.value_display = record.value_selection or ''
+ # Use the new selection field value
+ record.value_display = record.value_selection_field or record.value_selection or ''
elif record.parameter_value_type == 'boolean':
record.value_display = 'Sí' if record.value_boolean else 'No'
else:
@@ -278,18 +285,20 @@ class LimsResult(models.Model):
_('Para parámetros de texto solo se debe ingresar el valor de texto.')
)
elif value_type == 'selection':
- has_value = bool(record.value_selection)
+ # Check both fields for backward compatibility
+ has_value = bool(record.value_selection_field or record.value_selection)
if (record.value_numeric not in [False, 0.0]) or record.value_text or record.value_boolean:
raise ValidationError(
_('Para parámetros de selección solo se debe elegir una opción.')
)
# Validar que el valor seleccionado sea válido
+ actual_value = record.value_selection_field or record.value_selection
if has_value and record.parameter_id:
valid_options = record.parameter_id.get_selection_list()
- if valid_options and record.value_selection not in valid_options:
+ if valid_options and actual_value not in valid_options:
raise ValidationError(
_('El valor "%s" no es una opción válida. Opciones disponibles: %s') %
- (record.value_selection, ', '.join(valid_options))
+ (actual_value, ', '.join(valid_options))
)
elif value_type == 'boolean':
has_value = True # Boolean siempre tiene valor (True o False)
@@ -312,6 +321,7 @@ class LimsResult(models.Model):
self.value_numeric = False
self.value_text = False
self.value_selection = False
+ self.value_selection_field = False
self.value_boolean = False
# Si es selección, obtener las opciones
@@ -319,6 +329,12 @@ class LimsResult(models.Model):
# Esto se usará en las vistas para mostrar las opciones dinámicamente
pass
+ @api.onchange('value_selection_field')
+ def _onchange_value_selection_field(self):
+ """Sincroniza el campo de selección con el campo char."""
+ if self.parameter_value_type == 'selection':
+ self.value_selection = self.value_selection_field
+
def _get_selection_options(self):
"""Retorna las opciones de selección basadas en el parámetro."""
options = []
diff --git a/lims_management/views/lims_result_bulk_entry_views.xml b/lims_management/views/lims_result_bulk_entry_views.xml
index ece7fd5..65035c1 100644
--- a/lims_management/views/lims_result_bulk_entry_views.xml
+++ b/lims_management/views/lims_result_bulk_entry_views.xml
@@ -65,9 +65,8 @@
decoration-warning="is_out_of_range and not is_critical"/>
-
+
diff --git a/lims_management/views/lims_result_views.xml b/lims_management/views/lims_result_views.xml
index d7af9be..9022f41 100644
--- a/lims_management/views/lims_result_views.xml
+++ b/lims_management/views/lims_result_views.xml
@@ -34,9 +34,8 @@
decoration-warning="is_critical"/>
-
+
diff --git a/test/diagnose_selection_issue.py b/test/diagnose_selection_issue.py
new file mode 100644
index 0000000..298858c
--- /dev/null
+++ b/test/diagnose_selection_issue.py
@@ -0,0 +1,132 @@
+import odoo
+import json
+
+def diagnose_selection_issue(cr):
+ """Diagnose why selection widget shows 'Sin Opciones'"""
+ env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
+
+ print("="*80)
+ print("DIAGNOSING SELECTION WIDGET ISSUE")
+ print("="*80)
+
+ # 1. Check the specific test and parameter
+ test_id = 33
+ result_id = 43
+
+ # Get the test
+ test = env['lims.test'].browse(test_id)
+ if not test:
+ print(f"\nERROR: Test with ID {test_id} not found!")
+ return
+
+ print(f"\nTest: {test.name} (ID: {test.id})")
+ print(f"Category: {test.category_id.name}")
+
+ # Check parameters with selection type
+ selection_params = test.parameter_ids.filtered(lambda p: p.value_type == 'selection')
+ print(f"\nParameters with selection type: {len(selection_params)}")
+
+ for param in selection_params:
+ print(f"\n--- Parameter: {param.name} (ID: {param.id}) ---")
+ print(f"Value Type: {param.value_type}")
+ print(f"Selection Values (raw field): '{param.selection_values}'")
+ print(f"Selection Values (type): {type(param.selection_values)}")
+ print(f"Selection Values (bool): {bool(param.selection_values)}")
+
+ # Test get_selection_list method
+ try:
+ selection_list = param.get_selection_list()
+ print(f"\nget_selection_list() result:")
+ print(f" Type: {type(selection_list)}")
+ print(f" Content: {selection_list}")
+ print(f" Length: {len(selection_list) if isinstance(selection_list, list) else 'N/A'}")
+ except Exception as e:
+ print(f"\nERROR calling get_selection_list(): {str(e)}")
+
+ # 2. Check the specific result
+ result = env['lims.result'].browse(result_id)
+ if not result:
+ print(f"\n\nERROR: Result with ID {result_id} not found!")
+ return
+
+ print(f"\n\n--- Result Analysis (ID: {result_id}) ---")
+ print(f"Test: {result.test_id.name}")
+ print(f"Parameter: {result.parameter_id.name}")
+ print(f"Parameter Value Type: {result.parameter_id.value_type}")
+
+ # Check computed field
+ print(f"\nComputed field 'selection_options': {result.selection_options}")
+
+ # Manually test _get_selection_options
+ try:
+ # Clear cache to force recomputation
+ result.invalidate_recordset(['selection_options'])
+
+ # Access the field to trigger computation
+ options = result.selection_options
+ print(f"\nAfter invalidating cache:")
+ print(f" selection_options: {options}")
+ print(f" Type: {type(options)}")
+ except Exception as e:
+ print(f"\nERROR accessing selection_options: {str(e)}")
+
+ # 3. Direct method test
+ print(f"\n\n--- Direct Method Tests ---")
+
+ # Test parameter's get_selection_list directly
+ if result.parameter_id.value_type == 'selection':
+ param = result.parameter_id
+ print(f"\nParameter: {param.name}")
+ print(f"Selection Values: '{param.selection_values}'")
+
+ # Test parsing
+ if param.selection_values:
+ lines = param.selection_values.strip().split('\n')
+ print(f"\nParsing test:")
+ print(f" Lines after split: {lines}")
+ print(f" Number of lines: {len(lines)}")
+
+ parsed_options = []
+ for line in lines:
+ line = line.strip()
+ if line:
+ if '|' in line:
+ parts = line.split('|', 1)
+ if len(parts) == 2:
+ parsed_options.append((parts[0].strip(), parts[1].strip()))
+ else:
+ parsed_options.append((line, line))
+
+ print(f"\n Parsed options: {parsed_options}")
+
+ # 4. Check other results for the same test
+ print(f"\n\n--- Other Results for Test {test_id} ---")
+ other_results = env['lims.result'].search([('test_id', '=', test_id)], limit=5)
+ for res in other_results:
+ if res.parameter_id.value_type == 'selection':
+ print(f"\nResult ID: {res.id}")
+ print(f" Parameter: {res.parameter_id.name}")
+ print(f" Selection Options: {res.selection_options}")
+ print(f" Value: {res.value_text}")
+
+ # 5. Database check
+ print(f"\n\n--- Direct Database Check ---")
+ cr.execute("""
+ SELECT id, name, value_type, selection_values
+ FROM lims_parameter
+ WHERE value_type = 'selection'
+ AND id IN (SELECT parameter_id FROM lims_result WHERE id = %s)
+ """, (result_id,))
+
+ for row in cr.fetchall():
+ print(f"\nParameter ID: {row[0]}")
+ print(f" Name: {row[1]}")
+ print(f" Value Type: {row[2]}")
+ print(f" Selection Values: '{row[3]}'")
+ print(f" Selection Values (repr): {repr(row[3])}")
+
+if __name__ == '__main__':
+ db_name = 'odoo'
+ registry = odoo.registry(db_name)
+ with registry.cursor() as cr:
+ diagnose_selection_issue(cr)
\ No newline at end of file