From bd0daf3da7cbd5201ef716eb2a0dd81058b2fc2e Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Tue, 15 Jul 2025 11:47:32 -0600 Subject: [PATCH] feat(#51): Task 3 completada - Crear modelo lims.parameter.range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Creado modelo lims.parameter.range para rangos de referencia flexibles - Campos de condiciones: gender, age_min/max, pregnant - Campos de valores: normal_min/max, critical_min/max - Métodos helper: is_value_normal(), is_value_critical(), get_value_status() - Múltiples validaciones para garantizar consistencia de datos - Vistas con filtros por edad, género y condiciones especiales - Actualizado analysis_parameter con rangos en notebook 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lims_management/__manifest__.py | 1 + lims_management/models/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 523 -> 562 bytes lims_management/models/analysis_parameter.py | 10 +- lims_management/models/parameter_range.py | 214 ++++++++++++++++++ lims_management/security/ir.model.access.csv | 2 + .../views/analysis_parameter_views.xml | 15 ++ .../views/parameter_range_views.xml | 132 +++++++++++ 8 files changed, 370 insertions(+), 5 deletions(-) create mode 100644 lims_management/models/parameter_range.py create mode 100644 lims_management/views/parameter_range_views.xml diff --git a/lims_management/__manifest__.py b/lims_management/__manifest__.py index 68fe505..446edb0 100644 --- a/lims_management/__manifest__.py +++ b/lims_management/__manifest__.py @@ -37,6 +37,7 @@ 'views/res_config_settings_views.xml', 'views/menus.xml', 'views/product_template_parameter_views.xml', + 'views/parameter_range_views.xml', 'views/analysis_parameter_views.xml', ], 'demo': [ diff --git a/lims_management/models/__init__.py b/lims_management/models/__init__.py index f50d315..0a4252f 100644 --- a/lims_management/models/__init__.py +++ b/lims_management/models/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from . import analysis_parameter from . import product_template_parameter +from . import parameter_range from . import analysis_range from . import product from . import partner diff --git a/lims_management/models/__pycache__/__init__.cpython-312.pyc b/lims_management/models/__pycache__/__init__.cpython-312.pyc index 7f68a96b13adb7f79d499848b2ad63836b1d84cb..925a1d9cb15e333e06b2eeb8b6c0d9e705cd8e0d 100644 GIT binary patch delta 116 zcmeBX*~G$onwOW00SIPKD$DSj$ScX%F;Tr%AcY}?JBJ~cJBpi;p^`_Fcj5{g0sex- zqQuHSp)d2t^(-}Yj diff --git a/lims_management/models/analysis_parameter.py b/lims_management/models/analysis_parameter.py index 26300d4..d3a0248 100644 --- a/lims_management/models/analysis_parameter.py +++ b/lims_management/models/analysis_parameter.py @@ -61,11 +61,11 @@ class LimsAnalysisParameter(models.Model): string='Análisis que usan este parámetro' ) - # range_ids = fields.One2many( - # 'lims.parameter.range', - # 'parameter_id', - # string='Rangos de Referencia' - # ) + range_ids = fields.One2many( + 'lims.parameter.range', + 'parameter_id', + string='Rangos de Referencia' + ) # Campos computados analysis_count = fields.Integer( diff --git a/lims_management/models/parameter_range.py b/lims_management/models/parameter_range.py new file mode 100644 index 0000000..9b6803f --- /dev/null +++ b/lims_management/models/parameter_range.py @@ -0,0 +1,214 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api +from odoo.exceptions import ValidationError + + +class LimsParameterRange(models.Model): + _name = 'lims.parameter.range' + _description = 'Rangos de Referencia por Parámetro' + _order = 'parameter_id, gender desc, age_min' + _rec_name = 'name' + + parameter_id = fields.Many2one( + 'lims.analysis.parameter', + string='Parámetro', + required=True, + ondelete='cascade', + help='Parámetro al que aplica este rango de referencia' + ) + + name = fields.Char( + string='Descripción', + compute='_compute_name', + store=True, + help='Descripción automática del rango' + ) + + # Condiciones + gender = fields.Selection([ + ('male', 'Masculino'), + ('female', 'Femenino'), + ('both', 'Ambos') + ], + string='Género', + default='both', + required=True, + help='Género al que aplica este rango' + ) + + age_min = fields.Integer( + string='Edad Mínima', + default=0, + help='Edad mínima en años (inclusive)' + ) + + age_max = fields.Integer( + string='Edad Máxima', + default=999, + help='Edad máxima en años (inclusive)' + ) + + pregnant = fields.Boolean( + string='Embarazada', + default=False, + help='Marcar si este rango es específico para mujeres embarazadas' + ) + + # Valores de referencia + normal_min = fields.Float( + string='Valor Normal Mínimo', + help='Límite inferior del rango normal' + ) + + normal_max = fields.Float( + string='Valor Normal Máximo', + help='Límite superior del rango normal' + ) + + critical_min = fields.Float( + string='Valor Crítico Mínimo', + help='Por debajo de este valor es crítico' + ) + + critical_max = fields.Float( + string='Valor Crítico Máximo', + help='Por encima de este valor es crítico' + ) + + # Información adicional + interpretation = fields.Text( + string='Interpretación', + help='Guía de interpretación clínica para este rango' + ) + + # Campos relacionados para facilitar búsquedas + parameter_name = fields.Char( + related='parameter_id.name', + string='Nombre del Parámetro', + store=True, + readonly=True + ) + + parameter_code = fields.Char( + related='parameter_id.code', + string='Código del Parámetro', + store=True, + readonly=True + ) + + parameter_unit = fields.Char( + related='parameter_id.unit', + string='Unidad', + readonly=True + ) + + @api.depends('parameter_id', 'gender', 'age_min', 'age_max', 'pregnant') + def _compute_name(self): + for record in self: + if not record.parameter_id: + record.name = 'Nuevo rango' + continue + + parts = [record.parameter_id.name] + + # Agregar género si no es ambos + if record.gender != 'both': + gender_name = dict(self._fields['gender'].selection).get(record.gender, '') + parts.append(gender_name) + + # Agregar rango de edad + if record.age_min == 0 and record.age_max == 999: + parts.append('Todas las edades') + else: + parts.append(f"{record.age_min}-{record.age_max} años") + + # Agregar indicador de embarazo + if record.pregnant: + parts.append('Embarazada') + + record.name = ' - '.join(parts) + + @api.constrains('age_min', 'age_max') + def _check_age_range(self): + for record in self: + if record.age_min < 0: + raise ValidationError('La edad mínima no puede ser negativa.') + if record.age_max < record.age_min: + raise ValidationError('La edad máxima debe ser mayor o igual a la edad mínima.') + if record.age_max > 150: + raise ValidationError('La edad máxima no puede ser mayor a 150 años.') + + @api.constrains('normal_min', 'normal_max') + def _check_normal_range(self): + for record in self: + if record.normal_min and record.normal_max and record.normal_min > record.normal_max: + raise ValidationError('El valor normal mínimo debe ser menor o igual al valor normal máximo.') + + @api.constrains('critical_min', 'critical_max', 'normal_min', 'normal_max') + def _check_critical_range(self): + for record in self: + # Validar que crítico mínimo sea menor que normal mínimo + if record.critical_min and record.normal_min and record.critical_min > record.normal_min: + raise ValidationError('El valor crítico mínimo debe ser menor o igual al valor normal mínimo.') + + # Validar que crítico máximo sea mayor que normal máximo + if record.critical_max and record.normal_max and record.critical_max < record.normal_max: + raise ValidationError('El valor crítico máximo debe ser mayor o igual al valor normal máximo.') + + @api.constrains('gender', 'pregnant') + def _check_pregnant_gender(self): + for record in self: + if record.pregnant and record.gender == 'male': + raise ValidationError('No se puede marcar "Embarazada" para rangos masculinos.') + + @api.constrains('parameter_id', 'gender', 'age_min', 'age_max', 'pregnant') + def _check_unique_range(self): + for record in self: + # Buscar rangos duplicados + domain = [ + ('parameter_id', '=', record.parameter_id.id), + ('gender', '=', record.gender), + ('age_min', '=', record.age_min), + ('age_max', '=', record.age_max), + ('pregnant', '=', record.pregnant), + ('id', '!=', record.id) + ] + + if self.search_count(domain) > 0: + raise ValidationError('Ya existe un rango con estas mismas condiciones para este parámetro.') + + def is_value_normal(self, value): + """Verifica si un valor está dentro del rango normal""" + self.ensure_one() + if not value or not self.normal_min or not self.normal_max: + return True + return self.normal_min <= value <= self.normal_max + + def is_value_critical(self, value): + """Verifica si un valor está en rango crítico""" + self.ensure_one() + if not value: + return False + + # Crítico por debajo + if self.critical_min and value < self.critical_min: + return True + + # Crítico por encima + if self.critical_max and value > self.critical_max: + return True + + return False + + def get_value_status(self, value): + """Devuelve el estado del valor: 'normal', 'abnormal', 'critical'""" + self.ensure_one() + if not value: + return 'normal' + + if self.is_value_critical(value): + return 'critical' + elif not self.is_value_normal(value): + return 'abnormal' + else: + return 'normal' \ No newline at end of file diff --git a/lims_management/security/ir.model.access.csv b/lims_management/security/ir.model.access.csv index cbd70a9..00a5e97 100644 --- a/lims_management/security/ir.model.access.csv +++ b/lims_management/security/ir.model.access.csv @@ -3,6 +3,8 @@ access_lims_analysis_parameter_user,lims.analysis.parameter.user,model_lims_anal access_lims_analysis_parameter_manager,lims.analysis.parameter.manager,model_lims_analysis_parameter,group_lims_admin,1,1,1,1 access_product_template_parameter_user,product.template.parameter.user,model_product_template_parameter,base.group_user,1,0,0,0 access_product_template_parameter_manager,product.template.parameter.manager,model_product_template_parameter,group_lims_admin,1,1,1,1 +access_lims_parameter_range_user,lims.parameter.range.user,model_lims_parameter_range,base.group_user,1,0,0,0 +access_lims_parameter_range_manager,lims.parameter.range.manager,model_lims_parameter_range,group_lims_admin,1,1,1,1 access_lims_analysis_range_user,lims.analysis.range.user,model_lims_analysis_range,base.group_user,1,1,1,1 access_sale_order_receptionist,sale.order.receptionist,sale.model_sale_order,group_lims_receptionist,1,1,1,0 access_stock_lot_user,stock.lot.user,stock.model_stock_lot,base.group_user,1,1,1,1 diff --git a/lims_management/views/analysis_parameter_views.xml b/lims_management/views/analysis_parameter_views.xml index defa737..c902095 100644 --- a/lims_management/views/analysis_parameter_views.xml +++ b/lims_management/views/analysis_parameter_views.xml @@ -46,6 +46,21 @@ + + + + + + + + + + + + + + + diff --git a/lims_management/views/parameter_range_views.xml b/lims_management/views/parameter_range_views.xml new file mode 100644 index 0000000..e6f269f --- /dev/null +++ b/lims_management/views/parameter_range_views.xml @@ -0,0 +1,132 @@ + + + + + lims.parameter.range.form + lims.parameter.range + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + lims.parameter.range.list + lims.parameter.range + + + + + + + + + + + + + + + + + + + + lims.parameter.range.search + lims.parameter.range + + + + + + + + + + + + + + + + + + + + + + + + + + + Rangos de Referencia + lims.parameter.range + list,form + + +

+ Crear nuevo rango de referencia +

+

+ Los rangos de referencia definen los valores normales y críticos + para cada parámetro según edad, género y otras condiciones del paciente. +

+
+
+ + + +
\ No newline at end of file