clinical_laboratory/CLAUDE.md
Luis Ernesto Portillo Zaldivar 02237c6d8c fix(#71): Corregir errores en dashboards y scripts de inicialización
- 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 <noreply@anthropic.com>
2025-07-18 12:11:01 -06:00

14 KiB

CLAUDE.md

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.

Key Technologies

  • Odoo 18: ERP framework (Python-based)
  • PostgreSQL 15: Database
  • Docker & Docker Compose: Containerization
  • Gitea: Version control and issue tracking

Development Commands

Starting the Environment

# Start all services
docker-compose up -d

# MANDATORY: View initialization logs to check for errors
docker-compose logs odoo_init

# Stop and clean everything (removes volumes)
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
  4. Only proceed to next task if no errors are found
  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)
  4. Validate logs: Check for errors in initialization
  5. Commit & Push: git add -A && git commit -m "feat(#X): Task description" && git push
  6. Comment on issue: Update issue with task completion
  7. Mark task completed: Update todo list
  8. Proceed to next task: Only if no errors found

Database Operations

Direct PostgreSQL Access

# Connect to PostgreSQL
docker exec -it lims_db psql -U odoo -d odoo

For complex queries, use Python scripts with Odoo ORM:

  1. Create script (e.g., test/verify_products.py):
import odoo
import json

def verify_lab_order_products(cr):
    cr.execute("""SELECT ... FROM sale_order ...""")
    return cr.fetchall()

if __name__ == '__main__':
    db_name = 'lims_demo'
    registry = odoo.registry(db_name)
    with registry.cursor() as cr:
        results = verify_lab_order_products(cr)
        print(json.dumps(results, indent=4))
  1. Copy to container:
docker cp test/verify_products.py lims_odoo:/tmp/verify_products.py
  1. Execute:
docker-compose exec odoo python3 /tmp/verify_products.py

Gitea Integration

# Create issue
python utils/gitea_cli_helper.py create-issue --title "Title" --body "Description\nSupports multiple lines"

# Create PR with inline description
python utils/gitea_cli_helper.py create-pr --head "feature-branch" --base "dev" --title "Title" --body "Description"

# Create PR with description from file
python utils/gitea_cli_helper.py create-pr dev --title "feat(#31): Sample lifecycle" --description-file pr_description.txt

# Comment on issue
python utils/gitea_cli_helper.py comment-issue --issue-number 123 --body "Comment text"

# Close issue
python utils/gitea_cli_helper.py close-issue --issue-number 123

# Get issue details and comments
python utils/gitea_cli_helper.py get-issue --issue-number 8

# List all open issues
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
    • sale_order.py: Analysis orders and sample management
    • stock_lot.py: Sample tracking and lifecycle
    • analysis_range.py: Normal ranges for test results

Odoo 18 Specific Conventions

View Definitions

  • CRITICAL: Use <list> instead of <tree> in view XML - using <tree> 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 <list>)

Visibility Attributes

  • Use invisible attribute directly instead of attrs:

    <!-- Wrong (Odoo < 17) -->
    <field name="field" attrs="{'invisible': [('condition', '=', False)]}"/>
    
    <!-- Correct (Odoo 18) -->
    <field name="field" invisible="not condition"/>
    <field name="field" invisible="condition == False"/>
    

Context with ref()

  • Use eval attribute when using ref() in action contexts:

    <!-- Wrong - ref() undefined in client -->
    <field name="context">{'default_categ_id': ref('module.xml_id')}</field>
    
    <!-- Correct - evaluated on server -->
    <field name="context" eval="{'default_categ_id': ref('module.xml_id')}"/>
    

XPath in View Inheritance

  • Use flexible XPath expressions for robustness:
    <!-- More robust - works with list or tree -->
    <xpath expr="//field[@name='order_line']//field[@name='product_id']" position="attributes">
        <attribute name="domain">[('is_analysis', '=', True)]</attribute>
    </xpath>
    

Data Management

  • Initial Data: lims_management/data/ - Sequences, categories, basic configuration
  • 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.

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)
  • GITEA_REPO_NAME: Repository name (e.g., clinical_laboratory)

Important Patterns

Sample Lifecycle States

STATE_PENDING_COLLECTION = 'pending_collection'
STATE_COLLECTED = 'collected'
STATE_IN_ANALYSIS = 'in_analysis'
STATE_COMPLETED = 'completed'
STATE_CANCELLED = 'cancelled'

Barcode Generation

  • 13-digit format: YYMMDDNNNNNNC
  • Uses barcode Python library for Code-128 generation
  • Stored as PDF with human-readable text

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:
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,
        'is_lab_request': True,
        'order_line': [(0, 0, {
            'product_id': hemograma.product_variant_id.id,
            'product_uom_qty': 1
        })]
    })

if __name__ == '__main__':
    db_name = 'lims_demo'
    registry = odoo.registry(db_name)
    with registry.cursor() as cr:
        create_lab_requests(cr)
        cr.commit()
  1. Integrate in initialization or run separately

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

Al agregar archivos al manifest, seguir SIEMPRE este orden:

  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:
    • Modelos base (sin dependencias)
    • Modelos dependientes
    • Vistas que referencian acciones
    • menus.xml (SIEMPRE al final de views)
  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?
  • ¿Usaste id en lugar de active_id en contextos de formulario?
  • ¿Verificaste que todos los campos en las vistas existen en los modelos?
  • ¿Los nombres de métodos/acciones coinciden exactamente con los definidos en Python?
  • ¿Los widgets utilizados son válidos en Odoo 18?

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
  • Selección: selection, radio, selection_badge
  • Relaciones: many2one, many2many_tags
  • Estado: statusbar, badge, progressbar

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
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
  4. Si es necesario, simplificar la vista temporalmente para aislar el problema

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:

<!-- CORRECTO en Odoo 18 -->
<span t-field="record.barcode_field"
      t-options="{'widget': 'barcode', 'type': 'Code128', 'width': 250, 'height': 60, 'humanreadable': 1}"
      style="display: block;"/>

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:
    • type: Tipo de código ('Code128', 'EAN13', 'QR', etc.)
    • width: Ancho en píxeles
    • height: Alto en píxeles
    • humanreadable: 1 para mostrar texto legible, 0 para ocultarlo

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:

    <!-- En lugar de -->
    <h4>LABORATORIO CLÍNICO</h4>
    
    <!-- Usar -->
    <h4>LABORATORIO CL&#205;NICO</h4>
    
    • í = í
    • Í = Í
    • á = á
    • Á = Á
    • é = é
    • É = É
    • ó = ó
    • Ó = Ó
    • ú = ú
    • Ú = Ú
    • ñ = ñ
    • Ñ = Ñ
Layout de etiquetas múltiples por página
<!-- Contenedor principal sin salto de página -->
<div class="page">
    <t t-foreach="docs" t-as="o">
        <!-- Cada etiqueta como inline-block -->
        <div style="display: inline-block; vertical-align: top;
                    page-break-inside: avoid; overflow: hidden;">
            <!-- Contenido de la etiqueta -->
        </div>
    </t>
</div>