Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/.github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Instrucciones para Copilot – Revisión de código Odoo

## Contexto

Este repositorio contiene módulos Odoo. La versión objetivo está declarada en `__manifest__.py` de cada módulo. Las reglas específicas por dominio viven en `.github/instructions/*.instructions.md`, cada una con `applyTo:` que delimita a qué archivos aplica.

## Reglas globales (aplican a todo cambio)

1. **Responder siempre en español.**
2. Feedback **breve, concreto y accionable**. Lista corta de 3–7 puntos. Evitar párrafos largos y no repetir lo que ya dice la descripción del PR.
3. Corregir errores de tipeo u ortografía evidentes en nombres y comentarios (cuando sean claros).
4. No proponer traducciones de docstrings/comentarios entre idiomas.
5. No exigir docstrings en métodos que no los tienen. Si ya existe uno, PEP8 alcanza; falta de tipos o `return` **no es un error**.
6. No proponer cambios puramente estéticos (espacios, comillas, orden de imports).
7. Traducciones: `_()` y `self.env._()` son indistintos; solo marcar mensajes/textos al usuario que no estén envueltos.

## Resumen operativo

- **Si hay cambio estructural** (rename de campos almacenados, cambio de tipo, split/merge, nuevos `compute` con `store=True` con backfill, cambio de keys de `selection`, nuevas `UNIQUE`, cambios en `ir.model.data`/XML IDs) → **proponer script de migración** en `migrations/<version>/` con enfoque idempotente y en lotes. Ver `migrations.instructions.md`.
- **Si hay cambio en modelos** → aplicar `models.instructions.md`.
- **Si hay cambio en vistas XML** → `views.instructions.md`.
- **Si hay cambio en seguridad / ACL / `cr.execute` / `eval`** → `security.instructions.md`.
- **Si cambia `__manifest__.py`** → `manifest.instructions.md`.
- **Si el diff es grande y sensible a performance** → `performance.instructions.md`.
- **Si introduce funcionalidad no trivial sin tests** → `tests.instructions.md`.
- **Si hay texto al usuario sin `_()`** → `i18n.instructions.md`.

## Versionado Odoo

Cada módulo declara versión en `__manifest__.py`. Cuando hay diferencias relevantes entre v18 y v19, los archivos en `instructions/` marcan la regla como "Odoo 19+" o "Odoo 18".

Cambios clave de Odoo 19 a tener en cuenta (detalle en cada `instructions.md` específica):
- `_sql_constraints` → `models.Constraint`, `models.Index`, `models.UniqueIndex`.
- `@api.one`/`@api.multi` eliminados; `@api.ondelete` para validación de borrado.
- `<tree>` → `<list>`; `attrs={...}` → atributos directos (`invisible=`, `readonly=`).
- `t-esc` deprecado → `t-out`.
- `cr.execute(...)` crudo desaconsejado → clase `SQL` con `execute_query_dict()`.
- Dominios con clase `Domain` y operadores `&`, `|`, `~` sobre instancias.
- Crons: `_commit_progress(remaining=, processed=)` en lugar de `notify_progress`.
- `category_id` de `res.groups` → `privilege_id` + `res.groups.privilege`.

## Estilo del feedback

- Formato recomendado: `**categoría** · descripción concreta · sugerencia`.
- Un comentario por issue; no duplicar la misma observación en varios archivos.
- Preferir mencionar la regla concreta (ej. "queries parametrizadas") antes que la teoría.
- **Checklist rápida**:

| Categoría | Qué comprobar |
|---|---|
| Modelos | Relaciones con `comodel_name`/`ondelete`; `@api.depends` correcto; `super()` preservado |
| Vistas XML | Herencias con `xpath` acotado; campos existentes; nada de redefinir vistas enteras |
| Seguridad | ACL mínimo; sin `cr.execute` con interpolación; sin `eval()` sobre input externo |
| Migraciones | Cambios estructurales → script idempotente en lotes |
| Rendimiento | Sin `search`/`write`/`create` en loop; `mapped`/`filtered`/`search_count`/`_read_group` |
| i18n | Textos al usuario envueltos en `_()`; no marcar nombres técnicos ni claves de dict |
71 changes: 71 additions & 0 deletions src/.github/instructions/i18n.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
applyTo:
- "**/models/**/*.py"
- "**/wizards/**/*.py"
- "**/wizard/**/*.py"
- "**/controllers/**/*.py"
- "**/report/**/*.py"
- "**/i18n/**/*.po"
- "**/i18n/**/*.pot"
---

# Revisión de internacionalización (i18n)

## Marcar texto traducible

- Todo texto que se muestra al usuario debe estar envuelto en `_()` o `self.env._()` (indistinto):
```python
raise UserError(_("No se puede eliminar un pedido confirmado."))
return {'warning': {'title': _("Atención"), 'message': _("Stock insuficiente.")}}
```
- Import típico: `from odoo import _, _lt` (usar `_lt` cuando el texto se define a nivel módulo/clase y la traducción se resuelve en runtime).

## Qué marcar como issue

- `raise UserError("...")` o `ValidationError("...")` con string literal.
- `return {'warning': {'message': "texto"}}`.
- Mensajes de `raise`, `notifications`, toast, `display_name` calculado, labels en wizards, títulos de acciones construidas dinámicamente.
- Textos en `_message_post` que muestran al usuario.

## Qué NO marcar

- Nombres técnicos de campos (`'partner_id'`, `'name'`).
- Claves de diccionarios (`'state': 'draft'`).
- Logs técnicos (`_logger.info("...")`) — no se traducen.
- Nombres de xml_ids.
- Cadenas en tests, comentarios, docstrings.
- `fields.Char(string="Name")`: el `string=` se recoge para i18n automáticamente, no requiere `_()`.

## Uso correcto de `_()`

- `_()` resuelve traducción **en el momento de la llamada** (runtime del idioma del usuario).
- `_lt()` (lazy translate) para strings definidas a nivel módulo; la traducción se resuelve al serializar, útil en selecciones y listas de constantes.
- No concatenar fragmentos traducibles con `+`: usar `%` o f-string sobre la cadena ya traducida:
```python
# MAL (rompe traducción)
raise UserError(_("Error en ") + record.name)
# BIEN
raise UserError(_("Error en %s") % record.name)
```
- Evitar format con claves traducibles múltiples; preferir placeholders con nombre:
```python
raise UserError(_("Falta %(field)s en %(model)s") % {'field': name, 'model': model})
```

## Archivos `.po` / `.pot`

- No editar manualmente traducciones generadas por `odoo i18n export` salvo correcciones puntuales.
- Commits que solo tocan `.po` / `.pot` de exportación suelen ser benignos; no requieren tests ni script de migración.
- Si se agrega un idioma nuevo, verificar que esté listado en `i18n/` y que las cadenas base existan en `.pot`.

## Convención del equipo (ADHOC)

- Idioma destino principal: **español latinoamericano formal**. Evitar tuteo en mensajes de sistema ("usted" vs "tú").
- Mantener consistencia terminológica: "pedido" (no "orden"), "contacto" (no "partner" en user-facing), "factura", etc.
- Placeholders (`%s`, `%(name)s`) deben mantenerse idénticos entre el mensaje original y la traducción.

## Criterio de severidad

- **Medio**: texto al usuario sin `_()`, aislado.
- **Bajo**: patrón que podría mejorarse (concatenación con `+`, falta de `_lt` en constante de módulo).
- No-issue: archivo `.po` autogenerado con cambios de exportación rutinarios.
48 changes: 48 additions & 0 deletions src/.github/instructions/manifest.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
applyTo:
- "**/__manifest__.py"
---

# Revisión de `__manifest__.py`

## Archivos referenciados

- Todo archivo usado por el módulo (vistas, seguridad, datos, reportes, wizards, demo) debe estar listado en alguna de las claves del manifest (`data`, `demo`, `assets`).
- Si un archivo XML/CSV se borra del módulo, debe removerse del manifest; si se agrega uno nuevo, debe incluirse.
- Orden relativo importa: datos de seguridad antes de datos que los referencian; vistas después de sus modelos.

## Dependencias (`depends`)

- Deben listarse todos los módulos cuyos modelos/vistas/xml_ids se usan directamente.
- **No** declarar dependencias innecesarias (infla el árbol de instalación).
- Módulos de localización (`l10n_*`) solo cuando el módulo depende funcionalmente; no por conveniencia.

## Versión

- Formato `<serie>.<mayor>.<menor>.<patch>` (ej. `19.0.1.0.0`). La serie (`19.0`, `18.0`) debe coincidir con la rama y la versión de Odoo target.
- **Regla obligatoria de versión**: cualquier cambio estructural que requiera script en `migrations/` debe **bumpear la versión** del módulo, y la carpeta bajo `migrations/` debe coincidir.
- Solo comentar la versión **una vez por revisión**, aunque haya múltiples archivos afectados.

## Metadatos

- `name`, `summary`, `description` deben estar definidos y ser consistentes.
- `author`, `license` presentes. En Adhoc, típicamente `"ADHOC SA"` y licencia según convención del repo.
- `category` coherente con el tipo de módulo.
- `installable: True` salvo que explícitamente esté siendo discontinuado.
- `application: True` solo para módulos que deben aparecer como aplicación raíz (no para sub-módulos).

## Assets (bundles)

- `assets` debe listar bundles correctos (`web.assets_backend`, `web.assets_frontend`, `web.report_assets_common`, `web.assets_tests`, etc.).
- Extensiones coherentes: `.js`, `.scss`, `.css`, `.xml` (OWL templates).
- Archivos borrados deben quitarse también de `assets`.

## Hooks

- `pre_init_hook`, `post_init_hook`, `uninstall_hook`, `post_load`: si están declarados, verificar que apunten a funciones existentes en el módulo (`from . import hooks` o similar).
- Los hooks deben ser idempotentes y no dependientes de datos demo.

## Demo data

- Datos de demo en la key `demo`, **no** mezclados con `data`.
- Al introducir funcionalidad nueva que se beneficia de casos visibles, considerar agregar demo; al introducir módulo de configuración, no es necesario.
59 changes: 59 additions & 0 deletions src/.github/instructions/migrations.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
applyTo:
- "**/migrations/**/*.py"
- "**/__manifest__.py"
- "**/models/**/*.py"
---

# Revisión de scripts de migración

> Si el diff introduce cambio estructural en un modelo, **siempre** evaluar si corresponde proponer script en `migrations/<version>/`.

## Cuándo proponer script

1. **Rename de campo almacenado** (`Char`, `Many2one`, etc. o `compute` con `store=True`). **No** si es `compute` sin store.
2. **Rename de modelo**: siempre. Toca `ir.model`, `ir.model.data`, tablas relacionales, vistas, acciones.
3. **Cambio de tipo de campo** con cambio real en DB (`Char→Many2one`, `Selection→Many2one`, `Many2one→Many2many`). Cambios compatibles (`Char→Text`, ajustes de `Float`) no requieren script.
4. **Split/merge de campos**.
5. **Nuevo `compute` con `store=True`** que aplique a registros históricos → post-script de backfill en lotes. Advertir si el modelo tiene millones de registros.
6. **Cambio en keys de `selection`**: renombrar/eliminar existentes → script que mapee `old → new`. Agregar nuevas keys **no** requiere script.
7. **Cambio de dominio** en relacional que excluya valores usados históricamente → limpiar/remapear.
8. **Nueva `UNIQUE`/índice** (`_sql_constraints` o `models.Constraint`): pre-script que resuelva duplicados antes de crear la constraint.
9. **Cambios en `ir.model.data` / XML IDs** (rename `module.name → module2.name2`): script para actualizar referencias.
10. **Registros con `noupdate="1"`** cuyo contenido lógico cambia: forzar update por `xml_id`.
11. **Cambios en reglas de acceso / multi-company / multi-website**: rellenar campos obligatorios, recomputar ownership.

> **No** proponer script solo por `required=True` nuevo sin default, salvo que el diff evidencie datos históricos incompatibles.

## Pre / Post / End

- **pre**: antes del update. Preparar datos/esquemas para evitar fallos.
- **post**: después. Recalcular, limpiar, ajustar referencias.
- **end**: al final del upgrade global. Tareas cross-módulo o finales.

Regla: **rompe durante el upgrade → pre**; **recalcula después → post**; **global al final → end**.

## Mapeo cambio → acción

- **Rename campo almacenado** → pre: copiar datos viejo→nuevo. Post: cleanup + recomputes.
- **Rename modelo** → pre: mapear `ir.model`/`ir.model.data`. Post: re-enlazar vistas, acciones, menús, reglas.
- **Split/merge** → pre: copiar a nuevos campos antes de que el schema borre el viejo. Post: normalizar/recompute.
- **`compute` nuevo con `store=True`** → post: backfill en lotes (pre opcional en modelos grandes para preparar columna).
- **Cambio de tipo con conversión** → pre: columna temporal + conversión. Post: swap/rename/borrar vieja.
- **`selection` (remove/rename keys)** → pre: mapeo `old → new` (usar `change_field_selection_values` si aplica). Post: validar consistencia.
- **Nueva `UNIQUE`** → pre: resolver duplicados. Post: crear índice si aplica.
- **`noupdate="1"` con cambio lógico** → post: update por `xml_id`.

## Convenciones

- Ubicación: `migrations/<module_version>/` (ej. `migrations/19.0.1.0/`). Versión debe coincidir con `__manifest__.py`.
- Nombres: `pre_<desc>.py`, `post_<desc>.py`, `end_<desc>.py`.
- **Idempotentes**: seguros ante re-ejecución.
- **En lotes** (`batch_size` razonable) para datasets grandes.
- Logs claros (`_logger.info`); comentario al inicio documentando supuestos y garantías.
- Evitar transacciones muy largas; `env.cr.commit()` controlado o helpers de progreso.

## Versión del manifest

- Al introducir cambio estructural, **bumpear** versión en `__manifest__.py` para que el script corra (ej. `19.0.1.0 → 19.0.2.0`).
- La carpeta bajo `migrations/` debe coincidir con la nueva versión.
68 changes: 68 additions & 0 deletions src/.github/instructions/models.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
applyTo:
- "**/models/**/*.py"
- "**/wizards/**/*.py"
- "**/wizard/**/*.py"
- "**/report/**/*.py"
---

# Revisión de modelos Python

## Relaciones y campos

- `Many2one`/`One2many`/`Many2many` deben declarar `comodel_name` y `ondelete` apropiado. Evitar `ondelete='cascade'` sin justificación.
- Nombres de campos claros, consistentes, sin conflictos con campos heredados.
- `required=True` sin `default` **solo** si no hay datos históricos que puedan romperse. Si los hay, proponer `default` o migración.
- Campos `compute` con `store=True` que dependen de datos históricos pueden necesitar backfill (ver `migrations.instructions.md`).

## Decoradores `@api.*`

- `@api.depends` debe listar **todas** las dependencias reales, incluidas las dotted (`@api.depends('partner_id.email')`).
- `@api.constrains` **no** acepta dotted paths, solo nombres simples.
- `@api.onchange` no debe escribir a BD ni modificar campos computados.
- Evitar decoradores obsoletos: `@api.one`, `@api.multi` (Odoo 13+ no los acepta).
- **Odoo 18+**: para prevenir borrado usar `@api.ondelete(at_uninstall=False)` en vez de sobreescribir `unlink`.
- `@api.model` solo cuando el método no depende de `self` como recordset.
- `@api.model_create_multi` para métodos `create` que aceptan lista de dicts (obligatorio en Odoo 17+).

## Herencia y `super()`

- Métodos redefinidos deben llamar `super()` salvo que el contrato diga lo contrario. Preservar el tipo/shape del retorno.
- `_name` + `_inherit` juntos solo cuando se busca crear modelo nuevo (multi-table inheritance); marcar si no hay razón clara.
- No sobrescribir `create`/`write`/`unlink` solo para side effects triviales; preferir `@api.depends`, `@api.constrains` o `@api.ondelete`.

## Constraints e índices

- **Odoo 19+**: usar `models.Constraint(...)`, `models.Index(...)`, `models.UniqueIndex(...)` como declarativas a nivel de clase, en vez de `_sql_constraints`. Si el diff ya toca constraints, sugerir migrar a la nueva API.
- Mensajes de constraint deben ser traducibles (`_("...")`).
- Añadir `UNIQUE` sobre tabla con datos existentes puede fallar; ver `migrations.instructions.md`.

## ORM seguro y eficiente

- Evitar `search` dentro de loops → usar dominio con `in` sobre ids o `_read_group`.
- Evitar `write`/`create`/`unlink` uno a uno en loops → vectorizar sobre recordset.
- `create` en Odoo 17+: preferir lista de dicts `create([{...}, {...}])`.
- `mapped`, `filtered`, `search_count`, `search_fetch` antes que recorrer en Python.
- Navegación relacional segura: `rec.partner_id.email` devuelve falso si `partner_id` vacío; no duplicar el check.
- Acceso por índice (`recordset[0]`) puede lanzar `IndexError`; guardar con `if rec: ...` o rediseñar para operar sobre el recordset completo.
- Evitar `sudo()` amplio/innecesario en métodos de negocio; justificar cada uso.
- En Odoo 19, `cr.execute` crudo desaconsejado → usar clase `SQL` con `execute_query_dict()`. Si hay `cr.execute` con interpolación (`%`, f-string, `.format`) → bloqueante, ver `security.instructions.md`.

## Nombres y estilo

- Métodos privados prefijo `_`; en Odoo 19, preferir `@api.private` donde aplica.
- Métodos muy largos (>50 líneas) → sugerir split.
- Comparaciones booleanas: `if x:` / `if not x:` (no `== True` / `== False`).
- `else` después de `return` innecesario.
- Imports no utilizados deben removerse.

## Dominios

- En Odoo 19 es válido `Domain('field', 'op', 'value')` y combinar con `&`, `|`, `~`. No marcar como error.
- `Domain` permite uso en `filtered`: no hace falta convertir a lista.
- Nunca construir dominios como strings y pasarlos por `eval` (ver `security.instructions.md`).

## Selecciones

- Agregar nuevos values a un `selection` **no** requiere migración.
- Renombrar/eliminar keys existentes → proponer script que mapee `old → new` (ver `migrations.instructions.md`).
Loading
Loading