clinical_laboratory/GEMINI.md
Luis Ernesto Portillo Zaldivar 9c32be4c28 feat: Agregar comando list-open-issues a gitea_cli_helper.py
- Nuevo comando para listar todos los issues abiertos
- Muestra número, título, autor, fecha y URL
- Útil para verificar el estado de los issues
- Documentado en GEMINI.md con ejemplos
2025-07-15 00:06:37 -06:00

16 KiB

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

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

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

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

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

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:

# 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

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:

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.

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:

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):

<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):

<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:

<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:

<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.

<field name="special_field" invisible="not is_special"/>

O, de forma equivalente:

<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:

<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:

<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):

<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.

<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):

    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.

    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.

    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):

    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:

    volumes:
      - ./create_lab_requests.py:/app/create_lab_requests.py
    

    En init_odoo.py, añade la lógica para ejecutar el script:

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