From d51d3b5d697056ff97c920f8e47344c25ee15b89 Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Thu, 17 Jul 2025 11:17:26 -0600 Subject: [PATCH 1/4] feat(#71): Implementar dashboards para administrador del laboratorio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dashboard de Estado de Órdenes: Vista gráfica y pivot de órdenes por estado - Dashboard de Productividad de Técnicos: Análisis de pruebas por técnico - Dashboard de Muestras: Estado y distribución de muestras por tipo - Dashboard de Parámetros Fuera de Rango: Identificación de resultados críticos - Dashboard de Análisis Más Solicitados: Top de análisis por período - Dashboard de Distribución Demográfica: Tests por género y rango de edad - Agregar campos computed age_range, patient_gender y patient_age_range - Configurar menú de Dashboards solo para administradores 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- dashboard_analysis.md | 78 +++++ lims_management/__manifest__.py | 1 + .../__pycache__/partner.cpython-312.pyc | Bin 4720 -> 5978 bytes lims_management/models/lims_test.py | 15 + lims_management/models/partner.py | 39 +++ lims_management/views/dashboard_views.xml | 321 ++++++++++++++++++ lims_management/views/menus.xml | 45 +++ 7 files changed, 499 insertions(+) create mode 100644 dashboard_analysis.md create mode 100644 lims_management/views/dashboard_views.xml 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 0b833d5c24e2bacf7b0c5c62270b4fef5cd1c0ec..3134b6ca18f7fa66ae060457bdb075fa07f2d84b 100644 GIT binary patch delta 1869 zcmb`IUrbw79LMjy{og`MODUzaaNB}#*$OQ%kiq5@29h~kz(i#+_1bgO6-v9E+a?<_ zi-ycS?4j(aKzwlev;?xa^uY&xFj-<^eA%1}q}c=d##@>C!o%YE-Tr~b`Q~}}<$Hdg z-|zhS{myL*ZTW8VFSWHshK}F9yL!n}ziR%2{pkL_ne{utSv{f*vVFs%dSpipTaHaN zadF;EBXVvvZF0rPSu4j#>N#b>>Rja7Qf;!OD4P;xu8x2iYDStZZDqB3EGlE{=4%ERwD42%&fZ)Xk_meVpYeJzwZ!m45 zenlOE-cX>g2o!Y)`a*&JB2d&J=nn->6oI13AakHO5ef_xiK5EnKr;{u3>JZ+DnKpZML-q0o4m&p&hcZgZ*sF|Y=V~ts2#+di+k#l;N8E>RiG))t7kvH`Y@`aYqTvsot5j*dqd*~6j#D+EHpj*?Fs7gd#MZ%P5N<>>l;*{u0 zL|;X&QDP_&V})esZnthwWGbPW3IakXsVxz66}f{fRr9;3u4?|Rnz?2bY>Qo^Vmy+L zi@X%)p{`2JC0cX-15p-HCdo?)9x!$7!MnoE)a=Zug+&xyY-HB-|eBD&hG5Q6MOqkgLD10+wbml_Wa+| zT}Nj?S#!5d$m*WddG~whfpIWr)ep1&lW^-{Fj{H^?yyoa{9Zy`NYZeceV^Q;Hb|bE znsQp>1j`;LQzpTq6zwbxT(ll3S_DgCN{W)tOffc?dv5CEOh@`eM$Ek(&?EZwL0|Hw zl!&8JDvW3nhStdNVJST!pPPehg8XD&G6W%mof2BfIm@Z>S0GV6dP&6i3`lJf$D*+W zK2ImgqsSX(q_`AG#gYl-etF$wf}TmjO5gDi#GNERTSg_N>3L%$nUGUhj3s1z3Rbi& zB#{&k7w-~YNXJt#3^&CWfYye?k+>+!5h)%IhXoeDPQ5bxd7LS}B;}ohfyXJ|Ao03& zztx-7ZrAaT>iB1j!KW?Q7)#5;j^Q8s|Ef1wC)wr6f`h26ms_mMGoN1k_~JvW_p!#e z&oybf{!}qqe*sisfLyV@N;{FWUTJ`uDBB(AN9b|Wap^RUk|CR~;X{^f&N|lItM0!U I&=^Ah0q^&$b^rhX delta 716 zcmZva&ubGw6vuZqHBB0`rb#x5%_iM8wdpDiwT0r(SnZ+cL8MiX1xeSPAx((8_;wW$ zK@T}7f>`F@5AZKY4odz3?}BI9qZh%G9(wWQytQe1a1P&n^XBuuZ{ILM?bn?0C7GO% z#9G~Zxbrf(t9&QrPrnuNG)&^bXwfvy^s_zrU&c(y5*us(k5l`R9@(cRLrNLB3?rJO zNBYqNw=~VuLQ^&~g3q>3ZV$21mmVLQ(y6Z0+dnXKVL@%g4I^5l+R&bs4pkIHgJoWi zk})&zBk#x{8GR;`GJh2-5uNvAE?MMS{Py7E(KDp7 zAew=J5*1>|P{DXkh=oCs~N}ZhL*MwFFFO+RUD*Wd$qco4?ijc>Xo6Ngmf$uP= z*}yEHt?{K)OW^NQD{`WY;5drTzonO}Ga@u1VW%MBThL^FgNhr}*b=;T#SFbu>se8g|F>JezeojfO#BRb)MU5^E67 zjjsk!a4zo(PpfbFSJmZra+gSzf5_ce@t;HXPwc`ymp + + + + + + sale.order.lab.dashboard.graph + sale.order + + + + + + + + + + sale.order.lab.dashboard.pivot + sale.order + + + + + + + + + + + Estado de Órdenes + sale.order + graph,pivot,tree,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,tree,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,tree,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,tree,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,tree + [('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,tree + [('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"/> + + + + + + + + + + + + + + + + Date: Thu, 17 Jul 2025 12:55:48 -0600 Subject: [PATCH 2/4] =?UTF-8?q?fix(#71):=20Agregar=20referencias=20expl?= =?UTF-8?q?=C3=ADcitas=20de=20vistas=20en=20acciones=20de=20dashboards?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar graph_view_id y pivot_view_id en todas las acciones - Resolver error 'View types not defined tree found in act_window action' - Mantener view_mode con 'tree' según convención Odoo 18 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lims_management/views/dashboard_views.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lims_management/views/dashboard_views.xml b/lims_management/views/dashboard_views.xml index a204bc3..7c6602f 100644 --- a/lims_management/views/dashboard_views.xml +++ b/lims_management/views/dashboard_views.xml @@ -34,6 +34,8 @@ graph,pivot,tree,form [('is_lab_request', '=', True)] {'search_default_group_by_state': 1} + +

No hay órdenes de laboratorio registradas @@ -78,6 +80,8 @@ lims.test graph,pivot,tree,form {'search_default_group_by_technician': 1, 'search_default_this_month': 1} + +

No hay pruebas registradas @@ -122,6 +126,8 @@ graph,pivot,tree,form [('is_lab_sample', '=', True)] {'search_default_group_by_state': 1} + +

No hay muestras registradas @@ -168,6 +174,8 @@ graph,pivot,tree,form [('test_id.state', '=', 'validated')] {'search_default_out_of_range': 1} + +

No hay resultados fuera de rango @@ -214,6 +222,8 @@ graph,pivot,tree [('order_id.is_lab_request', '=', True), ('product_id.is_analysis', '=', True)] {'search_default_group_by_product': 1} + +

No hay análisis registrados @@ -258,6 +268,8 @@ graph,pivot,tree [('state', '=', 'validated')] {'search_default_this_year': 1} + +

No hay tests validados From 753b84936ee33a0313311b66897f84e0b45dea3f Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Thu, 17 Jul 2025 13:31:28 -0600 Subject: [PATCH 3/4] fix(#71): Corregir sintaxis de view_ids en acciones de dashboards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Cambiar graph_view_id y pivot_view_id por view_ids con sintaxis correcta - Usar eval con lista de tuplas según formato Odoo estándar - Resolver error 'Invalid field graph_view_id on model ir.actions.act_window' 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lims_management/views/dashboard_views.xml | 30 ++++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lims_management/views/dashboard_views.xml b/lims_management/views/dashboard_views.xml index 7c6602f..ed6e5e2 100644 --- a/lims_management/views/dashboard_views.xml +++ b/lims_management/views/dashboard_views.xml @@ -34,8 +34,9 @@ graph,pivot,tree,form [('is_lab_request', '=', True)] {'search_default_group_by_state': 1} - - +

No hay órdenes de laboratorio registradas @@ -80,8 +81,9 @@ lims.test graph,pivot,tree,form {'search_default_group_by_technician': 1, 'search_default_this_month': 1} - - +

No hay pruebas registradas @@ -126,8 +128,9 @@ graph,pivot,tree,form [('is_lab_sample', '=', True)] {'search_default_group_by_state': 1} - - +

No hay muestras registradas @@ -174,8 +177,9 @@ graph,pivot,tree,form [('test_id.state', '=', 'validated')] {'search_default_out_of_range': 1} - - +

No hay resultados fuera de rango @@ -222,8 +226,9 @@ graph,pivot,tree [('order_id.is_lab_request', '=', True), ('product_id.is_analysis', '=', True)] {'search_default_group_by_product': 1} - - +

No hay análisis registrados @@ -268,8 +273,9 @@ graph,pivot,tree [('state', '=', 'validated')] {'search_default_this_year': 1} - - +

No hay tests validados From 02237c6d8c4240382e5371c0726be8a6ae7377e9 Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Fri, 18 Jul 2025 12:11:01 -0600 Subject: [PATCH 4/4] =?UTF-8?q?fix(#71):=20Corregir=20errores=20en=20dashb?= =?UTF-8?q?oards=20y=20scripts=20de=20inicializaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Cambiar 'tree' por 'list' en view_mode de todas las acciones de dashboard - Corregir sintaxis de filtros de fecha usando context_today() y relativedelta - Eliminar campo booleano is_out_of_range como medida en gráfico - Corregir referencia a sample.state en lugar de sample.sample_state - Reemplazar sample.test_ids por búsqueda de tests asociados - Eliminar consulta SQL directa a columna logo inexistente - Corregir método invalidate_cache() por _invalidate_cache() - Agregar sección de notificaciones en CLAUDE.md Los dashboards ahora funcionan correctamente sin errores de JavaScript. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .claude/settings.local.json | 3 +- CLAUDE.md | 96 ++++++++++++++++++----- lims_management/views/dashboard_views.xml | 21 +++-- scripts/update_company_logo_odoo18.py | 27 +++---- test/create_demo_data.py | 7 +- test/test_notification.py | 45 +++++++++++ 6 files changed, 149 insertions(+), 50 deletions(-) create mode 100644 test/test_notification.py 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/lims_management/views/dashboard_views.xml b/lims_management/views/dashboard_views.xml index ed6e5e2..c739741 100644 --- a/lims_management/views/dashboard_views.xml +++ b/lims_management/views/dashboard_views.xml @@ -31,7 +31,7 @@ Estado de Órdenes sale.order - graph,pivot,tree,form + graph,pivot,list,form [('is_lab_request', '=', True)] {'search_default_group_by_state': 1} Productividad de Técnicos lims.test - graph,pivot,tree,form + graph,pivot,list,form {'search_default_group_by_technician': 1, 'search_default_this_month': 1} Dashboard de Muestras stock.lot - graph,pivot,tree,form + graph,pivot,list,form [('is_lab_sample', '=', True)] {'search_default_group_by_state': 1} - @@ -174,7 +173,7 @@ Parámetros Fuera de Rango lims.result - graph,pivot,tree,form + graph,pivot,list,form [('test_id.state', '=', 'validated')] {'search_default_out_of_range': 1} Análisis Más Solicitados sale.order.line - graph,pivot,tree + graph,pivot,list [('order_id.is_lab_request', '=', True), ('product_id.is_analysis', '=', True)] {'search_default_group_by_product': 1} Distribución Demográfica de Tests lims.test - graph,pivot,tree + graph,pivot,list [('state', '=', 'validated')] {'search_default_this_year': 1} - - - - + + + + diff --git a/scripts/update_company_logo_odoo18.py b/scripts/update_company_logo_odoo18.py index bf7bbfc..a38c5a4 100644 --- a/scripts/update_company_logo_odoo18.py +++ b/scripts/update_company_logo_odoo18.py @@ -63,28 +63,21 @@ try: env.cr.rollback() continue - # Verificación final con consulta directa a la BD + # Verificación final usando el ORM print("\n" + "="*60) - print("VERIFICACIÓN FINAL (consulta directa):") + print("VERIFICACIÓN FINAL:") print("="*60) - env.cr.execute(""" - SELECT id, name, - CASE WHEN logo IS NOT NULL THEN 'SI' ELSE 'NO' END as tiene_logo, - CASE WHEN logo IS NOT NULL THEN length(logo) ELSE 0 END as logo_size - FROM res_company - ORDER BY id - """) - - for row in env.cr.fetchall(): - print(f"\nEmpresa ID {row[0]}:") - print(f" - Nombre: {row[1]}") - print(f" - Logo presente: {row[2]}") - if row[2] == 'SI': - print(f" - Tamaño del logo (base64): {row[3]:,} caracteres") + # Verificar a través del ORM que es más seguro + for company in env['res.company'].search([], order='id'): + print(f"\nEmpresa ID {company.id}:") + print(f" - Nombre: {company.name}") + print(f" - Logo presente: {'SI' if company.logo else 'NO'}") + if company.logo: + print(f" - Tamaño del logo (base64): {len(company.logo):,} caracteres") # Forzar actualización de caché - env['res.company'].invalidate_cache() + env['res.company']._invalidate_cache() print("\nLogo de la empresa actualizado exitosamente.") print("NOTA: Si el logo no aparece en la interfaz, puede ser necesario:") diff --git a/test/create_demo_data.py b/test/create_demo_data.py index 7d4ff66..35da52e 100644 --- a/test/create_demo_data.py +++ b/test/create_demo_data.py @@ -216,12 +216,13 @@ def create_demo_lab_data(cr): # Procesar cada muestra for sample in order.generated_sample_ids: # Marcar como recolectada - if sample.sample_state == 'pending_collection': + if sample.state == 'pending_collection': sample.action_collect() print(f" ✓ Muestra {sample.name} recolectada") # Procesar pruebas de esta muestra - for test in sample.test_ids: + tests = env['lims.test'].search([('sample_id', '=', sample.id)]) + for test in tests: print(f" - Procesando prueba: {test.product_id.name}") # Iniciar proceso si está en borrador @@ -241,7 +242,7 @@ def create_demo_lab_data(cr): print(f" ✓ Resultados ingresados") # Validar las primeras 2 órdenes completas y algunas pruebas de la tercera - should_validate = (idx < 2) or (idx == 2 and test == sample.test_ids[0]) + should_validate = (idx < 2) or (idx == 2 and test == tests[0]) if should_validate and test.state == 'result_entered': test.action_validate() diff --git a/test/test_notification.py b/test/test_notification.py new file mode 100644 index 0000000..3e4fa4c --- /dev/null +++ b/test/test_notification.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +Script de prueba para demostrar el uso de notificaciones +cuando se completan tareas en el sistema LIMS +""" + +import time +import subprocess +import sys + +def notify_completion(): + """Envía una notificación de sonido cuando se completa una tarea""" + try: + # Ejecutar el comando de PowerShell para el beep + subprocess.run(['powershell.exe', '-c', '[System.Media.SystemSounds]::Beep.Play()'], check=True) + print("[OK] Notificación enviada exitosamente") + except subprocess.CalledProcessError: + print("[ERROR] Error al enviar notificación") + except FileNotFoundError: + print("[ERROR] PowerShell no encontrado en el sistema") + +def simulate_task(): + """Simula una tarea que toma tiempo""" + print("Iniciando tarea de prueba...") + + # Simular trabajo + for i in range(3): + print(f" Procesando... {i+1}/3") + time.sleep(1) + + print("[OK] Tarea completada!") + return True + +def main(): + print("=== Prueba de Sistema de Notificaciones LIMS ===\n") + + # Ejecutar tarea + if simulate_task(): + print("\nEnviando notificación de finalización...") + notify_completion() + + print("\n=== Fin de la prueba ===") + +if __name__ == "__main__": + main() \ No newline at end of file