
- 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>
477 lines
14 KiB
Markdown
477 lines
14 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```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
|
|
|
|
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))
|
|
```
|
|
|
|
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"
|
|
|
|
# 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 <list>, no <tree>"
|
|
- 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`:
|
|
|
|
```xml
|
|
<!-- 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:
|
|
|
|
```xml
|
|
<!-- 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:
|
|
```xml
|
|
<!-- 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
|
|
|
|
```python
|
|
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:
|
|
|
|
```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,
|
|
'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()
|
|
```
|
|
|
|
2. 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:
|
|
|
|
```xml
|
|
<!-- 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:
|
|
|
|
```xml
|
|
<!-- En lugar de -->
|
|
<h4>LABORATORIO CLÍNICO</h4>
|
|
|
|
<!-- Usar -->
|
|
<h4>LABORATORIO CLÍNICO</h4>
|
|
```
|
|
|
|
- í = í
|
|
- Í = Í
|
|
- á = á
|
|
- Á = Á
|
|
- é = é
|
|
- É = É
|
|
- ó = ó
|
|
- Ó = Ó
|
|
- ú = ú
|
|
- Ú = Ú
|
|
- ñ = ñ
|
|
- Ñ = Ñ
|
|
|
|
##### Layout de etiquetas múltiples por página
|
|
|
|
```xml
|
|
<!-- 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>
|
|
```
|