# Instrucciones para el uso de `tea` CLI

Este proyecto utiliza `tea` para interactuar con el repositorio de Gitea.

## Gestión de Gitea con `gitea_cli_helper.py`

Para interactuar con el repositorio de Gitea (crear issues, pull requests, comentar y cerrar issues) de forma robusta y con soporte para contenido multilínea, se recomienda utilizar el script de Python `gitea_cli_helper.py`. Este script lee la configuración sensible directamente desde el archivo `.env`.

### Configuración

Asegúrate de que las siguientes variables estén definidas en tu archivo `.env`:

-   `GITEA_API_KEY`: Tu Token de Acceso Personal (PAT) de Gitea.
-   `GITEA_API_KEY_URL`: La URL base de la API de tu instancia de Gitea (ej. `https://gitea.grupoconsiti.com/api/v1/`).
-   `GITEA_USERNAME`: Tu nombre de usuario de Gitea (propietario del repositorio).
-   `GITEA_REPO_NAME`: El nombre del repositorio (ej. `clinical_laboratory`).

### Uso

El script `gitea_cli_helper.py` utiliza `argparse` para diferentes comandos:

**IMPORTANTE**: Los archivos descriptivos (como `pr_description.txt`) creados para usar con el helper de Gitea:
- Pueden crearse dentro del proyecto temporalmente
- NO deben versionarse en git
- Deben eliminarse después de ser utilizados por el helper
- Se recomienda usar nombres descriptivos que faciliten su identificación para eliminación posterior

#### 1. Crear un Issue

```bash
python gitea_cli_helper.py create-issue --title "Título del Issue" --body "Descripción detallada del issue.\nSoporta múltiples líneas."
```

-   `--title`: Título del issue.
-   `--body`: Cuerpo o descripción del issue. Los saltos de línea (`\n`) se interpretarán correctamente.

#### 2. Crear un Pull Request

```bash
python gitea_cli_helper.py create-pr --head "tu-rama" --base "rama-destino" --title "Título del PR" --body "Descripción del Pull Request.\nSoporta múltiples líneas."
```

-   `--head`: Rama de origen (tu rama actual).
-   `--base`: Rama de destino (ej. `dev`, `main`).
-   `--title`: Título del Pull Request.
-   `--body`: Cuerpo o descripción del Pull Request.

#### 3. Comentar en un Issue

```bash
python gitea_cli_helper.py comment-issue --issue-number 123 --body "Este es un nuevo comentario.\nTambién soporta múltiples líneas."
```

-   `--issue-number`: Número del issue al que se desea añadir el comentario.
-   `--body`: Contenido del comentario.

#### 4. Cerrar un Issue

```bash
python gitea_cli_helper.py close-issue --issue-number 123
```

-   `--issue-number`: Número del issue a cerrar.

#### 5. Hacer Merge de un Pull Request

```bash
python gitea_cli_helper.py merge-pr --pr-number 46 --merge-method merge
```

-   `--pr-number`: Número del Pull Request a mergear.
-   `--merge-method`: Método de merge a utilizar. Opciones disponibles: `merge` (default), `squash`, `rebase`.

**IMPORTANTE**: Solo se permite hacer merge a la rama `dev`. El script validará automáticamente que el PR tenga como destino la rama `dev` antes de proceder. Si el PR apunta a otra rama (como `main`), el merge será rechazado con un mensaje de error.

**Ejemplo de uso:**
```bash
# Merge estándar (commit de merge)
python gitea_cli_helper.py merge-pr --pr-number 46

# Merge con squash (un solo commit con todos los cambios)
python gitea_cli_helper.py merge-pr --pr-number 46 --merge-method squash

# Merge con rebase (aplica commits individualmente sobre la rama base)
python gitea_cli_helper.py merge-pr --pr-number 46 --merge-method rebase
```

El script también verifica:
- Si el PR ya fue mergeado (mostrará mensaje informativo)
- Si el PR está cerrado sin mergear (error)
- Si el PR tiene conflictos o no es mergeable (error)

#### 6. Listar Issues Abiertos

```bash
python gitea_cli_helper.py list-open-issues
```

Lista todos los issues abiertos del repositorio, mostrando:
- Número del issue
- Título
- Etiquetas (si las tiene)
- Autor y fecha de creación
- URL del issue

**Ejemplo de salida:**
```
Issues abiertos (3):
--------------------------------------------------------------------------------
#15: [Extensión Opcional] Integración con Calendario para Citas
  Autor: luis_portillo | Creado: 2025-07-12
  URL: https://gitea.grupoconsiti.com/luis_portillo/clinical_laboratory/issues/15

#14: [Extensión Opcional] Portal Web para Pacientes/Médicos
  Autor: luis_portillo | Creado: 2025-07-12
  URL: https://gitea.grupoconsiti.com/luis_portillo/clinical_laboratory/issues/14
--------------------------------------------------------------------------------
Total: 2 issues abiertos
```

---


## Contexto del Proyecto

Al iniciar cada sesión de trabajo, es **mandatorio** leer los siguientes documentos para comprender el contexto completo de los requerimientos y el diseño técnico:

- `documents/requirements/RequerimientoInicial.md`
- `documents/requirements/ToBeDesing.md`

## Levantamiento de la Instancia de Odoo 18

Para levantar la instancia efímera de Odoo 18 junto con la base de datos de PostgreSQL, se utiliza Docker Compose.

**Comando de inicio:**
```bash
docker-compose up -d
```
Este comando levantará los servicios definidos en el archivo `docker-compose.yml` en modo "detached" (`-d`).

**Comando de detención y limpieza:**
Para detener los servicios y asegurar un estado limpio, **siempre se deben eliminar los volúmenes**, a menos que se indique lo contrario.
```bash
docker-compose down -v
```

### Verificación de la Inicialización

Después de levantar la instancia, es **mandatorio** verificar los registros del contenedor de inicialización para confirmar que los módulos se instalaron o actualizaron correctamente.

**Comando para ver los logs:**
```bash
docker-compose logs odoo_init
```

Busca errores en la salida. Si encuentras alguno, debes presentar un resumen del problema y sus posibles causas, como:
- **Dependencias faltantes:** Un módulo no se puede instalar porque requiere otro que no está presente.
- **Errores de sintaxis:** Problemas en archivos Python (`.py`) o XML (`.views`, `.xml`).
- **Permisos incorrectos:** Problemas de acceso a archivos o directorios.
- **Datos incorrectos:** Errores en los archivos de datos de demostración o iniciales.

### Política de Persistencia de la Instancia

Después de una instalación o actualización exitosa, la instancia de Odoo **debe permanecer activa** para permitir la validación manual por parte del usuario. **No se debe detener la instancia** (`docker-compose down -v`) hasta que el usuario confirme explícitamente que ha finalizado sus pruebas.

---

## Convenciones de Desarrollo en Odoo 18

Para evitar errores recurrentes, es **mandatorio** seguir las siguientes convenciones específicas para Odoo 18, especialmente en lo que respecta a la definición de vistas.

### Uso de Vistas de Lista (Tree Views)

En Odoo 18, la etiqueta `<tree>` ha sido **reemplazada por `<list>`**. El uso de `<tree>` provocará un error de validación (`ValueError: Wrong value for ir.ui.view.type: 'tree'`).

**Forma Incorrecta (Odoo < 18):**
```xml
<record id="view_example_tree" model="ir.ui.view">
    <field name="arch" type="xml">
        <tree string="Ejemplo">
            <field name="name"/>
        </tree>
    </field>
</record>
```

**Forma Correcta (Odoo 18):**
```xml
<record id="view_example_list" model="ir.ui.view">
    <field name="arch" type="xml">
        <list string="Ejemplo">
            <field name="name"/>
        </list>
    </field>
</record>
```

### Definición de Acciones de Ventana (`view_mode`)

Consecuente con el cambio anterior, al definir una acción de ventana (`ir.actions.act_window`) que deba mostrar una vista de lista, el `view_mode` debe ser `'list,form'` en lugar de `'tree,form'`.

**Forma Incorrecta:**
```xml
<record id="action_example" model="ir.actions.act_window">
    <field name="name">Ejemplo</field>
    <field name="res_model">example.model</field>
    <field name="view_mode">tree,form</field>
</record>
```

**Forma Correcta:**
```xml
<record id="action_example" model="ir.actions.act_window">
    <field name="name">Ejemplo</field>
    <field name="res_model">example.model</field>
    <field name="view_mode">list,form</field>
</record>

### Atributos de Visibilidad (`attrs`)

A partir de Odoo 17, el atributo `attrs` para controlar la visibilidad de los elementos ha sido **reemplazado por el uso directo de `invisible`**.

**Forma Incorrecta (Odoo < 17):**
```xml
<field name="special_field" attrs="{'invisible': [('is_special', '=', False)]}"/>
```

**Forma Correcta (Odoo 18):**
Se utiliza el atributo `invisible` con una expresión de dominio simplificada. La expresión se evalúa como verdadera para ocultar el campo.
```xml
<field name="special_field" invisible="not is_special"/>
```
O, de forma equivalente:
```xml
<field name="special_field" invisible="is_special == False"/>
```

### Uso de `ref()` en el Contexto de Acciones de Ventana

La función `ref('module.xml_id')` se utiliza para obtener el ID de base de datos de un registro a partir de su ID XML. Sin embargo, esta función **solo existe en el servidor**.

Cuando se define el `context` de una acción de ventana (`ir.actions.act_window`), este se evalúa en el cliente (navegador), donde `ref()` no está definido, causando un error `Name 'ref' is not defined`.

Para solucionar esto, el `context` debe ser evaluado en el servidor utilizando el atributo `eval`.

**Forma Incorrecta:**
```xml
<field name="context">{
    'default_categ_id': ref('lims_management.product_category_analysis')
}</field>
```
Esto envía la cadena `"{'default_categ_id': ref(...)}"` al cliente, que no puede procesarla.

**Forma Correcta:**
```xml
<field name="context" eval="{
    'default_categ_id': ref('lims_management.product_category_analysis')
}"/>
```
Al usar `eval`, Odoo ejecuta la expresión en el servidor, reemplaza `ref(...)` por el ID numérico correspondiente, y envía un diccionario JSON válido al cliente.

### Herencia de Vistas y XPath

Al heredar vistas para modificarlas, es crucial que las expresiones `XPath` sean precisas. Un error común es hacer referencia a campos o estructuras que han cambiado en la nueva versión de Odoo.

**Ejemplo de Error:**
Al intentar modificar las líneas de una orden de venta (`sale.order`), una expresión XPath que funcionaba en versiones anteriores puede fallar en Odoo 18.

**Expresión Incorrecta (para Odoo < 18):**
```xml
<xpath expr="//field[@name='order_line']/tree/field[@name='product_template_id']" position="attributes">
    <attribute name="domain">[('is_analysis', '=', True)]</attribute>
</xpath>
```
Esta expresión falla por dos razones:
1.  La vista de líneas ahora usa `<list>` en lugar de `<tree>`.
2.  El campo del producto en las líneas de venta es `product_id`, no `product_template_id`.

**Expresión Correcta (para Odoo 18):**
Para hacer la expresión más robusta y compatible, se puede usar `//` para buscar en cualquier nivel descendiente.
```xml
<xpath expr="//field[@name='order_line']//field[@name='product_id']" position="attributes">
    <attribute name="domain">[('is_analysis', '=', True)]</attribute>
</xpath>
```
Esta expresión busca el campo `product_id` dentro del campo `order_line`, sin importar si está dentro de una etiqueta `<list>` o `<tree>`, haciendo la herencia más resistente a cambios menores de estructura.

---

## Consultar la Base de Datos con un Script

Interactuar con la base de datos directamente usando `psql` a través de `docker-compose exec` puede ser complicado debido a la forma en que el shell maneja las comillas. Una alternativa más robusta y confiable es utilizar un script de Python que aproveche el ORM de Odoo.

### Procedimiento

1.  **Crear un Script de Python:**
    Crea un script que se conecte a la base de datos y ejecute la consulta deseada.

    **Ejemplo (`verify_products.py`):**
    ```python
    import odoo
    import json

    def verify_lab_order_products(cr):
        cr.execute("""
            SELECT
                so.name AS order_name,
                sol.id AS line_id,
                pt.name->>'en_US' AS product_name,
                pt.is_analysis
            FROM
                sale_order so
            JOIN
                sale_order_line sol ON so.id = sol.order_id
            JOIN
                product_product pp ON sol.product_id = pp.id
            JOIN
                product_template pt ON pp.product_tmpl_id = pt.id
            WHERE
                so.is_lab_request = TRUE;
        """)
        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.  **Copiar el Script al Contenedor:**
    Usa el comando `docker cp` para copiar el script al contenedor de Odoo.
    ```bash
    docker cp verify_products.py lims_odoo:/tmp/verify_products.py
    ```

3.  **Ejecutar el Script:**
    Ejecuta el script dentro del contenedor usando `docker-compose exec`.
    ```bash
    docker-compose exec odoo python3 /tmp/verify_products.py
    ```

Este método evita los problemas de entrecomillado y permite ejecutar consultas complejas de manera confiable.

---

## Creación de Datos de Demostración Complejos

Cuando los datos de demostración tienen dependencias complejas o requieren lógica de negocio (por ejemplo, cambiar el estado de un registro, o crear registros relacionados que dependen de otros), el uso de archivos XML puede ser limitado y propenso a errores de carga.

En estos casos, es preferible utilizar un script de Python para crear los datos de demostración.

### Procedimiento

1.  **Crear un Script de Python:**
    Crea un script que utilice el ORM de Odoo para crear los registros de demostración. Esto permite utilizar la lógica de negocio de los modelos, como los métodos `create` y `write`, y buscar registros existentes con `search` y `ref`.

    **Ejemplo (`create_lab_requests.py`):**
    ```python
    import odoo

    def create_lab_requests(cr):
        env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
        
        # Eliminar órdenes de venta de demostración no deseadas
        unwanted_orders = env['sale.order'].search([('name', 'in', ['S00001', ...])])
        for order in unwanted_orders:
            try:
                order.action_cancel()
            except Exception:
                pass
        try:
            unwanted_orders.unlink()
        except Exception:
            pass

        # Crear solicitudes de laboratorio
        patient1 = env.ref('lims_management.demo_patient_1')
        doctor1 = env.ref('lims_management.demo_doctor_1')
        hemograma = env.ref('lims_management.analysis_hemograma')

        env['sale.order'].create({
            'partner_id': patient1.id,
            'doctor_id': doctor1.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.  **Integrar el Script en la Inicialización:**
    Modifica el script `init_odoo.py` para que ejecute el script de creación de datos después de que Odoo haya terminado de instalar los módulos.

    **En `docker-compose.yml`**, asegúrate de que el script esté disponible en el contenedor `odoo_init`:
    ```yaml
    volumes:
      - ./create_lab_requests.py:/app/create_lab_requests.py
    ```

    **En `init_odoo.py`**, añade la lógica para ejecutar el script:
    ```python
    # --- Lógica para crear datos de demostración personalizados ---
    print("Creando datos de demostración complejos...")
    sys.stdout.flush()

    with open("/app/create_lab_requests.py", "r") as f:
        script_content = f.read()

    create_requests_command = f"""
        odoo shell -c {ODOO_CONF} -d {DB_NAME} <<'EOF'
{script_content}
EOF
    """

    result = subprocess.run(
        create_requests_command,
        shell=True,
        capture_output=True,
        text=True,
        check=False
    )
    ```
Este enfoque proporciona un control total sobre la creación de datos de demostración y evita los problemas de dependencia y orden de carga de los archivos XML.
