feat(#51): Task 1 completada - Crear modelo lims.analysis.parameter
- Creado modelo lims.analysis.parameter con campos: name, code, value_type, unit, etc. - Implementadas validaciones y constraints - Creadas vistas form, list y search - Agregado menú en Configuración - Configurados permisos de seguridad 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d709c5c1c7
commit
98aba1c747
|
@ -19,7 +19,9 @@
|
|||
"Bash(move lab_logo.png lims_management/static/img/lab_logo.png)",
|
||||
"WebFetch(domain:github.com)",
|
||||
"WebFetch(domain:apps.odoo.com)",
|
||||
"Bash(dir:*)"
|
||||
"Bash(dir:*)",
|
||||
"Bash(find:*)",
|
||||
"Bash(true)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
'views/lims_test_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/menus.xml',
|
||||
'views/analysis_parameter_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
'demo/z_lims_demo.xml',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import analysis_parameter
|
||||
from . import analysis_range
|
||||
from . import product
|
||||
from . import partner
|
||||
|
|
Binary file not shown.
125
lims_management/models/analysis_parameter.py
Normal file
125
lims_management/models/analysis_parameter.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
# -*- 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'
|
||||
)
|
||||
|
||||
# Relaciones - Se agregarán cuando se creen los modelos relacionados
|
||||
# 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'
|
||||
# )
|
||||
|
||||
@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)
|
|
@ -1,4 +1,6 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_lims_analysis_parameter_user,lims.analysis.parameter.user,model_lims_analysis_parameter,base.group_user,1,0,0,0
|
||||
access_lims_analysis_parameter_manager,lims.analysis.parameter.manager,model_lims_analysis_parameter,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
|
||||
|
|
|
110
lims_management/views/analysis_parameter_views.xml
Normal file
110
lims_management/views/analysis_parameter_views.xml
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Form View -->
|
||||
<record id="view_lims_analysis_parameter_form" model="ir.ui.view">
|
||||
<field name="name">lims.analysis.parameter.form</field>
|
||||
<field name="model">lims.analysis.parameter</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Parámetro de Análisis">
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="toggle_active"
|
||||
type="object"
|
||||
class="oe_stat_button"
|
||||
icon="fa-archive">
|
||||
<field name="active" widget="boolean_button"
|
||||
options="{'terminology': 'archive'}"/>
|
||||
</button>
|
||||
</div>
|
||||
<widget name="web_ribbon" title="Archivado" bg_color="bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="code" placeholder="Código" class="oe_inline"/>
|
||||
</h1>
|
||||
<h2>
|
||||
<field name="name" placeholder="Nombre del parámetro" class="oe_inline"/>
|
||||
</h2>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Información General">
|
||||
<field name="value_type"/>
|
||||
<field name="unit" invisible="value_type != 'numeric'"/>
|
||||
<field name="selection_values"
|
||||
invisible="value_type != 'selection'"
|
||||
placeholder="Positivo, Negativo, No concluyente"/>
|
||||
<field name="active" invisible="1"/>
|
||||
</group>
|
||||
<group string="Detalles">
|
||||
<field name="description" widget="text" nolabel="1" colspan="2"/>
|
||||
</group>
|
||||
</group>
|
||||
<!-- Notebook se agregará cuando se creen los modelos relacionados -->
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- List View -->
|
||||
<record id="view_lims_analysis_parameter_list" model="ir.ui.view">
|
||||
<field name="name">lims.analysis.parameter.list</field>
|
||||
<field name="model">lims.analysis.parameter</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Parámetros de Análisis">
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
<field name="value_type"/>
|
||||
<field name="unit" optional="show"/>
|
||||
<field name="active" invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Search View -->
|
||||
<record id="view_lims_analysis_parameter_search" model="ir.ui.view">
|
||||
<field name="name">lims.analysis.parameter.search</field>
|
||||
<field name="model">lims.analysis.parameter</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Buscar Parámetros">
|
||||
<field name="name" string="Parámetro"
|
||||
filter_domain="['|', ('name', 'ilike', self), ('code', 'ilike', self)]"/>
|
||||
<field name="code"/>
|
||||
<filter string="Numéricos" name="numeric" domain="[('value_type', '=', 'numeric')]"/>
|
||||
<filter string="Texto" name="text" domain="[('value_type', '=', 'text')]"/>
|
||||
<filter string="Sí/No" name="boolean" domain="[('value_type', '=', 'boolean')]"/>
|
||||
<filter string="Selección" name="selection" domain="[('value_type', '=', 'selection')]"/>
|
||||
<separator/>
|
||||
<filter string="Activos" name="active" domain="[('active', '=', True)]"/>
|
||||
<filter string="Archivados" name="archived" domain="[('active', '=', False)]"/>
|
||||
<group expand="0" string="Agrupar por">
|
||||
<filter string="Tipo de Valor" name="group_value_type" context="{'group_by': 'value_type'}"/>
|
||||
<filter string="Estado" name="group_active" context="{'group_by': 'active'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action -->
|
||||
<record id="action_lims_analysis_parameter" model="ir.actions.act_window">
|
||||
<field name="name">Parámetros de Análisis</field>
|
||||
<field name="res_model">lims.analysis.parameter</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_lims_analysis_parameter_search"/>
|
||||
<field name="context">{'search_default_active': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Crear nuevo parámetro
|
||||
</p>
|
||||
<p>
|
||||
Los parámetros definen qué valores se pueden registrar en los análisis de laboratorio.
|
||||
Cada parámetro tiene un tipo de dato, unidad de medida y rangos de referencia.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Menu -->
|
||||
<menuitem id="menu_lims_analysis_parameter"
|
||||
name="Parámetros de Análisis"
|
||||
parent="lims_management.lims_menu_config"
|
||||
action="action_lims_analysis_parameter"
|
||||
sequence="20"/>
|
||||
</odoo>
|
Loading…
Reference in New Issue
Block a user