feat(#51): Task 9 completada - Actualizar vistas de ingreso de resultados

This commit is contained in:
Luis Ernesto Portillo Zaldivar 2025-07-15 13:18:00 -06:00
parent cdb8a97d58
commit bac05b4bb2
7 changed files with 443 additions and 43 deletions

View File

@ -33,9 +33,11 @@
'views/analysis_views.xml',
'views/sale_order_views.xml',
'views/stock_lot_views.xml',
'views/lims_test_views.xml',
'views/res_config_settings_views.xml',
'views/menus.xml',
'views/lims_test_views.xml',
'views/lims_result_views.xml',
'views/lims_result_bulk_entry_views.xml',
'views/res_config_settings_views.xml',
'views/product_template_parameter_views.xml',
'views/parameter_range_views.xml',
'views/analysis_parameter_views.xml',

View File

@ -41,6 +41,13 @@ class LimsResult(models.Model):
readonly=True
)
parameter_code = fields.Char(
string='Código',
related='parameter_id.code',
store=True,
readonly=True
)
sequence = fields.Integer(
string='Secuencia',
default=10
@ -121,6 +128,12 @@ class LimsResult(models.Model):
store=True
)
result_status = fields.Selection([
('normal', 'Normal'),
('abnormal', 'Anormal'),
('critical', 'Crítico')
], string='Estado', compute='_compute_result_status', store=True)
@api.depends('test_id', 'parameter_name')
def _compute_display_name(self):
"""Calcula el nombre a mostrar."""
@ -205,6 +218,19 @@ class LimsResult(models.Model):
record.is_out_of_range = (status != 'normal')
record.is_critical = (status == 'critical')
@api.depends('parameter_id', 'value_numeric', 'is_out_of_range', 'is_critical', 'parameter_value_type')
def _compute_result_status(self):
"""Calcula el estado visual del resultado."""
for record in self:
if record.parameter_value_type != 'numeric':
record.result_status = 'normal'
elif record.is_critical:
record.result_status = 'critical'
elif record.is_out_of_range:
record.result_status = 'abnormal'
else:
record.result_status = 'normal'
@api.constrains('value_numeric', 'value_text', 'value_selection', 'value_boolean', 'parameter_value_type')
def _check_value_type(self):
"""Asegura que el valor ingresado corresponda al tipo de parámetro."""

View File

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Specialized Form View for Bulk Result Entry -->
<record id="view_lims_test_result_entry_form" model="ir.ui.view">
<field name="name">lims.test.result.entry.form</field>
<field name="model">lims.test</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<form string="Ingreso Rápido de Resultados">
<header>
<field name="state" widget="statusbar" statusbar_visible="draft,in_process,result_entered,validated"/>
<button name="action_start_process" string="Iniciar Análisis"
type="object" class="oe_highlight"
invisible="state != 'draft'"/>
<button name="action_enter_results" string="Guardar Resultados"
type="object" class="oe_highlight"
invisible="state != 'in_process'"/>
<button name="action_validate" string="Validar Resultados"
type="object" class="oe_highlight"
invisible="state != 'result_entered'"
groups="lims_management.group_lims_admin"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="name" readonly="1"/>
</h1>
<h2>
<field name="patient_id" readonly="1"/>
</h2>
<h3>
<field name="product_id" readonly="1"/>
</h3>
</div>
<group>
<group>
<field name="sample_id" readonly="1"/>
<field name="technician_id" readonly="state != 'in_process'"/>
</group>
<group>
<field name="create_date" readonly="1"/>
<field name="validation_date" readonly="1"/>
</group>
</group>
<separator string="Ingreso de Resultados"/>
<field name="result_ids"
readonly="state in ['validated', 'cancelled']"
context="{'form_view_ref': 'lims_management.view_lims_result_form'}">
<list string="Resultados" editable="bottom" create="0" delete="0">
<field name="sequence" invisible="1"/>
<field name="parameter_id" readonly="1" force_save="1"/>
<field name="parameter_code" readonly="1"/>
<field name="parameter_value_type" invisible="1"/>
<!-- Entrada rápida de valores -->
<field name="value_numeric"
invisible="parameter_value_type != 'numeric'"
widget="float"
options="{'digits': [16, 4]}"
decoration-danger="is_critical"
decoration-warning="is_out_of_range and not is_critical"/>
<field name="value_text"
invisible="parameter_value_type != 'text'"/>
<field name="value_selection"
invisible="parameter_value_type != 'selection'"
widget="selection"/>
<field name="value_boolean"
invisible="parameter_value_type != 'boolean'"
widget="boolean_toggle"/>
<!-- Información de referencia -->
<field name="parameter_unit"
invisible="parameter_value_type != 'numeric'"
readonly="1"/>
<field name="applicable_range_id"
widget="many2one_tags"
readonly="1"
options="{'no_open': True}"/>
<!-- Indicadores -->
<field name="result_status"
widget="badge"
decoration-success="result_status == 'normal'"
decoration-warning="result_status == 'abnormal'"
decoration-danger="result_status == 'critical'"/>
<!-- Campos ocultos -->
<field name="is_out_of_range" invisible="1"/>
<field name="is_critical" invisible="1"/>
<!-- Notas rápidas -->
<field name="notes" optional="show"/>
</list>
</field>
<group string="Observaciones Generales" invisible="state == 'draft'">
<field name="notes" nolabel="1"
placeholder="Ingrese observaciones generales sobre la prueba..."/>
</group>
</sheet>
</form>
</field>
</record>
<!-- Action for Quick Result Entry -->
<record id="action_lims_result_entry" model="ir.actions.act_window">
<field name="name">Ingreso Rápido de Resultados</field>
<field name="res_model">lims.test</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="view_lims_test_result_entry_form"/>
<field name="search_view_id" ref="view_lims_test_search"/>
<field name="domain">[('state', 'in', ['in_process', 'result_entered'])]</field>
<field name="context">{'search_default_my_tests': 1, 'search_default_in_process': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No hay pruebas pendientes de resultados
</p>
<p>
Las pruebas aparecerán aquí cuando estén listas para
el ingreso de resultados.
</p>
</field>
</record>
<!-- Result Summary Dashboard -->
<record id="view_lims_result_pivot" model="ir.ui.view">
<field name="name">lims.result.pivot</field>
<field name="model">lims.result</field>
<field name="arch" type="xml">
<pivot string="Análisis de Resultados">
<field name="parameter_id" type="row"/>
<field name="result_status" type="col"/>
<field name="test_id" type="measure"/>
</pivot>
</field>
</record>
<record id="view_lims_result_graph" model="ir.ui.view">
<field name="name">lims.result.graph</field>
<field name="model">lims.result</field>
<field name="arch" type="xml">
<graph string="Distribución de Resultados" type="pie">
<field name="result_status"/>
<field name="test_id" type="measure"/>
</graph>
</field>
</record>
<!-- Action for Result Analysis -->
<record id="action_lims_result_analysis" model="ir.actions.act_window">
<field name="name">Análisis de Resultados</field>
<field name="res_model">lims.result</field>
<field name="view_mode">pivot,graph,list</field>
<field name="help" type="html">
<p>
Análisis estadístico de los resultados de laboratorio.
</p>
</field>
</record>
<!-- Menu items -->
<menuitem id="menu_lims_result_entry"
name="Ingreso de Resultados"
parent="lims_management.lims_menu_laboratory"
action="action_lims_result_entry"
sequence="25"/>
<menuitem id="menu_lims_result_analysis"
name="Análisis de Resultados"
parent="lims_management.lims_menu_reports"
action="action_lims_result_analysis"
sequence="20"/>
</odoo>

View File

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Form View for lims.result -->
<record id="view_lims_result_form" model="ir.ui.view">
<field name="name">lims.result.form</field>
<field name="model">lims.result</field>
<field name="arch" type="xml">
<form string="Resultado de Análisis">
<sheet>
<group>
<group string="Información del Test">
<field name="test_id" readonly="1"/>
<field name="patient_id" readonly="1"/>
<field name="test_date" readonly="1"/>
</group>
<group string="Parámetro">
<field name="parameter_id" readonly="1"/>
<field name="parameter_code" readonly="1"/>
<field name="parameter_value_type" invisible="1"/>
<field name="parameter_unit" invisible="parameter_value_type != 'numeric'"/>
</group>
</group>
<group string="Valor del Resultado">
<group>
<field name="value_numeric"
invisible="parameter_value_type != 'numeric'"
widget="float"
options="{'digits': [16, 4]}"
decoration-danger="is_out_of_range"
decoration-warning="is_critical"/>
<field name="value_text"
invisible="parameter_value_type != 'text'"/>
<field name="value_selection"
invisible="parameter_value_type != 'selection'"
widget="selection"/>
<field name="value_boolean"
invisible="parameter_value_type != 'boolean'"
widget="boolean_toggle"/>
</group>
<group>
<field name="is_out_of_range" readonly="1"/>
<field name="is_critical" readonly="1"/>
</group>
</group>
<group string="Rango de Referencia" invisible="parameter_value_type != 'numeric'">
<field name="applicable_range_id" readonly="1">
<form>
<group>
<field name="normal_min"/>
<field name="normal_max"/>
<field name="critical_min"/>
<field name="critical_max"/>
</group>
</form>
</field>
</group>
<group string="Observaciones">
<field name="notes" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- List View for lims.result -->
<record id="view_lims_result_list" model="ir.ui.view">
<field name="name">lims.result.list</field>
<field name="model">lims.result</field>
<field name="arch" type="xml">
<list string="Resultados de Análisis" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="parameter_id" options="{'no_create': True, 'no_open': True}"/>
<field name="parameter_code" optional="show"/>
<field name="parameter_value_type" invisible="1"/>
<field name="value_numeric"
invisible="parameter_value_type != 'numeric'"
decoration-danger="is_out_of_range"
decoration-warning="is_critical"/>
<field name="value_text"
invisible="parameter_value_type != 'text'"/>
<field name="value_selection"
invisible="parameter_value_type != 'selection'"/>
<field name="value_boolean"
invisible="parameter_value_type != 'boolean'"
widget="boolean_toggle"/>
<field name="parameter_unit"
invisible="parameter_value_type != 'numeric'"
optional="show"/>
<field name="is_out_of_range" invisible="1"/>
<field name="is_critical" invisible="1"/>
<field name="applicable_range_id" optional="hide"/>
<field name="notes" optional="show"/>
</list>
</field>
</record>
<!-- Search View for lims.result -->
<record id="view_lims_result_search" model="ir.ui.view">
<field name="name">lims.result.search</field>
<field name="model">lims.result</field>
<field name="arch" type="xml">
<search string="Buscar Resultados">
<field name="test_id"/>
<field name="parameter_id"/>
<field name="parameter_name"/>
<field name="patient_id"/>
<separator/>
<filter string="Fuera de Rango" name="out_of_range"
domain="[('is_out_of_range', '=', True)]"/>
<filter string="Críticos" name="critical"
domain="[('is_critical', '=', True)]"/>
<separator/>
<filter string="Numéricos" name="numeric"
domain="[('parameter_value_type', '=', 'numeric')]"/>
<filter string="Texto" name="text"
domain="[('parameter_value_type', '=', 'text')]"/>
<filter string="Selección" name="selection"
domain="[('parameter_value_type', '=', 'selection')]"/>
<filter string="Sí/No" name="boolean"
domain="[('parameter_value_type', '=', 'boolean')]"/>
<group expand="0" string="Agrupar por">
<filter string="Test" name="group_test" context="{'group_by': 'test_id'}"/>
<filter string="Parámetro" name="group_parameter" context="{'group_by': 'parameter_id'}"/>
<filter string="Paciente" name="group_patient" context="{'group_by': 'patient_id'}"/>
<filter string="Tipo de Valor" name="group_value_type" context="{'group_by': 'parameter_value_type'}"/>
</group>
</search>
</field>
</record>
<!-- Action for lims.result -->
<record id="action_lims_result" model="ir.actions.act_window">
<field name="name">Resultados de Análisis</field>
<field name="res_model">lims.result</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_lims_result_search"/>
<field name="context">{'search_default_out_of_range': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No hay resultados registrados
</p>
<p>
Los resultados se crean automáticamente al generar las pruebas
de laboratorio basándose en los parámetros configurados.
</p>
</field>
</record>
<!-- Menu item -->
<menuitem id="menu_lims_result"
name="Resultados"
parent="lims_management.lims_menu_laboratory"
action="action_lims_result"
sequence="30"/>
</odoo>

View File

@ -59,24 +59,53 @@
<page string="Resultados">
<field name="result_ids"
readonly="state in ['validated', 'cancelled']"
context="{'default_test_id': id}">
<list string="Resultados" editable="bottom">
<field name="sequence" widget="handle"/>
<field name="parameter_id" options="{'no_create': True}"/>
context="{'default_test_id': id, 'default_patient_id': patient_id, 'default_test_date': create_date}"
mode="tree">
<list string="Resultados" editable="bottom"
decoration-danger="is_out_of_range and not is_critical"
decoration-warning="is_critical"
decoration-success="not is_out_of_range and not is_critical and parameter_value_type == 'numeric'">
<field name="sequence" widget="handle" optional="show"/>
<field name="parameter_id"
options="{'no_create': True, 'no_open': True}"
readonly="1"/>
<field name="parameter_code" optional="show" readonly="1"/>
<field name="parameter_value_type" invisible="1"/>
<!-- Campos de valor con mejores widgets -->
<field name="value_numeric"
invisible="parameter_value_type != 'numeric'"
decoration-danger="is_out_of_range"
decoration-warning="is_critical"/>
widget="float"
options="{'digits': [16, 4]}"
class="oe_edit_only"/>
<field name="value_text"
invisible="parameter_value_type != 'text'"/>
invisible="parameter_value_type != 'text'"
class="oe_edit_only"/>
<field name="value_selection"
invisible="parameter_value_type != 'selection'"/>
invisible="parameter_value_type != 'selection'"
widget="selection"
class="oe_edit_only"/>
<field name="value_boolean"
invisible="parameter_value_type != 'boolean'"/>
<field name="parameter_unit" optional="show"/>
invisible="parameter_value_type != 'boolean'"
widget="boolean_toggle"
class="oe_edit_only"/>
<!-- Unidad y rangos -->
<field name="parameter_unit"
invisible="parameter_value_type != 'numeric'"
optional="show"
readonly="1"/>
<field name="applicable_range_id"
optional="hide"
readonly="1"/>
<!-- Indicadores de estado -->
<field name="is_out_of_range" invisible="1"/>
<field name="is_critical" invisible="1"/>
<!-- Campo de estado visual -->
<field name="result_status"
widget="badge"
optional="show"
decoration-success="result_status == 'normal'"
decoration-warning="result_status == 'abnormal'"
decoration-danger="result_status == 'critical'"/>
<field name="notes" optional="show"/>
</list>
</field>
@ -198,23 +227,5 @@
</field>
</record>
<!-- Acción para lims.test -->
<record id="action_lims_test" model="ir.actions.act_window">
<field name="name">Pruebas de Laboratorio</field>
<field name="res_model">lims.test</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="view_lims_test_search"/>
<field name="context">{'search_default_my_tests': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Crear primera prueba de laboratorio
</p>
<p>
Aquí podrá gestionar las pruebas de laboratorio,
ingresar resultados y validarlos.
</p>
</field>
</record>
</data>
</odoo>

View File

@ -102,12 +102,43 @@
action="action_lims_lab_sample"
sequence="16"/>
<!-- Submenú de Laboratorio -->
<menuitem
id="lims_menu_laboratory"
name="Laboratorio"
parent="lims_menu_root"
sequence="20"/>
<!-- Acción para lims.test -->
<record id="action_lims_test" model="ir.actions.act_window">
<field name="name">Pruebas de Laboratorio</field>
<field name="res_model">lims.test</field>
<field name="view_mode">list,kanban,form</field>
<field name="context">{'search_default_my_tests': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Crear primera prueba de laboratorio
</p>
<p>
Aquí podrá gestionar las pruebas de laboratorio,
ingresar resultados y validarlos.
</p>
</field>
</record>
<!-- Menú para Pruebas -->
<menuitem id="menu_lims_tests"
name="Pruebas"
parent="lims_menu_root"
parent="lims_menu_laboratory"
action="action_lims_test"
sequence="25"/>
sequence="10"/>
<!-- Submenú de Reportes -->
<menuitem
id="lims_menu_reports"
name="Reportes"
parent="lims_menu_root"
sequence="90"/>
<!-- Submenú de Configuración -->
<menuitem
@ -168,11 +199,21 @@
action="action_lims_sample_type_catalog"
sequence="20"/>
<!-- Acción para abrir configuración de laboratorio -->
<record id="action_lims_config_settings" model="ir.actions.act_window">
<field name="name">Configuración</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module' : 'lims_management'}</field>
</record>
<!-- Menú de configuración de ajustes -->
<menuitem id="menu_lims_config_settings"
name="Ajustes"
parent="lims_menu_config"
action="lims_management.action_lims_config_settings"
action="action_lims_config_settings"
sequence="100"/>
</data>
</odoo>

View File

@ -23,15 +23,5 @@
</field>
</record>
<!-- Acción para abrir configuración de laboratorio -->
<record id="action_lims_config_settings" model="ir.actions.act_window">
<field name="name">Configuración</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module' : 'lims_management'}</field>
</record>
</data>
</odoo>