diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 02f7faa..ee793ed 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -27,7 +27,8 @@ "Bash(gh pr merge:*)", "Bash(git cherry-pick:*)", "Bash(del comment_issue_15.txt)", - "Bash(cat:*)" + "Bash(cat:*)", + "Bash(powershell.exe:*)" ], "deny": [] } diff --git a/CLAUDE.md b/CLAUDE.md index 28b0df6..97b1e27 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,6 +2,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +## Notifications + +When tasks complete, or you need autorizathion for an action notify me using: +powershell.exe -c "[System.Media.SystemSounds]::Beep.Play()" + ## Project Overview This is a Laboratory Information Management System (LIMS) module for Odoo 18 ERP, specifically designed for clinical laboratories. The module manages patients, samples, analyses, and test results. @@ -16,6 +21,7 @@ This is a Laboratory Information Management System (LIMS) module for Odoo 18 ERP ## Development Commands ### Starting the Environment + ```bash # Start all services docker-compose up -d @@ -30,10 +36,13 @@ docker-compose down -v **IMPORTANT**: Odoo initialization takes approximately 5 minutes. When using docker-compose commands, set timeout to 5 minutes (300000ms) to avoid premature timeouts. ### Instance Persistence Policy + After successful installation/update, the instance must remain active for user validation. Do NOT stop the instance until user explicitly confirms testing is complete. ### MANDATORY Testing Rule + **CRITICAL**: After EVERY task that modifies code, models, views, or data: + 1. Restart the ephemeral instance: `docker-compose down -v && docker-compose up -d` 2. Check initialization logs for errors: `docker-compose logs odoo_init | grep -i "error\|traceback\|exception"` 3. Verify successful completion: `docker-compose logs odoo_init | tail -30` @@ -41,7 +50,9 @@ After successful installation/update, the instance must remain active for user v 5. If errors are found, fix them before continuing ### Development Workflow per Task + When implementing issues with multiple tasks, follow this workflow for EACH task: + 1. **Stop instance**: `docker-compose down -v` 2. **Implement the task**: Make code changes 3. **Start instance**: `docker-compose up -d` (timeout: 300000ms) @@ -54,15 +65,18 @@ When implementing issues with multiple tasks, follow this workflow for EACH task ### Database Operations #### Direct PostgreSQL Access + ```bash # Connect to PostgreSQL docker exec -it lims_db psql -U odoo -d odoo ``` #### Python Script Method (Recommended) + For complex queries, use Python scripts with Odoo ORM: 1. Create script (e.g., `test/verify_products.py`): + ```python import odoo import json @@ -80,16 +94,19 @@ if __name__ == '__main__': ``` 2. Copy to container: + ```bash docker cp test/verify_products.py lims_odoo:/tmp/verify_products.py ``` 3. Execute: + ```bash docker-compose exec odoo python3 /tmp/verify_products.py ``` ### Gitea Integration + ```bash # Create issue python utils/gitea_cli_helper.py create-issue --title "Title" --body "Description\nSupports multiple lines" @@ -116,12 +133,14 @@ python utils/gitea_cli_helper.py list-open-issues ## Mandatory Reading At the start of each work session, read these documents to understand requirements and technical design: + - `documents/requirements/RequerimientoInicial.md` - `documents/requirements/ToBeDesing.md` ## Code Architecture ### Module Structure + - **lims_management/models/**: Core business logic - `partner.py`: Patient and healthcare provider management - `product.py`: Analysis types and categories @@ -132,31 +151,37 @@ At the start of each work session, read these documents to understand requiremen ### Odoo 18 Specific Conventions #### View Definitions + - **CRITICAL**: Use `` instead of `` in view XML - using `` causes error "El nodo raíz de una vista list debe ser , no " - View mode in actions must be `tree,form` not `list,form` (paradójicamente, el modo se llama "tree" pero el XML debe usar ``) #### Visibility Attributes + - Use `invisible` attribute directly instead of `attrs`: + ```xml - + ``` #### Context with ref() + - Use `eval` attribute when using `ref()` in action contexts: + ```xml {'default_categ_id': ref('module.xml_id')} - + ``` #### XPath in View Inheritance + - Use flexible XPath expressions for robustness: ```xml @@ -166,13 +191,15 @@ At the start of each work session, read these documents to understand requiremen ``` ### Data Management + - **Initial Data**: `lims_management/data/` - Sequences, categories, basic configuration -- **Demo Data**: +- **Demo Data**: - XML files in `lims_management/demo/` - Python scripts in `test/` directory for complex demo data creation - Use `noupdate="1"` for demo data to prevent reloading ### Security Model + - Access rights defined in `security/ir.model.access.csv` - Field-level security in `security/security.xml` - Group-based permissions: Laboratory Technician, Manager, etc. @@ -180,6 +207,7 @@ At the start of each work session, read these documents to understand requiremen ## Environment Variables Required in `.env` file: + - `GITEA_API_KEY`: Personal Access Token for Gitea - `GITEA_API_KEY_URL`: Gitea API base URL (e.g., `https://gitea.grupoconsiti.com/api/v1/`) - `GITEA_USERNAME`: Gitea username (repository owner) @@ -188,6 +216,7 @@ Required in `.env` file: ## Important Patterns ### Sample Lifecycle States + ```python STATE_PENDING_COLLECTION = 'pending_collection' STATE_COLLECTED = 'collected' @@ -197,6 +226,7 @@ STATE_CANCELLED = 'cancelled' ``` ### Barcode Generation + - 13-digit format: YYMMDDNNNNNNC - Uses `barcode` Python library for Code-128 generation - Stored as PDF with human-readable text @@ -204,30 +234,34 @@ STATE_CANCELLED = 'cancelled' ### Demo Data Creation #### XML Files (Simple Data) + - Use for basic records without complex dependencies - Place in `lims_management/demo/` - Use `noupdate="1"` to prevent reloading - **IMPORTANT**: Do NOT create sale.order records in XML demo files - use Python scripts instead #### Python Scripts (Complex Data) + For data with dependencies or business logic: #### Test Scripts + - **IMPORTANT**: Always create test scripts inside the `test/` folder within the project directory - Example: `test/test_sample_generation.py` - This ensures scripts are properly organized and accessible 1. Create script: + ```python import odoo def create_lab_requests(cr): env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) - + # Use ref() to get existing records patient1 = env.ref('lims_management.demo_patient_1') hemograma = env.ref('lims_management.analysis_hemograma') - + # Create records with business logic env['sale.order'].create({ 'partner_id': patient1.id, @@ -251,53 +285,64 @@ if __name__ == '__main__': ## Git Workflow ### Pre-commit Hook + Automatically installed via `scripts/install_hooks.sh`: + - Prevents commits to 'main' or 'dev' branches - Enforces feature branch workflow ### Branch Naming + - Feature branches: `feature/XX-description` (where XX is issue number) - Always create PRs to 'dev' branch, not 'main' ## Desarrollo de nuevos modelos y vistas -### Orden de carga en __manifest__.py +### Orden de carga en **manifest**.py + Al agregar archivos al manifest, seguir SIEMPRE este orden: -1. security/*.xml (grupos y categorías) + +1. security/\*.xml (grupos y categorías) 2. security/ir.model.access.csv -3. data/*.xml (secuencias, categorías, datos base) -4. views/*_views.xml en este orden específico: +3. data/\*.xml (secuencias, categorías, datos base) +4. views/\*\_views.xml en este orden específico: - Modelos base (sin dependencias) - - Modelos dependientes + - Modelos dependientes - Vistas que referencian acciones - menus.xml (SIEMPRE al final de views) -5. wizards/*.xml -6. reports/*.xml -7. demo/*.xml +5. wizards/\*.xml +6. reports/\*.xml +7. demo/\*.xml ### Desarrollo de modelos relacionados + Cuando crees modelos que se relacionan entre sí en el mismo issue: #### Fase 1: Modelos base + 1. Crear modelos SIN campos One2many 2. Solo incluir campos básicos y Many2one si el modelo referenciado ya existe 3. Probar que la instancia levante #### Fase 2: Relaciones + 1. Agregar campos One2many en los modelos padre 2. Verificar que todos los inverse_name existan 3. Probar nuevamente #### Fase 3: Vistas complejas + 1. Agregar vistas con referencias a acciones 2. Verificar que las acciones referenciadas ya estén definidas ### Contextos en vistas XML + - En formularios: usar `id` (NO `active_id`) - En acciones de ventana: usar `active_id` - En campos One2many: usar `parent` para referenciar el registro padre ### Checklist antes de reiniciar instancia + - [ ] ¿Los modelos referenciados en relaciones ya existen? - [ ] ¿Las acciones/vistas referenciadas se cargan ANTES? - [ ] ¿Los grupos en ir.model.access.csv coinciden con los de security.xml? @@ -309,17 +354,20 @@ Cuando crees modelos que se relacionan entre sí en el mismo issue: ### Desarrollo de vistas - Mejores prácticas #### Antes de crear vistas: + 1. **Verificar campos del modelo**: SIEMPRE revisar qué campos existen con `grep "fields\." models/archivo.py` 2. **Verificar métodos disponibles**: Buscar métodos con `grep "def action_" models/archivo.py` 3. **Verificar campos relacionados**: Confirmar que los campos related tienen la ruta correcta #### Orden de creación de vistas: + 1. **Primero**: Definir todas las acciones (ir.actions.act_window) en un solo lugar 2. **Segundo**: Crear las vistas (form, list, search, etc.) 3. **Tercero**: Crear los menús que referencian las acciones 4. **Cuarto**: Si hay referencias cruzadas entre archivos, considerar consolidar en un solo archivo #### Widgets válidos en Odoo 18: + - Numéricos: `float`, `integer`, `monetary` (NO `float_time` para datos generales) - Texto: `text`, `char`, `html` (NO `text_emojis`) - Booleanos: `boolean`, `boolean_toggle`, `boolean_button` @@ -330,22 +378,27 @@ Cuando crees modelos que se relacionan entre sí en el mismo issue: #### Errores comunes y soluciones: ##### Error: "External ID not found" + - **Causa**: Referencia a un ID que aún no fue cargado -- **Solución**: Reorganizar orden en __manifest__.py o mover definición al mismo archivo +- **Solución**: Reorganizar orden en **manifest**.py o mover definición al mismo archivo ##### Error: "Field 'X' does not exist" + - **Causa**: Vista referencia campo inexistente en el modelo - **Solución**: Verificar modelo y agregar campo o corregir nombre en vista ##### Error: "action_X is not a valid action" + - **Causa**: Nombre de método incorrecto en botón - **Solución**: Verificar nombre exacto del método en el modelo Python ##### Error: "Invalid widget" + - **Causa**: Uso de widget no existente o deprecated - **Solución**: Usar widgets estándar de Odoo 18 #### Estrategia de depuración: + 1. Leer el error completo en los logs 2. Identificar archivo y línea exacta del problema 3. Verificar que el elemento referenciado existe y está accesible @@ -354,16 +407,18 @@ Cuando crees modelos que se relacionan entre sí en el mismo issue: ### Manejo de códigos de barras en reportes QWeb (Odoo 18) #### Generación de códigos de barras + Para mostrar códigos de barras en reportes PDF, usar el widget nativo de Odoo: ```xml - ``` #### Consideraciones importantes: + 1. **NO usar** rutas directas como `/report/barcode/Code128/` - esta sintaxis está deprecated 2. **Usar siempre** `t-field` con el widget barcode para renderizado correcto 3. **Parámetros disponibles** en t-options: @@ -375,19 +430,23 @@ Para mostrar códigos de barras en reportes PDF, usar el widget nativo de Odoo: #### Problemas comunes y soluciones: ##### Código de barras vacío en PDF + - **Causa**: Campo computed sin store=True o sintaxis incorrecta - **Solución**: Asegurar que el campo esté almacenado y usar widget barcode ##### Caracteres especiales en reportes (tildes, ñ) + - **Problema**: Aparecen como "ñ" o "í" en lugar de "ñ" o "í" - **Solución**: Usar referencias numéricas de caracteres XML: + ```xml

LABORATORIO CLÍNICO

- +

LABORATORIO CLÍNICO

``` + - í = í - Í = Í - á = á @@ -402,15 +461,16 @@ Para mostrar códigos de barras en reportes PDF, usar el widget nativo de Odoo: - Ñ = Ñ ##### Layout de etiquetas múltiples por página + ```xml
-
-``` \ No newline at end of file +``` diff --git a/dashboard_analysis.md b/dashboard_analysis.md new file mode 100644 index 0000000..54479fd --- /dev/null +++ b/dashboard_analysis.md @@ -0,0 +1,78 @@ +# Análisis de Dashboards para LIMS - Issue #71 + +## Dashboards Implementables sin Módulos Adicionales ni Cambios Estructurales + +### 1. ✅ Dashboard de Estado de Órdenes +**Factibilidad**: Alta +- Usar vistas graph y pivot nativas de Odoo +- Datos disponibles: sale.order con is_lab_request=True +- Métricas: órdenes por estado, por fecha, por paciente + +### 2. ✅ Dashboard de Productividad de Técnicos +**Factibilidad**: Alta +- Datos disponibles: lims.test (technician_id, state, create_date, validation_date) +- Métricas: pruebas procesadas por técnico, tiempos promedio, estados + +### 3. ✅ Dashboard de Muestras +**Factibilidad**: Alta +- Datos disponibles: stock.lot con is_lab_sample=True +- Métricas: muestras por estado, rechazos, re-muestreos + +### 4. ✅ Dashboard de Parámetros Fuera de Rango +**Factibilidad**: Alta +- Datos disponibles: lims.result (is_out_of_range, is_critical) +- Métricas: resultados críticos, fuera de rango por parámetro + +### 5. ✅ Dashboard de Análisis Más Solicitados +**Factibilidad**: Alta +- Datos disponibles: sale.order.line con productos is_analysis=True +- Métricas: top análisis, tendencias por período + +### 6. ⚠️ Dashboard de Tiempos de Respuesta +**Factibilidad**: Media +- Requiere campos calculados (no almacenados actualmente) +- Necesitaría agregar campos store=True para métricas de tiempo + +### 7. ❌ Dashboard de Facturación +**Factibilidad**: Baja +- Requiere módulo account (facturación) +- No está en las dependencias actuales + +### 8. ❌ Dashboard de Inventario de Reactivos +**Factibilidad**: Baja +- Requiere configuración adicional de stock +- No hay modelo específico para reactivos + +## Implementación Técnica + +### Herramientas Disponibles en Odoo 18: +1. **Vistas Graph**: Gráficos de barras, líneas, pie +2. **Vistas Pivot**: Tablas dinámicas +3. **Vistas Cohort**: Análisis de cohortes +4. **Filtros y Agrupaciones**: Para segmentar datos +5. **Acciones de Servidor**: Para cálculos complejos + +### Estructura Propuesta: +```xml + + +``` + +## Recomendación + +Sugiero comenzar con los 5 dashboards marcados con ✅ ya que: +1. Utilizan datos existentes +2. No requieren cambios en modelos +3. Usan herramientas nativas de Odoo +4. Proveen valor inmediato al administrador + +Orden de implementación sugerido: +1. Dashboard de Estado de Órdenes (más básico) +2. Dashboard de Productividad de Técnicos +3. Dashboard de Muestras +4. Dashboard de Parámetros Fuera de Rango +5. Dashboard de Análisis Más Solicitados \ No newline at end of file diff --git a/lims_management/__manifest__.py b/lims_management/__manifest__.py index 16f9d1f..d188b78 100644 --- a/lims_management/__manifest__.py +++ b/lims_management/__manifest__.py @@ -45,6 +45,7 @@ 'views/analysis_parameter_views.xml', 'views/product_template_parameter_config_views.xml', 'views/parameter_dashboard_views.xml', + 'views/dashboard_views.xml', 'views/menus.xml', 'views/lims_config_views.xml', 'report/sample_label_report.xml', diff --git a/lims_management/models/__pycache__/partner.cpython-312.pyc b/lims_management/models/__pycache__/partner.cpython-312.pyc index 0b833d5..3134b6c 100644 Binary files a/lims_management/models/__pycache__/partner.cpython-312.pyc and b/lims_management/models/__pycache__/partner.cpython-312.pyc differ diff --git a/lims_management/models/lims_test.py b/lims_management/models/lims_test.py index 3a9f3c9..98f9699 100644 --- a/lims_management/models/lims_test.py +++ b/lims_management/models/lims_test.py @@ -116,6 +116,21 @@ class LimsTest(models.Model): default=lambda self: self.env.company ) + # Campos para dashboards demográficos + patient_gender = fields.Selection( + related='patient_id.gender', + string='Género del Paciente', + store=True, + readonly=True + ) + + patient_age_range = fields.Selection( + related='patient_id.age_range', + string='Rango de Edad', + store=True, + readonly=True + ) + @api.depends('company_id') def _compute_require_validation(self): """Calcula si la prueba requiere validación basado en configuración.""" diff --git a/lims_management/models/partner.py b/lims_management/models/partner.py index b2f567d..0c85b92 100644 --- a/lims_management/models/partner.py +++ b/lims_management/models/partner.py @@ -29,6 +29,17 @@ class ResPartner(models.Model): help="Edad calculada en años basada en la fecha de nacimiento" ) + age_range = fields.Selection([ + ('0-10', '0-10 años'), + ('11-20', '11-20 años'), + ('21-30', '21-30 años'), + ('31-40', '31-40 años'), + ('41-50', '41-50 años'), + ('51-60', '51-60 años'), + ('61-70', '61-70 años'), + ('71+', 'Más de 70 años') + ], string="Rango de Edad", compute='_compute_age_range', store=True) + is_pregnant = fields.Boolean( string="Embarazada", help="Marcar si la paciente está embarazada (solo aplica para género femenino)" @@ -54,6 +65,34 @@ class ResPartner(models.Model): else: partner.age = 0 + @api.depends('birthdate_date') + def _compute_age_range(self): + """Calcula el rango de edad basado en la edad""" + for partner in self: + if partner.birthdate_date: + today = date.today() + delta = relativedelta(today, partner.birthdate_date) + age = delta.years + + if age <= 10: + partner.age_range = '0-10' + elif age <= 20: + partner.age_range = '11-20' + elif age <= 30: + partner.age_range = '21-30' + elif age <= 40: + partner.age_range = '31-40' + elif age <= 50: + partner.age_range = '41-50' + elif age <= 60: + partner.age_range = '51-60' + elif age <= 70: + partner.age_range = '61-70' + else: + partner.age_range = '71+' + else: + partner.age_range = False + @api.constrains('is_pregnant', 'gender') def _check_pregnant_gender(self): """Valida que solo pacientes de género femenino puedan estar embarazadas""" diff --git a/lims_management/views/dashboard_views.xml b/lims_management/views/dashboard_views.xml new file mode 100644 index 0000000..c739741 --- /dev/null +++ b/lims_management/views/dashboard_views.xml @@ -0,0 +1,338 @@ + + + + + + + sale.order.lab.dashboard.graph + sale.order + + + + + + + + + + sale.order.lab.dashboard.pivot + sale.order + + + + + + + + + + + Estado de Órdenes + sale.order + graph,pivot,list,form + [('is_lab_request', '=', True)] + {'search_default_group_by_state': 1} + + +

+ No hay órdenes de laboratorio registradas +

+

+ Este dashboard muestra el estado actual de todas las órdenes de laboratorio. +

+
+
+ + + + + + lims.test.technician.productivity.graph + lims.test + + + + + + + + + + + lims.test.technician.productivity.pivot + lims.test + + + + + + + + + + + Productividad de Técnicos + lims.test + graph,pivot,list,form + {'search_default_group_by_technician': 1, 'search_default_this_month': 1} + + +

+ No hay pruebas registradas +

+

+ Este dashboard muestra la productividad de cada técnico del laboratorio. +

+
+
+ + + + + + stock.lot.sample.status.graph + stock.lot + + + + + + + + + + stock.lot.sample.type.pivot + stock.lot + + + + + + + + + + + Dashboard de Muestras + stock.lot + graph,pivot,list,form + [('is_lab_sample', '=', True)] + {'search_default_group_by_state': 1} + + +

+ No hay muestras registradas +

+

+ Este dashboard muestra el estado de todas las muestras del laboratorio. +

+
+
+ + + + + + lims.result.out.of.range.graph + lims.result + + + + + + + + + + lims.result.critical.pivot + lims.result + + + + + + + + + + + + Parámetros Fuera de Rango + lims.result + graph,pivot,list,form + [('test_id.state', '=', 'validated')] + {'search_default_out_of_range': 1} + + +

+ No hay resultados fuera de rango +

+

+ Este dashboard muestra los parámetros que están fuera de los rangos normales. +

+
+
+ + + + + + sale.order.line.top.analysis.graph + sale.order.line + + + + + + + + + + + sale.order.line.analysis.period.pivot + sale.order.line + + + + + + + + + + + + Análisis Más Solicitados + sale.order.line + graph,pivot,list + [('order_id.is_lab_request', '=', True), ('product_id.is_analysis', '=', True)] + {'search_default_group_by_product': 1} + + +

+ No hay análisis registrados +

+

+ Este dashboard muestra los análisis más solicitados en el laboratorio. +

+
+
+ + + + + + lims.test.gender.distribution.graph + lims.test + + + + + + + + + + lims.test.demographics.pivot + lims.test + + + + + + + + + + + Distribución Demográfica de Tests + lims.test + graph,pivot,list + [('state', '=', 'validated')] + {'search_default_this_year': 1} + + +

+ No hay tests validados +

+

+ Este dashboard muestra la distribución de tests por características demográficas de los pacientes. +

+
+
+ + + + + + lims.test.dashboard.search + lims.test + + + + + + + + + + + + + + + + + + + + + + + + + + + lims.result.dashboard.search + lims.result + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/lims_management/views/menus.xml b/lims_management/views/menus.xml index c8bd994..a726237 100644 --- a/lims_management/views/menus.xml +++ b/lims_management/views/menus.xml @@ -155,6 +155,51 @@ action="action_lims_result" sequence="30"/> + + + + + + + + + + + + + + + +