feat(#51): Task 2 completada - Crear modelo product.template.parameter
- Creado modelo product.template.parameter para asociar parámetros a análisis - Campos: sequence, required, instructions - Relación Many2one con analysis.parameter y product.template - Agregadas vistas embebidas en product.template - Actualizado analysis.parameter con relación One2many - 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
98aba1c747
commit
92f8894164
|
@ -36,6 +36,7 @@
|
||||||
'views/lims_test_views.xml',
|
'views/lims_test_views.xml',
|
||||||
'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/analysis_parameter_views.xml',
|
'views/analysis_parameter_views.xml',
|
||||||
],
|
],
|
||||||
'demo': [
|
'demo': [
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from . import analysis_parameter
|
from . import analysis_parameter
|
||||||
|
from . import product_template_parameter
|
||||||
from . import analysis_range
|
from . import analysis_range
|
||||||
from . import product
|
from . import product
|
||||||
from . import partner
|
from . import partner
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -54,12 +54,12 @@ class LimsAnalysisParameter(models.Model):
|
||||||
help='Si está desmarcado, el parámetro no estará disponible para nuevas configuraciones'
|
help='Si está desmarcado, el parámetro no estará disponible para nuevas configuraciones'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Relaciones - Se agregarán cuando se creen los modelos relacionados
|
# Relaciones
|
||||||
# template_parameter_ids = fields.One2many(
|
template_parameter_ids = fields.One2many(
|
||||||
# 'product.template.parameter',
|
'product.template.parameter',
|
||||||
# 'parameter_id',
|
'parameter_id',
|
||||||
# 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',
|
||||||
|
@ -67,6 +67,18 @@ class LimsAnalysisParameter(models.Model):
|
||||||
# string='Rangos de Referencia'
|
# 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')
|
@api.constrains('code')
|
||||||
def _check_code_unique(self):
|
def _check_code_unique(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
|
|
|
@ -27,6 +27,13 @@ class ProductTemplate(models.Model):
|
||||||
'analysis_id',
|
'analysis_id',
|
||||||
string="Rangos de Referencia"
|
string="Rangos de Referencia"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parameter_ids = fields.One2many(
|
||||||
|
'product.template.parameter',
|
||||||
|
'product_tmpl_id',
|
||||||
|
string="Parámetros del Análisis",
|
||||||
|
help="Parámetros que se medirán en este análisis"
|
||||||
|
)
|
||||||
|
|
||||||
is_sample_type = fields.Boolean(
|
is_sample_type = fields.Boolean(
|
||||||
string="Es Tipo de Muestra",
|
string="Es Tipo de Muestra",
|
||||||
|
|
109
lims_management/models/product_template_parameter.py
Normal file
109
lims_management/models/product_template_parameter.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class ProductTemplateParameter(models.Model):
|
||||||
|
_name = 'product.template.parameter'
|
||||||
|
_description = 'Parámetros por Análisis'
|
||||||
|
_order = 'product_tmpl_id, sequence, id'
|
||||||
|
_rec_name = 'parameter_id'
|
||||||
|
|
||||||
|
product_tmpl_id = fields.Many2one(
|
||||||
|
'product.template',
|
||||||
|
string='Análisis',
|
||||||
|
required=True,
|
||||||
|
ondelete='cascade',
|
||||||
|
domain=[('is_analysis', '=', True)],
|
||||||
|
help='Análisis al que pertenece este parámetro'
|
||||||
|
)
|
||||||
|
|
||||||
|
parameter_id = fields.Many2one(
|
||||||
|
'lims.analysis.parameter',
|
||||||
|
string='Parámetro',
|
||||||
|
required=True,
|
||||||
|
ondelete='restrict',
|
||||||
|
help='Parámetro de laboratorio'
|
||||||
|
)
|
||||||
|
|
||||||
|
sequence = fields.Integer(
|
||||||
|
string='Secuencia',
|
||||||
|
default=10,
|
||||||
|
help='Orden en que aparecerá el parámetro en los resultados'
|
||||||
|
)
|
||||||
|
|
||||||
|
required = fields.Boolean(
|
||||||
|
string='Obligatorio',
|
||||||
|
default=True,
|
||||||
|
help='Si está marcado, este parámetro debe tener un valor en los resultados'
|
||||||
|
)
|
||||||
|
|
||||||
|
instructions = fields.Text(
|
||||||
|
string='Instrucciones específicas',
|
||||||
|
help='Instrucciones especiales para este parámetro en este análisis'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Campos relacionados para facilitar búsquedas y vistas
|
||||||
|
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',
|
||||||
|
store=True,
|
||||||
|
readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
|
parameter_value_type = fields.Selection(
|
||||||
|
related='parameter_id.value_type',
|
||||||
|
string='Tipo de Valor',
|
||||||
|
store=True,
|
||||||
|
readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
|
parameter_unit = fields.Char(
|
||||||
|
related='parameter_id.unit',
|
||||||
|
string='Unidad',
|
||||||
|
readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
('unique_param_per_analysis',
|
||||||
|
'UNIQUE(product_tmpl_id, parameter_id)',
|
||||||
|
'El parámetro ya está configurado para este análisis. Cada parámetro solo puede aparecer una vez por análisis.')
|
||||||
|
]
|
||||||
|
|
||||||
|
@api.constrains('sequence')
|
||||||
|
def _check_sequence(self):
|
||||||
|
for record in self:
|
||||||
|
if record.sequence < 0:
|
||||||
|
raise ValidationError('La secuencia debe ser un número positivo.')
|
||||||
|
|
||||||
|
def name_get(self):
|
||||||
|
result = []
|
||||||
|
for record in self:
|
||||||
|
name = f"{record.product_tmpl_id.name} - [{record.parameter_code}] {record.parameter_name}"
|
||||||
|
if record.parameter_unit:
|
||||||
|
name += f" ({record.parameter_unit})"
|
||||||
|
result.append((record.id, name))
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create(self, vals):
|
||||||
|
# Si no se especifica secuencia, asignar la siguiente disponible
|
||||||
|
if 'sequence' not in vals and 'product_tmpl_id' in vals:
|
||||||
|
max_sequence = self.search([
|
||||||
|
('product_tmpl_id', '=', vals['product_tmpl_id'])
|
||||||
|
], order='sequence desc', limit=1).sequence
|
||||||
|
vals['sequence'] = (max_sequence or 0) + 10
|
||||||
|
return super(ProductTemplateParameter, self).create(vals)
|
||||||
|
|
||||||
|
def copy_data(self, default=None):
|
||||||
|
default = dict(default or {})
|
||||||
|
# Al duplicar, incrementar la secuencia
|
||||||
|
default['sequence'] = self.sequence + 10
|
||||||
|
return super(ProductTemplateParameter, self).copy_data(default)
|
|
@ -1,6 +1,8 @@
|
||||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
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_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_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_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
|
||||||
|
|
|
|
@ -8,6 +8,13 @@
|
||||||
<form string="Parámetro de Análisis">
|
<form string="Parámetro de Análisis">
|
||||||
<sheet>
|
<sheet>
|
||||||
<div class="oe_button_box" name="button_box">
|
<div class="oe_button_box" name="button_box">
|
||||||
|
<button name="%(lims_management.action_product_template_parameter)d"
|
||||||
|
type="action"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-flask"
|
||||||
|
context="{'search_default_parameter_id': id}">
|
||||||
|
<field name="analysis_count" widget="statinfo" string="Análisis"/>
|
||||||
|
</button>
|
||||||
<button name="toggle_active"
|
<button name="toggle_active"
|
||||||
type="object"
|
type="object"
|
||||||
class="oe_stat_button"
|
class="oe_stat_button"
|
||||||
|
@ -38,7 +45,17 @@
|
||||||
<field name="description" widget="text" nolabel="1" colspan="2"/>
|
<field name="description" widget="text" nolabel="1" colspan="2"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<!-- Notebook se agregará cuando se creen los modelos relacionados -->
|
<notebook>
|
||||||
|
<page string="Análisis Configurados" name="analysis">
|
||||||
|
<field name="template_parameter_ids">
|
||||||
|
<list>
|
||||||
|
<field name="product_tmpl_id"/>
|
||||||
|
<field name="sequence"/>
|
||||||
|
<field name="required"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
|
@ -54,6 +71,7 @@
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="value_type"/>
|
<field name="value_type"/>
|
||||||
<field name="unit" optional="show"/>
|
<field name="unit" optional="show"/>
|
||||||
|
<field name="analysis_count" optional="show"/>
|
||||||
<field name="active" invisible="1"/>
|
<field name="active" invisible="1"/>
|
||||||
</list>
|
</list>
|
||||||
</field>
|
</field>
|
||||||
|
|
|
@ -39,6 +39,21 @@
|
||||||
<separator string="Rangos de Referencia"/>
|
<separator string="Rangos de Referencia"/>
|
||||||
<field name="value_range_ids"
|
<field name="value_range_ids"
|
||||||
view_id="lims_management.view_lims_analysis_range_tree"/>
|
view_id="lims_management.view_lims_analysis_range_tree"/>
|
||||||
|
<separator string="Parámetros del Análisis"/>
|
||||||
|
<field name="parameter_ids"
|
||||||
|
context="{'default_product_tmpl_id': id}">
|
||||||
|
<list editable="bottom">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="parameter_id"
|
||||||
|
options="{'no_create': True}"
|
||||||
|
domain="[('active', '=', True)]"/>
|
||||||
|
<field name="parameter_code"/>
|
||||||
|
<field name="parameter_value_type"/>
|
||||||
|
<field name="parameter_unit"/>
|
||||||
|
<field name="required"/>
|
||||||
|
<field name="instructions" optional="show"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
</page>
|
</page>
|
||||||
</xpath>
|
</xpath>
|
||||||
<!-- Añade el campo is_analysis cerca del nombre del producto para fácil acceso -->
|
<!-- Añade el campo is_analysis cerca del nombre del producto para fácil acceso -->
|
||||||
|
|
93
lims_management/views/product_template_parameter_views.xml
Normal file
93
lims_management/views/product_template_parameter_views.xml
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Form View -->
|
||||||
|
<record id="view_product_template_parameter_form" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.parameter.form</field>
|
||||||
|
<field name="model">product.template.parameter</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Parámetro del Análisis">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<group string="Información General">
|
||||||
|
<field name="product_tmpl_id" readonly="1"/>
|
||||||
|
<field name="parameter_id"
|
||||||
|
options="{'no_create': True}"
|
||||||
|
context="{'form_view_ref': 'lims_management.view_lims_analysis_parameter_form'}"/>
|
||||||
|
<field name="sequence"/>
|
||||||
|
<field name="required"/>
|
||||||
|
</group>
|
||||||
|
<group string="Detalles del Parámetro">
|
||||||
|
<field name="parameter_code"/>
|
||||||
|
<field name="parameter_value_type"/>
|
||||||
|
<field name="parameter_unit"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="Instrucciones Específicas">
|
||||||
|
<field name="instructions" nolabel="1" placeholder="Ingrese instrucciones especiales para este parámetro en este análisis..."/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- List View -->
|
||||||
|
<record id="view_product_template_parameter_list" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.parameter.list</field>
|
||||||
|
<field name="model">product.template.parameter</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list string="Parámetros por Análisis" editable="bottom">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="parameter_id"
|
||||||
|
options="{'no_create': True}"
|
||||||
|
domain="[('active', '=', True)]"/>
|
||||||
|
<field name="parameter_code"/>
|
||||||
|
<field name="parameter_value_type"/>
|
||||||
|
<field name="parameter_unit"/>
|
||||||
|
<field name="required"/>
|
||||||
|
<field name="instructions" optional="show"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Search View -->
|
||||||
|
<record id="view_product_template_parameter_search" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.parameter.search</field>
|
||||||
|
<field name="model">product.template.parameter</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Buscar Parámetros">
|
||||||
|
<field name="product_tmpl_id"/>
|
||||||
|
<field name="parameter_id"/>
|
||||||
|
<field name="parameter_name"/>
|
||||||
|
<field name="parameter_code"/>
|
||||||
|
<filter string="Obligatorios" name="required" domain="[('required', '=', True)]"/>
|
||||||
|
<filter string="Opcionales" name="optional" domain="[('required', '=', False)]"/>
|
||||||
|
<separator/>
|
||||||
|
<filter string="Numéricos" name="numeric" domain="[('parameter_value_type', '=', 'numeric')]"/>
|
||||||
|
<filter string="Texto" name="text" domain="[('parameter_value_type', '=', 'text')]"/>
|
||||||
|
<group expand="0" string="Agrupar por">
|
||||||
|
<filter string="Análisis" name="group_product" context="{'group_by': 'product_tmpl_id'}"/>
|
||||||
|
<filter string="Parámetro" name="group_parameter" context="{'group_by': 'parameter_id'}"/>
|
||||||
|
<filter string="Tipo de Valor" name="group_value_type" context="{'group_by': 'parameter_value_type'}"/>
|
||||||
|
<filter string="Obligatorio" name="group_required" context="{'group_by': 'required'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Action -->
|
||||||
|
<record id="action_product_template_parameter" model="ir.actions.act_window">
|
||||||
|
<field name="name">Parámetros por Análisis</field>
|
||||||
|
<field name="res_model">product.template.parameter</field>
|
||||||
|
<field name="view_mode">list,form</field>
|
||||||
|
<field name="search_view_id" ref="view_product_template_parameter_search"/>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="o_view_nocontent_smiling_face">
|
||||||
|
Configurar parámetros para análisis
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Aquí puede ver y configurar qué parámetros se miden en cada análisis,
|
||||||
|
su orden de aparición y si son obligatorios u opcionales.
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
Loading…
Reference in New Issue
Block a user