# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## 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>` - using `<tree>` causes `ValueError: Wrong value for ir.ui.view.type: 'tree'`
- View mode in actions must be `list,form` not `tree,form`

#### 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?