feat(#51): Task 3 completada - Crear modelo lims.parameter.range
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
651f90966d
commit
bd0daf3da7
|
@ -37,6 +37,7 @@
|
||||||
'views/res_config_settings_views.xml',
|
'views/res_config_settings_views.xml',
|
||||||
'views/menus.xml',
|
'views/menus.xml',
|
||||||
'views/product_template_parameter_views.xml',
|
'views/product_template_parameter_views.xml',
|
||||||
|
'views/parameter_range_views.xml',
|
||||||
'views/analysis_parameter_views.xml',
|
'views/analysis_parameter_views.xml',
|
||||||
],
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import analysis_parameter
|
from . import analysis_parameter
|
||||||
from . import product_template_parameter
|
from . import product_template_parameter
|
||||||
|
from . import parameter_range
|
||||||
from . import analysis_range
|
from . import analysis_range
|
||||||
from . import product
|
from . import product
|
||||||
from . import partner
|
from . import partner
|
||||||
|
|
Binary file not shown.
|
@ -61,11 +61,11 @@ class LimsAnalysisParameter(models.Model):
|
||||||
string='Análisis que usan este parámetro'
|
string='Análisis que usan este parámetro'
|
||||||
)
|
)
|
||||||
|
|
||||||
# range_ids = fields.One2many(
|
range_ids = fields.One2many(
|
||||||
# 'lims.parameter.range',
|
'lims.parameter.range',
|
||||||
# 'parameter_id',
|
'parameter_id',
|
||||||
# string='Rangos de Referencia'
|
string='Rangos de Referencia'
|
||||||
# )
|
)
|
||||||
|
|
||||||
# Campos computados
|
# Campos computados
|
||||||
analysis_count = fields.Integer(
|
analysis_count = fields.Integer(
|
||||||
|
|
214
lims_management/models/parameter_range.py
Normal file
214
lims_management/models/parameter_range.py
Normal file
|
@ -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'
|
|
@ -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_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_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_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_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_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
|
access_stock_lot_user,stock.lot.user,stock.model_stock_lot,base.group_user,1,1,1,1
|
||||||
|
|
|
|
@ -46,6 +46,21 @@
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
|
<page string="Rangos de Referencia" name="ranges">
|
||||||
|
<field name="range_ids" context="{'default_parameter_id': id}">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="gender"/>
|
||||||
|
<field name="age_min"/>
|
||||||
|
<field name="age_max"/>
|
||||||
|
<field name="pregnant" optional="show"/>
|
||||||
|
<field name="normal_min"/>
|
||||||
|
<field name="normal_max"/>
|
||||||
|
<field name="critical_min" optional="show"/>
|
||||||
|
<field name="critical_max" optional="show"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
<page string="Análisis Configurados" name="analysis">
|
<page string="Análisis Configurados" name="analysis">
|
||||||
<field name="template_parameter_ids">
|
<field name="template_parameter_ids">
|
||||||
<list>
|
<list>
|
||||||
|
|
132
lims_management/views/parameter_range_views.xml
Normal file
132
lims_management/views/parameter_range_views.xml
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Form View -->
|
||||||
|
<record id="view_lims_parameter_range_form" model="ir.ui.view">
|
||||||
|
<field name="name">lims.parameter.range.form</field>
|
||||||
|
<field name="model">lims.parameter.range</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Rango de Referencia">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<group string="Parámetro">
|
||||||
|
<field name="parameter_id"
|
||||||
|
options="{'no_create': True}"
|
||||||
|
context="{'form_view_ref': 'lims_management.view_lims_analysis_parameter_form'}"/>
|
||||||
|
<field name="parameter_unit"/>
|
||||||
|
</group>
|
||||||
|
<group string="Condiciones">
|
||||||
|
<field name="gender"/>
|
||||||
|
<field name="age_min"/>
|
||||||
|
<field name="age_max"/>
|
||||||
|
<field name="pregnant" invisible="gender == 'male'"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="Valores de Referencia">
|
||||||
|
<group>
|
||||||
|
<label for="normal_min"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<field name="normal_min" class="oe_inline"/>
|
||||||
|
<span class="oe_inline"> - </span>
|
||||||
|
<field name="normal_max" class="oe_inline"/>
|
||||||
|
<field name="parameter_unit" class="oe_inline" readonly="1"/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<label for="critical_min"/>
|
||||||
|
<div class="o_row">
|
||||||
|
<span class="oe_inline">< </span>
|
||||||
|
<field name="critical_min" class="oe_inline"/>
|
||||||
|
<span class="oe_inline"> o > </span>
|
||||||
|
<field name="critical_max" class="oe_inline"/>
|
||||||
|
<field name="parameter_unit" class="oe_inline" readonly="1"/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="Interpretación Clínica">
|
||||||
|
<field name="interpretation" nolabel="1"
|
||||||
|
placeholder="Ingrese guías de interpretación clínica para este rango..."/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- List View -->
|
||||||
|
<record id="view_lims_parameter_range_list" model="ir.ui.view">
|
||||||
|
<field name="name">lims.parameter.range.list</field>
|
||||||
|
<field name="model">lims.parameter.range</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list string="Rangos de Referencia" editable="bottom">
|
||||||
|
<field name="parameter_id" optional="hide"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="gender"/>
|
||||||
|
<field name="age_min"/>
|
||||||
|
<field name="age_max"/>
|
||||||
|
<field name="pregnant" optional="show"/>
|
||||||
|
<field name="normal_min"/>
|
||||||
|
<field name="normal_max"/>
|
||||||
|
<field name="critical_min" optional="show"/>
|
||||||
|
<field name="critical_max" optional="show"/>
|
||||||
|
<field name="parameter_unit" optional="show"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Search View -->
|
||||||
|
<record id="view_lims_parameter_range_search" model="ir.ui.view">
|
||||||
|
<field name="name">lims.parameter.range.search</field>
|
||||||
|
<field name="model">lims.parameter.range</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Buscar Rangos">
|
||||||
|
<field name="parameter_id"/>
|
||||||
|
<field name="parameter_name"/>
|
||||||
|
<field name="parameter_code"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<filter string="Masculino" name="male" domain="[('gender', '=', 'male')]"/>
|
||||||
|
<filter string="Femenino" name="female" domain="[('gender', '=', 'female')]"/>
|
||||||
|
<filter string="Ambos" name="both" domain="[('gender', '=', 'both')]"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="Embarazadas" name="pregnant" domain="[('pregnant', '=', True)]"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="Pediátrico (<18)" name="pediatric"
|
||||||
|
domain="[('age_min', '<', 18)]"/>
|
||||||
|
<filter string="Adulto (18-65)" name="adult"
|
||||||
|
domain="[('age_min', '>=', 18), ('age_max', '<=', 65)]"/>
|
||||||
|
<filter string="Geriátrico (>65)" name="geriatric"
|
||||||
|
domain="[('age_max', '>', 65)]"/>
|
||||||
|
<group expand="0" string="Agrupar por">
|
||||||
|
<filter string="Parámetro" name="group_parameter"
|
||||||
|
context="{'group_by': 'parameter_id'}"/>
|
||||||
|
<filter string="Género" name="group_gender"
|
||||||
|
context="{'group_by': 'gender'}"/>
|
||||||
|
<filter string="Embarazo" name="group_pregnant"
|
||||||
|
context="{'group_by': 'pregnant'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Action -->
|
||||||
|
<record id="action_lims_parameter_range" model="ir.actions.act_window">
|
||||||
|
<field name="name">Rangos de Referencia</field>
|
||||||
|
<field name="res_model">lims.parameter.range</field>
|
||||||
|
<field name="view_mode">list,form</field>
|
||||||
|
<field name="search_view_id" ref="view_lims_parameter_range_search"/>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
Crear nuevo rango de referencia
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Menu -->
|
||||||
|
<menuitem id="menu_lims_parameter_range"
|
||||||
|
name="Rangos de Referencia"
|
||||||
|
parent="lims_management.lims_menu_config"
|
||||||
|
action="action_lims_parameter_range"
|
||||||
|
sequence="25"/>
|
||||||
|
</odoo>
|
Loading…
Reference in New Issue
Block a user