
Implementación completa del sistema de catálogo de parámetros flexible:
✅ **Tasks completadas:**
- Task 1-12: Todas las tareas implementadas exitosamente
- Task 13: No aplicable (no hay reportes desarrollados aún)
**Características principales:**
- Catálogo centralizado de parámetros reutilizables
- Rangos de referencia flexibles por edad/género/embarazo
- Generación automática de resultados basada en configuración
- Integración completa con el flujo existente
- 36 parámetros demo y 31 rangos de referencia
- Tests automatizados completos
**Modelos implementados:**
- lims.analysis.parameter
- lims.parameter.range
- product.template.parameter
La Task 13 se omitió ya que no existen reportes desarrollados en el módulo actualmente.
144 lines
4.7 KiB
Python
144 lines
4.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo import models, fields, api
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class LimsAnalysisParameter(models.Model):
|
|
_name = 'lims.analysis.parameter'
|
|
_description = 'Catálogo de Parámetros de Laboratorio'
|
|
_order = 'name'
|
|
_rec_name = 'name'
|
|
|
|
name = fields.Char(
|
|
string='Nombre',
|
|
required=True,
|
|
help='Nombre descriptivo del parámetro (ej: Hemoglobina)'
|
|
)
|
|
|
|
code = fields.Char(
|
|
string='Código',
|
|
required=True,
|
|
help='Código único del parámetro (ej: HGB)'
|
|
)
|
|
|
|
value_type = fields.Selection([
|
|
('numeric', 'Numérico'),
|
|
('text', 'Texto'),
|
|
('boolean', 'Sí/No'),
|
|
('selection', 'Selección')
|
|
],
|
|
string='Tipo de Valor',
|
|
required=True,
|
|
default='numeric',
|
|
help='Tipo de dato que acepta este parámetro'
|
|
)
|
|
|
|
unit = fields.Char(
|
|
string='Unidad de Medida',
|
|
help='Unidad de medida del parámetro (ej: g/dL, mg/dL, %)'
|
|
)
|
|
|
|
selection_values = fields.Text(
|
|
string='Valores de Selección',
|
|
help='Para tipo "Selección", ingrese los valores posibles separados por comas'
|
|
)
|
|
|
|
description = fields.Text(
|
|
string='Descripción',
|
|
help='Descripción detallada del parámetro y su significado clínico'
|
|
)
|
|
|
|
active = fields.Boolean(
|
|
string='Activo',
|
|
default=True,
|
|
help='Si está desmarcado, el parámetro no estará disponible para nuevas configuraciones'
|
|
)
|
|
|
|
category_id = fields.Many2one(
|
|
'product.category',
|
|
string='Categoría',
|
|
domain="[('parent_id.name', '=', 'Análisis de Laboratorio')]",
|
|
help='Categoría del parámetro para agrupar en reportes'
|
|
)
|
|
|
|
# Relaciones
|
|
template_parameter_ids = fields.One2many(
|
|
'product.template.parameter',
|
|
'parameter_id',
|
|
string='Análisis que usan este parámetro'
|
|
)
|
|
|
|
range_ids = fields.One2many(
|
|
'lims.parameter.range',
|
|
'parameter_id',
|
|
string='Rangos de Referencia'
|
|
)
|
|
|
|
# Campos computados
|
|
analysis_count = fields.Integer(
|
|
string='Cantidad de Análisis',
|
|
compute='_compute_analysis_count',
|
|
store=True
|
|
)
|
|
|
|
@api.depends('template_parameter_ids')
|
|
def _compute_analysis_count(self):
|
|
for record in self:
|
|
record.analysis_count = len(record.template_parameter_ids)
|
|
|
|
@api.constrains('code')
|
|
def _check_code_unique(self):
|
|
for record in self:
|
|
if self.search_count([
|
|
('code', '=', record.code),
|
|
('id', '!=', record.id)
|
|
]) > 0:
|
|
raise ValidationError(f'El código "{record.code}" ya existe. Los códigos deben ser únicos.')
|
|
|
|
@api.constrains('value_type', 'selection_values')
|
|
def _check_selection_values(self):
|
|
for record in self:
|
|
if record.value_type == 'selection' and not record.selection_values:
|
|
raise ValidationError('Debe especificar los valores de selección para parámetros de tipo "Selección".')
|
|
|
|
@api.constrains('value_type', 'unit')
|
|
def _check_numeric_unit(self):
|
|
for record in self:
|
|
if record.value_type == 'numeric' and not record.unit:
|
|
raise ValidationError('Los parámetros numéricos deben tener una unidad de medida.')
|
|
|
|
def get_selection_list(self):
|
|
"""Devuelve la lista de valores de selección como una lista de Python"""
|
|
self.ensure_one()
|
|
if self.value_type == 'selection' and self.selection_values:
|
|
return [val.strip() for val in self.selection_values.split(',') if val.strip()]
|
|
return []
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
# Convertir código a mayúsculas
|
|
if 'code' in vals:
|
|
vals['code'] = vals['code'].upper()
|
|
return super(LimsAnalysisParameter, self).create(vals)
|
|
|
|
def write(self, vals):
|
|
# Convertir código a mayúsculas
|
|
if 'code' in vals:
|
|
vals['code'] = vals['code'].upper()
|
|
return super(LimsAnalysisParameter, self).write(vals)
|
|
|
|
def name_get(self):
|
|
result = []
|
|
for record in self:
|
|
name = f"[{record.code}] {record.name}"
|
|
if record.unit:
|
|
name += f" ({record.unit})"
|
|
result.append((record.id, name))
|
|
return result
|
|
|
|
@api.model
|
|
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
|
args = args or []
|
|
if name:
|
|
args = ['|', ('code', operator, name), ('name', operator, name)] + args
|
|
return self._search(args, limit=limit, access_rights_uid=name_get_uid) |