diff --git a/GEMINI.md b/GEMINI.md index 1c15ed2..7a40a21 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -140,3 +140,104 @@ Busca errores en la salida. Si encuentras alguno, debes presentar un resumen del - **Errores de sintaxis:** Problemas en archivos Python (`.py`) o XML (`.views`, `.xml`). - **Permisos incorrectos:** Problemas de acceso a archivos o directorios. - **Datos incorrectos:** Errores en los archivos de datos de demostración o iniciales. + +### Política de Persistencia de la Instancia + +Después de una instalación o actualización exitosa, la instancia de Odoo **debe permanecer activa** para permitir la validación manual por parte del usuario. **No se debe detener la instancia** (`docker-compose down -v`) hasta que el usuario confirme explícitamente que ha finalizado sus pruebas. + +--- + +## Convenciones de Desarrollo en Odoo 18 + +Para evitar errores recurrentes, es **mandatorio** seguir las siguientes convenciones específicas para Odoo 18, especialmente en lo que respecta a la definición de vistas. + +### Uso de Vistas de Lista (Tree Views) + +En Odoo 18, la etiqueta `` ha sido **reemplazada por ``**. El uso de `` provocará un error de validación (`ValueError: Wrong value for ir.ui.view.type: 'tree'`). + +**Forma Incorrecta (Odoo < 18):** +```xml + + + + + + + +``` + +**Forma Correcta (Odoo 18):** +```xml + + + + + + + +``` + +### Definición de Acciones de Ventana (`view_mode`) + +Consecuente con el cambio anterior, al definir una acción de ventana (`ir.actions.act_window`) que deba mostrar una vista de lista, el `view_mode` debe ser `'list,form'` en lugar de `'tree,form'`. + +**Forma Incorrecta:** +```xml + + Ejemplo + example.model + tree,form + +``` + +**Forma Correcta:** +```xml + + Ejemplo + example.model + list,form + + +### Atributos de Visibilidad (`attrs`) + +A partir de Odoo 17, el atributo `attrs` para controlar la visibilidad de los elementos ha sido **reemplazado por el uso directo de `invisible`**. + +**Forma Incorrecta (Odoo < 17):** +```xml + +``` + +**Forma Correcta (Odoo 18):** +Se utiliza el atributo `invisible` con una expresión de dominio simplificada. La expresión se evalúa como verdadera para ocultar el campo. +```xml + +``` +O, de forma equivalente: +```xml + +``` + +### Uso de `ref()` en el Contexto de Acciones de Ventana + +La función `ref('module.xml_id')` se utiliza para obtener el ID de base de datos de un registro a partir de su ID XML. Sin embargo, esta función **solo existe en el servidor**. + +Cuando se define el `context` de una acción de ventana (`ir.actions.act_window`), este se evalúa en el cliente (navegador), donde `ref()` no está definido, causando un error `Name 'ref' is not defined`. + +Para solucionar esto, el `context` debe ser evaluado en el servidor utilizando el atributo `eval`. + +**Forma Incorrecta:** +```xml +{ + 'default_categ_id': ref('lims_management.product_category_analysis') +} +``` +Esto envía la cadena `"{'default_categ_id': ref(...)}"` al cliente, que no puede procesarla. + +**Forma Correcta:** +```xml + +``` +Al usar `eval`, Odoo ejecuta la expresión en el servidor, reemplaza `ref(...)` por el ID numérico correspondiente, y envía un diccionario JSON válido al cliente. +``` diff --git a/documents/plans/ISSUE5_PLAN.md b/documents/plans/ISSUE5_PLAN.md new file mode 100644 index 0000000..181312a --- /dev/null +++ b/documents/plans/ISSUE5_PLAN.md @@ -0,0 +1,84 @@ +# Plan de Actividades: Issue #5 - Catálogo de Análisis Clínicos + +## TODO + +- [x] **Extender el Modelo de Productos (`product.template`):** + + - [x] Crear `lims_management/models/product.py`. + - [x] Heredar de `product.template`. + - [x] Añadir campo booleano `is_analysis`. + - [x] Añadir campo de selección `analysis_type`. + - [x] Añadir campo de texto `technical_specifications`. + - [x] Crear campo `value_range` (One2many) que enlace al nuevo modelo `lims.analysis.range`. + +- [x] **Crear el Modelo para Rangos de Referencia (`lims.analysis.range`):** + + - [x] Crear `lims_management/models/analysis_range.py`. + - [x] Definir campos: `analysis_id` (Many2one), `gender`, `age_min`, `age_max`, `min_value`, `max_value`, `unit_of_measure`. + +- [x] **Definir Permisos de Seguridad:** + + - [x] Modificar `lims_management/security/ir.model.access.csv`. + - [x] Añadir permisos para el modelo `lims.analysis.range`. + +- [x] **Crear las Vistas para el Catálogo de Análisis:** + + - [x] Crear `lims_management/views/analysis_views.xml`. + - [x] Crear vista de lista/Kanban para análisis clínicos. + - [x] Heredar de la vista de formulario de productos para añadir la pestaña "Configuración de Análisis". + - [x] Mostrar campos condicionalmente (`is_analysis = True`). + - [x] Crear una vista de árbol independiente para los rangos de referencia (`lims.analysis.range`). + - [x] En la vista de formulario del producto, referenciar la nueva vista de árbol para el campo `value_range_ids`. + - [x] **(Nuevo)** Modificar las etiquetas en la acción de ventana para que se muestre "Análisis Clinico" en lugar de "Producto". + +- [x] **Crear el Menú "Catálogo de Análisis":** + + - [x] Modificar `lims_management/views/menus.xml`. + - [x] Crear una nueva acción de ventana (`ir.actions.act_window`). + - [x] **(Nuevo)** Definir valores por defecto en el `context` de la acción para: `type`, `purchase_ok`, `categ_id`. + - [x] **(Nuevo)** Crear la categoría de producto "Análisis Clínico" mediante un archivo de datos. + - [x] Crear un `menuitem` para "Catálogo de Análisis". + +- [x] **Actualizar el Manifiesto (`__manifest__.py`):** + + - [x] Añadir los nuevos modelos al `__init__.py` de la carpeta `models`. + - [x] Añadir el nuevo archivo de vistas a la lista `data` en `__manifest__.py`. + +- [x] **Verificación Final:** + - [x] Reiniciar la instancia de Odoo (`docker-compose down -v` y `docker-compose up -d`). + - [x] Revisar logs de `odoo_init`. + - [x] Verificar la funcionalidad en la interfaz de Odoo. + +- [x] **(Nuevo) Crear Datos de Demostración:** + - [x] Crear el archivo `demo/analysis_demo.xml`. + - [x] Definir registros de ejemplo para análisis clínicos (Hemograma, Perfil Lipídico, etc.). + - [x] Asegurarse de que los datos de demostración incluyan la configuración de los rangos de referencia. + - [x] Añadir el archivo `analysis_demo.xml` a la clave `demo` en `__manifest__.py`. + +--- + +## Consideraciones Adicionales para Valores por Defecto + +### 1. Análisis y Justificación + +Para mejorar la experiencia de usuario y asegurar la consistencia de los datos, se realizó un análisis para determinar qué campos del modelo `product.template` deberían tener valores por defecto al crear un nuevo "Análisis Clínico". Esta decisión se basa en el estudio del código fuente de Odoo 18 (`product.template.py`) y los documentos de requerimientos y diseño del proyecto. + +- **Análisis del Modelo `product.template`:** La revisión del modelo base de productos en Odoo 18 revel�� campos clave que definen el comportamiento de un producto en el sistema, tales como `type`, `sale_ok`, `purchase_ok`, y `categ_id`. Estos campos son fundamentales para que un producto se integre correctamente con los módulos de Ventas, Compras e Inventario. + +- **Relación con los Requerimientos y Diseño Técnico:** + - El documento `RequerimientoInicial.md` especifica que los análisis clínicos deben ser considerados **servicios facturables**. Esto justifica la necesidad de configurar los análisis como productos de tipo "Servicio" (`type='service'`) que se puedan vender (`sale_ok=True`). + - El documento `ToBeDesing.md` enfatiza la importancia de **categorizar** la información para mantener el sistema organizado y facilitar los filtros. Esto respalda la creación de una categoría de producto específica, "Análisis Clínico", para agrupar todos estos servicios y separarlos de otros productos que la empresa pueda manejar. + - Ambos documentos dejan claro que el laboratorio presta estos servicios, pero no los compra. Por lo tanto, deshabilitar la opción de compra (`purchase_ok=False`) es coherente con el flujo de negocio, evitando que los análisis aparezcan en contextos de compra. + +### 2. Propuesta de Valores por Defecto + +Basado en el análisis anterior, se implementarán los siguientes valores por defecto al crear un nuevo análisis clínico desde su menú correspondiente: + +| Campo en `product.template` | Valor por Defecto Recomendado | Justificación | +| :------------------------------------- | :---------------------------- | :------------------------------------------------------------------------------------------------------ | +| **`type` (Tipo de Producto)** | `'service'` (Servicio) | Un análisis es un servicio prestado, no un bien físico. Esto evita que Odoo intente gestionar su stock. | +| **`purchase_ok` (Se puede Comprar)** | `False` (Falso) | El laboratorio vende análisis, no los compra. Esto limpia la interfaz en los flujos de compra. | +| **`categ_id` (Categoría de Producto)** | `Análisis Clínico` | Permite agrupar, filtrar y aplicar reglas contables específicas a todos los análisis clínicos. | +| **`sale_ok` (Se puede Vender)** | `True` (Verdadero) | Esencial para que los análisis puedan ser añadidos a las órdenes de laboratorio (órdenes de venta). | + +Estos valores se configurarán en el `context` de la acción de ventana (`ir.actions.act_window`) que gestiona la creación de nuevos análisis, asegurando que cada nuevo registro se cree con la configuración correcta de forma automática. diff --git a/lims_management/__manifest__.py b/lims_management/__manifest__.py index a4a94ac..00cceeb 100644 --- a/lims_management/__manifest__.py +++ b/lims_management/__manifest__.py @@ -16,16 +16,19 @@ 'website': "https://gitea.grupoconsiti.com/luis_portillo/clinical_laboratory", 'category': 'Industries', 'version': '18.0.1.0.0', - 'depends': ['base'], + 'depends': ['base', 'product'], 'data': [ 'security/lims_security.xml', 'security/ir.model.access.csv', 'data/ir_sequence.xml', + 'data/product_category.xml', 'views/partner_views.xml', + 'views/analysis_views.xml', 'views/menus.xml', ], 'demo': [ - 'data/lims_demo.xml', + 'demo/lims_demo.xml', + 'demo/analysis_demo.xml', ], 'installable': True, 'application': True, diff --git a/lims_management/__pycache__/__init__.cpython-312.pyc b/lims_management/__pycache__/__init__.cpython-312.pyc index 52b6a3e..98b96b4 100644 Binary files a/lims_management/__pycache__/__init__.cpython-312.pyc and b/lims_management/__pycache__/__init__.cpython-312.pyc differ diff --git a/lims_management/data/product_category.xml b/lims_management/data/product_category.xml new file mode 100644 index 0000000..bb91f38 --- /dev/null +++ b/lims_management/data/product_category.xml @@ -0,0 +1,8 @@ + + + + + Análisis Clínico + + + diff --git a/lims_management/demo/analysis_demo.xml b/lims_management/demo/analysis_demo.xml new file mode 100644 index 0000000..edeb551 --- /dev/null +++ b/lims_management/demo/analysis_demo.xml @@ -0,0 +1,71 @@ + + + + + + + + Hemograma Completo + True + hematology + + service + + + + El hemograma completo es un análisis de sangre que mide los niveles de los principales componentes sanguíneos: glóbulos rojos, glóbulos blancos y plaquetas. + + + + + + + male + 18 + 99 + 4.5 + 5.9 + millones/µL + + + + female + 18 + 99 + 4.0 + 5.2 + millones/µL + + + + + Perfil Lipídico + True + chemistry + + service + + + + Mide los niveles de colesterol y otros lípidos en la sangre. Incluye Colesterol Total, LDL, HDL y Triglicéridos. + + + + + + + 0 + 200 + mg/dL + + + + + + 0 + 100 + mg/dL + + + + diff --git a/lims_management/data/lims_demo.xml b/lims_management/demo/lims_demo.xml similarity index 100% rename from lims_management/data/lims_demo.xml rename to lims_management/demo/lims_demo.xml diff --git a/lims_management/models/__init__.py b/lims_management/models/__init__.py index 1d3c2e9..cdf3ffc 100644 --- a/lims_management/models/__init__.py +++ b/lims_management/models/__init__.py @@ -1,2 +1,4 @@ # -*- coding: utf-8 -*- -from . import partner \ No newline at end of file +from . import partner +from . import product +from . import analysis_range \ No newline at end of file diff --git a/lims_management/models/__pycache__/__init__.cpython-312.pyc b/lims_management/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..ddb34cf Binary files /dev/null and b/lims_management/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/lims_management/models/__pycache__/analysis_range.cpython-312.pyc b/lims_management/models/__pycache__/analysis_range.cpython-312.pyc new file mode 100644 index 0000000..dc8ed33 Binary files /dev/null and b/lims_management/models/__pycache__/analysis_range.cpython-312.pyc differ diff --git a/lims_management/models/__pycache__/partner.cpython-312.pyc b/lims_management/models/__pycache__/partner.cpython-312.pyc new file mode 100644 index 0000000..8282316 Binary files /dev/null and b/lims_management/models/__pycache__/partner.cpython-312.pyc differ diff --git a/lims_management/models/__pycache__/product.cpython-312.pyc b/lims_management/models/__pycache__/product.cpython-312.pyc new file mode 100644 index 0000000..6e5e283 Binary files /dev/null and b/lims_management/models/__pycache__/product.cpython-312.pyc differ diff --git a/lims_management/models/analysis_range.py b/lims_management/models/analysis_range.py new file mode 100644 index 0000000..c527226 --- /dev/null +++ b/lims_management/models/analysis_range.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields + +class LimsAnalysisRange(models.Model): + _name = 'lims.analysis.range' + _description = 'Rangos de Referencia para Análisis Clínicos' + + analysis_id = fields.Many2one( + 'product.template', + string="Análisis", + required=True, + ondelete='cascade' + ) + gender = fields.Selection([ + ('male', 'Masculino'), + ('female', 'Femenino'), + ('both', 'Ambos') + ], string="Género", default='both') + + age_min = fields.Integer(string="Edad Mínima", default=0) + age_max = fields.Integer(string="Edad Máxima", default=99) + + min_value = fields.Float(string="Valor Mínimo") + max_value = fields.Float(string="Valor Máximo") + + unit_of_measure = fields.Char(string="Unidad de Medida") diff --git a/lims_management/models/product.py b/lims_management/models/product.py new file mode 100644 index 0000000..248a0d4 --- /dev/null +++ b/lims_management/models/product.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields + +class ProductTemplate(models.Model): + _inherit = 'product.template' + + is_analysis = fields.Boolean( + string="Es un Análisis Clínico", + help="Marcar si este producto es un análisis clínico." + ) + analysis_type = fields.Selection([ + ('hematology', 'Hematología'), + ('chemistry', 'Química Clínica'), + ('microbiology', 'Microbiología'), + ('immunology', 'Inmunología'), + ('endocrinology', 'Endocrinología'), + ('other', 'Otro') + ], string="Tipo de Análisis") + + technical_specifications = fields.Text( + string="Especificaciones Técnicas" + ) + + value_range_ids = fields.One2many( + 'lims.analysis.range', + 'analysis_id', + string="Rangos de Referencia" + ) diff --git a/lims_management/security/ir.model.access.csv b/lims_management/security/ir.model.access.csv index 97dd8b9..c12dc28 100644 --- a/lims_management/security/ir.model.access.csv +++ b/lims_management/security/ir.model.access.csv @@ -1 +1,2 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_lims_analysis_range_user,lims.analysis.range.user,model_lims_analysis_range,base.group_user,1,1,1,1 diff --git a/lims_management/views/analysis_views.xml b/lims_management/views/analysis_views.xml new file mode 100644 index 0000000..93584a6 --- /dev/null +++ b/lims_management/views/analysis_views.xml @@ -0,0 +1,49 @@ + + + + + + lims.analysis.range.tree + lims.analysis.range + + + + + + + + + + + + + + + product.template.form.lims + product.template + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lims_management/views/menus.xml b/lims_management/views/menus.xml index f6229ec..e4dc041 100644 --- a/lims_management/views/menus.xml +++ b/lims_management/views/menus.xml @@ -52,5 +52,39 @@ parent="lims_menu_root" action="action_lims_doctor" sequence="30"/> + + + + + + + Análisis Clínicos + product.template + kanban,form + [('is_analysis', '=', True)] + + +

+ Crea un nuevo análisis clínico +

+
+
+ + +