435 lines
16 KiB
Markdown
435 lines
16 KiB
Markdown
# Instrucciones para el uso de `tea` CLI
|
|
|
|
Este proyecto utiliza `tea` para interactuar con el repositorio de Gitea.
|
|
|
|
## Crear un Issue (Modo no Interactivo)
|
|
|
|
Para crear un nuevo issue de forma no interactiva, se utiliza el siguiente comando, proporcionando todos los datos necesarios mediante flags:
|
|
|
|
```bash
|
|
tea issue create --title "Título del Issue" --description "Descripción detallada del issue." --labels "etiqueta1,etiqueta2"
|
|
```
|
|
|
|
- `--title`: Especifica el título del issue.
|
|
- `--description`: Especifica la descripción o cuerpo del issue. Para contenido multilínea, ver la sección "Manejo de Contenido Multilínea".
|
|
- `--labels`: Especifica una o más etiquetas separadas por comas.
|
|
|
|
---
|
|
|
|
## Comentar en un Issue
|
|
|
|
Para agregar un comentario a un issue existente, se utiliza el comando `comment` seguido del número del issue y el texto del comentario entre comillas.
|
|
|
|
**Formato correcto:**
|
|
|
|
```bash
|
|
tea comment <NÚMERO_ISSUE> "Tu comentario aquí"
|
|
```
|
|
|
|
**Ejemplo:**
|
|
|
|
```bash
|
|
tea comment 3 "Comentario de prueba"
|
|
```
|
|
|
|
**Nota:** No se deben utilizar flags como `-i` o `--message`. El formato es directo.
|
|
|
|
### Manejo de Contenido Multilínea
|
|
|
|
Para comentarios o descripciones que contengan múltiples líneas, es crucial escapar los saltos de línea (`\n`) dentro de la cadena. Esto asegura que el shell interprete todo el contenido como un único argumento.
|
|
|
|
**Ejemplo para `tea comment`:**
|
|
|
|
```bash
|
|
tea comment <NÚMERO_ISSUE> "Línea 1 del comentario.\nLínea 2 del comentario.\nLínea 3 del comentario."
|
|
```
|
|
|
|
**Ejemplo para `tea issue create` (en `--description`):**
|
|
|
|
```bash
|
|
tea issue create --title "Título del Issue" --description "Descripción detallada del issue.\nEsta es la segunda línea de la descripción.\nY esta es la tercera." --labels "etiqueta1"
|
|
```
|
|
|
|
**Nota sobre compatibilidad de Shell:**
|
|
En entornos Windows, el uso de `printf` para mensajes multilínea puede no estar disponible. En su lugar, se puede intentar usar `echo` con saltos de línea escapados (`\n`), aunque su comportamiento puede variar. La forma más robusta es asegurar que toda la cadena se pase como un único argumento al comando `tea`.
|
|
|
|
|
|
---
|
|
|
|
## Realizar Commits
|
|
|
|
Debido a problemas de interpretación de comillas en el shell de ejecución, el uso de `git commit -m "mensaje"` puede fallar. Para evitar estos problemas, se debe pasar el mensaje del commit a través de la entrada estándar (`stdin`).
|
|
|
|
### Política de Mensajes de Commit
|
|
|
|
**Es mandatorio que el título de cada commit referencie el número del issue que resuelve.** Esto se hace para mantener una trazabilidad clara entre el código y las tareas.
|
|
|
|
**Formato del Título:**
|
|
```
|
|
<tipo>(#<issue_id>): <descripción breve>
|
|
```
|
|
- **`<tipo>`:** `feat` (nueva funcionalidad), `fix` (corrección de bug), `docs` (cambios en documentación), `style` (formato), `refactor`, `test`, `chore` (otras tareas).
|
|
- **`(<issue_id>)`:** El número del issue entre paréntesis y precedido de `#`.
|
|
|
|
**Ejemplo:**
|
|
```
|
|
feat(#4): Agregar campos de género y fecha de nacimiento al paciente
|
|
```
|
|
|
|
### Método Recomendado
|
|
|
|
Utiliza el comando `echo` y una tubería (`|`) para enviar el mensaje a `git commit -F -`.
|
|
|
|
**Commit de una sola línea:**
|
|
|
|
```bash
|
|
echo "feat(#4): Tu mensaje de commit conciso" | git commit -F -
|
|
```
|
|
|
|
**Commit multilínea:**
|
|
Para mensajes de commit multilínea, la forma más segura es usar `printf` que maneja mejor los saltos de línea (`
|
|
`):
|
|
|
|
```bash
|
|
printf "feat(#4): Título del commit
|
|
|
|
Cuerpo del mensaje con descripción detallada." | git commit -F -
|
|
```
|
|
|
|
Esto asegura que el formato del mensaje del commit se preserve correctamente.
|
|
|
|
|
|
---
|
|
|
|
## Crear un Pull Request
|
|
|
|
Para crear un pull request (PR), se utiliza el comando `tea pulls create`. Debes especificar la rama base (hacia donde van los cambios) y la rama `head` (tu rama actual), junto con un título que referencie el issue que resuelve.
|
|
|
|
**Formato del comando:**
|
|
|
|
```bash
|
|
tea pulls create --base "<rama_base>" --head "<tu_rama>" --title "<Tipo>(#issue): Título descriptivo"
|
|
```
|
|
|
|
**Ejemplo:**
|
|
|
|
```bash
|
|
tea pulls create --base "dev" --head "feature/3-core-setup" --title "feat(#3): Actualiza instrucciones en GEMINI.md"
|
|
```
|
|
|
|
- `--base`: La rama de destino (ej. `dev`, `main`).
|
|
- `--head`: Tu rama de trabajo actual.
|
|
- `--title`: Un título claro que incluya el tipo de cambio (`feat`, `fix`, `docs`) y el número de issue.
|
|
|
|
---
|
|
|
|
## 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.
|